diff --git a/Phraseanet-production-client/dist/production.js b/Phraseanet-production-client/dist/production.js index c99e182a99..31b78ccfd1 100644 --- a/Phraseanet-production-client/dist/production.js +++ b/Phraseanet-production-client/dist/production.js @@ -2588,7 +2588,7 @@ var leafletMap = function leafletMap(services) { if (editable) { $noticeButton = (0, _jquery2.default)(''); - $noticeBox = (0, _jquery2.default)('
' + localeService.t("prod:mapboxgl: title info") + '' + localeService.t("prod:mapboxgl: description info : right click to add position") + '
'); + $noticeBox = (0, _jquery2.default)('
' + localeService.t("mapboxgl title info") + '' + localeService.t("mapboxgl description info") + '
'); controlContainerEdit.append($noticeButton); controlContainerEdit.append($noticeBox); @@ -2628,7 +2628,7 @@ var leafletMap = function leafletMap(services) { if (drawable) { $noticeButtonJs = (0, _jquery2.default)(''); - $noticeBoxJs = (0, _jquery2.default)('
' + localeService.t("prod:mapboxjs: title notice") + '' + localeService.t("prod:mapboxjs: description notice") + '
'); + $noticeBoxJs = (0, _jquery2.default)('
' + localeService.t("mapboxjs title notice") + '' + localeService.t("mapboxjs description notice") + '
'); controlContainerSearch.append($noticeButtonJs); controlContainerSearch.append($noticeBoxJs); @@ -2637,7 +2637,7 @@ var leafletMap = function leafletMap(services) { if (editable) { $noticeButtonJs = (0, _jquery2.default)(''); - $noticeBoxJs = (0, _jquery2.default)('
' + localeService.t("prod:mapboxjs: title info") + '' + localeService.t("prod:mapboxjs: description info : right click to add position") + '
'); + $noticeBoxJs = (0, _jquery2.default)('
' + localeService.t("mapboxjs title info") + '' + localeService.t("mapboxjs description info") + '
'); controlContainerEdit.append($noticeButtonJs); controlContainerEdit.append($noticeBoxJs); @@ -50602,8 +50602,20 @@ var markerGLCollection = function markerGLCollection(services) { }; var setPoint = function setPoint(marker) { + var markerId = marker.properties.recordIndex; - var markerElement = getMarker(marker.properties._rid); + if (marker.properties._rid !== undefined) { + markerId = marker.properties._rid; + } + + var markerElement = getMarker(markerId); + + if (markerElement === undefined) { + var el = document.createElement('div'); + el.className = 'mapboxGl-phrasea-marker'; + + markerElement = markerGl[markerId] = new mapboxgl.Marker(el); + } markerElement.feature = { properties: { @@ -50682,7 +50694,6 @@ var markerGLCollection = function markerGLCollection(services) { markerSelected.togglePopup(); - cachedGeoJson.features[0].geometry.coordinates = [marker._originalPosition.lng, marker._originalPosition.lat]; resetMarkerPosition($content, markerSelected); }); }; diff --git a/Phraseanet-production-client/dist/production.min.js b/Phraseanet-production-client/dist/production.min.js index c99e182a99..31b78ccfd1 100644 --- a/Phraseanet-production-client/dist/production.min.js +++ b/Phraseanet-production-client/dist/production.min.js @@ -2588,7 +2588,7 @@ var leafletMap = function leafletMap(services) { if (editable) { $noticeButton = (0, _jquery2.default)(''); - $noticeBox = (0, _jquery2.default)('
' + localeService.t("prod:mapboxgl: title info") + '' + localeService.t("prod:mapboxgl: description info : right click to add position") + '
'); + $noticeBox = (0, _jquery2.default)('
' + localeService.t("mapboxgl title info") + '' + localeService.t("mapboxgl description info") + '
'); controlContainerEdit.append($noticeButton); controlContainerEdit.append($noticeBox); @@ -2628,7 +2628,7 @@ var leafletMap = function leafletMap(services) { if (drawable) { $noticeButtonJs = (0, _jquery2.default)(''); - $noticeBoxJs = (0, _jquery2.default)('
' + localeService.t("prod:mapboxjs: title notice") + '' + localeService.t("prod:mapboxjs: description notice") + '
'); + $noticeBoxJs = (0, _jquery2.default)('
' + localeService.t("mapboxjs title notice") + '' + localeService.t("mapboxjs description notice") + '
'); controlContainerSearch.append($noticeButtonJs); controlContainerSearch.append($noticeBoxJs); @@ -2637,7 +2637,7 @@ var leafletMap = function leafletMap(services) { if (editable) { $noticeButtonJs = (0, _jquery2.default)(''); - $noticeBoxJs = (0, _jquery2.default)('
' + localeService.t("prod:mapboxjs: title info") + '' + localeService.t("prod:mapboxjs: description info : right click to add position") + '
'); + $noticeBoxJs = (0, _jquery2.default)('
' + localeService.t("mapboxjs title info") + '' + localeService.t("mapboxjs description info") + '
'); controlContainerEdit.append($noticeButtonJs); controlContainerEdit.append($noticeBoxJs); @@ -50602,8 +50602,20 @@ var markerGLCollection = function markerGLCollection(services) { }; var setPoint = function setPoint(marker) { + var markerId = marker.properties.recordIndex; - var markerElement = getMarker(marker.properties._rid); + if (marker.properties._rid !== undefined) { + markerId = marker.properties._rid; + } + + var markerElement = getMarker(markerId); + + if (markerElement === undefined) { + var el = document.createElement('div'); + el.className = 'mapboxGl-phrasea-marker'; + + markerElement = markerGl[markerId] = new mapboxgl.Marker(el); + } markerElement.feature = { properties: { @@ -50682,7 +50694,6 @@ var markerGLCollection = function markerGLCollection(services) { markerSelected.togglePopup(); - cachedGeoJson.features[0].geometry.coordinates = [marker._originalPosition.lng, marker._originalPosition.lat]; resetMarkerPosition($content, markerSelected); }); }; diff --git a/Phraseanet-production-client/src/components/geolocalisation/providers/mapbox.js b/Phraseanet-production-client/src/components/geolocalisation/providers/mapbox.js index 6483f49917..b4b5ca8760 100644 --- a/Phraseanet-production-client/src/components/geolocalisation/providers/mapbox.js +++ b/Phraseanet-production-client/src/components/geolocalisation/providers/mapbox.js @@ -454,7 +454,7 @@ const leafletMap = (services) => { $noticeButton = $(''); $noticeBox = $('
' + - localeService.t("prod:mapboxgl: title info") + '' + localeService.t("prod:mapboxgl: description info : right click to add position") + '
'); + localeService.t("mapboxgl title info") + '' + localeService.t("mapboxgl description info") + ''); controlContainerEdit.append($noticeButton); controlContainerEdit.append($noticeBox); @@ -495,7 +495,7 @@ const leafletMap = (services) => { $noticeButtonJs = $(''); $noticeBoxJs = $('
' + - localeService.t("prod:mapboxjs: title notice") + '' + localeService.t("prod:mapboxjs: description notice") + '
'); + localeService.t("mapboxjs title notice") + '' + localeService.t("mapboxjs description notice") + ''); controlContainerSearch.append($noticeButtonJs); controlContainerSearch.append($noticeBoxJs); @@ -505,7 +505,7 @@ const leafletMap = (services) => { $noticeButtonJs = $(''); $noticeBoxJs = $('
' + - localeService.t("prod:mapboxjs: title info") + '' + localeService.t("prod:mapboxjs: description info : right click to add position") + '
'); + localeService.t("mapboxjs title info") + '' + localeService.t("mapboxjs description info") + ''); controlContainerEdit.append($noticeButtonJs); controlContainerEdit.append($noticeBoxJs); diff --git a/Phraseanet-production-client/src/components/geolocalisation/providers/markerGLCollection.js b/Phraseanet-production-client/src/components/geolocalisation/providers/markerGLCollection.js index adc9e71b48..ff09e0f4d3 100644 --- a/Phraseanet-production-client/src/components/geolocalisation/providers/markerGLCollection.js +++ b/Phraseanet-production-client/src/components/geolocalisation/providers/markerGLCollection.js @@ -39,8 +39,20 @@ const markerGLCollection = (services) => { }; const setPoint = (marker) => { + let markerId = marker.properties.recordIndex; - let markerElement = getMarker(marker.properties._rid); + if (marker.properties._rid !== undefined) { + markerId = marker.properties._rid; + } + + let markerElement = getMarker(markerId); + + if (markerElement === undefined) { + let el = document.createElement('div'); + el.className = 'mapboxGl-phrasea-marker'; + + markerElement = markerGl[markerId] = new mapboxgl.Marker(el); + } markerElement.feature = { properties : { @@ -134,7 +146,6 @@ const markerGLCollection = (services) => { markerSelected.togglePopup(); - cachedGeoJson.features[0].geometry.coordinates = [marker._originalPosition.lng, marker._originalPosition.lat]; resetMarkerPosition($content, markerSelected); }); } diff --git a/lib/Alchemy/Phrasea/Controller/Prod/LanguageController.php b/lib/Alchemy/Phrasea/Controller/Prod/LanguageController.php index fd780d1a31..db213e6a63 100644 --- a/lib/Alchemy/Phrasea/Controller/Prod/LanguageController.php +++ b/lib/Alchemy/Phrasea/Controller/Prod/LanguageController.php @@ -159,12 +159,12 @@ class LanguageController 'description notice' => $translator->trans('prod:mapboxgl: description notice'), 'title-map-dialog' => $translator->trans('prod:mapboxgl: title map dialog'), 'create new user' => $translator->trans('prod:push: create new user'), - 'prod:mapboxjs: title notice' => $translator->trans('prod:mapboxjs: title notice'), - 'prod:mapboxjs: description notice' => $translator->trans('prod:mapboxjs: description notice'), - 'prod:mapboxjs: title info' => $translator->trans('prod:mapboxjs: title info'), - 'prod:mapboxjs: description info : right click to add position' => $translator->trans('prod:mapboxjs: description info : right click to add position'), - 'prod:mapboxgl: title info' => $translator->trans('prod:mapboxgl: title info'), - 'prod:mapboxgl: description info : right click to add position' => $translator->trans('prod:mapboxgl: description info : right click to add position'), + 'mapboxjs title notice' => $translator->trans('prod:mapboxjs: title notice'), + 'mapboxjs description notice' => $translator->trans('prod:mapboxjs: description notice'), + 'mapboxjs title info' => $translator->trans('prod:mapboxjs: title info'), + 'mapboxjs description info' => $translator->trans('prod:mapboxjs: description info : right click to add position'), + 'mapboxgl title info' => $translator->trans('prod:mapboxgl: title info'), + 'mapboxgl description info' => $translator->trans('prod:mapboxgl: description info : right click to add position'), 'prod:videoeditor:subtitletab:message:: error' => $translator->trans('prod:videoeditor:subtitletab:message:: error'), 'prod:videoeditor:subtitletab:message:: success' => $translator->trans('prod:videoeditor:subtitletab:message:: success'), 'Edit expose title' => $translator->trans('prod:workzone:expose:modal:: title'), diff --git a/lib/Alchemy/Phrasea/SearchEngine/Elastic/ElasticsearchOptions.php b/lib/Alchemy/Phrasea/SearchEngine/Elastic/ElasticsearchOptions.php index 28f8a83613..4f7de4c7af 100644 --- a/lib/Alchemy/Phrasea/SearchEngine/Elastic/ElasticsearchOptions.php +++ b/lib/Alchemy/Phrasea/SearchEngine/Elastic/ElasticsearchOptions.php @@ -60,7 +60,7 @@ class ElasticsearchOptions 'replicas' => 0, 'minScore' => 4, 'highlight' => true, - 'max_result_window' => 500000, + 'maxResultWindow' => 500000, 'populate_order' => self::POPULATE_ORDER_RID, 'populate_direction' => self::POPULATE_DIRECTION_DESC, 'activeTab' => null, @@ -76,7 +76,7 @@ class ElasticsearchOptions $self->setReplicas($options['replicas']); $self->setMinScore($options['minScore']); $self->setHighlight($options['highlight']); - $self->setMaxResultWindow($options['max_result_window']); + $self->setMaxResultWindow($options['maxResultWindow']); $self->setPopulateOrder($options['populate_order']); $self->setPopulateDirection($options['populate_direction']); $self->setActiveTab($options['activeTab']); diff --git a/lib/Alchemy/Phrasea/WorkerManager/Controller/AdminConfigurationController.php b/lib/Alchemy/Phrasea/WorkerManager/Controller/AdminConfigurationController.php index c4ecbd1150..1477ceba94 100644 --- a/lib/Alchemy/Phrasea/WorkerManager/Controller/AdminConfigurationController.php +++ b/lib/Alchemy/Phrasea/WorkerManager/Controller/AdminConfigurationController.php @@ -229,6 +229,29 @@ class AdminConfigurationController extends Controller ]); } + public function validationReminderAction(PhraseaApplication $app, Request $request) + { + $interval = $app['conf']->get(['workers', 'validationReminder', 'interval'], 7200); + + if ($request->getMethod() == 'POST') { + $reminderInterval = (int)$request->request->get('worker_reminder_interval'); + // save the period interval in second + $app['conf']->set(['workers', 'validationReminder', 'interval'], $reminderInterval); + + /** @var AMQPConnection $serverConnection */ + $serverConnection = $this->app['alchemy_worker.amqp.connection']; + // reinitialize the validation reminder queues + $serverConnection->reinitializeQueue([MessagePublisher::VALIDATION_REMINDER_QUEUE]); + $this->app['alchemy_worker.message.publisher']->initializeLoopQueue(MessagePublisher::VALIDATION_REMINDER_TYPE); + + return $app->redirectPath('worker_admin'); + } + + return $this->render('admin/worker-manager/worker_validation_reminder.html.twig', [ + 'interval' => $interval + ]); + } + public function populateStatusAction(PhraseaApplication $app, Request $request) { $databoxIds = $request->get('sbasIds'); @@ -255,7 +278,7 @@ class AdminConfigurationController extends Controller // reinitialize the pull queues $serverConnection->reinitializeQueue([MessagePublisher::PULL_QUEUE]); - $this->app['alchemy_worker.message.publisher']->initializePullAssets(); + $this->app['alchemy_worker.message.publisher']->initializeLoopQueue(MessagePublisher::PULL_ASSETS_TYPE); return $app->redirectPath('worker_admin'); } diff --git a/lib/Alchemy/Phrasea/WorkerManager/Provider/AlchemyWorkerServiceProvider.php b/lib/Alchemy/Phrasea/WorkerManager/Provider/AlchemyWorkerServiceProvider.php index 71d965de42..44496f4849 100644 --- a/lib/Alchemy/Phrasea/WorkerManager/Provider/AlchemyWorkerServiceProvider.php +++ b/lib/Alchemy/Phrasea/WorkerManager/Provider/AlchemyWorkerServiceProvider.php @@ -20,6 +20,7 @@ use Alchemy\Phrasea\WorkerManager\Worker\PullAssetsWorker; use Alchemy\Phrasea\WorkerManager\Worker\Resolver\TypeBasedWorkerResolver; use Alchemy\Phrasea\WorkerManager\Worker\SubdefCreationWorker; use Alchemy\Phrasea\WorkerManager\Worker\SubtitleWorker; +use Alchemy\Phrasea\WorkerManager\Worker\ValidationReminderWorker; use Alchemy\Phrasea\WorkerManager\Worker\WebhookWorker; use Alchemy\Phrasea\WorkerManager\Worker\WorkerInvoker; use Alchemy\Phrasea\WorkerManager\Worker\WriteMetadatasWorker; @@ -151,6 +152,10 @@ class AlchemyWorkerServiceProvider implements PluginProviderInterface $app['alchemy_worker.type_based_worker_resolver']->addFactory(MessagePublisher::FTP_TYPE, new CallableWorkerFactory(function () use ($app) { return new FtpWorker($app); })); + + $app['alchemy_worker.type_based_worker_resolver']->addFactory(MessagePublisher::VALIDATION_REMINDER_TYPE, new CallableWorkerFactory(function () use ($app) { + return new ValidationReminderWorker($app); + })); } /** diff --git a/lib/Alchemy/Phrasea/WorkerManager/Provider/ControllerServiceProvider.php b/lib/Alchemy/Phrasea/WorkerManager/Provider/ControllerServiceProvider.php index 772c899e5f..a53aa5e91d 100644 --- a/lib/Alchemy/Phrasea/WorkerManager/Provider/ControllerServiceProvider.php +++ b/lib/Alchemy/Phrasea/WorkerManager/Provider/ControllerServiceProvider.php @@ -88,6 +88,10 @@ class ControllerServiceProvider implements ControllerProviderInterface, ServiceP ->method('GET|POST') ->bind('worker_admin_pullAssets'); + $controllers->match('/validation-reminder', 'controller.worker.admin.configuration:validationReminderAction') + ->method('GET|POST') + ->bind('worker_admin_validationReminder'); + $controllers->match('/queue-monitor', 'controller.worker.admin.configuration:queueMonitorAction') ->method('GET') ->bind('worker_admin_queue_monitor'); diff --git a/lib/Alchemy/Phrasea/WorkerManager/Queue/AMQPConnection.php b/lib/Alchemy/Phrasea/WorkerManager/Queue/AMQPConnection.php index bf355ae04d..421921f1c0 100644 --- a/lib/Alchemy/Phrasea/WorkerManager/Queue/AMQPConnection.php +++ b/lib/Alchemy/Phrasea/WorkerManager/Queue/AMQPConnection.php @@ -27,13 +27,14 @@ class AMQPConnection MessagePublisher::WEBHOOK_TYPE => MessagePublisher::WEBHOOK_QUEUE, MessagePublisher::ASSETS_INGEST_TYPE => MessagePublisher::ASSETS_INGEST_QUEUE, MessagePublisher::CREATE_RECORD_TYPE => MessagePublisher::CREATE_RECORD_QUEUE, - MessagePublisher::PULL_QUEUE => MessagePublisher::PULL_QUEUE, + MessagePublisher::PULL_ASSETS_TYPE => MessagePublisher::PULL_QUEUE, MessagePublisher::POPULATE_INDEX_TYPE => MessagePublisher::POPULATE_INDEX_QUEUE, MessagePublisher::DELETE_RECORD_TYPE => MessagePublisher::DELETE_RECORD_QUEUE, MessagePublisher::MAIN_QUEUE_TYPE => MessagePublisher::MAIN_QUEUE, MessagePublisher::SUBTITLE_TYPE => MessagePublisher::SUBTITLE_QUEUE, MessagePublisher::EXPOSE_UPLOAD_TYPE => MessagePublisher::EXPOSE_UPLOAD_QUEUE, - MessagePublisher::FTP_TYPE => MessagePublisher::FTP_QUEUE + MessagePublisher::FTP_TYPE => MessagePublisher::FTP_QUEUE, + MessagePublisher::VALIDATION_REMINDER_TYPE => MessagePublisher::VALIDATION_REMINDER_QUEUE, ]; // the corresponding worker queues and retry queues, loop queue @@ -46,7 +47,8 @@ class AMQPConnection MessagePublisher::CREATE_RECORD_QUEUE => MessagePublisher::RETRY_CREATE_RECORD_QUEUE, MessagePublisher::POPULATE_INDEX_QUEUE => MessagePublisher::RETRY_POPULATE_INDEX_QUEUE, MessagePublisher::PULL_QUEUE => MessagePublisher::LOOP_PULL_QUEUE, - MessagePublisher::FTP_QUEUE => MessagePublisher::RETRY_FTP_QUEUE + MessagePublisher::FTP_QUEUE => MessagePublisher::RETRY_FTP_QUEUE, + MessagePublisher::VALIDATION_REMINDER_QUEUE => MessagePublisher::LOOP_VALIDATION_REMINDER_QUEUE ]; public static $defaultFailedQueues = [ @@ -65,6 +67,11 @@ class AMQPConnection MessagePublisher::SUBDEF_QUEUE => MessagePublisher::DELAYED_SUBDEF_QUEUE ]; + public static $defaultLoopTypes = [ + MessagePublisher::PULL_ASSETS_TYPE, + MessagePublisher::VALIDATION_REMINDER_TYPE + ]; + // default message TTL in retry queue in millisecond const RETRY_DELAY = 10000; @@ -271,6 +278,18 @@ class AMQPConnection isset($config['pull_assets']['pullInterval']) ) { // convert in milli second return (int)($config['pull_assets']['pullInterval']) * 1000; + } elseif ($routing == MessagePublisher::VALIDATION_REMINDER_QUEUE) { + + if (isset($config['validationReminder']) && + isset($config['validationReminder']['interval'])) { + + // convert in milli second + return (int)($config['validationReminder']['interval']) * 1000; + } + + // default value to 2 hour if not set + return (int) 7200 * 1000; + } elseif (isset($config['retry_queue']) && isset($config['retry_queue'][array_search($routing, AMQPConnection::$defaultQueues)])) { diff --git a/lib/Alchemy/Phrasea/WorkerManager/Queue/MessageHandler.php b/lib/Alchemy/Phrasea/WorkerManager/Queue/MessageHandler.php index b06bbab265..7f13668903 100644 --- a/lib/Alchemy/Phrasea/WorkerManager/Queue/MessageHandler.php +++ b/lib/Alchemy/Phrasea/WorkerManager/Queue/MessageHandler.php @@ -4,6 +4,7 @@ namespace Alchemy\Phrasea\WorkerManager\Queue; use Alchemy\Phrasea\WorkerManager\Worker\ProcessPool; use Alchemy\Phrasea\WorkerManager\Worker\WorkerInvoker; +use PhpAmqpLib\Channel\AMQPChannel; use PhpAmqpLib\Message\AMQPMessage; use PhpAmqpLib\Wire\AMQPTable; use Ramsey\Uuid\Uuid; @@ -61,7 +62,7 @@ class MessageHandler } // if message is yet executed 3 times, save the unprocessed message in the corresponding failed queues - if ($count > self::MAX_OF_TRY && $data['message_type'] != MessagePublisher::PULL_ASSETS_TYPE) { + if ($count > self::MAX_OF_TRY && !in_array($data['message_type'], AMQPConnection::$defaultLoopTypes)) { $this->messagePublisher->publishFailedMessage($data['payload'], $headers, AMQPConnection::$defaultFailedQueues[$data['message_type']]); $logMessage = sprintf("Rabbit message executed 3 times, it's to be saved in %s , payload >>> %s", @@ -75,8 +76,8 @@ class MessageHandler try { $workerInvoker->invokeWorker($data['message_type'], json_encode($data['payload'])); - if ($data['message_type'] == MessagePublisher::PULL_ASSETS_TYPE) { - // make a loop for the pull assets + if (in_array($data['message_type'], AMQPConnection::$defaultLoopTypes)) { + // make a loop for the loop type $channel->basic_nack($message->delivery_info['delivery_tag']); } else { $channel->basic_ack($message->delivery_info['delivery_tag']); @@ -101,19 +102,26 @@ class MessageHandler foreach (AMQPConnection::$defaultQueues as $queueName) { if ($argQueueName ) { if (in_array($queueName, $argQueueName)) { - $serverConnection->setQueue($queueName); - - // give prefetch message to a worker consumer at a time - $channel->basic_qos(null, $prefetchCount, null); - $channel->basic_consume($queueName, Uuid::uuid4(), false, false, false, false, $callback); + $this->runConsumer($queueName, $serverConnection, $channel, $prefetchCount, $callback); } } else { - $serverConnection->setQueue($queueName); - - // give prefetch message to a worker consumer at a time - $channel->basic_qos(null, $prefetchCount, null); - $channel->basic_consume($queueName, Uuid::uuid4(), false, false, false, false, $callback); + $this->runConsumer($queueName, $serverConnection, $channel, $prefetchCount, $callback); } } } + + private function runConsumer($queueName, AMQPConnection $serverConnection, AMQPChannel $channel, $prefetchCount, $callback) + { + // initialize validation reminder when starting consumer + if ($queueName == MessagePublisher::VALIDATION_REMINDER_QUEUE) { + $serverConnection->reinitializeQueue([MessagePublisher::VALIDATION_REMINDER_QUEUE]); + $this->messagePublisher->initializeLoopQueue(MessagePublisher::VALIDATION_REMINDER_TYPE); + } + + $serverConnection->setQueue($queueName); + + // give prefetch message to a worker consumer at a time + $channel->basic_qos(null, $prefetchCount, null); + $channel->basic_consume($queueName, Uuid::uuid4(), false, false, false, false, $callback); + } } diff --git a/lib/Alchemy/Phrasea/WorkerManager/Queue/MessagePublisher.php b/lib/Alchemy/Phrasea/WorkerManager/Queue/MessagePublisher.php index cc455c37b7..6abfc198ce 100644 --- a/lib/Alchemy/Phrasea/WorkerManager/Queue/MessagePublisher.php +++ b/lib/Alchemy/Phrasea/WorkerManager/Queue/MessagePublisher.php @@ -18,6 +18,7 @@ class MessagePublisher const POPULATE_INDEX_TYPE = 'populateIndex'; const PULL_ASSETS_TYPE = 'pullAssets'; const SUBDEF_CREATION_TYPE = 'subdefCreation'; + const VALIDATION_REMINDER_TYPE = 'validationReminder'; const WRITE_METADATAS_TYPE = 'writeMetadatas'; const WEBHOOK_TYPE = 'webhook'; @@ -40,6 +41,7 @@ class MessagePublisher const POPULATE_INDEX_QUEUE = 'populateindex-queue'; const PULL_QUEUE = 'pull-queue'; const SUBDEF_QUEUE = 'subdef-queue'; + const VALIDATION_REMINDER_QUEUE = 'validationReminder-queue'; const WEBHOOK_QUEUE = 'webhook-queue'; // retry queue @@ -53,8 +55,10 @@ class MessagePublisher const RETRY_SUBDEF_QUEUE = 'retry-subdef-queue'; const RETRY_WEBHOOK_QUEUE = 'retry-webhook-queue'; - // use this queue to make a loop on a consumer - const LOOP_PULL_QUEUE = 'loop-pull-queue'; + // use those queue to make a loop on a consumer + const LOOP_PULL_QUEUE = 'loop-pull-queue'; + const LOOP_VALIDATION_REMINDER_QUEUE = 'loop-validationReminder-queue'; + // all failed queue, if message is treated over 3 times it goes to the failed queue const FAILED_ASSETS_INGEST_QUEUE = 'failed-ingest-queue'; @@ -126,16 +130,16 @@ class MessagePublisher return true; } - public function initializePullAssets() + public function initializeLoopQueue($type) { $payload = [ - 'message_type' => self::PULL_ASSETS_TYPE, + 'message_type' => $type, 'payload' => [ 'initTimestamp' => new \DateTime('now', new \DateTimeZone('UTC')) ] ]; - $this->publishMessage($payload, self::PULL_QUEUE); + $this->publishMessage($payload, AMQPConnection::$defaultQueues[$type]); } public function connectionClose() diff --git a/lib/Alchemy/Phrasea/WorkerManager/Worker/ValidationReminderWorker.php b/lib/Alchemy/Phrasea/WorkerManager/Worker/ValidationReminderWorker.php new file mode 100644 index 0000000000..4801d209d2 --- /dev/null +++ b/lib/Alchemy/Phrasea/WorkerManager/Worker/ValidationReminderWorker.php @@ -0,0 +1,193 @@ +app = $app; + $this->messagePublisher = $this->app['alchemy_worker.message.publisher']; + $this->logger = $this->app['alchemy_worker.logger']; + } + + public function process(array $payload) + { + $this->setDelivererLocator(new LazyLocator($this->app, 'notification.deliverer')); + + $days = (int)$this->getConf()->get(['registry', 'actions', 'validation-reminder-days']); + + $interval = sprintf('P%dD', $days); + $now = new DateTime(); + + $dateTo = clone($now); + try { + $dateTo->add(new DateInterval($interval)); + } catch(\Exception $e) { + $this->logger->error(sprintf('Bad interval "%s" ?', $interval)); + return ; + } + + foreach ($this->getValidationParticipantRepository()->findNotConfirmedAndNotRemindedParticipantsByExpireDate($dateTo, $now) as $participant) { + $validationSession = $participant->getSession(); + $basket = $validationSession->getBasket(); + + $canSend = true; + + $user = $participant->getUser(); // always ok ! + try { + $str_email = $user->getEmail(); // force to hydrate + } catch (\Exception $e) { + $this->logger->error('user not found!'); + $canSend = false; + } + + $emails[] = + + // 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); + } + catch (\Exception $e) { + // not unique token ? should not happen + $canSend = false; + } + + if(!$canSend) { + continue; + } + + 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()]); + } + + $this->doRemind($participant, $basket, $url); + } + + $this->getEntityManager()->flush(); + } + + 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; + + $userFrom = $basket->getValidation()->getInitiator(); + $userTo = $participant->getUser(); + + if ($this->shouldSendNotificationFor($participant->getUser(), 'eventsmanager_notify_validationreminder')) { + $readyToSend = false; + $title = $receiver = $emitter = null; + try { + $title = $basket->getName(); + + $receiver = Receiver::fromUser($userTo); + $emitter = Emitter::fromUser($userFrom); + + $readyToSend = true; + } + catch (\Exception $e) { + // no-op + } + + if ($readyToSend) { + $this->logger->info(sprintf(' -> remind "%s" from "%s" to "%s"', $title, $emitter->getEmail(), $receiver->getEmail())); + + $mail = MailInfoValidationReminder::create($this->app, $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->app['events-manager']->notify($params['to'], 'eventsmanager_notify_validationreminder', $datas, $mailed); + } + + /** + * @param User $user + * @param $type + * @return mixed + */ + private function shouldSendNotificationFor(User $user, $type) + { + return $this->app['settings']->getUserNotificationSetting($user, $type); + } + + /** + * @return PropertyAccess + */ + private function getConf() + { + return $this->app['conf']; + } + + /** + * @return EntityManagerInterface + */ + private function getEntityManager() + { + return $this->app['orm.em']; + } + + /** + * @return ValidationParticipantRepository + */ + private function getValidationParticipantRepository() + { + return $this->app['repo.validation-participants']; + } + + /** + * @return TokenRepository + */ + private function getTokenRepository() + { + return $this->app['repo.tokens']; + } +} diff --git a/resources/locales/messages.de.xlf b/resources/locales/messages.de.xlf index 8b590e525e..8bbf70379c 100644 --- a/resources/locales/messages.de.xlf +++ b/resources/locales/messages.de.xlf @@ -1,6 +1,6 @@ - +
The source node in most cases contains the sample message as written by the developer. If it looks like a dot-delimitted string such as "form.label.firstname", then the developer has not provided a default message. @@ -4460,7 +4460,7 @@ My application Meine Anwendung - web/account/account.html.twig + web/account/account.html.twig My baskets @@ -4542,7 +4542,7 @@ No Nein web/developers/applications.html.twig - web/account/account.html.twig + web/account/account.html.twig No URL available @@ -4919,7 +4919,7 @@ Paniers Sammelkörbe - web/account/account.html.twig + web/account/account.html.twig lightbox/IE6/validate.html.twig web/lightbox/index.html.twig web/lightbox/validate.html.twig @@ -7700,7 +7700,7 @@ Yes Ja web/developers/applications.html.twig - web/account/account.html.twig + web/account/account.html.twig user/import/view.html.twig @@ -8756,11 +8756,6 @@ falsches Passwort Controller/Root/AccountController.php - - admin::compte-utilisateur:ftp: Nombre d'essais max - maximale Anzahl von Versuchen - web/account/account.html.twig - admin::compte-utilisateur:ftp: Utiliser le mode passif Passiv Mode benutzen @@ -9133,7 +9128,27 @@ admin::workermanager: Rabbit config error Konfigurationsfehler des Nachrichten Managers - admin/worker-manager/index.html.twig + admin/worker-manager/index.html.twig + + + admin::workermanager:tab:Reminder: Interval in second + admin::workermanager:tab:Reminder: Interval in second + admin/worker-manager/worker_validation_reminder.html.twig + + + admin::workermanager:tab:Reminder: Start + admin::workermanager:tab:Reminder: Start + admin/worker-manager/worker_validation_reminder.html.twig + + + admin::workermanager:tab:Reminder: Stop + admin::workermanager:tab:Reminder: Stop + admin/worker-manager/worker_validation_reminder.html.twig + + + admin::workermanager:tab:Reminder: description + admin::workermanager:tab:Reminder: description + admin/worker-manager/worker_validation_reminder.html.twig admin::workermanager:tab:configuration: title @@ -9248,6 +9263,11 @@ admin::workermanager:tab:queueMonitor: title Warteschlange + admin/worker-manager/index.html.twig + + + admin::workermanager:tab:reminder: title + admin::workermanager:tab:reminder: title admin/worker-manager/index.html.twig @@ -9629,7 +9649,7 @@ admin:worker Retrieve configuration error Worker Abruf Konfigurationsfehler - admin/worker-manager/index.html.twig + admin/worker-manager/index.html.twig alert @@ -10012,7 +10032,7 @@ web/developers/application_form.html.twig web/account/access.html.twig web/account/reset-email.html.twig - web/account/account.html.twig + web/account/account.html.twig prod/actions/edit_default.html.twig prod/actions/edit_default.html.twig Bridge/Flickr/photo_modify.html.twig @@ -10820,7 +10840,7 @@ login::notification: Changements enregistres Veränderungen wurden bestätigt - Controller/Root/AccountController.php + Controller/Root/AccountController.php login::notification: Mise a jour du mot de passe avec succes @@ -11522,12 +11542,12 @@ phraseanet::account The account has been deleted Ihr Benutzerkonto wurde gelöscht - Controller/Root/AccountController.php + Controller/Root/AccountController.php > ]]> Ihr Benutzerkonto kann nur durch die Administration Anwendung gelöscht werden. - web/account/account.html.twig + web/account/account.html.twig phraseanet::account: A confirmation e-mail has been sent. Please follow the instructions contained to continue account deletion @@ -11537,32 +11557,32 @@ phraseanet::account: Are you sure you want to delete your account? Möchten Sie Ihr Konto wirklich löschen? - web/account/account.html.twig + web/account/account.html.twig phraseanet::account: Delete my account Mein Benutzerkonto löschen - web/account/account.html.twig + web/account/account.html.twig phraseanet::account: I am agree to delete my account Ich bin damit einverstanden, mein Konto zu löschen - web/account/account.html.twig + web/account/account.html.twig phraseanet::account: I am agree to delete my account, need confirmation on mail Ich bin damit einverstanden, mein Konto zu löschen, und ich möchte eine E-Mail Bestätigung - web/account/account.html.twig + web/account/account.html.twig phraseanet::account: List of data to be deleted Liste der zu löschenden Daten - web/account/account.html.twig + web/account/account.html.twig phraseanet::account: My phraseanet account Mein Phraseanet Benutzerkonto - web/account/account.html.twig + web/account/account.html.twig phraseanet::chargement @@ -11596,12 +11616,12 @@ Mailserver-Ausfall Controller/Root/AccountController.php Controller/Root/AccountController.php - Controller/Root/AccountController.php + Controller/Root/AccountController.php phraseanet::error: failed to revoke some user access Fehler beim Widerruf von Nutzerrechten - Controller/Root/AccountController.php + Controller/Root/AccountController.php phraseanet::jours:: dimanche diff --git a/resources/locales/messages.en.xlf b/resources/locales/messages.en.xlf index 91dee24e92..db7dac424b 100644 --- a/resources/locales/messages.en.xlf +++ b/resources/locales/messages.en.xlf @@ -1,6 +1,6 @@ - +
The source node in most cases contains the sample message as written by the developer. If it looks like a dot-delimitted string such as "form.label.firstname", then the developer has not provided a default message. @@ -4463,7 +4463,7 @@ My application My application - web/account/account.html.twig + web/account/account.html.twig My baskets @@ -4545,7 +4545,7 @@ No No web/developers/applications.html.twig - web/account/account.html.twig + web/account/account.html.twig No URL available @@ -4922,7 +4922,7 @@ Paniers Baskets - web/account/account.html.twig + web/account/account.html.twig lightbox/IE6/validate.html.twig web/lightbox/index.html.twig web/lightbox/validate.html.twig @@ -7703,7 +7703,7 @@ Yes Yes web/developers/applications.html.twig - web/account/account.html.twig + web/account/account.html.twig user/import/view.html.twig @@ -8759,11 +8759,6 @@ Wrong password Controller/Root/AccountController.php - - admin::compte-utilisateur:ftp: Nombre d'essais max - Max. retry - web/account/account.html.twig - admin::compte-utilisateur:ftp: Utiliser le mode passif Use passive mode @@ -9136,7 +9131,27 @@ admin::workermanager: Rabbit config error Message Queue configuration error - admin/worker-manager/index.html.twig + admin/worker-manager/index.html.twig + + + admin::workermanager:tab:Reminder: Interval in second + admin::workermanager:tab:Reminder: Interval in second + admin/worker-manager/worker_validation_reminder.html.twig + + + admin::workermanager:tab:Reminder: Start + admin::workermanager:tab:Reminder: Start + admin/worker-manager/worker_validation_reminder.html.twig + + + admin::workermanager:tab:Reminder: Stop + admin::workermanager:tab:Reminder: Stop + admin/worker-manager/worker_validation_reminder.html.twig + + + admin::workermanager:tab:Reminder: description + admin::workermanager:tab:Reminder: description + admin/worker-manager/worker_validation_reminder.html.twig admin::workermanager:tab:configuration: title @@ -9251,6 +9266,11 @@ admin::workermanager:tab:queueMonitor: title Queues + admin/worker-manager/index.html.twig + + + admin::workermanager:tab:reminder: title + admin::workermanager:tab:reminder: title admin/worker-manager/index.html.twig @@ -9632,7 +9652,7 @@ admin:worker Retrieve configuration error Worker Retrieve configuration error - admin/worker-manager/index.html.twig + admin/worker-manager/index.html.twig alert @@ -10015,7 +10035,7 @@ web/developers/application_form.html.twig web/account/access.html.twig web/account/reset-email.html.twig - web/account/account.html.twig + web/account/account.html.twig prod/actions/edit_default.html.twig prod/actions/edit_default.html.twig Bridge/Flickr/photo_modify.html.twig @@ -10823,7 +10843,7 @@ login::notification: Changements enregistres Changes saved - Controller/Root/AccountController.php + Controller/Root/AccountController.php login::notification: Mise a jour du mot de passe avec succes @@ -11525,12 +11545,12 @@ phraseanet::account The account has been deleted The account has been deleted - Controller/Root/AccountController.php + Controller/Root/AccountController.php > ]]> Your rights do not allow to perform this action. Your account can only be deleted via the Administration interface. - web/account/account.html.twig + web/account/account.html.twig phraseanet::account: A confirmation e-mail has been sent. Please follow the instructions contained to continue account deletion @@ -11540,32 +11560,32 @@ phraseanet::account: Are you sure you want to delete your account? Are you sure you wish to delete your account? - web/account/account.html.twig + web/account/account.html.twig phraseanet::account: Delete my account Delete my account - web/account/account.html.twig + web/account/account.html.twig phraseanet::account: I am agree to delete my account I agree to delete my account - web/account/account.html.twig + web/account/account.html.twig phraseanet::account: I am agree to delete my account, need confirmation on mail I agree to delete my account and I need a confirmation by email - web/account/account.html.twig + web/account/account.html.twig phraseanet::account: List of data to be deleted List of data to be deleted - web/account/account.html.twig + web/account/account.html.twig phraseanet::account: My phraseanet account My Phraseanet account - web/account/account.html.twig + web/account/account.html.twig phraseanet::chargement @@ -11599,12 +11619,12 @@ Mail-server error Controller/Root/AccountController.php Controller/Root/AccountController.php - Controller/Root/AccountController.php + Controller/Root/AccountController.php phraseanet::error: failed to revoke some user access Failed to revoke some user access - Controller/Root/AccountController.php + Controller/Root/AccountController.php phraseanet::jours:: dimanche diff --git a/resources/locales/messages.fr.xlf b/resources/locales/messages.fr.xlf index 24f44e8f3e..be1a227b12 100644 --- a/resources/locales/messages.fr.xlf +++ b/resources/locales/messages.fr.xlf @@ -1,6 +1,6 @@ - +
The source node in most cases contains the sample message as written by the developer. If it looks like a dot-delimitted string such as "form.label.firstname", then the developer has not provided a default message. @@ -4460,7 +4460,7 @@ My application Mon application - web/account/account.html.twig + web/account/account.html.twig My baskets @@ -4542,7 +4542,7 @@ No Non web/developers/applications.html.twig - web/account/account.html.twig + web/account/account.html.twig No URL available @@ -4919,7 +4919,7 @@ Paniers Paniers - web/account/account.html.twig + web/account/account.html.twig lightbox/IE6/validate.html.twig web/lightbox/index.html.twig web/lightbox/validate.html.twig @@ -7702,7 +7702,7 @@ Pour les utilisateurs authentifiés, la demande de validation est également dis Yes Oui web/developers/applications.html.twig - web/account/account.html.twig + web/account/account.html.twig user/import/view.html.twig @@ -8759,11 +8759,6 @@ Si vous recevez cet e-mail sans l'avoir sollicité, merci de l'ignorer ou de le Le mot de passe est erroné Controller/Root/AccountController.php - - admin::compte-utilisateur:ftp: Nombre d'essais max - Nombre d'essais - web/account/account.html.twig - admin::compte-utilisateur:ftp: Utiliser le mode passif Utiliser le mode passif @@ -9136,7 +9131,27 @@ Si vous recevez cet e-mail sans l'avoir sollicité, merci de l'ignorer ou de le admin::workermanager: Rabbit config error Erreur dans la configuration du gestionnaire de messages - admin/worker-manager/index.html.twig + admin/worker-manager/index.html.twig + + + admin::workermanager:tab:Reminder: Interval in second + admin::workermanager:tab:Reminder: Interval in second + admin/worker-manager/worker_validation_reminder.html.twig + + + admin::workermanager:tab:Reminder: Start + admin::workermanager:tab:Reminder: Start + admin/worker-manager/worker_validation_reminder.html.twig + + + admin::workermanager:tab:Reminder: Stop + admin::workermanager:tab:Reminder: Stop + admin/worker-manager/worker_validation_reminder.html.twig + + + admin::workermanager:tab:Reminder: description + admin::workermanager:tab:Reminder: description + admin/worker-manager/worker_validation_reminder.html.twig admin::workermanager:tab:configuration: title @@ -9251,6 +9266,11 @@ Si vous recevez cet e-mail sans l'avoir sollicité, merci de l'ignorer ou de le admin::workermanager:tab:queueMonitor: title Files de message + admin/worker-manager/index.html.twig + + + admin::workermanager:tab:reminder: title + admin::workermanager:tab:reminder: title admin/worker-manager/index.html.twig @@ -9632,7 +9652,7 @@ Si vous recevez cet e-mail sans l'avoir sollicité, merci de l'ignorer ou de le admin:worker Retrieve configuration error Erreur lors de la récupération de la configuration des workers - admin/worker-manager/index.html.twig + admin/worker-manager/index.html.twig alert @@ -10015,7 +10035,7 @@ Si vous recevez cet e-mail sans l'avoir sollicité, merci de l'ignorer ou de le web/developers/application_form.html.twig web/account/access.html.twig web/account/reset-email.html.twig - web/account/account.html.twig + web/account/account.html.twig prod/actions/edit_default.html.twig prod/actions/edit_default.html.twig Bridge/Flickr/photo_modify.html.twig @@ -10823,7 +10843,7 @@ Si vous recevez cet e-mail sans l'avoir sollicité, merci de l'ignorer ou de le login::notification: Changements enregistres Changements confirmés - Controller/Root/AccountController.php + Controller/Root/AccountController.php login::notification: Mise a jour du mot de passe avec succes @@ -11525,12 +11545,12 @@ Si vous recevez cet e-mail sans l'avoir sollicité, merci de l'ignorer ou de le phraseanet::account The account has been deleted Le compte a été supprimé - Controller/Root/AccountController.php + Controller/Root/AccountController.php > ]]> Vos droits ne vous permettent pas de réaliser cette action, votre compte ne peut être supprimé que via l'interface d'Administration. - web/account/account.html.twig + web/account/account.html.twig phraseanet::account: A confirmation e-mail has been sent. Please follow the instructions contained to continue account deletion @@ -11540,32 +11560,32 @@ Si vous recevez cet e-mail sans l'avoir sollicité, merci de l'ignorer ou de le phraseanet::account: Are you sure you want to delete your account? Etes-vous sûr(e) de vouloir supprimer votre compte? - web/account/account.html.twig + web/account/account.html.twig phraseanet::account: Delete my account Supprimer mon compte - web/account/account.html.twig + web/account/account.html.twig phraseanet::account: I am agree to delete my account Je suis d'accord pour supprimer mon compte - web/account/account.html.twig + web/account/account.html.twig phraseanet::account: I am agree to delete my account, need confirmation on mail Je suis d'accord pour supprimer mon compte et j'ai besoin d'une confirmation par email - web/account/account.html.twig + web/account/account.html.twig phraseanet::account: List of data to be deleted Liste des données à supprimer - web/account/account.html.twig + web/account/account.html.twig phraseanet::account: My phraseanet account Mon compte Phraseanet - web/account/account.html.twig + web/account/account.html.twig phraseanet::chargement @@ -11599,12 +11619,12 @@ Si vous recevez cet e-mail sans l'avoir sollicité, merci de l'ignorer ou de le Echec du serveur de mails Controller/Root/AccountController.php Controller/Root/AccountController.php - Controller/Root/AccountController.php + Controller/Root/AccountController.php phraseanet::error: failed to revoke some user access Erreur pendant la révocation de droits utilisateur - Controller/Root/AccountController.php + Controller/Root/AccountController.php phraseanet::jours:: dimanche diff --git a/resources/locales/messages.nl.xlf b/resources/locales/messages.nl.xlf index e25f78b98b..0a84b67147 100644 --- a/resources/locales/messages.nl.xlf +++ b/resources/locales/messages.nl.xlf @@ -1,6 +1,6 @@ - +
The source node in most cases contains the sample message as written by the developer. If it looks like a dot-delimitted string such as "form.label.firstname", then the developer has not provided a default message. @@ -9138,7 +9138,27 @@ admin::workermanager: Rabbit config error admin::workermanager: Rabbit config error - admin/worker-manager/index.html.twig + admin/worker-manager/index.html.twig + + + admin::workermanager:tab:Reminder: Interval in second + admin::workermanager:tab:Reminder: Interval in second + admin/worker-manager/worker_validation_reminder.html.twig + + + admin::workermanager:tab:Reminder: Start + admin::workermanager:tab:Reminder: Start + admin/worker-manager/worker_validation_reminder.html.twig + + + admin::workermanager:tab:Reminder: Stop + admin::workermanager:tab:Reminder: Stop + admin/worker-manager/worker_validation_reminder.html.twig + + + admin::workermanager:tab:Reminder: description + admin::workermanager:tab:Reminder: description + admin/worker-manager/worker_validation_reminder.html.twig admin::workermanager:tab:configuration: title @@ -9253,6 +9273,11 @@ admin::workermanager:tab:queueMonitor: title admin::workermanager:tab:queueMonitor: title + admin/worker-manager/index.html.twig + + + admin::workermanager:tab:reminder: title + admin::workermanager:tab:reminder: title admin/worker-manager/index.html.twig @@ -9634,7 +9659,7 @@ admin:worker Retrieve configuration error admin:worker Retrieve configuration error - admin/worker-manager/index.html.twig + admin/worker-manager/index.html.twig alert @@ -10825,7 +10850,7 @@ login::notification: Changements enregistres Veranderingen zijn bewaard - Controller/Root/AccountController.php + Controller/Root/AccountController.php login::notification: Mise a jour du mot de passe avec succes @@ -11527,7 +11552,7 @@ phraseanet::account The account has been deleted phraseanet::account The account has been deleted - Controller/Root/AccountController.php + Controller/Root/AccountController.php > ]]> @@ -11601,12 +11626,12 @@ De email server is mislukt Controller/Root/AccountController.php Controller/Root/AccountController.php - Controller/Root/AccountController.php + Controller/Root/AccountController.php phraseanet::error: failed to revoke some user access phraseanet::error: failed to revoke some user access - Controller/Root/AccountController.php + Controller/Root/AccountController.php phraseanet::jours:: dimanche diff --git a/resources/locales/validators.de.xlf b/resources/locales/validators.de.xlf index 169456ad77..9730763588 100644 --- a/resources/locales/validators.de.xlf +++ b/resources/locales/validators.de.xlf @@ -1,6 +1,6 @@ - +
The source node in most cases contains the sample message as written by the developer. If it looks like a dot-delimitted string such as "form.label.firstname", then the developer has not provided a default message. diff --git a/resources/locales/validators.en.xlf b/resources/locales/validators.en.xlf index 18537bcc6e..dd16962b89 100644 --- a/resources/locales/validators.en.xlf +++ b/resources/locales/validators.en.xlf @@ -1,6 +1,6 @@ - +
The source node in most cases contains the sample message as written by the developer. If it looks like a dot-delimitted string such as "form.label.firstname", then the developer has not provided a default message. diff --git a/resources/locales/validators.fr.xlf b/resources/locales/validators.fr.xlf index 6eab348ff8..7d0d88a503 100644 --- a/resources/locales/validators.fr.xlf +++ b/resources/locales/validators.fr.xlf @@ -1,6 +1,6 @@ - +
The source node in most cases contains the sample message as written by the developer. If it looks like a dot-delimitted string such as "form.label.firstname", then the developer has not provided a default message. diff --git a/resources/locales/validators.nl.xlf b/resources/locales/validators.nl.xlf index 2741bef602..343a715c91 100644 --- a/resources/locales/validators.nl.xlf +++ b/resources/locales/validators.nl.xlf @@ -1,6 +1,6 @@ - +
The source node in most cases contains the sample message as written by the developer. If it looks like a dot-delimitted string such as "form.label.firstname", then the developer has not provided a default message. diff --git a/templates/web/admin/worker-manager/index.html.twig b/templates/web/admin/worker-manager/index.html.twig index 77056fc325..556dbc059f 100644 --- a/templates/web/admin/worker-manager/index.html.twig +++ b/templates/web/admin/worker-manager/index.html.twig @@ -40,6 +40,11 @@ {{ 'admin::workermanager:tab:metadata: title' |trans }} +