diff --git a/Dockerfile b/Dockerfile
index 8231d48455..f01f4d8197 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -209,7 +209,7 @@ CMD ["php-fpm", "-F"]
FROM phraseanet-fpm as phraseanet-worker
ENTRYPOINT ["docker/phraseanet/worker/entrypoint.sh"]
-CMD ["bin/console", "task-manager:scheduler:run"]
+CMD ["bin/console", "worker:execute"]
#########################################################################
# phraseanet-nginx
diff --git a/bin/console b/bin/console
index c49fc89052..744c7c7959 100755
--- a/bin/console
+++ b/bin/console
@@ -58,6 +58,9 @@ use Alchemy\Phrasea\Command\User\UserPasswordCommand;
use Alchemy\Phrasea\Command\User\UserListCommand;
use Alchemy\Phrasea\Command\UpgradeDBDatas;
use Alchemy\Phrasea\Command\ApplyRightsCommand;
+use Alchemy\Phrasea\WorkerManager\Command\WorkerExecuteCommand;
+use Alchemy\Phrasea\WorkerManager\Command\WorkerRunServiceCommand;
+use Alchemy\Phrasea\WorkerManager\Command\WorkerShowConfigCommand;
require_once __DIR__ . '/../lib/autoload.php';
@@ -162,9 +165,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(new WorkerExecuteCommand());
+$cli->command(new WorkerRunServiceCommand());
+$cli->command(new WorkerShowConfigCommand());
$cli->loadPlugins();
diff --git a/composer.json b/composer.json
index 0fd22effb0..d6bff1dc85 100644
--- a/composer.json
+++ b/composer.json
@@ -133,7 +133,8 @@
"facebook/graph-sdk": "^5.6",
"box/spout": "^2.7",
"paragonie/random-lib": "^2.0",
- "czproject/git-php": "^3.17"
+ "czproject/git-php": "^3.17",
+ "php-amqplib/php-amqplib": "^2.9"
},
"require-dev": {
"mikey179/vfsstream": "~1.5",
diff --git a/composer.lock b/composer.lock
index 112a4ecd0f..9f559ecf9e 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "008ff0b5d3d13b4f0ce5d34348ded83a",
+ "content-hash": "d986d21a2ad9125f83251d4c3943ffd7",
"packages": [
{
"name": "alchemy-fr/tcpdf-clone",
@@ -5145,6 +5145,83 @@
],
"time": "2019-03-20T17:19:05+00:00"
},
+ {
+ "name": "php-amqplib/php-amqplib",
+ "version": "v2.11.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-amqplib/php-amqplib.git",
+ "reference": "6353c5d2d3021a301914bc6566e695c99cfeb742"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-amqplib/php-amqplib/zipball/6353c5d2d3021a301914bc6566e695c99cfeb742",
+ "reference": "6353c5d2d3021a301914bc6566e695c99cfeb742",
+ "shasum": ""
+ },
+ "require": {
+ "ext-mbstring": "*",
+ "ext-sockets": "*",
+ "php": ">=5.6.3",
+ "phpseclib/phpseclib": "^2.0.0"
+ },
+ "conflict": {
+ "php": "7.4.0 - 7.4.1"
+ },
+ "replace": {
+ "videlalvaro/php-amqplib": "self.version"
+ },
+ "require-dev": {
+ "ext-curl": "*",
+ "nategood/httpful": "^0.2.20",
+ "phpunit/phpunit": "^5.7|^6.5|^7.0",
+ "squizlabs/php_codesniffer": "^2.5"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.11-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "PhpAmqpLib\\": "PhpAmqpLib/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "LGPL-2.1-or-later"
+ ],
+ "authors": [
+ {
+ "name": "Alvaro Videla",
+ "role": "Original Maintainer"
+ },
+ {
+ "name": "Raúl Araya",
+ "email": "nubeiro@gmail.com",
+ "role": "Maintainer"
+ },
+ {
+ "name": "Luke Bakken",
+ "email": "luke@bakken.io",
+ "role": "Maintainer"
+ },
+ {
+ "name": "Ramūnas Dronga",
+ "email": "github@ramuno.lt",
+ "role": "Maintainer"
+ }
+ ],
+ "description": "Formerly videlalvaro/php-amqplib. This library is a pure PHP implementation of the AMQP protocol. It's been tested against RabbitMQ.",
+ "homepage": "https://github.com/php-amqplib/php-amqplib/",
+ "keywords": [
+ "message",
+ "queue",
+ "rabbitmq"
+ ],
+ "time": "2020-05-13T13:56:11+00:00"
+ },
{
"name": "php-ffmpeg/php-ffmpeg",
"version": "v0.15",
@@ -7978,39 +8055,6 @@
],
"time": "2016-11-25T06:54:22+00:00"
},
- {
- "name": "phpexiftool/exiftool",
- "version": "10.10",
- "source": {
- "type": "git",
- "url": "https://github.com/alchemy-fr/exiftool.git",
- "reference": "0833cab894c890353192a83011428525a318bedf"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/alchemy-fr/exiftool/zipball/0833cab894c890353192a83011428525a318bedf",
- "reference": "0833cab894c890353192a83011428525a318bedf",
- "shasum": ""
- },
- "type": "library",
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "Perl Licensing"
- ],
- "authors": [
- {
- "name": "Phil Harvey",
- "email": "phil@owl.phy.queensu.ca",
- "homepage": "http://www.sno.phy.queensu.ca/~phil/exiftool/"
- }
- ],
- "description": "Exiftool is a library for reading, writing and editing meta information. This package is not PHP, but required for the main PHP driver : PHP Exiftool",
- "keywords": [
- "exiftool",
- "metadatas"
- ],
- "time": "2016-01-25T11:10:14+00:00"
- },
{
"name": "phpspec/prophecy",
"version": "v1.6.2",
diff --git a/config/configuration.sample.yml b/config/configuration.sample.yml
index a912e61876..968d309c88 100644
--- a/config/configuration.sample.yml
+++ b/config/configuration.sample.yml
@@ -313,6 +313,7 @@ geocoding-providers:
- '2.335062'
default-zoom: 5
marker-default-zoom: 9
+ position-fields: []
geonames-field-mapping: true
cityfields: City, Ville
provincefields: Province
@@ -330,3 +331,5 @@ workers:
user_account:
deleting_policies:
email_confirmation: true
+
+Console_logger_enabled_environments: [test]
diff --git a/lib/Alchemy/Phrasea/Application.php b/lib/Alchemy/Phrasea/Application.php
index 13c7728f86..4d0d2ab2d5 100644
--- a/lib/Alchemy/Phrasea/Application.php
+++ b/lib/Alchemy/Phrasea/Application.php
@@ -87,6 +87,8 @@ use Alchemy\Phrasea\Media\MediaAccessorResolver;
use Alchemy\Phrasea\Media\PermalinkMediaResolver;
use Alchemy\Phrasea\Media\TechnicalDataServiceProvider;
use Alchemy\Phrasea\Model\Entities\User;
+use Alchemy\Phrasea\WorkerManager\Provider\AlchemyWorkerServiceProvider;
+use Alchemy\Phrasea\WorkerManager\Provider\QueueWorkerServiceProvider;
use Alchemy\QueueProvider\QueueServiceProvider;
use Alchemy\WorkerProvider\WorkerServiceProvider;
use Doctrine\DBAL\Event\ConnectionEventArgs;
@@ -268,6 +270,11 @@ class Application extends SilexApplication
$this->register(new OrderServiceProvider());
$this->register(new WebhookServiceProvider());
+ if ($this['configuration.store']->isSetup()) {
+ $this->register(new QueueWorkerServiceProvider());
+ $this->register(new AlchemyWorkerServiceProvider());
+ }
+
$this['monolog'] = $this->share(
$this->extend('monolog', function (LoggerInterface $logger, Application $app) {
diff --git a/lib/Alchemy/Phrasea/Application/RouteLoader.php b/lib/Alchemy/Phrasea/Application/RouteLoader.php
index 2542ff25d1..fc1b25e042 100644
--- a/lib/Alchemy/Phrasea/Application/RouteLoader.php
+++ b/lib/Alchemy/Phrasea/Application/RouteLoader.php
@@ -6,6 +6,7 @@ use Alchemy\EmbedProvider\EmbedServiceProvider;
use Alchemy\Phrasea\Application;
use Alchemy\Phrasea\ControllerProvider as Providers;
use Alchemy\Phrasea\Report\ControllerProvider\ProdReportControllerProvider;
+use Alchemy\Phrasea\WorkerManager\Provider\ControllerServiceProvider as WorkerManagerProvider;
use Assert\Assertion;
use Silex\ControllerProviderInterface;
@@ -28,6 +29,7 @@ class RouteLoader
'/admin/setup' => Providers\Admin\Setup::class,
'/admin/subdefs' => Providers\Admin\Subdefs::class,
'/admin/task-manager' => Providers\Admin\TaskManager::class,
+ '/admin/worker-manager' => WorkerManagerProvider::class,
'/admin/users' => Providers\Admin\Users::class,
'/client/' => Providers\Client\Root::class,
'/datafiles' => Providers\Datafiles::class,
diff --git a/lib/Alchemy/Phrasea/Controller/Admin/RootController.php b/lib/Alchemy/Phrasea/Controller/Admin/RootController.php
index 1d46c27b15..295633f4dc 100644
--- a/lib/Alchemy/Phrasea/Controller/Admin/RootController.php
+++ b/lib/Alchemy/Phrasea/Controller/Admin/RootController.php
@@ -368,6 +368,7 @@ class RootController extends Controller
'collection',
'user',
'users',
+ 'workermanager'
];
$feature = 'connected';
diff --git a/lib/Alchemy/Phrasea/Controller/Api/V1Controller.php b/lib/Alchemy/Phrasea/Controller/Api/V1Controller.php
index 42339169d6..cd445c9e8f 100644
--- a/lib/Alchemy/Phrasea/Controller/Api/V1Controller.php
+++ b/lib/Alchemy/Phrasea/Controller/Api/V1Controller.php
@@ -87,6 +87,8 @@ use Alchemy\Phrasea\SearchEngine\SearchEngineResult;
use Alchemy\Phrasea\Status\StatusStructure;
use Alchemy\Phrasea\TaskManager\LiveInformation;
use Alchemy\Phrasea\Utilities\NullableDateTime;
+use Alchemy\Phrasea\WorkerManager\Event\AssetsCreateEvent;
+use Alchemy\Phrasea\WorkerManager\Event\WorkerEvents;
use Doctrine\ORM\EntityManager;
use Guzzle\Http\Client as Guzzle;
use League\Fractal\Resource\Item;
@@ -217,6 +219,30 @@ class V1Controller extends Controller
return $this->showTaskAction($request, $task);
}
+ /**
+ * Use with the uploader service
+ * @param Request $request
+ * @return Response
+ */
+ public function sendAssetsInQueue(Request $request)
+ {
+ $jsonBodyHelper = $this->getJsonBodyHelper();
+ $schema = $this->app['json-schema.ref_resolver']->resolve($this->app['json-schema.base_uri']. 'assets_enqueue.json');
+ $data = $request->getContent();
+
+ $errors = $jsonBodyHelper->validateJson(json_decode($data), $schema);
+
+ if (count($errors) > 0) {
+ return Result::createError($request, 422, $errors[0])->createResponse();
+ }
+
+ $this->dispatch(WorkerEvents::ASSETS_CREATE, new AssetsCreateEvent(json_decode($data, true)));
+
+ return Result::create($request, [
+ "data" => json_decode($data),
+ ])->createResponse();
+ }
+
private function getCacheInformation()
{
$caches = [
diff --git a/lib/Alchemy/Phrasea/ControllerProvider/Api/V1.php b/lib/Alchemy/Phrasea/ControllerProvider/Api/V1.php
index d64689eca2..bdc7a8cd50 100644
--- a/lib/Alchemy/Phrasea/ControllerProvider/Api/V1.php
+++ b/lib/Alchemy/Phrasea/ControllerProvider/Api/V1.php
@@ -284,6 +284,9 @@ class V1 extends Api implements ControllerProviderInterface, ServiceProviderInte
$controllers->post('/accounts/unlock/{token}/', 'controller.api.v1:unlockAccount')
->before('controller.api.v1:ensureUserManagementRights');
+ // the api route for the uploader service
+ $controllers->post('/upload/enqueue/', 'controller.api.v1:sendAssetsInQueue');
+
return $controllers;
}
}
diff --git a/lib/Alchemy/Phrasea/ControllerProvider/ControllerProviderServiceProvider.php b/lib/Alchemy/Phrasea/ControllerProvider/ControllerProviderServiceProvider.php
index 4cbec52624..39e59ed7fd 100644
--- a/lib/Alchemy/Phrasea/ControllerProvider/ControllerProviderServiceProvider.php
+++ b/lib/Alchemy/Phrasea/ControllerProvider/ControllerProviderServiceProvider.php
@@ -54,6 +54,7 @@ class ControllerProviderServiceProvider implements ServiceProviderInterface
Admin\Setup::class => [],
Admin\Subdefs::class => [],
Admin\TaskManager::class => [],
+ \Alchemy\Phrasea\WorkerManager\Provider\ControllerServiceProvider::class => [],
Admin\Users::class => [],
Client\Root::class => [],
Datafiles::class => [],
diff --git a/lib/Alchemy/Phrasea/Core/Configuration/DisplaySettingService.php b/lib/Alchemy/Phrasea/Core/Configuration/DisplaySettingService.php
index 24fcff9b2c..11987a9d5c 100644
--- a/lib/Alchemy/Phrasea/Core/Configuration/DisplaySettingService.php
+++ b/lib/Alchemy/Phrasea/Core/Configuration/DisplaySettingService.php
@@ -50,7 +50,8 @@ class DisplaySettingService
'bask_val_order' => 'nat',
'basket_caption_display' => '0',
'basket_status_display' => '0',
- 'basket_title_display' => '0'
+ 'basket_title_display' => '0',
+ 'basket_type_display' => '0'
];
/**
diff --git a/lib/Alchemy/Phrasea/Core/Configuration/RegistryFormManipulator.php b/lib/Alchemy/Phrasea/Core/Configuration/RegistryFormManipulator.php
index 9ebd69c829..786e244c09 100644
--- a/lib/Alchemy/Phrasea/Core/Configuration/RegistryFormManipulator.php
+++ b/lib/Alchemy/Phrasea/Core/Configuration/RegistryFormManipulator.php
@@ -181,19 +181,14 @@ class RegistryFormManipulator
],
'custom-links' => [
[
- 'linkName' => 'Phraseanet store',
- 'linkLanguage' => 'fr',
- 'linkUrl' => 'https://alchemy.odoo.com/shop',
- 'linkLocation' => 'help-menu',
- 'linkOrder' => '1',
- ],
- [
- 'linkName' => 'Phraseanet store',
- 'linkLanguage' => 'en',
- 'linkUrl' => 'https://alchemy.odoo.com/en_US/shop',
- 'linkLocation' => 'help-menu',
- 'linkOrder' => '1',
- ],
+ 'linkName' => 'Phraseanet store',
+ 'linkLanguage' => 'all',
+ 'linkUrl' => 'https://store.alchemy.fr',
+ 'linkLocation' => 'help-menu',
+ 'linkOrder' => 1,
+ 'linkBold' => false,
+ 'linkColor' => ''
+ ]
]
];
}
diff --git a/lib/Alchemy/Phrasea/Core/Event/Subscriber/ExportSubscriber.php b/lib/Alchemy/Phrasea/Core/Event/Subscriber/ExportSubscriber.php
index d3c05a64b6..85bc845be2 100644
--- a/lib/Alchemy/Phrasea/Core/Event/Subscriber/ExportSubscriber.php
+++ b/lib/Alchemy/Phrasea/Core/Event/Subscriber/ExportSubscriber.php
@@ -47,65 +47,10 @@ 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_CREATE => 'onCreateExportMail',
];
}
}
diff --git a/lib/Alchemy/Phrasea/Core/Event/Subscriber/RecordEditSubscriber.php b/lib/Alchemy/Phrasea/Core/Event/Subscriber/RecordEditSubscriber.php
index 8307749eb5..51a6f49af0 100644
--- a/lib/Alchemy/Phrasea/Core/Event/Subscriber/RecordEditSubscriber.php
+++ b/lib/Alchemy/Phrasea/Core/Event/Subscriber/RecordEditSubscriber.php
@@ -33,7 +33,6 @@ class RecordEditSubscriber implements EventSubscriberInterface
RecordEvents::ROTATE => 'onRecordChange',
RecordEvents::COLLECTION_CHANGED => 'onCollectionChanged',
RecordEvents::SUBDEFINITION_CREATE => 'onSubdefinitionCreate',
- RecordEvents::DELETE => 'onDelete',
);
}
@@ -59,12 +58,6 @@ class RecordEditSubscriber implements EventSubscriberInterface
$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/Provider/RepositoriesServiceProvider.php b/lib/Alchemy/Phrasea/Core/Provider/RepositoriesServiceProvider.php
index b4d4e42fc7..72f64fa52c 100644
--- a/lib/Alchemy/Phrasea/Core/Provider/RepositoriesServiceProvider.php
+++ b/lib/Alchemy/Phrasea/Core/Provider/RepositoriesServiceProvider.php
@@ -147,6 +147,15 @@ class RepositoriesServiceProvider implements ServiceProviderInterface
$app['repo.webhook-delivery'] = $app->share(function (PhraseaApplication $app) {
return $app['orm.em']->getRepository('Phraseanet:WebhookEventDelivery');
});
+ $app['repo.worker-running-job'] = $app->share(function (PhraseaApplication $app) {
+ return $app['orm.em']->getRepository('Phraseanet:WorkerRunningJob');
+ });
+ $app['repo.worker-running-populate'] = $app->share(function (PhraseaApplication $app) {
+ return $app['orm.em']->getRepository('Phraseanet:WorkerRunningPopulate');
+ });
+ $app['repo.worker-running-uploader'] = $app->share(function (PhraseaApplication $app) {
+ return $app['orm.em']->getRepository('Phraseanet:WorkerRunningUploader');
+ });
$app['repo.databoxes'] = $app->share(function (PhraseaApplication $app) {
$appbox = $app->getApplicationBox();
diff --git a/lib/Alchemy/Phrasea/Core/Provider/TwigServiceProvider.php b/lib/Alchemy/Phrasea/Core/Provider/TwigServiceProvider.php
index 9e4507ba8d..cfb66c192a 100644
--- a/lib/Alchemy/Phrasea/Core/Provider/TwigServiceProvider.php
+++ b/lib/Alchemy/Phrasea/Core/Provider/TwigServiceProvider.php
@@ -120,9 +120,9 @@ class TwigServiceProvider implements ServiceProviderInterface
$twig->addFilter(new \Twig_SimpleFilter('linkify', function (\Twig_Environment $twig, $string) use ($app) {
return preg_replace(
- "(([^']{1})((https?|file):((/{2,4})|(\\{2,4}))[\w:#%/;$()~_?/\-=\\\.&]*)([^']{1}))"
+ "/(\\W|^)(https?:\/{2,4}[\\w:#%\/;$()~_?\/\-=\\\.&]+)/m"
,
- '$1 $2 $7'
+ '$1$2 $7'
, $string
);
}, ['needs_environment' => true, 'is_safe' => ['html']]));
diff --git a/lib/Alchemy/Phrasea/Core/Version.php b/lib/Alchemy/Phrasea/Core/Version.php
index 82a64c0955..a0ab93df50 100644
--- a/lib/Alchemy/Phrasea/Core/Version.php
+++ b/lib/Alchemy/Phrasea/Core/Version.php
@@ -16,8 +16,8 @@ class Version
/**
* @var string
*/
-
- private $number = '4.1.0-alpha.26a';
+
+ private $number = '4.1.0-alpha.29a';
/**
* @var string
diff --git a/lib/Alchemy/Phrasea/Model/Entities/WorkerRunningJob.php b/lib/Alchemy/Phrasea/Model/Entities/WorkerRunningJob.php
new file mode 100644
index 0000000000..b94ae1514c
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Model/Entities/WorkerRunningJob.php
@@ -0,0 +1,239 @@
+id;
+ }
+
+ /**
+ * @param $databoxId
+ * @return $this
+ */
+ public function setDataboxId($databoxId)
+ {
+ $this->databoxId = $databoxId;
+
+ return $this;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getDataboxId()
+ {
+ return $this->databoxId;
+ }
+
+
+ /**
+ * @param $recordId
+ * @return $this
+ */
+ public function setRecordId($recordId)
+ {
+ $this->recordId = $recordId;
+
+ return $this;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getRecordId()
+ {
+ return $this->recordId;
+
+ }
+
+
+ /**
+ * @param $work
+ * @return $this
+ */
+ public function setWork($work)
+ {
+ $this->work = $work;
+
+ return $this;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getWork()
+ {
+ return $this->work;
+ }
+
+
+ /**
+ * @param $workOn
+ * @return $this
+ */
+ public function setWorkOn($workOn)
+ {
+ $this->workOn = $workOn;
+
+ return $this;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getWorkOn()
+ {
+ return $this->workOn;
+ }
+
+ /**
+ * @return \DateTime
+ */
+ public function getCreated()
+ {
+ return $this->created;
+ }
+
+ /**
+ * @param \DateTime $published
+ * @return $this
+ */
+ public function setPublished(\DateTime $published)
+ {
+ $this->published = $published;
+
+ return $this;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getPublished()
+ {
+ return $this->published;
+ }
+
+ /**
+ * @param \DateTime $finished
+ * @return $this
+ */
+ public function setFinished(\DateTime $finished)
+ {
+ $this->finished = $finished;
+
+ return $this;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getFinished()
+ {
+ return $this->finished;
+ }
+
+ /**
+ * @param $status
+ * @return $this
+ */
+ public function setStatus($status)
+ {
+ $this->status = $status;
+
+ return $this;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getStatus()
+ {
+ return $this->status;
+ }
+
+ public function getWorkName()
+ {
+ switch ($this->work) {
+ case PhraseaTokens::MAKE_SUBDEF:
+ return 'MAKE_SUBDEF';
+ case PhraseaTokens::WRITE_META_DOC:
+ return 'WRITE_META_DOC';
+ case PhraseaTokens::WRITE_META_SUBDEF:
+ return 'WRITE_META_SUBDEF';
+ default:
+ return $this->work;
+
+ }
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Model/Entities/WorkerRunningPopulate.php b/lib/Alchemy/Phrasea/Model/Entities/WorkerRunningPopulate.php
new file mode 100644
index 0000000000..08250e31e7
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Model/Entities/WorkerRunningPopulate.php
@@ -0,0 +1,219 @@
+id;
+ }
+
+ /**
+ * @param $host
+ * @return $this
+ */
+ public function setHost($host)
+ {
+ $this->host = $host;
+
+ return $this;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getHost()
+ {
+ return $this->host;
+ }
+
+ /**
+ * @param $port
+ * @return $this
+ */
+ public function setPort($port)
+ {
+ $this->port = $port;
+
+ return $this;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getPort()
+ {
+ return $this->port;
+ }
+
+ /**
+ * @param $indexName
+ * @return $this
+ */
+ public function setIndexName($indexName)
+ {
+ $this->indexName = $indexName;
+
+ return $this;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getIndexName()
+ {
+ return $this->indexName;
+ }
+
+ /**
+ * @param $databoxId
+ * @return $this
+ */
+ public function setDataboxId($databoxId)
+ {
+ $this->databoxId = $databoxId;
+
+ return $this;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getDataboxId()
+ {
+ return $this->databoxId;
+ }
+
+ /**
+ * @return \DateTime
+ */
+ public function getCreated()
+ {
+ return $this->created;
+ }
+
+ /**
+ * @param \DateTime $published
+ * @return $this
+ */
+ public function setPublished(\DateTime $published)
+ {
+ $this->published = $published;
+
+ return $this;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getPublished()
+ {
+ return $this->published;
+ }
+
+ /**
+ * @param \DateTime $finished
+ * @return $this
+ */
+ public function setFinished(\DateTime $finished)
+ {
+ $this->finished = $finished;
+
+ return $this;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getFinished()
+ {
+ return $this->finished;
+ }
+
+ /**
+ * @param $status
+ * @return $this
+ */
+ public function setStatus($status)
+ {
+ $this->status = $status;
+
+ return $this;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getStatus()
+ {
+ return $this->status;
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Model/Entities/WorkerRunningUploader.php b/lib/Alchemy/Phrasea/Model/Entities/WorkerRunningUploader.php
new file mode 100644
index 0000000000..df8b3ec0f8
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Model/Entities/WorkerRunningUploader.php
@@ -0,0 +1,198 @@
+id;
+ }
+
+ /**
+ * @param $commitId
+ * @return $this
+ */
+ public function setCommitId($commitId)
+ {
+ $this->commitId = $commitId;
+
+ return $this;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getCommitId()
+ {
+ return $this->commitId;
+ }
+
+ /**
+ * @param $assetId
+ * @return $this
+ */
+ public function setAssetId($assetId)
+ {
+ $this->assetId = $assetId;
+
+ return $this;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getAssetId()
+ {
+ return $this->assetId;
+ }
+
+ /**
+ * @return \DateTime
+ */
+ public function getCreated()
+ {
+ return $this->created;
+ }
+
+ /**
+ * @param \DateTime $published
+ * @return $this
+ */
+ public function setPublished(\DateTime $published)
+ {
+ $this->published = $published;
+
+ return $this;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getPublished()
+ {
+ return $this->published;
+ }
+
+ /**
+ * @param \DateTime $finished
+ * @return $this
+ */
+ public function setFinished(\DateTime $finished)
+ {
+ $this->finished = $finished;
+
+ return $this;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getFinished()
+ {
+ return $this->finished;
+ }
+
+ /**
+ * @param $status
+ * @return $this
+ */
+ public function setStatus($status)
+ {
+ $this->status = $status;
+
+ return $this;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getStatus()
+ {
+ return $this->status;
+ }
+
+ /**
+ * @param $type
+ * @return $this
+ */
+ public function setType($type)
+ {
+ $this->type = $type;
+
+ return $this;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getType()
+ {
+ return $this->type;
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Model/Repositories/WorkerRunningJobRepository.php b/lib/Alchemy/Phrasea/Model/Repositories/WorkerRunningJobRepository.php
new file mode 100644
index 0000000000..a3b584058d
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Model/Repositories/WorkerRunningJobRepository.php
@@ -0,0 +1,105 @@
+createResultSetMappingBuilder('w');
+ $rsm->addScalarResult('work_on','work_on');
+
+ $sql = 'SELECT work_on
+ FROM WorkerRunningJob
+ WHERE ((work & :write_meta) > 0 OR ((work & :make_subdef) > 0 AND work_on = :work_on) )
+ AND record_id = :record_id
+ AND databox_id = :databox_id
+ AND status = :status';
+
+ $query = $this->_em->createNativeQuery($sql, $rsm);
+ $query->setParameters([
+ 'write_meta' => PhraseaTokens::WRITE_META,
+ 'make_subdef'=> PhraseaTokens::MAKE_SUBDEF,
+ 'work_on' => $subdefName,
+ 'record_id' => $recordId,
+ 'databox_id' => $databoxId,
+ 'status' => WorkerRunningJob::RUNNING
+ ]
+ );
+
+ return count($query->getResult()) == 0;
+ }
+
+ /**
+ * return true if we can write meta
+ *
+ * @param $subdefName
+ * @param $recordId
+ * @param $databoxId
+ * @return bool
+ */
+ public function canWriteMetadata($subdefName, $recordId, $databoxId)
+ {
+ $rsm = $this->createResultSetMappingBuilder('w');
+ $rsm->addScalarResult('work_on','work_on');
+
+ $sql = 'SELECT work_on
+ FROM WorkerRunningJob
+ WHERE ((work & :make_subdef) > 0 OR ((work & :write_meta) > 0 AND work_on = :work_on) )
+ AND record_id = :record_id
+ AND databox_id = :databox_id
+ AND status = :status';
+
+ $query = $this->_em->createNativeQuery($sql, $rsm);
+ $query->setParameters([
+ 'make_subdef'=> PhraseaTokens::MAKE_SUBDEF,
+ 'write_meta' => PhraseaTokens::WRITE_META,
+ 'work_on' => $subdefName,
+ 'record_id' => $recordId,
+ 'databox_id' => $databoxId,
+ 'status' => WorkerRunningJob::RUNNING
+ ]
+ );
+
+ return count($query->getResult()) == 0;
+ }
+
+ public function truncateWorkerTable()
+ {
+ $connection = $this->_em->getConnection();
+ $platform = $connection->getDatabasePlatform();
+ $this->_em->beginTransaction();
+ try {
+ $connection->executeUpdate($platform->getTruncateTableSQL('WorkerRunningJob'));
+ } catch (\Exception $e) {
+ $this->_em->rollback();
+ }
+ }
+
+ public function deleteFinishedWorks()
+ {
+ $this->_em->beginTransaction();
+ try {
+ $this->_em->getConnection()->delete('WorkerRunningJob', ['status' => WorkerRunningJob::FINISHED]);
+ $this->_em->commit();
+ } catch (\Exception $e) {
+ $this->_em->rollback();
+ }
+ }
+
+ public function getEntityManager()
+ {
+ return parent::getEntityManager();
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Model/Repositories/WorkerRunningPopulateRepository.php b/lib/Alchemy/Phrasea/Model/Repositories/WorkerRunningPopulateRepository.php
new file mode 100644
index 0000000000..6e7063a20d
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Model/Repositories/WorkerRunningPopulateRepository.php
@@ -0,0 +1,29 @@
+createQueryBuilder('w');
+ $qb->where($qb->expr()->in('w.databoxId', $databoxIds))
+ ->andWhere('w.status = :status')
+ ->setParameter('status', WorkerRunningPopulate::RUNNING)
+ ;
+
+ return count($qb->getQuery()->getResult());
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Model/Repositories/WorkerRunningUploaderRepository.php b/lib/Alchemy/Phrasea/Model/Repositories/WorkerRunningUploaderRepository.php
new file mode 100644
index 0000000000..6af3580d0d
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Model/Repositories/WorkerRunningUploaderRepository.php
@@ -0,0 +1,35 @@
+createQueryBuilder('w');
+ $res = $qb
+ ->where('w.commitId = :commitId')
+ ->andWhere('w.status != :status')
+ ->setParameters([
+ 'commitId' => $commitId,
+ 'status' => WorkerRunningUploader::DOWNLOADED
+ ])
+ ->getQuery()
+ ->getResult()
+ ;
+
+ return count($res) == 0;
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Setup/Installer.php b/lib/Alchemy/Phrasea/Setup/Installer.php
index 74cb131e9b..0bd40fd5cd 100644
--- a/lib/Alchemy/Phrasea/Setup/Installer.php
+++ b/lib/Alchemy/Phrasea/Setup/Installer.php
@@ -198,7 +198,7 @@ class Installer
$config['main']['database']['driver'] = 'pdo_mysql';
$config['main']['database']['charset'] = 'UTF8';
- $config['main']['binaries'] = $binaryData;
+ $config['main']['binaries'] = array_merge($config['main']['binaries'], $binaryData);
$config['servername'] = $serverName;
$config['main']['key'] = $this->app['random.medium']->generateString(16);
diff --git a/lib/Alchemy/Phrasea/TaskManager/Job/ArchiveJob.php b/lib/Alchemy/Phrasea/TaskManager/Job/ArchiveJob.php
index de418a4e71..35bc878008 100644
--- a/lib/Alchemy/Phrasea/TaskManager/Job/ArchiveJob.php
+++ b/lib/Alchemy/Phrasea/TaskManager/Job/ArchiveJob.php
@@ -181,11 +181,12 @@ class ArchiveJob extends AbstractJob
$dom = new \DOMDocument();
$dom->formatOutput = true;
+ /** @var \DOMElement $root */
$root = $dom->appendChild($dom->createElement('root'));
$nnew = $this->listFilesPhase1($app, $dom, $root, $path_in, $server_coll_id, 0, $TColls);
if ($app['debug']) {
- $this->log('debug', "=========== listFilesPhase1 ========== (returned " . $nnew . ")\n" . $dom->saveXML());
+ $this->log('debug', "== listFilesPhase1 returned " . $nnew . ")\n" . $dom->saveXML());
}
if (!$this->isStarted()) {
@@ -193,15 +194,16 @@ class ArchiveJob extends AbstractJob
}
// wait for files to be cold
- $this->pause($cold);
-
- if (!$this->isStarted()) {
- return;
+ for($i=0; $i<($cold*2); $i++) {
+ if (!$this->isStarted()) {
+ return;
+ }
+ $this->pause(0.5);
}
$this->listFilesPhase2($app, $dom, $root, $path_in, 0);
if ($app['debug']) {
- $this->log('debug', "=========== listFilesPhase2 ========== : \n" . $dom->saveXML());
+ $this->log('debug', "== listFilesPhase2\n" . $dom->saveXML());
}
if (!$this->isStarted()) {
@@ -210,31 +212,35 @@ class ArchiveJob extends AbstractJob
$this->makePairs($dom, $root, $path_in, $path_archived, $path_error, false, 0, $tmask, $tmaskgrp);
if ($app['debug']) {
- $this->log('debug', "=========== makePairs ========== : \n" . $dom->saveXML());
- }
-
- $r = $this->removeBadGroups($app, $dom, $root, $path_in, $path_archived, $path_error, 0, $moveError);
- if ($app['debug']) {
- $this->log('debug', "=========== removeBadGroups ========== (returned " . ((Boolean) $r ? 'true' : 'false') . ") : \n" . $dom->saveXML());
- }
-
- $this->archive($app, $databox, $dom, $root, $path_in, $path_archived, $path_error, 0, $moveError, $moveArchived, $stat0, $stat1);
- if ($app['debug']) {
- $this->log('debug', "=========== archive ========== : \n" . $dom->saveXML());
+ $this->log('debug', "== makePairs\n" . $dom->saveXML());
}
if (!$this->isStarted()) {
return;
}
+ $this->removeBadGroups($app, $dom, $root, $path_in, $path_archived, $path_error, 0, $moveError);
+ if ($app['debug']) {
+ $this->log('debug', "== removeBadGroups\n" . $dom->saveXML());
+ }
+
+ if (!$this->isStarted()) {
+ return;
+ }
+
+ $this->archive($app, $databox, $dom, $root, $path_in, $path_archived, $path_error, 0, $moveError, $moveArchived, $stat0, $stat1);
+ if ($app['debug']) {
+ $this->log('debug', "== archive\n" . $dom->saveXML());
+ }
+
$this->bubbleResults($dom, $root, $path_in, 0, \p4field::isyes($settings->copy_spe));
if ($app['debug']) {
- $this->log('debug', "=========== bubbleResults ========== : \n" . $dom->saveXML());
+ $this->log('debug', "== bubbleResults\n" . $dom->saveXML());
}
$moved = $this->moveFiles($app, $dom, $root, $path_in, $path_archived, $path_error, 0, $moveArchived, $moveError);
if ($app['debug']) {
- $this->log('debug', "=========== moveFiles ========== (returned " . ($moved ? 'true' : 'false') . ") : \n" . $dom->saveXML());
+ $this->log('debug', "== moveFiles returned " . ($moved ? 'true' : 'false') . "\n" . $dom->saveXML());
}
}
}
@@ -243,15 +249,17 @@ class ArchiveJob extends AbstractJob
{
$nnew = 0;
- if (false !== $sxDotPhrasea = @simplexml_load_file($path . '/.phrasea.xml')) {
+ $magicfile = $magicmethod = null;
+
+ if (($sxDotPhrasea = @simplexml_load_file($path . '/.phrasea.xml')) !== false) {
// test for magic file
if (($magicfile = trim((string) ($sxDotPhrasea->magicfile))) != '') {
$magicmethod = strtoupper($sxDotPhrasea->magicfile['method']);
- if ($magicmethod == 'LOCK' && true === $app['filesystem']->exists($path . '/' . $magicfile)) {
- return;
- } elseif ($magicmethod == 'UNLOCK' && false === $app['filesystem']->exists($path . '/' . $magicfile)) {
- return;
+ if ($magicmethod == 'LOCK' && ($app['filesystem']->exists($path . '/' . $magicfile) === true)) {
+ return 0;
+ } elseif ($magicmethod == 'UNLOCK' && ($app['filesystem']->exists($path . '/' . $magicfile) === false)) {
+ return 0;
}
}
@@ -278,6 +286,7 @@ class ArchiveJob extends AbstractJob
continue;
}
+ /** @var \DOMElement $n */
if (is_dir($path . '/' . $file)) {
$n = $node->appendChild($dom->createElement('file'));
$n->setAttribute('isdir', '1');
@@ -293,6 +302,16 @@ class ArchiveJob extends AbstractJob
foreach (["size", "ctime", "mtime"] as $k) {
$n->setAttribute($k, $stat[$k]);
}
+ // special file
+ if($file == '.phrasea.xml') {
+ $n->setAttribute('match', '*');
+ }
+ // special file
+ if($file === $magicfile) {
+ $n->setAttribute('match', '*');
+ $node->setAttribute('magicfile', $magicfile);
+ $node->setAttribute('magicmethod', $magicmethod);
+ }
$nnew++;
}
$n->setAttribute('cid', $server_coll_id);
@@ -335,27 +354,31 @@ class ArchiveJob extends AbstractJob
$dnl = @$xp->query('./file[@name="' . $file . '"]', $node);
if ($dnl && $dnl->length == 0) {
if (is_dir($path . '/' . $file)) {
+ /** @var \DOMElement $n */
$n = $node->appendChild($dom->createElement('file'));
$n->setAttribute('isdir', '1');
$n->setAttribute('name', $file);
$nnew += $this->listFilesPhase2($app, $dom, $n, $path . '/' . $file, $depth + 1);
} else {
+ /** @var \DOMElement $n */
$n = $node->appendChild($dom->createElement('file'));
$n->setAttribute('name', $file);
$nnew++;
}
- $this->setBranchHot($dom, $n);
+ $this->setBranchHot($n);
} elseif ($dnl && $dnl->length == 1) {
- $dnl->item(0)->setAttribute('temperature', 'cold');
+ /** @var \DOMElement $n */
+ $n = $dnl->item(0);
+ $n->setAttribute('temperature', 'cold');
if (is_dir($path . '/' . $file)) {
- $this->listFilesPhase2($app, $dom, $dnl->item(0), $path . '/' . $file, $depth + 1);
+ $this->listFilesPhase2($app, $dom, $n, $path . '/' . $file, $depth + 1);
} else {
$stat = stat($path . '/' . $file);
foreach (["size", "ctime", "mtime"] as $k) {
- if ($dnl->item(0)->getAttribute($k) != $stat[$k]) {
- $this->setBranchHot($dom, $dnl->item(0));
+ if ($n->getAttribute($k) != $stat[$k]) {
+ $this->setBranchHot($n);
break;
}
}
@@ -399,13 +422,16 @@ class ArchiveJob extends AbstractJob
if ($dnl->length == 1) {
// this group is old (don't care about any linked files), just flag it
$n->setAttribute('grp', 'tocomplete');
- $dnl->item(0)->setAttribute('match', '*');
+ /** @var \DOMElement $_n */
+ $_n = $dnl->item(0);
+ $_n->setAttribute('match', '*');
// recurse only if group is ok
$this->makePairs($dom, $n, $path . '/' . $name, $path_archived, $path_error, true, $depth + 1, $tmask, $tmaskgrp);
} else {
// this group in new (to be created)
// do we need one (or both) linked file ? (caption or representation)
$err = false;
+ /** @var \DOMElement[] $flink */
$flink = ['caption' => null, 'representation' => null];
foreach ($flink as $linkName => $v) {
@@ -473,12 +499,12 @@ class ArchiveJob extends AbstractJob
// this is a file
if (!$n->getAttribute('match')) {
// because match can be set before
- if ($name == '.phrasea.xml') {
- // special file(s) always ok
- $n->setAttribute('match', '*');
- } else {
+// if ($name == '.phrasea.xml') {
+// // special file(s) always ok
+// $n->setAttribute('match', '*');
+// } else {
$this->checkMatch($dom, $n, $tmask);
- }
+// }
}
}
}
@@ -503,7 +529,7 @@ class ArchiveJob extends AbstractJob
// if root of hotfolder if hot, die...
if ($depth == 0 && $node->getAttribute('temperature') == 'hot') {
- return $ret;
+ return;
}
$nodesToDel = [];
@@ -522,7 +548,7 @@ class ArchiveJob extends AbstractJob
$name = $n->getAttribute('name');
if ($n->getAttribute('isdir')) {
- $ret |= $this->removeBadGroups($app, $dom, $n, $path . '/' . $name
+ $this->removeBadGroups($app, $dom, $n, $path . '/' . $name
, $path_archived . '/' . $name
, $path_error . '/' . $name
, $depth + 1, $moveError);
@@ -642,7 +668,7 @@ class ArchiveJob extends AbstractJob
}
if ($node->getAttribute('temperature') == 'hot') {
- return;
+ return 0;
}
$ret = 0;
@@ -758,7 +784,7 @@ class ArchiveJob extends AbstractJob
}
}
- if (!$n->getAttribute('keep')) {
+ if (!$n->getAttribute('keep') && !$n->getAttribute('match')) {
$this->log('debug', sprintf('delete \'%s\'', $path . '/' . $name));
try {
@@ -792,7 +818,9 @@ class ArchiveJob extends AbstractJob
if ($dnl->length == 1) {
// the caption file exists
$node->setAttribute('match', $captionFileName);
- $dnl->item(0)->setAttribute('match', '*');
+ /** @var \DOMElement $n */
+ $n = $dnl->item(0);
+ $n->setAttribute('match', '*');
} else {
// the caption file is missing
$node->setAttribute('match', '?');
@@ -817,7 +845,7 @@ class ArchiveJob extends AbstractJob
return ($f[0] == '.' && $f != '.phrasea.xml' && $f != '.grouping.xml') || $f == 'thumbs.db' || $f == 'par-system';
}
- private function setBranchHot(\DOMDocument $dom, \DOMElement $node)
+ private function setBranchHot(\DOMElement $node)
{
for ($n = $node; $n; $n = $n->parentNode) {
if ($n->nodeType == XML_ELEMENT_NODE) {
@@ -847,8 +875,10 @@ class ArchiveJob extends AbstractJob
if ($node->getAttribute('grp') == 'tocreate') {
$representationFileName = null;
+ /** @var \DOMElement $representationFileNode */
$representationFileNode = null;
$captionFileName = null;
+ /** @var \DOMElement $captionFileNode */
$captionFileNode = null;
$cid = $node->getAttribute('cid');
$genericdoc = null;
@@ -903,6 +933,7 @@ class ArchiveJob extends AbstractJob
}
file_put_contents($groupingFile, '');
+ /** @var \DOMElement $n */
$n = $node->appendChild($dom->createElement('file'));
$n->setAttribute('name', '.grouping.xml');
$n->setAttribute('temperature', 'cold');
@@ -1025,6 +1056,8 @@ class ArchiveJob extends AbstractJob
}
$story = \record_adapter::createStory($app, $collection);
+
+ $story->setStatus($status);
$app['subdef.substituer']->substituteDocument($story, $media);
$story->set_metadatas($metadatas->toMetadataArray($metadatasStructure), true);
@@ -1080,12 +1113,14 @@ class ArchiveJob extends AbstractJob
$file->addAttribute(new BorderAttribute\Status($app, $status));
- $file->addAttribute(new BorderAttribute\Metadata(new Metadata(new PhraseaTag\TfFilepath(), new MonoValue($media->getFile()->getRealPath()))));
- $file->addAttribute(new BorderAttribute\Metadata(new Metadata(new PhraseaTag\TfDirname(), new MonoValue(dirname($media->getFile()->getRealPath())))));
+ /** @var \MediaVorus\File $mediaFile */
+ $mediaFile = $media->getFile();
+ $file->addAttribute(new BorderAttribute\Metadata(new Metadata(new PhraseaTag\TfFilepath(), new MonoValue($mediaFile->getRealPath()))));
+ $file->addAttribute(new BorderAttribute\Metadata(new Metadata(new PhraseaTag\TfDirname(), new MonoValue(dirname($mediaFile->getRealPath())))));
- $file->addAttribute(new BorderAttribute\Metadata(new Metadata(new PhraseaTag\TfAtime(), new MonoValue($media->getFile()->getATime()))));
- $file->addAttribute(new BorderAttribute\Metadata(new Metadata(new PhraseaTag\TfMtime(), new MonoValue($media->getFile()->getMTime()))));
- $file->addAttribute(new BorderAttribute\Metadata(new Metadata(new PhraseaTag\TfCtime(), new MonoValue($media->getFile()->getCTime()))));
+ $file->addAttribute(new BorderAttribute\Metadata(new Metadata(new PhraseaTag\TfAtime(), new MonoValue($mediaFile->getATime()))));
+ $file->addAttribute(new BorderAttribute\Metadata(new Metadata(new PhraseaTag\TfMtime(), new MonoValue($mediaFile->getMTime()))));
+ $file->addAttribute(new BorderAttribute\Metadata(new Metadata(new PhraseaTag\TfCtime(), new MonoValue($mediaFile->getCTime()))));
foreach ($metadatas as $meta) {
$file->addAttribute(new BorderAttribute\Metadata($meta));
@@ -1102,8 +1137,13 @@ class ArchiveJob extends AbstractJob
$record = null;
$postProcess = function ($element, $visa, $code) use (&$record) {
- $record = $element;
- };
+ $r = isset($visa); // one way to avoid "variable not used" with phpstorm 10. ugly.
+ unset($r); //
+ $r = isset($code); // one way to avoid "variable not used" with phpstorm 10. ugly.
+ unset($r); //
+
+ $record = $element;
+ };
/** @var borderManager $borderManager */
$borderManager = $app['border-manager'];
@@ -1206,12 +1246,13 @@ class ArchiveJob extends AbstractJob
}
}
- $this->archiveFileAndCaption($app, $databox, $dom, $node, $captionFileNode, $path, $path_archived, $path_error, $grp_rid, $nodesToDel, $stat0, $stat1, $moveError, $moveArchived);
+ $this->archiveFileAndCaption($app, $databox, $node, $captionFileNode, $path, $path_archived, $path_error, $grp_rid, $nodesToDel, $stat0, $stat1, $moveError, $moveArchived);
}
/**
*
- * @param \DOMDOcument $dom
+ * @param Application $app
+ * @param \databox $databox
* @param \DOMElement $node
* @param \DOMElement $captionFileNode
* @param string $path
@@ -1219,8 +1260,12 @@ class ArchiveJob extends AbstractJob
* @param string $path_error
* @param integer $grp_rid
* @param array $nodesToDel out, filled with files to delete
+ * @param $stat0
+ * @param $stat1
+ * @param $moveError
+ * @param $moveArchived
*/
- private function archiveFileAndCaption(Application $app, \databox $databox, \DOMDocument $dom, \DOMElement $node, \DOMElement $captionFileNode = null, $path, $path_archived, $path_error, $grp_rid, array &$nodesToDel, $stat0, $stat1, $moveError, $moveArchived)
+ private function archiveFileAndCaption(Application $app, \databox $databox, \DOMElement $node, \DOMElement $captionFileNode = null, $path, $path_archived, $path_error, $grp_rid, array &$nodesToDel, $stat0, $stat1, $moveError, $moveArchived)
{
// quick fix to reconnect if mysql is lost
$app->getApplicationBox()->get_connection();
@@ -1422,6 +1467,7 @@ class ArchiveJob extends AbstractJob
{
$ret = new MetadataBag();
+ /** @var \databox_field $databox_field */
foreach ($metadatasStructure as $databox_field) {
if ($bag->containsKey($databox_field->get_tag()->getTagname())) {
$ret->set($databox_field->get_name(), $bag->get($databox_field->get_tag()->getTagname()));
diff --git a/lib/Alchemy/Phrasea/WorkerManager/Command/WorkerExecuteCommand.php b/lib/Alchemy/Phrasea/WorkerManager/Command/WorkerExecuteCommand.php
new file mode 100644
index 0000000000..70e7b41f05
--- /dev/null
+++ b/lib/Alchemy/Phrasea/WorkerManager/Command/WorkerExecuteCommand.php
@@ -0,0 +1,91 @@
+setDescription('Listen queues define on configuration, launch corresponding service for execution')
+ ->addOption('preserve-payload', 'p', InputOption::VALUE_NONE, 'Preserve temporary payload file')
+ ->addOption('queue-name', '', InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'The name of queues to be consuming')
+ ->addOption('max-processes', 'm', InputOption::VALUE_REQUIRED, 'The max number of process allow to run (default 4) ')
+ ->addOption('MWG', '', InputOption::VALUE_NONE, 'Enable MWG metadata compatibility (use only for write metadata service)')
+ ->addOption('clear-metadatas', '', InputOption::VALUE_NONE, 'Delete metadatas from documents if not compliant with Database structure (use only for write metadata service)')
+ ->setHelp('');
+
+ return $this;
+ }
+
+ protected function doExecute(InputInterface $input, OutputInterface $output)
+ {
+ $MWG = false;
+ $clearMetadatas = false;
+
+ $argQueueName = $input->getOption('queue-name');
+ $maxProcesses = intval($input->getOption('max-processes'));
+
+ /** @var AMQPConnection $serverConnection */
+ $serverConnection = $this->container['alchemy_worker.amqp.connection'];
+
+ /** @var AMQPChannel $channel */
+ $channel = $serverConnection->getChannel();
+
+ if ($channel == null) {
+ $output->writeln("Can't connect to rabbit, check configuration!");
+
+ return;
+ }
+
+ $serverConnection->declareExchange();
+
+ /** @var WorkerInvoker $workerInvoker */
+ $workerInvoker = $this->container['alchemy_worker.worker_invoker'];
+
+ if ($input->getOption('max-processes') != null && $maxProcesses == 0) {
+ $output->writeln('Invalid max-processes option.Need an integer');
+
+ return;
+ } elseif($maxProcesses) {
+ $workerInvoker->setMaxProcessPoolValue($maxProcesses);
+ }
+
+ if ($input->getOption('MWG')) {
+ $MWG = true;
+ }
+
+ if ($input->getOption('clear-metadatas')) {
+ $clearMetadatas = true;
+ }
+
+ if ($input->getOption('preserve-payload')) {
+ $workerInvoker->preservePayloads();
+ }
+
+ /** @var MessageHandler $messageHandler */
+ $messageHandler = $this->container['alchemy_worker.message.handler'];
+ $messageHandler->consume($serverConnection, $workerInvoker, $argQueueName, $maxProcesses);
+
+ while (count($channel->callbacks)) {
+ $output->writeln("[*] Waiting for messages. To exit press CTRL+C");
+ $channel->wait();
+ }
+
+ $serverConnection->connectionClose();
+ }
+
+}
diff --git a/lib/Alchemy/Phrasea/WorkerManager/Command/WorkerRunServiceCommand.php b/lib/Alchemy/Phrasea/WorkerManager/Command/WorkerRunServiceCommand.php
new file mode 100644
index 0000000000..d757bc47be
--- /dev/null
+++ b/lib/Alchemy/Phrasea/WorkerManager/Command/WorkerRunServiceCommand.php
@@ -0,0 +1,56 @@
+setDescription('Execute a service')
+ ->addArgument('type')
+ ->addArgument('body')
+ ->addOption('preserve-payload', 'p', InputOption::VALUE_NONE, 'Preserve temporary payload file');
+
+ return $this;
+ }
+
+ protected function doExecute(InputInterface $input, OutputInterface $output)
+ {
+ /** @var WorkerResolverInterface $workerResolver */
+ $workerResolver = $this->container['alchemy_worker.type_based_worker_resolver'];
+
+ $type = $input->getArgument('type');
+ $body = file_get_contents($input->getArgument('body'));
+
+ if ($body === false) {
+ $output->writeln('Unable to read payload file');
+
+ return;
+ }
+
+ $body = json_decode($body, true);
+
+ if (json_last_error() !== JSON_ERROR_NONE) {
+ $output->writeln('Invalid message body');
+
+ return;
+ }
+
+ $worker = $workerResolver->getWorker($type, $body);
+
+ $worker->process($body);
+
+ if (! $input->getOption('preserve-payload')) {
+ unlink($input->getArgument('body'));
+ }
+
+ }
+}
diff --git a/lib/Alchemy/Phrasea/WorkerManager/Command/WorkerShowConfigCommand.php b/lib/Alchemy/Phrasea/WorkerManager/Command/WorkerShowConfigCommand.php
new file mode 100644
index 0000000000..98f13063de
--- /dev/null
+++ b/lib/Alchemy/Phrasea/WorkerManager/Command/WorkerShowConfigCommand.php
@@ -0,0 +1,27 @@
+setDescription('Show queues configuration');
+ }
+
+ public function doExecute(InputInterface $input, OutputInterface $output)
+ {
+ $serverConfiguration = $this->container['conf']->get(['workers', 'queue', 'worker-queue']);
+
+ $output->writeln(['', 'Configured server: ']);
+
+ $output->writeln(['Rabbit Server : ' . Yaml::dump($serverConfiguration, 0), '']);
+ }
+}
diff --git a/lib/Alchemy/Phrasea/WorkerManager/Controller/AdminConfigurationController.php b/lib/Alchemy/Phrasea/WorkerManager/Controller/AdminConfigurationController.php
new file mode 100644
index 0000000000..debc443095
--- /dev/null
+++ b/lib/Alchemy/Phrasea/WorkerManager/Controller/AdminConfigurationController.php
@@ -0,0 +1,224 @@
+app['alchemy_worker.amqp.connection'];
+
+ /** @var WorkerRunningJobRepository $repoWorker */
+ $repoWorker = $app['repo.worker-running-job'];
+
+ return $this->render('admin/worker-manager/index.html.twig', [
+ 'isConnected' => ($serverConnection->getChannel() != null) ? true : false,
+ 'workerRunningJob' => $repoWorker->findAll(),
+ ]);
+ }
+
+ /**
+ * @param PhraseaApplication $app
+ * @param Request $request
+ * @return mixed
+ */
+ public function configurationAction(PhraseaApplication $app, Request $request)
+ {
+ $retryQueueConfig = $this->getRetryQueueConfiguration();
+
+ $form = $app->form(new WorkerConfigurationType(), $retryQueueConfig);
+
+ $form->handleRequest($request);
+
+ if ($form->isValid()) {
+ // save config in file
+ $app['conf']->set(['workers', 'retry_queue'], $form->getData());
+
+ $queues = array_intersect_key(AMQPConnection::$defaultQueues, $retryQueueConfig);
+ $retryQueuesToReset = array_intersect_key(AMQPConnection::$defaultRetryQueues, array_flip($queues));
+
+ /** @var AMQPConnection $serverConnection */
+ $serverConnection = $this->app['alchemy_worker.amqp.connection'];
+ // change the queue TTL
+ $serverConnection->reinitializeQueue($retryQueuesToReset);
+ $serverConnection->reinitializeQueue(AMQPConnection::$defaultDelayedQueues);
+
+ return $app->redirectPath('worker_admin');
+ }
+
+ return $this->render('admin/worker-manager/worker_configuration.html.twig', [
+ 'form' => $form->createView()
+ ]);
+ }
+
+ public function infoAction(PhraseaApplication $app, Request $request)
+ {
+ /** @var WorkerRunningJobRepository $repoWorker */
+ $repoWorker = $app['repo.worker-running-job'];
+
+ $workerRunningJob = [];
+
+ $reload = ($request->query->get('reload')) == 1 ? true : false ;
+
+ if ($request->query->get('running') == 1 && $request->query->get('finished') == 1) {
+ $workerRunningJob = $repoWorker->findAll();
+ } elseif ($request->query->get('running') == 1) {
+ $workerRunningJob = $repoWorker->findBy(['status' => WorkerRunningJob::RUNNING]);
+ } elseif ($request->query->get('finished') == 1) {
+ $workerRunningJob = $repoWorker->findBy(['status' => WorkerRunningJob::FINISHED]);
+ }
+
+ return $this->render('admin/worker-manager/worker_info.html.twig', [
+ 'workerRunningJob' => $workerRunningJob,
+ 'reload' => $reload
+ ]);
+ }
+
+ public function truncateTableAction(PhraseaApplication $app, Request $request)
+ {
+ /** @var WorkerRunningJobRepository $repoWorker */
+ $repoWorker = $app['repo.worker-running-job'];
+ $repoWorker->truncateWorkerTable();
+
+ return $app->redirectPath('worker_admin');
+ }
+
+ public function deleteFinishedAction(PhraseaApplication $app, Request $request)
+ {
+ /** @var WorkerRunningJobRepository $repoWorker */
+ $repoWorker = $app['repo.worker-running-job'];
+ $repoWorker->deleteFinishedWorks();
+
+ return $app->redirectPath('worker_admin');
+ }
+
+ public function searchengineAction(PhraseaApplication $app, Request $request)
+ {
+ $options = $this->getElasticsearchOptions();
+
+ $form = $app->form(new WorkerSearchengineType(), $options);
+
+ $form->handleRequest($request);
+
+ if ($form->isValid()) {
+ $populateInfo = $this->getData($form);
+
+ $this->getDispatcher()->dispatch(WorkerEvents::POPULATE_INDEX, new PopulateIndexEvent($populateInfo));
+
+ return $app->redirectPath('worker_admin');
+ }
+
+ return $this->render('admin/worker-manager/worker_searchengine.html.twig', [
+ 'form' => $form->createView()
+ ]);
+ }
+
+ public function subviewAction(PhraseaApplication $app)
+ {
+ return $this->render('admin/worker-manager/worker_subview.html.twig', [
+ ]);
+ }
+
+ public function metadataAction(PhraseaApplication $app)
+ {
+ return $this->render('admin/worker-manager/worker_metadata.html.twig', [
+ ]);
+ }
+
+ public function populateStatusAction(PhraseaApplication $app, Request $request)
+ {
+ $databoxIds = $request->get('sbasIds');
+
+ /** @var WorkerRunningPopulateRepository $repoWorkerPopulate */
+ $repoWorkerPopulate = $app['repo.worker-running-populate'];
+
+ return $repoWorkerPopulate->checkPopulateStatusByDataboxIds($databoxIds);
+ }
+
+ public function pullAssetsAction(PhraseaApplication $app, Request $request)
+ {
+ $pullAssetsConfig = $this->getPullAssetsConfiguration();
+ $form = $app->form(new WorkerPullAssetsType(), $pullAssetsConfig);
+
+ $form->handleRequest($request);
+ if ($form->isValid()) {
+ /** @var AMQPConnection $serverConnection */
+ $serverConnection = $this->app['alchemy_worker.amqp.connection'];
+ $serverConnection->setQueue(MessagePublisher::PULL_QUEUE);
+
+ // save new pull config
+ $app['conf']->set(['workers', 'pull_assets'], array_merge($pullAssetsConfig, $form->getData()));
+
+ // reinitialize the pull queues
+ $serverConnection->reinitializeQueue([MessagePublisher::PULL_QUEUE]);
+ $this->app['alchemy_worker.message.publisher']->initializePullAssets();
+
+ return $app->redirectPath('worker_admin');
+ }
+
+ return $this->render('admin/worker-manager/worker_pull_assets.html.twig', [
+ 'form' => $form->createView()
+ ]);
+ }
+
+ /**
+ * @return EventDispatcherInterface
+ */
+ private function getDispatcher()
+ {
+ return $this->app['dispatcher'];
+ }
+
+ /**
+ * @return ElasticsearchOptions
+ */
+ private function getElasticsearchOptions()
+ {
+ return $this->app['elasticsearch.options'];
+ }
+
+ /**
+ * @param FormInterface $form
+ * @return array
+ */
+ private function getData(FormInterface $form)
+ {
+ /** @var ElasticsearchOptions $options */
+ $options = $form->getData();
+
+ $data['host'] = $options->getHost();
+ $data['port'] = $options->getPort();
+ $data['indexName'] = $options->getIndexName();
+ $data['databoxIds'] = $form->getExtraData()['sbas'];
+
+ return $data;
+ }
+
+ private function getPullAssetsConfiguration()
+ {
+ return $this->app['conf']->get(['workers', 'pull_assets'], []);
+ }
+
+ private function getRetryQueueConfiguration()
+ {
+ return $this->app['conf']->get(['workers', 'retry_queue'], []);
+ }
+}
diff --git a/lib/Alchemy/Phrasea/WorkerManager/Event/AssetsCreateEvent.php b/lib/Alchemy/Phrasea/WorkerManager/Event/AssetsCreateEvent.php
new file mode 100644
index 0000000000..7cd5856374
--- /dev/null
+++ b/lib/Alchemy/Phrasea/WorkerManager/Event/AssetsCreateEvent.php
@@ -0,0 +1,21 @@
+data = $data;
+ }
+
+ public function getData()
+ {
+ return $this->data;
+ }
+
+}
diff --git a/lib/Alchemy/Phrasea/WorkerManager/Event/AssetsCreationFailureEvent.php b/lib/Alchemy/Phrasea/WorkerManager/Event/AssetsCreationFailureEvent.php
new file mode 100644
index 0000000000..2e26d250e3
--- /dev/null
+++ b/lib/Alchemy/Phrasea/WorkerManager/Event/AssetsCreationFailureEvent.php
@@ -0,0 +1,34 @@
+payload = $payload;
+ $this->workerMessage = $workerMessage;
+ $this->count = $count;
+ }
+
+ public function getPayload()
+ {
+ return $this->payload;
+ }
+
+ public function getWorkerMessage()
+ {
+ return $this->workerMessage;
+ }
+
+ public function getCount()
+ {
+ return $this->count;
+ }
+}
diff --git a/lib/Alchemy/Phrasea/WorkerManager/Event/AssetsCreationRecordFailureEvent.php b/lib/Alchemy/Phrasea/WorkerManager/Event/AssetsCreationRecordFailureEvent.php
new file mode 100644
index 0000000000..8e76fc4394
--- /dev/null
+++ b/lib/Alchemy/Phrasea/WorkerManager/Event/AssetsCreationRecordFailureEvent.php
@@ -0,0 +1,35 @@
+payload = $payload;
+ $this->workerMessage = $workerMessage;
+ $this->count = $count;
+ }
+
+ public function getPayload()
+ {
+ return $this->payload;
+ }
+
+ public function getWorkerMessage()
+ {
+ return $this->workerMessage;
+ }
+
+ public function getCount()
+ {
+ return $this->count;
+ }
+}
diff --git a/lib/Alchemy/Phrasea/WorkerManager/Event/ExportMailFailureEvent.php b/lib/Alchemy/Phrasea/WorkerManager/Event/ExportMailFailureEvent.php
new file mode 100644
index 0000000000..30b03a011a
--- /dev/null
+++ b/lib/Alchemy/Phrasea/WorkerManager/Event/ExportMailFailureEvent.php
@@ -0,0 +1,55 @@
+emitterUserId = $emitterUserId;
+ $this->tokenValue = $tokenValue;
+ $this->destinationMails = $destinationMails;
+ $this->params = $params;
+ $this->workerMessage = $workerMessage;
+ $this->count = $count;
+ }
+
+ public function getEmitterUserId()
+ {
+ return $this->emitterUserId;
+ }
+
+ public function getTokenValue()
+ {
+ return $this->tokenValue;
+ }
+
+ public function getDestinationMails()
+ {
+ return $this->destinationMails;
+ }
+
+ public function getParams()
+ {
+ return $this->params;
+ }
+
+ public function getWorkerMessage()
+ {
+ return $this->workerMessage;
+ }
+
+ public function getCount()
+ {
+ return $this->count;
+ }
+}
diff --git a/lib/Alchemy/Phrasea/WorkerManager/Event/PopulateIndexEvent.php b/lib/Alchemy/Phrasea/WorkerManager/Event/PopulateIndexEvent.php
new file mode 100644
index 0000000000..2287c9b5c5
--- /dev/null
+++ b/lib/Alchemy/Phrasea/WorkerManager/Event/PopulateIndexEvent.php
@@ -0,0 +1,25 @@
+data = $data;
+ }
+
+ /**
+ * @return array
+ */
+ public function getData()
+ {
+ return $this->data;
+ }
+
+}
diff --git a/lib/Alchemy/Phrasea/WorkerManager/Event/PopulateIndexFailureEvent.php b/lib/Alchemy/Phrasea/WorkerManager/Event/PopulateIndexFailureEvent.php
new file mode 100644
index 0000000000..34c112e227
--- /dev/null
+++ b/lib/Alchemy/Phrasea/WorkerManager/Event/PopulateIndexFailureEvent.php
@@ -0,0 +1,55 @@
+host = $host;
+ $this->port = $port;
+ $this->indexName = $indexName;
+ $this->databoxId = $databoxId;
+ $this->workerMessage = $workerMessage;
+ $this->count = $count;
+ }
+
+ public function getHost()
+ {
+ return $this->host;
+ }
+
+ public function getPort()
+ {
+ return $this->port;
+ }
+
+ public function getIndexName()
+ {
+ return $this->indexName;
+ }
+
+ public function getDataboxId()
+ {
+ return $this->databoxId;
+ }
+
+ public function getWorkerMessage()
+ {
+ return $this->workerMessage;
+ }
+
+ public function getCount()
+ {
+ return $this->count;
+ }
+}
diff --git a/lib/Alchemy/Phrasea/WorkerManager/Event/StoryCreateCoverEvent.php b/lib/Alchemy/Phrasea/WorkerManager/Event/StoryCreateCoverEvent.php
new file mode 100644
index 0000000000..965f22eec6
--- /dev/null
+++ b/lib/Alchemy/Phrasea/WorkerManager/Event/StoryCreateCoverEvent.php
@@ -0,0 +1,21 @@
+data = $data;
+ }
+
+ public function getData()
+ {
+ return $this->data;
+ }
+
+}
diff --git a/lib/Alchemy/Phrasea/WorkerManager/Event/SubdefinitionCreationFailureEvent.php b/lib/Alchemy/Phrasea/WorkerManager/Event/SubdefinitionCreationFailureEvent.php
new file mode 100644
index 0000000000..1628c76bb1
--- /dev/null
+++ b/lib/Alchemy/Phrasea/WorkerManager/Event/SubdefinitionCreationFailureEvent.php
@@ -0,0 +1,37 @@
+subdefName = $subdefName;
+ $this->workerMessage = $workerMessage;
+ $this->count = $count;
+ }
+
+ public function getSubdefName()
+ {
+ return $this->subdefName;
+ }
+
+ public function getWorkerMessage()
+ {
+ return $this->workerMessage;
+ }
+
+ public function getCount()
+ {
+ return $this->count;
+ }
+}
diff --git a/lib/Alchemy/Phrasea/WorkerManager/Event/SubdefinitionWritemetaEvent.php b/lib/Alchemy/Phrasea/WorkerManager/Event/SubdefinitionWritemetaEvent.php
new file mode 100644
index 0000000000..6949f2a54b
--- /dev/null
+++ b/lib/Alchemy/Phrasea/WorkerManager/Event/SubdefinitionWritemetaEvent.php
@@ -0,0 +1,47 @@
+subdefName = $subdefName;
+ $this->status = $status;
+ $this->workerMessage = $workerMessage;
+ $this->count = $count;
+ }
+
+ public function getSubdefName()
+ {
+ return $this->subdefName;
+ }
+
+ public function getStatus()
+ {
+ return $this->status;
+ }
+
+ public function getWorkerMessage()
+ {
+ return $this->workerMessage;
+ }
+
+ public function getCount()
+ {
+ return $this->count;
+ }
+}
diff --git a/lib/Alchemy/Phrasea/WorkerManager/Event/WebhookDeliverFailureEvent.php b/lib/Alchemy/Phrasea/WorkerManager/Event/WebhookDeliverFailureEvent.php
new file mode 100644
index 0000000000..f31790cad3
--- /dev/null
+++ b/lib/Alchemy/Phrasea/WorkerManager/Event/WebhookDeliverFailureEvent.php
@@ -0,0 +1,41 @@
+webhookEventId = $webhookEventId;
+ $this->workerMessage = $workerMessage;
+ $this->count = $count;
+ $this->deleveryId = $deleveryId;
+ }
+
+ public function getWebhookEventId()
+ {
+ return $this->webhookEventId;
+ }
+
+ public function getWorkerMessage()
+ {
+ return $this->workerMessage;
+ }
+
+ public function getCount()
+ {
+ return $this->count;
+ }
+
+ public function getDeleveryId()
+ {
+ return $this->deleveryId;
+ }
+}
diff --git a/lib/Alchemy/Phrasea/WorkerManager/Event/WorkerEvents.php b/lib/Alchemy/Phrasea/WorkerManager/Event/WorkerEvents.php
new file mode 100644
index 0000000000..d195369a4a
--- /dev/null
+++ b/lib/Alchemy/Phrasea/WorkerManager/Event/WorkerEvents.php
@@ -0,0 +1,22 @@
+add(MessagePublisher::ASSETS_INGEST_TYPE, 'text', [
+ 'label' => 'admin::workermanager:tab:workerconfig: Ingest retry delay in ms'
+ ])
+ ->add(MessagePublisher::CREATE_RECORD_TYPE, 'text', [
+ 'label' => 'admin::workermanager:tab:workerconfig: Create record retry delay in ms'
+ ])
+ ->add(MessagePublisher::SUBDEF_CREATION_TYPE, 'text', [
+ 'label' => 'admin::workermanager:tab:workerconfig: Subdefinition retry delay in ms'
+ ])
+ ->add(MessagePublisher::WRITE_METADATAS_TYPE, 'text', [
+ 'label' => 'admin::workermanager:tab:workerconfig: Metadatas retry delay in ms'
+ ])
+ ->add(MessagePublisher::WEBHOOK_TYPE, 'text', [
+ 'label' => 'admin::workermanager:tab:workerconfig: Webhook retry delay in ms'
+ ])
+ ->add(MessagePublisher::EXPORT_MAIL_TYPE, 'text', [
+ 'label' => 'admin::workermanager:tab:workerconfig: Export mail retry delay in ms'
+ ])
+ ->add(MessagePublisher::POPULATE_INDEX_TYPE, 'text', [
+ 'label' => 'admin::workermanager:tab:workerconfig: Populate Index retry delay in ms'
+ ])
+ ->add('delayedSubdef', 'text', [
+ 'label' => 'admin::workermanager:tab:workerconfig: Subdef delay in ms'
+ ])
+ ->add('delayedWriteMeta', 'text', [
+ 'label' => 'admin::workermanager:tab:workerconfig: Write meta delay in ms'
+ ])
+ ;
+ }
+
+ public function getName()
+ {
+ return 'worker_configuration';
+ }
+}
diff --git a/lib/Alchemy/Phrasea/WorkerManager/Form/WorkerPullAssetsType.php b/lib/Alchemy/Phrasea/WorkerManager/Form/WorkerPullAssetsType.php
new file mode 100644
index 0000000000..511c3e22c7
--- /dev/null
+++ b/lib/Alchemy/Phrasea/WorkerManager/Form/WorkerPullAssetsType.php
@@ -0,0 +1,37 @@
+add('endpointCommit', 'text', [
+ 'label' => 'admin::workermanager:tab:pullassets: Endpoint get commit'
+ ])
+ ->add('endpointToken', 'text', [
+ 'label' => 'admin::workermanager:tab:pullassets: Endpoint get token'
+ ])
+ ->add('clientSecret', 'text', [
+ 'label' => 'admin::workermanager:tab:pullassets: Client secret'
+ ])
+ ->add('clientId', 'text', [
+ 'label' => 'admin::workermanager:tab:pullassets: Client ID'
+ ])
+ ->add('pullInterval', 'text', [
+ 'label' => 'admin::workermanager:tab:pullassets: Fetching interval in second'
+ ])
+ ;
+ }
+
+ public function getName()
+ {
+ return 'worker_pullAssets';
+ }
+}
diff --git a/lib/Alchemy/Phrasea/WorkerManager/Form/WorkerSearchengineType.php b/lib/Alchemy/Phrasea/WorkerManager/Form/WorkerSearchengineType.php
new file mode 100644
index 0000000000..2f86bdda7a
--- /dev/null
+++ b/lib/Alchemy/Phrasea/WorkerManager/Form/WorkerSearchengineType.php
@@ -0,0 +1,48 @@
+add('host', 'text', [
+ 'label' => 'admin::workermanager:tab:searchengine: Elasticsearch server host',
+ 'constraints' => new NotBlank(),
+ ])
+ ->add('port', 'integer', [
+ 'label' => 'admin::workermanager:tab:searchengine: Elasticsearch service port',
+ 'constraints' => [
+ new Range(['min' => 1, 'max' => 65535]),
+ new NotBlank()
+ ]
+ ])
+ ->add('indexName', 'text', [
+ 'label' => 'admin::workermanager:tab:searchengine: Elasticsearch index name',
+ 'constraints' => new NotBlank(),
+ 'attr' =>['data-class'=>'inline']
+ ])
+ ;
+ }
+
+ public function setDefaultOptions(OptionsResolverInterface $resolver)
+ {
+ $resolver->setDefaults([
+ 'allow_extra_fields' => true
+ ]);
+ }
+
+ public function getName()
+ {
+ return 'worker_searchengine';
+ }
+}
diff --git a/lib/Alchemy/Phrasea/WorkerManager/Provider/AlchemyWorkerServiceProvider.php b/lib/Alchemy/Phrasea/WorkerManager/Provider/AlchemyWorkerServiceProvider.php
new file mode 100644
index 0000000000..ecabd28649
--- /dev/null
+++ b/lib/Alchemy/Phrasea/WorkerManager/Provider/AlchemyWorkerServiceProvider.php
@@ -0,0 +1,148 @@
+share(function () {
+ return new TypeBasedWorkerResolver();
+ });
+
+ $app['alchemy_worker.logger'] = $app->share(function (Application $app) {
+ $logger = new $app['monolog.logger.class']('alchemy-service logger');
+ $logger->pushHandler(new RotatingFileHandler(
+ $app['log.path'] . DIRECTORY_SEPARATOR . 'worker_service.log',
+ 10,
+ Logger::INFO
+ ));
+
+ return $logger;
+ });
+
+ // use the console logger
+ $loggerSetter = function (LoggerAwareInterface $loggerAware) use ($app) {
+ if (isset($app['logger'])) {
+ $loggerAware->setLogger($app['logger']);
+ }
+
+ return $loggerAware;
+ };
+
+ $app['alchemy_worker.process_pool'] = $app->share(function (Application $app) use ($loggerSetter) {
+ return $loggerSetter(new ProcessPool());
+ });
+
+ $app['alchemy_worker.worker_invoker'] = $app->share(function (Application $app) use ($loggerSetter) {
+ return $loggerSetter(new WorkerInvoker($app['alchemy_worker.process_pool']));
+ });
+
+
+ // register workers
+ $app['alchemy_worker.type_based_worker_resolver']->addFactory(MessagePublisher::SUBDEF_CREATION_TYPE, new CallableWorkerFactory(function () use ($app) {
+ return (new SubdefCreationWorker(
+ $app['subdef.generator'],
+ $app['alchemy_worker.message.publisher'],
+ $app['alchemy_worker.logger'],
+ $app['dispatcher'],
+ $app['phraseanet.filesystem'],
+ $app['repo.worker-running-job'],
+ $app['elasticsearch.indexer']
+ ))
+ ->setApplicationBox($app['phraseanet.appbox'])
+ ;
+ }));
+
+ $app['alchemy_worker.type_based_worker_resolver']->addFactory(MessagePublisher::WRITE_METADATAS_TYPE, new CallableWorkerFactory(function () use ($app) {
+ return (new WriteMetadatasWorker(
+ $app['exiftool.writer'],
+ $app['alchemy_worker.logger'],
+ $app['alchemy_worker.message.publisher'],
+ $app['repo.worker-running-job']
+ ))
+ ->setApplicationBox($app['phraseanet.appbox'])
+ ->setDispatcher($app['dispatcher'])
+ ->setEntityManagerLocator(new LazyLocator($app, 'orm.em'))
+ ;
+ }));
+
+ $app['alchemy_worker.type_based_worker_resolver']->addFactory(MessagePublisher::EXPORT_MAIL_TYPE, new CallableWorkerFactory(function () use ($app) {
+ return (new ExportMailWorker($app))
+ ->setDelivererLocator(new LazyLocator($app, 'notification.deliverer'));
+ }));
+
+ $app['alchemy_worker.type_based_worker_resolver']->addFactory(MessagePublisher::ASSETS_INGEST_TYPE, new CallableWorkerFactory(function () use ($app) {
+ return (new AssetsIngestWorker($app))
+ ->setEntityManagerLocator(new LazyLocator($app, 'orm.em'));
+ }));
+
+ $app['alchemy_worker.type_based_worker_resolver']->addFactory(MessagePublisher::WEBHOOK_TYPE, new CallableWorkerFactory(function () use ($app) {
+ return (new WebhookWorker($app))
+ ->setDispatcher($app['dispatcher']);
+ }));
+
+ $app['alchemy_worker.type_based_worker_resolver']->addFactory(MessagePublisher::CREATE_RECORD_TYPE, new CallableWorkerFactory(function () use ($app) {
+ return (new CreateRecordWorker($app))
+ ->setApplicationBox($app['phraseanet.appbox'])
+ ->setBorderManagerLocator(new LazyLocator($app, 'border-manager'))
+ ->setEntityManagerLocator(new LazyLocator($app, 'orm.em'))
+ ->setFileSystemLocator(new LazyLocator($app, 'filesystem'))
+ ->setTemporaryFileSystemLocator(new LazyLocator($app, 'temporary-filesystem'))
+ ->setDispatcher($app['dispatcher']);
+ }));
+
+ $app['alchemy_worker.type_based_worker_resolver']->addFactory(MessagePublisher::POPULATE_INDEX_TYPE, new CallableWorkerFactory(function () use ($app) {
+ return (new PopulateIndexWorker($app['alchemy_worker.message.publisher'], $app['elasticsearch.indexer'], $app['repo.worker-running-populate']))
+ ->setApplicationBox($app['phraseanet.appbox'])
+ ->setDispatcher($app['dispatcher']);
+ }));
+
+ $app['alchemy_worker.type_based_worker_resolver']->addFactory(MessagePublisher::PULL_ASSETS_TYPE, new CallableWorkerFactory(function () use ($app) {
+ return new PullAssetsWorker($app['alchemy_worker.message.publisher'], $app['conf'], $app['repo.worker-running-uploader']);
+ }));
+
+ $app['alchemy_worker.type_based_worker_resolver']->addFactory(MessagePublisher::DELETE_RECORD_TYPE, new CallableWorkerFactory(function () use ($app) {
+ return (new DeleteRecordWorker())
+ ->setApplicationBox($app['phraseanet.appbox']);
+ }));
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function boot(Application $app)
+ {
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function create(PhraseaApplication $app)
+ {
+ return new static();
+ }
+
+}
diff --git a/lib/Alchemy/Phrasea/WorkerManager/Provider/ControllerServiceProvider.php b/lib/Alchemy/Phrasea/WorkerManager/Provider/ControllerServiceProvider.php
new file mode 100644
index 0000000000..7fdc988b93
--- /dev/null
+++ b/lib/Alchemy/Phrasea/WorkerManager/Provider/ControllerServiceProvider.php
@@ -0,0 +1,115 @@
+share(function (PhraseaApplication $app) {
+ return new AdminConfigurationController($app);
+ });
+
+ // example of route to check webhook
+ $app->post('/webhook', array($this, 'getWebhookData'));
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function boot(Application $app)
+ {
+ }
+
+ public function connect(Application $app)
+ {
+ $controllers = $this->createAuthenticatedCollection($app);
+ $firewall = $this->getFirewall($app);
+
+ $controllers->before(function () use ($firewall) {
+ $firewall->requireRight(\ACL::TASKMANAGER);
+ });
+
+ $controllers->match('/', 'controller.worker.admin.configuration:indexAction')
+ ->method('GET')
+ ->bind('worker_admin');
+
+ $controllers->match('/configuration', 'controller.worker.admin.configuration:configurationAction')
+ ->method('GET|POST')
+ ->bind('worker_admin_configuration');
+
+ $controllers->match('/info', 'controller.worker.admin.configuration:infoAction')
+ ->method('GET')
+ ->bind('worker_admin_info');
+
+ $controllers->match('/truncate', 'controller.worker.admin.configuration:truncateTableAction')
+ ->method('POST')
+ ->bind('worker_admin_truncate');
+
+ $controllers->match('/delete-finished', 'controller.worker.admin.configuration:deleteFinishedAction')
+ ->method('POST')
+ ->bind('worker_admin_delete_finished');
+
+ $controllers->match('/searchengine', 'controller.worker.admin.configuration:searchengineAction')
+ ->method('GET|POST')
+ ->bind('worker_admin_searchengine');
+
+ $controllers->match('/subview', 'controller.worker.admin.configuration:subviewAction')
+ ->method('GET|POST')
+ ->bind('worker_admin_subview');
+
+ $controllers->match('/metadata', 'controller.worker.admin.configuration:metadataAction')
+ ->method('GET|POST')
+ ->bind('worker_admin_metadata');
+
+ $controllers->get('/populate-status', 'controller.worker.admin.configuration:populateStatusAction')
+ ->bind('worker_admin_populate_status');
+
+ $controllers->match('/pull-assets', 'controller.worker.admin.configuration:pullAssetsAction')
+ ->method('GET|POST')
+ ->bind('worker_admin_pullAssets');
+
+ return $controllers;
+ }
+
+ public function getWebhookData(Application $app, Request $request)
+ {
+ $messagePubliser = $this->getMessagePublisher($app);
+ $messagePubliser->pushLog("RECEIVED ON phraseanet WEBHOOK URL TEST = ". $request->getUri() . " DATA : ". $request->getContent());
+
+ return 0;
+ }
+
+ /**
+ * @param Application $app
+ * @return Firewall
+ */
+ private function getFirewall(Application $app)
+ {
+ return $app['firewall'];
+ }
+
+ /**
+ * @param Application $app
+ * @return MessagePublisher
+ */
+ private function getMessagePublisher(Application $app)
+ {
+ return $app['alchemy_worker.message.publisher'];
+ }
+}
diff --git a/lib/Alchemy/Phrasea/WorkerManager/Provider/QueueWorkerServiceProvider.php b/lib/Alchemy/Phrasea/WorkerManager/Provider/QueueWorkerServiceProvider.php
new file mode 100644
index 0000000000..7de8ef2604
--- /dev/null
+++ b/lib/Alchemy/Phrasea/WorkerManager/Provider/QueueWorkerServiceProvider.php
@@ -0,0 +1,94 @@
+share(function (Application $app) {
+ return new AMQPConnection($app['conf']);
+ });
+
+ $app['alchemy_worker.message.handler'] = $app->share(function (Application $app) {
+ return new MessageHandler($app['alchemy_worker.message.publisher']);
+ });
+
+ $app['alchemy_worker.message.publisher'] = $app->share(function (Application $app) {
+ return new MessagePublisher($app['alchemy_worker.amqp.connection'], $app['alchemy_worker.logger']);
+ });
+
+ $app['alchemy_worker.webhook.publisher'] = $app->share(function (Application $app) {
+ return new WebhookPublisher($app['alchemy_worker.message.publisher']);
+ });
+
+ $app['manipulator.webhook-event'] = $app->share(function (Application $app) {
+ return new WebhookEventManipulator(
+ $app['orm.em'],
+ $app['repo.webhook-event'],
+ $app['alchemy_worker.webhook.publisher']
+ );
+ });
+
+ $app['dispatcher'] = $app->share(
+ $app->extend('dispatcher', function (EventDispatcherInterface $dispatcher, Application $app) {
+
+ $dispatcher->addSubscriber(
+ new RecordSubscriber($app, new LazyLocator($app, 'phraseanet.appbox'))
+ );
+ $dispatcher->addSubscriber(new ExportSubscriber($app['alchemy_worker.message.publisher']));
+ $dispatcher->addSubscriber(new AssetsIngestSubscriber($app['alchemy_worker.message.publisher']));
+ $dispatcher->addSubscriber(new SearchengineSubscriber($app['alchemy_worker.message.publisher']));
+ $dispatcher->addSubscriber(new WebhookSubscriber($app['alchemy_worker.message.publisher']));
+
+ return $dispatcher;
+ })
+ );
+
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function boot(Application $app)
+ {
+
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function create(PhraseaApplication $app)
+ {
+ return new static();
+ }
+
+}
diff --git a/lib/Alchemy/Phrasea/WorkerManager/Queue/AMQPConnection.php b/lib/Alchemy/Phrasea/WorkerManager/Queue/AMQPConnection.php
new file mode 100644
index 0000000000..24de790286
--- /dev/null
+++ b/lib/Alchemy/Phrasea/WorkerManager/Queue/AMQPConnection.php
@@ -0,0 +1,259 @@
+ MessagePublisher::METADATAS_QUEUE,
+ MessagePublisher::SUBDEF_CREATION_TYPE => MessagePublisher::SUBDEF_QUEUE,
+ MessagePublisher::EXPORT_MAIL_TYPE => MessagePublisher::EXPORT_QUEUE,
+ 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::POPULATE_INDEX_TYPE => MessagePublisher::POPULATE_INDEX_QUEUE,
+ MessagePublisher::DELETE_RECORD_TYPE => MessagePublisher::DELETE_RECORD_QUEUE
+ ];
+
+ // the corresponding worker queues and retry queues, loop queue
+ public static $defaultRetryQueues = [
+ MessagePublisher::METADATAS_QUEUE => MessagePublisher::RETRY_METADATAS_QUEUE,
+ MessagePublisher::SUBDEF_QUEUE => MessagePublisher::RETRY_SUBDEF_QUEUE,
+ MessagePublisher::EXPORT_QUEUE => MessagePublisher::RETRY_EXPORT_QUEUE,
+ MessagePublisher::WEBHOOK_QUEUE => MessagePublisher::RETRY_WEBHOOK_QUEUE,
+ MessagePublisher::ASSETS_INGEST_QUEUE => MessagePublisher::RETRY_ASSETS_INGEST_QUEUE,
+ 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
+ ];
+
+ // default message TTL in retry queue in millisecond
+ public static $defaultFailedQueues = [
+ MessagePublisher::WRITE_METADATAS_TYPE => MessagePublisher::FAILED_METADATAS_QUEUE,
+ MessagePublisher::SUBDEF_CREATION_TYPE => MessagePublisher::FAILED_SUBDEF_QUEUE,
+ MessagePublisher::EXPORT_MAIL_TYPE => MessagePublisher::FAILED_EXPORT_QUEUE,
+ MessagePublisher::WEBHOOK_TYPE => MessagePublisher::FAILED_WEBHOOK_QUEUE,
+ MessagePublisher::ASSETS_INGEST_TYPE => MessagePublisher::FAILED_ASSETS_INGEST_QUEUE,
+ MessagePublisher::CREATE_RECORD_TYPE => MessagePublisher::FAILED_CREATE_RECORD_QUEUE,
+ MessagePublisher::POPULATE_INDEX_TYPE => MessagePublisher::FAILED_POPULATE_INDEX_QUEUE
+ ];
+
+ public static $defaultDelayedQueues = [
+ MessagePublisher::METADATAS_QUEUE => MessagePublisher::DELAYED_METADATAS_QUEUE,
+ MessagePublisher::SUBDEF_QUEUE => MessagePublisher::DELAYED_SUBDEF_QUEUE
+ ];
+
+ // default message TTL in retry queue in millisecond
+ const RETRY_DELAY = 10000;
+
+ // default message TTL in delayed queue in millisecond
+ const DELAY = 5000;
+
+ public function __construct(PropertyAccess $conf)
+ {
+ $defaultConfiguration = [
+ 'host' => 'localhost',
+ 'port' => 5672,
+ 'user' => 'guest',
+ 'password' => 'guest',
+ 'vhost' => '/'
+ ];
+
+ $this->hostConfig = $conf->get(['workers', 'queue', 'worker-queue'], $defaultConfiguration);
+ $this->conf = $conf;
+ }
+
+ public function getConnection()
+ {
+ if (!isset($this->connection)) {
+ try{
+ $this->connection = new AMQPStreamConnection(
+ $this->hostConfig['host'],
+ $this->hostConfig['port'],
+ $this->hostConfig['user'],
+ $this->hostConfig['password'],
+ $this->hostConfig['vhost']
+ );
+
+ } catch (\Exception $e) {
+
+ }
+ }
+
+ return $this->connection;
+ }
+
+ public function getChannel()
+ {
+ if (!isset($this->channel)) {
+ $this->getConnection();
+ if (isset($this->connection)) {
+ $this->channel = $this->connection->channel();
+
+ return $this->channel;
+ }
+
+ return null;
+ } else {
+ return $this->channel;
+ }
+ }
+
+ public function declareExchange()
+ {
+ if (isset($this->channel)) {
+ $this->channel->exchange_declare(self::ALCHEMY_EXCHANGE, 'direct', false, true, false);
+ $this->channel->exchange_declare(self::RETRY_ALCHEMY_EXCHANGE, 'direct', false, true, false);
+ }
+ }
+
+ /**
+ * @param $queueName
+ * @return AMQPChannel|null
+ */
+ public function setQueue($queueName)
+ {
+ if (!isset($this->channel)) {
+ $this->getChannel();
+ if (!isset($this->channel)) {
+ // can't connect to rabbit
+ return null;
+ }
+
+ $this->declareExchange();
+ }
+
+ if (isset(self::$defaultRetryQueues[$queueName])) {
+ $this->channel->queue_declare($queueName, false, true, false, false, false, new AMQPTable([
+ 'x-dead-letter-exchange' => self::RETRY_ALCHEMY_EXCHANGE, // the exchange to which republish a 'dead' message
+ 'x-dead-letter-routing-key' => self::$defaultRetryQueues[$queueName] // the routing key to apply to this 'dead' message
+ ]));
+
+ $this->channel->queue_bind($queueName, self::ALCHEMY_EXCHANGE, $queueName);
+
+ // declare also the corresponding retry queue
+ // use this to delay the delivery of a message to the alchemy-exchange
+ $this->channel->queue_declare(self::$defaultRetryQueues[$queueName], false, true, false, false, false, new AMQPTable([
+ 'x-dead-letter-exchange' => AMQPConnection::ALCHEMY_EXCHANGE,
+ 'x-dead-letter-routing-key' => $queueName,
+ 'x-message-ttl' => $this->getTtlRetryPerRouting($queueName)
+ ]));
+
+ $this->channel->queue_bind(self::$defaultRetryQueues[$queueName], AMQPConnection::RETRY_ALCHEMY_EXCHANGE, self::$defaultRetryQueues[$queueName]);
+
+ } elseif (in_array($queueName, self::$defaultRetryQueues)) {
+ // if it's a retry queue
+ $routing = array_search($queueName, AMQPConnection::$defaultRetryQueues);
+ $this->channel->queue_declare($queueName, false, true, false, false, false, new AMQPTable([
+ 'x-dead-letter-exchange' => AMQPConnection::ALCHEMY_EXCHANGE,
+ 'x-dead-letter-routing-key' => $routing,
+ 'x-message-ttl' => $this->getTtlRetryPerRouting($routing)
+ ]));
+
+ $this->channel->queue_bind($queueName, AMQPConnection::RETRY_ALCHEMY_EXCHANGE, $queueName);
+ } elseif (in_array($queueName, self::$defaultFailedQueues)) {
+ // if it's a failed queue
+ $this->channel->queue_declare($queueName, false, true, false, false, false);
+
+ $this->channel->queue_bind($queueName, AMQPConnection::RETRY_ALCHEMY_EXCHANGE, $queueName);
+ } elseif (in_array($queueName, self::$defaultDelayedQueues)) {
+ // if it's a delayed queue
+ $routing = array_search($queueName, AMQPConnection::$defaultDelayedQueues);
+ $this->channel->queue_declare($queueName, false, true, false, false, false, new AMQPTable([
+ 'x-dead-letter-exchange' => AMQPConnection::ALCHEMY_EXCHANGE,
+ 'x-dead-letter-routing-key' => $routing,
+ 'x-message-ttl' => $this->getTtlDelayedPerRouting($routing)
+ ]));
+
+ $this->channel->queue_bind($queueName, AMQPConnection::RETRY_ALCHEMY_EXCHANGE, $queueName);
+ } else {
+ $this->channel->queue_declare($queueName, false, true, false, false, false);
+
+ $this->channel->queue_bind($queueName, AMQPConnection::ALCHEMY_EXCHANGE, $queueName);
+ }
+
+ return $this->channel;
+ }
+
+ public function reinitializeQueue(array $queuNames)
+ {
+ if (!isset($this->channel)) {
+ $this->getChannel();
+ $this->declareExchange();
+ }
+ foreach ($queuNames as $queuName) {
+ if (in_array($queuName, self::$defaultQueues)) {
+ $this->channel->queue_purge($queuName);
+ } else {
+ $this->channel->queue_delete($queuName);
+ }
+
+ if (isset(self::$defaultRetryQueues[$queuName])) {
+ $this->channel->queue_delete(self::$defaultRetryQueues[$queuName]);
+ }
+
+ $this->setQueue($queuName);
+ }
+ }
+
+ public function connectionClose()
+ {
+ $this->channel->close();
+ $this->connection->close();
+ }
+
+ /**
+ * @param $routing
+ * @return int
+ */
+ private function getTtlRetryPerRouting($routing)
+ {
+ $config = $this->conf->get(['workers']);
+
+ if ($routing == MessagePublisher::PULL_QUEUE &&
+ isset($config['pull_assets']) &&
+ isset($config['pull_assets']['pullInterval']) ) {
+ // convert in milli second
+ return (int)($config['pull_assets']['pullInterval']) * 1000;
+ } elseif (isset($config['retry_queue']) &&
+ isset($config['retry_queue'][array_search($routing, AMQPConnection::$defaultQueues)])) {
+
+ return (int)($config['retry_queue'][array_search($routing, AMQPConnection::$defaultQueues)]);
+ }
+
+ return self::RETRY_DELAY;
+ }
+
+ private function getTtlDelayedPerRouting($routing)
+ {
+ $delayed = [
+ MessagePublisher::METADATAS_QUEUE => 'delayedWriteMeta',
+ MessagePublisher::SUBDEF_QUEUE => 'delayedSubdef'
+ ];
+
+ $config = $this->conf->get(['workers']);
+
+ if (isset($config['retry_queue']) && isset($config['retry_queue'][$delayed[$routing]])) {
+ return (int)$config['retry_queue'][$delayed[$routing]];
+ }
+
+ return self::DELAY;
+ }
+}
diff --git a/lib/Alchemy/Phrasea/WorkerManager/Queue/MessageHandler.php b/lib/Alchemy/Phrasea/WorkerManager/Queue/MessageHandler.php
new file mode 100644
index 0000000000..b06bbab265
--- /dev/null
+++ b/lib/Alchemy/Phrasea/WorkerManager/Queue/MessageHandler.php
@@ -0,0 +1,119 @@
+messagePublisher = $messagePublisher;
+ }
+
+ public function consume(AMQPConnection $serverConnection, WorkerInvoker $workerInvoker, $argQueueName, $maxProcesses)
+ {
+ $publisher = $this->messagePublisher;
+
+ $channel = $serverConnection->getChannel();
+
+ if ($channel == null) {
+ $this->messagePublisher->pushLog("Can't connect to rabbit, check configuration!", "error");
+
+ return ;
+ }
+
+ $serverConnection->declareExchange();
+
+ // define consume callbacks
+ $callback = function (AMQPMessage $message) use ($channel, $workerInvoker, $publisher) {
+
+ $data = json_decode($message->getBody(), true);
+
+ $count = 0;
+
+ if ($message->has('application_headers')) {
+ /** @var AMQPTable $headers */
+ $headers = $message->get('application_headers');
+
+ $headerData = $headers->getNativeData();
+ if (isset($headerData['x-death'])) {
+ $xDeathHeader = $headerData['x-death'];
+
+ foreach ($xDeathHeader as $xdeath) {
+ $queue = $xdeath['queue'];
+ if (!in_array($queue, AMQPConnection::$defaultQueues)) {
+ continue;
+ }
+
+ $count = $xdeath['count'];
+ $data['payload']['count'] = $count;
+ }
+ }
+ }
+
+ // 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) {
+ $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",
+ AMQPConnection::$defaultFailedQueues[$data['message_type']],
+ json_encode($data['payload'])
+ );
+ $this->messagePublisher->pushLog($logMessage);
+
+ $channel->basic_ack($message->delivery_info['delivery_tag']);
+ } else {
+ 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
+ $channel->basic_nack($message->delivery_info['delivery_tag']);
+ } else {
+ $channel->basic_ack($message->delivery_info['delivery_tag']);
+ }
+
+ $oldPayload = $data['payload'];
+ $message = $data['message_type'].' to be consumed! >> Payload ::'. json_encode($oldPayload);
+
+ $publisher->pushLog($message);
+ } catch (\Exception $e) {
+ $channel->basic_nack($message->delivery_info['delivery_tag']);
+ }
+ }
+ };
+
+ $prefetchCount = ProcessPool::MAX_PROCESSES;
+
+ if ($maxProcesses) {
+ $prefetchCount = $maxProcesses;
+ }
+
+ 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);
+ }
+ } 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);
+ }
+ }
+ }
+}
diff --git a/lib/Alchemy/Phrasea/WorkerManager/Queue/MessagePublisher.php b/lib/Alchemy/Phrasea/WorkerManager/Queue/MessagePublisher.php
new file mode 100644
index 0000000000..c191017bf8
--- /dev/null
+++ b/lib/Alchemy/Phrasea/WorkerManager/Queue/MessagePublisher.php
@@ -0,0 +1,156 @@
+serverConnection = $serverConnection;
+ $this->logger = $logger;
+ }
+
+ public function publishMessage(array $payload, $queueName, $retryCount = null, $workerMessage = '')
+ {
+ // add published timestamp to all message payload
+ $payload['payload']['published'] = time();
+ $msg = new AMQPMessage(json_encode($payload));
+ $routing = array_search($queueName, AMQPConnection::$defaultRetryQueues);
+
+ if (count($retryCount) && $routing != false) {
+ // add a message header information
+ $headers = new AMQPTable([
+ 'x-death' => [
+ [
+ 'count' => $retryCount,
+ 'exchange' => AMQPConnection::ALCHEMY_EXCHANGE,
+ 'queue' => $routing,
+ 'routing-keys' => $routing,
+ 'reason' => 'rejected', // rejected is sended like nack
+ 'time' => new \DateTime('now', new \DateTimeZone('UTC'))
+ ]
+ ],
+ 'worker-message' => $workerMessage
+ ]);
+
+ $msg->set('application_headers', $headers);
+ }
+
+ $channel = $this->serverConnection->setQueue($queueName);
+
+ if ($channel == null) {
+ $this->pushLog("Can't connect to rabbit, check configuration!", "error");
+
+ return true;
+ }
+
+ $exchange = in_array($queueName, AMQPConnection::$defaultQueues) ? AMQPConnection::ALCHEMY_EXCHANGE : AMQPConnection::RETRY_ALCHEMY_EXCHANGE;
+ $channel->basic_publish($msg, $exchange, $queueName);
+
+ return true;
+ }
+
+ public function initializePullAssets()
+ {
+ $payload = [
+ 'message_type' => self::PULL_ASSETS_TYPE,
+ 'payload' => [
+ 'initTimestamp' => new \DateTime('now', new \DateTimeZone('UTC'))
+ ]
+ ];
+
+ $this->publishMessage($payload, self::PULL_QUEUE);
+ }
+
+ public function connectionClose()
+ {
+ $this->serverConnection->connectionClose();
+ }
+
+ /**
+ * @param $message
+ * @param string $method
+ * @param array $context
+ */
+ public function pushLog($message, $method = 'info', $context = [])
+ {
+ // write logs directly in file
+
+ call_user_func(array($this->logger, $method), $message, $context);
+ }
+
+ public function publishFailedMessage(array $payload, AMQPTable $headers, $queueName)
+ {
+ $msg = new AMQPMessage(json_encode($payload));
+ $msg->set('application_headers', $headers);
+
+ $channel = $this->serverConnection->setQueue($queueName);
+ if ($channel == null) {
+ $this->pushLog("Can't connect to rabbit, check configuration!", "error");
+
+ return ;
+ }
+
+ $channel->basic_publish($msg, AMQPConnection::RETRY_ALCHEMY_EXCHANGE, $queueName);
+ }
+}
diff --git a/lib/Alchemy/Phrasea/WorkerManager/Queue/WebhookPublisher.php b/lib/Alchemy/Phrasea/WorkerManager/Queue/WebhookPublisher.php
new file mode 100644
index 0000000000..a1bdcc888a
--- /dev/null
+++ b/lib/Alchemy/Phrasea/WorkerManager/Queue/WebhookPublisher.php
@@ -0,0 +1,29 @@
+messagePublisher = $messagePublisher;
+ }
+
+ public function publishWebhookEvent(WebhookEvent $event)
+ {
+ $payload = [
+ 'message_type' => MessagePublisher::WEBHOOK_TYPE,
+ 'payload' => [
+ 'id' => $event->getId()
+ ]
+ ];
+
+ $this->messagePublisher->publishMessage($payload, MessagePublisher::WEBHOOK_QUEUE);
+ }
+}
diff --git a/lib/Alchemy/Phrasea/WorkerManager/Subscriber/AssetsIngestSubscriber.php b/lib/Alchemy/Phrasea/WorkerManager/Subscriber/AssetsIngestSubscriber.php
new file mode 100644
index 0000000000..2cc75abbe5
--- /dev/null
+++ b/lib/Alchemy/Phrasea/WorkerManager/Subscriber/AssetsIngestSubscriber.php
@@ -0,0 +1,73 @@
+messagePublisher = $messagePublisher;
+ }
+
+ public function onAssetsCreate(AssetsCreateEvent $event)
+ {
+ // this is an uploader PUSH mode
+ $payload = [
+ 'message_type' => MessagePublisher::ASSETS_INGEST_TYPE,
+ 'payload' => array_merge($event->getData(), ['type' => WorkerRunningUploader::TYPE_PUSH])
+ ];
+
+
+ $this->messagePublisher->publishMessage($payload, MessagePublisher::ASSETS_INGEST_QUEUE);
+ }
+
+ public function onAssetsCreationFailure(AssetsCreationFailureEvent $event)
+ {
+ $payload = [
+ 'message_type' => MessagePublisher::ASSETS_INGEST_TYPE,
+ 'payload' => $event->getPayload()
+ ];
+
+ $this->messagePublisher->publishMessage(
+ $payload,
+ MessagePublisher::RETRY_ASSETS_INGEST_QUEUE,
+ $event->getCount(),
+ $event->getWorkerMessage()
+ );
+ }
+
+ public function onAssetsCreationRecordFailure(AssetsCreationRecordFailureEvent $event)
+ {
+ $payload = [
+ 'message_type' => MessagePublisher::CREATE_RECORD_TYPE,
+ 'payload' => $event->getPayload()
+ ];
+
+ $this->messagePublisher->publishMessage(
+ $payload,
+ MessagePublisher::RETRY_CREATE_RECORD_QUEUE,
+ $event->getCount(),
+ $event->getWorkerMessage()
+ );
+ }
+
+ public static function getSubscribedEvents()
+ {
+ return [
+ WorkerEvents::ASSETS_CREATE => 'onAssetsCreate',
+ WorkerEvents::ASSETS_CREATION_FAILURE => 'onAssetsCreationFailure',
+ WorkerEvents::ASSETS_CREATION_RECORD_FAILURE => 'onAssetsCreationRecordFailure'
+ ];
+ }
+}
diff --git a/lib/Alchemy/Phrasea/WorkerManager/Subscriber/ExportSubscriber.php b/lib/Alchemy/Phrasea/WorkerManager/Subscriber/ExportSubscriber.php
new file mode 100644
index 0000000000..d75d662db4
--- /dev/null
+++ b/lib/Alchemy/Phrasea/WorkerManager/Subscriber/ExportSubscriber.php
@@ -0,0 +1,64 @@
+messagePublisher = $messagePublisher;
+ }
+
+ public function onExportMailCreate(ExportMailEvent $event)
+ {
+ $payload = [
+ 'message_type' => MessagePublisher::EXPORT_MAIL_TYPE,
+ 'payload' => [
+ 'emitterUserId' => $event->getEmitterUserId(),
+ 'tokenValue' => $event->getTokenValue(),
+ 'destinationMails' => serialize($event->getDestinationMails()),
+ 'params' => serialize($event->getParams())
+ ]
+ ];
+
+ $this->messagePublisher->publishMessage($payload, MessagePublisher::EXPORT_QUEUE);
+ }
+
+ public function onExportMailFailure(ExportMailFailureEvent $event)
+ {
+ $payload = [
+ 'message_type' => MessagePublisher::EXPORT_MAIL_TYPE,
+ 'payload' => [
+ 'emitterUserId' => $event->getEmitterUserId(),
+ 'tokenValue' => $event->getTokenValue(),
+ 'destinationMails' => serialize($event->getDestinationMails()),
+ 'params' => serialize($event->getParams())
+ ]
+ ];
+
+ $this->messagePublisher->publishMessage(
+ $payload,
+ MessagePublisher::RETRY_EXPORT_QUEUE,
+ $event->getCount(),
+ $event->getWorkerMessage()
+ );
+ }
+
+ public static function getSubscribedEvents()
+ {
+ return [
+ PhraseaEvents::EXPORT_MAIL_CREATE => 'onExportMailCreate',
+ WorkerEvents::EXPORT_MAIL_FAILURE => 'onExportMailFailure'
+ ];
+ }
+}
diff --git a/lib/Alchemy/Phrasea/WorkerManager/Subscriber/RecordSubscriber.php b/lib/Alchemy/Phrasea/WorkerManager/Subscriber/RecordSubscriber.php
new file mode 100644
index 0000000000..d225a0c37a
--- /dev/null
+++ b/lib/Alchemy/Phrasea/WorkerManager/Subscriber/RecordSubscriber.php
@@ -0,0 +1,331 @@
+messagePublisher = $app['alchemy_worker.message.publisher'];
+ $this->workerResolver = $app['alchemy_worker.type_based_worker_resolver'];
+ $this->app = $app;
+ $this->appboxLocator = $appboxLocator;
+ }
+
+ public function onSubdefinitionCreate(SubdefinitionCreateEvent $event)
+ {
+ $record = $this->getApplicationBox()->get_databox($event->getRecord()->getDataboxId())->get_record($event->getRecord()->getRecordId());
+
+ if (!$record->isStory()) {
+ $subdefs = $record->getDatabox()->get_subdef_structure()->getSubdefGroup($record->getType());
+
+ foreach ($subdefs as $subdef) {
+ $payload = [
+ 'message_type' => MessagePublisher::SUBDEF_CREATION_TYPE,
+ 'payload' => [
+ 'recordId' => $event->getRecord()->getRecordId(),
+ 'databoxId' => $event->getRecord()->getDataboxId(),
+ 'subdefName' => $subdef->get_name(),
+ 'status' => $event->isNewRecord() ? MessagePublisher::NEW_RECORD_MESSAGE : ''
+ ]
+ ];
+
+ $this->messagePublisher->publishMessage($payload, MessagePublisher::SUBDEF_QUEUE);
+ }
+ }
+ }
+
+ public function onDelete(DeleteEvent $event)
+ {
+ // first remove record from the grid answer, so first delete the record in the index elastic
+ $this->app['dispatcher']->dispatch(RecordEvents::DELETED, new DeletedEvent($event->getRecord()));
+
+ // publish payload to queue
+ $payload = [
+ 'message_type' => MessagePublisher::DELETE_RECORD_TYPE,
+ 'payload' => [
+ 'recordId' => $event->getRecord()->getRecordId(),
+ 'databoxId' => $event->getRecord()->getDataboxId(),
+ ]
+ ];
+
+ $this->messagePublisher->publishMessage($payload, MessagePublisher::DELETE_RECORD_QUEUE);
+ }
+
+ public function onSubdefinitionCreationFailure(SubdefinitionCreationFailureEvent $event)
+ {
+ $payload = [
+ 'message_type' => MessagePublisher::SUBDEF_CREATION_TYPE,
+ 'payload' => [
+ 'recordId' => $event->getRecord()->getRecordId(),
+ 'databoxId' => $event->getRecord()->getDataboxId(),
+ 'subdefName' => $event->getSubdefName(),
+ 'status' => ''
+ ]
+ ];
+
+ $repoWorker = $this->getRepoWorker();
+ $em = $repoWorker->getEntityManager();
+ $workerRunningJob = $repoWorker->findOneBy([
+ 'databoxId' => $event->getRecord()->getDataboxId(),
+ 'recordId' => $event->getRecord()->getRecordId(),
+ 'work' => PhraseaTokens::MAKE_SUBDEF,
+ 'workOn' => $event->getSubdefName()
+ ]);
+
+ $em->beginTransaction();
+ try {
+ $em->remove($workerRunningJob);
+ $em->flush();
+ $em->commit();
+ } catch (\Exception $e) {
+ $em->rollback();
+ }
+
+ $this->messagePublisher->publishMessage(
+ $payload,
+ MessagePublisher::RETRY_SUBDEF_QUEUE,
+ $event->getCount(),
+ $event->getWorkerMessage()
+ );
+ }
+
+ public function onRecordCreated(RecordEvent $event)
+ {
+ $this->messagePublisher->pushLog(sprintf('The %s= %d was successfully created',
+ ($event->getRecord()->isStory() ? "story story_id" : "record record_id"),
+ $event->getRecord()->getRecordId()
+ ));
+ }
+
+ public function onMetadataChanged(MetadataChangedEvent $event)
+ {
+ $databoxId = $event->getRecord()->getDataboxId();
+ $recordId = $event->getRecord()->getRecordId();
+
+ $mediaSubdefRepository = $this->getMediaSubdefRepository($databoxId);
+ $mediaSubdefs = $mediaSubdefRepository->findByRecordIdsAndNames([$recordId]);
+
+ $databox = $this->getApplicationBox()->get_databox($databoxId);
+ $record = $databox->get_record($recordId);
+ $type = $record->getType();
+
+ foreach ($mediaSubdefs as $subdef) {
+ // check subdefmetadatarequired from the subview setup in admin
+ if ( $subdef->get_name() == 'document' || $this->isSubdefMetadataUpdateRequired($databox, $type, $subdef->get_name())) {
+ if ($subdef->is_physically_present()) {
+ $payload = [
+ 'message_type' => MessagePublisher::WRITE_METADATAS_TYPE,
+ 'payload' => [
+ 'recordId' => $recordId,
+ 'databoxId' => $databoxId,
+ 'subdefName' => $subdef->get_name()
+ ]
+ ];
+
+ $this->messagePublisher->publishMessage($payload, MessagePublisher::METADATAS_QUEUE);
+ } else {
+ $payload = [
+ 'message_type' => MessagePublisher::WRITE_METADATAS_TYPE,
+ 'payload' => [
+ 'recordId' => $recordId,
+ 'databoxId' => $databoxId,
+ 'subdefName' => $subdef->get_name()
+ ]
+ ];
+
+ $logMessage = sprintf("Subdef %s is not physically present! to be passed in the %s ! payload >>> %s",
+ $subdef->get_name(),
+ MessagePublisher::RETRY_METADATAS_QUEUE,
+ json_encode($payload)
+ );
+ $this->messagePublisher->pushLog($logMessage);
+
+ $this->messagePublisher->publishMessage(
+ $payload,
+ MessagePublisher::RETRY_METADATAS_QUEUE,
+ 2,
+ 'Subdef is not physically present!'
+ );
+ }
+ }
+ }
+
+ }
+
+ public function onStoryCreateCover(StoryCreateCoverEvent $event)
+ {
+ /** @var WorkerFactoryInterface[] $factories */
+ $factories = $this->workerResolver->getFactories();
+
+ /** @var CreateRecordWorker $createRecordWorker */
+ $createRecordWorker = $factories[MessagePublisher::CREATE_RECORD_TYPE]->createWorker();
+
+ $createRecordWorker->setStoryCover($event->getData());
+ }
+
+ public function onSubdefinitionWritemeta(SubdefinitionWritemetaEvent $event)
+ {
+ if ($event->getStatus() == SubdefinitionWritemetaEvent::FAILED) {
+ $payload = [
+ 'message_type' => MessagePublisher::WRITE_METADATAS_TYPE,
+ 'payload' => [
+ 'recordId' => $event->getRecord()->getRecordId(),
+ 'databoxId' => $event->getRecord()->getDataboxId(),
+ 'subdefName' => $event->getSubdefName()
+ ]
+ ];
+
+ $logMessage = sprintf("Subdef %s write meta failed, error : %s ! to be passed in the %s ! payload >>> %s",
+ $event->getSubdefName(),
+ $event->getWorkerMessage(),
+ MessagePublisher::RETRY_METADATAS_QUEUE,
+ json_encode($payload)
+ );
+ $this->messagePublisher->pushLog($logMessage);
+
+ $jeton = ($event->getSubdefName() == "document") ? PhraseaTokens::WRITE_META_DOC : PhraseaTokens::WRITE_META_SUBDEF;
+
+ $repoWorker = $this->getRepoWorker();
+ $em = $repoWorker->getEntityManager();
+ $workerRunningJob = $repoWorker->findOneBy([
+ 'databoxId' => $event->getRecord()->getDataboxId(),
+ 'recordId' => $event->getRecord()->getRecordId(),
+ 'work' => $jeton,
+ 'workOn' => $event->getSubdefName()
+ ]);
+
+ $em->beginTransaction();
+ try {
+ $em->remove($workerRunningJob);
+ $em->flush();
+ $em->commit();
+ } catch (\Exception $e) {
+ $em->rollback();
+ }
+
+ $this->messagePublisher->publishMessage(
+ $payload,
+ MessagePublisher::RETRY_METADATAS_QUEUE,
+ $event->getCount(),
+ $event->getWorkerMessage()
+ );
+
+ } else {
+ $databoxId = $event->getRecord()->getDataboxId();
+ $recordId = $event->getRecord()->getRecordId();
+
+ $databox = $this->getApplicationBox()->get_databox($databoxId);
+ $record = $databox->get_record($recordId);
+ $type = $record->getType();
+
+ $subdef = $record->get_subdef($event->getSubdefName());
+
+ // only the required writemetadata from admin > subview setup is to be writing
+ if ($subdef->get_name() == 'document' || $this->isSubdefMetadataUpdateRequired($databox, $type, $subdef->get_name())) {
+ $payload = [
+ 'message_type' => MessagePublisher::WRITE_METADATAS_TYPE,
+ 'payload' => [
+ 'recordId' => $recordId,
+ 'databoxId' => $databoxId,
+ 'subdefName' => $event->getSubdefName()
+ ]
+ ];
+
+ $this->messagePublisher->publishMessage($payload, MessagePublisher::METADATAS_QUEUE);
+ }
+ }
+
+ }
+
+ public static function getSubscribedEvents()
+ {
+ return [
+ RecordEvents::CREATED => 'onRecordCreated',
+ RecordEvents::SUBDEFINITION_CREATE => 'onSubdefinitionCreate',
+ RecordEvents::DELETE => 'onDelete',
+ WorkerEvents::SUBDEFINITION_CREATION_FAILURE => 'onSubdefinitionCreationFailure',
+ RecordEvents::METADATA_CHANGED => 'onMetadataChanged',
+ WorkerEvents::STORY_CREATE_COVER => 'onStoryCreateCover',
+ WorkerEvents::SUBDEFINITION_WRITE_META => 'onSubdefinitionWritemeta'
+ ];
+ }
+
+ /**
+ * @param $databoxId
+ *
+ * @return MediaSubdefRepository
+ */
+ private function getMediaSubdefRepository($databoxId)
+ {
+ return $this->app['provider.repo.media_subdef']->getRepositoryForDatabox($databoxId);
+ }
+
+ /**
+ * @param \databox $databox
+ * @param string $subdefType
+ * @param string $subdefName
+ * @return bool
+ */
+ private function isSubdefMetadataUpdateRequired(\databox $databox, $subdefType, $subdefName)
+ {
+ if ($databox->get_subdef_structure()->hasSubdef($subdefType, $subdefName)) {
+ return $databox->get_subdef_structure()->get_subdef($subdefType, $subdefName)->isMetadataUpdateRequired();
+ }
+
+ return false;
+ }
+
+ /**
+ * @return \appbox
+ */
+ private function getApplicationBox()
+ {
+ $callable = $this->appboxLocator;
+
+ return $callable();
+ }
+
+ /**
+ * @return WorkerRunningJobRepository
+ */
+ private function getRepoWorker()
+ {
+ return $this->app['repo.worker-running-job'];
+ }
+}
diff --git a/lib/Alchemy/Phrasea/WorkerManager/Subscriber/SearchengineSubscriber.php b/lib/Alchemy/Phrasea/WorkerManager/Subscriber/SearchengineSubscriber.php
new file mode 100644
index 0000000000..7f73f6ff44
--- /dev/null
+++ b/lib/Alchemy/Phrasea/WorkerManager/Subscriber/SearchengineSubscriber.php
@@ -0,0 +1,69 @@
+messagePublisher = $messagePublisher;
+ }
+
+ public function onPopulateIndex(PopulateIndexEvent $event)
+ {
+ $populateInfo = $event->getData();
+
+ // make payload per databoxId
+ foreach ($populateInfo['databoxIds'] as $databoxId) {
+ $payload = [
+ 'message_type' => MessagePublisher::POPULATE_INDEX_TYPE,
+ 'payload' => [
+ 'host' => $populateInfo['host'],
+ 'port' => $populateInfo['port'],
+ 'indexName' => $populateInfo['indexName'],
+ 'databoxId' => $databoxId
+ ]
+ ];
+
+ $this->messagePublisher->publishMessage($payload, MessagePublisher::POPULATE_INDEX_QUEUE);
+ }
+ }
+
+ public function onPopulateIndexFailure(PopulateIndexFailureEvent $event)
+ {
+ $payload = [
+ 'message_type' => MessagePublisher::POPULATE_INDEX_TYPE,
+ 'payload' => [
+ 'host' => $event->getHost(),
+ 'port' => $event->getPort(),
+ 'indexName' => $event->getIndexName(),
+ 'databoxId' => $event->getDataboxId(),
+ ]
+ ];
+
+ $this->messagePublisher->publishMessage(
+ $payload,
+ MessagePublisher::RETRY_POPULATE_INDEX_QUEUE,
+ $event->getCount(),
+ $event->getWorkerMessage()
+ );
+ }
+
+ public static function getSubscribedEvents()
+ {
+ return [
+ WorkerEvents::POPULATE_INDEX => 'onPopulateIndex',
+ WorkerEvents::POPULATE_INDEX_FAILURE => 'onPopulateIndexFailure'
+ ];
+ }
+}
+
diff --git a/lib/Alchemy/Phrasea/WorkerManager/Subscriber/WebhookSubscriber.php b/lib/Alchemy/Phrasea/WorkerManager/Subscriber/WebhookSubscriber.php
new file mode 100644
index 0000000000..eb45da3a37
--- /dev/null
+++ b/lib/Alchemy/Phrasea/WorkerManager/Subscriber/WebhookSubscriber.php
@@ -0,0 +1,48 @@
+messagePublisher = $messagePublisher;
+ }
+
+ public function onWebhookDeliverFailure(WebhookDeliverFailureEvent $event)
+ {
+ // count = 0 mean do not retry because no api application defined
+ if ($event->getCount() != 0) {
+ $payload = [
+ 'message_type' => MessagePublisher::WEBHOOK_TYPE,
+ 'payload' => [
+ 'id' => $event->getWebhookEventId(),
+ 'delivery_id' => $event->getDeleveryId(),
+ ]
+ ];
+
+ $this->messagePublisher->publishMessage(
+ $payload,
+ MessagePublisher::RETRY_WEBHOOK_QUEUE,
+ $event->getCount(),
+ $event->getWorkerMessage()
+ );
+ }
+
+ }
+
+ public static function getSubscribedEvents()
+ {
+ return [
+ WorkerEvents::WEBHOOK_DELIVER_FAILURE => 'onWebhookDeliverFailure',
+ ];
+ }
+}
diff --git a/lib/Alchemy/Phrasea/WorkerManager/Worker/AssetsIngestWorker.php b/lib/Alchemy/Phrasea/WorkerManager/Worker/AssetsIngestWorker.php
new file mode 100644
index 0000000000..fe8c191ebe
--- /dev/null
+++ b/lib/Alchemy/Phrasea/WorkerManager/Worker/AssetsIngestWorker.php
@@ -0,0 +1,166 @@
+app = $app;
+ $this->messagePublisher = $this->app['alchemy_worker.message.publisher'];
+ }
+
+ public function process(array $payload)
+ {
+ $assets = $payload['assets'];
+ $this->repoWorkerUploader = $this->getWorkerRunningUploaderRepository();
+
+ $this->saveAssetsList($payload['commit_id'], $assets, $payload['published'], $payload['type']);
+
+ $uploaderClient = new Client(['base_uri' => $payload['base_url']]);
+
+ //get first asset informations to check if it's a story
+ try {
+ $body = $uploaderClient->get('/assets/'.$assets[0], [
+ 'headers' => [
+ 'Authorization' => 'AssetToken '.$payload['token']
+ ]
+ ])->getBody()->getContents();
+ } catch(\Exception $e) {
+ $count = isset($payload['count']) ? $payload['count'] + 1 : 2 ;
+
+ $this->app['dispatcher']->dispatch(WorkerEvents::ASSETS_CREATION_FAILURE, new AssetsCreationFailureEvent(
+ $payload,
+ 'Error when getting assets information !' . $e->getMessage(),
+ $count
+ ));
+
+ return;
+ }
+
+ $body = json_decode($body,true);
+
+ $storyId = null;
+
+ if (!empty($body['formData']['is_story'])) {
+ $storyId = $this->createStory($body);
+ }
+
+ foreach ($assets as $assetId) {
+ $createRecordMessage['message_type'] = MessagePublisher::CREATE_RECORD_TYPE;
+ $createRecordMessage['payload'] = [
+ 'asset' => $assetId,
+ 'publisher' => $payload['publisher'],
+ 'assetToken' => $payload['token'],
+ 'storyId' => $storyId,
+ 'base_url' => $payload['base_url'],
+ 'commit_id' => $payload['commit_id']
+ ];
+
+ $this->messagePublisher->publishMessage($createRecordMessage, MessagePublisher::CREATE_RECORD_QUEUE);
+ }
+ }
+
+ private function createStory(array $body)
+ {
+ $storyId = null;
+
+ $userRepository = $this->getUserRepository();
+ $user = null;
+
+ if (!empty($body['formData']['phraseanet_submiter_email'])) {
+ $user = $userRepository->findByEmail($body['formData']['phraseanet_submiter_email']);
+ }
+
+ if ($user === null && !empty($body['formData']['phraseanet_user_submiter_id'])) {
+ $user = $userRepository->find($body['formData']['phraseanet_user_submiter_id']);
+ }
+
+ if ($user !== null) {
+ $base_id = $body['formData']['collection_destination'];
+
+ $collection = \collection::getByBaseId($this->app, $base_id);
+
+ $story = \record_adapter::createStory($this->app, $collection);
+ $storyId = $story->getRecordId();
+
+ $storyWZ = new StoryWZ();
+
+ $storyWZ->setUser($user);
+ $storyWZ->setRecord($story);
+
+ $entityManager = $this->getEntityManager();
+ $entityManager->persist($storyWZ);
+ $entityManager->flush();
+ }
+
+ return $storyId;
+ }
+
+ /**
+ * @return UserRepository
+ */
+ private function getUserRepository()
+ {
+ return $this->app['repo.users'];
+ }
+
+ /**
+ * @return WorkerRunningUploaderRepository
+ */
+ private function getWorkerRunningUploaderRepository()
+ {
+ return $this->app['repo.worker-running-uploader'];
+ }
+
+ private function saveAssetsList($commitId, $assetsId, $published, $type)
+ {
+ $em = $this->repoWorkerUploader->getEntityManager();
+ $em->beginTransaction();
+ $date = new \DateTime();
+
+ try {
+ foreach ($assetsId as $assetId) {
+ $workerRunningUploader = new WorkerRunningUploader();
+ $workerRunningUploader
+ ->setCommitId($commitId)
+ ->setAssetId($assetId)
+ ->setPublished($date->setTimestamp($published))
+ ->setStatus(WorkerRunningUploader::RUNNING)
+ ->setType($type)
+ ;
+
+ $em->persist($workerRunningUploader);
+
+ unset($workerRunningUploader);
+ }
+
+ $em->flush();
+
+ $em->commit();
+ } catch(\Exception $e) {
+ $em->rollback();
+ }
+ }
+}
diff --git a/lib/Alchemy/Phrasea/WorkerManager/Worker/CreateRecordWorker.php b/lib/Alchemy/Phrasea/WorkerManager/Worker/CreateRecordWorker.php
new file mode 100644
index 0000000000..80850bf9ec
--- /dev/null
+++ b/lib/Alchemy/Phrasea/WorkerManager/Worker/CreateRecordWorker.php
@@ -0,0 +1,346 @@
+app = $app;
+ $this->logger = $this->app['alchemy_worker.logger'];
+ $this->messagePublisher = $this->app['alchemy_worker.message.publisher'];
+ }
+
+ public function process(array $payload)
+ {
+ $this->repoWorkerUploader = $this->getWorkerRunningUploaderRepository();
+ $em = $this->repoWorkerUploader->getEntityManager();
+
+ $uploaderClient = new Client(['base_uri' => $payload['base_url']]);
+
+ //get asset informations
+ $body = $uploaderClient->get('/assets/'.$payload['asset'], [
+ 'headers' => [
+ 'Authorization' => 'AssetToken '.$payload['assetToken']
+ ]
+ ])->getBody()->getContents();
+
+ $body = json_decode($body,true);
+
+ $tempfile = $this->getTemporaryFilesystem()->createTemporaryFile('download_', null, pathinfo($body['originalName'], PATHINFO_EXTENSION));
+
+
+ /** @var WorkerRunningUploader $workerRunningUploader */
+ $workerRunningUploader = $this->repoWorkerUploader->findOneBy([
+ 'commitId' => $payload['commit_id'],
+ 'assetId' => $payload['asset']
+ ]);
+
+ //download the asset
+ try {
+ $res = $uploaderClient->get('/assets/'.$payload['asset'].'/download', [
+ 'headers' => [
+ 'Authorization' => 'AssetToken '.$payload['assetToken']
+ ],
+ 'save_to' => $tempfile
+ ]);
+ } catch (\Exception $e) {
+ $count = isset($payload['count']) ? $payload['count'] + 1 : 2 ;
+
+ // send to retry queue
+ $this->dispatch(WorkerEvents::ASSETS_CREATION_RECORD_FAILURE, new AssetsCreationRecordFailureEvent(
+ $payload,
+ 'Error when downloading assets!',
+ $count
+ ));
+
+ $em->remove($workerRunningUploader);
+ $em->flush();
+
+ return;
+ }
+
+
+ if ($res->getStatusCode() !== 200) {
+ $workerMessage = sprintf('Error %s downloading "%s"', $res->getStatusCode(), $payload['base_url'].'/assets/'.$payload['asset'].'/download');
+ $this->logger->error($workerMessage);
+
+ $count = isset($payload['count']) ? $payload['count'] + 1 : 2 ;
+
+ // send to retry queue
+ $this->dispatch(WorkerEvents::ASSETS_CREATION_RECORD_FAILURE, new AssetsCreationRecordFailureEvent(
+ $payload,
+ $workerMessage,
+ $count
+ ));
+
+ $em->remove($workerRunningUploader);
+ $em->flush();
+
+ return;
+ }
+
+ if ($workerRunningUploader != null) {
+ $em->beginTransaction();
+ try {
+ $workerRunningUploader->setStatus(WorkerRunningUploader::DOWNLOADED);
+
+ $em->persist($workerRunningUploader);
+
+ $em->flush();
+ $em->commit();
+ } catch (\Exception $e) {
+ $em->rollback();
+ }
+
+ }
+
+ $canAck = $this->repoWorkerUploader->canAck($payload['commit_id']);
+
+ // if all assets in the commit are downloaded , send ack to the uploader
+ if ($canAck) {
+ // post ack to the uploader
+ $uploaderClient->post('/commits/' . $payload['commit_id'] . '/ack', [
+ 'headers' => [
+ 'Authorization' => 'AssetToken '.$payload['assetToken']
+ ],
+ 'json' => [
+ 'acknowledged' => true
+ ]
+ ]
+ );
+ }
+
+ $lazaretSession = new LazaretSession();
+
+ $userRepository = $this->getUserRepository();
+ $user = null;
+
+ if (!empty($body['formData']['phraseanet_submiter_email'])) {
+ $user = $userRepository->findByEmail($body['formData']['phraseanet_submiter_email']);
+ }
+
+ if ($user === null && !empty($body['formData']['phraseanet_user_submiter_id'])) {
+ $user = $userRepository->find($body['formData']['phraseanet_user_submiter_id']);
+ }
+
+ if ($user !== null) {
+ $lazaretSession->setUser($user);
+ }
+
+ $this->getEntityManager()->persist($lazaretSession);
+
+
+ $renamedFilename = $tempfile;
+ $media = $this->app->getMediaFromUri($renamedFilename);
+
+ if (!isset($body['formData']['collection_destination'])) {
+ $this->messagePublisher->pushLog("The collection_destination is not defined");
+
+ return ;
+ }
+
+ $base_id = $body['formData']['collection_destination'];
+ $collection = \collection::getByBaseId($this->app, $base_id);
+ $sbasId = $collection->get_sbas_id();
+
+ $packageFile = new File($this->app, $media, $collection, $body['originalName']);
+
+ // get metadata and status
+ $statusbit = null;
+ foreach ($body['formData'] as $key => $value) {
+ if (strstr($key, 'metadata')) {
+ $tMeta = explode('-', $key);
+
+ $metaField = $collection->get_databox()->get_meta_structure()->get_element($tMeta[1]);
+
+ $packageFile->addAttribute(new MetaField($metaField, [$value]));
+ }
+
+ if (strstr($key, 'statusbit')) {
+ $tStatus = explode('-', $key);
+ $statusbit[$tStatus[1]] = $value;
+ }
+ }
+
+ if (!is_null($statusbit)) {
+ $status = '';
+ foreach (range(0, 31) as $i) {
+ $status .= isset($statusbit[$i]) ? ($statusbit[$i] ? '1' : '0') : '0';
+ }
+ $packageFile->addAttribute(new Status($this->app, strrev($status)));
+ }
+
+ $reasons = [];
+ $elementCreated = null;
+
+ $callback = function ($element, Visa $visa) use (&$reasons, &$elementCreated) {
+ foreach ($visa->getResponses() as $response) {
+ if (!$response->isOk()) {
+ $reasons[] = $response->getMessage($this->app['translator']);
+ }
+ }
+
+ $elementCreated = $element;
+ };
+
+ $this->getBorderManager()->process($lazaretSession, $packageFile, $callback);
+
+
+ if ($elementCreated instanceof \record_adapter) {
+ $this->dispatch(PhraseaEvents::RECORD_UPLOAD, new RecordEdit($elementCreated));
+ } else {
+ $this->messagePublisher->pushLog(sprintf('The file was moved to the quarantine: %s', json_encode($reasons)));
+ /** @var LazaretFile $elementCreated */
+ $this->dispatch(PhraseaEvents::LAZARET_CREATE, new LazaretEvent($elementCreated));
+ }
+
+ // add record in a story if story is defined
+
+ if (is_int($payload['storyId']) && $elementCreated instanceof \record_adapter) {
+ $this->addRecordInStory($user, $elementCreated, $sbasId, $payload['storyId'], $body['formData']);
+ }
+
+ }
+
+ /**
+ * @param string $data databoxId_storyId_recordId subdefName
+ */
+ public function setStoryCover($data)
+ {
+ // get databoxId , storyId , recordId
+ $tData = explode('_', $data);
+
+ $record = $this->findDataboxById($tData[0])->get_record($tData[2]);
+
+ $story = $this->findDataboxById($tData[0])->get_record($tData[1]);
+ $subdefName = $tData[3];
+
+ $subdef = $record->get_subdef($tData[3]);
+ $media = $this->app->getMediaFromUri($subdef->getRealPath());
+ $this->getSubdefSubstituer()->substituteSubdef($story, $subdefName, $media); // subdefName = thumbnail | preview
+
+ $this->messagePublisher->pushLog(sprintf("Cover %s set for story story_id= %d with the record record_id = %d", $subdefName, $story->getRecordId(), $record->getRecordId()));
+ }
+
+ /**
+ * @param $user
+ * @param \record_adapter $elementCreated
+ * @param $sbasId
+ * @param $storyId
+ * @param $formData
+ */
+ private function addRecordInStory($user, $elementCreated, $sbasId, $storyId, $formData)
+ {
+ $story = new \record_adapter($this->app, $sbasId, $storyId);
+
+ if (!$this->getAclForUser($user)->has_right_on_base($story->getBaseId(), \ACL::CANMODIFRECORD)) {
+ $this->messagePublisher->pushLog(sprintf("The user %s can not add document to the story story_id = %d", $user->getLogin(), $story->getRecordId()));
+
+ throw new AccessDeniedHttpException('You can not add document to this Story');
+ }
+
+ if (!$story->hasChild($elementCreated)) {
+ $story->appendChild($elementCreated);
+
+ if (SubdefCreationWorker::checkIfFirstChild($story, $elementCreated)) {
+ // add metadata to the story
+ $metadatas = [];
+ foreach ($formData as $key => $value) {
+ if (strstr($key, 'metadata')) {
+ $tMeta = explode('-', $key);
+
+ $metaField = $elementCreated->getDatabox()->get_meta_structure()->get_element($tMeta[1]);
+
+ $metadatas[] = [
+ 'meta_struct_id' => $metaField->get_id(),
+ 'meta_id' => null,
+ 'value' => $value,
+ ];
+ }
+ }
+
+ $story->set_metadatas($metadatas)->rebuild_subdefs();
+ }
+
+ $this->messagePublisher->pushLog(sprintf('The record record_id= %d was successfully added in the story record_id= %d', $elementCreated->getRecordId(), $story->getRecordId()));
+ $this->dispatch(PhraseaEvents::RECORD_EDIT, new RecordEdit($story));
+ }
+ }
+
+ /**
+ * @return UserRepository
+ */
+ private function getUserRepository()
+ {
+ return $this->app['repo.users'];
+ }
+
+ /**
+ * @param User $user
+ * @return \ACL
+ */
+ private function getAclForUser(User $user)
+ {
+ $aclProvider = $this->app['acl'];
+
+ return $aclProvider->get($user);
+ }
+
+ /**
+ * @return SubdefSubstituer
+ */
+ private function getSubdefSubstituer()
+ {
+ return $this->app['subdef.substituer'];
+ }
+
+ /**
+ * @return WorkerRunningUploaderRepository
+ */
+ private function getWorkerRunningUploaderRepository()
+ {
+ return $this->app['repo.worker-running-uploader'];
+ }
+}
diff --git a/lib/Alchemy/Phrasea/WorkerManager/Worker/DeleteRecordWorker.php b/lib/Alchemy/Phrasea/WorkerManager/Worker/DeleteRecordWorker.php
new file mode 100644
index 0000000000..859054710a
--- /dev/null
+++ b/lib/Alchemy/Phrasea/WorkerManager/Worker/DeleteRecordWorker.php
@@ -0,0 +1,17 @@
+findDataboxById($payload['databoxId'])->get_record($payload['recordId']);
+
+ $record->delete();
+ }
+}
diff --git a/lib/Alchemy/Phrasea/WorkerManager/Worker/ExportMailWorker.php b/lib/Alchemy/Phrasea/WorkerManager/Worker/ExportMailWorker.php
new file mode 100644
index 0000000000..80a56f3147
--- /dev/null
+++ b/lib/Alchemy/Phrasea/WorkerManager/Worker/ExportMailWorker.php
@@ -0,0 +1,102 @@
+app = $app;
+ }
+
+ public function process(array $payload)
+ {
+ $destMails = unserialize($payload['destinationMails']);
+
+ $params = unserialize($payload['params']);
+
+ /** @var UserRepository $userRepository */
+ $userRepository = $this->app['repo.users'];
+
+ $user = $userRepository->find($payload['emitterUserId']);
+
+ /** @var TokenRepository $tokenRepository */
+ $tokenRepository = $this->app['repo.tokens'];
+
+ /** @var Token $token */
+ $token = $tokenRepository->findValidToken($payload['tokenValue']);
+
+ $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) {
+ $count = isset($payload['count']) ? $payload['count'] + 1 : 2 ;
+
+ // notify to send to the retry queue
+ $this->app['dispatcher']->dispatch(WorkerEvents::EXPORT_MAIL_FAILURE, new ExportMailFailureEvent(
+ $payload['emitterUserId'],
+ $payload['tokenValue'],
+ $remaingEmails,
+ $payload['params'],
+ 'some mails failed',
+ $count
+ ));
+
+ 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
+ )
+ );
+ }
+ }
+
+ }
+}
diff --git a/lib/Alchemy/Phrasea/WorkerManager/Worker/Factory/CallableWorkerFactory.php b/lib/Alchemy/Phrasea/WorkerManager/Worker/Factory/CallableWorkerFactory.php
new file mode 100644
index 0000000000..d794e000b7
--- /dev/null
+++ b/lib/Alchemy/Phrasea/WorkerManager/Worker/Factory/CallableWorkerFactory.php
@@ -0,0 +1,33 @@
+factory = $factory;
+ }
+
+ /**
+ * @return WorkerInterface
+ */
+ public function createWorker()
+ {
+ $factory = $this->factory;
+ $worker = $factory();
+
+ if (! $worker instanceof WorkerInterface) {
+ throw new \RuntimeException('Invalid worker created, expected an instance of \Alchemy\Phrasea\WorkerManager\Worker\WorkerInterface');
+ }
+
+ return $worker;
+ }
+}
\ No newline at end of file
diff --git a/lib/Alchemy/Phrasea/WorkerManager/Worker/Factory/WorkerFactoryInterface.php b/lib/Alchemy/Phrasea/WorkerManager/Worker/Factory/WorkerFactoryInterface.php
new file mode 100644
index 0000000000..c7b221e9bb
--- /dev/null
+++ b/lib/Alchemy/Phrasea/WorkerManager/Worker/Factory/WorkerFactoryInterface.php
@@ -0,0 +1,13 @@
+indexer = $indexer;
+ $this->messagePublisher = $messagePublisher;
+ $this->repoWorkerPopulate = $repoWorkerPopulate;
+ }
+
+ public function process(array $payload)
+ {
+ $em = $this->repoWorkerPopulate->getEntityManager();
+ $em->beginTransaction();
+ $date = new \DateTime();
+
+ try {
+ $workerRunningPopulate = new WorkerRunningPopulate();
+ $workerRunningPopulate
+ ->setHost($payload['host'])
+ ->setPort($payload['port'])
+ ->setIndexName($payload['indexName'])
+ ->setDataboxId($payload['databoxId'])
+ ->setPublished($date->setTimestamp($payload['published']))
+ ->setStatus(WorkerRunningPopulate::RUNNING)
+ ;
+
+ $em->persist($workerRunningPopulate);
+
+ $em->flush();
+
+ $em->commit();
+ } catch (\Exception $e) {
+ $em->rollback();
+ }
+
+ /** @var ElasticsearchOptions $options */
+ $options = $this->indexer->getIndex()->getOptions();
+
+ $options->setIndexName($payload['indexName']);
+ $options->setHost($payload['host']);
+ $options->setPort($payload['port']);
+
+ $databoxId = $payload['databoxId'];
+
+ $indexExists = $this->indexer->indexExists();
+
+ if (!$indexExists) {
+ $workerMessage = sprintf("Index %s don't exist!", $payload['indexName']);
+ $this->messagePublisher->pushLog($workerMessage);
+
+ $count = isset($payload['count']) ? $payload['count'] + 1 : 2 ;
+
+ // send to retry queue
+ $this->dispatch(WorkerEvents::POPULATE_INDEX_FAILURE, new PopulateIndexFailureEvent(
+ $payload['host'],
+ $payload['port'],
+ $payload['indexName'],
+ $payload['databoxId'],
+ $workerMessage,
+ $count
+ ));
+ } else {
+ $databox = $this->findDataboxById($databoxId);
+
+ try {
+ $r = $this->indexer->populateIndex(Indexer::THESAURUS | Indexer::RECORDS, $databox); // , $temporary);
+
+ $this->messagePublisher->pushLog(sprintf(
+ "Indexation of databox \"%s\" finished in %0.2f sec (Mem. %0.2f Mo)",
+ $databox->get_dbname(),
+ $r['duration']/1000,
+ $r['memory']/1048576
+ ));
+ } catch(\Exception $e) {
+ if ($workerRunningPopulate != null) {
+
+ $em->remove($workerRunningPopulate);
+
+ $em->flush();
+ }
+
+ $workerMessage = sprintf("Error on indexing : %s ", $e->getMessage());
+ $this->messagePublisher->pushLog($workerMessage);
+
+ $count = isset($payload['count']) ? $payload['count'] + 1 : 2 ;
+
+ // notify to send a retry
+ $this->dispatch(WorkerEvents::POPULATE_INDEX_FAILURE, new PopulateIndexFailureEvent(
+ $payload['host'],
+ $payload['port'],
+ $payload['indexName'],
+ $payload['databoxId'],
+ $workerMessage,
+ $count
+ ));
+ }
+ }
+
+ // tell that the populate is finished
+ if ($workerRunningPopulate != null) {
+ $workerRunningPopulate
+ ->setStatus(WorkerRunningPopulate::FINISHED)
+ ->setFinished(new \DateTime('now'))
+ ;
+
+ $em->persist($workerRunningPopulate);
+
+ $em->flush();
+ }
+ }
+
+}
diff --git a/lib/Alchemy/Phrasea/WorkerManager/Worker/ProcessPool.php b/lib/Alchemy/Phrasea/WorkerManager/Worker/ProcessPool.php
new file mode 100644
index 0000000000..81917d3e56
--- /dev/null
+++ b/lib/Alchemy/Phrasea/WorkerManager/Worker/ProcessPool.php
@@ -0,0 +1,100 @@
+logger = new NullLogger();
+ }
+
+ public function setMaxProcesses($maxProcesses)
+ {
+ $this->maxProcesses = max(1, $maxProcesses);
+ }
+
+ /**
+ * Sets a logger instance on the object
+ *
+ * @param LoggerInterface $logger
+ * @return null
+ */
+ public function setLogger(LoggerInterface $logger)
+ {
+ $this->logger = $logger;
+ }
+
+ /**
+ * @param array $processArguments
+ * @param string|null $workingDirectory
+ * @return Process
+ */
+ public function getWorkerProcess(array $processArguments, $workingDirectory = null)
+ {
+ $this->detachFinishedProcesses();
+ $this->waitForNextSlot();
+
+ $builder = new ProcessBuilder($processArguments);
+
+ $builder->setWorkingDirectory($workingDirectory ?: getcwd());
+
+ return ($this->processes[] = $builder->getProcess());
+ }
+
+ private function detachFinishedProcesses()
+ {
+ $runningProcesses = [];
+
+ foreach ($this->processes as $process) {
+ if ($process->isRunning()) {
+ $runningProcesses[] = $process;
+ } else {
+ $process->stop(0);
+ }
+ }
+
+ $this->processes = $runningProcesses;
+ }
+
+ private function waitForNextSlot()
+ {
+ $this->logger->debug(
+ sprintf('Checking for available process slot: %d processes found.', count($this->processes))
+ );
+
+ $interval = 1;
+
+ while (count($this->processes) >= $this->maxProcesses) {
+ $this->logger->debug(sprintf('%d Max process count reached, will retry in %d second.', $this->maxProcesses, $interval));
+
+ sleep($interval);
+
+ $this->detachFinishedProcesses();
+ $interval = min(10, $interval + 1);
+ }
+ }
+}
diff --git a/lib/Alchemy/Phrasea/WorkerManager/Worker/PullAssetsWorker.php b/lib/Alchemy/Phrasea/WorkerManager/Worker/PullAssetsWorker.php
new file mode 100644
index 0000000000..6090ff733d
--- /dev/null
+++ b/lib/Alchemy/Phrasea/WorkerManager/Worker/PullAssetsWorker.php
@@ -0,0 +1,155 @@
+messagePublisher = $messagePublisher;
+ $this->conf = $conf;
+ $this->repoWorkerUploader = $repoWorkerUploader;
+ }
+
+ public function process(array $payload)
+ {
+ $config = $this->conf->get(['workers']);
+
+ if (isset($config['pull_assets'])) {
+ $config = $config['pull_assets'];
+ } else {
+ return;
+ }
+
+ $uploaderClient = new Client();
+
+ // if a token exist , use it
+ if (isset($config['assetToken'])) {
+ $res = $this->getCommits($uploaderClient, $config);
+ if ($res == null) {
+ return;
+ }
+
+ // if Unauthorized get a new token first
+ if ($res->getStatusCode() == 401) {
+ if (($config = $this->generateToken($uploaderClient, $config)) === null) {
+ return;
+ };
+ $res = $this->getCommits($uploaderClient, $config);
+ }
+ } else { // if there is not a token , get one from the uploader service
+ if (($config = $this->generateToken($uploaderClient, $config)) === null) {
+ return;
+ };
+ if (($res = $this->getCommits($uploaderClient, $config)) === null) {
+ return;
+ }
+ }
+
+ $body = $res->getBody()->getContents();
+ $body = json_decode($body,true);
+ $commits = $body['hydra:member'];
+
+ $urlInfo = parse_url($config['endpointCommit']);
+ $baseUrl = $urlInfo['scheme'] . '://' . $urlInfo['host'] .':'.$urlInfo['port'];
+
+ foreach ($commits as $commit) {
+ // send only payload in ingest-queue if the commit is ack false and it is not being creating
+ if (!$commit['acknowledged'] && !$this->isCommitToBeCreating($commit['id'])) {
+ $this->messagePublisher->pushLog("A new commit found in the uploader ! commit_ID : ".$commit['id']);
+
+ // this is an uploader PULL mode
+ $payload = [
+ 'message_type' => MessagePublisher::ASSETS_INGEST_TYPE,
+ 'payload' => [
+ 'assets' => array_map(function($asset) {
+ return str_replace('/assets/', '', $asset);
+ }, $commit['assets']),
+ 'publisher' => $commit['userId'],
+ 'commit_id' => $commit['id'],
+ 'token' => $commit['token'],
+ 'base_url' => $baseUrl,
+ 'type' => WorkerRunningUploader::TYPE_PULL
+ ]
+ ];
+
+ $this->messagePublisher->publishMessage($payload, MessagePublisher::ASSETS_INGEST_QUEUE);
+ }
+ }
+
+ }
+
+ /**
+ * @param Client $uploaderClient
+ * @param array $config
+ * @return \Psr\Http\Message\ResponseInterface|null
+ */
+ private function getCommits(Client $uploaderClient, array $config)
+ {
+ try {
+ $res = $uploaderClient->get($config['endpointCommit'], [
+ 'headers' => [
+ 'Authorization' => 'AssetToken '.$config['assetToken']
+ ]
+ ]);
+ } catch(\Exception $e) {
+ $this->messagePublisher->pushLog("An error occurred when fetching endpointCommit : " . $e->getMessage());
+
+ return null;
+ }
+
+ return $res;
+ }
+
+ /**
+ * @param Client $uploaderClient
+ * @param array $config
+ * @return array|null
+ */
+ private function generateToken(Client $uploaderClient, array $config)
+ {
+ try {
+ $tokenBody = $uploaderClient->post($config['endpointToken'], [
+ 'json' => [
+ 'client_id' => $config['clientId'],
+ 'client_secret' => $config['clientSecret'],
+ 'grant_type' => 'client_credentials',
+ 'scope' => 'uploader:commit_list'
+ ]
+ ])->getBody()->getContents();
+ } catch (\Exception $e) {
+ $this->messagePublisher->pushLog("An error occurred when fetching endpointToken : " . $e->getMessage());
+
+ return null;
+ }
+
+ $tokenBody = json_decode($tokenBody,true);
+
+ $this->conf->set(['workers', 'pull_assets', 'assetToken'], $tokenBody['access_token']);
+
+ return $this->conf->get(['workers', 'pull_assets']);
+ }
+
+ /**
+ * @param $commitId
+ * @return bool
+ */
+ private function isCommitToBeCreating($commitId)
+ {
+ $res = $this->repoWorkerUploader->findBy(['commitId' => $commitId]);
+
+ return count($res) != 0;
+ }
+}
diff --git a/lib/Alchemy/Phrasea/WorkerManager/Worker/Resolver/TypeBasedWorkerResolver.php b/lib/Alchemy/Phrasea/WorkerManager/Worker/Resolver/TypeBasedWorkerResolver.php
new file mode 100644
index 0000000000..e03d163bca
--- /dev/null
+++ b/lib/Alchemy/Phrasea/WorkerManager/Worker/Resolver/TypeBasedWorkerResolver.php
@@ -0,0 +1,46 @@
+factories[$messageType] = $workerFactory;
+ }
+
+ /**
+ * @return WorkerFactoryInterface[]
+ */
+ public function getFactories()
+ {
+ return $this->factories;
+ }
+
+ public function getWorker($messageType, array $message)
+ {
+ if (isset($this->workers[$messageType])) {
+ return $this->workers[$messageType];
+ }
+
+ if (isset($this->factories[$messageType])) {
+ return $this->workers[$messageType] = $this->factories[$messageType]->createWorker();
+ }
+
+ throw new \RuntimeException('Invalid worker type requested: ' . $messageType);
+ }
+}
diff --git a/lib/Alchemy/Phrasea/WorkerManager/Worker/Resolver/WorkerResolverInterface.php b/lib/Alchemy/Phrasea/WorkerManager/Worker/Resolver/WorkerResolverInterface.php
new file mode 100644
index 0000000000..2c23b15c9e
--- /dev/null
+++ b/lib/Alchemy/Phrasea/WorkerManager/Worker/Resolver/WorkerResolverInterface.php
@@ -0,0 +1,15 @@
+subdefGenerator = $subdefGenerator;
+ $this->messagePublisher = $messagePublisher;
+ $this->logger = $logger;
+ $this->dispatcher = $dispatcher;
+ $this->filesystem = $filesystem;
+ $this->repoWorker = $repoWorker;
+ $this->indexer = $indexer;
+ }
+
+ public function process(array $payload)
+ {
+ if(isset($payload['recordId']) && isset($payload['databoxId'])) {
+ $recordId = $payload['recordId'];
+ $databoxId = $payload['databoxId'];
+ $wantedSubdef = [$payload['subdefName']];
+
+ $databox = $this->findDataboxById($databoxId);
+ $record = $databox->get_record($recordId);
+
+ $oldLogger = $this->subdefGenerator->getLogger();
+
+ if (!$record->isStory()) {
+ // check if there is a write meta running for the record or the same task running
+ $canCreateSubdef = $this->repoWorker->canCreateSubdef($payload['subdefName'], $recordId, $databoxId);
+
+ if (!$canCreateSubdef) {
+ // the file is in used to write meta
+ $payload = [
+ 'message_type' => MessagePublisher::SUBDEF_CREATION_TYPE,
+ 'payload' => $payload
+ ];
+ $this->messagePublisher->publishMessage($payload, MessagePublisher::DELAYED_SUBDEF_QUEUE);
+
+ return ;
+ }
+
+ // tell that a file is in used to create subdef
+ $em = $this->repoWorker->getEntityManager();
+ $em->beginTransaction();
+
+ try {
+ $date = new \DateTime();
+ $workerRunningJob = new WorkerRunningJob();
+ $workerRunningJob
+ ->setDataboxId($databoxId)
+ ->setRecordId($recordId)
+ ->setWork(PhraseaTokens::MAKE_SUBDEF)
+ ->setWorkOn($payload['subdefName'])
+ ->setPublished($date->setTimestamp($payload['published']))
+ ->setStatus(WorkerRunningJob::RUNNING)
+ ;
+
+ $em->persist($workerRunningJob);
+ $em->flush();
+
+ $em->commit();
+ } catch (\Exception $e) {
+ $em->rollback();
+ }
+
+ $this->subdefGenerator->setLogger($this->logger);
+
+ try {
+ $this->subdefGenerator->generateSubdefs($record, $wantedSubdef);
+ } catch (\Exception $e) {
+ $em->beginTransaction();
+ try {
+ $em->remove($workerRunningJob);
+ $em->flush();
+ $em->commit();
+ } catch (\Exception $e) {
+ $em->rollback();
+ }
+ }
+
+ // begin to check if the subdef is successfully generated
+ $subdef = $record->getDatabox()->get_subdef_structure()->getSubdefGroup($record->getType())->getSubdef($payload['subdefName']);
+ $filePathToCheck = null;
+
+ if ($record->has_subdef($payload['subdefName']) ) {
+ $filePathToCheck = $record->get_subdef($payload['subdefName'])->getRealPath();
+ }
+
+ $filePathToCheck = $this->filesystem->generateSubdefPathname($record, $subdef, $filePathToCheck);
+
+ if (!$this->filesystem->exists($filePathToCheck)) {
+
+ $count = isset($payload['count']) ? $payload['count'] + 1 : 2 ;
+
+ $this->dispatcher->dispatch(WorkerEvents::SUBDEFINITION_CREATION_FAILURE, new SubdefinitionCreationFailureEvent(
+ $record,
+ $payload['subdefName'],
+ 'Subdef generation failed !',
+ $count
+ ));
+
+ $this->subdefGenerator->setLogger($oldLogger);
+ return ;
+ }
+ // checking ended
+
+ // order to write meta for the subdef if needed
+ $this->dispatcher->dispatch(WorkerEvents::SUBDEFINITION_WRITE_META, new SubdefinitionWritemetaEvent($record, $payload['subdefName']));
+
+ $this->subdefGenerator->setLogger($oldLogger);
+
+ // update jeton when subdef is created
+ $this->updateJeton($record);
+
+ $parents = $record->get_grouping_parents();
+
+ // create a cover for a story
+ // used when uploaded via uploader-service and grouped as a story
+ if (!$parents->is_empty() && isset($payload['status']) && $payload['status'] == MessagePublisher::NEW_RECORD_MESSAGE && in_array($payload['subdefName'], array('thumbnail', 'preview'))) {
+ foreach ($parents->get_elements() as $story) {
+ if (self::checkIfFirstChild($story, $record)) {
+ $data = implode('_', [$databoxId, $story->getRecordId(), $recordId, $payload['subdefName']]);
+
+ $this->dispatcher->dispatch(WorkerEvents::STORY_CREATE_COVER, new StoryCreateCoverEvent($data));
+ }
+ }
+ }
+
+ // update elastic
+ $this->indexer->flushQueue();
+
+ // tell that we have finished to work on this file
+ $em->beginTransaction();
+ try {
+ $workerRunningJob->setStatus(WorkerRunningJob::FINISHED);
+ $workerRunningJob->setFinished(new \DateTime('now'));
+ $em->persist($workerRunningJob);
+ $em->flush();
+ $em->commit();
+ } catch (\Exception $e) {
+ $em->rollback();
+ }
+ }
+ }
+ }
+
+ public static function checkIfFirstChild(\record_adapter $story, \record_adapter $record)
+ {
+ $sql = "SELECT * FROM regroup WHERE rid_parent = :parent_record_id AND rid_child = :children_id and ord = :ord";
+
+ $connection = $record->getDatabox()->get_connection();
+
+ $stmt = $connection->prepare($sql);
+
+ $stmt->execute([
+ ':parent_record_id' => $story->getRecordId(),
+ ':children_id' => $record->getRecordId(),
+ ':ord' => 0,
+ ]);
+
+ $row = $stmt->fetch(\PDO::FETCH_ASSOC);
+
+ $stmt->closeCursor();
+
+ if ($row) {
+ return true;
+ }
+
+ return false;
+ }
+
+ private function updateJeton(\record_adapter $record)
+ {
+ $connection = $record->getDatabox()->get_connection();
+ $connection->beginTransaction();
+
+ // mark subdef created
+ $sql = 'UPDATE record'
+ . ' SET jeton=(jeton & ~(:token)), moddate=NOW()'
+ . ' WHERE record_id=:record_id';
+
+ $stmt = $connection->prepare($sql);
+
+ $stmt->execute([
+ ':record_id' => $record->getRecordId(),
+ ':token' => PhraseaTokens::MAKE_SUBDEF,
+ ]);
+
+ $connection->commit();
+ $stmt->closeCursor();
+ }
+}
diff --git a/lib/Alchemy/Phrasea/WorkerManager/Worker/WebhookWorker.php b/lib/Alchemy/Phrasea/WorkerManager/Worker/WebhookWorker.php
new file mode 100644
index 0000000000..13700e77fb
--- /dev/null
+++ b/lib/Alchemy/Phrasea/WorkerManager/Worker/WebhookWorker.php
@@ -0,0 +1,206 @@
+app = $app;
+ $this->messagePublisher = $app['alchemy_worker.message.publisher'];
+ }
+
+ /**
+ * @param array $payload
+ */
+ public function process(array $payload)
+ {
+ if (isset($payload['id'])) {
+ $webhookEventId = $payload['id'];
+ $app = $this->app;
+
+ $httpClient = new GuzzleClient();
+ $version = new Version();
+ $httpClient->setUserAgent(sprintf('Phraseanet/%s (%s)', $version->getNumber(), $version->getName()));
+
+ $httpClient->getEventDispatcher()->addListener('request.error', function (Event $event) {
+ // override guzzle default behavior of throwing exceptions
+ // when 4xx & 5xx responses are encountered
+ $event->stopPropagation();
+ }, -254);
+
+ // Set callback which logs success or failure
+ $subscriber = new CallbackBackoffStrategy(function ($retries, Request $request, $response, $e) use ($app, $webhookEventId, $payload) {
+ $retry = true;
+ if ($response && (null !== $deliverId = parse_url($request->getUrl(), PHP_URL_FRAGMENT))) {
+ /** @var WebhookEventDelivery $delivery */
+ $delivery = $app['repo.webhook-delivery']->find($deliverId);
+
+ $logContext = [ 'host' => $request->getHost() ];
+
+ if ($response->isSuccessful()) {
+ $app['manipulator.webhook-delivery']->deliverySuccess($delivery);
+
+ $logType = 'info';
+ $logEntry = sprintf('Deliver success event "%d:%s" for app "%s"',
+ $delivery->getWebhookEvent()->getId(), $delivery->getWebhookEvent()->getName(),
+ $delivery->getThirdPartyApplication()->getName()
+ );
+
+ $retry = false;
+ } else {
+ $app['manipulator.webhook-delivery']->deliveryFailure($delivery);
+
+ $logType = 'error';
+ $logEntry = sprintf('Deliver failure event "%d:%s" for app "%s"',
+ $delivery->getWebhookEvent()->getId(), $delivery->getWebhookEvent()->getName(),
+ $delivery->getThirdPartyApplication()->getName()
+ );
+
+ $count = isset($payload['count']) ? $payload['count'] + 1 : 2 ;
+
+ $this->dispatch(WorkerEvents::WEBHOOK_DELIVER_FAILURE, new WebhookDeliverFailureEvent(
+ $webhookEventId,
+ $logEntry,
+ $count,
+ $deliverId
+ ));
+ }
+
+ $app['alchemy_worker.message.publisher']->pushLog($logEntry, $logType, $logContext);
+
+ return $retry;
+ }
+ }, true, new CurlBackoffStrategy());
+
+ // set max retries
+ $subscriber = new TruncatedBackoffStrategy(1, $subscriber);
+ $subscriber = new BackoffPlugin($subscriber);
+
+ $httpClient->addSubscriber($subscriber);
+
+
+ $thirdPartyApplications = $this->app['repo.api-applications']->findWithDefinedWebhookCallback();
+
+ /** @var WebhookEvent|null $webhookevent */
+ $webhookevent = $this->app['repo.webhook-event']->find($webhookEventId);
+
+ if ($webhookevent !== null) {
+ $app['manipulator.webhook-event']->processed($webhookevent);
+
+ $this->messagePublisher->pushLog(sprintf('Processing event "%s" with id %d', $webhookevent->getName(), $webhookevent->getId()));
+ // send requests
+ $this->deliverEvent($httpClient, $thirdPartyApplications, $webhookevent, $payload);
+ }
+ }
+ }
+
+ private function deliverEvent(GuzzleClient $httpClient, array $thirdPartyApplications, WebhookEvent $webhookevent, $payload)
+ {
+ if (count($thirdPartyApplications) === 0) {
+ $workerMessage = 'No applications defined to listen for webhook events';
+ $this->messagePublisher->pushLog($workerMessage);
+
+ // count = 0 mean do not retry because no api application defined
+ $this->dispatch(WorkerEvents::WEBHOOK_DELIVER_FAILURE, new WebhookDeliverFailureEvent($webhookevent->getId(), $workerMessage, 0));
+
+ return;
+ }
+
+ // format event data
+ if (!isset($payload['delivery_id'])) {
+ $webhookData = $webhookevent->getData();
+ $webhookData['time'] = $webhookevent->getCreated();
+ $webhookevent->setData($webhookData);
+ }
+
+ /** @var ProcessorInterface $eventProcessor */
+ $eventProcessor = $this->app['webhook.processor_factory']->get($webhookevent);
+ $data = $eventProcessor->process($webhookevent);
+
+ // batch requests
+ $batch = BatchBuilder::factory()
+ ->transferRequests(10)
+ ->build();
+
+ /** @var ApiApplication $thirdPartyApplication */
+ foreach ($thirdPartyApplications as $thirdPartyApplication) {
+ $creator = $thirdPartyApplication->getCreator();
+
+ if ($creator == null) {
+ continue;
+ }
+
+ $creatorGrantedBaseIds = array_keys($this->app['acl']->get($creator)->get_granted_base());
+
+ $concernedBaseIds = array_intersect($webhookevent->getCollectionBaseIds(), $creatorGrantedBaseIds);
+
+ if (count($webhookevent->getCollectionBaseIds()) != 0 && count($concernedBaseIds) == 0) {
+ continue;
+ }
+
+ if (isset($payload['delivery_id']) && $payload['delivery_id'] != null) {
+ /** @var WebhookEventDelivery $delivery */
+ $delivery = $this->app['repo.webhook-delivery']->find($payload['delivery_id']);
+
+ // only the app url to retry
+ if ($delivery->getThirdPartyApplication()->getId() != $thirdPartyApplication->getId()) {
+ continue;
+ }
+ } else {
+ $delivery = $this->app['manipulator.webhook-delivery']->create($thirdPartyApplication, $webhookevent);
+ }
+
+ // append delivery id as url anchor
+ $uniqueUrl = $this->getUrl($thirdPartyApplication, $delivery);
+
+ // create http request with data as request body
+ $batch->add($httpClient->createRequest('POST', $uniqueUrl, [
+ 'Content-Type' => 'application/vnd.phraseanet.event+json'
+ ], json_encode($data)));
+ }
+
+ try {
+ $batch->flush();
+ } catch (\Exception $e) {
+ $this->messagePublisher->pushLog($e->getMessage());
+ $this->messagePublisher->publishFailedMessage(
+ $payload,
+ new AMQPTable(['worker-message' => $e->getMessage()]),
+ MessagePublisher::FAILED_WEBHOOK_QUEUE
+ );
+ }
+ }
+
+ private function getUrl(ApiApplication $application, WebhookEventDelivery $delivery)
+ {
+ return sprintf('%s#%s', $application->getWebhookUrl(), $delivery->getId());
+ }
+}
diff --git a/lib/Alchemy/Phrasea/WorkerManager/Worker/WorkerInterface.php b/lib/Alchemy/Phrasea/WorkerManager/Worker/WorkerInterface.php
new file mode 100644
index 0000000000..73b8dde300
--- /dev/null
+++ b/lib/Alchemy/Phrasea/WorkerManager/Worker/WorkerInterface.php
@@ -0,0 +1,12 @@
+binaryPath = $_SERVER['SCRIPT_NAME'];
+ $this->environment = $environment;
+ $this->processPool = $processPool;
+ $this->logger = new NullLogger();
+ }
+
+ public function preservePayloads()
+ {
+ $this->preservePayloads = true;
+ }
+
+ /**
+ * Sets a logger instance on the object
+ *
+ * @param LoggerInterface $logger
+ * @return null
+ */
+ public function setLogger(LoggerInterface $logger)
+ {
+ $this->logger = $logger;
+ }
+
+ public function setPrefix($prefix)
+ {
+ $this->prefix = $prefix;
+ }
+
+ /**
+ * @param string $messageType
+ * @param string $payload
+ */
+ public function invokeWorker($messageType, $payload)
+ {
+ $args = [
+ $this->binaryPath,
+ $this->command,
+ '-vv',
+ $messageType,
+ $this->createPayloadFile($payload)
+ ];
+
+ if ($this->environment) {
+ $args[] = sprintf('-e=%s', $this->environment);
+ }
+
+ if ($this->preservePayloads) {
+ $args[] = '--preserve-payload';
+ }
+
+ $process = $this->processPool->getWorkerProcess($args, getcwd());
+
+ $this->logger->debug('Invoking shell command: ' . $process->getCommandLine());
+
+ try {
+ $process->start([$this, 'logWorkerOutput']);
+ } catch (ProcessRuntimeException $e) {
+ $process->stop();
+
+ throw new \RuntimeException(sprintf('Command "%s" failed: %s', $process->getCommandLine(),
+ $e->getMessage()), 0, $e);
+ }
+ }
+
+ public function logWorkerOutput($stream, $output)
+ {
+ if ($stream == 'err') {
+ $this->logger->error($output);
+ } else {
+ $this->logger->info($output);
+ }
+ }
+
+ public function setMaxProcessPoolValue($maxProcesses)
+ {
+ $this->processPool->setMaxProcesses($maxProcesses);
+ }
+
+ private function createPayloadFile($payload)
+ {
+ $path = tempnam(sys_get_temp_dir(), $this->prefix);
+
+ if (file_put_contents($path, $payload) === false) {
+ throw new \RuntimeException('Cannot write payload file to path: ' . $path);
+ }
+
+ return $path;
+ }
+}
diff --git a/lib/Alchemy/Phrasea/WorkerManager/Worker/WriteMetadatasWorker.php b/lib/Alchemy/Phrasea/WorkerManager/Worker/WriteMetadatasWorker.php
new file mode 100644
index 0000000000..6c02078ac1
--- /dev/null
+++ b/lib/Alchemy/Phrasea/WorkerManager/Worker/WriteMetadatasWorker.php
@@ -0,0 +1,313 @@
+writer = $writer;
+ $this->logger = $logger;
+ $this->messagePublisher = $messagePublisher;
+ $this->repoWorker = $repoWorker;
+ }
+
+ public function process(array $payload)
+ {
+ if (isset($payload['recordId']) && isset($payload['databoxId'])) {
+ $recordId = $payload['recordId'];
+ $databoxId = $payload['databoxId'];
+
+ $MWG = isset($payload['MWG']) ? $payload['MWG'] : false;
+ $clearDoc = isset($payload['clearDoc']) ? $payload['clearDoc'] : false;
+ $databox = $this->findDataboxById($databoxId);
+
+
+ $param = ($payload['subdefName'] == "document") ? PhraseaTokens::WRITE_META_DOC : PhraseaTokens::WRITE_META_SUBDEF;
+
+ // check if there is a make subdef running for the record or the same task running
+ $canWriteMeta = $this->repoWorker->canWriteMetadata($payload['subdefName'], $recordId, $databoxId);
+
+ if (!$canWriteMeta) {
+ // the file is in used to generate subdef
+ $payload = [
+ 'message_type' => MessagePublisher::WRITE_METADATAS_TYPE,
+ 'payload' => $payload
+ ];
+ $this->messagePublisher->publishMessage($payload, MessagePublisher::DELAYED_METADATAS_QUEUE);
+
+ return ;
+ }
+
+ // tell that a file is in used to create subdef
+ $em = $this->getEntityManager();
+ $em->beginTransaction();
+
+ try {
+ $date = new \DateTime();
+ $workerRunningJob = new WorkerRunningJob();
+ $workerRunningJob
+ ->setDataboxId($databoxId)
+ ->setRecordId($recordId)
+ ->setWork($param)
+ ->setWorkOn($payload['subdefName'])
+ ->setPublished($date->setTimestamp($payload['published']))
+ ->setStatus(WorkerRunningJob::RUNNING)
+ ;
+
+ $em->persist($workerRunningJob);
+ $em->flush();
+
+ $em->commit();
+ } catch (\Exception $e) {
+ $em->rollback();
+ }
+
+ $record = $databox->get_record($recordId);
+
+ $subdef = $record->get_subdef($payload['subdefName']);
+
+ if ($subdef->is_physically_present()) {
+ $metadata = new MetadataBag();
+
+ // add Uuid in metadatabag
+ if ($record->getUuid()) {
+ $metadata->add(
+ new Metadata(
+ new Tag\XMPExif\ImageUniqueID(),
+ new Mono($record->getUuid())
+ )
+ );
+ $metadata->add(
+ new Metadata(
+ new Tag\ExifIFD\ImageUniqueID(),
+ new Mono($record->getUuid())
+ )
+ );
+ $metadata->add(
+ new Metadata(
+ new Tag\IPTC\UniqueDocumentID(),
+ new Mono($record->getUuid())
+ )
+ );
+ }
+
+ // read document fields and add to metadatabag
+ $caption = $record->get_caption();
+ foreach ($databox->get_meta_structure() as $fieldStructure) {
+
+ $tagName = $fieldStructure->get_tag()->getTagname();
+ $fieldName = $fieldStructure->get_name();
+
+ // skip fields with no src
+ if ($tagName == '' || $tagName == 'Phraseanet:no-source') {
+ continue;
+ }
+
+ // check exiftool known tags to skip Phraseanet:tf-*
+ try {
+ $tag = TagFactory::getFromRDFTagname($tagName);
+ if(!$tag->isWritable()) {
+ continue;
+ }
+ } catch (TagUnknown $e) {
+ continue;
+ }
+
+ try {
+ $field = $caption->get_field($fieldName);
+ $fieldValues = $field->get_values();
+
+ if ($fieldStructure->is_multi()) {
+ $values = array();
+ foreach ($fieldValues as $value) {
+ $values[] = $this->removeNulChar($value->getValue());
+ }
+
+ $value = new Multi($values);
+ } else {
+ $fieldValue = array_pop($fieldValues);
+ $value = $this->removeNulChar($fieldValue->getValue());
+
+ // fix the dates edited into phraseanet
+ if($fieldStructure->get_type() === $fieldStructure::TYPE_DATE) {
+ try {
+ $value = self::fixDate($value); // will return NULL if the date is not valid
+ }
+ catch (\Exception $e) {
+ $value = null; // do NOT write back to iptc
+ }
+ }
+
+ if($value !== null) { // do not write invalid dates
+ $value = new Mono($value);
+ }
+ }
+ } catch(\Exception $e) {
+ // the field is not set in the record, erase it
+ if ($fieldStructure->is_multi()) {
+ $value = new Multi(array(''));
+ }
+ else {
+ $value = new Mono('');
+ }
+ }
+
+ if($value !== null) { // do not write invalid data
+ $metadata->add(
+ new Metadata($fieldStructure->get_tag(), $value)
+ );
+ }
+ }
+
+ $this->writer->reset();
+
+ if ($MWG) {
+ $this->writer->setModule(Writer::MODULE_MWG, true);
+ }
+
+ $this->writer->erase($subdef->get_name() != 'document' || $clearDoc, true);
+
+ // write meta in file
+ try {
+ $this->writer->write($subdef->getRealPath(), $metadata);
+
+ $this->messagePublisher->pushLog(sprintf('meta written for sbasid=%1$d - recordid=%2$d (%3$s)', $databox->get_sbas_id(), $recordId, $subdef->get_name() ));
+ } catch (\Exception $e) {
+ $workerMessage = sprintf('meta NOT written for sbasid=%1$d - recordid=%2$d (%3$s) because "%s"', $databox->get_sbas_id(), $recordId, $subdef->get_name() , $e->getMessage());
+ $this->logger->error($workerMessage);
+
+ $count = isset($payload['count']) ? $payload['count'] + 1 : 2 ;
+
+ $this->dispatch(WorkerEvents::SUBDEFINITION_WRITE_META, new SubdefinitionWritemetaEvent(
+ $record,
+ $payload['subdefName'],
+ SubdefinitionWritemetaEvent::FAILED,
+ $workerMessage,
+ $count
+ ));
+ }
+
+ // mark write metas finished
+ $this->updateJeton($record);
+ } else {
+ $count = isset($payload['count']) ? $payload['count'] + 1 : 2 ;
+
+ $this->dispatch(WorkerEvents::SUBDEFINITION_WRITE_META, new SubdefinitionWritemetaEvent(
+ $record,
+ $payload['subdefName'],
+ SubdefinitionWritemetaEvent::FAILED,
+ 'Subdef is not physically present!',
+ $count
+ ));
+ }
+
+
+ // tell that we have finished to work on this file
+ $em->beginTransaction();
+ try {
+ $workerRunningJob->setStatus(WorkerRunningJob::FINISHED);
+ $workerRunningJob->setFinished(new \DateTime('now'));
+ $em->persist($workerRunningJob);
+ $em->flush();
+ $em->commit();
+ } catch (\Exception $e) {
+ $em->rollback();
+ }
+
+ }
+
+ }
+
+ private function removeNulChar($value)
+ {
+ return str_replace("\0", "", $value);
+ }
+
+ private function updateJeton(\record_adapter $record)
+ {
+ $connection = $record->getDatabox()->get_connection();
+
+ $connection->beginTransaction();
+ $stmt = $connection->prepare('UPDATE record SET jeton=(jeton & ~(:token)), moddate=NOW() WHERE record_id = :record_id');
+
+ $stmt->execute([
+ ':record_id' => $record->getRecordId(),
+ ':token' => PhraseaTokens::WRITE_META,
+ ]);
+
+ $connection->commit();
+ $stmt->closeCursor();
+ }
+
+ /**
+ * re-format a phraseanet date for iptc writing
+ * return NULL if the date is not valid
+ *
+ * @param string $value
+ * @return string|null
+ */
+ private static function fixDate($value)
+ {
+ $date = null;
+ try {
+ $a = explode(';', preg_replace('/\D+/', ';', trim($value)));
+ switch (count($a)) {
+ case 3: // yyyy;mm;dd
+ $date = new \DateTime($a[0] . '-' . $a[1] . '-' . $a[2]);
+ $date = $date->format('Y-m-d H:i:s');
+ break;
+ case 6: // yyyy;mm;dd;hh;mm;ss
+ $date = new \DateTime($a[0] . '-' . $a[1] . '-' . $a[2] . ' ' . $a[3] . ':' . $a[4] . ':' . $a[5]);
+ $date = $date->format('Y-m-d H:i:s');
+ break;
+ }
+ }
+ catch (\Exception $e) {
+ $date = null;
+ }
+
+ return $date;
+ }
+}
diff --git a/lib/classes/databox/status.php b/lib/classes/databox/status.php
index 744041e034..397f1eacea 100644
--- a/lib/classes/databox/status.php
+++ b/lib/classes/databox/status.php
@@ -157,8 +157,8 @@ class databox_status
* compute ((0 M s1) M s2) where M is the "mask" operator
* nb : s1,s2 are binary mask strings as "01x0xx1xx0x", no other format (hex) supported
*
- * @param $stat1 a binary mask "010x1xx0.." STRING
- * @param $stat2 a binary mask "x100x1..." STRING
+ * @param string $stat1 a binary mask "010x1xx0.."
+ * @param string $stat2 a binary mask "x100x1..."
*
* @return string
*/
diff --git a/lib/classes/patch/410alpha28a.php b/lib/classes/patch/410alpha28a.php
new file mode 100644
index 0000000000..2ff20e4b32
--- /dev/null
+++ b/lib/classes/patch/410alpha28a.php
@@ -0,0 +1,174 @@
+release;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function concern()
+ {
+ return $this->concern;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function require_all_upgrades()
+ {
+ return false;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getDoctrineMigrations()
+ {
+ return [];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function apply(base $appbox, Application $app)
+ {
+ // add geoloc section if not exist
+ if (!$app['conf']->has(['geocoding-providers'])) {
+ $providers[0] = [
+ 'map-provider' => 'mapboxWebGL',
+ 'enabled' => false,
+ 'public-key' => '',
+ 'map-layers' => [
+ 0 => [
+ 'name' => 'Light',
+ 'value' => 'mapbox://styles/mapbox/light-v9'
+ ],
+ 1 => [
+ 'name' => 'Streets',
+ 'value' => 'mapbox://styles/mapbox/streets-v9'
+ ],
+ 2 => [
+ 'name' => 'Basic',
+ 'value' => 'mapbox://styles/mapbox/basic-v9'
+ ],
+ 3 => [
+ 'name' => 'Satellite',
+ 'value' => 'mapbox://styles/mapbox/satellite-v9'
+ ],
+ 4 => [
+ 'name' => 'Dark',
+ 'value' => 'mapbox://styles/mapbox/dark-v9'
+ ]
+ ],
+ 'transition-mapboxgl' => [
+ 0 => [
+ 'animate' => true,
+ 'speed' => '2.2',
+ 'curve' => '1.42'
+ ]
+ ],
+ 'default-position' => [
+ '48.879162',
+ '2.335062'
+ ],
+ 'default-zoom' => 5,
+ 'marker-default-zoom' => 9,
+ 'position-fields' => [],
+ 'geonames-field-mapping' => true,
+ 'cityfields' => 'City, Ville',
+ 'provincefields' => 'Province',
+ 'countryfields' => 'Country, Pays'
+ ];
+
+ $app['conf']->set(['geocoding-providers'], $providers);
+ }
+
+ // add video-editor section if not exist
+ if (!$app['conf']->has(['video-editor'])) {
+ $videoEditor = [
+ 'ChapterVttFieldName' => 'VideoTextTrackChapters',
+ 'seekBackwardStep' => 500,
+ 'seekForwardStep' => 500,
+ 'playbackRates' => [
+ 1,
+ '1.5',
+ 3
+ ]
+ ];
+
+ $app['conf']->set(['video-editor'], $videoEditor);
+ }
+
+ // add api_token_header if not exist
+ if (!$app['conf']->has(['main', 'api_token_header'])) {
+ $app['conf']->set(['main', 'api_token_header'], false);
+ }
+
+ // insert timeout if not exist
+ if (!$app['conf']->has(['main', 'binaries', 'ffmpeg_timeout'])) {
+ $app['conf']->set(['main', 'binaries', 'ffmpeg_timeout'], 3600);
+ }
+ if (!$app['conf']->has(['main', 'binaries', 'ffprobe_timeout'])) {
+ $app['conf']->set(['main', 'binaries', 'ffprobe_timeout'], 60);
+ }
+ if (!$app['conf']->has(['main', 'binaries', 'gs_timeout'])) {
+ $app['conf']->set(['main', 'binaries', 'gs_timeout'], 60);
+ }
+ if (!$app['conf']->has(['main', 'binaries', 'mp4box_timeout'])) {
+ $app['conf']->set(['main', 'binaries', 'mp4box_timeout'], 60);
+ }
+ if (!$app['conf']->has(['main', 'binaries', 'swftools_timeout'])) {
+ $app['conf']->set(['main', 'binaries', 'swftools_timeout'], 60);
+ }
+ if (!$app['conf']->has(['main', 'binaries', 'unoconv_timeout'])) {
+ $app['conf']->set(['main', 'binaries', 'unoconv_timeout'], 60);
+ }
+ if (!$app['conf']->has(['main', 'binaries', 'exiftool_timeout'])) {
+ $app['conf']->set(['main', 'binaries', 'exiftool_timeout'], 60);
+ }
+
+ // custom-link section, remove default store
+ $app['conf']->remove(['registry', 'custom-links', 0]);
+ $app['conf']->remove(['registry', 'custom-links', 1]);
+
+ $customLinks = [
+ 'linkName' => 'Phraseanet store',
+ 'linkLanguage' => 'all',
+ 'linkUrl' => 'https://store.alchemy.fr',
+ 'linkLocation' => 'help-menu',
+ 'linkOrder' => 1,
+ 'linkBold' => false,
+ 'linkColor' => ''
+ ];
+
+ $app['conf']->set(['registry', 'custom-links', 0], $customLinks);
+
+ return true;
+ }
+}
diff --git a/lib/conf.d/configuration.yml b/lib/conf.d/configuration.yml
index 93faaefdd8..c28999a566 100644
--- a/lib/conf.d/configuration.yml
+++ b/lib/conf.d/configuration.yml
@@ -10,6 +10,7 @@ main:
maintenance: false
key: ''
api_require_ssl: true
+ api_token_header: false
database:
host: 'sql-host'
port: 3306
diff --git a/lib/conf.d/json_schema/assets_enqueue.json b/lib/conf.d/json_schema/assets_enqueue.json
new file mode 100644
index 0000000000..416f321fb6
--- /dev/null
+++ b/lib/conf.d/json_schema/assets_enqueue.json
@@ -0,0 +1,30 @@
+{
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "title": "New assets",
+ "description": "List of assets to enqueue on Phraseanet",
+ "type": "object",
+ "properties": {
+ "assets": {
+ "type": "array"
+ },
+ "publisher": {
+ "type": "string"
+ },
+ "token": {
+ "type": "string"
+ },
+ "base_url": {
+ "type": "string"
+ },
+ "commit_id": {
+ "type": "string"
+ }
+ },
+ "required": [
+ "assets",
+ "publisher",
+ "token",
+ "base_url",
+ "commit_id"
+ ]
+}
diff --git a/package.json b/package.json
index ce1213df70..4ca7818565 100644
--- a/package.json
+++ b/package.json
@@ -65,7 +65,7 @@
"normalize-css": "^2.1.0",
"npm": "^6.0.0",
"npm-modernizr": "^2.8.3",
- "phraseanet-production-client": "0.34.205-d",
+ "phraseanet-production-client": "0.34.211-d",
"requirejs": "^2.3.5",
"tinymce": "^4.0.28",
"underscore": "^1.8.3",
diff --git a/resources/locales/messages.de.xlf b/resources/locales/messages.de.xlf
index 4b07696b43..22fe17d401 100644
--- a/resources/locales/messages.de.xlf
+++ b/resources/locales/messages.de.xlf
@@ -1,16 +1,16 @@
-
+
-
+
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.
-
-
- Form/Login/PhraseaAuthenticationForm.php
+
+
Form/Configuration/EmailFormType.php
+ Form/Login/PhraseaAuthenticationForm.php
Add
@@ -111,9 +111,9 @@
%basket_length% documents
%basket_length% Dokument(e)
- mobile/lightbox/validate.html.twig
web/lightbox/index.html.twig
web/lightbox/index.html.twig
+ mobile/lightbox/validate.html.twig
%countable% documents can not be modified.
@@ -164,9 +164,9 @@
%n_par_page% par page
%n_par_page% pro Seite
- web/admin/users.html.twig
- web/admin/users.html.twig
- web/admin/users.html.twig
+ web/admin/users.html.twig
+ web/admin/users.html.twig
+ web/admin/users.html.twig
%name% est momentanement indisponible a cause d'un trop grand nombre de requetes
@@ -193,16 +193,16 @@
%nb_view% vue
%nb_view% Ansicht
- Bridge/Dailymotion/element_informations.html.twig
- Bridge/Flickr/element_informations.html.twig
Bridge/Youtube/element_informations.html.twig
+ Bridge/Flickr/element_informations.html.twig
+ Bridge/Dailymotion/element_informations.html.twig
%nb_view% vues
%nb_view% Ansichten
- Bridge/Dailymotion/element_informations.html.twig
- Bridge/Flickr/element_informations.html.twig
Bridge/Youtube/element_informations.html.twig
+ Bridge/Flickr/element_informations.html.twig
+ Bridge/Dailymotion/element_informations.html.twig
selectionnes]]>
@@ -546,7 +546,7 @@
Access
Zugriff
actions/Feedback/List-Share.html.twig
- web/admin/editusers.html.twig
+ web/admin/editusers.html.twig
Access code
@@ -566,17 +566,17 @@
Access to HD
Zugriff auf HD Download
- web/admin/editusers.html.twig
+ web/admin/editusers.html.twig
Access to image tools
Zugriff auf Bild Werkzeug wird erlaubt
- web/admin/editusers.html.twig
+ web/admin/editusers.html.twig
Access to preview
Zugriff auf Vorschaubild
- web/admin/editusers.html.twig
+ web/admin/editusers.html.twig
Access to the above bases constitutes acceptance of the following Terms of Use (TOU).
@@ -628,12 +628,12 @@
Actions
Aktionen
- Bridge/Dailymotion/actioncontainers.html.twig
- Bridge/Dailymotion/actionelements.html.twig
- Bridge/Flickr/actioncontainers.html.twig
- Bridge/Flickr/actionelements.html.twig
- Bridge/Youtube/actioncontainers.html.twig
Bridge/Youtube/actionelements.html.twig
+ Bridge/Youtube/actioncontainers.html.twig
+ Bridge/Flickr/actionelements.html.twig
+ Bridge/Flickr/actioncontainers.html.twig
+ Bridge/Dailymotion/actionelements.html.twig
+ Bridge/Dailymotion/actioncontainers.html.twig
Activate highlight
@@ -644,13 +644,13 @@
Active
Aktiv
- web/admin/editusers.html.twig
+ web/admin/editusers.html.twig
Activer
aktivieren
- web/admin/editusers_timelimit_sbas.html.twig
admin/databox/databox.html.twig
+ web/admin/editusers_timelimit_sbas.html.twig
web/admin/editusers_timelimit.html.twig
@@ -671,8 +671,8 @@
Add
Hinzufügen
- prod/User/Add.html.twig
prod/actions/Push.html.twig
+ prod/User/Add.html.twig
prod/upload/lazaret.html.twig
prod/upload/lazaret.html.twig
@@ -751,7 +751,7 @@
Adresse email du nouvel utilisateur
Email Adresse des neuen Benutzers
- web/admin/index.html.twig
+ web/admin/index.html.twig
Advanced Search
@@ -789,6 +789,11 @@
den Titel anzeigen
web/prod/index.html.twig
+
+ Afficher le type
+ Typ anzeigen
+ web/prod/index.html.twig
+
Afficher les status
die Status anzeigen
@@ -827,21 +832,21 @@
Ajouter a
Hinzufügen zu
- Bridge/Dailymotion/actionelements.html.twig
- Bridge/Flickr/actionelements.html.twig
Bridge/Youtube/actionelements.html.twig
+ Bridge/Flickr/actionelements.html.twig
+ Bridge/Dailymotion/actionelements.html.twig
Ajouter ma selection courrante
Meine aktuelle Auswahl hinzufügen
- prod/Baskets/Create.html.twig
- prod/Story/Create.html.twig
prod/orders/order_item.html.twig
+ prod/Story/Create.html.twig
+ prod/Baskets/Create.html.twig
Ajouter un nouvel utilisateur
einen neuen Benutzer hinzufügen
- web/admin/index.html.twig
+ web/admin/index.html.twig
Ajouter un publisher
@@ -864,6 +869,7 @@
actions/Feedback/list.html.twig
actions/Feedback/list.html.twig
WorkZone/Browser/Browser.html.twig
+ admin/worker-manager/worker_searchengine.html.twig
task-manager/task-editor/subdefs.html.twig
@@ -889,47 +895,47 @@
Allowed to access report
Zugriff auf Report wird erlaubt
- web/admin/editusers.html.twig
+ web/admin/editusers.html.twig
Allowed to add
Hinzufügen wird erlaubt
- web/admin/editusers.html.twig
+ web/admin/editusers.html.twig
Allowed to add in basket
Erlaubt, Dokumente in Sammelkörbe hinzuzufügen
- web/admin/editusers.html.twig
+ web/admin/editusers.html.twig
Allowed to change statuses
Status Veränderungen werden erlaubt
- web/admin/editusers.html.twig
+ web/admin/editusers.html.twig
Allowed to delete
Löschen wird erlaubt
- web/admin/editusers.html.twig
+ web/admin/editusers.html.twig
Allowed to edit
Verarbeitung wird erlaubt
- web/admin/editusers.html.twig
+ web/admin/editusers.html.twig
Allowed to order
Ordnen wird erlaubt
- web/admin/editusers.html.twig
+ web/admin/editusers.html.twig
Allowed to publish
Veröffentlichen wird erlaubt
- web/admin/editusers.html.twig
+ web/admin/editusers.html.twig
Allowed to push
Push wird erlaubt
- web/admin/editusers.html.twig
+ web/admin/editusers.html.twig
Alphabetic asc
@@ -952,22 +958,9 @@
An error occured
Ein Fehler ist aufgetreten
- Controller/Prod/LazaretController.php
- Controller/Prod/MoveCollectionController.php
- Controller/Prod/ToolsController.php
- Controller/Prod/BasketController.php
- Controller/Prod/StoryController.php
+ Model/Manipulator/LazaretManipulator.php
+ Model/Manipulator/LazaretManipulator.php
Controller/Admin/DataboxesController.php
- Controller/Admin/DataboxController.php
- Controller/Admin/DataboxController.php
- Controller/Admin/DataboxController.php
- Controller/Admin/DataboxController.php
- Controller/Admin/DataboxController.php
- Controller/Admin/DataboxController.php
- Controller/Admin/DataboxController.php
- Controller/Admin/DataboxController.php
- Controller/Admin/DataboxController.php
- Controller/Admin/DataboxController.php
Controller/Admin/CollectionController.php
Controller/Admin/CollectionController.php
Controller/Admin/CollectionController.php
@@ -979,13 +972,26 @@
Controller/Admin/CollectionController.php
Controller/Admin/CollectionController.php
Controller/Admin/CollectionController.php
- Model/Manipulator/LazaretManipulator.php
- Model/Manipulator/LazaretManipulator.php
+ Controller/Admin/DataboxController.php
+ Controller/Admin/DataboxController.php
+ Controller/Admin/DataboxController.php
+ Controller/Admin/DataboxController.php
+ Controller/Admin/DataboxController.php
+ Controller/Admin/DataboxController.php
+ Controller/Admin/DataboxController.php
+ Controller/Admin/DataboxController.php
+ Controller/Admin/DataboxController.php
+ Controller/Admin/DataboxController.php
+ Controller/Prod/LazaretController.php
+ Controller/Prod/MoveCollectionController.php
+ Controller/Prod/ToolsController.php
+ Controller/Prod/BasketController.php
+ Controller/Prod/StoryController.php
admin/collection/suggested_value.html.twig
admin/collection/collection.html.twig
- admin/databox/databox.html.twig
- web/admin/users.html.twig
+ web/admin/users.html.twig
task-manager/task-editor/task.html.twig
+ admin/databox/databox.html.twig
web/admin/databases.html.twig
@@ -1021,20 +1027,20 @@
An error occured, please retry or contact an admin if problem persists
Ein Fehler ist aufgetreten, bitte wiederholen Sie oder wenden Sie sich an Ihren Systemadministrator
- prod/orders/order_box.html.twig
prod/orders/order_item.html.twig
+ prod/orders/order_box.html.twig
An error occurred
Ein Fehler ist aufgetreten
Order/Controller/ProdOrderController.php
- Controller/Prod/BasketController.php
+ Controller/Admin/CollectionController.php
Controller/Admin/SearchEngineController.php
Controller/Admin/DataboxController.php
- Controller/Admin/CollectionController.php
+ Controller/Prod/BasketController.php
Controller/Api/V3Controller.php
- Controller/Api/V1Controller.php
- Controller/Api/V1Controller.php
+ Controller/Api/V1Controller.php
+ Controller/Api/V1Controller.php
web/admin/statusbit.html.twig
@@ -1126,14 +1132,14 @@
Apply a template
Eine Schablone anwenden
- web/admin/users.html.twig
- web/admin/editusers.html.twig
+ web/admin/users.html.twig
+ web/admin/editusers.html.twig
Apply changes
Änderungen anwenden
- actions/Property/type.html.twig
actions/Property/index.html.twig
+ actions/Property/type.html.twig
Apply status on story children.
@@ -1143,7 +1149,7 @@
Apply template
Vorlage anwenden
- web/admin/index.html.twig
+ web/admin/index.html.twig
Apply to all selected documents
@@ -1159,7 +1165,7 @@
Are you sure you want delete users rights ?
Sind Sie sicher, dass Sie die Benutzerzugriffe löschen möchten?
- web/admin/users.html.twig
+ web/admin/users.html.twig
Are you sure you want to delete this application?
@@ -1180,13 +1186,13 @@
Are you sure you want to reset rights?
Sind Sie sicher, die Rechte zurückzusetzen?
- web/admin/editusers.html.twig
+ web/admin/editusers.html.twig
Attention
Vorsicht
Controller/Prod/LanguageController.php
- web/admin/index.html.twig
+ web/admin/index.html.twig
Attention !
@@ -1272,8 +1278,8 @@
Audio Codec
Audio Codec
- Media/Subdef/Video.php
Media/Subdef/Audio.php
+ Media/Subdef/Video.php
Audio Samplerate
@@ -1364,20 +1370,20 @@
Autorisation d'acces
Zugriffsberechtigung
- api/auth/end_user_authorization.html.twig
api/auth/end_user_authorization.html.twig
+ api/auth/end_user_authorization.html.twig
Autoriser
Berechtigen
- api/auth/end_user_authorization.html.twig
api/auth/end_user_authorization.html.twig
+ api/auth/end_user_authorization.html.twig
Autorisez-vous l'application %application_name% a acceder a votre contenu sur %home_title% ?
Ermächtigen Sie die Anwendung %application_name%, Ihren Inhalt auf %home_title% zuzugreifen?
- api/auth/end_user_authorization.html.twig
api/auth/end_user_authorization.html.twig
+ api/auth/end_user_authorization.html.twig
Available in multi-export tab
@@ -1392,11 +1398,11 @@
Back
Zurück
- mobile/lightbox/basket_element.html.twig
- mobile/lightbox/validate.html.twig
mobile/lightbox/index.html.twig
mobile/lightbox/index.html.twig
mobile/lightbox/index.html.twig
+ mobile/lightbox/basket_element.html.twig
+ mobile/lightbox/validate.html.twig
Back to Feedback
@@ -1416,10 +1422,10 @@
Bad request format, only JSON is allowed
Bad Request Format, nur JSON wird erlaubt
- Controller/Root/AccountController.php
- Controller/Admin/DataboxController.php
Controller/Admin/RootController.php
Controller/Admin/RootController.php
+ Controller/Admin/DataboxController.php
+ Controller/Root/AccountController.php
Bad request, please contact an admin
@@ -1434,9 +1440,9 @@
Base %base%
Datenbank %base%
- web/admin/editusers_quotas.html.twig
web/admin/editusers_timelimit_sbas.html.twig
web/admin/editusers_timelimit.html.twig
+ web/admin/editusers_quotas.html.twig
Base could not be created
@@ -1556,7 +1562,7 @@
CHAMPS
Felder
- web/admin/tree.html.twig
+ web/admin/tree.html.twig
Camera Model
@@ -1568,10 +1574,10 @@
Cancel
Abbrechen
Controller/Prod/LanguageController.php
- prod/User/Add.html.twig
- prod/actions/delete_records_confirm_form.html.twig
- actions/Property/type.html.twig
actions/Property/index.html.twig
+ actions/Property/type.html.twig
+ prod/actions/delete_records_confirm_form.html.twig
+ prod/User/Add.html.twig
admin/fields/templates.html.twig
task-manager/task-editor/task.html.twig
user/import/view.html.twig
@@ -1600,9 +1606,9 @@
Categorie
Kategorie
- Bridge/Dailymotion/video_modify.html.twig
Bridge/Youtube/upload.html.twig
Bridge/Youtube/video_modify.html.twig
+ Bridge/Dailymotion/video_modify.html.twig
Ce champ est decrit comme element du %DublinCoreElementSet%
@@ -1634,12 +1640,12 @@
Dieses Feld ist ein Pflichtfeld
Bridge/Api/Dailymotion.php
Bridge/Api/Dailymotion.php
- Bridge/Api/Flickr.php
- Bridge/Api/Flickr.php
Bridge/Api/Youtube.php
Bridge/Api/Youtube.php
Bridge/Api/Youtube.php
Bridge/Api/Youtube.php
+ Bridge/Api/Flickr.php
+ Bridge/Api/Flickr.php
Ce champ est relie a une branche de thesaurus
@@ -1662,10 +1668,10 @@
Dieses Feld ist zu lang, maximal %length% Zeichen
Bridge/Api/Dailymotion.php
Bridge/Api/Dailymotion.php
- Bridge/Api/Flickr.php
- Bridge/Api/Flickr.php
Bridge/Api/Youtube.php
Bridge/Api/Youtube.php
+ Bridge/Api/Flickr.php
+ Bridge/Api/Flickr.php
Ce champ est utilise en titre a l'affichage
@@ -1741,8 +1747,8 @@
Choisir
wählen
- prod/Baskets/Reorder.html.twig
prod/Story/Reorder.html.twig
+ prod/Baskets/Reorder.html.twig
web/admin/subdefs.html.twig
web/admin/subdefs.html.twig
web/admin/subdefs.html.twig
@@ -1754,8 +1760,8 @@
Choose a new password
Wählen Sie ein neues Passwort aus
- web/account/change-password.html.twig
web/login/renew-password.html.twig
+ web/account/change-password.html.twig
Choose the title of the document to export
@@ -1770,8 +1776,8 @@
Clear
Klar
- admin/task-manager/log_task.html.twig
admin/task-manager/log_scheduler.html.twig
+ admin/task-manager/log_task.html.twig
Clear list
@@ -1807,8 +1813,8 @@
Codec Audio
Audio Codec
- web/common/technical_datas.html.twig
actions/Tools/videoEditor.html.twig
+ web/common/technical_datas.html.twig
Codec Video
@@ -1818,15 +1824,15 @@
Collection
Kollektion
- prod/upload/lazaret.html.twig
prod/Story/Create.html.twig
+ prod/upload/lazaret.html.twig
admin/databox/details.html.twig
Collection %collection%
%collection% Kollektion
- web/admin/editusers_quotas.html.twig
web/admin/editusers_timelimit.html.twig
+ web/admin/editusers_quotas.html.twig
Collection empty successful
@@ -1872,9 +1878,9 @@
Company
Unternehmen
- prod/User/Add.html.twig
actions/Feedback/list.html.twig
actions/Feedback/ListsMacros.html.twig
+ prod/User/Add.html.twig
Complete the fields below to register on %instance_title%!
@@ -1889,10 +1895,10 @@
Confidentialite
Vertraulichkeit
- Bridge/Dailymotion/upload.html.twig
- Bridge/Dailymotion/video_modify.html.twig
Bridge/Youtube/upload.html.twig
Bridge/Youtube/video_modify.html.twig
+ Bridge/Dailymotion/upload.html.twig
+ Bridge/Dailymotion/video_modify.html.twig
Confidentialite : privee
@@ -1917,7 +1923,7 @@
Confirm reset users rights before applying template
Zurücksetzung von Benutzerrechten bestätigen, bevor Sie die Vorlage anwenden
- web/admin/index.html.twig
+ web/admin/index.html.twig
Confirmation de votre mot de passe
@@ -1932,13 +1938,13 @@
Connection
Verbindung
- login/providers/bind.html.twig
- login/providers/bind.html.twig
- login/providers/mapping.html.twig
- login/providers/mapping.html.twig
- login/oauth/login.html.twig
web/login/index.html.twig
web/login/index.html.twig
+ login/oauth/login.html.twig
+ login/providers/mapping.html.twig
+ login/providers/mapping.html.twig
+ login/providers/bind.html.twig
+ login/providers/bind.html.twig
Connection is OK but database does not exists or can not be accessed
@@ -2059,9 +2065,9 @@
Creer
erstellen
- Bridge/Dailymotion/actioncontainers.html.twig
- Bridge/Flickr/actioncontainers.html.twig
Bridge/Youtube/actioncontainers.html.twig
+ Bridge/Flickr/actioncontainers.html.twig
+ Bridge/Dailymotion/actioncontainers.html.twig
Creer la tache d'ecriture des metadonnees
@@ -2086,17 +2092,17 @@
Creer un model
eine Vorlage erstellen
- web/admin/index.html.twig
+ web/admin/index.html.twig
Creer un modele
eine Vorlage erstellen
- web/admin/index.html.twig
+ web/admin/index.html.twig
Creer un utilisateur
einen Benutzer erstellen
- web/admin/index.html.twig
+ web/admin/index.html.twig
Creer une nouvelle applications
@@ -2106,8 +2112,8 @@
Creer une playlist
einen neuen Playlist erstellen
- Bridge/Dailymotion/playlist_createcontainer.html.twig
Bridge/Youtube/playlist_createcontainer.html.twig
+ Bridge/Dailymotion/playlist_createcontainer.html.twig
Creez une application pour commencer a utiliser l'API Phraseanet
@@ -2251,21 +2257,21 @@
Date de création
Erstellungsdatum
- prod/Baskets/Reorder.html.twig
prod/Story/Reorder.html.twig
+ prod/Baskets/Reorder.html.twig
Date de demande
Abfrage Datum
+ prod/orders/order_item.html.twig
prod/orders/order_box.html.twig
prod/orders/order_box.html.twig
- prod/orders/order_item.html.twig
Date de modification
Änderungsdatum
- prod/Baskets/Reorder.html.twig
prod/Story/Reorder.html.twig
+ prod/Baskets/Reorder.html.twig
Date(s) from field(s)
@@ -2281,9 +2287,9 @@
Deadline
Termin
+ prod/orders/order_item.html.twig
prod/orders/order_box.html.twig
prod/orders/order_box.html.twig
- prod/orders/order_item.html.twig
Dear %user%,
@@ -2380,8 +2386,8 @@
Delete all users rights
Alle Nutzerrechte löschen
- web/admin/users.html.twig
- web/admin/editusers.html.twig
+ web/admin/users.html.twig
+ web/admin/editusers.html.twig
Delete basket
@@ -2416,16 +2422,16 @@
Deny
verweigern
- login/oauth/authorize-access.html.twig
prod/orders/order_item.html.twig
prod/orders/order_item.html.twig
+ login/oauth/authorize-access.html.twig
Deplacement %n_element% elements
Bewegung von %n_element% Elemente
- Bridge/Dailymotion/video_moveinto_playlist.html.twig
- Bridge/Flickr/photo_moveinto_photoset.html.twig
Bridge/Youtube/video_moveinto_playlist.html.twig
+ Bridge/Flickr/photo_moveinto_photoset.html.twig
+ Bridge/Dailymotion/video_moveinto_playlist.html.twig
Dernier access
@@ -2435,8 +2441,8 @@
Derniere mise a jour le %updated_on%
letztes Update am %updated_on%
- prod/results/entry.html.twig
prod/results/feeds_entry.html.twig
+ prod/results/entry.html.twig
Derniers envois
@@ -2451,24 +2457,24 @@
Description
Beschreibung
- web/developers/application_form.html.twig
- Bridge/Dailymotion/upload.html.twig
- Bridge/Dailymotion/video_modify.html.twig
+ Bridge/Youtube/upload.html.twig
+ Bridge/Youtube/playlist_createcontainer.html.twig
+ Bridge/Youtube/video_modify.html.twig
Bridge/Flickr/photoset_createcontainer.html.twig
Bridge/Flickr/upload.html.twig
Bridge/Flickr/photo_modify.html.twig
- Bridge/Youtube/playlist_createcontainer.html.twig
- Bridge/Youtube/upload.html.twig
- Bridge/Youtube/video_modify.html.twig
+ Bridge/Dailymotion/upload.html.twig
+ Bridge/Dailymotion/video_modify.html.twig
prod/Tooltip/DCESFieldInfo.html.twig
+ web/developers/application_form.html.twig
Deselect all
Alle abwählen
prod/actions/Push.html.twig
actions/Feedback/list.html.twig
- web/report/form_date_and_base.html.twig
web/report/report_layout_child.html.twig
+ web/report/form_date_and_base.html.twig
Design of personalization logo section
@@ -2499,8 +2505,8 @@
Dimension
Grösse
- Media/Subdef/Video.php
Media/Subdef/Image.php
+ Media/Subdef/Video.php
Media/Subdef/Unknown.php
@@ -2650,8 +2656,8 @@
E-mail
E-Mail Adresse
- Form/Login/PhraseaRegisterForm.php
Form/Login/PhraseaForgotPasswordForm.php
+ Form/Login/PhraseaRegisterForm.php
E-mail domain
@@ -2706,19 +2712,19 @@
Edition de 1 element
Bearbeitung von 1 Element
- Bridge/Dailymotion/video_modify.html.twig
- Bridge/Flickr/photo_modify.html.twig
Bridge/Youtube/video_modify.html.twig
+ Bridge/Flickr/photo_modify.html.twig
+ Bridge/Dailymotion/video_modify.html.twig
Edition des droits de %display_name%
Bearbeitung von %display_name% Rechten
- web/admin/editusers.html.twig
+ web/admin/editusers.html.twig
Edition des droits de %number% utilisateurs
Bearbeitung von %number% Benutzer Rechten
- web/admin/editusers.html.twig
+ web/admin/editusers.html.twig
Edition impossible
@@ -2761,8 +2767,8 @@
Email
Email Adresse
- web/admin/dashboard.html.twig
admin/publications/fiche.html.twig
+ web/admin/dashboard.html.twig
Email '%email%' for login '%login%' already exists in database
@@ -2792,8 +2798,8 @@
Email successfully confirmed
E-Mail erfolgreich bestätigt
- Notification/Mail/MailSuccessEmailConfirmationUnregistered.php
Notification/Mail/MailSuccessEmailConfirmationRegistered.php
+ Notification/Mail/MailSuccessEmailConfirmationUnregistered.php
Email test result : %email_status%
@@ -3016,8 +3022,8 @@
Erreur !
Fehler !
- mobile/lightbox/error.html.twig
web/lightbox/error.html.twig
+ mobile/lightbox/error.html.twig
Erreur : soit les parametres sont incorrects, soit le serveur distant ne repond pas
@@ -3110,8 +3116,8 @@
Error while sending the file
Fehler beim Datei Senden
- Controller/Admin/DataboxController.php
Controller/Admin/CollectionController.php
+ Controller/Admin/DataboxController.php
Error while uploading
@@ -3137,14 +3143,14 @@
Etes vous sur de supprimer %number% playlists ?
Sind Sie sicher, %number% Playlisten zu löschen?
- Bridge/Dailymotion/playlist_deleteelement.html.twig
Bridge/Youtube/playlist_deleteelement.html.twig
+ Bridge/Dailymotion/playlist_deleteelement.html.twig
Etes vous sur de supprimer %number% videos ?
Sind Sie sicher, %number% Videos zu löschen?
- Bridge/Dailymotion/video_deleteelement.html.twig
Bridge/Youtube/video_deleteelement.html.twig
+ Bridge/Dailymotion/video_deleteelement.html.twig
Ex : Paris, bleu, montagne
@@ -3207,10 +3213,10 @@
Feedback
Feedback
Controller/Prod/LanguageController.php
- web/prod/toolbar.html.twig
+ prod/WorkZone/Macros.html.twig
prod/WorkZone/Basket.html.twig
prod/WorkZone/Story.html.twig
- prod/WorkZone/Macros.html.twig
+ web/prod/toolbar.html.twig
Feeds
@@ -3265,11 +3271,11 @@
File is not present in quarantine anymore, please refresh
Datei befindet sich nicht mehr in der Quarantäne, bitte aktualisieren
- Controller/Prod/LazaretController.php
- Controller/Prod/LazaretController.php
Model/Manipulator/LazaretManipulator.php
Model/Manipulator/LazaretManipulator.php
Model/Manipulator/LazaretManipulator.php
+ Controller/Prod/LazaretController.php
+ Controller/Prod/LazaretController.php
File is too big : 64k max
@@ -3304,8 +3310,8 @@
Fils disponibles
Verfügbare Threads
- actions/publish/publish.html.twig
actions/publish/publish_edit.html.twig
+ actions/publish/publish.html.twig
Filter
@@ -3327,13 +3333,13 @@
Vorname / Nachname
actions/Feedback/ListsMacros.html.twig
web/admin/users.html.twig
- web/admin/users.html.twig
+ web/admin/users.html.twig
Flash
Flash
- web/common/technical_datas.html.twig
web/prod/index.html.twig
+ web/common/technical_datas.html.twig
FlashFired
@@ -3374,10 +3380,10 @@
Forgot password?
Passwort vergessen ?
- login/providers/bind.html.twig
- login/providers/mapping.html.twig
- login/oauth/login.html.twig
web/login/index.html.twig
+ login/oauth/login.html.twig
+ login/providers/mapping.html.twig
+ login/providers/bind.html.twig
Forgot your password?
@@ -3558,9 +3564,9 @@
Guest
Gast
- mobile/common/menubar.html.twig
web/common/menubar.html.twig
web/common/menubar.html.twig
+ mobile/common/menubar.html.twig
Guest access
@@ -3591,8 +3597,8 @@
Hello %username%
Hallo %username%
api/auth/native_app_access_token.html.twig
- api/auth/end_user_authorization.html.twig
api/auth/end_user_authorization.html.twig
+ api/auth/end_user_authorization.html.twig
Help
@@ -3612,11 +3618,11 @@
Home
Hautpseite
- mobile/lightbox/basket_element.html.twig
- mobile/lightbox/validate.html.twig
- mobile/lightbox/feed.html.twig
- login/include/language-block.html.twig
login/layout/base-layout.html.twig
+ login/include/language-block.html.twig
+ mobile/lightbox/basket_element.html.twig
+ mobile/lightbox/feed.html.twig
+ mobile/lightbox/validate.html.twig
Homepage slideshow
@@ -3707,8 +3713,8 @@
Images par secondes
Bilder pro Sekunde
- web/common/technical_datas.html.twig
actions/Tools/videoEditor.html.twig
+ web/common/technical_datas.html.twig
Imagette indisponible
@@ -3755,9 +3761,9 @@
Informations
Informationen
- web/account/base.html.twig
- web/admin/dashboard.html.twig
admin/user/registrations.html.twig
+ web/admin/dashboard.html.twig
+ web/account/base.html.twig
Informations personnelles
@@ -3767,7 +3773,7 @@
Infos
Informationen
- web/admin/editusers.html.twig
+ web/admin/editusers.html.twig
Inscription
@@ -3793,8 +3799,8 @@
Invalid file format
Ungültiges Datei Format
- Controller/Admin/DataboxController.php
Controller/Admin/CollectionController.php
+ Controller/Admin/DataboxController.php
Invalid file type
@@ -3805,9 +3811,9 @@
Invalid file type, only (%supported_file_types%) file formats are supported
ungültiger Dateityp, nur (%supported_file_types%) Dateitypen werden unterstützt
- admin/statusbit/edit.html.twig
- admin/databox/databox.html.twig
user/import/file.html.twig
+ admin/databox/databox.html.twig
+ admin/statusbit/edit.html.twig
Invalid file type, only (%supported_file_types%) file formats are supported'
@@ -3817,8 +3823,8 @@
Invalid labels parameter
ungültige Labels Parameter
- Controller/Admin/DataboxController.php
Controller/Admin/CollectionController.php
+ Controller/Admin/DataboxController.php
Invalid link.
@@ -3844,8 +3850,8 @@
Inverser
umkehren
- prod/Baskets/Reorder.html.twig
prod/Story/Reorder.html.twig
+ prod/Baskets/Reorder.html.twig
It is not recommended to install Phraseanet without HTTPS support
@@ -3880,8 +3886,8 @@
L'upload a echoue
Upload ist fehlgeschlagen
- Bridge/Api/Flickr.php
Bridge/Api/Youtube.php
+ Bridge/Api/Flickr.php
L'upload concernant le record %title% sur le compte %bridge_name% a echoue pour les raisons suivantes : %reason%
@@ -3891,17 +3897,17 @@
L'utilisateur approuve ce document
Benutzer genehmigt dieses Dokument
- prod/WorkZone/Macros.html.twig
+ prod/WorkZone/Macros.html.twig
L'utilisateur desapprouve ce document
Benutzer lehnt dieses Dokument ab
- prod/WorkZone/Macros.html.twig
+ prod/WorkZone/Macros.html.twig
L'utilisateur n'a pas encore donne son avis sur ce document
Benutzer hat seine Meinung noch nicht gegeben
- prod/WorkZone/Macros.html.twig
+ prod/WorkZone/Macros.html.twig
La connection vers le serveur distant est OK
@@ -3962,7 +3968,7 @@
Last applied template
letzte angewandte Vorlage
- web/admin/users.html.twig
+ web/admin/users.html.twig
Last name is required
@@ -4028,15 +4034,15 @@
Le poids maximum d'un fichier est de %size%
Maximales Gewicht von Datei ist %size%
Bridge/Api/Dailymotion.php
- Bridge/Api/Flickr.php
Bridge/Api/Youtube.php
+ Bridge/Api/Flickr.php
Le record n'a pas de fichier physique
Der Datensatz hat keine physikalische Datei
Bridge/Api/Dailymotion.php
- Bridge/Api/Flickr.php
Bridge/Api/Youtube.php
+ Bridge/Api/Flickr.php
Le token n'a pas encore ete genere
@@ -4057,9 +4063,9 @@
Les elements ne peuvent etre uploades (problemes de type ou de droit)
Die Elemente können nicht hochgeladen werden (Typ oder Rechte Problem).
- Bridge/Dailymotion/upload.html.twig
- Bridge/Flickr/upload.html.twig
Bridge/Youtube/upload.html.twig
+ Bridge/Flickr/upload.html.twig
+ Bridge/Dailymotion/upload.html.twig
Les indications donnees ci dessous sont a titre informatif.
@@ -4179,10 +4185,10 @@
Login
Benutzername
Form/Login/PhraseaAuthenticationForm.php
- login/providers/bind.html.twig
- login/providers/mapping.html.twig
- login/oauth/login.html.twig
actions/Feedback/ListsMacros.html.twig
+ login/oauth/login.html.twig
+ login/providers/mapping.html.twig
+ login/providers/bind.html.twig
Login %login% already exists in database
@@ -4202,8 +4208,8 @@
Login to link your account
Einloggen, um Ihr Konto zu verknüpfen
- login/providers/bind.html.twig
login/providers/mapping.html.twig
+ login/providers/bind.html.twig
Logs
@@ -4239,32 +4245,32 @@
Manage DB fields
Datenbank Felder verwalten
- web/admin/editusers.html.twig
+ web/admin/editusers.html.twig
Manage Database
Datenbank verwalten
- web/admin/editusers.html.twig
+ web/admin/editusers.html.twig
Manage Thesaurus
Thesaurus verwalten
- web/admin/editusers.html.twig
+ web/admin/editusers.html.twig
Manage collection
Kollektion verwalten
- web/admin/editusers.html.twig
+ web/admin/editusers.html.twig
Manage users
Benutzer verwalten
- web/admin/editusers.html.twig
+ web/admin/editusers.html.twig
Manage values lists
Wertelisten verwalten
- web/admin/editusers.html.twig
+ web/admin/editusers.html.twig
Mandatory
@@ -4345,8 +4351,8 @@
Missing labels parameter
Labels-Parameter fehlt
- Controller/Admin/DataboxController.php
Controller/Admin/CollectionController.php
+ Controller/Admin/DataboxController.php
Missing mandatory parameter %parameter%
@@ -4488,8 +4494,8 @@
Ne pas autoriser
Nicht berechtigen
- api/auth/end_user_authorization.html.twig
api/auth/end_user_authorization.html.twig
+ api/auth/end_user_authorization.html.twig
Ne pas creer de DataBox maintenant
@@ -4522,15 +4528,15 @@
Next
Weiter
- actions/Feedback/ListsMacros.html.twig
prod/orders/order_box.html.twig
prod/orders/order_box.html.twig
+ actions/Feedback/ListsMacros.html.twig
No
Nein
- web/account/account.html.twig
web/developers/applications.html.twig
+ web/account/account.html.twig
No URL available
@@ -4647,13 +4653,13 @@
Nom du nouveau modele
Name der neue Vorlage
- web/admin/index.html.twig
+ web/admin/index.html.twig
Nom du nouveau panier
Titel
- prod/Baskets/Create.html.twig
prod/orders/order_item.html.twig
+ prod/Baskets/Create.html.twig
Non-Restreinte (publique)
@@ -4665,7 +4671,7 @@
None
Keine
Form/Configuration/EmailFormType.php
- web/admin/users.html.twig
+ web/admin/users.html.twig
admin/user/registrations.html.twig
@@ -4832,10 +4838,10 @@
Or login with
Oder Anmeldung mit
- web/login/register.html.twig
- login/oauth/login.html.twig
- web/login/index.html.twig
api/auth/end_user_authorization.html.twig
+ web/login/index.html.twig
+ login/oauth/login.html.twig
+ web/login/register.html.twig
Order
@@ -4846,15 +4852,15 @@
Order has been denied
Bestellung wurde verweigert
Order/Controller/ProdOrderController.php
- prod/orders/order_box.html.twig
prod/orders/order_item.html.twig
+ prod/orders/order_box.html.twig
Order has been sent
Bestellung wurde gesendet
Order/Controller/ProdOrderController.php
- prod/orders/order_box.html.twig
prod/orders/order_item.html.twig
+ prod/orders/order_box.html.twig
Orders manager
@@ -4907,19 +4913,19 @@
Paniers
Sammelkörbe
+ web/lightbox/index.html.twig
+ lightbox/IE6/validate.html.twig
+ web/lightbox/validate.html.twig
+ web/account/account.html.twig
mobile/lightbox/index.html.twig
mobile/lightbox/index.html.twig
- web/account/account.html.twig
- web/lightbox/validate.html.twig
- lightbox/IE6/validate.html.twig
- web/lightbox/index.html.twig
Par %author%
Von %author%
- mobile/lightbox/feed.html.twig
- prod/results/entry.html.twig
prod/results/feeds_entry.html.twig
+ prod/results/entry.html.twig
+ mobile/lightbox/feed.html.twig
Password
@@ -5022,8 +5028,8 @@
Playlist
Playlist
- Bridge/Dailymotion/actionelements.html.twig
Bridge/Youtube/actionelements.html.twig
+ Bridge/Dailymotion/actionelements.html.twig
Playlists
@@ -5183,9 +5189,9 @@
Previous
Zurück
- actions/Feedback/ListsMacros.html.twig
prod/orders/order_box.html.twig
prod/orders/order_box.html.twig
+ actions/Feedback/ListsMacros.html.twig
Print
@@ -5195,8 +5201,8 @@
Problemes de connexion ?
Verbindungsprobleme ?
- api/auth/end_user_authorization.html.twig
api/auth/end_user_authorization.html.twig
+ api/auth/end_user_authorization.html.twig
Process the registration
@@ -5226,10 +5232,10 @@
Publications
Veröffentlichungen
- web/common/menubar.html.twig
web/prod/index.html.twig
- web/admin/tree.html.twig
+ web/common/menubar.html.twig
admin/publications/wrapper.html.twig
+ web/admin/tree.html.twig
Publier
@@ -5302,7 +5308,6 @@
Push::filter starts
beginnt mit
actions/Feedback/list.html.twig
- web/admin/users.html.twig
Push::une validation est une demande d'appreciation a d'autres personnes
@@ -5432,13 +5437,14 @@
Re-initialiser
Zurücksetzen
- prod/Baskets/Reorder.html.twig
- prod/Story/Reorder.html.twig
web/prod/index.html.twig
+ prod/Story/Reorder.html.twig
+ prod/Baskets/Reorder.html.twig
Re-ordonner
wieder ordnen
+ prod/Story/Reorder.html.twig
prod/WorkZone/Macros.html.twig
prod/WorkZone/Macros.html.twig
prod/WorkZone/Macros.html.twig
@@ -5446,7 +5452,6 @@
prod/WorkZone/Macros.html.twig
prod/WorkZone/Macros.html.twig
prod/Baskets/Reorder.html.twig
- prod/Story/Reorder.html.twig
Read-only
@@ -5557,7 +5562,7 @@
Record Not Found
Datensatz wurde nicht gefunden
- Controller/Api/V1Controller.php
+ Controller/Api/V1Controller.php
Record removed from basket
@@ -5599,8 +5604,8 @@
Register
Registrieren
login/include/register-link-block.html.twig
- web/login/register.html.twig
web/login/register-provider.html.twig
+ web/login/register.html.twig
web/login/register-classic.html.twig
@@ -5617,13 +5622,13 @@
Reglages:: reglages d acces guest
Gast Zugriff Einstellungen
web/admin/users.html.twig
- web/admin/editusers.html.twig
+ web/admin/editusers.html.twig
Reglages:: reglages d inscitpition automatisee
Auto Register Einstellungen
web/admin/users.html.twig
- web/admin/editusers.html.twig
+ web/admin/editusers.html.twig
Relevance
@@ -5659,7 +5664,7 @@
Remove watermark
Wasserzeichen entfernen
- web/admin/editusers.html.twig
+ web/admin/editusers.html.twig
Rename
@@ -5670,8 +5675,8 @@
Renew password
Passwort erneuern
Notification/Mail/MailRequestPasswordUpdate.php
- web/account/change-password.html.twig
web/login/renew-password.html.twig
+ web/account/change-password.html.twig
Reorder collections
@@ -5681,8 +5686,8 @@
Reordonner automatiquement
automatisch neu anordnen
- prod/Baskets/Reorder.html.twig
prod/Story/Reorder.html.twig
+ prod/Baskets/Reorder.html.twig
Repertoire de stockage des fichiers
@@ -5718,7 +5723,7 @@
Require email validation to activate the account
Email Validierung erfordern, um Konto zu aktivieren
- web/admin/index.html.twig
+ web/admin/index.html.twig
Required
@@ -5738,12 +5743,12 @@
Reset and apply
Zurücksetzen und Vorlage anwenden
- web/admin/index.html.twig
+ web/admin/index.html.twig
Reset and apply template
Zurücksetzen und Vorlage anwenden
- web/admin/index.html.twig
+ web/admin/index.html.twig
Reset cache
@@ -5753,7 +5758,7 @@
Reset rights before applying template?
Möchten Sie die Benutzerrechte zurücksetzen, bevor Sie die Vorlage anwenden?
- web/admin/index.html.twig
+ web/admin/index.html.twig
Resolution
@@ -5789,8 +5794,8 @@
Restriction
Einschränkung
- web/admin/editusers_quotas.html.twig
admin/publications/list.html.twig
+ web/admin/editusers_quotas.html.twig
Restrictions de telechargement
@@ -5835,12 +5840,12 @@
Rights
Rechte
- web/admin/editusers.html.twig
+ web/admin/editusers.html.twig
Rights will be erased before applying template, do you confirm?
Benutzerrechte werden gelöscht, bevor Sie die Vorlage anwenden. Bestätigen?
- web/admin/index.html.twig
+ web/admin/index.html.twig
Role
@@ -5900,19 +5905,19 @@
SUBDEFS
Unterauflösungen
- web/admin/tree.html.twig
+ web/admin/tree.html.twig
Save
Speichern
- web/account/change-password.html.twig
- web/login/renew-password.html.twig
+ actions/Feedback/list.html.twig
web/developers/application.html.twig
web/developers/application.html.twig
- actions/Feedback/list.html.twig
- admin/search-engine/general-aggregation.html.twig
- admin/search-engine/elastic-search.html.twig
task-manager/task-editor/task.html.twig
+ admin/search-engine/elastic-search.html.twig
+ admin/search-engine/general-aggregation.html.twig
+ web/login/renew-password.html.twig
+ web/account/change-password.html.twig
Save all changes
@@ -5958,7 +5963,7 @@
SearchEngine settings
Suchmaschine Einstellungen
web/setup/step2.html.twig
- web/admin/tree.html.twig
+ web/admin/tree.html.twig
Security
@@ -5968,8 +5973,8 @@
See
Sehen
- WorkZone/Browser/Browser.html.twig
prod/WorkZone/Macros.html.twig
+ WorkZone/Browser/Browser.html.twig
See documentation about structure manipulation.
@@ -5985,8 +5990,8 @@
See my order
Meine Bestellung ansehen
- Notification/Mail/MailInfoOrderDelivered.php
Notification/Mail/MailInfoOrderCancelled.php
+ Notification/Mail/MailInfoOrderDelivered.php
See others
@@ -6018,8 +6023,8 @@
Alle auswählen
prod/actions/Push.html.twig
actions/Feedback/list.html.twig
- web/report/form_date_and_base.html.twig
web/report/report_layout_child.html.twig
+ web/report/form_date_and_base.html.twig
Select all collections
@@ -6031,12 +6036,12 @@
Dateien auswählen...
prod/upload/upload.html.twig
prod/upload/upload-flash.html.twig
- admin/statusbit/edit.html.twig
- admin/statusbit/edit.html.twig
admin/collection/collection.html.twig
admin/collection/collection.html.twig
admin/collection/collection.html.twig
user/import/file.html.twig
+ admin/statusbit/edit.html.twig
+ admin/statusbit/edit.html.twig
Selected base(s)
@@ -6052,19 +6057,19 @@
Send
Senden
Controller/Prod/LanguageController.php
- web/login/forgot-password.html.twig
+ prod/orders/order_item.html.twig
+ prod/orders/order_item.html.twig
prod/actions/Push.html.twig
prod/actions/Push.html.twig
prod/upload/upload.html.twig
prod/upload/upload-flash.html.twig
- prod/orders/order_item.html.twig
- prod/orders/order_item.html.twig
web/admin/dashboard.html.twig
+ web/login/forgot-password.html.twig
Send an email to the user to setup his password
Eine Email zum Benutzer senden, um sein Passwort festzulegen
- web/admin/index.html.twig
+ web/admin/index.html.twig
Send to Facebook
@@ -6117,7 +6122,7 @@
Set download quotas
Download Quoten festlegen
- web/admin/editusers.html.twig
+ web/admin/editusers.html.twig
Set labels
@@ -6128,18 +6133,18 @@
Set statuses restrictions
Status Beschränkungen festlegen
- web/admin/editusers.html.twig
+ web/admin/editusers.html.twig
Set time restrictions
Zeitliche Beschränkungen festlegen
- web/admin/editusers.html.twig
+ web/admin/editusers.html.twig
Setup
Aufstellung
- web/admin/tree.html.twig
web/admin/setup.html.twig
+ web/admin/tree.html.twig
Setup my password
@@ -6211,9 +6216,9 @@
Size
Grösse
- web/common/technical_datas.html.twig
actions/Tools/videoEditor.html.twig
actions/Download/prepare.html.twig
+ web/common/technical_datas.html.twig
Slide show
@@ -6307,8 +6312,8 @@
Start validation
Bestätigung starten
- Notification/Mail/MailInfoValidationRequest.php
Notification/Mail/MailInfoValidationReminder.php
+ Notification/Mail/MailInfoValidationRequest.php
Started
@@ -6358,7 +6363,7 @@
Story Not Found
Bericht wurde nicht gefunden
Controller/Api/V3Controller.php
- Controller/Api/V1Controller.php
+ Controller/Api/V1Controller.php
Story created
@@ -6430,21 +6435,27 @@
Successful install
Erfolgreiche Installation
- web/admin/index.html.twig
+ web/admin/index.html.twig
Successful removal
erfolgreiches Löschen
- Controller/Admin/DataboxController.php
- Controller/Admin/DataboxController.php
Controller/Admin/CollectionController.php
Controller/Admin/CollectionController.php
Controller/Admin/CollectionController.php
Controller/Admin/CollectionController.php
+ Controller/Admin/DataboxController.php
+ Controller/Admin/DataboxController.php
Successful update
Erfolgreiches Update
+ Controller/Admin/CollectionController.php
+ Controller/Admin/CollectionController.php
+ Controller/Admin/CollectionController.php
+ Controller/Admin/CollectionController.php
+ Controller/Admin/CollectionController.php
+ Controller/Admin/CollectionController.php
Controller/Admin/DataboxController.php
Controller/Admin/DataboxController.php
Controller/Admin/DataboxController.php
@@ -6452,12 +6463,6 @@
Controller/Admin/DataboxController.php
Controller/Admin/DataboxController.php
Controller/Admin/DataboxController.php
- Controller/Admin/CollectionController.php
- Controller/Admin/CollectionController.php
- Controller/Admin/CollectionController.php
- Controller/Admin/CollectionController.php
- Controller/Admin/CollectionController.php
- Controller/Admin/CollectionController.php
actions/Tools/videoEditor.html.twig
admin/collection/suggested_value.html.twig
admin/collection/collection.html.twig
@@ -6491,19 +6496,19 @@
Suppression de %n_element% playlists
Löschen von %n_element% Playlisten
- Bridge/Dailymotion/playlist_deleteelement.html.twig
Bridge/Youtube/playlist_deleteelement.html.twig
+ Bridge/Dailymotion/playlist_deleteelement.html.twig
Suppression de %n_element% videos
Löschen von %n_element% Videos
- Bridge/Dailymotion/video_deleteelement.html.twig
Bridge/Youtube/video_deleteelement.html.twig
+ Bridge/Dailymotion/video_deleteelement.html.twig
Supprimer
Löschen
- web/admin/users.html.twig
+ web/admin/users.html.twig
Supprimer egalement les documents rattaches a ces regroupements
@@ -6534,11 +6539,11 @@
Tags
Tags
- Bridge/Dailymotion/upload.html.twig
- Bridge/Dailymotion/video_modify.html.twig
- Bridge/Flickr/upload.html.twig
Bridge/Youtube/upload.html.twig
Bridge/Youtube/video_modify.html.twig
+ Bridge/Flickr/upload.html.twig
+ Bridge/Dailymotion/upload.html.twig
+ Bridge/Dailymotion/video_modify.html.twig
Target Device
@@ -6597,9 +6602,9 @@
Terms of use
Nutzungsbedingungen
Controller/Prod/TOUController.php
+ web/admin/tree.html.twig
web/login/cgus.html.twig
login/layout/base-layout.html.twig
- web/admin/tree.html.twig
The Phraseanet Web API allows other web application to rely on this instance
@@ -6680,8 +6685,8 @@
The file is too big
Datei ist zu gross
- Controller/Admin/DataboxController.php
Controller/Admin/CollectionController.php
+ Controller/Admin/DataboxController.php
The file was moved to the quarantine
@@ -6701,8 +6706,8 @@
The publication has been stopped
Veröffentlichung wurde gestoppt
- Controller/Admin/DataboxController.php
Controller/Admin/CollectionController.php
+ Controller/Admin/DataboxController.php
The record was successfully created
@@ -6805,7 +6810,7 @@
This field is required
Dieses Feld ist erforderlich
- web/admin/index.html.twig
+ web/admin/index.html.twig
This field represents the title of the document
@@ -6816,12 +6821,12 @@
This file is too big
Datei ist zu gross
Controller/Prod/LanguageController.php
- web/admin/index.html.twig
+ web/admin/index.html.twig
This is a template
Hier ist eine Schablone
- web/admin/users.html.twig
+ web/admin/users.html.twig
This link is valid until
@@ -6836,12 +6841,12 @@
This user does not participate to the validation but is only viewer.
Dieser Benutzer darf nicht teilnehmen, nur ansehen.
- prod/WorkZone/Macros.html.twig
+ prod/WorkZone/Macros.html.twig
This user has no rights
Dieser Benutzer besitzt keine Rechte
- web/admin/users.html.twig
+ web/admin/users.html.twig
This year
@@ -6872,16 +6877,16 @@
Titre
Titel
- Bridge/Dailymotion/upload.html.twig
- Bridge/Dailymotion/video_modify.html.twig
+ Bridge/Youtube/upload.html.twig
+ Bridge/Youtube/playlist_createcontainer.html.twig
+ Bridge/Youtube/video_modify.html.twig
Bridge/Flickr/photoset_createcontainer.html.twig
Bridge/Flickr/upload.html.twig
Bridge/Flickr/photo_modify.html.twig
- Bridge/Youtube/playlist_createcontainer.html.twig
- Bridge/Youtube/upload.html.twig
- Bridge/Youtube/video_modify.html.twig
- prod/Baskets/Reorder.html.twig
+ Bridge/Dailymotion/upload.html.twig
+ Bridge/Dailymotion/video_modify.html.twig
prod/Story/Reorder.html.twig
+ prod/Baskets/Reorder.html.twig
admin/publications/list.html.twig
admin/publications/list.html.twig
admin/publications/fiche.html.twig
@@ -7002,8 +7007,8 @@
Unable to add file to Phraseanet
Unmöglich, Datei zu Phraseanet hinzuzufügen
- Controller/Prod/UploadController.php
Controller/Admin/FeedController.php
+ Controller/Prod/UploadController.php
Unable to add usr to list
@@ -7157,16 +7162,16 @@
Upload
Upload
- web/common/menubar.html.twig
- Bridge/Dailymotion/upload.html.twig
- Bridge/Dailymotion/upload.html.twig
- Bridge/Flickr/upload.html.twig
- Bridge/Flickr/upload.html.twig
+ actions/Bridge/index.html.twig
Bridge/Youtube/upload.html.twig
Bridge/Youtube/upload.html.twig
- actions/Bridge/index.html.twig
+ Bridge/Flickr/upload.html.twig
+ Bridge/Flickr/upload.html.twig
+ Bridge/Dailymotion/upload.html.twig
+ Bridge/Dailymotion/upload.html.twig
prod/upload/upload.html.twig
prod/upload/upload-flash.html.twig
+ web/common/menubar.html.twig
Upload URL is not set, please contact an admin
@@ -7319,8 +7324,8 @@
VALIDATION
Bestätigung
- web/lightbox/validate.html.twig
web/lightbox/agreement_box.html.twig
+ web/lightbox/validate.html.twig
Validate e-mail address
@@ -7330,11 +7335,11 @@
Validation
Bestätigung
- eventsmanager/notify/validationdone.php
eventsmanager/notify/validationreminder.php
eventsmanager/notify/validationreminder.php
eventsmanager/notify/validate.php
eventsmanager/notify/validate.php
+ eventsmanager/notify/validationdone.php
lightbox/IE6/validate.html.twig
@@ -7350,11 +7355,11 @@
Validations
Bestätigung
+ web/lightbox/index.html.twig
+ lightbox/IE6/validate.html.twig
+ web/lightbox/validate.html.twig
mobile/lightbox/index.html.twig
mobile/lightbox/index.html.twig
- web/lightbox/validate.html.twig
- lightbox/IE6/validate.html.twig
- web/lightbox/index.html.twig
Validations received
@@ -7425,14 +7430,14 @@
Voici vos paniers
Ihre Sammelkörbe
- mobile/lightbox/index.html.twig
web/lightbox/index.html.twig
+ mobile/lightbox/index.html.twig
Voici vos validations en cours
Hier sind Ihre aktuellen Bewertungen
- mobile/lightbox/index.html.twig
web/lightbox/index.html.twig
+ mobile/lightbox/index.html.twig
Votre adresse email
@@ -7557,10 +7562,10 @@
Vous n'avez selectionne aucun element
Sie haben kein Element ausgewählt.
- Bridge/Dailymotion/upload.html.twig
- Bridge/Flickr/upload.html.twig
- Bridge/Youtube/upload.html.twig
actions/Bridge/index.html.twig
+ Bridge/Youtube/upload.html.twig
+ Bridge/Flickr/upload.html.twig
+ Bridge/Dailymotion/upload.html.twig
Vous ne pouvez pas editer plusieurs elements simultanement
@@ -7647,8 +7652,8 @@
Which playlist you want to put you %number% elements into ?
Welche Playlist möchten Sie für Ihre %number% Elemente benutzen?
- Bridge/Dailymotion/video_moveinto_playlist.html.twig
Bridge/Youtube/video_moveinto_playlist.html.twig
+ Bridge/Dailymotion/video_moveinto_playlist.html.twig
Whoops, looks like something went wrong.
@@ -7657,6 +7662,11 @@
Phrasea/Core/PhraseaExceptionHandler.php
Event/Subscriber/ApiOauth2ErrorsSubscriber.php
+
+ Worker Manager
+ Worker Manager
+ web/admin/tree.html.twig
+
Would you like to continue ?
Möchten Sie, fortzufahren?
@@ -7670,8 +7680,8 @@
Would you like to reset rights before applying the template?
Möchten Sie die Benutzerrechte zurücksetzen, bevor Sie die Vorlage anwenden?
- web/admin/index.html.twig
- web/admin/index.html.twig
+ web/admin/index.html.twig
+ web/admin/index.html.twig
Write Metas
@@ -7693,9 +7703,9 @@
Yes
Ja
- web/account/account.html.twig
web/developers/applications.html.twig
user/import/view.html.twig
+ web/account/account.html.twig
You are Admin
@@ -8009,85 +8019,85 @@
action : bridge
Bridge
- web/prod/index.html.twig
+ web/prod/index.html.twig
action : collection
Verschieben
- web/prod/toolbar.html.twig
prod/WorkZone/Basket.html.twig
prod/WorkZone/Story.html.twig
+ web/prod/toolbar.html.twig
action : editer
Bearbeiten
prod/preview/caption.html.twig
- web/prod/toolbar.html.twig
prod/WorkZone/Basket.html.twig
prod/WorkZone/Story.html.twig
+ web/prod/toolbar.html.twig
action : exporter
Exportieren
- web/lightbox/validate.html.twig
- web/lightbox/feed.html.twig
- lightbox/IE6/validate.html.twig
- lightbox/IE6/feed.html.twig
+ web/prod/index.html.twig
prod/preview/tools.html.twig
- web/prod/toolbar.html.twig
prod/WorkZone/Basket.html.twig
prod/WorkZone/Story.html.twig
prod/results/record.html.twig
prod/results/record.html.twig
prod/results/record.html.twig
- web/prod/index.html.twig
+ web/prod/toolbar.html.twig
+ lightbox/IE6/feed.html.twig
+ lightbox/IE6/validate.html.twig
+ web/lightbox/feed.html.twig
+ web/lightbox/validate.html.twig
action : outils
Werkzeuge
- web/prod/toolbar.html.twig
prod/WorkZone/Basket.html.twig
prod/WorkZone/Story.html.twig
+ web/prod/toolbar.html.twig
action : print
Drucken
prod/preview/tools.html.twig
- web/prod/toolbar.html.twig
prod/WorkZone/Basket.html.twig
prod/WorkZone/Story.html.twig
prod/results/record.html.twig
prod/results/record.html.twig
prod/results/record.html.twig
+ web/prod/toolbar.html.twig
action : publier
Veröffentlichen
- web/prod/toolbar.html.twig
+ web/prod/index.html.twig
prod/WorkZone/Basket.html.twig
prod/WorkZone/Story.html.twig
- web/prod/index.html.twig
+ web/prod/toolbar.html.twig
action : push
Push
- web/prod/toolbar.html.twig
prod/WorkZone/Basket.html.twig
prod/WorkZone/Story.html.twig
+ web/prod/toolbar.html.twig
action : status
Eigenschaften
- web/prod/toolbar.html.twig
prod/WorkZone/Basket.html.twig
prod/WorkZone/Story.html.twig
+ web/prod/toolbar.html.twig
action : supprimer
Löschen
- web/prod/toolbar.html.twig
prod/WorkZone/Macros.html.twig
prod/WorkZone/Macros.html.twig
+ web/prod/toolbar.html.twig
action:: nouveau panier
@@ -8291,7 +8301,7 @@
admin::base: preferences de collection
Kollektionseinstellungen
- web/admin/tree.html.twig
+ web/admin/tree.html.twig
admin::base: structure
@@ -8467,7 +8477,7 @@
admin::collection: ordre des collections
Kollektionen Ordnung
- web/admin/tree.html.twig
+ web/admin/tree.html.twig
admin::collection:: Gestionnaires des commandes
@@ -8494,9 +8504,9 @@
Tätigkeit
Core/Provider/RegistrationServiceProvider.php
Controller/Admin/UserController.php
- web/account/account.html.twig
admin/user/registrations.html.twig
- web/admin/editusers.html.twig
+ web/admin/editusers.html.twig
+ web/account/account.html.twig
admin::compte-utilisateur adresse
@@ -8504,9 +8514,9 @@
Core/Provider/RegistrationServiceProvider.php
Controller/Admin/UserController.php
web/common/dialog_export.html.twig
- web/account/account.html.twig
admin/user/registrations.html.twig
- web/admin/editusers.html.twig
+ web/admin/editusers.html.twig
+ web/account/account.html.twig
admin::compte-utilisateur changer mon mot de passe
@@ -8519,8 +8529,8 @@
Core/Provider/RegistrationServiceProvider.php
Controller/Admin/UserController.php
web/common/dialog_export.html.twig
+ web/admin/editusers.html.twig
web/account/account.html.twig
- web/admin/editusers.html.twig
admin::compte-utilisateur confirmer la nouvelle adresse email
@@ -8530,12 +8540,12 @@
admin::compte-utilisateur date de creation
Erstellungsdatum
- web/admin/users.html.twig
+ web/admin/users.html.twig
admin::compte-utilisateur dernier modele applique
Letzte angewendete Vorlage
- web/admin/users.html.twig
+ web/admin/users.html.twig
admin::compte-utilisateur email
@@ -8543,11 +8553,11 @@
Event/Subscriber/RegistrationSubscriber.php
Controller/Admin/UserController.php
web/common/dialog_export.html.twig
- web/account/account.html.twig
- web/admin/users.html.twig
- web/admin/connected-users.html.twig
+ web/admin/users.html.twig
admin/user/registrations.html.twig
- web/admin/editusers.html.twig
+ web/admin/editusers.html.twig
+ web/admin/connected-users.html.twig
+ web/account/account.html.twig
admin::compte-utilisateur fax
@@ -8555,13 +8565,13 @@
Core/Provider/RegistrationServiceProvider.php
Controller/Admin/UserController.php
web/common/dialog_export.html.twig
+ web/admin/editusers.html.twig
web/account/account.html.twig
- web/admin/editusers.html.twig
admin::compte-utilisateur id utilisateur
Benutzername
- web/admin/users.html.twig
+ web/admin/users.html.twig
admin::compte-utilisateur identifiant
@@ -8569,33 +8579,33 @@
Core/Provider/RegistrationServiceProvider.php
api/auth/end_user_authorization.html.twig
web/common/dialog_export.html.twig
+ web/admin/users.html.twig
+ admin/user/registrations.html.twig
+ web/admin/editusers.html.twig
web/account/account.html.twig
web/account/account.html.twig
web/account/reset-email.html.twig
- web/admin/users.html.twig
- admin/user/registrations.html.twig
- web/admin/editusers.html.twig
admin::compte-utilisateur mot de passe
Passwort
api/auth/end_user_authorization.html.twig
web/common/dialog_export.html.twig
+ web/setup/step2.html.twig
web/account/account.html.twig
web/account/reset-email.html.twig
- web/setup/step2.html.twig
admin::compte-utilisateur nom
Name
- Event/Subscriber/RegistrationSubscriber.php
Core/Provider/RegistrationServiceProvider.php
+ Event/Subscriber/RegistrationSubscriber.php
Controller/Admin/UserController.php
web/common/dialog_export.html.twig
- web/account/account.html.twig
- web/admin/connected-users.html.twig
admin/user/registrations.html.twig
- web/admin/editusers.html.twig
+ web/admin/editusers.html.twig
+ web/admin/connected-users.html.twig
+ web/account/account.html.twig
admin::compte-utilisateur nouvelle adresse email
@@ -8606,7 +8616,7 @@
admin::compte-utilisateur pays
Land
Controller/Admin/UserController.php
- web/admin/users.html.twig
+ web/admin/users.html.twig
admin::compte-utilisateur poste
@@ -8614,27 +8624,27 @@
Core/Provider/RegistrationServiceProvider.php
Controller/Admin/UserController.php
web/common/dialog_export.html.twig
- web/account/account.html.twig
admin/user/registrations.html.twig
- web/admin/editusers.html.twig
+ web/admin/editusers.html.twig
+ web/account/account.html.twig
admin::compte-utilisateur prenom
Vorname
- Event/Subscriber/RegistrationSubscriber.php
Core/Provider/RegistrationServiceProvider.php
+ Event/Subscriber/RegistrationSubscriber.php
Controller/Admin/UserController.php
web/common/dialog_export.html.twig
- web/account/account.html.twig
admin/user/registrations.html.twig
- web/admin/editusers.html.twig
+ web/admin/editusers.html.twig
+ web/account/account.html.twig
admin::compte-utilisateur sexe
Anrede
Core/Provider/RegistrationServiceProvider.php
+ web/admin/editusers.html.twig
web/account/account.html.twig
- web/admin/editusers.html.twig
admin::compte-utilisateur societe
@@ -8642,11 +8652,11 @@
Core/Provider/RegistrationServiceProvider.php
Controller/Admin/UserController.php
web/common/dialog_export.html.twig
- web/account/account.html.twig
- web/admin/users.html.twig
- web/admin/connected-users.html.twig
+ web/admin/users.html.twig
admin/user/registrations.html.twig
- web/admin/editusers.html.twig
+ web/admin/editusers.html.twig
+ web/admin/connected-users.html.twig
+ web/account/account.html.twig
admin::compte-utilisateur tel
@@ -8658,10 +8668,10 @@
Telefon
Controller/Admin/UserController.php
web/common/dialog_export.html.twig
- web/account/account.html.twig
- web/admin/connected-users.html.twig
admin/user/registrations.html.twig
- web/admin/editusers.html.twig
+ web/admin/editusers.html.twig
+ web/admin/connected-users.html.twig
+ web/account/account.html.twig
admin::compte-utilisateur un email de confirmation vient de vous etre envoye. Veuillez suivre les instructions contenue pour continuer
@@ -8674,8 +8684,8 @@
Core/Provider/RegistrationServiceProvider.php
Controller/Admin/UserController.php
web/common/dialog_export.html.twig
+ web/admin/editusers.html.twig
web/account/account.html.twig
- web/admin/editusers.html.twig
admin::compte-utilisateur: L'email a correctement ete mis a jour
@@ -8770,24 +8780,24 @@
Frau
Core/Provider/RegistrationServiceProvider.php
web/common/dialog_export.html.twig
+ web/admin/editusers.html.twig
web/account/account.html.twig
- web/admin/editusers.html.twig
admin::compte-utilisateur:sexe: mademoiselle
Fräulein
Core/Provider/RegistrationServiceProvider.php
web/common/dialog_export.html.twig
+ web/admin/editusers.html.twig
web/account/account.html.twig
- web/admin/editusers.html.twig
admin::compte-utilisateur:sexe: monsieur
Herr
Core/Provider/RegistrationServiceProvider.php
web/common/dialog_export.html.twig
+ web/admin/editusers.html.twig
web/account/account.html.twig
- web/admin/editusers.html.twig
admin::monitor: bases sur lesquelles l'utilisateur est connecte :
@@ -8816,10 +8826,10 @@
admin::monitor: module client
Client
Controller/Admin/ConnectedUsersController.php
+ lib/classes/phrasea.php
classes/record/preview.php
classes/record/preview.php
classes/record/preview.php
- lib/classes/phrasea.php
web/admin/connected-users.html.twig
@@ -8838,10 +8848,10 @@
admin::monitor: module production
Prod
Controller/Admin/ConnectedUsersController.php
+ lib/classes/phrasea.php
classes/record/preview.php
classes/record/preview.php
classes/record/preview.php
- lib/classes/phrasea.php
web/admin/connected-users.html.twig
@@ -8870,10 +8880,10 @@
admin::monitor: module validation
Lightbox
+ Controller/Admin/ConnectedUsersController.php
Phrasea/Controller/LightboxController.php
Phrasea/Controller/LightboxController.php
Phrasea/Controller/LightboxController.php
- Controller/Admin/ConnectedUsersController.php
lib/classes/phrasea.php
web/common/menubar.html.twig
@@ -8910,8 +8920,8 @@
admin::plugins: plugins
Plugins
- web/admin/tree.html.twig
admin/plugins/index.html.twig
+ web/admin/tree.html.twig
admin::plugins: retrieveConfigurationError
@@ -8991,7 +9001,7 @@
admin::status: reglage des status
Status Einstellungen
- web/admin/tree.html.twig
+ web/admin/tree.html.twig
admin::status: texte a afficher
@@ -9008,7 +9018,7 @@
admin::structure: reglage de la structure
Struktur Einstellung
- web/admin/tree.html.twig
+ web/admin/tree.html.twig
admin::sugval: Attention, passer en mode graphique implique la perte des modifications du xml si vous n'appliquez pas les changements avant.
@@ -9060,39 +9070,330 @@
Gastbenutzer filtern
web/admin/users.html.twig
+
+ admin::users:edit: Manage inline selection
+ Eine Option auswählen
+ web/admin/editusers.html.twig
+
+
+ admin::users:edit: check all right
+ Alle Rechte auf die Kollektion geben
+ web/admin/index.html.twig
+ web/admin/editusers.html.twig
+
+
+ admin::users:edit: check left right
+ admin::users:edit: check left right
+ web/admin/index.html.twig
+
+
+ admin::users:edit: check read right
+ Leserechte auf die Kollektion geben
+ web/admin/editusers.html.twig
+
+
+ admin::users:filter: content
+ enthält
+ web/admin/users.html.twig
+
+
+ admin::users:filter: start with
+ beginnt mit
+ web/admin/users.html.twig
+
admin::utilisateurs: bases de donnees
Datenbanken
- web/admin/tree.html.twig
+ web/admin/tree.html.twig
admin::utilisateurs: demandes en cours
Anfragen
- web/admin/tree.html.twig
+ web/admin/tree.html.twig
admin::utilisateurs: gestionnaire de taches
Aufgabe-Scheduler
- web/admin/tree.html.twig
+ web/admin/tree.html.twig
admin::utilisateurs: utilisateurs
Benutzer
- web/admin/tree.html.twig
- web/admin/tree.html.twig
- web/admin/tree.html.twig
+ web/admin/tree.html.twig
+ web/admin/tree.html.twig
+ web/admin/tree.html.twig
admin::utilisateurs: utilisateurs connectes
Eingeloggte Benutzer
- web/admin/tree.html.twig
web/admin/connected-users.html.twig
+ web/admin/tree.html.twig
+
+
+ admin::workermanager: Rabbit config error
+ Konfigurationsfehler des Nachrichten Managers
+ admin/worker-manager/index.html.twig
+
+
+ admin::workermanager:tab:configuration: title
+ Allgemeine Einstellungen
+ admin/worker-manager/index.html.twig
+
+
+ admin::workermanager:tab:metadata: description
+ Schreiben von Metadaten
+ admin/worker-manager/worker_metadata.html.twig
+
+
+ admin::workermanager:tab:metadata: title
+ Metadaten
+ admin/worker-manager/index.html.twig
+
+
+ admin::workermanager:tab:pullassets: Client ID
+ Client ID
+ WorkerManager/Form/WorkerPullAssetsType.php
+
+
+ admin::workermanager:tab:pullassets: Client secret
+ Client secret
+ WorkerManager/Form/WorkerPullAssetsType.php
+
+
+ admin::workermanager:tab:pullassets: Endpoint get commit
+ Endpunkt für Commits (Uploader Adresse)
+ WorkerManager/Form/WorkerPullAssetsType.php
+
+
+ admin::workermanager:tab:pullassets: Endpoint get token
+ Endpunkt für Authentifizierungstoken
+ WorkerManager/Form/WorkerPullAssetsType.php
+
+
+ admin::workermanager:tab:pullassets: Fetching interval in second
+ Abrufintervall in Sekunden
+ WorkerManager/Form/WorkerPullAssetsType.php
+
+
+ admin::workermanager:tab:pullassets: Initialize pull assets
+ Prozess starten
+ admin/worker-manager/worker_pull_assets.html.twig
+
+
+ admin::workermanager:tab:pullassets: description
+ Uploader Einstellungen im "Pull" Modus
+ admin/worker-manager/worker_pull_assets.html.twig
+
+
+ admin::workermanager:tab:pullassets: title
+ Uploader
+ admin/worker-manager/index.html.twig
+
+
+ admin::workermanager:tab:searchengine: Databox to populate
+ Databox auswählen
+ admin/worker-manager/worker_searchengine.html.twig
+
+
+ admin::workermanager:tab:searchengine: Elasticsearch index name
+ Elasticsearch Index Name
+ WorkerManager/Form/WorkerSearchengineType.php
+
+
+ admin::workermanager:tab:searchengine: Elasticsearch server host
+ Elasticsearch Server Host
+ WorkerManager/Form/WorkerSearchengineType.php
+
+
+ admin::workermanager:tab:searchengine: Elasticsearch service port
+ Elasticsearch Service Port
+ WorkerManager/Form/WorkerSearchengineType.php
+
+
+ admin::workermanager:tab:searchengine: Populate
+ Indexierung
+ admin/worker-manager/worker_searchengine.html.twig
+
+
+ admin::workermanager:tab:searchengine: Warning Worker is in process to indexing one of the selected databox
+ Ein Indexierung Prozess ist im Gange für eine Databox, Sie dürfen nicht ein zweites starten, solange das erste nicht beendet ist.
+ admin/worker-manager/worker_searchengine.html.twig
+
+
+ admin::workermanager:tab:searchengine: description
+ Indexierung Prozess manueller Start
+ admin/worker-manager/worker_searchengine.html.twig
+
+
+ admin::workermanager:tab:searchengine: title
+ Indexierung
+ admin/worker-manager/index.html.twig
+
+
+ admin::workermanager:tab:subview: description
+ Auflösungen Herstellung
+ admin/worker-manager/worker_subview.html.twig
+
+
+ admin::workermanager:tab:subview: title
+ Auflösung
+ admin/worker-manager/index.html.twig
+
+
+ admin::workermanager:tab:workerconfig: Create record retry delay in ms
+ Wiederholungsverzögerung für Datensatzerstellung, in ms (Standardwert: 30 sek.)
+ WorkerManager/Form/WorkerConfigurationType.php
+
+
+ admin::workermanager:tab:workerconfig: Export mail retry delay in ms
+ Wiederholungsverzögerung für Mail Export, in ms (Standardwert: 30 sek.)
+ WorkerManager/Form/WorkerConfigurationType.php
+
+
+ admin::workermanager:tab:workerconfig: Ingest retry delay in ms
+ Wiederholungsverzögerung für Upload, in ms (Standardwert: 30 sek.)
+ WorkerManager/Form/WorkerConfigurationType.php
+
+
+ admin::workermanager:tab:workerconfig: Metadatas retry delay in ms
+ Wiederholungsverzögerung für Metadaten Schreiben, in ms (Standardwert: 30 sek.)
+ WorkerManager/Form/WorkerConfigurationType.php
+
+
+ admin::workermanager:tab:workerconfig: Populate Index retry delay in ms
+ Wiederholungsverzögerung für Indexierung, in ms (Standardwert: 30 sek.)
+ WorkerManager/Form/WorkerConfigurationType.php
+
+
+ admin::workermanager:tab:workerconfig: Subdef delay in ms
+ Verzögerung für Auflösung Herstellung, in ms (Standardwert: 20 sek.); benutzen, wenn Datei von einem Prozess gesperrt wird
+ WorkerManager/Form/WorkerConfigurationType.php
+
+
+ admin::workermanager:tab:workerconfig: Subdefinition retry delay in ms
+ Wiederholungsverzögerung für Auflösung Herstellung, in ms (Standardwert: 30 sek.)
+ WorkerManager/Form/WorkerConfigurationType.php
+
+
+ admin::workermanager:tab:workerconfig: Webhook retry delay in ms
+ Wiederholungsverzögerung für Webhook, in ms (Standardwert: 30 sek.)
+ WorkerManager/Form/WorkerConfigurationType.php
+
+
+ admin::workermanager:tab:workerconfig: Write meta delay in ms
+ Verzögerung für Metadaten Schreiben, in ms (Standardwert: 20 sek.); benutzen, wenn Datei von einem Prozess gesperrt wird
+ WorkerManager/Form/WorkerConfigurationType.php
+
+
+ admin::workermanager:tab:workerconfig: title
+ Workers allgemeine Einstellungen
+ admin/worker-manager/worker_configuration.html.twig
+
+
+ admin::workermanager:tab:workerconfig: warning
+ Es ist erforderlich, Meldungswarteschlangen zu zerstören, für die Einbeziehung von neuen Zeitintervallen.
+ admin/worker-manager/worker_configuration.html.twig
+
+
+ admin::workermanager:tab:workerconfig:Apply in queue
+ Auf Meldungswarteschlangen anwenden
+ admin/worker-manager/worker_configuration.html.twig
+
+
+ admin::workermanager:tab:workerinfo: Display finished work
+ Abgeschlossene Arbeiten anzeigen
+ admin/worker-manager/worker_info.html.twig
+
+
+ admin::workermanager:tab:workerinfo: Display running work
+ Laufende Arbeiten anzeigen
+ admin/worker-manager/worker_info.html.twig
+
+
+ admin::workermanager:tab:workerinfo: Erase all finished
+ Abgeschlossene Arbeiten löschen
+ admin/worker-manager/worker_info.html.twig
+
+
+ admin::workermanager:tab:workerinfo: Erase all information
+ Alle Arbeiten löschen
+ admin/worker-manager/worker_info.html.twig
+
+
+ admin::workermanager:tab:workerinfo: Refresh list
+ Liste aktualisieren
+ admin/worker-manager/worker_info.html.twig
+
+
+ admin::workermanager:tab:workerinfo: Warning! Erase all finished
+ Alle abgeschlossene Arbeiten löschen
+ admin/worker-manager/worker_info.html.twig
+
+
+ admin::workermanager:tab:workerinfo: Warning! Erase all information
+ Vorsicht! Löscht alle Informationen auf abgeschlossene und laufende Arbeiten
+ admin/worker-manager/worker_info.html.twig
+
+
+ admin::workermanager:tab:workerinfo: created
+ Begonnen
+ admin/worker-manager/worker_info.html.twig
+
+
+ admin::workermanager:tab:workerinfo: databox_id
+ Databox ID
+ admin/worker-manager/worker_info.html.twig
+
+
+ admin::workermanager:tab:workerinfo: description
+ Auftragsjournal
+ admin/worker-manager/worker_info.html.twig
+
+
+ admin::workermanager:tab:workerinfo: finished
+ Beendet
+ admin/worker-manager/worker_info.html.twig
+
+
+ admin::workermanager:tab:workerinfo: published
+ Erstellt
+ admin/worker-manager/worker_info.html.twig
+
+
+ admin::workermanager:tab:workerinfo: record_id
+ Record ID
+ admin/worker-manager/worker_info.html.twig
+
+
+ admin::workermanager:tab:workerinfo: status
+ Status
+ admin/worker-manager/worker_info.html.twig
+
+
+ admin::workermanager:tab:workerinfo: title
+ Auftragsjournal
+ admin/worker-manager/index.html.twig
+
+
+ admin::workermanager:tab:workerinfo: work
+ Auftrag
+ admin/worker-manager/worker_info.html.twig
+
+
+ admin::workermanager:tab:workerinfo: work_on
+ Auftrag auf
+ admin/worker-manager/worker_info.html.twig
+
+
+ admin:worker Retrieve configuration error
+ Worker Abruf Konfigurationsfehler
+ admin/worker-manager/index.html.twig
alert
Vorsicht
- actions/Tools/videoEditor.html.twig
actions/Tools/index.html.twig
+ actions/Tools/videoEditor.html.twig
all caches services have been flushed
@@ -9133,8 +9434,8 @@
audio
Audio
Phrasea/Twig/PhraseanetExtension.php
- web/admin/subdefs.html.twig
task-manager/task-editor/subdefs.html.twig
+ web/admin/subdefs.html.twig
avril
@@ -9195,54 +9496,54 @@
abbrechen
Controller/Prod/LanguageController.php
Controller/Prod/LanguageController.php
- web/thesaurus/link-field-step1.html.twig
- web/thesaurus/export-text-dialog.html.twig
- web/thesaurus/accept.html.twig
- web/thesaurus/accept.html.twig
- web/thesaurus/import-dialog.html.twig
- web/thesaurus/export-topics-dialog.html.twig
+ prod/actions/edit_default.html.twig
+ prod/actions/edit_default.html.twig
+ Bridge/Youtube/video_modify.html.twig
+ Bridge/Youtube/video_modify.html.twig
+ Bridge/Flickr/photo_modify.html.twig
+ Bridge/Flickr/photo_modify.html.twig
+ Bridge/Dailymotion/video_modify.html.twig
+ Bridge/Dailymotion/video_modify.html.twig
web/thesaurus/new-term.html.twig
web/thesaurus/new-term.html.twig
web/thesaurus/thesaurus.html.twig
web/thesaurus/thesaurus.html.twig
web/thesaurus/thesaurus.html.twig
web/thesaurus/link-field-step2.html.twig
+ web/thesaurus/import-dialog.html.twig
+ web/thesaurus/accept.html.twig
+ web/thesaurus/accept.html.twig
+ web/thesaurus/export-text-dialog.html.twig
+ web/thesaurus/link-field-step1.html.twig
+ web/thesaurus/export-topics-dialog.html.twig
web/common/dialog_export.html.twig
web/common/dialog_export.html.twig
web/common/dialog_export.html.twig
web/common/dialog_export.html.twig
- web/account/reset-email.html.twig
- Bridge/Dailymotion/video_modify.html.twig
- Bridge/Dailymotion/video_modify.html.twig
- Bridge/Flickr/photo_modify.html.twig
- Bridge/Flickr/photo_modify.html.twig
- Bridge/Youtube/video_modify.html.twig
- Bridge/Youtube/video_modify.html.twig
- prod/actions/edit_default.html.twig
- prod/actions/edit_default.html.twig
- web/report/all_content.html.twig
+ web/admin/index.html.twig
admin/collection/create.html.twig
admin/publications/fiche.html.twig
- web/admin/index.html.twig
+ web/report/all_content.html.twig
+ web/account/reset-email.html.twig
boutton::appliquer
Anwenden
- web/admin/users.html.twig
+ web/admin/users.html.twig
boutton::chercher
suchen
+ web/prod/index.html.twig
web/thesaurus/thesaurus.html.twig
web/thesaurus/thesaurus.html.twig
- web/prod/index.html.twig
- web/admin/users.html.twig
+ web/admin/users.html.twig
boutton::choisir
Auswählen
- web/admin/users.html.twig
- web/admin/editusers.html.twig
+ web/admin/users.html.twig
+ web/admin/editusers.html.twig
boutton::commander
@@ -9264,8 +9565,8 @@
boutton::editer
Bearbeiten
- prod/results/entry.html.twig
prod/results/feeds_entry.html.twig
+ prod/results/entry.html.twig
boutton::enregistrer
@@ -9286,22 +9587,22 @@
boutton::exporter
Exportieren
- web/admin/users.html.twig
+ web/admin/users.html.twig
boutton::fermer
schliessen
Controller/Prod/LanguageController.php
+ prod/actions/edit_default.html.twig
+ prod/actions/edit_default.html.twig
+ prod/actions/Push.html.twig
+ web/lightbox/sc_note.html.twig
web/thesaurus/export-topics.html.twig
web/thesaurus/accept.html.twig
web/thesaurus/accept.html.twig
web/thesaurus/properties.html.twig
web/thesaurus/link-field-step3.html.twig
web/common/dialog_export.html.twig
- web/lightbox/sc_note.html.twig
- prod/actions/Push.html.twig
- prod/actions/edit_default.html.twig
- prod/actions/edit_default.html.twig
web/report/all_content.html.twig
@@ -9317,10 +9618,10 @@
boutton::modifier
ändern
- Bridge/Dailymotion/actionelement.html.twig
- Bridge/Flickr/actionelement.html.twig
Bridge/Youtube/actionelement.html.twig
- web/admin/users.html.twig
+ Bridge/Flickr/actionelement.html.twig
+ Bridge/Dailymotion/actionelement.html.twig
+ web/admin/users.html.twig
boutton::monter
@@ -9376,26 +9677,26 @@
boutton::retour
Zurück
- web/developers/application_form.html.twig
- web/developers/application.html.twig
- Bridge/Dailymotion/video_moveinto_playlist.html.twig
- Bridge/Dailymotion/playlist_createcontainer.html.twig
- Bridge/Dailymotion/video_deleteelement.html.twig
- Bridge/Dailymotion/playlist_deleteelement.html.twig
+ Bridge/Youtube/video_moveinto_playlist.html.twig
+ Bridge/Youtube/video_deleteelement.html.twig
+ Bridge/Youtube/playlist_createcontainer.html.twig
+ Bridge/Youtube/playlist_deleteelement.html.twig
Bridge/Flickr/photoset_deleteelement.html.twig
Bridge/Flickr/photoset_createcontainer.html.twig
Bridge/Flickr/photo_deleteelement.html.twig
Bridge/Flickr/photo_moveinto_photoset.html.twig
- Bridge/Youtube/video_moveinto_playlist.html.twig
- Bridge/Youtube/playlist_createcontainer.html.twig
- Bridge/Youtube/video_deleteelement.html.twig
- Bridge/Youtube/playlist_deleteelement.html.twig
- admin/statusbit/edit.html.twig
+ Bridge/Dailymotion/video_moveinto_playlist.html.twig
+ Bridge/Dailymotion/video_deleteelement.html.twig
+ Bridge/Dailymotion/playlist_createcontainer.html.twig
+ Bridge/Dailymotion/playlist_deleteelement.html.twig
+ web/developers/application_form.html.twig
+ web/developers/application.html.twig
admin/collection/details.html.twig
- admin/databox/details.html.twig
- admin/publications/fiche.html.twig
user/import/file.html.twig
- web/admin/editusers.html.twig
+ admin/databox/details.html.twig
+ web/admin/editusers.html.twig
+ admin/publications/fiche.html.twig
+ admin/statusbit/edit.html.twig
boutton::retry
@@ -9420,18 +9721,17 @@
boutton::supprimer
Löschen
Controller/Prod/LanguageController.php
- web/thesaurus/presets.html.twig
- Bridge/Dailymotion/actioncontainers.html.twig
- Bridge/Dailymotion/actionelements.html.twig
- Bridge/Flickr/actioncontainers.html.twig
- Bridge/Flickr/actionelements.html.twig
- actions/Bridge/disconnected.html.twig
- Bridge/Youtube/actioncontainers.html.twig
- Bridge/Youtube/actionelements.html.twig
- prod/results/entry.html.twig
- prod/results/feeds_entry.html.twig
web/prod/index.html.twig
- web/admin/subdefs.html.twig
+ actions/Bridge/disconnected.html.twig
+ Bridge/Youtube/actionelements.html.twig
+ Bridge/Youtube/actioncontainers.html.twig
+ Bridge/Flickr/actionelements.html.twig
+ Bridge/Flickr/actioncontainers.html.twig
+ Bridge/Dailymotion/actionelements.html.twig
+ Bridge/Dailymotion/actioncontainers.html.twig
+ prod/results/feeds_entry.html.twig
+ prod/results/entry.html.twig
+ web/thesaurus/presets.html.twig
admin/collection/suggested_value.html.twig
admin/collection/collection.html.twig
admin/collection/collection.html.twig
@@ -9439,21 +9739,22 @@
admin/collection/collection.html.twig
admin/publications/list.html.twig
admin/publications/fiche.html.twig
+ web/admin/subdefs.html.twig
boutton::telecharger
Download
- web/common/dialog_export.html.twig
web/lightbox/feed_options_box.html.twig
web/lightbox/sc_options_box.html.twig
+ web/common/dialog_export.html.twig
boutton::telecharger tous les documents
Alle Dokumente herunterladen
- web/lightbox/validate.html.twig
- web/lightbox/feed.html.twig
- lightbox/IE6/validate.html.twig
lightbox/IE6/feed.html.twig
+ lightbox/IE6/validate.html.twig
+ web/lightbox/feed.html.twig
+ web/lightbox/validate.html.twig
boutton::tester
@@ -9465,57 +9766,57 @@
boutton::valider
Bestätigen
Controller/Prod/LanguageController.php
- mobile/lightbox/note_form.html.twig
- web/thesaurus/link-field-step1.html.twig
- web/thesaurus/export-text-dialog.html.twig
- web/thesaurus/accept.html.twig
- web/thesaurus/import-dialog.html.twig
- web/thesaurus/export-topics-dialog.html.twig
- web/thesaurus/new-term.html.twig
- web/thesaurus/new-term.html.twig
- web/thesaurus/thesaurus.html.twig
- web/thesaurus/index.html.twig
- web/thesaurus/link-field-step2.html.twig
- web/account/access.html.twig
- web/account/account.html.twig
- web/account/reset-email.html.twig
- web/developers/application_form.html.twig
- Bridge/Dailymotion/video_moveinto_playlist.html.twig
- Bridge/Dailymotion/playlist_createcontainer.html.twig
- Bridge/Dailymotion/video_deleteelement.html.twig
- Bridge/Dailymotion/playlist_deleteelement.html.twig
- Bridge/Dailymotion/video_modify.html.twig
+ web/prod/index.html.twig
+ prod/actions/edit_default.html.twig
+ prod/actions/edit_default.html.twig
+ Bridge/Youtube/video_moveinto_playlist.html.twig
+ Bridge/Youtube/video_deleteelement.html.twig
+ Bridge/Youtube/playlist_createcontainer.html.twig
+ Bridge/Youtube/playlist_deleteelement.html.twig
+ Bridge/Youtube/video_modify.html.twig
Bridge/Flickr/photoset_deleteelement.html.twig
Bridge/Flickr/photoset_createcontainer.html.twig
Bridge/Flickr/photo_deleteelement.html.twig
- Bridge/Flickr/photo_moveinto_photoset.html.twig
Bridge/Flickr/photo_modify.html.twig
- Bridge/Youtube/video_moveinto_playlist.html.twig
- Bridge/Youtube/playlist_createcontainer.html.twig
- Bridge/Youtube/video_deleteelement.html.twig
- Bridge/Youtube/playlist_deleteelement.html.twig
- Bridge/Youtube/video_modify.html.twig
- prod/actions/edit_default.html.twig
- prod/actions/edit_default.html.twig
- prod/Baskets/Reorder.html.twig
- prod/Baskets/Update.html.twig
+ Bridge/Flickr/photo_moveinto_photoset.html.twig
+ Bridge/Dailymotion/video_moveinto_playlist.html.twig
+ Bridge/Dailymotion/video_deleteelement.html.twig
+ Bridge/Dailymotion/playlist_createcontainer.html.twig
+ Bridge/Dailymotion/playlist_deleteelement.html.twig
+ Bridge/Dailymotion/video_modify.html.twig
prod/Story/Reorder.html.twig
- web/prod/index.html.twig
- web/report/all_content.html.twig
- web/admin/setup.html.twig
- web/admin/dashboard.html.twig
- web/admin/subdefs.html.twig
- admin/statusbit/edit.html.twig
- web/admin/structure.html.twig
+ prod/Baskets/Update.html.twig
+ prod/Baskets/Reorder.html.twig
+ web/developers/application_form.html.twig
+ web/thesaurus/index.html.twig
+ web/thesaurus/new-term.html.twig
+ web/thesaurus/new-term.html.twig
+ web/thesaurus/thesaurus.html.twig
+ web/thesaurus/link-field-step2.html.twig
+ web/thesaurus/import-dialog.html.twig
+ web/thesaurus/accept.html.twig
+ web/thesaurus/export-text-dialog.html.twig
+ web/thesaurus/link-field-step1.html.twig
+ web/thesaurus/export-topics-dialog.html.twig
admin/collection/suggested_value.html.twig
admin/collection/create.html.twig
admin/collection/reorder.html.twig
admin/collection/collection.html.twig
+ user/import/view.html.twig
+ admin/user/registrations.html.twig
+ web/admin/structure.html.twig
+ web/admin/setup.html.twig
+ web/admin/editusers.html.twig
admin/publications/list.html.twig
admin/publications/fiche.html.twig
- admin/user/registrations.html.twig
- user/import/view.html.twig
- web/admin/editusers.html.twig
+ web/admin/dashboard.html.twig
+ admin/statusbit/edit.html.twig
+ web/admin/subdefs.html.twig
+ web/report/all_content.html.twig
+ web/account/access.html.twig
+ web/account/account.html.twig
+ web/account/reset-email.html.twig
+ mobile/lightbox/note_form.html.twig
boutton::vue graphique
@@ -9635,8 +9936,8 @@
commande::utilisation prevue
Verwendungszweck
- web/common/dialog_export.html.twig
prod/orders/order_item.html.twig
+ web/common/dialog_export.html.twig
copyClipboardLabel
@@ -9704,8 +10005,8 @@
dans %feed_name%
in %feed_name%
- prod/results/entry.html.twig
prod/results/feeds_entry.html.twig
+ prod/results/entry.html.twig
date dajout
@@ -9725,7 +10026,7 @@
delete
Löschen
- prod/WorkZone/Macros.html.twig
+ prod/WorkZone/Macros.html.twig
descendant
@@ -9741,9 +10042,9 @@
document
Dokument
Phrasea/Twig/PhraseanetExtension.php
+ task-manager/task-editor/subdefs.html.twig
web/admin/subdefs.html.twig
web/admin/subdefs.html.twig
- task-manager/task-editor/subdefs.html.twig
document original
@@ -9754,10 +10055,10 @@
edit
Bearbeiten
+ prod/WorkZone/Macros.html.twig
web/account/account.html.twig
web/account/account.html.twig
web/account/account.html.twig
- prod/WorkZone/Macros.html.twig
edit: chosiir limage du regroupement
@@ -9900,8 +10201,8 @@
flash
Flash
Phrasea/Twig/PhraseanetExtension.php
- web/admin/subdefs.html.twig
task-manager/task-editor/subdefs.html.twig
+ web/admin/subdefs.html.twig
for the following reasons : %reasons%
@@ -9973,8 +10274,8 @@
help::help-search: relaunch search without filter
Filter entfernen und wieder suchen
- prod/results/help.html.twig
web/prod/index.html.twig
+ prod/results/help.html.twig
help::help-section-bullet: check-spelling
@@ -10035,8 +10336,8 @@
image
Bild
Phrasea/Twig/PhraseanetExtension.php
- web/admin/subdefs.html.twig
task-manager/task-editor/subdefs.html.twig
+ web/admin/subdefs.html.twig
image rotation
@@ -10141,9 +10442,9 @@
lightbox::recaptitulatif
Übersicht
- mobile/lightbox/validate.html.twig
- web/lightbox/validate.html.twig
web/lightbox/agreement_box.html.twig
+ web/lightbox/validate.html.twig
+ mobile/lightbox/validate.html.twig
lightbox::see_less_basket
@@ -10158,44 +10459,44 @@
lightbox:feedback:sendreport:warnwindows:cancel
Sendung abbrechen und Feedback fortsetzen
- mobile/lightbox/validate.html.twig
web/lightbox/validate.html.twig
+ mobile/lightbox/validate.html.twig
lightbox:feedback:sendreport:warnwindows:message
Zusammenfassung der Feedback Auswahlmöglichkeiten
- mobile/lightbox/validate.html.twig
web/lightbox/validate.html.twig
+ mobile/lightbox/validate.html.twig
lightbox:feedback:sendreport:warnwindows:record_approved
genehmigt
- mobile/lightbox/validate.html.twig
web/lightbox/validate.html.twig
+ mobile/lightbox/validate.html.twig
lightbox:feedback:sendreport:warnwindows:record_rejected
abgelehnt
- mobile/lightbox/validate.html.twig
web/lightbox/validate.html.twig
+ mobile/lightbox/validate.html.twig
lightbox:feedback:sendreport:warnwindows:record_unexpressed
unausgesprochen
- mobile/lightbox/validate.html.twig
web/lightbox/validate.html.twig
+ mobile/lightbox/validate.html.twig
lightbox:feedback:sendreport:warnwindows:title
Feedback Übersicht
- mobile/lightbox/validate.html.twig
web/lightbox/validate.html.twig
+ mobile/lightbox/validate.html.twig
lightbox:feedback:sendreport:warnwindows:validate
Bericht Sendung bestätigen
- mobile/lightbox/validate.html.twig
web/lightbox/validate.html.twig
+ mobile/lightbox/validate.html.twig
login:: Changer mon adresse email
@@ -10215,10 +10516,10 @@
login:: Mon compte
Mein Benutzerkonto
- mobile/common/menubar.html.twig
web/common/menubar.html.twig
web/common/menubar.html.twig
web/account/account.html.twig
+ mobile/common/menubar.html.twig
login:: Visitez le lien suivant et suivez les instructions pour continuer, sinon ignorez cet email et il ne se passera rien
@@ -10271,14 +10572,14 @@
login::register: merci d'avoir confirme votre adresse email
Wir danken Ihnen für die Bestätigung Ihrer E-Mail Adresse
- Notification/Mail/MailSuccessEmailConfirmationUnregistered.php
Notification/Mail/MailSuccessEmailConfirmationRegistered.php
+ Notification/Mail/MailSuccessEmailConfirmationUnregistered.php
login::register: sujet email : confirmation de votre adresse email
E-Mail Adressen Bestätigung
- Notification/Mail/MailRequestEmailUpdate.php
Notification/Mail/MailRequestEmailConfirmation.php
+ Notification/Mail/MailRequestEmailUpdate.php
login::register:email: Voici un compte rendu du traitement de vos demandes d'acces :
@@ -10345,15 +10646,15 @@
no
Nein
web/common/technical_datas.html.twig
- web/account/sessions.html.twig
- web/admin/subdefs.html.twig
user/import/view.html.twig
+ web/admin/subdefs.html.twig
+ web/account/sessions.html.twig
no image selected
Kein Bild wurde ausgewählt
- actions/Tools/videoEditor.html.twig
actions/Tools/index.html.twig
+ actions/Tools/videoEditor.html.twig
non
@@ -10364,10 +10665,10 @@
notice
Beschreibung
- web/lightbox/validate.html.twig
- web/lightbox/feed.html.twig
- lightbox/IE6/validate.html.twig
lightbox/IE6/feed.html.twig
+ lightbox/IE6/validate.html.twig
+ web/lightbox/feed.html.twig
+ web/lightbox/validate.html.twig
nouveau
@@ -10608,10 +10909,10 @@
order-manager::order-list: order-id
Bestellung Nummer
- prod/orders/order_box.html.twig
- prod/orders/order_box.html.twig
prod/orders/order_item.html.twig
prod/orders/order_item.html.twig
+ prod/orders/order_box.html.twig
+ prod/orders/order_box.html.twig
order-manager::order-list: pending
@@ -10631,9 +10932,9 @@
order-manager::order-list: treated-documents
Verarbeitete(r) Datensatz(¨e)
+ prod/orders/order_item.html.twig
prod/orders/order_box.html.twig
prod/orders/order_box.html.twig
- prod/orders/order_item.html.twig
original logo
@@ -10670,17 +10971,17 @@
panier:: ordre Validation ascendante
gut bewertet
- prod/WorkZone/Macros.html.twig
+ prod/WorkZone/Macros.html.twig
panier:: ordre Validation descendante
weniger gut bewertet
- prod/WorkZone/Macros.html.twig
+ prod/WorkZone/Macros.html.twig
panier:: ordre du panier
Sammelkorb Reihenfolge
- prod/WorkZone/Macros.html.twig
+ prod/WorkZone/Macros.html.twig
panier:: renommer le panier
@@ -10715,8 +11016,8 @@
paniers::description du nouveau panier
Beschreibung
- prod/Baskets/Create.html.twig
prod/orders/order_item.html.twig
+ prod/Baskets/Create.html.twig
par %user_name%
@@ -10783,10 +11084,10 @@
phraseanet:: adresse
Adresse
web/common/dialog_export.html.twig
- web/account/account.html.twig
web/setup/step2.html.twig
admin/collection/collection.html.twig
web/admin/connected-users.html.twig
+ web/account/account.html.twig
phraseanet:: aide
@@ -10823,9 +11124,9 @@
phraseanet:: deconnection
Abmeldung
+ actions/Bridge/wrapper.html.twig
web/common/menubar.html.twig
web/common/menubar.html.twig
- actions/Bridge/wrapper.html.twig
phraseanet:: details
@@ -10899,13 +11200,13 @@
phraseanet:: thesaurus
Thesaurus
- web/thesaurus/load-thesaurus.html.twig
- web/thesaurus/load-thesaurus.html.twig
- web/thesaurus/thesaurus.html.twig
- web/thesaurus/thesaurus.html.twig
- web/thesaurus/index.html.twig
web/prod/tab_headers.html.twig
prod/actions/edit_default.html.twig
+ web/thesaurus/index.html.twig
+ web/thesaurus/thesaurus.html.twig
+ web/thesaurus/thesaurus.html.twig
+ web/thesaurus/load-thesaurus.html.twig
+ web/thesaurus/load-thesaurus.html.twig
phraseanet:: tri
@@ -10915,10 +11216,10 @@
phraseanet:: tri par date
nach Datum sortieren
- web/thesaurus/export-topics-dialog.html.twig
web/prod/index.html.twig
web/prod/index.html.twig
web/prod/index.html.twig
+ web/thesaurus/export-topics-dialog.html.twig
phraseanet:: tri par nom
@@ -10992,8 +11293,8 @@
phraseanet::chargement
Bitte warten...
Controller/Prod/LanguageController.php
- web/thesaurus/thesaurus.html.twig
prod/actions/edit_default.html.twig
+ web/thesaurus/thesaurus.html.twig
admin/collection/suggested_value.html.twig
@@ -11125,8 +11426,8 @@
phraseanet::type:: documents
Dokumente
- web/prod/toolbar.html.twig
web/prod/index.html.twig
+ web/prod/toolbar.html.twig
phraseanet::type:: images
@@ -11177,18 +11478,18 @@
preview:: arreter le diaporama
stoppen
prod/preview/result_train.html.twig
- prod/preview/result_train_options.html.twig
- prod/preview/feed_train.html.twig
prod/preview/basket_train.html.twig
+ prod/preview/feed_train.html.twig
+ prod/preview/result_train_options.html.twig
prod/preview/reg_train.html.twig
preview:: demarrer le diaporama
Dia-Schau
prod/preview/result_train.html.twig
- prod/preview/result_train_options.html.twig
- prod/preview/feed_train.html.twig
prod/preview/basket_train.html.twig
+ prod/preview/feed_train.html.twig
+ prod/preview/result_train_options.html.twig
prod/preview/reg_train.html.twig
@@ -11361,20 +11662,20 @@
prive
privat
- Bridge/Dailymotion/upload.html.twig
Bridge/Youtube/upload.html.twig
+ Bridge/Dailymotion/upload.html.twig
privé
privat
- Bridge/Dailymotion/video_modify.html.twig
Bridge/Youtube/video_modify.html.twig
+ Bridge/Dailymotion/video_modify.html.twig
processing
verarbeitend
- actions/Tools/videoEditor.html.twig
actions/Tools/index.html.twig
+ actions/Tools/videoEditor.html.twig
prod::Les enregistrements ne provienent pas tous de la meme base et ne peuvent donc etre traites ensemble
@@ -11390,7 +11691,7 @@
prod::action:property title
Eigenschaften
- web/prod/index.html.twig
+ web/prod/index.html.twig
prod::advancesearch:tooltips:datefield_restriction_explanation
@@ -11566,22 +11867,22 @@ Vorsicht: die aktuelle Werte werden durch die neue Werte überschrieben
prod::notification: notification title
Benachrichtigungen
- web/prod/index.html.twig
+ web/prod/index.html.twig
prod::push: List name can not be empty
Name der Liste wird erfordert
- web/prod/index.html.twig
+ web/prod/index.html.twig
prod::push: New list title
Neue Liste
- web/prod/index.html.twig
+ web/prod/index.html.twig
prod::push: add
Hinzufügen
- web/prod/index.html.twig
+ web/prod/index.html.twig
prod::push:push_set_title
@@ -11732,7 +12033,7 @@ Vorsicht: die aktuelle Werte werden durch die neue Werte überschrieben
prod::videoTools:chapterTitle
Kapiteltitel
- web/prod/index.html.twig
+ web/prod/index.html.twig
prod::workzone:Actions
@@ -11839,58 +12140,58 @@ Vorsicht: die aktuelle Werte werden durch die neue Werte überschrieben
public
öffentlich
- Bridge/Dailymotion/upload.html.twig
- Bridge/Dailymotion/video_modify.html.twig
Bridge/Youtube/upload.html.twig
Bridge/Youtube/video_modify.html.twig
+ Bridge/Dailymotion/upload.html.twig
+ Bridge/Dailymotion/video_modify.html.twig
publication : autheur
Autor
- actions/publish/publish.html.twig
actions/publish/publish_edit.html.twig
+ actions/publish/publish.html.twig
publication : email autheur
Autor E-Mail
- actions/publish/publish.html.twig
actions/publish/publish_edit.html.twig
+ actions/publish/publish.html.twig
publication : sous titre
Untertitel
- actions/publish/publish.html.twig
actions/publish/publish_edit.html.twig
+ actions/publish/publish.html.twig
-
+
publication : subtitle alert
- publication : subtitle alert
- actions/publish/publish.html.twig
+ Zeichenanzahl > 1024
actions/publish/publish_edit.html.twig
+ actions/publish/publish.html.twig
-
+
publication : subtitle warning
- publication : subtitle warning
- actions/publish/publish.html.twig
+ Höchtens 1024 Zeichen
actions/publish/publish_edit.html.twig
+ actions/publish/publish.html.twig
-
+
publication : title alert
- publication : title alert
- actions/publish/publish.html.twig
+ Zeichenanzahl > 128
actions/publish/publish_edit.html.twig
+ actions/publish/publish.html.twig
-
+
publication : title warning
- publication : title warning
- actions/publish/publish.html.twig
+ Höchstens 128 Zeichen
actions/publish/publish_edit.html.twig
+ actions/publish/publish.html.twig
publication : titre
Titel
- actions/publish/publish.html.twig
actions/publish/publish_edit.html.twig
+ actions/publish/publish.html.twig
publication::Voici votre fil RSS personnel. Il vous permettra d'etre tenu au courrant des publications.
@@ -11950,9 +12251,9 @@ Vorsicht: die aktuelle Werte werden durch die neue Werte überschrieben
rafraichir
Aktualisieren
+ prod/WorkZone/Macros.html.twig
prod/WorkZone/Basket.html.twig
prod/WorkZone/Story.html.twig
- prod/WorkZone/Macros.html.twig
prod/results/feeds.html.twig
prod/results/feeds.html.twig
@@ -12143,8 +12444,8 @@ Vorsicht: die aktuelle Werte werden durch die neue Werte überschrieben
report:: Connexion
Verbindungen
classes/module/report.php
- web/report/report_layout.html.twig
web/report/all_content.html.twig
+ web/report/report_layout.html.twig
report:: Databox content
@@ -12268,8 +12569,8 @@ Vorsicht: die aktuelle Werte werden durch die neue Werte überschrieben
report:: modules
Module
- module/report/filter.php
module/report/nav.php
+ module/report/filter.php
report:: navigateur
@@ -12289,21 +12590,21 @@ Vorsicht: die aktuelle Werte werden durch die neue Werte überschrieben
report:: non-renseigne
nicht ausgefüllt
- module/report/sent.php
- module/report/download.php
+ module/report/nav.php
module/report/activity.php
module/report/activity.php
module/report/activity.php
+ module/report/filter.php
module/report/edit.php
+ module/report/sent.php
+ module/report/download.php
+ module/report/add.php
+ module/report/validate.php
+ module/report/push.php
+ module/report/question.php
module/report/connexion.php
module/report/connexion.php
module/report/connexion.php
- module/report/validate.php
- module/report/question.php
- module/report/filter.php
- module/report/push.php
- module/report/add.php
- module/report/nav.php
report:: page d'accueil
@@ -12336,8 +12637,8 @@ Vorsicht: die aktuelle Werte werden durch die neue Werte überschrieben
report:: question
Anfrage
classes/module/report.php
- module/report/question.php
module/report/filter.php
+ module/report/question.php
report:: questions
@@ -12583,7 +12884,7 @@ Vorsicht: die aktuelle Werte werden durch die neue Werte überschrieben
select at least one user
Einen Benutzer auswählen
- web/admin/users.html.twig
+ web/admin/users.html.twig
separe par un espace
@@ -12729,8 +13030,8 @@ Vorsicht: die aktuelle Werte werden durch die neue Werte überschrieben
status:: numero de bit
Status Nummer
- admin/statusbit/edit.html.twig
web/admin/statusbit.html.twig
+ admin/statusbit/edit.html.twig
status:: retrouver sous forme de filtre dans la recherche
@@ -12841,8 +13142,8 @@ Vorsicht: die aktuelle Werte werden durch die neue Werte überschrieben
task::ftp:Status about your FTP transfert from %application% to %server%
Zustand von FTP Übertragung von %application% zum Server %server%
- Notification/Mail/MailSuccessFTPReceiver.php
Notification/Mail/MailSuccessFTPSender.php
+ Notification/Mail/MailSuccessFTPReceiver.php
task::ftp:TENTATIVE no %number%, %date%
@@ -12887,8 +13188,8 @@ Vorsicht: die aktuelle Werte werden durch die neue Werte überschrieben
task::ftp:proxy
Proxy
- task-manager/task-editor/ftp.html.twig
task-manager/task-editor/ftp-pull.html.twig
+ task-manager/task-editor/ftp.html.twig
task::ftp:proxy password
@@ -12898,8 +13199,8 @@ Vorsicht: die aktuelle Werte werden durch die neue Werte überschrieben
task::ftp:proxy port
Proxy Port
- task-manager/task-editor/ftp.html.twig
task-manager/task-editor/ftp-pull.html.twig
+ task-manager/task-editor/ftp.html.twig
task::ftp:proxy user
@@ -12994,8 +13295,8 @@ Vorsicht: die aktuelle Werte werden durch die neue Werte überschrieben
thesaurus:: Lier la branche de thesaurus
Die Verzweigung verbinden?
- web/thesaurus/link-field-step3.html.twig
web/thesaurus/link-field-step2.html.twig
+ web/thesaurus/link-field-step3.html.twig
thesaurus:: Lier la branche de thesaurus au champ
@@ -13149,9 +13450,9 @@ Vorsicht: die aktuelle Werte werden durch die neue Werte überschrieben
thesaurus:: export au format texte
Text
+ web/thesaurus/export-text.html.twig
web/thesaurus/export-text-dialog.html.twig
web/thesaurus/export-text-dialog.html.twig
- web/thesaurus/export-text.html.twig
thesaurus:: export en topics
@@ -13442,15 +13743,15 @@ Vorsicht: die aktuelle Werte werden durch die neue Werte überschrieben
thesaurus::menu: supprimer
Löschen
- web/thesaurus/properties.html.twig
web/thesaurus/thesaurus.html.twig
web/thesaurus/thesaurus.html.twig
+ web/thesaurus/properties.html.twig
thumbnail validation
Miniaturansicht Bestätigung
- actions/Tools/videoEditor.html.twig
actions/Tools/index.html.twig
+ actions/Tools/videoEditor.html.twig
tout le monde
@@ -13496,35 +13797,35 @@ Vorsicht: die aktuelle Werte werden durch die neue Werte überschrieben
upload:: Status :
Status
+ prod/upload/lazaret.html.twig
prod/upload/upload.html.twig
prod/upload/upload-flash.html.twig
- prod/upload/lazaret.html.twig
users rights have been reseted
Nutzerrechte wurden zurückgesetzt
- web/admin/users.html.twig
+ web/admin/users.html.twig
validate
Bestätigen
- actions/Tools/videoEditor.html.twig
actions/Tools/index.html.twig
actions/Tools/index.html.twig
actions/Tools/index.html.twig
actions/Tools/index.html.twig
+ actions/Tools/videoEditor.html.twig
validation:: NON
Nein
- mobile/lightbox/basket_element.html.twig
web/lightbox/agreement_box.html.twig
+ mobile/lightbox/basket_element.html.twig
validation:: OUI
Ja
- mobile/lightbox/basket_element.html.twig
web/lightbox/agreement_box.html.twig
+ mobile/lightbox/basket_element.html.twig
validation:: editer ma note
@@ -13544,10 +13845,10 @@ Vorsicht: die aktuelle Werte werden durch die neue Werte überschrieben
validation::envoyer mon rapport
Meinen Bericht senden
- mobile/lightbox/validate.html.twig
- mobile/lightbox/validate.html.twig
web/lightbox/basket_options.html.twig
web/lightbox/basket_options.html.twig
+ mobile/lightbox/validate.html.twig
+ mobile/lightbox/validate.html.twig
version
@@ -13562,8 +13863,8 @@ Vorsicht: die aktuelle Werte werden durch die neue Werte überschrieben
video
Video
- web/admin/subdefs.html.twig
task-manager/task-editor/subdefs.html.twig
+ web/admin/subdefs.html.twig
video range extractor
@@ -13701,8 +14002,8 @@ Vorsicht: die aktuelle Werte werden durch die neue Werte überschrieben
yes
Ja
web/common/technical_datas.html.twig
- web/account/sessions.html.twig
web/admin/subdefs.html.twig
+ web/account/sessions.html.twig
you are about to change the representation thumbnail of your video
diff --git a/resources/locales/messages.en.xlf b/resources/locales/messages.en.xlf
index 22a12fe5d7..0137de668d 100644
--- a/resources/locales/messages.en.xlf
+++ b/resources/locales/messages.en.xlf
@@ -1,16 +1,16 @@
-
+
-
+
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.
-
-
- Form/Login/PhraseaAuthenticationForm.php
+
+
Form/Configuration/EmailFormType.php
+ Form/Login/PhraseaAuthenticationForm.php
Add
@@ -111,9 +111,9 @@
%basket_length% documents
%basket_length% document(s)
- mobile/lightbox/validate.html.twig
web/lightbox/index.html.twig
web/lightbox/index.html.twig
+ mobile/lightbox/validate.html.twig
%countable% documents can not be modified.
@@ -164,9 +164,9 @@
%n_par_page% par page
%n_par_page% per page
- web/admin/users.html.twig
- web/admin/users.html.twig
- web/admin/users.html.twig
+ web/admin/users.html.twig
+ web/admin/users.html.twig
+ web/admin/users.html.twig
%name% est momentanement indisponible a cause d'un trop grand nombre de requetes
@@ -193,16 +193,16 @@
%nb_view% vue
%nb_view% view
- Bridge/Dailymotion/element_informations.html.twig
- Bridge/Flickr/element_informations.html.twig
Bridge/Youtube/element_informations.html.twig
+ Bridge/Flickr/element_informations.html.twig
+ Bridge/Dailymotion/element_informations.html.twig
%nb_view% vues
%nb_view% views
- Bridge/Dailymotion/element_informations.html.twig
- Bridge/Flickr/element_informations.html.twig
Bridge/Youtube/element_informations.html.twig
+ Bridge/Flickr/element_informations.html.twig
+ Bridge/Dailymotion/element_informations.html.twig
selectionnes]]>
@@ -546,7 +546,7 @@
Access
Access
actions/Feedback/List-Share.html.twig
- web/admin/editusers.html.twig
+ web/admin/editusers.html.twig
Access code
@@ -566,17 +566,17 @@
Access to HD
Access to Documents
- web/admin/editusers.html.twig
+ web/admin/editusers.html.twig
Access to image tools
Access to media tools
- web/admin/editusers.html.twig
+ web/admin/editusers.html.twig
Access to preview
Access subviews
- web/admin/editusers.html.twig
+ web/admin/editusers.html.twig
Access to the above bases constitutes acceptance of the following Terms of Use (TOU).
@@ -628,12 +628,12 @@
Actions
Actions
- Bridge/Dailymotion/actioncontainers.html.twig
- Bridge/Dailymotion/actionelements.html.twig
- Bridge/Flickr/actioncontainers.html.twig
- Bridge/Flickr/actionelements.html.twig
- Bridge/Youtube/actioncontainers.html.twig
Bridge/Youtube/actionelements.html.twig
+ Bridge/Youtube/actioncontainers.html.twig
+ Bridge/Flickr/actionelements.html.twig
+ Bridge/Flickr/actioncontainers.html.twig
+ Bridge/Dailymotion/actionelements.html.twig
+ Bridge/Dailymotion/actioncontainers.html.twig
Activate highlight
@@ -644,13 +644,13 @@
Active
Active
- web/admin/editusers.html.twig
+ web/admin/editusers.html.twig
Activer
Activate
- web/admin/editusers_timelimit_sbas.html.twig
admin/databox/databox.html.twig
+ web/admin/editusers_timelimit_sbas.html.twig
web/admin/editusers_timelimit.html.twig
@@ -671,8 +671,8 @@
Add
Add
- prod/User/Add.html.twig
prod/actions/Push.html.twig
+ prod/User/Add.html.twig
prod/upload/lazaret.html.twig
prod/upload/lazaret.html.twig
@@ -752,7 +752,7 @@
Adresse email du nouvel utilisateur
New user e-mail address
- web/admin/index.html.twig
+ web/admin/index.html.twig
Advanced Search
@@ -790,6 +790,11 @@
Show Title
web/prod/index.html.twig
+
+ Afficher le type
+ Display type
+ web/prod/index.html.twig
+
Afficher les status
Show Status
@@ -828,21 +833,21 @@
Ajouter a
Add to
- Bridge/Dailymotion/actionelements.html.twig
- Bridge/Flickr/actionelements.html.twig
Bridge/Youtube/actionelements.html.twig
+ Bridge/Flickr/actionelements.html.twig
+ Bridge/Dailymotion/actionelements.html.twig
Ajouter ma selection courrante
Add my current selection
- prod/Baskets/Create.html.twig
- prod/Story/Create.html.twig
prod/orders/order_item.html.twig
+ prod/Story/Create.html.twig
+ prod/Baskets/Create.html.twig
Ajouter un nouvel utilisateur
Add a new user
- web/admin/index.html.twig
+ web/admin/index.html.twig
Ajouter un publisher
@@ -865,6 +870,7 @@
actions/Feedback/list.html.twig
actions/Feedback/list.html.twig
WorkZone/Browser/Browser.html.twig
+ admin/worker-manager/worker_searchengine.html.twig
task-manager/task-editor/subdefs.html.twig
@@ -890,47 +896,47 @@
Allowed to access report
Allow to access Report
- web/admin/editusers.html.twig
+ web/admin/editusers.html.twig
Allowed to add
Allow to add
- web/admin/editusers.html.twig
+ web/admin/editusers.html.twig
Allowed to add in basket
Allow to add document to basket
- web/admin/editusers.html.twig
+ web/admin/editusers.html.twig
Allowed to change statuses
Allow to change status
- web/admin/editusers.html.twig
+ web/admin/editusers.html.twig
Allowed to delete
Allow to delete
- web/admin/editusers.html.twig
+ web/admin/editusers.html.twig
Allowed to edit
Allow to edit
- web/admin/editusers.html.twig
+ web/admin/editusers.html.twig
Allowed to order
Allow to order
- web/admin/editusers.html.twig
+ web/admin/editusers.html.twig
Allowed to publish
Allow to publish
- web/admin/editusers.html.twig
+ web/admin/editusers.html.twig
Allowed to push
Allow to push
- web/admin/editusers.html.twig
+ web/admin/editusers.html.twig
Alphabetic asc
@@ -953,22 +959,9 @@
An error occured
An error occurred
- Controller/Prod/LazaretController.php
- Controller/Prod/MoveCollectionController.php
- Controller/Prod/ToolsController.php
- Controller/Prod/BasketController.php
- Controller/Prod/StoryController.php
+ Model/Manipulator/LazaretManipulator.php
+ Model/Manipulator/LazaretManipulator.php
Controller/Admin/DataboxesController.php
- Controller/Admin/DataboxController.php
- Controller/Admin/DataboxController.php
- Controller/Admin/DataboxController.php
- Controller/Admin/DataboxController.php
- Controller/Admin/DataboxController.php
- Controller/Admin/DataboxController.php
- Controller/Admin/DataboxController.php
- Controller/Admin/DataboxController.php
- Controller/Admin/DataboxController.php
- Controller/Admin/DataboxController.php
Controller/Admin/CollectionController.php
Controller/Admin/CollectionController.php
Controller/Admin/CollectionController.php
@@ -980,13 +973,26 @@
Controller/Admin/CollectionController.php
Controller/Admin/CollectionController.php
Controller/Admin/CollectionController.php
- Model/Manipulator/LazaretManipulator.php
- Model/Manipulator/LazaretManipulator.php
+ Controller/Admin/DataboxController.php
+ Controller/Admin/DataboxController.php
+ Controller/Admin/DataboxController.php
+ Controller/Admin/DataboxController.php
+ Controller/Admin/DataboxController.php
+ Controller/Admin/DataboxController.php
+ Controller/Admin/DataboxController.php
+ Controller/Admin/DataboxController.php
+ Controller/Admin/DataboxController.php
+ Controller/Admin/DataboxController.php
+ Controller/Prod/LazaretController.php
+ Controller/Prod/MoveCollectionController.php
+ Controller/Prod/ToolsController.php
+ Controller/Prod/BasketController.php
+ Controller/Prod/StoryController.php
admin/collection/suggested_value.html.twig
admin/collection/collection.html.twig
- admin/databox/databox.html.twig
- web/admin/users.html.twig
+ web/admin/users.html.twig
task-manager/task-editor/task.html.twig
+ admin/databox/databox.html.twig
web/admin/databases.html.twig
@@ -1022,20 +1028,20 @@
An error occured, please retry or contact an admin if problem persists
An error occurred. Please retry or contact an administrator if problem persists.
- prod/orders/order_box.html.twig
prod/orders/order_item.html.twig
+ prod/orders/order_box.html.twig
An error occurred
An error occurred
Order/Controller/ProdOrderController.php
- Controller/Prod/BasketController.php
+ Controller/Admin/CollectionController.php
Controller/Admin/SearchEngineController.php
Controller/Admin/DataboxController.php
- Controller/Admin/CollectionController.php
+ Controller/Prod/BasketController.php
Controller/Api/V3Controller.php
- Controller/Api/V1Controller.php
- Controller/Api/V1Controller.php
+ Controller/Api/V1Controller.php
+ Controller/Api/V1Controller.php
web/admin/statusbit.html.twig
@@ -1127,14 +1133,14 @@
Apply a template
Apply a template
- web/admin/users.html.twig
- web/admin/editusers.html.twig
+ web/admin/users.html.twig
+ web/admin/editusers.html.twig
Apply changes
Apply changes
- actions/Property/type.html.twig
actions/Property/index.html.twig
+ actions/Property/type.html.twig
Apply status on story children.
@@ -1144,7 +1150,7 @@
Apply template
Apply template
- web/admin/index.html.twig
+ web/admin/index.html.twig
Apply to all selected documents
@@ -1160,7 +1166,7 @@
Are you sure you want delete users rights ?
Are you sure you want to remove existing users rights?
- web/admin/users.html.twig
+ web/admin/users.html.twig
Are you sure you want to delete this application?
@@ -1181,13 +1187,13 @@
Are you sure you want to reset rights?
Are you sure you want to reset the user rights?
- web/admin/editusers.html.twig
+ web/admin/editusers.html.twig
Attention
Warning
Controller/Prod/LanguageController.php
- web/admin/index.html.twig
+ web/admin/index.html.twig
Attention !
@@ -1273,8 +1279,8 @@
Audio Codec
Audio Codec
- Media/Subdef/Video.php
Media/Subdef/Audio.php
+ Media/Subdef/Video.php
Audio Samplerate
@@ -1365,20 +1371,20 @@
Autorisation d'acces
Access authorization
- api/auth/end_user_authorization.html.twig
api/auth/end_user_authorization.html.twig
+ api/auth/end_user_authorization.html.twig
Autoriser
Authorize
- api/auth/end_user_authorization.html.twig
api/auth/end_user_authorization.html.twig
+ api/auth/end_user_authorization.html.twig
Autorisez-vous l'application %application_name% a acceder a votre contenu sur %home_title% ?
Allow application %application_name% to access content on %home_title%?
- api/auth/end_user_authorization.html.twig
api/auth/end_user_authorization.html.twig
+ api/auth/end_user_authorization.html.twig
Available in multi-export tab
@@ -1393,11 +1399,11 @@
Back
Previous
- mobile/lightbox/basket_element.html.twig
- mobile/lightbox/validate.html.twig
mobile/lightbox/index.html.twig
mobile/lightbox/index.html.twig
mobile/lightbox/index.html.twig
+ mobile/lightbox/basket_element.html.twig
+ mobile/lightbox/validate.html.twig
Back to Feedback
@@ -1417,10 +1423,10 @@
Bad request format, only JSON is allowed
Bad request format. Only JSON is allowed.
- Controller/Root/AccountController.php
- Controller/Admin/DataboxController.php
Controller/Admin/RootController.php
Controller/Admin/RootController.php
+ Controller/Admin/DataboxController.php
+ Controller/Root/AccountController.php
Bad request, please contact an admin
@@ -1435,9 +1441,9 @@
Base %base%
Database %base%
- web/admin/editusers_quotas.html.twig
web/admin/editusers_timelimit_sbas.html.twig
web/admin/editusers_timelimit.html.twig
+ web/admin/editusers_quotas.html.twig
Base could not be created
@@ -1557,7 +1563,7 @@
CHAMPS
Fields setup
- web/admin/tree.html.twig
+ web/admin/tree.html.twig
Camera Model
@@ -1569,10 +1575,10 @@
Cancel
Cancel
Controller/Prod/LanguageController.php
- prod/User/Add.html.twig
- prod/actions/delete_records_confirm_form.html.twig
- actions/Property/type.html.twig
actions/Property/index.html.twig
+ actions/Property/type.html.twig
+ prod/actions/delete_records_confirm_form.html.twig
+ prod/User/Add.html.twig
admin/fields/templates.html.twig
task-manager/task-editor/task.html.twig
user/import/view.html.twig
@@ -1601,9 +1607,9 @@
Categorie
Category
- Bridge/Dailymotion/video_modify.html.twig
Bridge/Youtube/upload.html.twig
Bridge/Youtube/video_modify.html.twig
+ Bridge/Dailymotion/video_modify.html.twig
Ce champ est decrit comme element du %DublinCoreElementSet%
@@ -1635,12 +1641,12 @@
This field is mandatory
Bridge/Api/Dailymotion.php
Bridge/Api/Dailymotion.php
- Bridge/Api/Flickr.php
- Bridge/Api/Flickr.php
Bridge/Api/Youtube.php
Bridge/Api/Youtube.php
Bridge/Api/Youtube.php
Bridge/Api/Youtube.php
+ Bridge/Api/Flickr.php
+ Bridge/Api/Flickr.php
Ce champ est relie a une branche de thesaurus
@@ -1663,10 +1669,10 @@
Too long, expecting %length% characters max.
Bridge/Api/Dailymotion.php
Bridge/Api/Dailymotion.php
- Bridge/Api/Flickr.php
- Bridge/Api/Flickr.php
Bridge/Api/Youtube.php
Bridge/Api/Youtube.php
+ Bridge/Api/Flickr.php
+ Bridge/Api/Flickr.php
Ce champ est utilise en titre a l'affichage
@@ -1743,8 +1749,8 @@
Choisir
Choose
- prod/Baskets/Reorder.html.twig
prod/Story/Reorder.html.twig
+ prod/Baskets/Reorder.html.twig
web/admin/subdefs.html.twig
web/admin/subdefs.html.twig
web/admin/subdefs.html.twig
@@ -1756,8 +1762,8 @@
Choose a new password
Choose a new password
- web/account/change-password.html.twig
web/login/renew-password.html.twig
+ web/account/change-password.html.twig
Choose the title of the document to export
@@ -1772,8 +1778,8 @@
Clear
Clear
- admin/task-manager/log_task.html.twig
admin/task-manager/log_scheduler.html.twig
+ admin/task-manager/log_task.html.twig
Clear list
@@ -1809,8 +1815,8 @@
Codec Audio
Audio codec
- web/common/technical_datas.html.twig
actions/Tools/videoEditor.html.twig
+ web/common/technical_datas.html.twig
Codec Video
@@ -1820,15 +1826,15 @@
Collection
Collection
- prod/upload/lazaret.html.twig
prod/Story/Create.html.twig
+ prod/upload/lazaret.html.twig
admin/databox/details.html.twig
Collection %collection%
Collection %collection%
- web/admin/editusers_quotas.html.twig
web/admin/editusers_timelimit.html.twig
+ web/admin/editusers_quotas.html.twig
Collection empty successful
@@ -1874,9 +1880,9 @@
Company
Company
- prod/User/Add.html.twig
actions/Feedback/list.html.twig
actions/Feedback/ListsMacros.html.twig
+ prod/User/Add.html.twig
Complete the fields below to register on %instance_title%!
@@ -1891,10 +1897,10 @@
Confidentialite
Confidentiality
- Bridge/Dailymotion/upload.html.twig
- Bridge/Dailymotion/video_modify.html.twig
Bridge/Youtube/upload.html.twig
Bridge/Youtube/video_modify.html.twig
+ Bridge/Dailymotion/upload.html.twig
+ Bridge/Dailymotion/video_modify.html.twig
Confidentialite : privee
@@ -1919,7 +1925,7 @@
Confirm reset users rights before applying template
Confirm the reset of selected users' rights before applying the template
- web/admin/index.html.twig
+ web/admin/index.html.twig
Confirmation de votre mot de passe
@@ -1934,13 +1940,13 @@
Connection
Login
- login/providers/bind.html.twig
- login/providers/bind.html.twig
- login/providers/mapping.html.twig
- login/providers/mapping.html.twig
- login/oauth/login.html.twig
web/login/index.html.twig
web/login/index.html.twig
+ login/oauth/login.html.twig
+ login/providers/mapping.html.twig
+ login/providers/mapping.html.twig
+ login/providers/bind.html.twig
+ login/providers/bind.html.twig
Connection is OK but database does not exists or can not be accessed
@@ -2061,9 +2067,9 @@
Creer
Create
- Bridge/Dailymotion/actioncontainers.html.twig
- Bridge/Flickr/actioncontainers.html.twig
Bridge/Youtube/actioncontainers.html.twig
+ Bridge/Flickr/actioncontainers.html.twig
+ Bridge/Dailymotion/actioncontainers.html.twig
Creer la tache d'ecriture des metadonnees
@@ -2088,17 +2094,17 @@
Creer un model
Create a template user
- web/admin/index.html.twig
+ web/admin/index.html.twig
Creer un modele
Create a template
- web/admin/index.html.twig
+ web/admin/index.html.twig
Creer un utilisateur
Create a new user
- web/admin/index.html.twig
+ web/admin/index.html.twig
Creer une nouvelle applications
@@ -2108,8 +2114,8 @@
Creer une playlist
Create a Playlist
- Bridge/Dailymotion/playlist_createcontainer.html.twig
Bridge/Youtube/playlist_createcontainer.html.twig
+ Bridge/Dailymotion/playlist_createcontainer.html.twig
Creez une application pour commencer a utiliser l'API Phraseanet
@@ -2254,21 +2260,21 @@
Date de création
Creation date
- prod/Baskets/Reorder.html.twig
prod/Story/Reorder.html.twig
+ prod/Baskets/Reorder.html.twig
Date de demande
Request date
+ prod/orders/order_item.html.twig
prod/orders/order_box.html.twig
prod/orders/order_box.html.twig
- prod/orders/order_item.html.twig
Date de modification
Modification date
- prod/Baskets/Reorder.html.twig
prod/Story/Reorder.html.twig
+ prod/Baskets/Reorder.html.twig
Date(s) from field(s)
@@ -2284,9 +2290,9 @@
Deadline
Deadline
+ prod/orders/order_item.html.twig
prod/orders/order_box.html.twig
prod/orders/order_box.html.twig
- prod/orders/order_item.html.twig
Dear %user%,
@@ -2383,8 +2389,8 @@
Delete all users rights
Delete all users rights
- web/admin/users.html.twig
- web/admin/editusers.html.twig
+ web/admin/users.html.twig
+ web/admin/editusers.html.twig
Delete basket
@@ -2419,16 +2425,16 @@
Deny
Deny
- login/oauth/authorize-access.html.twig
prod/orders/order_item.html.twig
prod/orders/order_item.html.twig
+ login/oauth/authorize-access.html.twig
Deplacement %n_element% elements
Moving %n_element% documents
- Bridge/Dailymotion/video_moveinto_playlist.html.twig
- Bridge/Flickr/photo_moveinto_photoset.html.twig
Bridge/Youtube/video_moveinto_playlist.html.twig
+ Bridge/Flickr/photo_moveinto_photoset.html.twig
+ Bridge/Dailymotion/video_moveinto_playlist.html.twig
Dernier access
@@ -2438,8 +2444,8 @@
Derniere mise a jour le %updated_on%
Last Update on %updated_on%
- prod/results/entry.html.twig
prod/results/feeds_entry.html.twig
+ prod/results/entry.html.twig
Derniers envois
@@ -2454,24 +2460,24 @@
Description
Caption
- web/developers/application_form.html.twig
- Bridge/Dailymotion/upload.html.twig
- Bridge/Dailymotion/video_modify.html.twig
+ Bridge/Youtube/upload.html.twig
+ Bridge/Youtube/playlist_createcontainer.html.twig
+ Bridge/Youtube/video_modify.html.twig
Bridge/Flickr/photoset_createcontainer.html.twig
Bridge/Flickr/upload.html.twig
Bridge/Flickr/photo_modify.html.twig
- Bridge/Youtube/playlist_createcontainer.html.twig
- Bridge/Youtube/upload.html.twig
- Bridge/Youtube/video_modify.html.twig
+ Bridge/Dailymotion/upload.html.twig
+ Bridge/Dailymotion/video_modify.html.twig
prod/Tooltip/DCESFieldInfo.html.twig
+ web/developers/application_form.html.twig
Deselect all
Deselect all
prod/actions/Push.html.twig
actions/Feedback/list.html.twig
- web/report/form_date_and_base.html.twig
web/report/report_layout_child.html.twig
+ web/report/form_date_and_base.html.twig
Design of personalization logo section
@@ -2502,8 +2508,8 @@
Dimension
Size
- Media/Subdef/Video.php
Media/Subdef/Image.php
+ Media/Subdef/Video.php
Media/Subdef/Unknown.php
@@ -2653,8 +2659,8 @@
E-mail
E-mail
- Form/Login/PhraseaRegisterForm.php
Form/Login/PhraseaForgotPasswordForm.php
+ Form/Login/PhraseaRegisterForm.php
E-mail domain
@@ -2709,19 +2715,19 @@
Edition de 1 element
Editing 1 document
- Bridge/Dailymotion/video_modify.html.twig
- Bridge/Flickr/photo_modify.html.twig
Bridge/Youtube/video_modify.html.twig
+ Bridge/Flickr/photo_modify.html.twig
+ Bridge/Dailymotion/video_modify.html.twig
Edition des droits de %display_name%
%display_name% user right edition
- web/admin/editusers.html.twig
+ web/admin/editusers.html.twig
Edition des droits de %number% utilisateurs
User rights edition of %number% users
- web/admin/editusers.html.twig
+ web/admin/editusers.html.twig
Edition impossible
@@ -2764,8 +2770,8 @@
Email
E-mail
- web/admin/dashboard.html.twig
admin/publications/fiche.html.twig
+ web/admin/dashboard.html.twig
Email '%email%' for login '%login%' already exists in database
@@ -2795,8 +2801,8 @@
Email successfully confirmed
E-mail successfully confirmed.
- Notification/Mail/MailSuccessEmailConfirmationUnregistered.php
Notification/Mail/MailSuccessEmailConfirmationRegistered.php
+ Notification/Mail/MailSuccessEmailConfirmationUnregistered.php
Email test result : %email_status%
@@ -3019,8 +3025,8 @@
Erreur !
Error !
- mobile/lightbox/error.html.twig
web/lightbox/error.html.twig
+ mobile/lightbox/error.html.twig
Erreur : soit les parametres sont incorrects, soit le serveur distant ne repond pas
@@ -3113,8 +3119,8 @@
Error while sending the file
Error while sending the file
- Controller/Admin/DataboxController.php
Controller/Admin/CollectionController.php
+ Controller/Admin/DataboxController.php
Error while uploading
@@ -3140,14 +3146,14 @@
Etes vous sur de supprimer %number% playlists ?
Do you confirm that you want to delete of %number% playlists ?
- Bridge/Dailymotion/playlist_deleteelement.html.twig
Bridge/Youtube/playlist_deleteelement.html.twig
+ Bridge/Dailymotion/playlist_deleteelement.html.twig
Etes vous sur de supprimer %number% videos ?
Confirm delete of %number% videos ?
- Bridge/Dailymotion/video_deleteelement.html.twig
Bridge/Youtube/video_deleteelement.html.twig
+ Bridge/Dailymotion/video_deleteelement.html.twig
Ex : Paris, bleu, montagne
@@ -3210,10 +3216,10 @@
Feedback
Feedback
Controller/Prod/LanguageController.php
- web/prod/toolbar.html.twig
+ prod/WorkZone/Macros.html.twig
prod/WorkZone/Basket.html.twig
prod/WorkZone/Story.html.twig
- prod/WorkZone/Macros.html.twig
+ web/prod/toolbar.html.twig
Feeds
@@ -3268,11 +3274,11 @@
File is not present in quarantine anymore, please refresh
Document is not in quarantine anymore, please refresh
- Controller/Prod/LazaretController.php
- Controller/Prod/LazaretController.php
Model/Manipulator/LazaretManipulator.php
Model/Manipulator/LazaretManipulator.php
Model/Manipulator/LazaretManipulator.php
+ Controller/Prod/LazaretController.php
+ Controller/Prod/LazaretController.php
File is too big : 64k max
@@ -3307,8 +3313,8 @@
Fils disponibles
Available feed
- actions/publish/publish.html.twig
actions/publish/publish_edit.html.twig
+ actions/publish/publish.html.twig
Filter
@@ -3330,13 +3336,13 @@
First/Last name
actions/Feedback/ListsMacros.html.twig
web/admin/users.html.twig
- web/admin/users.html.twig
+ web/admin/users.html.twig
Flash
Flash
- web/common/technical_datas.html.twig
web/prod/index.html.twig
+ web/common/technical_datas.html.twig
FlashFired
@@ -3377,10 +3383,10 @@
Forgot password?
Forgot password?
- login/providers/bind.html.twig
- login/providers/mapping.html.twig
- login/oauth/login.html.twig
web/login/index.html.twig
+ login/oauth/login.html.twig
+ login/providers/mapping.html.twig
+ login/providers/bind.html.twig
Forgot your password?
@@ -3561,9 +3567,9 @@
Guest
Guest
- mobile/common/menubar.html.twig
web/common/menubar.html.twig
web/common/menubar.html.twig
+ mobile/common/menubar.html.twig
Guest access
@@ -3594,8 +3600,8 @@
Hello %username%
Hi %username%
api/auth/native_app_access_token.html.twig
- api/auth/end_user_authorization.html.twig
api/auth/end_user_authorization.html.twig
+ api/auth/end_user_authorization.html.twig
Help
@@ -3615,11 +3621,11 @@
Home
Home
- mobile/lightbox/basket_element.html.twig
- mobile/lightbox/validate.html.twig
- mobile/lightbox/feed.html.twig
- login/include/language-block.html.twig
login/layout/base-layout.html.twig
+ login/include/language-block.html.twig
+ mobile/lightbox/basket_element.html.twig
+ mobile/lightbox/feed.html.twig
+ mobile/lightbox/validate.html.twig
Homepage slideshow
@@ -3710,8 +3716,8 @@
Images par secondes
Image per second
- web/common/technical_datas.html.twig
actions/Tools/videoEditor.html.twig
+ web/common/technical_datas.html.twig
Imagette indisponible
@@ -3758,9 +3764,9 @@
Informations
Info
- web/account/base.html.twig
- web/admin/dashboard.html.twig
admin/user/registrations.html.twig
+ web/admin/dashboard.html.twig
+ web/account/base.html.twig
Informations personnelles
@@ -3770,7 +3776,7 @@
Infos
Info
- web/admin/editusers.html.twig
+ web/admin/editusers.html.twig
Inscription
@@ -3796,8 +3802,8 @@
Invalid file format
Invalid file format
- Controller/Admin/DataboxController.php
Controller/Admin/CollectionController.php
+ Controller/Admin/DataboxController.php
Invalid file type
@@ -3808,9 +3814,9 @@
Invalid file type, only (%supported_file_types%) file formats are supported
Invalid file type. Only %supported_file_types% file formats are supported.
- admin/statusbit/edit.html.twig
- admin/databox/databox.html.twig
user/import/file.html.twig
+ admin/databox/databox.html.twig
+ admin/statusbit/edit.html.twig
Invalid file type, only (%supported_file_types%) file formats are supported'
@@ -3820,8 +3826,8 @@
Invalid labels parameter
Invalid label parameters
- Controller/Admin/DataboxController.php
Controller/Admin/CollectionController.php
+ Controller/Admin/DataboxController.php
Invalid link.
@@ -3847,8 +3853,8 @@
Inverser
Reverse
- prod/Baskets/Reorder.html.twig
prod/Story/Reorder.html.twig
+ prod/Baskets/Reorder.html.twig
It is not recommended to install Phraseanet without HTTPS support
@@ -3883,8 +3889,8 @@
L'upload a echoue
Upload failed
- Bridge/Api/Flickr.php
Bridge/Api/Youtube.php
+ Bridge/Api/Flickr.php
L'upload concernant le record %title% sur le compte %bridge_name% a echoue pour les raisons suivantes : %reason%
@@ -3894,17 +3900,17 @@
L'utilisateur approuve ce document
User approves this document
- prod/WorkZone/Macros.html.twig
+ prod/WorkZone/Macros.html.twig
L'utilisateur desapprouve ce document
User disapproves this document
- prod/WorkZone/Macros.html.twig
+ prod/WorkZone/Macros.html.twig
L'utilisateur n'a pas encore donne son avis sur ce document
User hasn't decided yet
- prod/WorkZone/Macros.html.twig
+ prod/WorkZone/Macros.html.twig
La connection vers le serveur distant est OK
@@ -3965,7 +3971,7 @@
Last applied template
Applied user template
- web/admin/users.html.twig
+ web/admin/users.html.twig
Last name is required
@@ -4031,15 +4037,15 @@
Le poids maximum d'un fichier est de %size%
Maximum filesize is %size%
Bridge/Api/Dailymotion.php
- Bridge/Api/Flickr.php
Bridge/Api/Youtube.php
+ Bridge/Api/Flickr.php
Le record n'a pas de fichier physique
No physical file for this record
Bridge/Api/Dailymotion.php
- Bridge/Api/Flickr.php
Bridge/Api/Youtube.php
+ Bridge/Api/Flickr.php
Le token n'a pas encore ete genere
@@ -4060,9 +4066,9 @@
Les elements ne peuvent etre uploades (problemes de type ou de droit)
Selected files can't be uploaded (file type or rights error)
- Bridge/Dailymotion/upload.html.twig
- Bridge/Flickr/upload.html.twig
Bridge/Youtube/upload.html.twig
+ Bridge/Flickr/upload.html.twig
+ Bridge/Dailymotion/upload.html.twig
Les indications donnees ci dessous sont a titre informatif.
@@ -4182,10 +4188,10 @@
Login
Login
Form/Login/PhraseaAuthenticationForm.php
- login/providers/bind.html.twig
- login/providers/mapping.html.twig
- login/oauth/login.html.twig
actions/Feedback/ListsMacros.html.twig
+ login/oauth/login.html.twig
+ login/providers/mapping.html.twig
+ login/providers/bind.html.twig
Login %login% already exists in database
@@ -4205,8 +4211,8 @@
Login to link your account
Login to link your account
- login/providers/bind.html.twig
login/providers/mapping.html.twig
+ login/providers/bind.html.twig
Logs
@@ -4242,32 +4248,32 @@
Manage DB fields
Manage documentary fields of database
- web/admin/editusers.html.twig
+ web/admin/editusers.html.twig
Manage Database
Manage database
- web/admin/editusers.html.twig
+ web/admin/editusers.html.twig
Manage Thesaurus
Manage thesaurus
- web/admin/editusers.html.twig
+ web/admin/editusers.html.twig
Manage collection
Manage collection
- web/admin/editusers.html.twig
+ web/admin/editusers.html.twig
Manage users
Manage users
- web/admin/editusers.html.twig
+ web/admin/editusers.html.twig
Manage values lists
Manage values lists
- web/admin/editusers.html.twig
+ web/admin/editusers.html.twig
Mandatory
@@ -4348,8 +4354,8 @@
Missing labels parameter
Labels missing parameter
- Controller/Admin/DataboxController.php
Controller/Admin/CollectionController.php
+ Controller/Admin/DataboxController.php
Missing mandatory parameter %parameter%
@@ -4491,8 +4497,8 @@
Ne pas autoriser
Do not authorize
- api/auth/end_user_authorization.html.twig
api/auth/end_user_authorization.html.twig
+ api/auth/end_user_authorization.html.twig
Ne pas creer de DataBox maintenant
@@ -4525,15 +4531,15 @@
Next
Next
- actions/Feedback/ListsMacros.html.twig
prod/orders/order_box.html.twig
prod/orders/order_box.html.twig
+ actions/Feedback/ListsMacros.html.twig
No
No
- web/account/account.html.twig
web/developers/applications.html.twig
+ web/account/account.html.twig
No URL available
@@ -4650,13 +4656,13 @@
Nom du nouveau modele
New template name
- web/admin/index.html.twig
+ web/admin/index.html.twig
Nom du nouveau panier
Name
- prod/Baskets/Create.html.twig
prod/orders/order_item.html.twig
+ prod/Baskets/Create.html.twig
Non-Restreinte (publique)
@@ -4668,7 +4674,7 @@
None
None
Form/Configuration/EmailFormType.php
- web/admin/users.html.twig
+ web/admin/users.html.twig
admin/user/registrations.html.twig
@@ -4835,10 +4841,10 @@
Or login with
Or login with
- web/login/register.html.twig
- login/oauth/login.html.twig
- web/login/index.html.twig
api/auth/end_user_authorization.html.twig
+ web/login/index.html.twig
+ login/oauth/login.html.twig
+ web/login/register.html.twig
Order
@@ -4849,15 +4855,15 @@
Order has been denied
The Record has been denied.
Order/Controller/ProdOrderController.php
- prod/orders/order_box.html.twig
prod/orders/order_item.html.twig
+ prod/orders/order_box.html.twig
Order has been sent
The record has been sent.
Order/Controller/ProdOrderController.php
- prod/orders/order_box.html.twig
prod/orders/order_item.html.twig
+ prod/orders/order_box.html.twig
Orders manager
@@ -4910,19 +4916,19 @@
Paniers
Baskets
+ web/lightbox/index.html.twig
+ lightbox/IE6/validate.html.twig
+ web/lightbox/validate.html.twig
+ web/account/account.html.twig
mobile/lightbox/index.html.twig
mobile/lightbox/index.html.twig
- web/account/account.html.twig
- web/lightbox/validate.html.twig
- lightbox/IE6/validate.html.twig
- web/lightbox/index.html.twig
Par %author%
by %author%
- mobile/lightbox/feed.html.twig
- prod/results/entry.html.twig
prod/results/feeds_entry.html.twig
+ prod/results/entry.html.twig
+ mobile/lightbox/feed.html.twig
Password
@@ -5025,8 +5031,8 @@
Playlist
Playlist
- Bridge/Dailymotion/actionelements.html.twig
Bridge/Youtube/actionelements.html.twig
+ Bridge/Dailymotion/actionelements.html.twig
Playlists
@@ -5186,9 +5192,9 @@
Previous
Previous
- actions/Feedback/ListsMacros.html.twig
prod/orders/order_box.html.twig
prod/orders/order_box.html.twig
+ actions/Feedback/ListsMacros.html.twig
Print
@@ -5198,8 +5204,8 @@
Problemes de connexion ?
Connection problems?
- api/auth/end_user_authorization.html.twig
api/auth/end_user_authorization.html.twig
+ api/auth/end_user_authorization.html.twig
Process the registration
@@ -5229,10 +5235,10 @@
Publications
Publications
- web/common/menubar.html.twig
web/prod/index.html.twig
- web/admin/tree.html.twig
+ web/common/menubar.html.twig
admin/publications/wrapper.html.twig
+ web/admin/tree.html.twig
Publier
@@ -5305,7 +5311,6 @@
Push::filter starts
Starts with
actions/Feedback/list.html.twig
- web/admin/users.html.twig
Push::une validation est une demande d'appreciation a d'autres personnes
@@ -5435,13 +5440,14 @@
Re-initialiser
Reset
- prod/Baskets/Reorder.html.twig
- prod/Story/Reorder.html.twig
web/prod/index.html.twig
+ prod/Story/Reorder.html.twig
+ prod/Baskets/Reorder.html.twig
Re-ordonner
Set order
+ prod/Story/Reorder.html.twig
prod/WorkZone/Macros.html.twig
prod/WorkZone/Macros.html.twig
prod/WorkZone/Macros.html.twig
@@ -5449,7 +5455,6 @@
prod/WorkZone/Macros.html.twig
prod/WorkZone/Macros.html.twig
prod/Baskets/Reorder.html.twig
- prod/Story/Reorder.html.twig
Read-only
@@ -5560,7 +5565,7 @@
Record Not Found
Record not found
- Controller/Api/V1Controller.php
+ Controller/Api/V1Controller.php
Record removed from basket
@@ -5602,8 +5607,8 @@
Register
Register
login/include/register-link-block.html.twig
- web/login/register.html.twig
web/login/register-provider.html.twig
+ web/login/register.html.twig
web/login/register-classic.html.twig
@@ -5620,13 +5625,13 @@
Reglages:: reglages d acces guest
Guest access setup
web/admin/users.html.twig
- web/admin/editusers.html.twig
+ web/admin/editusers.html.twig
Reglages:: reglages d inscitpition automatisee
Auto register setup
web/admin/users.html.twig
- web/admin/editusers.html.twig
+ web/admin/editusers.html.twig
Relevance
@@ -5662,7 +5667,7 @@
Remove watermark
Remove watermark
- web/admin/editusers.html.twig
+ web/admin/editusers.html.twig
Rename
@@ -5673,8 +5678,8 @@
Renew password
Renew password
Notification/Mail/MailRequestPasswordUpdate.php
- web/account/change-password.html.twig
web/login/renew-password.html.twig
+ web/account/change-password.html.twig
Reorder collections
@@ -5684,8 +5689,8 @@
Reordonner automatiquement
Sort automatically
- prod/Baskets/Reorder.html.twig
prod/Story/Reorder.html.twig
+ prod/Baskets/Reorder.html.twig
Repertoire de stockage des fichiers
@@ -5721,7 +5726,7 @@
Require email validation to activate the account
Require e-mail validation to activate the account.
- web/admin/index.html.twig
+ web/admin/index.html.twig
Required
@@ -5741,12 +5746,12 @@
Reset and apply
Reset and apply
- web/admin/index.html.twig
+ web/admin/index.html.twig
Reset and apply template
Reset and apply template
- web/admin/index.html.twig
+ web/admin/index.html.twig
Reset cache
@@ -5756,7 +5761,7 @@
Reset rights before applying template?
Reset rights before applying template?
- web/admin/index.html.twig
+ web/admin/index.html.twig
Resolution
@@ -5792,8 +5797,8 @@
Restriction
Restriction
- web/admin/editusers_quotas.html.twig
admin/publications/list.html.twig
+ web/admin/editusers_quotas.html.twig
Restrictions de telechargement
@@ -5838,12 +5843,12 @@