diff --git a/bin/console b/bin/console index 2dcc02e275..c49fc89052 100755 --- a/bin/console +++ b/bin/console @@ -57,6 +57,7 @@ use Alchemy\Phrasea\Command\User\UserCreateCommand; use Alchemy\Phrasea\Command\User\UserPasswordCommand; use Alchemy\Phrasea\Command\User\UserListCommand; use Alchemy\Phrasea\Command\UpgradeDBDatas; +use Alchemy\Phrasea\Command\ApplyRightsCommand; require_once __DIR__ . '/../lib/autoload.php'; @@ -93,6 +94,7 @@ $cli->command(new \module_console_aboutLicense('about:license')); $cli->command(new CheckConfig('check:config')); $cli->command(new UpgradeDBDatas('system:upgrade-datas')); +$cli->command(new ApplyRightsCommand('system:apply-rights')); $cli->command(new \module_console_systemMailCheck('system:mail-check')); $cli->command(new \module_console_systemBackupDB('system:backup-db')); @@ -160,9 +162,9 @@ $cli->command(new QueryParseCommand()); $cli->command(new QuerySampleCommand()); $cli->command(new FindConceptsCommand()); -$cli->command($cli['alchemy_worker.commands.run_dispatcher_command']); -$cli->command($cli['alchemy_worker.commands.run_worker_command']); -$cli->command($cli['alchemy_worker.commands.show_configuration']); +//$cli->command($cli['alchemy_worker.commands.run_dispatcher_command']); +//$cli->command($cli['alchemy_worker.commands.run_worker_command']); +//$cli->command($cli['alchemy_worker.commands.show_configuration']); $cli->loadPlugins(); diff --git a/lib/Alchemy/Phrasea/Account/AccountService.php b/lib/Alchemy/Phrasea/Account/AccountService.php index 4c0e594e64..567a404a94 100644 --- a/lib/Alchemy/Phrasea/Account/AccountService.php +++ b/lib/Alchemy/Phrasea/Account/AccountService.php @@ -167,11 +167,11 @@ class AccountService * @param string $login * @throws AccountException */ - public function deleteAccount($login = null) + public function deleteAccount($login = null, array $grantedBaseIdList = array()) { $user = $this->getUserOrCurrentUser($login); - $this->userManipulator->delete($user); + $this->userManipulator->delete($user, $grantedBaseIdList); } /** diff --git a/lib/Alchemy/Phrasea/Authentication/RegistrationService.php b/lib/Alchemy/Phrasea/Authentication/RegistrationService.php index 7ffdf8c489..2ffe87f0cd 100644 --- a/lib/Alchemy/Phrasea/Authentication/RegistrationService.php +++ b/lib/Alchemy/Phrasea/Authentication/RegistrationService.php @@ -328,11 +328,9 @@ class RegistrationService $autoReg = $acl->get_granted_base(); - $granted = []; foreach ($autoReg as $baseId => $collection) { $granted[$baseId] = $collection->get_label($this->app['locale']); - } - if(count($granted) > 0) { + $this->app['manipulator.webhook-event']->create( WebhookEvent::USER_REGISTRATION_GRANTED, WebhookEvent::USER_REGISTRATION_TYPE, @@ -340,8 +338,11 @@ class RegistrationService 'user_id' => $user->getId(), 'granted' => $granted, 'rejected' => [] - ] + ], + [$baseId] ); + + unset($granted); } diff --git a/lib/Alchemy/Phrasea/Border/Manager.php b/lib/Alchemy/Phrasea/Border/Manager.php index d8abc9ac99..d017d206e6 100644 --- a/lib/Alchemy/Phrasea/Border/Manager.php +++ b/lib/Alchemy/Phrasea/Border/Manager.php @@ -14,6 +14,8 @@ namespace Alchemy\Phrasea\Border; use Alchemy\Phrasea\Application; use Alchemy\Phrasea\Border\Checker\CheckerInterface; use Alchemy\Phrasea\Border\Attribute\AttributeInterface; +use Alchemy\Phrasea\Core\Event\Record\RecordEvents; +use Alchemy\Phrasea\Core\Event\Record\SubdefinitionCreateEvent; use Alchemy\Phrasea\Exception\RuntimeException; use Alchemy\Phrasea\Metadata\Tag\TfArchivedate; use Alchemy\Phrasea\Metadata\Tag\TfQuarantine; @@ -333,7 +335,7 @@ class Manager $this->app['phraseanet.metadata-setter']->replaceMetadata($newMetadata, $element); if(!$nosubdef) { - $element->rebuild_subdefs(); + $this->app['dispatcher']->dispatch(RecordEvents::SUBDEFINITION_CREATE, new SubdefinitionCreateEvent($element, true)); } return $element; diff --git a/lib/Alchemy/Phrasea/Command/ApplyRightsCommand.php b/lib/Alchemy/Phrasea/Command/ApplyRightsCommand.php new file mode 100644 index 0000000000..bcf0b3d0e7 --- /dev/null +++ b/lib/Alchemy/Phrasea/Command/ApplyRightsCommand.php @@ -0,0 +1,89 @@ +setDescription('Apply right on databox, inject appbox:basusr to dboxes:collusr') + ->addOption('user_id', null, InputOption::VALUE_REQUIRED, 'the user ID to apply rights') + ; + + return $this; + } + + protected function doExecute(InputInterface $input, OutputInterface $output) + { + $userId = $input->getOption('user_id'); + $userRepository = $this->container['repo.users']; + + if ($userId) { + if (($user = $userRepository->find($userId)) === null) { + $output->writeln('user not found!'); + + return 0; + } + + $this->injectRightsSbas($user); + } else { + foreach ($userRepository->findAll() as $user) { + $this->injectRightsSbas($user); + } + } + + $output->writeln('Apply right on databox finished!'); + + return 0; + } + + private function injectRightsSbas(User $user) + { + $userAcl = $this->container->getAclForUser($user); + + foreach ($userAcl->get_granted_sbas() as $databox) { + + $userAcl->delete_injected_rights_sbas($databox); + + $sql = "INSERT INTO collusr + (site, usr_id, coll_id, mask_and, mask_xor, ord) + VALUES (:site_id, :usr_id, :coll_id, :mask_and, :mask_xor, :ord)"; + $stmt = $databox->get_connection()->prepare($sql); + $iord = 0; + + // fix collusr if user has right on collection + foreach ($userAcl->get_granted_base([], [$databox->get_sbas_id()]) as $collection) { + try { + $stmt->execute([ + ':site_id' => $this->container['conf']->get(['main', 'key']), + ':usr_id' => $user->getId(), + ':coll_id' => $collection->get_coll_id(), + ':mask_and' => $userAcl->get_mask_and($collection->get_base_id()), + ':mask_xor' => $userAcl->get_mask_xor($collection->get_base_id()), + ':ord' => $iord++ + ]); + } catch (DBALException $e) { + + } + } + + $stmt->closeCursor(); + } + } +} diff --git a/lib/Alchemy/Phrasea/Controller/Admin/UserController.php b/lib/Alchemy/Phrasea/Controller/Admin/UserController.php index 35ff646b8c..a1487603d8 100644 --- a/lib/Alchemy/Phrasea/Controller/Admin/UserController.php +++ b/lib/Alchemy/Phrasea/Controller/Admin/UserController.php @@ -515,9 +515,9 @@ class UserController extends Controller $denyColl[] = $label; $hookData['rejected'][$bas] = $label; } - } - $this->app['manipulator.webhook-event']->create($hookName, $hookType, $hookData); + $this->app['manipulator.webhook-event']->create($hookName, $hookType, $hookData, [$bas]); + } if ($user->hasMailNotificationsActivated() && (0 !== count($acceptColl) || 0 !== count($denyColl))) { $message = ''; diff --git a/lib/Alchemy/Phrasea/Controller/Prod/ExportController.php b/lib/Alchemy/Phrasea/Controller/Prod/ExportController.php index df6f2a9f19..f4692c99ac 100644 --- a/lib/Alchemy/Phrasea/Controller/Prod/ExportController.php +++ b/lib/Alchemy/Phrasea/Controller/Prod/ExportController.php @@ -15,6 +15,7 @@ use Alchemy\Phrasea\Application\Helper\FilesystemAware; use Alchemy\Phrasea\Application\Helper\NotifierAware; use Alchemy\Phrasea\Controller\Controller; use Alchemy\Phrasea\Core\Event\ExportFailureEvent; +use Alchemy\Phrasea\Core\Event\ExportMailEvent; use Alchemy\Phrasea\Core\PhraseaEvents; use Alchemy\Phrasea\Exception\InvalidArgumentException; use Alchemy\Phrasea\Model\Manipulator\TokenManipulator; @@ -193,42 +194,26 @@ class ExportController extends Controller $token = $this->getTokenManipulator()->createEmailExportToken(serialize($list)); if (count($destMails) > 0) { - //zip documents - \set_export::build_zip( - $this->app, - $token, - $list, - $this->app['tmp.download.path'].'/'. $token->getValue() . '.zip' - ); + $emitterId = $this->getAuthenticatedUser()->getId(); - $remaingEmails = $destMails; + $tokenValue = $token->getValue(); $url = $this->app->url('prepare_download', ['token' => $token->getValue(), 'anonymous' => false, 'type' => \Session_Logger::EVENT_EXPORTMAIL]); - $user = $this->getAuthenticatedUser(); - $emitter = new Emitter($user->getDisplayName(), $user->getEmail()); + $params = [ + 'url' => $url, + 'textmail' => $request->request->get('textmail'), + 'reading_confirm' => !!$request->request->get('reading_confirm', false), + 'ssttid' => $ssttid = $request->request->get('ssttid', ''), + 'lst' => $lst = $request->request->get('lst', ''), + ]; - foreach ($destMails as $key => $mail) { - try { - $receiver = new Receiver(null, trim($mail)); - } catch (InvalidArgumentException $e) { - continue; - } - - $mail = MailRecordsExport::create($this->app, $receiver, $emitter, $request->request->get('textmail')); - $mail->setButtonUrl($url); - $mail->setExpiration($token->getExpiration()); - - $this->deliver($mail, !!$request->request->get('reading_confirm', false)); - unset($remaingEmails[$key]); - } - - //some mails failed - if (count($remaingEmails) > 0) { - foreach ($remaingEmails as $mail) { - $this->dispatch(PhraseaEvents::EXPORT_MAIL_FAILURE, new ExportFailureEvent($this->getAuthenticatedUser(), $ssttid, $lst, \eventsmanager_notify_downloadmailfail::MAIL_FAIL, $mail)); - } - } + $this->dispatch(PhraseaEvents::EXPORT_MAIL_CREATE, new ExportMailEvent( + $emitterId, + $tokenValue, + $destMails, + $params + )); } return $this->app->json([ diff --git a/lib/Alchemy/Phrasea/Controller/Prod/RecordController.php b/lib/Alchemy/Phrasea/Controller/Prod/RecordController.php index 0a948d6d1d..8e9a609811 100644 --- a/lib/Alchemy/Phrasea/Controller/Prod/RecordController.php +++ b/lib/Alchemy/Phrasea/Controller/Prod/RecordController.php @@ -14,6 +14,8 @@ use Alchemy\Phrasea\Application\Helper\EntityManagerAware; use Alchemy\Phrasea\Application\Helper\SearchEngineAware; use Alchemy\Phrasea\Controller\Controller; use Alchemy\Phrasea\Controller\RecordsRequest; +use Alchemy\Phrasea\Core\Event\Record\DeleteEvent; +use Alchemy\Phrasea\Core\Event\Record\RecordEvents; use Alchemy\Phrasea\Core\Event\RecordEdit; use Alchemy\Phrasea\Core\PhraseaEvents; use Alchemy\Phrasea\Model\Entities\BasketElement; @@ -234,7 +236,7 @@ class RecordController extends Controller if($trashCollectionsBySbasId[$sbasId] !== null) { if($record->getCollection()->get_coll_id() == $trashCollectionsBySbasId[$sbasId]->get_coll_id()) { // record is already in trash so delete it - $record->delete(); + $this->getEventDispatcher()->dispatch(RecordEvents::DELETE, new DeleteEvent($record)); } else { // move to trash collection $record->move_to_collection($trashCollectionsBySbasId[$sbasId], $this->getApplicationBox()); @@ -247,7 +249,7 @@ class RecordController extends Controller } } else { // no trash collection, delete - $record->delete(); + $this->getEventDispatcher()->dispatch(RecordEvents::DELETE, new DeleteEvent($record)); } } catch (\Exception $e) { } diff --git a/lib/Alchemy/Phrasea/Controller/Prod/StoryController.php b/lib/Alchemy/Phrasea/Controller/Prod/StoryController.php index 9a65ed61fa..a90f0a0ed1 100644 --- a/lib/Alchemy/Phrasea/Controller/Prod/StoryController.php +++ b/lib/Alchemy/Phrasea/Controller/Prod/StoryController.php @@ -14,6 +14,8 @@ use Alchemy\Phrasea\Application\Helper\EntityManagerAware; use Alchemy\Phrasea\Controller\Controller; use Alchemy\Phrasea\Controller\RecordsRequest; use Alchemy\Phrasea\Controller\Exception as ControllerException; +use Alchemy\Phrasea\Core\Event\Record\RecordEvents; +use Alchemy\Phrasea\Core\Event\Record\SubdefinitionCreateEvent; use Alchemy\Phrasea\Core\Event\RecordEdit; use Alchemy\Phrasea\Core\PhraseaEvents; use Alchemy\Phrasea\Model\Entities\StoryWZ; @@ -68,7 +70,9 @@ class StoryController extends Controller break; } - $story->set_metadatas($metadatas)->rebuild_subdefs(); + $recordAdapter = $story->set_metadatas($metadatas); + // tell phraseanet to rebuild subdef + $this->dispatch(RecordEvents::SUBDEFINITION_CREATE, new SubdefinitionCreateEvent($recordAdapter)); $storyWZ = new StoryWZ(); $storyWZ->setUser($this->getAuthenticatedUser()); diff --git a/lib/Alchemy/Phrasea/Controller/Prod/ToolsController.php b/lib/Alchemy/Phrasea/Controller/Prod/ToolsController.php index 872f385455..65f72a8a36 100644 --- a/lib/Alchemy/Phrasea/Controller/Prod/ToolsController.php +++ b/lib/Alchemy/Phrasea/Controller/Prod/ToolsController.php @@ -16,6 +16,7 @@ use Alchemy\Phrasea\Application\Helper\SubDefinitionSubstituerAware; use Alchemy\Phrasea\Controller\Controller; use Alchemy\Phrasea\Controller\RecordsRequest; use Alchemy\Phrasea\Core\Event\Record\RecordEvents; +use Alchemy\Phrasea\Core\Event\Record\SubdefinitionCreateEvent; use Alchemy\Phrasea\Exception\RuntimeException; use Alchemy\Phrasea\Metadata\PhraseanetMetadataReader; use Alchemy\Phrasea\Metadata\PhraseanetMetadataSetter; @@ -156,7 +157,7 @@ class ToolsController extends Controller } if (!$substituted || $force) { - $record->rebuild_subdefs(); + $this->dispatch(RecordEvents::SUBDEFINITION_CREATE, new SubdefinitionCreateEvent($record)); } } diff --git a/lib/Alchemy/Phrasea/Controller/Root/AccountController.php b/lib/Alchemy/Phrasea/Controller/Root/AccountController.php index 6bf2d11f17..b56d428cec 100644 --- a/lib/Alchemy/Phrasea/Controller/Root/AccountController.php +++ b/lib/Alchemy/Phrasea/Controller/Root/AccountController.php @@ -518,7 +518,9 @@ class AccountController extends Controller $this->getApiApplicationManipulator()->deleteApiApplications($applications); - // revoke access and delete phraseanet user account + // get list of old granted base_id then revoke access and delete phraseanet user account + + $oldGrantedBaseIds = array_keys($this->app->getAclForUser($user)->get_granted_base()); $list = array_keys($this->app['repo.collections-registry']->getBaseIdMap()); @@ -542,8 +544,9 @@ class AccountController extends Controller $mail = null; } - $this->app['manipulator.user']->delete($user); + $mail = MailSuccessAccountDelete::create($this->app, $receiver); + $this->app['manipulator.user']->delete($user, [$user->getId() => $oldGrantedBaseIds]); if($mail) { $this->deliver($mail); } diff --git a/lib/Alchemy/Phrasea/Core/Event/ExportMailEvent.php b/lib/Alchemy/Phrasea/Core/Event/ExportMailEvent.php new file mode 100644 index 0000000000..3acafb4c70 --- /dev/null +++ b/lib/Alchemy/Phrasea/Core/Event/ExportMailEvent.php @@ -0,0 +1,53 @@ +emitterUserId = $emitterUserId; + $this->tokenValue = $tokenValue; + $this->destinationMails = $destMails; + $this->params = $params; + } + + public function getTokenValue() + { + return $this->tokenValue; + } + + public function getDestinationMails() + { + return $this->destinationMails; + } + + public function getEmitterUserId() + { + return $this->emitterUserId; + } + + public function getParams() + { + return $this->params; + } +} diff --git a/lib/Alchemy/Phrasea/Core/Event/Record/DeleteEvent.php b/lib/Alchemy/Phrasea/Core/Event/Record/DeleteEvent.php new file mode 100644 index 0000000000..8d8bfdb379 --- /dev/null +++ b/lib/Alchemy/Phrasea/Core/Event/Record/DeleteEvent.php @@ -0,0 +1,7 @@ +isNewRecord = $isNewRecord; + } + + /** + * @return bool + */ + public function isNewRecord() + { + return $this->isNewRecord; + } +} diff --git a/lib/Alchemy/Phrasea/Core/Event/Subscriber/ExportSubscriber.php b/lib/Alchemy/Phrasea/Core/Event/Subscriber/ExportSubscriber.php index fca66dab4a..d3c05a64b6 100644 --- a/lib/Alchemy/Phrasea/Core/Event/Subscriber/ExportSubscriber.php +++ b/lib/Alchemy/Phrasea/Core/Event/Subscriber/ExportSubscriber.php @@ -12,7 +12,15 @@ namespace Alchemy\Phrasea\Core\Event\Subscriber; use Alchemy\Phrasea\Core\Event\ExportFailureEvent; +use Alchemy\Phrasea\Core\Event\ExportMailEvent; use Alchemy\Phrasea\Core\PhraseaEvents; +use Alchemy\Phrasea\Exception\InvalidArgumentException; +use Alchemy\Phrasea\Model\Entities\Token; +use Alchemy\Phrasea\Model\Repositories\TokenRepository; +use Alchemy\Phrasea\Model\Repositories\UserRepository; +use Alchemy\Phrasea\Notification\Emitter; +use Alchemy\Phrasea\Notification\Mail\MailRecordsExport; +use Alchemy\Phrasea\Notification\Receiver; class ExportSubscriber extends AbstractNotificationSubscriber { @@ -39,10 +47,65 @@ class ExportSubscriber extends AbstractNotificationSubscriber $this->app['event-manager']->notify($params['usr_id'], 'eventsmanager_notify_downloadmailfail', $datas, $mailed); } + public function onCreateExportMail(ExportMailEvent $event) + { + $destMails = $event->getDestinationMails(); + + $params = $event->getParams(); + + /** @var UserRepository $userRepository */ + $userRepository = $this->app['repo.users']; + + $user = $userRepository->find($event->getEmitterUserId()); + + /** @var TokenRepository $tokenRepository */ + $tokenRepository = $this->app['repo.tokens']; + + /** @var Token $token */ + $token = $tokenRepository->findValidToken($event->getTokenValue()); + + $list = unserialize($token->getData()); + + //zip documents + \set_export::build_zip( + $this->app, + $token, + $list, + $this->app['tmp.download.path'].'/'. $token->getValue() . '.zip' + ); + + $remaingEmails = $destMails; + + $emitter = new Emitter($user->getDisplayName(), $user->getEmail()); + + foreach ($destMails as $key => $mail) { + try { + $receiver = new Receiver(null, trim($mail)); + } catch (InvalidArgumentException $e) { + continue; + } + + $mail = MailRecordsExport::create($this->app, $receiver, $emitter, $params['textmail']); + $mail->setButtonUrl($params['url']); + $mail->setExpiration($token->getExpiration()); + + $this->deliver($mail, $params['reading_confirm']); + unset($remaingEmails[$key]); + } + + //some mails failed + if (count($remaingEmails) > 0) { + foreach ($remaingEmails as $mail) { + $this->app['dispatcher']->dispatch(PhraseaEvents::EXPORT_MAIL_FAILURE, new ExportFailureEvent($user, $params['ssttid'], $params['lst'], \eventsmanager_notify_downloadmailfail::MAIL_FAIL, $mail)); + } + } + } + public static function getSubscribedEvents() { return [ - PhraseaEvents::EXPORT_MAIL_FAILURE => 'onMailExportFailure' + PhraseaEvents::EXPORT_MAIL_FAILURE => 'onMailExportFailure', + PhraseaEvents::EXPORT_MAIL_CREATE => 'onCreateExportMail', ]; } } diff --git a/lib/Alchemy/Phrasea/Core/Event/Subscriber/FeedEntrySubscriber.php b/lib/Alchemy/Phrasea/Core/Event/Subscriber/FeedEntrySubscriber.php index 8bc7cb2b52..e229cc3f44 100644 --- a/lib/Alchemy/Phrasea/Core/Event/Subscriber/FeedEntrySubscriber.php +++ b/lib/Alchemy/Phrasea/Core/Event/Subscriber/FeedEntrySubscriber.php @@ -33,7 +33,8 @@ class FeedEntrySubscriber extends AbstractNotificationSubscriber $this->app['manipulator.webhook-event']->create( WebhookEvent::NEW_FEED_ENTRY, WebhookEvent::FEED_ENTRY_TYPE, - array_merge(array('feed_id' => $entry->getFeed()->getId()), $params) + array_merge(array('feed_id' => $entry->getFeed()->getId()), $params), + $entry->getFeed()->getBaseId() ? [$entry->getFeed()->getBaseId()] : [] ); $datas = json_encode($params); diff --git a/lib/Alchemy/Phrasea/Core/Event/Subscriber/OrderSubscriber.php b/lib/Alchemy/Phrasea/Core/Event/Subscriber/OrderSubscriber.php index 142537f418..01f7e7b235 100644 --- a/lib/Alchemy/Phrasea/Core/Event/Subscriber/OrderSubscriber.php +++ b/lib/Alchemy/Phrasea/Core/Event/Subscriber/OrderSubscriber.php @@ -41,13 +41,13 @@ class OrderSubscriber extends AbstractNotificationSubscriber public function onCreate(OrderEvent $event) { - $base_ids = array_unique(array_map(function (OrderElement $element) { + $baseIds = array_unique(array_map(function (OrderElement $element) { return $element->getBaseId(); }, iterator_to_array($event->getOrder()->getElements()))); $query = $this->app['phraseanet.user-query']; /** @var User[] $users */ - $users = $query->on_base_ids($base_ids) + $users = $query->on_base_ids($baseIds) ->who_have_right([\ACL::ORDER_MASTER]) ->execute()->get_results(); @@ -60,10 +60,12 @@ class OrderSubscriber extends AbstractNotificationSubscriber 'order_id' => $event->getOrder()->getId(), ]); - $notifier = $this->notifierRegistry->getNotifier($event->getOrder()->getNotificationMethod()); + // notify by webhook + $notifier = $this->notifierRegistry->getNotifier(Order::NOTIFY_WEBHOOK); - $notifier->notifyCreation($event->getOrder(), $event->getOrder()->getUser()); + $notifier->notifyCreation($event->getOrder(), $event->getOrder()->getUser(), $baseIds); + // notify by mail $notifier = $this->notifierRegistry->getNotifier(Order::NOTIFY_MAIL); foreach ($users as $user) { @@ -85,7 +87,13 @@ class OrderSubscriber extends AbstractNotificationSubscriber public function onDeliver(OrderDeliveryEvent $event) { + // notify by webhook + $notifier = $this->notifierRegistry->getNotifier(Order::NOTIFY_WEBHOOK); + $notifier->notifyDelivery($event->getDelivery(), $event->getDelivery()->getPartialOrder()->getBaseIds()); + $notified = false; + + // actually NotificationMethod is always by mail $notifier = $this->notifierRegistry->getNotifier($event->getOrder()->getNotificationMethod()); $notificationData = json_encode([ 'from' => $event->getDelivery()->getAdmin()->getId(), @@ -109,7 +117,13 @@ class OrderSubscriber extends AbstractNotificationSubscriber public function onDeny(OrderDeliveryEvent $event) { + // notify by webhook + $notifier = $this->notifierRegistry->getNotifier(Order::NOTIFY_WEBHOOK); + $notifier->notifyDenial($event->getDelivery(), $event->getDelivery()->getPartialOrder()->getBaseIds()); + $notified = false; + + // actually NotificationMethod is always by mail $notifier = $this->notifierRegistry->getNotifier($event->getOrder()->getNotificationMethod()); $notificationData = json_encode([ 'from' => $event->getDelivery()->getAdmin()->getId(), diff --git a/lib/Alchemy/Phrasea/Core/Event/Subscriber/RecordEditSubscriber.php b/lib/Alchemy/Phrasea/Core/Event/Subscriber/RecordEditSubscriber.php index d5c0c69f54..8307749eb5 100644 --- a/lib/Alchemy/Phrasea/Core/Event/Subscriber/RecordEditSubscriber.php +++ b/lib/Alchemy/Phrasea/Core/Event/Subscriber/RecordEditSubscriber.php @@ -11,8 +11,10 @@ namespace Alchemy\Phrasea\Core\Event\Subscriber; use Alchemy\Phrasea\Core\Event\Record\CollectionChangedEvent; +use Alchemy\Phrasea\Core\Event\Record\DeleteEvent; use Alchemy\Phrasea\Core\Event\Record\RecordEvent; use Alchemy\Phrasea\Core\Event\Record\RecordEvents; +use Alchemy\Phrasea\Core\Event\Record\SubdefinitionCreateEvent; use Alchemy\Phrasea\Core\Event\RecordEdit; use Alchemy\Phrasea\Core\PhraseaEvents; use Alchemy\Phrasea\Metadata\Tag\TfEditdate; @@ -26,10 +28,12 @@ class RecordEditSubscriber implements EventSubscriberInterface public static function getSubscribedEvents() { return array( - PhraseaEvents::RECORD_EDIT => 'onEdit', - PhraseaEvents::RECORD_UPLOAD => 'onEdit', - RecordEvents::ROTATE => 'onRecordChange', - RecordEvents::COLLECTION_CHANGED => 'onCollectionChanged', + PhraseaEvents::RECORD_EDIT => 'onEdit', + PhraseaEvents::RECORD_UPLOAD => 'onEdit', + RecordEvents::ROTATE => 'onRecordChange', + RecordEvents::COLLECTION_CHANGED => 'onCollectionChanged', + RecordEvents::SUBDEFINITION_CREATE => 'onSubdefinitionCreate', + RecordEvents::DELETE => 'onDelete', ); } @@ -49,6 +53,18 @@ class RecordEditSubscriber implements EventSubscriberInterface $recordAdapter->clearStampCache(); } + public function onSubdefinitionCreate(SubdefinitionCreateEvent $event) + { + $recordAdapter = $this->convertToRecordAdapter($event->getRecord()); + $recordAdapter->rebuild_subdefs(); + } + + public function onDelete(DeleteEvent $event) + { + $recordAdapter = $this->convertToRecordAdapter($event->getRecord()); + $recordAdapter->delete(); + } + public function onEdit(RecordEdit $event) { static $into = false; diff --git a/lib/Alchemy/Phrasea/Core/Event/Subscriber/WebhookSubdefEventSubscriber.php b/lib/Alchemy/Phrasea/Core/Event/Subscriber/WebhookSubdefEventSubscriber.php new file mode 100644 index 0000000000..ca5d88b2ab --- /dev/null +++ b/lib/Alchemy/Phrasea/Core/Event/Subscriber/WebhookSubdefEventSubscriber.php @@ -0,0 +1,75 @@ +app = $app; + } + + public function onSubdefCreated(SubDefinitionCreatedEvent $event) + { + $eventData = [ + 'databox_id' => $event->getRecord()->getDataboxId(), + 'record_id' => $event->getRecord()->getRecordId(), + 'subdef' => $event->getSubDefinitionName() + ]; + + $this->app['manipulator.webhook-event']->create( + WebhookEvent::RECORD_SUBDEF_CREATED, + WebhookEvent::RECORD_SUBDEF_TYPE, + $eventData + ); + } + + public function onSubdefCreationFailed(SubDefinitionCreationFailedEvent $event) + { + $eventData = [ + 'databox_id' => $event->getRecord()->getDataboxId(), + 'record_id' => $event->getRecord()->getRecordId(), + 'subdef' => $event->getSubDefinitionName() + ]; + + $this->app['manipulator.webhook-event']->create( + WebhookEvent::RECORD_SUBDEF_FAILED, + WebhookEvent::RECORD_SUBDEF_TYPE, + $eventData + ); + } + + public function onSubdefsCreated(SubDefinitionsCreatedEvent $event) + { + $eventData = [ + 'databox_id' => $event->getRecord()->getDataboxId(), + 'record_id' => $event->getRecord()->getRecordId(), + 'subdef_count' => count($event->getMedia()) + ]; + + $this->app['manipulator.webhook-event']->create( + WebhookEvent::RECORD_SUBDEFS_CREATED, + WebhookEvent::RECORD_SUBDEF_TYPE, + $eventData + ); + } + + public static function getSubscribedEvents() + { + return [ + RecordEvents::SUB_DEFINITION_CREATED => 'onSubdefCreated', + RecordEvents::SUB_DEFINITIONS_CREATED => 'onSubdefsCreated', + RecordEvents::SUB_DEFINITION_CREATION_FAILED => 'onSubdefCreationFailed' + ]; + } +} diff --git a/lib/Alchemy/Phrasea/Core/Event/Subscriber/WebhookUserEventSubscriber.php b/lib/Alchemy/Phrasea/Core/Event/Subscriber/WebhookUserEventSubscriber.php index a2f935bf89..74c1c6c23a 100644 --- a/lib/Alchemy/Phrasea/Core/Event/Subscriber/WebhookUserEventSubscriber.php +++ b/lib/Alchemy/Phrasea/Core/Event/Subscriber/WebhookUserEventSubscriber.php @@ -45,7 +45,7 @@ class WebhookUserEventSubscriber implements EventSubscriberInterface 'user_id' => $event->getUserId(), 'email' => $event->getEmailAddress(), 'login' => $event->getLogin() - ]); + ], $event->getGrantedBaseIds()); } public static function getSubscribedEvents() diff --git a/lib/Alchemy/Phrasea/Core/Event/User/DeletedEvent.php b/lib/Alchemy/Phrasea/Core/Event/User/DeletedEvent.php index b67ba45b24..1528917245 100644 --- a/lib/Alchemy/Phrasea/Core/Event/User/DeletedEvent.php +++ b/lib/Alchemy/Phrasea/Core/Event/User/DeletedEvent.php @@ -36,4 +36,12 @@ class DeletedEvent extends UserEvent { return $this->args['email']; } + + /** + * @return array + */ + public function getGrantedBaseIds() + { + return $this->args['grantedBaseIds']; + } } diff --git a/lib/Alchemy/Phrasea/Core/PhraseaEvents.php b/lib/Alchemy/Phrasea/Core/PhraseaEvents.php index 97c9c10ba5..90adac69dd 100644 --- a/lib/Alchemy/Phrasea/Core/PhraseaEvents.php +++ b/lib/Alchemy/Phrasea/Core/PhraseaEvents.php @@ -48,7 +48,8 @@ final class PhraseaEvents const BRIDGE_UPLOAD_FAILURE = 'bridge.upload-failure'; const EXPORT_MAIL_FAILURE = 'export.mail-failure'; - const EXPORT_CREATE = 'export.create'; + const EXPORT_CREATE = 'export.create'; + const EXPORT_MAIL_CREATE = 'export.mail-create'; const RECORD_EDIT = 'record.edit'; const RECORD_UPLOAD = 'record.upload'; diff --git a/lib/Alchemy/Phrasea/Core/Provider/WebhookServiceProvider.php b/lib/Alchemy/Phrasea/Core/Provider/WebhookServiceProvider.php index 4b280c92f0..43a5aeafa4 100644 --- a/lib/Alchemy/Phrasea/Core/Provider/WebhookServiceProvider.php +++ b/lib/Alchemy/Phrasea/Core/Provider/WebhookServiceProvider.php @@ -2,6 +2,7 @@ namespace Alchemy\Phrasea\Core\Provider; +use Alchemy\Phrasea\Core\Event\Subscriber\WebhookSubdefEventSubscriber; use Alchemy\Phrasea\Webhook\EventProcessorFactory; use Alchemy\Phrasea\Webhook\EventProcessorWorker; use Alchemy\Phrasea\Webhook\WebhookInvoker; @@ -10,6 +11,7 @@ use Alchemy\Worker\CallableWorkerFactory; use Alchemy\Worker\TypeBasedWorkerResolver; use Silex\Application; use Silex\ServiceProviderInterface; +use Symfony\Component\EventDispatcher\EventDispatcher; class WebhookServiceProvider implements ServiceProviderInterface { @@ -58,6 +60,14 @@ class WebhookServiceProvider implements ServiceProviderInterface return $resolver; } ); + + $app['dispatcher'] = $app->share( + $app->extend('dispatcher', function (EventDispatcher $dispatcher, Application $app) { + $dispatcher->addSubscriber(new WebhookSubdefEventSubscriber($app)); + + return $dispatcher; + }) + ); } private function createAlias(Application $app, $alias, $targetServiceKey) @@ -69,6 +79,6 @@ class WebhookServiceProvider implements ServiceProviderInterface public function boot(Application $app) { - // no-op + } } diff --git a/lib/Alchemy/Phrasea/Core/Provider/WorkerConfigurationServiceProvider.php b/lib/Alchemy/Phrasea/Core/Provider/WorkerConfigurationServiceProvider.php index 1b2e15d4f0..9931d4649f 100644 --- a/lib/Alchemy/Phrasea/Core/Provider/WorkerConfigurationServiceProvider.php +++ b/lib/Alchemy/Phrasea/Core/Provider/WorkerConfigurationServiceProvider.php @@ -32,11 +32,12 @@ class WorkerConfigurationServiceProvider implements ServiceProviderInterface $app['alchemy_queues.queues'] = $app->share(function (Application $app) { $defaultConfiguration = [ 'worker-queue' => [ - 'registry' => 'alchemy_worker.queue_registry', - 'host' => 'localhost', - 'port' => 5672, - 'user' => 'guest', - 'vhost' => '/' + 'registry' => 'alchemy_worker.queue_registry', + 'host' => 'localhost', + 'port' => 5672, + 'user' => 'guest', + 'password' => 'guest', + 'vhost' => '/' ] ]; @@ -46,19 +47,22 @@ class WorkerConfigurationServiceProvider implements ServiceProviderInterface $queueConfigurations = $configuration->get(['workers', 'queue'], $defaultConfiguration); - $queueConfiguration = reset($queueConfigurations); - $queueKey = key($queueConfigurations); + $config = []; - if (! isset($queueConfiguration['name'])) { - if (! is_string($queueKey)) { - throw new \RuntimeException('Invalid queue configuration: configuration has no key or name.'); + foreach($queueConfigurations as $name => $queueConfiguration) { + $queueKey = $name; + + if (! isset($queueConfiguration['name'])) { + if (! is_string($queueKey)) { + throw new \RuntimeException('Invalid queue configuration: configuration has no key or name.'); + } + + $queueConfiguration['name'] = $queueKey; } - $queueConfiguration['name'] = $queueKey; + $config[$queueConfiguration['name']] = $queueConfiguration ; } - $config = [ $queueConfiguration['name'] => $queueConfiguration ]; - return $config; } catch (RuntimeException $exception) { diff --git a/lib/Alchemy/Phrasea/Helper/User/Edit.php b/lib/Alchemy/Phrasea/Helper/User/Edit.php index 2c7513fb3f..970b2ec8e1 100644 --- a/lib/Alchemy/Phrasea/Helper/User/Edit.php +++ b/lib/Alchemy/Phrasea/Helper/User/Edit.php @@ -73,10 +73,12 @@ class Edit extends \Alchemy\Phrasea\Helper\Helper { $list = array_keys($this->app->getAclForUser($this->app->getAuthenticatedUser())->get_granted_base([\ACL::CANADMIN])); + $oldGrantedBaseIds = array_keys($this->app->getAclForUser($user)->get_granted_base()); + $this->app->getAclForUser($user)->revoke_access_from_bases($list); if ($this->app->getAclForUser($user)->is_phantom()) { - $this->app['manipulator.user']->delete($user); + $this->app['manipulator.user']->delete($user, [$user->getId() => $oldGrantedBaseIds]); } return $this; @@ -583,8 +585,8 @@ class Edit extends \Alchemy\Phrasea\Helper\Helper $user = $this->app['repo.users']->find($usr_id); $this->app->getAclForUser($user)->revoke_access_from_bases($delete) - ->give_access_to_base($create) - ->give_access_to_sbas($create_sbas); + ->give_access_to_sbas($create_sbas) // give access to sbas before bas + ->give_access_to_base($create); foreach ($update as $base_id => $rights) { $this->app->getAclForUser($user) diff --git a/lib/Alchemy/Phrasea/Media/SubdefGenerator.php b/lib/Alchemy/Phrasea/Media/SubdefGenerator.php index 88867551a1..9612e3bc19 100644 --- a/lib/Alchemy/Phrasea/Media/SubdefGenerator.php +++ b/lib/Alchemy/Phrasea/Media/SubdefGenerator.php @@ -18,6 +18,7 @@ use Alchemy\Phrasea\Core\Event\Record\SubDefinitionCreationEvent; use Alchemy\Phrasea\Core\Event\Record\SubDefinitionsCreationEvent; use Alchemy\Phrasea\Core\Event\Record\SubDefinitionCreationFailedEvent; use Alchemy\Phrasea\Core\Event\Record\RecordEvents; +use Alchemy\Phrasea\Databox\Subdef\MediaSubdefRepository; use Alchemy\Phrasea\Filesystem\FilesystemService; use Alchemy\Phrasea\Media\Subdef\Specification\PdfSpecification; use MediaAlchemyst\Alchemyst; @@ -170,13 +171,53 @@ class SubdefGenerator unset($this->tmpFilePath); } - $this->dispatch( - RecordEvents::SUB_DEFINITIONS_CREATED, - new SubDefinitionsCreatedEvent( - $record, - $mediaCreated - ) - ); + // if we created subdef one by one + if (count($wanted_subdefs) == 1) { + $mediaSubdefRepository = $this->getMediaSubdefRepository($record->getDataboxId()); + $mediaSubdefs = $mediaSubdefRepository->findByRecordIdsAndNames([$record->getRecordId()]); + $medias = []; + foreach ($mediaSubdefs as $subdef) { + try { + $medias[$subdef->get_name()] = $this->mediavorus->guess($subdef->getRealPath()); + } catch (MediaVorusFileNotFoundException $e) { + + } + } + + $this->dispatch( + RecordEvents::SUB_DEFINITIONS_CREATED, + new SubDefinitionsCreatedEvent( + $record, + $medias + ) + ); + } else { + $this->dispatch( + RecordEvents::SUB_DEFINITIONS_CREATED, + new SubDefinitionsCreatedEvent( + $record, + $mediaCreated + ) + ); + } + } + + /** + * set a logger to use + * @param LoggerInterface $logger + */ + public function setLogger(LoggerInterface $logger) + { + $this->logger = $logger; + } + + /** + * to get the logger + * @return LoggerInterface + */ + public function getLogger() + { + return $this->logger; } private function generateSubdef(\record_adapter $record, \databox_subdef $subdef_class, $pathdest) @@ -276,4 +317,14 @@ class SubdefGenerator $i = floor(log($bytes, 1024)); return round($bytes / pow(1024, $i), [0,0,2,2,3][$i]).['B','kB','MB','GB'][$i]; } + + /** + * @param $databoxId + * + * @return MediaSubdefRepository|Object + */ + private function getMediaSubdefRepository($databoxId) + { + return $this->app['provider.repo.media_subdef']->getRepositoryForDatabox($databoxId); + } } diff --git a/lib/Alchemy/Phrasea/Media/SubdefSubstituer.php b/lib/Alchemy/Phrasea/Media/SubdefSubstituer.php index 2259fa572d..8f7aa61e90 100644 --- a/lib/Alchemy/Phrasea/Media/SubdefSubstituer.php +++ b/lib/Alchemy/Phrasea/Media/SubdefSubstituer.php @@ -13,6 +13,7 @@ namespace Alchemy\Phrasea\Media; use Alchemy\Phrasea\Application; use Alchemy\Phrasea\Core\Event\Record\MediaSubstitutedEvent; use Alchemy\Phrasea\Core\Event\Record\RecordEvents; +use Alchemy\Phrasea\Core\Event\Record\SubdefinitionCreateEvent; use Alchemy\Phrasea\Filesystem\FilesystemService; use MediaAlchemyst\Alchemyst; use MediaAlchemyst\Exception\ExceptionInterface as MediaAlchemystException; @@ -79,7 +80,7 @@ class SubdefSubstituer $record->write_metas(); if ($shouldSubdefsBeRebuilt) { - $record->rebuild_subdefs(); + $this->dispatcher->dispatch(RecordEvents::SUBDEFINITION_CREATE, new SubdefinitionCreateEvent($record)); } $this->dispatcher->dispatch(RecordEvents::MEDIA_SUBSTITUTED, new MediaSubstitutedEvent($record)); diff --git a/lib/Alchemy/Phrasea/Model/Entities/WebhookEvent.php b/lib/Alchemy/Phrasea/Model/Entities/WebhookEvent.php index 275650f1b3..a1091f531f 100644 --- a/lib/Alchemy/Phrasea/Model/Entities/WebhookEvent.php +++ b/lib/Alchemy/Phrasea/Model/Entities/WebhookEvent.php @@ -68,6 +68,14 @@ class WebhookEvent */ private $created; + /** + * List of collection base_id concerned + * @var array + * + * @ORM\Column(name="collection_base_ids", type="json_array", nullable=true) + */ + private $collectionBaseIds; + /** * @param \DateTime $created * @@ -175,4 +183,24 @@ class WebhookEvent return $this; } + + /** + * @param array $collectionBaseIds + * + * @return $this + */ + public function setCollectionBaseIds(array $collectionBaseIds) + { + $this->collectionBaseIds = $collectionBaseIds; + + return $this; + } + + /** + * @return array + */ + public function getCollectionBaseIds() + { + return $this->collectionBaseIds; + } } diff --git a/lib/Alchemy/Phrasea/Model/Manipulator/UserManipulator.php b/lib/Alchemy/Phrasea/Model/Manipulator/UserManipulator.php index 3d0815370e..d36fbf527a 100644 --- a/lib/Alchemy/Phrasea/Model/Manipulator/UserManipulator.php +++ b/lib/Alchemy/Phrasea/Model/Manipulator/UserManipulator.php @@ -126,8 +126,9 @@ class UserManipulator implements ManipulatorInterface * Deletes a user. * * @param User|User[] $users + * @param array $grantedBaseIdList List of the old granted base_id per userId [user_id => [base_id, ...] ] */ - public function delete($users) + public function delete($users, array $grantedBaseIdList = array()) { /** @var User $user */ foreach ($this->makeTraversable($users) as $user) { @@ -146,9 +147,10 @@ class UserManipulator implements ManipulatorInterface new DeletedEvent( null, array( - 'user_id'=>$old_id, - 'login'=>$old_login, - 'email'=>$old_email + 'user_id' => $old_id, + 'login' => $old_login, + 'email' => $old_email, + 'grantedBaseIds' => isset($grantedBaseIdList[$old_id]) ? $grantedBaseIdList[$old_id] : [] ) ) ); diff --git a/lib/Alchemy/Phrasea/Model/Manipulator/WebhookEventManipulator.php b/lib/Alchemy/Phrasea/Model/Manipulator/WebhookEventManipulator.php index 40e7f812a7..90cbf66b65 100644 --- a/lib/Alchemy/Phrasea/Model/Manipulator/WebhookEventManipulator.php +++ b/lib/Alchemy/Phrasea/Model/Manipulator/WebhookEventManipulator.php @@ -12,7 +12,7 @@ namespace Alchemy\Phrasea\Model\Manipulator; use Alchemy\Phrasea\Model\Entities\WebhookEvent; -use Alchemy\Phrasea\Webhook\WebhookPublisher; +use Alchemy\Phrasea\Webhook\WebhookPublisherInterface; use Doctrine\Common\Persistence\ObjectManager; use Doctrine\ORM\EntityRepository; @@ -29,18 +29,18 @@ class WebhookEventManipulator implements ManipulatorInterface private $repository; /** - * @var WebhookPublisher + * @var WebhookPublisherInterface */ private $publisher; - public function __construct(ObjectManager $om, EntityRepository $repo, WebhookPublisher $publisher) + public function __construct(ObjectManager $om, EntityRepository $repo, WebhookPublisherInterface $publisher) { $this->om = $om; $this->repository = $repo; $this->publisher = $publisher; } - public function create($eventName, $type, array $data) + public function create($eventName, $type, array $data, array $collectionBaseIds = array()) { $event = new WebhookEvent(); @@ -48,6 +48,10 @@ class WebhookEventManipulator implements ManipulatorInterface $event->setType($type); $event->setData($data); + if (count($collectionBaseIds) > 0) { + $event->setCollectionBaseIds($collectionBaseIds); + } + $this->update($event); $this->publisher->publishWebhookEvent($event); diff --git a/lib/Alchemy/Phrasea/Order/Controller/BaseOrderController.php b/lib/Alchemy/Phrasea/Order/Controller/BaseOrderController.php index 5b2e90d4cb..cf3c84e981 100644 --- a/lib/Alchemy/Phrasea/Order/Controller/BaseOrderController.php +++ b/lib/Alchemy/Phrasea/Order/Controller/BaseOrderController.php @@ -172,7 +172,7 @@ class BaseOrderController extends Controller $manager->persist($element); } - $delivery = new OrderDelivery($order, $acceptor, count($basketElements)); + $delivery = new OrderDelivery($order, $acceptor, count($basketElements), $partialOrder); $this->dispatch(PhraseaEvents::ORDER_DELIVER, new OrderDeliveryEvent($delivery)); } @@ -198,11 +198,13 @@ class BaseOrderController extends Controller $elements = $this->findRequestedElements($order_id, $elementIds, $acceptor); $order = $this->findOr404($order_id); + $partialOrder = new PartialOrder($order, $elements); + $this->getOrderValidator()->deny($acceptor, new PartialOrder($order, $elements)); try { if (!empty($elements)) { - $delivery = new OrderDelivery($order, $acceptor, count($elements)); + $delivery = new OrderDelivery($order, $acceptor, count($elements), $partialOrder); $this->dispatch(PhraseaEvents::ORDER_DENY, new OrderDeliveryEvent($delivery)); } diff --git a/lib/Alchemy/Phrasea/Order/OrderDelivery.php b/lib/Alchemy/Phrasea/Order/OrderDelivery.php index 5d8f3313ff..f36a98525f 100644 --- a/lib/Alchemy/Phrasea/Order/OrderDelivery.php +++ b/lib/Alchemy/Phrasea/Order/OrderDelivery.php @@ -31,16 +31,23 @@ class OrderDelivery */ private $quantity; + /** + * @var PartialOrder + */ + private $partialOrder; + /** * @param Order $deliveredOrder * @param User $manager * @param int $quantity + * @param PartialOrder $partialOrder */ - public function __construct(Order $deliveredOrder, User $manager, $quantity) + public function __construct(Order $deliveredOrder, User $manager, $quantity, PartialOrder $partialOrder) { - $this->order = $deliveredOrder; - $this->admin = $manager; - $this->quantity = $quantity; + $this->order = $deliveredOrder; + $this->admin = $manager; + $this->quantity = $quantity; + $this->partialOrder = $partialOrder; } /** @@ -66,4 +73,12 @@ class OrderDelivery { return $this->quantity; } + + /** + * @return PartialOrder + */ + public function getPartialOrder() + { + return $this->partialOrder; + } } diff --git a/lib/Alchemy/Phrasea/Order/ValidationNotifier.php b/lib/Alchemy/Phrasea/Order/ValidationNotifier.php index e79b9ef888..419f180520 100644 --- a/lib/Alchemy/Phrasea/Order/ValidationNotifier.php +++ b/lib/Alchemy/Phrasea/Order/ValidationNotifier.php @@ -19,20 +19,23 @@ interface ValidationNotifier /** * @param Order $order * @param User $recipient + * @param array $baseIds * @return void */ - public function notifyCreation(Order $order, User $recipient); + public function notifyCreation(Order $order, User $recipient, array $baseIds = array()); /** * @param OrderDelivery $delivery + * @param array $baseIds * @return void */ - public function notifyDelivery(OrderDelivery $delivery); + public function notifyDelivery(OrderDelivery $delivery, array $baseIds = array()); /** * @param OrderDelivery $delivery + * @param array $baseIds * @return void */ - public function notifyDenial(OrderDelivery $delivery); + public function notifyDenial(OrderDelivery $delivery, array $baseIds = array()); } diff --git a/lib/Alchemy/Phrasea/Order/ValidationNotifier/CompositeNotifier.php b/lib/Alchemy/Phrasea/Order/ValidationNotifier/CompositeNotifier.php index a3e75cebcb..b38d51a3ca 100644 --- a/lib/Alchemy/Phrasea/Order/ValidationNotifier/CompositeNotifier.php +++ b/lib/Alchemy/Phrasea/Order/ValidationNotifier/CompositeNotifier.php @@ -26,8 +26,9 @@ class CompositeNotifier implements ValidationNotifier /** * @param Order $order * @param User $recipient + * @param array $baseIds */ - public function notifyCreation(Order $order, User $recipient) + public function notifyCreation(Order $order, User $recipient, array $baseIds = array()) { foreach ($this->notifiers as $notifier) { $notifier->notifyCreation($order, $recipient); @@ -36,21 +37,23 @@ class CompositeNotifier implements ValidationNotifier /** * @param OrderDelivery $delivery + * @param array $baseIds */ - public function notifyDelivery(OrderDelivery $delivery) + public function notifyDelivery(OrderDelivery $delivery, array $baseIds = array()) { foreach ($this->notifiers as $notifier) { - $notifier->notifyDelivery($delivery); + $notifier->notifyDelivery($delivery, $baseIds); } } /** * @param OrderDelivery $delivery + * @param array $baseIds */ - public function notifyDenial(OrderDelivery $delivery) + public function notifyDenial(OrderDelivery $delivery, array $baseIds = array()) { foreach ($this->notifiers as $notifier) { - $notifier->notifyDenial($delivery); + $notifier->notifyDenial($delivery, $baseIds); } } } diff --git a/lib/Alchemy/Phrasea/Order/ValidationNotifier/MailNotifier.php b/lib/Alchemy/Phrasea/Order/ValidationNotifier/MailNotifier.php index 8a6541793a..9934fafb08 100644 --- a/lib/Alchemy/Phrasea/Order/ValidationNotifier/MailNotifier.php +++ b/lib/Alchemy/Phrasea/Order/ValidationNotifier/MailNotifier.php @@ -46,8 +46,9 @@ class MailNotifier implements ValidationNotifier /** * @param Order $order * @param User $recipient + * @param array $baseIds */ - public function notifyCreation(Order $order, User $recipient) + public function notifyCreation(Order $order, User $recipient, array $baseIds = array()) { $mail = MailInfoNewOrder::create($this->application, Receiver::fromUser($recipient)); @@ -58,8 +59,9 @@ class MailNotifier implements ValidationNotifier /** * @param OrderDelivery $delivery + * @param array $baseIds */ - public function notifyDelivery(OrderDelivery $delivery) + public function notifyDelivery(OrderDelivery $delivery, array $baseIds = array()) { $order = $delivery->getOrder(); @@ -85,8 +87,9 @@ class MailNotifier implements ValidationNotifier /** * @param OrderDelivery $delivery + * @param array $baseIds */ - public function notifyDenial(OrderDelivery $delivery) + public function notifyDenial(OrderDelivery $delivery, array $baseIds = array()) { $sender = Emitter::fromUser($delivery->getAdmin()); $recipient = Receiver::fromUser($delivery->getOrder()->getUser()); diff --git a/lib/Alchemy/Phrasea/Order/ValidationNotifier/WebhookNotifier.php b/lib/Alchemy/Phrasea/Order/ValidationNotifier/WebhookNotifier.php index 3634e8e907..4762ff43ec 100644 --- a/lib/Alchemy/Phrasea/Order/ValidationNotifier/WebhookNotifier.php +++ b/lib/Alchemy/Phrasea/Order/ValidationNotifier/WebhookNotifier.php @@ -47,21 +47,23 @@ class WebhookNotifier implements ValidationNotifier /** * @param Order $order * @param User $recipient + * @param array $baseIds */ - public function notifyCreation(Order $order, User $recipient) + public function notifyCreation(Order $order, User $recipient, array $baseIds = array()) { $eventData = [ 'order_id' => $order->getId(), 'user_id' => $recipient->getId(), ]; - $this->getManipulator()->create(WebhookEvent::ORDER_CREATED, WebhookEvent::ORDER_TYPE, $eventData); + $this->getManipulator()->create(WebhookEvent::ORDER_CREATED, WebhookEvent::ORDER_TYPE, $eventData, $baseIds); } /** * @param OrderDelivery $delivery + * @param array $baseIds */ - public function notifyDelivery(OrderDelivery $delivery) + public function notifyDelivery(OrderDelivery $delivery, array $baseIds = array()) { $eventData = [ 'order_id' => $delivery->getOrder()->getId(), @@ -69,13 +71,14 @@ class WebhookNotifier implements ValidationNotifier 'quantity' => $delivery->getQuantity() ]; - $this->getManipulator()->create(WebhookEvent::ORDER_DELIVERED, WebhookEvent::ORDER_TYPE, $eventData); + $this->getManipulator()->create(WebhookEvent::ORDER_DELIVERED, WebhookEvent::ORDER_TYPE, $eventData, $baseIds); } /** * @param OrderDelivery $delivery + * @param array $baseIds */ - public function notifyDenial(OrderDelivery $delivery) + public function notifyDenial(OrderDelivery $delivery, array $baseIds = array()) { $eventData = [ 'order_id' => $delivery->getOrder()->getId(), @@ -83,6 +86,6 @@ class WebhookNotifier implements ValidationNotifier 'quantity' => $delivery->getQuantity() ]; - $this->getManipulator()->create(WebhookEvent::ORDER_DENIED, WebhookEvent::ORDER_TYPE, $eventData); + $this->getManipulator()->create(WebhookEvent::ORDER_DENIED, WebhookEvent::ORDER_TYPE, $eventData, $baseIds); } } diff --git a/lib/Alchemy/Phrasea/TaskManager/Job/ArchiveJob.php b/lib/Alchemy/Phrasea/TaskManager/Job/ArchiveJob.php index 532eb8bbb5..de418a4e71 100644 --- a/lib/Alchemy/Phrasea/TaskManager/Job/ArchiveJob.php +++ b/lib/Alchemy/Phrasea/TaskManager/Job/ArchiveJob.php @@ -12,6 +12,8 @@ namespace Alchemy\Phrasea\TaskManager\Job; use Alchemy\Phrasea\Application; +use Alchemy\Phrasea\Core\Event\Record\RecordEvents; +use Alchemy\Phrasea\Core\Event\Record\SubdefinitionCreateEvent; use Alchemy\Phrasea\Exception\RuntimeException; use Alchemy\Phrasea\Border\File; use Alchemy\Phrasea\Border\Manager as borderManager; @@ -1001,7 +1003,7 @@ class ArchiveJob extends AbstractJob { // quick fix to reconnect if mysql is lost $app->getApplicationBox()->get_connection(); - $databox->get_connection(); + $collection->get_connection(); $status = \databox_status::operation_or($stat0, $stat1); @@ -1032,7 +1034,8 @@ class ArchiveJob extends AbstractJob } $story->setStatus(\databox_status::operation_or($stat0, $stat1)); - $story->rebuild_subdefs(); + + $app['dispatcher']->dispatch(RecordEvents::SUBDEFINITION_CREATE, new SubdefinitionCreateEvent($story)); unset($media); diff --git a/lib/Alchemy/Phrasea/Webhook/EventProcessorFactory.php b/lib/Alchemy/Phrasea/Webhook/EventProcessorFactory.php index 90edefc10c..79109a624c 100644 --- a/lib/Alchemy/Phrasea/Webhook/EventProcessorFactory.php +++ b/lib/Alchemy/Phrasea/Webhook/EventProcessorFactory.php @@ -12,6 +12,7 @@ use Alchemy\Phrasea\Webhook\Processor\ProcessorFactory; use Alchemy\Phrasea\Webhook\Processor\ProcessorInterface; use Alchemy\Phrasea\Webhook\Processor\UserDeletedProcessorFactory; use Alchemy\Phrasea\Webhook\Processor\UserRegistrationProcessorFactory; +use Alchemy\Phrasea\Webhook\Processor\SubdefEventProcessor; class EventProcessorFactory { @@ -29,7 +30,10 @@ class EventProcessorFactory $this->registerFactory(WebhookEvent::FEED_ENTRY_TYPE, new FeedEntryProcessorFactory($app)); $this->registerFactory(WebhookEvent::USER_REGISTRATION_TYPE, new UserRegistrationProcessorFactory($app)); $this->registerFactory(WebhookEvent::ORDER_TYPE, new OrderNotificationProcessorFactory($app)); - $this->registerFactory(WebhookEvent::USER_DELETED_TYPE, new UserDeletedProcessorFactory($app)); + $this->registerFactory(WebhookEvent::USER_DELETED_TYPE, new UserDeletedProcessorFactory()); + $this->registerCallableFactory(WebhookEvent::RECORD_SUBDEF_TYPE, function () { + return new SubdefEventProcessor(); + }); } /** @@ -43,7 +47,7 @@ class EventProcessorFactory /** * @param string $eventType - * @param callback|callable $callable + * @param callback|callable|\Closure $callable */ public function registerCallableFactory($eventType, $callable) { diff --git a/lib/Alchemy/Phrasea/Webhook/Processor/FeedEntryProcessor.php b/lib/Alchemy/Phrasea/Webhook/Processor/FeedEntryProcessor.php index 7fd4500c49..c873bbb680 100644 --- a/lib/Alchemy/Phrasea/Webhook/Processor/FeedEntryProcessor.php +++ b/lib/Alchemy/Phrasea/Webhook/Processor/FeedEntryProcessor.php @@ -34,17 +34,16 @@ class FeedEntryProcessor implements ProcessorInterface { $data = $event->getData(); - if (!isset($data->entry_id)) { + if (!isset($data['entry_id'])) { return null; } - $entry = $this->entryRepository->find($data->entry_id); + $entry = $this->entryRepository->find($data['entry_id']); if (null === $entry) { return null; } - $data = $event->getData(); $feed = $entry->getFeed(); $query = $this->userQuery; @@ -54,8 +53,8 @@ class FeedEntryProcessor implements ProcessorInterface ->include_templates(false) ->email_not_null(true); - if ($feed->getCollection($this->app)) { - $query->on_base_ids([$feed->getCollection($this->app)->get_base_id()]); + if ($feed->getCollection($this->application)) { + $query->on_base_ids([$feed->getCollection($this->application)->get_base_id()]); } $start = 0; @@ -76,7 +75,7 @@ class FeedEntryProcessor implements ProcessorInterface return [ 'event' => $event->getName(), - 'users_were_notified' => isset($data->notify_email) ?: (bool) $data->notify_email, + 'users_were_notified' => isset($data['notify_email']) ? (bool) $data['notify_email'] : false, 'feed' => [ 'id' => $feed->getId(), 'title' => $feed->getTitle(), diff --git a/lib/Alchemy/Phrasea/Webhook/Processor/SubdefEventProcessor.php b/lib/Alchemy/Phrasea/Webhook/Processor/SubdefEventProcessor.php new file mode 100644 index 0000000000..4a442ed5db --- /dev/null +++ b/lib/Alchemy/Phrasea/Webhook/Processor/SubdefEventProcessor.php @@ -0,0 +1,17 @@ + $event->getName(), + 'data' => $event->getData() + ]; + } +} diff --git a/lib/Alchemy/Phrasea/Webhook/WebhookPublisher.php b/lib/Alchemy/Phrasea/Webhook/WebhookPublisher.php index 4304a5319f..2beaec292f 100644 --- a/lib/Alchemy/Phrasea/Webhook/WebhookPublisher.php +++ b/lib/Alchemy/Phrasea/Webhook/WebhookPublisher.php @@ -19,7 +19,7 @@ use Alchemy\Queue\MessageQueueRegistry; * Class WebhookPublisher publishes webhook event notifications in message queues * @package Alchemy\Phrasea\Webhook */ -class WebhookPublisher +class WebhookPublisher implements WebhookPublisherInterface { /** * @var MessageQueueRegistry diff --git a/lib/Alchemy/Phrasea/Webhook/WebhookPublisherInterface.php b/lib/Alchemy/Phrasea/Webhook/WebhookPublisherInterface.php new file mode 100644 index 0000000000..f8b4751ef0 --- /dev/null +++ b/lib/Alchemy/Phrasea/Webhook/WebhookPublisherInterface.php @@ -0,0 +1,10 @@ +getDataboxConnection()->executeUpdate($sql, ['type' => $type, 'record_id' => $this->getRecordId()]); if ($old_type !== $type) { - $this->rebuild_subdefs(); + $this->dispatch(RecordEvents::SUBDEFINITION_CREATE, new SubdefinitionCreateEvent($this)); } $this->type = $type; @@ -341,7 +342,8 @@ class record_adapter implements RecordInterface, cache_cacheableInterface array(':mime' => $mime, ':record_id' => $this->getRecordId()) )) { - $this->rebuild_subdefs(); + $this->dispatch(RecordEvents::SUBDEFINITION_CREATE, new SubdefinitionCreateEvent($this)); + $this->delete_data_from_cache(); } @@ -1735,7 +1737,9 @@ class record_adapter implements RecordInterface, cache_cacheableInterface throw new Exception('This record is not a grouping'); } - $selections = $this->getDatabox()->getRecordRepository()->findChildren([$this->getRecordId()], null, $offset, $max_items); + $user = $this->getAuthenticatedUser(); + + $selections = $this->getDatabox()->getRecordRepository()->findChildren([$this->getRecordId()], $user, $offset, $max_items); return reset($selections); } @@ -1745,7 +1749,9 @@ class record_adapter implements RecordInterface, cache_cacheableInterface */ public function get_grouping_parents() { - $selections = $this->getDatabox()->getRecordRepository()->findParents([$this->getRecordId()]); + $user = $this->getAuthenticatedUser(); + + $selections = $this->getDatabox()->getRecordRepository()->findParents([$this->getRecordId()], $user); return reset($selections); } @@ -1948,4 +1954,15 @@ class record_adapter implements RecordInterface, cache_cacheableInterface { return $this->app['provider.repo.media_subdef']->getRepositoryForDatabox($this->getDataboxId()); } + + /** + * @return User|null + */ + protected function getAuthenticatedUser() + { + /** @var \Alchemy\Phrasea\Authentication\Authenticator $authenticator */ + $authenticator = $this->app['authentication']; + + return $authenticator->getUser(); + } } diff --git a/resources/locales/messages.en.xlf b/resources/locales/messages.en.xlf index 4c9560d4e9..4b5369e3e6 100644 --- a/resources/locales/messages.en.xlf +++ b/resources/locales/messages.en.xlf @@ -1,4 +1,4 @@ - +
@@ -7,8 +7,8 @@
- - + + Form/Login/PhraseaAuthenticationForm.php Form/Configuration/EmailFormType.php @@ -13432,14 +13432,14 @@ It is possible to place several search areas August prod/WorkZone/Basket.html.twig - + workzone:datepicker:closeText - workzone:datepicker:closeText + Close prod/WorkZone/Basket.html.twig - + workzone:datepicker:currentText - workzone:datepicker:currentText + Current prod/WorkZone/Basket.html.twig @@ -13543,9 +13543,9 @@ It is possible to place several search areas prod/WorkZone/Basket.html.twig prod/Tooltip/Basket.html.twig - + workzone:feedback:expiration-open - workzone:feedback:expiration-open + Feedback open until prod/WorkZone/Basket.html.twig prod/Tooltip/Basket.html.twig diff --git a/resources/locales/messages.fr.xlf b/resources/locales/messages.fr.xlf index be1a6c9a97..d830633044 100644 --- a/resources/locales/messages.fr.xlf +++ b/resources/locales/messages.fr.xlf @@ -1,4 +1,4 @@ - +
@@ -7,8 +7,8 @@
- - + + Form/Login/PhraseaAuthenticationForm.php Form/Configuration/EmailFormType.php @@ -13540,15 +13540,15 @@ Si vous recevez cet e-mail sans l'avoir sollicité, merci de l'ignorer ou de le Mercredi prod/WorkZone/Basket.html.twig
- + workzone:feedback:expiration-closed - workzone:feedback:expiration-closed + Validation close depuis prod/WorkZone/Basket.html.twig prod/Tooltip/Basket.html.twig - + workzone:feedback:expiration-open - workzone:feedback:expiration-open + validation ouverte jusqu'au prod/WorkZone/Basket.html.twig prod/Tooltip/Basket.html.twig diff --git a/tests/Alchemy/Tests/Phrasea/Controller/Prod/ExportTest.php b/tests/Alchemy/Tests/Phrasea/Controller/Prod/ExportTest.php index 886df13e81..e49a967d0c 100644 --- a/tests/Alchemy/Tests/Phrasea/Controller/Prod/ExportTest.php +++ b/tests/Alchemy/Tests/Phrasea/Controller/Prod/ExportTest.php @@ -169,7 +169,8 @@ class ExportTest extends \PhraseanetAuthenticatedWebTestCase */ public function testExportMail() { - $this->mockNotificationDeliverer('Alchemy\Phrasea\Notification\Mail\MailRecordsExport'); + // deliver method removed in the listener +// $this->mockNotificationDeliverer('Alchemy\Phrasea\Notification\Mail\MailRecordsExport'); $this->getClient()->request('POST', '/prod/export/mail/', [ 'lst' => $this->getRecord1()->getId(), diff --git a/tests/Alchemy/Tests/Phrasea/Functional/UserDeletionTest.php b/tests/Alchemy/Tests/Phrasea/Functional/UserDeletionTest.php index 2db0eefa19..5ac42170c7 100644 --- a/tests/Alchemy/Tests/Phrasea/Functional/UserDeletionTest.php +++ b/tests/Alchemy/Tests/Phrasea/Functional/UserDeletionTest.php @@ -79,7 +79,7 @@ class UserDeletionTest extends \PhraseanetAuthenticatedWebTestCase $apiLog = $apiLogManipulator->create($account, new Request(), new Response()); $apiLogId = $apiLog->getId(); - $this->userManipulator->delete($this->user, true); + $this->userManipulator->delete($this->user); $this->assertTrue($this->user->isDeleted(), 'User was not properly deleted'); $apiLogRepository->clear(); diff --git a/tests/Alchemy/Tests/Phrasea/Model/Repositories/WebhookEventRepositoryTest.php b/tests/Alchemy/Tests/Phrasea/Model/Repositories/WebhookEventRepositoryTest.php index 7d21844d66..d27c00738e 100644 --- a/tests/Alchemy/Tests/Phrasea/Model/Repositories/WebhookEventRepositoryTest.php +++ b/tests/Alchemy/Tests/Phrasea/Model/Repositories/WebhookEventRepositoryTest.php @@ -12,6 +12,6 @@ class WebhookEventRepositoryTest extends \PhraseanetTestCase { $events = self::$DI['app']['orm.em']->getRepository('Phraseanet:WebhookEvent')->findUnprocessedEvents(); // I have no clue as to why this magic number is here, probably best to discard test - $this->assertCount(6, $events); + $this->assertCount(41, $events); } } diff --git a/tests/Alchemy/Tests/Phrasea/Webhook/Processor/FeedEntryProcessorTest.php b/tests/Alchemy/Tests/Phrasea/Webhook/Processor/FeedEntryProcessorTest.php index 60b518e443..39c3ae57d4 100644 --- a/tests/Alchemy/Tests/Phrasea/Webhook/Processor/FeedEntryProcessorTest.php +++ b/tests/Alchemy/Tests/Phrasea/Webhook/Processor/FeedEntryProcessorTest.php @@ -59,6 +59,6 @@ class FeedEntryProcessorTest extends \PhraseanetTestCase self::$DI['app']['repo.feed-entries'], self::$DI['app']['phraseanet.user-query'] ); - $this->assertEquals($processor->process($event), null); + $this->assertInternalType(\PHPUnit_Framework_Constraint_IsType::TYPE_ARRAY, $processor->process($event)); } }