renderPushTemplate($request, 'Push'); } public function sharebasketFormAction(Request $request) { return $this->renderPushTemplate($request, 'Sharebasket'); } /** ---------------------------------------------------------------------------------- * a simple push is made by the current user to many "receivers" (=participants) * * this is the same code as "validation" request, except here we don't create a validation session * * * @param Request $request * @return JsonResponse */ public function sendAction(Request $request) { $ret = [ 'success' => false, 'message' => $this->app->trans('Unable to send the documents') ]; try { $pusher = new RecordHelper\Push($this->app, $request); $push_name = $request->request->get( 'name', $this->app->trans('Push from %user%', [ '%user%' => $this->getAuthenticatedUser()->getDisplayName(), ]) ); $push_description = $request->request->get('push_description'); $receivers = $request->request->get('participants'); if (!is_array($receivers) || empty($receivers)) { throw new ControllerException($this->app->trans('No receivers specified')); } if (!is_array($pusher->get_elements()) || empty($pusher->get_elements())) { throw new ControllerException($this->app->trans('No elements to push')); } $manager = $this->getEntityManager(); foreach ($receivers as $receiver) { try { /** @var User $user_receiver */ $user_receiver = $this->getUserRepository()->find($receiver['usr_id']); } catch (Exception $e) { throw new ControllerException( $this->app->trans('Unknown user %user_id%', ['%user_id%' => $receiver['usr_id']]) ); } $Basket = new Basket(); $Basket->setName($push_name); $Basket->setDescription($push_description); $Basket->setUser($user_receiver); $Basket->setPusher($this->getAuthenticatedUser()); $Basket->markUnread(); $manager->persist($Basket); foreach ($pusher->get_elements() as $element) { $basketElement = new BasketElement(); $basketElement->setRecord($element); $basketElement->setBasket($Basket); $manager->persist($basketElement); $Basket->addElement($basketElement); if ($receiver['HD']) { $this->getAclForUser($user_receiver)->grant_hd_on( $basketElement->getRecord($this->app), $this->getAuthenticatedUser(), ACL::GRANT_ACTION_PUSH ); } else { $this->getAclForUser($user_receiver)->grant_preview_on( $basketElement->getRecord($this->app), $this->getAuthenticatedUser(), ACL::GRANT_ACTION_PUSH ); } $this->getDataboxLogger($element->getDatabox())->log( $element, Session_Logger::EVENT_PUSH, $user_receiver->getId(), '' ); } $manager->flush(); $arguments = [ 'basket' => $Basket->getId(), ]; // here we send an email to each participant // // if we don't request the user to auth (=type his login/pwd), // we generate a !!!! 'view' !!!! token to be included as 'LOG' parameter in url // // - the 'view' token is created to give access to lightbox in "compare" mode, NOT "validation" // - the 'view' token has no expiration // if (!$this->getConf()->get(['registry', 'actions', 'enable-push-authentication']) || !$request->get('force_authentication') ) { $arguments['LOG'] = $this->getTokenManipulator()->createBasketAccessToken($Basket, $user_receiver)->getValue(); } $url = $this->app->url('lightbox_compare', $arguments); $receipt = $request->get('recept') ? $this->getAuthenticatedUser()->getEmail() : ''; $this->dispatch( PhraseaEvents::BASKET_PUSH, new PushEvent($Basket, $request->request->get('message'), $url, $receipt) ); } $manager->flush(); $message = $this->app->trans( '%quantity_records% records have been sent to %quantity_users% users', [ '%quantity_records%' => count($pusher->get_elements()), '%quantity_users%' => count($receivers), ] ); $ret = [ 'success' => true, 'message' => $message ]; } catch (ControllerException $e) { $ret['message'] = $e->getMessage() . $e->getFile() . $e->getLine(); } return $this->app->json($ret); } /** ---------------------------------------------------------------------------------- * a sharebasket request is made by the current user to many participants * * this is the same code as "send" request (=simple push), except here we * - create a validation session, * - register participants and data... * * * @param Request $request * @return JsonResponse * @throws Exception * * @TODO This is too slow even for 100 records + 100 participants --> move this to a worker */ public function sharebasketAction(Request $request) { if (!$this->isCrsfValid($request, 'prodShareBasket')) { return $this->app->json(['success' => false, 'message' => 'invalid form']); } $ret = [ 'success' => false, 'message' => $this->app->trans('Unable to send the documents') ]; $manager = $this->getEntityManager(); $manager->beginTransaction(); try { $pusher = $this->getPushFromRequest($request); $validation_name = $request->request->get('name', $this->app->trans( 'Validation from %user%', [ '%user%' => $this->getAuthenticatedUser()->getDisplayName(), ])); $validation_description = $request->request->get('message'); $isFeedback = $request->request->get('isFeedback') == "1"; $participants = $request->request->get('participants'); if (!is_array($participants) || empty($participants)) { throw new ControllerException($this->app->trans('No participants specified')); } if (!is_array($pusher->get_elements()) || empty($pusher->get_elements())) { throw new ControllerException($this->app->trans('No elements to validate')); } // a sharebasket must apply to a basket... // if ($pusher->is_basket()) { $basket = $pusher->get_original_basket(); if($basket->isVoteBasket()) { // this basket is already under vote // we check this is from the same initator (me) if(!$basket->isVoteInitiator($this->getAuthenticatedUser())) { // one tries to initiate a vote session on a basket which already has another vote initiaton throw new ControllerException("basket already have another vote initiator"); } } } else { // ...so if we got a list of elements (records), we create a basket for those $basket = new Basket(); $basket ->setName($validation_name) ->setDescription($validation_description) ->setUser($this->getAuthenticatedUser()) ->markUnread(); $manager->persist($basket); foreach ($pusher->get_elements() as $element) { $basketElement = new BasketElement(); $basketElement->setRecord($element); $basketElement->setBasket($basket); $manager->persist($basketElement); $basket->addElement($basketElement); } } if(!empty($shareExpiresDate = $request->request->get('shareExpires'))) { $shareExpiresDate = new DateTime($shareExpiresDate); // d: "Y-m-d" } else { $shareExpiresDate = null; } $basket->setShareExpires($shareExpiresDate); // can be null if(!empty($voteExpiresDate = $request->request->get('voteExpires'))) { $voteExpiresDate = new DateTime($voteExpiresDate); // d: "Y-m-d" } else { $voteExpiresDate = null; } $basket->setVoteExpires($voteExpiresDate); // can be null, will be used for token if($isFeedback) { // in case of feedback, the owner must be participant (to see others votes) // he is already on the participants $basket->setVoteInitiator($this->getAuthenticatedUser()); } else { // for a simple share, we will ignore the owner $basket->setVoteInitiator(null); } $manager->persist($basket); $manager->flush(); $manager->refresh($basket); $message = $this->app->trans( '%quantity_records% records have been sent for validation to %quantity_users% users', [ '%quantity_records%' => count($pusher->get_elements()), '%quantity_users%' => count($request->request->get('participants')), ] ); $ret = [ 'success' => true, 'message' => $message, ]; $manager->commit(); $this->dispatch( PhraseaEvents::BASKET_SHARE, new ShareEvent( $request, $basket, $this->getAuthenticatedUser() ) ); } catch (ControllerException $e) { $ret['message'] = $e->getMessage(); $manager->rollback(); } return $this->app->json($ret); } /** * @param $usr_id * @return JsonResponse */ public function getUserAction($usr_id) { $data = null; $query = $this->createUserQuery(); $query->on_bases_where_i_am($this->getAclForUser($this->getAuthenticatedUser()), [ACL::CANPUSH]); $query->in([$usr_id]); $result = $query->include_phantoms() ->limit(0, 1) ->execute()->get_results(); if ($result) { foreach ($result as $user) { $data = $this->formatUser($user); } } return $this->app->json($data); } /** * @param $list_id * @return JsonResponse */ public function getListAction($list_id) { $data = null; $repository = $this->getUserListRepository(); $list = $repository->findUserListByUserAndId($this->getAuthenticatedUser(), $list_id); if ($list) { $data = $this->formatUserList($list); } return $this->app->json($data); } /** * @param Request $request * @return JsonResponse * @throws Exception */ public function addUserAction(Request $request) { if (!$this->isCrsfValid($request, 'prodShareAddUser')) { return $this->app->json(['success' => false , 'message' => 'invalid add user form'], 403); } $result = ['success' => false, 'message' => '', 'user' => null]; try { if (!$this->getAclForUser($this->getAuthenticatedUser())->has_right(ACL::CANADMIN)) throw new ControllerException($this->app->trans('You are not allowed to add users')); if (!$request->request->get('firstname')) throw new ControllerException($this->app->trans('First name is required')); if (!$request->request->get('lastname')) throw new ControllerException($this->app->trans('Last name is required')); if (!$request->request->get('email')) throw new ControllerException($this->app->trans('Email is required')); if (!Swift_Validate::email($request->request->get('email'))) throw new ControllerException($this->app->trans('Email is invalid')); } catch (ControllerException $e) { $result['message'] = $e->getMessage(); return $this->app->json($result); } $user = null; $email = $request->request->get('email'); if (null !== $user = $this->getUserRepository()->findByEmail($email)) { $result['message'] = $this->app->trans('User already exists'); $result['success'] = true; $result['user'] = $this->formatUser($user); return $this->app->json($result); } try { $manager = $this->getEntityManager(); $password = $this->getRandomGenerator()->generateString(128); $user = $this->getUserManipulator()->createUser($email, $password, $email); $user ->setFirstName($request->request->get('firstname')) ->setLastName($request->request->get('lastname')) ; if ($request->request->get('company')) { $user->setCompany($request->request->get('company')); } if ($request->request->get('job')) { $user->setJob($request->request->get('job')); } if ($request->request->get('city')) { $this->getUserManipulator()->setGeonameId($user, $request->request->get('city')); } $manager->persist($user); $manager->flush(); $result['message'] = $this->app->trans('User successfully created'); $result['success'] = true; $result['user'] = $this->formatUser($user); } catch (Exception $e) { $result['message'] = $this->app->trans('Error while creating user'); } return $this->app->json($result); } /** * @param Request $request * @return string */ public function getAddUserFormAction(Request $request) { $params = ['callback' => $request->query->get('callback')]; $this->setSessionFormToken('prodShareAddUser'); return $this->render('prod/User/Add.html.twig', $params); } /** * @param Request $request * @return JsonResponse */ public function searchUserAction(Request $request) { $query = $this->createUserQuery(); $query->on_bases_where_i_am($this->getAclForUser($this->getAuthenticatedUser()), [ACL::CANPUSH]); $query ->like(User_Query::LIKE_FIRSTNAME, $request->query->get('query')) ->like(User_Query::LIKE_LASTNAME, $request->query->get('query')) ->like(User_Query::LIKE_LOGIN, $request->query->get('query')) ->like(User_Query::LIKE_EMAIL, $request->query->get('query')) ->like(User_Query::LIKE_COMPANY, $request->query->get('query')) ->like_match(User_Query::LIKE_MATCH_OR); $result = $query ->include_phantoms() ->limit(0, 50) ->execute()->get_results(); $repository = $this->getUserListRepository(); $lists = $repository->findUserListLike($this->getAuthenticatedUser(), $request->query->get('query')); $data = []; if ($lists) { foreach ($lists as $list) { $data[] = $this->formatUserList($list); } } if ($result) { foreach ($result as $user) { $data[] = $this->formatUser($user); } } return $this->app->json($data); } /** * @param Request $request * @param $list_id * @return Response */ public function editListAction(Request $request, $list_id) { $repository = $this->getUserListRepository(); $list = $repository->findUserListByUserAndId($this->getAuthenticatedUser(), $list_id); $query = $this->createUserQuery(); $query->on_bases_where_i_am($this->getAclForUser($this->getAuthenticatedUser()), [ACL::CANPUSH]); if ($request->get('query')) { $query ->like($request->get('like_field'), $request->get('query')) ->like_match(User_Query::LIKE_MATCH_OR); } if (is_array($request->get('EmailDomain'))) { $query->haveEmailDomains($request->get('EmailDomain')); } if (is_array($request->get('Activity'))) { $query->haveActivities($request->get('Activity')); } if (is_array($request->get('Template'))) { $query->haveTemplate($request->get('Template')); } if (is_array($request->get('Company'))) { $query->inCompanies($request->get('Company')); } if (is_array($request->get('Country'))) { $query->inCountries($request->get('Country')); } if (is_array($request->get('Position'))) { $query->havePositions($request->get('Position')); } $sort = $request->get('srt', 'usr_creationdate'); $ord = $request->get('ord', 'desc'); $perPage = 10; $offset_start = Max(((int) $request->get('page') - 1) * $perPage, 0); $query->sort_by($sort, $ord); $results = $query ->include_phantoms() ->limit($offset_start, $perPage) ->execute()->get_results(); $params = [ 'query' => $query, 'results' => $results, 'list' => $list, 'sort' => $sort, 'ord' => $ord, ]; if ($request->get('type') === 'fragment') { return new Response( $this->render('prod/actions/Feedback/ResultTable.html.twig', $params) ); } return new Response( $this->render('prod/actions/Feedback/list.html.twig', $params) ); } /** * update the expiration date of a validation session * also update the expiration of the participants validation tokens * * @param Request $request * @return JsonResponse * @throws Exception */ public function updateExpirationAction(Request $request) { $ret = [ 'success' => false, 'message' => 'Expiration date not updated!' ]; // sanity check if (is_null($request->request->get('date'))) { throw new Exception('The provided date is null!'); } $manager = $this->getEntityManager(); $manager->beginTransaction(); try { $basket = $this->getBasketRepository()->findUserBasket($request->request->get('basket_id'), $this->app->getAuthenticatedUser(), true); $expirationDate = new DateTime($request->request->get('date') . " 23:59:59"); if ($basket->isVoteBasket()) { // update validation tokens expiration // /** @var BasketParticipant $participant */ foreach($basket->getParticipants() as $participant) { try { if(!is_null($token = $this->getTokenRepository()->findValidationToken($basket, $participant->getUser()))) { if($participant->getUser()->getId() === $basket->getVoteInitiator()->getId()) { // the initiator keeps a no-expiration token $token->setExpiration(null); // shoud already be null, but who knows... } else { // the "normal" user token is fixed $token->setExpiration($expirationDate); } } } catch (Exception $e) { // not unique token ? should not happen. // no-op } } $basket->setVoteExpires($expirationDate); $manager->persist($basket); $manager->flush(); $manager->commit(); $ret = [ 'success' => true, 'message' => $this->app->trans('Expiration date successfully updated!') ]; } elseif ($basket->getParticipants()->count() > 0 && !$basket->isVoteBasket()) { if (empty($request->request->get('date'))) { $basket->setShareExpires(null); } else { $basket->setShareExpires($expirationDate); } $manager->persist($basket); $manager->flush(); $manager->commit(); $ret = [ 'success' => true, 'message' => $this->app->trans('Expiration date successfully updated!') ]; } // update records_rights expiration foreach ($basket->getParticipants() as $participant) { $userAcl = $this->getAclForUser($participant->getUser()); foreach ($basket->getElements() as $bElement) { $userAcl->update_expire_grant_hd($bElement->getRecord($this->app), \ACL::GRANT_ACTION_VALIDATE, $request->request->get('date') . " 23:59:59"); } } } catch (Exception $e) { $ret = [ 'success' => false, 'message' => $e->getMessage() ]; $manager->rollback(); } return $this->app->json($ret); } private function formatUser(User $user) { $subtitle = array_filter([$user->getJob(), $user->getCompany()]); return [ 'type' => 'USER', 'usr_id' => $user->getId(), 'firstname' => $user->getFirstName(), 'lastname' => $user->getLastName(), 'email' => $user->getEmail(), 'display_name' => $user->getDisplayName(), 'subtitle' => implode(', ', $subtitle), ]; } private function formatUserList(UsrList $list) { $entries = []; foreach ($list->getEntries() as $entry) { $entries[] = [ 'Id' => $entry->getId(), 'User' => $this->formatUser($entry->getUser()), ]; } return [ 'type' => 'LIST', 'list_id' => $list->getId(), 'name' => $list->getName(), 'length' => count($entries), 'entries' => $entries, ]; } /** * from a list of records, return "users" from field(s) declared as vocabularyType/user * * this list of users will be displayed as "RecommendedUsers" for push * !!!!!!!!!!!!!!! todo : also for share baskets ? !!!!!!!!!!!! * !!!!! useless (?) in 4.1 since editing a vocab field does nothing special : * the field value has no vocab/id value * * * @param array|record_adapter[] $selection * @return ArrayCollection Users */ private function getUsersInSelectionExtractor($selection) { $users = new ArrayCollection(); foreach ($selection as $record) { foreach ($record->get_caption()->get_fields() as $caption_field) { foreach ($caption_field->get_values() as $value) { if (!$value->getVocabularyType()) continue; if ($value->getVocabularyType()->getType() !== 'User') continue; $user = $value->getResource(); $users->set($user->getId(), $user); } } } return $users; } /** * @return UsrListRepository */ private function getUserListRepository() { return $this->app['repo.usr-lists']; } /** * @param Request $request * @param $context * @return string */ public function renderPushTemplate(Request $request, $context) { $push = $this->getPushFromRequest($request); $feedbackaction = $request->request->get('feedbackaction'); $participants = []; $participantsHDRight = []; $participantUserIds = ''; $initiatorUserId = null; if ($context === 'Sharebasket') { if ($push->is_basket() ) { // edit an existing sharebasket // $basket = $push->get_original_basket(); $participants = $basket->getParticipants(); $participantUserIds = implode('_', $basket->getListParticipantsUserId()); // $initiatorUserId = $basket->isVoteBasket() // ? $basket->getVoteInitiator()->getId() // : $this->getAuthenticatedUser()->getId(); $initiatorUserId = $basket->getVoteInitiator() ? $basket->getVoteInitiator()->getId() : null; foreach ($participants as $participant) { $userAcl = $this->getAclForUser($participant->getUser()); if (count($basket->getElements()) > 0) { $participantsHDRight[$participant->getUser()->getId()] = true; } foreach ($basket->getElements() as $bElement) { if(!$userAcl->has_hd_grant($bElement->getRecord($this->app))) { $participantsHDRight[$participant->getUser()->getId()] = false; break 1; } } } } else { // initiate a share from a list of records // add the initiator in the participant list window when the first time to create a feedback $basketParticipant = new BasketParticipant($this->getAuthenticatedUser()); $basketParticipant->setCanSeeOthers(1); array_push($participants, $basketParticipant); $participantUserIds = $this->getAuthenticatedUser()->getId(); // list with a single user // $initiatorUserId = $this->getAuthenticatedUser()->getId(); $initiatorUserId = null; } } else { // context = "Push" } $repository = $this->getUserListRepository(); $recommendedUsers = $this->getUsersInSelectionExtractor($push->get_elements()); $this->setSessionFormToken('prodShareBasket'); return $this->render( 'prod/actions/Push.html.twig', [ 'push' => $push, 'message' => '', 'lists' => $repository->findUserLists($this->getAuthenticatedUser()), 'context' => $context, 'RecommendedUsers' => $recommendedUsers, 'participants' => $participants, 'participantUserIds' => $participantUserIds, 'feedbackAction' => $feedbackaction, 'owner' => $this->getAuthenticatedUser(), 'initiatorUserId' => $initiatorUserId, 'participantsHDRight' => $participantsHDRight ] ); } /** * @return UserRepository */ private function getUserRepository() { return $this->app['repo.users']; } /** * @return TokenManipulator */ private function getTokenManipulator() { return $this->app['manipulator.token']; } /** * @param Request $request * @return RecordHelper\Push */ private function getPushFromRequest(Request $request) { return new RecordHelper\Push($this->app, $request); } /** * @return UserManipulator */ private function getUserManipulator() { return $this->app['manipulator.user']; } /** * @return Generator */ private function getRandomGenerator() { return $this->app['random.medium']; } /** * @return BasketRepository */ private function getBasketRepository() { return $this->app['repo.baskets']; } /** * @return TokenRepository */ private function getTokenRepository() { return $this->app['repo.tokens']; } }