diff --git a/.env b/.env index ab636e5433..dea64d2819 100644 --- a/.env +++ b/.env @@ -68,7 +68,7 @@ PHRASEANET_SWFTOOLS_TIMEOUT=120 PHRASEANET_UNOCON_TIMEOUT=120 PHRASEANET_EXIFTOOL_TIMEOUT=120 -# network +# network : comma separated list of IP ou SUBNETS PHRASEANET_TRUSTED_PROXIES= diff --git a/bin/console b/bin/console index 744c7c7959..cbcb1b4a74 100755 --- a/bin/console +++ b/bin/console @@ -40,6 +40,7 @@ use Alchemy\Phrasea\Command\Plugin\AddPlugin; use Alchemy\Phrasea\Command\Plugin\RemovePlugin; use Alchemy\Phrasea\Command\CheckConfig; use Alchemy\Phrasea\Command\SearchEngine\MappingUpdateCommand; +use Alchemy\Phrasea\Command\SendValidationRemindersCommand; use Alchemy\Phrasea\Command\Setup\H264ConfigurationDumper; use Alchemy\Phrasea\Command\Setup\H264MappingGenerator; use Alchemy\Phrasea\Command\Setup\XSendFileConfigurationDumper; @@ -169,6 +170,8 @@ $cli->command(new WorkerExecuteCommand()); $cli->command(new WorkerRunServiceCommand()); $cli->command(new WorkerShowConfigCommand()); +$cli->command(new SendValidationRemindersCommand()); + $cli->loadPlugins(); $cli->run(); diff --git a/docker/phraseanet/entrypoint.sh b/docker/phraseanet/entrypoint.sh index 36c645e181..4fd1e76c79 100755 --- a/docker/phraseanet/entrypoint.sh +++ b/docker/phraseanet/entrypoint.sh @@ -16,7 +16,10 @@ if [ -f "$FILE" ]; then bin/setup system:config set registry.general.title $PHRASEANET_PROJECT_NAME fi if [[ -n $PHRASEANET_TRUSTED_PROXIES ]]; then - bin/setup system:config add trusted-proxies $PHRASEANET_TRUSTED_PROXIES + for i in $(echo $PHRASEANET_TRUSTED_PROXIES | sed "s/,/ /g") + do + bin/setup system:config add trusted-proxies $i + done fi bin/setup system:config set main.binaries.ffmpeg_timeout $PHRASEANET_FFMPEG_TIMEOUT diff --git a/lib/Alchemy/Phrasea/Command/Command.php b/lib/Alchemy/Phrasea/Command/Command.php index e7acfabd44..9ce36b2824 100644 --- a/lib/Alchemy/Phrasea/Command/Command.php +++ b/lib/Alchemy/Phrasea/Command/Command.php @@ -114,14 +114,17 @@ abstract class Command extends SymfoCommand implements CommandInterface */ public function getFormattedDuration($seconds) { - $duration = ceil($seconds) . ' seconds'; - - if ($duration > 60) { - $duration = round($duration / 60, 1) . ' minutes'; - } elseif ($duration > 3600) { - $duration = round($duration / (60 * 60), 1) . ' hours'; - } elseif ($duration > (24 * 60 * 60)) { - $duration = round($duration / (24 * 60 * 60), 1) . ' days'; + if ($seconds > (24 * 3600)) { + $duration = round($seconds / (24 * 3600), 1) . ' days'; + } + elseif ($seconds > 3600) { + $duration = round($seconds / (3600), 1) . ' hours'; + } + elseif ($seconds > 60) { + $duration = round($seconds / 60, 1) . ' minutes'; + } + else { + $duration = ceil($seconds) . ' seconds'; } return $duration; diff --git a/lib/Alchemy/Phrasea/Command/SendValidationRemindersCommand.php b/lib/Alchemy/Phrasea/Command/SendValidationRemindersCommand.php new file mode 100644 index 0000000000..659325fd98 --- /dev/null +++ b/lib/Alchemy/Phrasea/Command/SendValidationRemindersCommand.php @@ -0,0 +1,351 @@ +setDescription('Send validation reminders. (experimental)'); + $this->addOption('dry',null, InputOption::VALUE_NONE,'dry run, list but don\'t act'); + $this->addOption('now', null,InputArgument::OPTIONAL, 'fake today'); + $this->addOption('days', null,InputArgument::OPTIONAL, 'overwrite validation-reminder-days'); + } + + + /** + * sanity check the cmd line options + * + * @return bool + */ + protected function sanitizeArgs() + { + $r = true; + + // --dry + $this->dry = $this->input->getOption('dry') ? true : false; + + // --now + if(($v = $this->input->getOption('now')) !== null) { + try { + $this->now = new DateTime($v); + } + catch (Exception $e) { + $this->output->writeln(sprintf('bad --date "%s"', $v)); + $r = false; + } + } + else { + $this->now = new DateTime(); + } + + // --days + if(($v = $this->input->getOption('days')) !== null) { + if(($this->days = (int)$v) <= 0) { + $this->output->writeln(sprintf('--days must be > 0 (bad value "%s")', $v)); + $r = false; + } + } + else { + $this->days = (int)$this->getConf()->get(['registry', 'actions', 'validation-reminder-days']); + } + + return $r; + } + + protected function doExecute(InputInterface $input, OutputInterface $output) + { + $this->input = $input; + $this->output = $output; + + $this->setDelivererLocator(new LazyLocator($this->container, 'notification.deliverer')); + + if(!$this->sanitizeArgs()) { + return -1; + } + + $date_to = clone($this->now); + $interval = sprintf('P%dD', $this->days); + try { + $date_to->add(new DateInterval($interval)); + } + catch(Exception $e) { + $this->output->writeln(sprintf('Bad interval "%s" ?', $interval)); + return -1; + } + + if($this->dry) { + $this->output->writeln('dry mode : emails will NOT be sent'); + } + + $output->writeln(sprintf('from "%s" to "%s" (days=%d), ', $this->now->format(self::DATE_FMT), $date_to->format(self::DATE_FMT), $this->days)); + + $fmt = ' participant: %-11s user: %-10s %s token: %-10s '; + //$output->writeln(sprintf($fmt, 'session', 'basket', 'participant', 'user', 'token', 'email')); + + $last_session = null; + foreach ($this->getValidationParticipantRepository()->findNotConfirmedAndNotRemindedParticipantsByExpireDate($date_to, $this->now) as $participant) { + $validationSession = $participant->getSession(); + $basket = $validationSession->getBasket(); + + // change session ? display header + if($validationSession->getId() !== $last_session) { + try { + $basket_name = $basket->getName(); + } + catch(Exception $e) { + // basket not found ? + $basket_name = '?'; + } + + try { + $initiator_email = $validationSession->getInitiator()->getEmail(); + } + catch(Exception $e) { + // initiator user not found ? + $initiator_email = '?'; + } + + $output->writeln(''); + $output->writeln(sprintf('session_id: %s (created %s by "%s", expire %s), basket_id: %s ("%s")', + $validationSession->getId(), + $validationSession->getCreated()->format(self::DATE_FMT), + $initiator_email, + $validationSession->getExpires()->format(self::DATE_FMT), + $basket->getId(), + $basket_name + )); + + $last_session = $validationSession->getId(); + } + + // now display participant + $can_send = true; + + // fu..ing doctrine : we can get user id if user does not exists ! we must try to hydrate to get an exception ! + $user = $participant->getUser(); // always ok ! + try { + $str_email = $user->getEmail(); // force to hydrate + } + catch (Exception $e) { + $str_email = 'user not found'; + $can_send = false; + } + + // find the token if exists + // nb : a validation may have not generated tokens if forcing auth was required upon creation + $token = null; + try { + $token = $this->getTokenRepository()->findValidationToken($basket, $user); + if($token) { + $str_token = sprintf('%s (cre. %s, exp. %s)', + $this->dotdot($token->getValue(), 10), + $token->getCreated()->format(self::DATE_FMT), + ($token->getExpiration())? $token->getExpiration()->format(self::DATE_FMT): "null" + ); + } + else { + $str_token = '(no token))'; // token not found + } + } + catch (Exception $e) { + // not unique token ? should not happen + $str_token = sprintf('%s', $e->getMessage()); + $can_send = false; + } + + $output->writeln(sprintf($fmt, + $this->dotdot($participant->getId(), 10), + $this->dotdot($user->getId(), 10), + $this->dotdot($str_email, 30, 'left', '"', '"'), + $str_token + ) + ); + + if(!$can_send) { + continue; + } + + if(!is_null($token)) { + $url = $this->container->url('lightbox_validation', ['basket' => $basket->getId(), 'LOG' => $token->getValue()]); + } + else { + $url = $this->container->url('lightbox_validation', ['basket' => $basket->getId()]); + } + + // $this->dispatch(PhraseaEvents::VALIDATION_REMINDER, new ValidationEvent($participant, $basket, $url)); + $this->doRemind($participant, $basket, $url); + } + + $this->getEntityManager()->flush(); + + return 0; + } + + /** + * format a string to a specified length + * + * @param string $s + * @param int $l + * @param string $align 'left' or 'right' + * @param string $pfx prefix to add, e.g '("' + * @param string $sfx suffix to add, e.g '")' + * @return string + */ + private function dotdot($s, $l, $align='left', $pfx='', $sfx='') + { + $l -= (strlen($pfx) + strlen($sfx)); + if(strlen($s) > $l) { + $s = $pfx . substr($s, 0, $l-1) . "\xE2\x80\xA6" . $sfx; + } + else { + $spc = str_repeat(' ', $l-strlen($s)); + $s = $align=='left' ? ($pfx . $s . $sfx . $spc) : ($spc . $pfx . $s . $sfx); + } + + return $s; + } + + private function doRemind(ValidationParticipant $participant, Basket $basket, $url) + { + $params = [ + 'from' => $basket->getValidation()->getInitiator()->getId(), + 'to' => $participant->getUser()->getId(), + 'ssel_id' => $basket->getId(), + 'url' => $url, + ]; + + $datas = json_encode($params); + + $mailed = false; + + $user_from = $basket->getValidation()->getInitiator(); + $user_to = $participant->getUser(); + + if ($this->shouldSendNotificationFor($participant->getUser(), 'eventsmanager_notify_validationreminder')) { + $readyToSend = false; + $title = $receiver = $emitter = null; + try { + $title = $basket->getName(); + + $receiver = Receiver::fromUser($user_to); + $emitter = Emitter::fromUser($user_from); + + $readyToSend = true; + } + catch (Exception $e) { + // no-op + } + + if ($readyToSend) { + $this->output->writeln(sprintf(' -> remind "%s" from "%s" to "%s"', $title, $emitter->getEmail(), $receiver->getEmail())); + if(!$this->dry) { + // for real + $mail = MailInfoValidationReminder::create($this->container, $receiver, $emitter); + $mail->setButtonUrl($params['url']); + $mail->setTitle($title); + + $this->deliver($mail); + $mailed = true; + + $participant->setReminded(new DateTime('now')); + $this->getEntityManager()->persist($participant); + } + } + } + + return $this->container['events-manager']->notify($params['to'], 'eventsmanager_notify_validationreminder', $datas, $mailed); + } + + + + /** + * @return EntityManagerInterface + */ + private function getEntityManager() + { + return $this->container['orm.em']; + } + + /** + * @return PropertyAccess + */ + protected function getConf() + { + return $this->container['conf']; + } + + /** + * @return ValidationParticipantRepository + */ + private function getValidationParticipantRepository() + { + return $this->container['repo.validation-participants']; + } + + /** + * @return TokenRepository + */ + private function getTokenRepository() + { + return $this->container['repo.tokens']; + } + + /** + * @param User $user + * @param $type + * @return mixed + */ + protected function shouldSendNotificationFor(User $user, $type) + { + return $this->container['settings']->getUserNotificationSetting($user, $type); + } +} diff --git a/lib/Alchemy/Phrasea/Controller/LightboxController.php b/lib/Alchemy/Phrasea/Controller/LightboxController.php index 19c7378c1f..79d1490a2b 100644 --- a/lib/Alchemy/Phrasea/Controller/LightboxController.php +++ b/lib/Alchemy/Phrasea/Controller/LightboxController.php @@ -18,8 +18,10 @@ use Alchemy\Phrasea\Model\Entities\Basket; use Alchemy\Phrasea\Model\Entities\FeedEntry; use Alchemy\Phrasea\Model\Entities\Token; use Alchemy\Phrasea\Model\Entities\ValidationData; +use Alchemy\Phrasea\Model\Manipulator\TokenManipulator; use Alchemy\Phrasea\Model\Repositories\BasketElementRepository; use Alchemy\Phrasea\Model\Repositories\BasketRepository; +use Alchemy\Phrasea\Model\Repositories\TokenRepository; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; @@ -416,6 +418,7 @@ class LightboxController extends Controller /** * @param Basket $basket * @return Response + * @throws \Doctrine\ORM\NonUniqueResultException */ public function ajaxSetReleaseAction(Basket $basket) { @@ -431,8 +434,17 @@ class LightboxController extends Controller $this->assertAtLeastOneElementAgreed($basket); $participant = $basket->getValidation()->getParticipant($this->getAuthenticatedUser()); - /** @var Token $token */ - $token = $this->app['manipulator.token']->createBasketValidationToken($basket); + // find / create a "validate" token so the initator of the session can view results (no expiration) + $initiatorUser = $basket->getValidation()->getInitiator(); + + if(is_null($token = $this->getTokenRepository()->findValidationToken($basket, $initiatorUser))) { + // should not happen since when a validation is created, the initiator is force-included as a participant + $token = $this->getTokenManipulator()->createBasketValidationToken($basket, $initiatorUser, null); + } + else { + // a token already exists for the initiator + $token->setExpiration(null); // the expiration for initiator should already be null... + } $url = $this->app->url('lightbox', ['LOG' => $token->getValue()]); $this->dispatch(PhraseaEvents::VALIDATION_DONE, new ValidationEvent($participant, $basket, $url)); @@ -443,7 +455,8 @@ class LightboxController extends Controller $this->app['orm.em']->flush(); $data = ['error' => false, 'datas' => $this->app->trans('Envoie avec succes')]; - } catch (Exception $e) { + } + catch (Exception $e) { $data = ['error' => true, 'datas' => $e->getMessage()]; } @@ -510,4 +523,21 @@ class LightboxController extends Controller $message = $this->app->trans('You have to give your feedback at least on one document to send a report'); throw new Exception($message); } + + /** + * @return TokenManipulator + */ + private function getTokenManipulator() + { + return $this->app['manipulator.token']; + } + + /** + * @return TokenRepository + */ + private function getTokenRepository() + { + return $this->app['repo.tokens']; + } + } diff --git a/lib/Alchemy/Phrasea/Controller/Prod/PushController.php b/lib/Alchemy/Phrasea/Controller/Prod/PushController.php index e378caba24..a4de6b17ad 100644 --- a/lib/Alchemy/Phrasea/Controller/Prod/PushController.php +++ b/lib/Alchemy/Phrasea/Controller/Prod/PushController.php @@ -9,6 +9,7 @@ */ namespace Alchemy\Phrasea\Controller\Prod; +use ACL; use Alchemy\Phrasea\Application\Helper\DataboxLoggerAware; use Alchemy\Phrasea\Application\Helper\DispatcherAware; use Alchemy\Phrasea\Application\Helper\EntityManagerAware; @@ -28,13 +29,22 @@ use Alchemy\Phrasea\Model\Entities\ValidationParticipant; use Alchemy\Phrasea\Model\Entities\ValidationSession; use Alchemy\Phrasea\Model\Manipulator\TokenManipulator; use Alchemy\Phrasea\Model\Manipulator\UserManipulator; +use Alchemy\Phrasea\Model\Repositories\BasketRepository; +use Alchemy\Phrasea\Model\Repositories\TokenRepository; use Alchemy\Phrasea\Model\Repositories\UserRepository; use Alchemy\Phrasea\Model\Repositories\UsrListRepository; +use DateTime; use Doctrine\Common\Collections\ArrayCollection; +use Exception; use RandomLib\Generator; +use record_adapter; +use Session_Logger; +use Swift_Validate; +use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +use User_Query; class PushController extends Controller { @@ -53,6 +63,16 @@ class PushController extends Controller return $this->renderPushTemplate($request, 'Feedback'); } + + /** ---------------------------------------------------------------------------------- + * 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 = [ @@ -86,7 +106,7 @@ class PushController extends Controller try { /** @var User $user_receiver */ $user_receiver = $this->getUserRepository()->find($receiver['usr_id']); - } catch (\Exception $e) { + } catch (Exception $e) { throw new ControllerException( $this->app->trans('Unknown user %user_id%', ['%user_id%' => $receiver['usr_id']]) ); @@ -114,19 +134,19 @@ class PushController extends Controller $this->getAclForUser($user_receiver)->grant_hd_on( $basketElement->getRecord($this->app), $this->getAuthenticatedUser(), - \ACL::GRANT_ACTION_PUSH + ACL::GRANT_ACTION_PUSH ); } else { $this->getAclForUser($user_receiver)->grant_preview_on( $basketElement->getRecord($this->app), $this->getAuthenticatedUser(), - \ACL::GRANT_ACTION_PUSH + ACL::GRANT_ACTION_PUSH ); } $this->getDataboxLogger($element->getDatabox())->log( $element, - \Session_Logger::EVENT_VALIDATE, + Session_Logger::EVENT_VALIDATE, $user_receiver->getId(), '' ); @@ -138,9 +158,15 @@ class PushController extends Controller 'basket' => $Basket->getId(), ]; - if (!$this->getConf()->get(['registry', 'actions', 'enable-push-authentication']) - || !$request->get('force_authentication') - ) { + // 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(); } @@ -176,6 +202,18 @@ class PushController extends Controller return $this->app->json($ret); } + + /** ---------------------------------------------------------------------------------- + * a validation 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 + */ public function validateAction(Request $request) { $ret = [ @@ -205,9 +243,13 @@ class PushController extends Controller throw new ControllerException($this->app->trans('No elements to validate')); } + // a validation must apply to a basket... + // if ($pusher->is_basket()) { $basket = $pusher->get_original_basket(); - } else { + } + else { + // ...so if we got a list of elements (records), we create a basket for those $basket = new Basket(); $basket->setName($validation_name); $basket->setDescription($validation_description); @@ -230,24 +272,35 @@ class PushController extends Controller $manager->refresh($basket); + // if the basket is already under validation, we work on it + // else we create a validationSession + // + $expireDate = null; if (!$basket->getValidation()) { + // create the validationSession $Validation = new ValidationSession(); $Validation->setInitiator($this->getAuthenticatedUser()); $Validation->setBasket($basket); + // add an expiration date if a duration was specified $duration = (int)$request->request->get('duration'); if ($duration > 0) { - $date = new \DateTime('+' . $duration . ' day' . ($duration > 1 ? 's' : '')); - $Validation->setExpires($date); + $expireDate = new DateTime('+' . $duration . ' day' . ($duration > 1 ? 's' : '')); + $Validation->setExpires($expireDate); } $basket->setValidation($Validation); $manager->persist($Validation); - } else { + } + else { + // go on with existing validationSession $Validation = $basket->getValidation(); + $expireDate = $Validation->getExpires(); } + // we always add the author of the validation request (current user) to the participants + // $found = false; foreach ($participants as $participant) { if ($participant['usr_id'] === $this->getAuthenticatedUser()->getId()) { @@ -265,7 +318,11 @@ class PushController extends Controller ]; } + // add participants to the validationSession + // foreach ($participants as $key => $participant) { + + // sanity check foreach (['see_others', 'usr_id', 'agree', 'HD'] as $mandatoryParam) { if (!array_key_exists($mandatoryParam, $participant)) { throw new ControllerException( @@ -277,17 +334,21 @@ class PushController extends Controller try { /** @var User $participantUser */ $participantUser = $this->getUserRepository()->find($participant['usr_id']); - } catch (\Exception $e) { + } + catch (Exception $e) { throw new ControllerException( $this->app->trans('Unknown user %usr_id%', ['%usr_id%' => $participant['usr_id']]) ); } + // end of sanity check + // if participant already exists, skip insertion try { $Validation->getParticipant($participantUser); continue; - } catch (NotFoundHttpException $e) { - + } + catch (NotFoundHttpException $e) { + // participant not yet exists, create } $validationParticipant = new ValidationParticipant(); @@ -309,13 +370,14 @@ class PushController extends Controller $this->getAclForUser($participantUser)->grant_hd_on( $basketElement->getRecord($this->app), $this->getAuthenticatedUser(), - \ACL::GRANT_ACTION_VALIDATE + ACL::GRANT_ACTION_VALIDATE ); - } else { + } + else { $this->getAclForUser($participantUser)->grant_preview_on( $basketElement->getRecord($this->app), $this->getAuthenticatedUser(), - \ACL::GRANT_ACTION_VALIDATE + ACL::GRANT_ACTION_VALIDATE ); } @@ -324,7 +386,7 @@ class PushController extends Controller $this->getDataboxLogger($basketElement->getRecord($this->app)->getDatabox())->log( $basketElement->getRecord($this->app), - \Session_Logger::EVENT_PUSH, + Session_Logger::EVENT_PUSH, $participantUser->getId(), '' ); @@ -332,6 +394,7 @@ class PushController extends Controller $validationParticipant->addData($validationData); } + /** @var ValidationParticipant $validationParticipant */ $validationParticipant = $manager->merge($validationParticipant); $manager->flush(); @@ -340,10 +403,22 @@ class PushController extends Controller 'basket' => $basket->getId(), ]; - if (!$this->getConf()->get(['registry', 'actions', 'enable-push-authentication']) - || !$request->get('force_authentication') - ) { - $arguments['LOG'] = $this->getTokenManipulator()->createBasketAccessToken($basket, $participantUser)->getValue(); + // here we send an email to each participant + // + // if we don't request the user to auth (=type his login/pwd), + // we generate a !!!! 'validate' !!!! token to be included as 'LOG' parameter in url + // + // - the 'validate' token has same expiration as validation-session (except for initiator) + // + if (!$this->getConf()->get(['registry', 'actions', 'enable-push-authentication']) || !$request->get('force_authentication') ) { + if($participantUser->getId() === $this->getAuthenticatedUser()->getId()) { + // the initiator of the validation gets a no-expire token (so he can see result after validation expiration) + $arguments['LOG'] = $this->getTokenManipulator()->createBasketValidationToken($basket, $participantUser, null)->getValue(); + } + else { + // a "normal" participant/user gets a expiring token + $arguments['LOG'] = $this->getTokenManipulator()->createBasketValidationToken($basket, $participantUser, $expireDate)->getValue(); + } } $url = $this->app->url('lightbox_validation', $arguments); @@ -390,12 +465,16 @@ class PushController extends Controller 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->on_bases_where_i_am($this->getAclForUser($this->getAuthenticatedUser()), [ACL::CANPUSH]); $query->in([$usr_id]); @@ -412,6 +491,10 @@ class PushController extends Controller return $this->app->json($data); } + /** + * @param $list_id + * @return JsonResponse + */ public function getListAction($list_id) { $data = null; @@ -426,12 +509,17 @@ class PushController extends Controller return $this->app->json($data); } + /** + * @param Request $request + * @return JsonResponse + * @throws Exception + */ public function addUserAction(Request $request) { $result = ['success' => false, 'message' => '', 'user' => null]; try { - if (!$this->getAclForUser($this->getAuthenticatedUser())->has_right(\ACL::CANADMIN)) + 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')) @@ -443,7 +531,7 @@ class PushController extends Controller if (!$request->request->get('email')) throw new ControllerException($this->app->trans('Email is required')); - if (!\Swift_Validate::email($request->request->get('email'))) + if (!Swift_Validate::email($request->request->get('email'))) throw new ControllerException($this->app->trans('Email is invalid')); } catch (ControllerException $e) { $result['message'] = $e->getMessage(); @@ -490,13 +578,17 @@ class PushController extends Controller $result['message'] = $this->app->trans('User successfully created'); $result['success'] = true; $result['user'] = $this->formatUser($user); - } catch (\Exception $e) { + } 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')]; @@ -504,17 +596,21 @@ class PushController extends Controller 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->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); + ->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() @@ -541,18 +637,23 @@ class PushController extends Controller 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]); + $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); + ->like_match(User_Query::LIKE_MATCH_OR); } if (is_array($request->get('EmailDomain'))) { @@ -606,38 +707,75 @@ class PushController extends Controller ); } + /** + * 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' => $this->app->trans('Unable to save the expiration date') - ]; + // sanity check if (is_null($request->request->get('date'))) { - $ret['message'] = $this->app->trans('The provided date is null!'); - return $this->app->json($ret); + throw new Exception('The provided date is null!'); } - $repository = $this->app['repo.baskets']; + $manager = $this->getEntityManager(); $manager->beginTransaction(); try { - $basket = $repository->findUserBasket($request->request->get('basket_id'), $this->app->getAuthenticatedUser(), true); - $date = new \DateTime($request->request->get('date') . " 23:59:59"); + $basket = $this->getBasketRepository()->findUserBasket($request->request->get('basket_id'), $this->app->getAuthenticatedUser(), true); + $expirationDate = new DateTime($request->request->get('date') . " 23:59:59"); $validation = $basket->getValidation(); if (is_null($validation)) { - return $this->app->json($ret); + throw new Exception('Unable to find the validation session'); } - $validation->setExpires($date); + + // update validation tokens expiration + // + /** @var ValidationParticipant $participant */ + foreach($validation->getParticipants() as $participant) { + try { + if(!is_null($token = $this->getTokenRepository()->findValidationToken($basket, $participant->getUser()))) { + if($participant->getUser()->getId() === $validation->getInitiator()->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 + } + } + + $validation->setExpires($expirationDate); $manager->persist($validation); $manager->flush(); $manager->commit(); - $ret['message'] = 'Expiration date successfully updated!'; - } catch (\Exception $e) { - $ret['message'] = $e->getMessage(); + $ret = [ + 'success' => true, + 'message' => $this->app->trans('Expiration date successfully updated!') + ]; + } + 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()]); @@ -674,8 +812,8 @@ class PushController extends Controller } /** - * @param array|\record_adapter[] $selection - * @return User[] + * @param array|record_adapter[] $selection + * @return ArrayCollection Users */ private function getUsersInSelectionExtractor($selection) { @@ -773,4 +911,20 @@ class PushController extends Controller 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']; + } + } diff --git a/lib/Alchemy/Phrasea/Controller/Root/LoginController.php b/lib/Alchemy/Phrasea/Controller/Root/LoginController.php index ae23a98a8a..862b5ffd84 100644 --- a/lib/Alchemy/Phrasea/Controller/Root/LoginController.php +++ b/lib/Alchemy/Phrasea/Controller/Root/LoginController.php @@ -55,6 +55,7 @@ use Alchemy\Phrasea\Form\Login\PhraseaAuthenticationForm; use Alchemy\Phrasea\Form\Login\PhraseaForgotPasswordForm; use Alchemy\Phrasea\Form\Login\PhraseaRecoverPasswordForm; use Alchemy\Phrasea\Form\Login\PhraseaRegisterForm; +use DateTime; use Doctrine\ORM\EntityManagerInterface; use Neutron\ReCaptcha\ReCaptcha; use RandomLib\Generator; @@ -153,7 +154,7 @@ class LoginController extends Controller 'great' => $this->app->trans('Great'), ]); - $response->setExpires(new \DateTime('+1 day')); + $response->setExpires(new DateTime('+1 day')); return $response; } @@ -593,25 +594,41 @@ class LoginController extends Controller // move this in an event public function postAuthProcess(Request $request, User $user) { - $date = new \DateTime('+' . (int) $this->getConf()->get(['registry', 'actions', 'validation-reminder-days']) . ' days'); + $date = new DateTime('+' . (int) $this->getConf()->get(['registry', 'actions', 'validation-reminder-days']) . ' days'); $manager = $this->getEntityManager(); + /* + * PHRAS-3214_validation-tokens-refacto : This code is moved to console command "SendValidationRemindersCommand.php" + * foreach ($this->getValidationParticipantRepository()->findNotConfirmedAndNotRemindedParticipantsByExpireDate($date) as $participant) { $validationSession = $participant->getSession(); $basket = $validationSession->getBasket(); - if (null === $token = $this->getTokenRepository()->findValidationToken($basket, $participant->getUser())) { - continue; + // find the token if exists + // nb : a validation may have not generated tokens if forcing auth was required upon creation + try { + $token = $this->getTokenRepository()->findValidationToken($basket, $participant->getUser()); + } + catch (\Exception $e) { + // not unique token ? should not happen + $token = null; + } + + if(!is_null($token)) { + $url = $this->app->url('lightbox_validation', ['basket' => $basket->getId(), 'LOG' => $token->getValue()]); + } + else { + $url = $this->app->url('lightbox_validation', ['basket' => $basket->getId()]); } - $url = $this->app->url('lightbox_validation', ['basket' => $basket->getId(), 'LOG' => $token->getValue()]); $this->dispatch(PhraseaEvents::VALIDATION_REMINDER, new ValidationEvent($participant, $basket, $url)); - $participant->setReminded(new \DateTime('now')); + $participant->setReminded(new DateTime('now')); $manager->persist($participant); } $manager->flush(); + */ $session = $this->getAuthenticator()->openAccount($user); diff --git a/lib/Alchemy/Phrasea/Core/Event/Subscriber/ValidationSubscriber.php b/lib/Alchemy/Phrasea/Core/Event/Subscriber/ValidationSubscriber.php index 232b03b8d0..9f788c115b 100644 --- a/lib/Alchemy/Phrasea/Core/Event/Subscriber/ValidationSubscriber.php +++ b/lib/Alchemy/Phrasea/Core/Event/Subscriber/ValidationSubscriber.php @@ -108,6 +108,9 @@ class ValidationSubscriber extends AbstractNotificationSubscriber return $this->app['events-manager']->notify($params['to'], 'eventsmanager_notify_validationdone', $datas, $mailed); } + /* + * PHRAS-3214_validation-tokens-refacto : This code is moved to console command "SendValidationRemindersCommand.php" + * public function onRemind(ValidationEvent $event) { $params = [ @@ -150,13 +153,15 @@ class ValidationSubscriber extends AbstractNotificationSubscriber return $this->app['events-manager']->notify($params['to'], 'eventsmanager_notify_validationreminder', $datas, $mailed); } + */ public static function getSubscribedEvents() { return [ PhraseaEvents::VALIDATION_CREATE => 'onCreate', PhraseaEvents::VALIDATION_DONE => 'onFinish', - PhraseaEvents::VALIDATION_REMINDER => 'onRemind', + // PHRAS-3214_validation-tokens-refacto : This code is moved to console command "SendValidationRemindersCommand.php" + // PhraseaEvents::VALIDATION_REMINDER => 'onRemind', ]; } } diff --git a/lib/Alchemy/Phrasea/Model/Entities/ValidationParticipant.php b/lib/Alchemy/Phrasea/Model/Entities/ValidationParticipant.php index 9322f00050..f8ab4f56f3 100644 --- a/lib/Alchemy/Phrasea/Model/Entities/ValidationParticipant.php +++ b/lib/Alchemy/Phrasea/Model/Entities/ValidationParticipant.php @@ -11,6 +11,9 @@ namespace Alchemy\Phrasea\Model\Entities; +use DateTime; +use Doctrine\Common\Collections\ArrayCollection; +use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; /** @@ -63,11 +66,11 @@ class ValidationParticipant private $session; /** - * Constructor + * ValidationParticipant constructor. */ public function __construct() { - $this->datas = new \Doctrine\Common\Collections\ArrayCollection(); + $this->datas = new ArrayCollection(); } /** @@ -91,7 +94,7 @@ class ValidationParticipant /** * @param User $user * - * @return AggregateToken + * @return self */ public function setUser(User $user) { @@ -158,7 +161,7 @@ class ValidationParticipant * Set can_agree * * @param boolean $canAgree - * @return ValidationParticipant + * @return self */ public function setCanAgree($canAgree) { @@ -181,7 +184,7 @@ class ValidationParticipant * Set can_see_others * * @param boolean $canSeeOthers - * @return ValidationParticipant + * @return self */ public function setCanSeeOthers($canSeeOthers) { @@ -203,7 +206,7 @@ class ValidationParticipant /** * Set reminded * - * @param \DateTime $reminded + * @param DateTime $reminded * @return ValidationParticipant */ public function setReminded($reminded) @@ -216,7 +219,7 @@ class ValidationParticipant /** * Get reminded * - * @return \DateTime + * @return DateTime */ public function getReminded() { @@ -249,7 +252,7 @@ class ValidationParticipant /** * Get datas * - * @return \Doctrine\Common\Collections\Collection + * @return Collection */ public function getDatas() { diff --git a/lib/Alchemy/Phrasea/Model/Entities/ValidationSession.php b/lib/Alchemy/Phrasea/Model/Entities/ValidationSession.php index dccc80281a..7c44411928 100644 --- a/lib/Alchemy/Phrasea/Model/Entities/ValidationSession.php +++ b/lib/Alchemy/Phrasea/Model/Entities/ValidationSession.php @@ -12,6 +12,9 @@ namespace Alchemy\Phrasea\Model\Entities; use Alchemy\Phrasea\Application; +use DateTime; +use Doctrine\Common\Collections\ArrayCollection; +use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; use Gedmo\Mapping\Annotation as Gedmo; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; @@ -70,7 +73,7 @@ class ValidationSession */ public function __construct() { - $this->participants = new \Doctrine\Common\Collections\ArrayCollection(); + $this->participants = new ArrayCollection(); } /** @@ -118,10 +121,10 @@ class ValidationSession /** * Set created * - * @param \DateTime $created + * @param DateTime $created * @return ValidationSession */ - public function setCreated(\DateTime $created) + public function setCreated(DateTime $created) { $this->created = $created; @@ -131,7 +134,7 @@ class ValidationSession /** * Get created * - * @return \DateTime + * @return DateTime */ public function getCreated() { @@ -141,10 +144,10 @@ class ValidationSession /** * Set updated * - * @param \DateTime $updated + * @param DateTime $updated * @return ValidationSession */ - public function setUpdated(\DateTime $updated) + public function setUpdated(DateTime $updated) { $this->updated = $updated; @@ -154,7 +157,7 @@ class ValidationSession /** * Get updated * - * @return \DateTime + * @return DateTime */ public function getUpdated() { @@ -164,7 +167,7 @@ class ValidationSession /** * Set expires * - * @param \DateTime $expires + * @param DateTime $expires * @return ValidationSession */ public function setExpires($expires) @@ -177,7 +180,7 @@ class ValidationSession /** * Get expires * - * @return \DateTime + * @return DateTime|null */ public function getExpires() { @@ -233,7 +236,7 @@ class ValidationSession /** * Get participants * - * @return \Doctrine\Common\Collections\Collection + * @return Collection */ public function getParticipants() { @@ -246,7 +249,7 @@ class ValidationSession return null; } - $date_obj = new \DateTime(); + $date_obj = new DateTime(); return $date_obj > $this->getExpires(); } @@ -261,16 +264,18 @@ class ValidationSession return $app->trans('Vous avez envoye cette demande a %n% utilisateurs', ['%n%' => count($this->getParticipants()) - 1]); } else { if ($this->getParticipant($user)->getCanSeeOthers()) { - return $app->trans('Processus de validation recu de %user% et concernant %n% utilisateurs', ['%user%' => $this->getInitiator($app)->getDisplayName(), '%n%' => count($this->getParticipants()) - 1]); + return $app->trans('Processus de validation recu de %user% et concernant %n% utilisateurs', ['%user%' => $this->getInitiator()->getDisplayName(), '%n%' => count($this->getParticipants()) - 1]); } - return $app->trans('Processus de validation recu de %user%', ['%user%' => $this->getInitiator($app)->getDisplayName()]); + return $app->trans('Processus de validation recu de %user%', ['%user%' => $this->getInitiator()->getDisplayName()]); } } /** * Get a participant * + * @param User $user + * * @return ValidationParticipant */ public function getParticipant(User $user) diff --git a/lib/Alchemy/Phrasea/Model/Manipulator/TokenManipulator.php b/lib/Alchemy/Phrasea/Model/Manipulator/TokenManipulator.php index 56c4e9fc36..a1d322b871 100644 --- a/lib/Alchemy/Phrasea/Model/Manipulator/TokenManipulator.php +++ b/lib/Alchemy/Phrasea/Model/Manipulator/TokenManipulator.php @@ -16,8 +16,11 @@ use Alchemy\Phrasea\Model\Entities\FeedEntry; use Alchemy\Phrasea\Model\Entities\Token; use Alchemy\Phrasea\Model\Entities\User; use Alchemy\Phrasea\Model\Repositories\TokenRepository; +use DateTime; use Doctrine\Common\Persistence\ObjectManager; +use InvalidArgumentException; use RandomLib\Generator; +use RuntimeException; class TokenManipulator implements ManipulatorInterface { @@ -56,19 +59,19 @@ class TokenManipulator implements ManipulatorInterface /** * @param User|null $user * @param string $type - * @param \DateTime|null $expiration + * @param DateTime|null $expiration * @param mixed|null $data * * @return Token */ - public function create(User $user = null, $type, \DateTime $expiration = null, $data = null) + public function create($user, $type, $expiration = null, $data = null) { $this->removeExpiredTokens(); $n = 0; do { if ($n++ > 1024) { - throw new \RuntimeException('Unable to create a token.'); + throw new RuntimeException('Unable to create a token.'); } $value = $this->random->generateString(32, self::LETTERS_AND_NUMBERS); $found = null !== $this->om->getRepository('Phraseanet:Token')->find($value); @@ -90,17 +93,18 @@ class TokenManipulator implements ManipulatorInterface /** * @param Basket $basket - * @param User $user + * @param User $user + * @param DateTime|null $expiration * * @return Token */ - public function createBasketValidationToken(Basket $basket, User $user = null) + public function createBasketValidationToken(Basket $basket, User $user, $expiration) { if (null === $basket->getValidation()) { - throw new \InvalidArgumentException('A validation token requires a validation basket.'); + throw new InvalidArgumentException('A validation token requires a validation basket.'); } - return $this->create($user ?: $basket->getValidation()->getInitiator(), self::TYPE_VALIDATE, new \DateTime('+10 days'), $basket->getId()); + return $this->create($user, self::TYPE_VALIDATE, $expiration, $basket->getId()); } /** @@ -131,7 +135,6 @@ class TokenManipulator implements ManipulatorInterface * @param User[] $users * @param FeedEntry $entry * @return Token[] - * @throws \Doctrine\DBAL\DBALException */ public function createFeedEntryTokens($users, FeedEntry $entry) { @@ -165,7 +168,7 @@ class TokenManipulator implements ManipulatorInterface */ public function createDownloadToken(User $user, $data) { - return $this->create($user, self::TYPE_DOWNLOAD, new \DateTime('+3 hours'), $data); + return $this->create($user, self::TYPE_DOWNLOAD, new DateTime('+3 hours'), $data); } /** @@ -175,7 +178,7 @@ class TokenManipulator implements ManipulatorInterface */ public function createEmailExportToken($data) { - return $this->create(null, self::TYPE_EMAIL, new \DateTime('+1 day'), $data); + return $this->create(null, self::TYPE_EMAIL, new DateTime('+1 day'), $data); } /** @@ -186,7 +189,7 @@ class TokenManipulator implements ManipulatorInterface */ public function createResetEmailToken(User $user, $email) { - return $this->create($user, self::TYPE_EMAIL_RESET, new \DateTime('+1 day'), $email); + return $this->create($user, self::TYPE_EMAIL_RESET, new DateTime('+1 day'), $email); } /** @@ -196,17 +199,18 @@ class TokenManipulator implements ManipulatorInterface */ public function createAccountUnlockToken(User $user) { - return $this->create($user, self::TYPE_ACCOUNT_UNLOCK, new \DateTime('+3 days')); + return $this->create($user, self::TYPE_ACCOUNT_UNLOCK, new DateTime('+3 days')); } /** * @param User $user + * @param string $email * * @return Token */ public function createAccountDeleteToken(User $user, $email) { - return $this->create($user, self::TYPE_ACCOUNT_DELETE, new \DateTime('+1 hour'), $email); + return $this->create($user, self::TYPE_ACCOUNT_DELETE, new DateTime('+1 hour'), $email); } /** @@ -216,7 +220,7 @@ class TokenManipulator implements ManipulatorInterface */ public function createResetPasswordToken(User $user) { - return $this->create($user, self::TYPE_PASSWORD, new \DateTime('+1 day')); + return $this->create($user, self::TYPE_PASSWORD, new DateTime('+1 day')); } /** diff --git a/lib/Alchemy/Phrasea/Model/Repositories/TokenRepository.php b/lib/Alchemy/Phrasea/Model/Repositories/TokenRepository.php index 8e40f9ebd8..b0fb3bf4f1 100644 --- a/lib/Alchemy/Phrasea/Model/Repositories/TokenRepository.php +++ b/lib/Alchemy/Phrasea/Model/Repositories/TokenRepository.php @@ -6,7 +6,9 @@ use Alchemy\Phrasea\Model\Entities\Basket; use Alchemy\Phrasea\Model\Entities\Token; use Alchemy\Phrasea\Model\Entities\User; use Alchemy\Phrasea\Model\Manipulator\TokenManipulator; +use DateTime; use Doctrine\ORM\EntityRepository; +use Doctrine\ORM\NonUniqueResultException; /** * TokenRepository @@ -20,7 +22,7 @@ class TokenRepository extends EntityRepository * @param Basket $basket * @param User $user * @return Token|null - * @throws \Doctrine\ORM\NonUniqueResultException + * @throws NonUniqueResultException */ public function findValidationToken(Basket $basket, User $user) { @@ -44,7 +46,7 @@ class TokenRepository extends EntityRepository /** * @param string $value * @return Token|null - * @throws \Doctrine\ORM\NonUniqueResultException + * @throws NonUniqueResultException */ public function findValidToken($value) { @@ -68,7 +70,7 @@ class TokenRepository extends EntityRepository WHERE t.expiration < :date'; $query = $this->_em->createQuery($dql); - $query->setParameters([':date' => new \DateTime()]); + $query->setParameters([':date' => new DateTime()]); return $query->getResult(); } diff --git a/lib/Alchemy/Phrasea/Model/Repositories/ValidationParticipantRepository.php b/lib/Alchemy/Phrasea/Model/Repositories/ValidationParticipantRepository.php index 6374df2953..4fd55a75de 100644 --- a/lib/Alchemy/Phrasea/Model/Repositories/ValidationParticipantRepository.php +++ b/lib/Alchemy/Phrasea/Model/Repositories/ValidationParticipantRepository.php @@ -12,6 +12,7 @@ namespace Alchemy\Phrasea\Model\Repositories; use Alchemy\Phrasea\Model\Entities\ValidationParticipant; +use DateTime; use Doctrine\ORM\EntityRepository; use Doctrine\DBAL\Types\Type; @@ -21,10 +22,12 @@ class ValidationParticipantRepository extends EntityRepository /** * Retrieve all not reminded participants where the validation has not expired * - * @param $expireDate The expiration Date + * @param $expireDate DateTime The expiration Date + * @param $today DateTime fake "today" to allow to get past/future events + * (used by SendValidationRemindersCommand.php to debug with --dry) * @return ValidationParticipant[] */ - public function findNotConfirmedAndNotRemindedParticipantsByExpireDate(\DateTime $expireDate) + public function findNotConfirmedAndNotRemindedParticipantsByExpireDate(DateTime $expireDate, DateTime $today=null) { $dql = ' SELECT p, s @@ -33,10 +36,14 @@ class ValidationParticipantRepository extends EntityRepository JOIN s.basket b WHERE p.is_confirmed = 0 AND p.reminded IS NULL - AND s.expires < :date AND s.expires > CURRENT_TIMESTAMP()'; + AND s.expires < :date AND s.expires > ' . ($today===null ? 'CURRENT_TIMESTAMP()' : ':today'); - return $this->_em->createQuery($dql) - ->setParameter('date', $expireDate, Type::DATETIME) - ->getResult(); + $q = $this->_em->createQuery($dql) + ->setParameter('date', $expireDate, Type::DATETIME); + if($today !== null) { + $q->setParameter('today', $today, Type::DATETIME); + } + + return $q->getResult(); } } diff --git a/tests/Alchemy/Tests/Phrasea/Controller/Root/LoginTest.php b/tests/Alchemy/Tests/Phrasea/Controller/Root/LoginTest.php index b17d31de7c..39bb699910 100644 --- a/tests/Alchemy/Tests/Phrasea/Controller/Root/LoginTest.php +++ b/tests/Alchemy/Tests/Phrasea/Controller/Root/LoginTest.php @@ -2076,9 +2076,11 @@ class LoginTest extends \PhraseanetAuthenticatedWebTestCase ->disableOriginalConstructor() ->getMock(); + /* $repo->expects($participants ? $this->once() : $this->never()) ->method('findNotConfirmedAndNotRemindedParticipantsByExpireDate') ->will($this->returnValue([])); + */ $app['repo.validation-participants'] = $repo; } diff --git a/tests/Alchemy/Tests/Phrasea/Model/Manipulator/TokenManipulatorTest.php b/tests/Alchemy/Tests/Phrasea/Model/Manipulator/TokenManipulatorTest.php index a6bbf88b86..cea3c8b4a1 100644 --- a/tests/Alchemy/Tests/Phrasea/Model/Manipulator/TokenManipulatorTest.php +++ b/tests/Alchemy/Tests/Phrasea/Model/Manipulator/TokenManipulatorTest.php @@ -4,6 +4,7 @@ namespace Alchemy\Tests\Phrasea\Model\Manipulator; use Alchemy\Phrasea\Model\Entities\Token; use Alchemy\Phrasea\Model\Manipulator\TokenManipulator; +use DateTime; /** * @group functional @@ -35,37 +36,38 @@ class TokenManipulatorTest extends \PhraseanetTestCase return [ [true, TokenManipulator::TYPE_RSS, null, null], [false, TokenManipulator::TYPE_RSS, null, null], - [false, TokenManipulator::TYPE_RSS, new \DateTime('-1 day'), 'data'], + [false, TokenManipulator::TYPE_RSS, new DateTime('-1 day'), 'data'], ]; } public function testCreateBasketValidationToken() { $manipulator = new TokenManipulator(self::$DI['app']['orm.em'], self::$DI['app']['random.low'], self::$DI['app']['repo.tokens'], self::$DI['app']['tmp.download.path']); - $token = $manipulator->createBasketValidationToken(self::$DI['basket_4'], self::$DI['user_1']); + $expire = new DateTime('+10 days'); + $token = $manipulator->createBasketValidationToken(self::$DI['basket_4'], self::$DI['user_1'], $expire); $this->assertSame(self::$DI['basket_4']->getId(), $token->getData()); $this->assertSame(self::$DI['user_1'], $token->getUser()); $this->assertSame(TokenManipulator::TYPE_VALIDATE, $token->getType()); - $this->assertDateNear('+10 days', $token->getExpiration()); + $this->assertSame($expire, $token->getExpiration()); } - public function testCreateBasketValidationTokenWithoutUser() + public function testCreateBasketValidationTokenWithoutExpiration() { $manipulator = new TokenManipulator(self::$DI['app']['orm.em'], self::$DI['app']['random.low'], self::$DI['app']['repo.tokens'], self::$DI['app']['tmp.download.path']); - $token = $manipulator->createBasketValidationToken(self::$DI['basket_4']); + $token = $manipulator->createBasketValidationToken(self::$DI['basket_4'], self::$DI['user_1'], null); $this->assertSame(self::$DI['basket_4']->getId(), $token->getData()); - $this->assertSame(self::$DI['basket_4']->getValidation()->getInitiator(), $token->getUser()); + $this->assertSame(self::$DI['user_1'], $token->getUser()); $this->assertSame(TokenManipulator::TYPE_VALIDATE, $token->getType()); - $this->assertDateNear('+10 days', $token->getExpiration()); + $this->assertSame(null, $token->getExpiration()); } public function testCreateBasketValidationTokenWithInvalidBasket() { $manipulator = new TokenManipulator(self::$DI['app']['orm.em'], self::$DI['app']['random.low'], self::$DI['app']['repo.tokens'], self::$DI['app']['tmp.download.path']); $this->setExpectedException('InvalidArgumentException', 'A validation token requires a validation basket.'); - $manipulator->createBasketValidationToken(self::$DI['basket_1']); + $manipulator->createBasketValidationToken(self::$DI['basket_1'], self::$DI['user_1'], null); } public function testCreateBasketAccessToken()