diff --git a/.dockerignore b/.dockerignore
index 95d7045edf..12389ce613 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -7,6 +7,7 @@
/.env.*
/env.*
/.git
+/.gitignore
/.travis.yml
/AUTHORS
/CONTRIBUTORS
diff --git a/Dockerfile b/Dockerfile
index 3feefd2d3b..8231d48455 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -4,7 +4,11 @@
#########################################################################
FROM php:7.0-fpm-stretch as phraseanet-system
-RUN apt-get update \
+
+ENV FFMPEG_VERSION=4.2.2
+
+RUN echo "deb http://deb.debian.org/debian stretch main non-free" > /etc/apt/sources.list \
+ && apt-get update \
&& apt-get install -y \
apt-transport-https \
ca-certificates \
@@ -40,6 +44,29 @@ RUN apt-get update \
libreoffice-math \
libreoffice-writer \
libreoffice-pdfimport \
+ # FFmpeg
+ yasm \
+ libvorbis-dev \
+ texi2html \
+ nasm \
+ zlib1g-dev \
+ libx264-dev \
+ libfdk-aac-dev \
+ libopus-dev \
+ libvpx-dev \
+ libmp3lame-dev \
+ libogg-dev \
+ libopencore-amrnb-dev \
+ libopencore-amrwb-dev \
+ libdc1394-22-dev \
+ libx11-dev \
+ libswscale-dev \
+ libpostproc-dev \
+ libxvidcore-dev \
+ libtheora-dev \
+ libgsm1-dev \
+ libfreetype6-dev \
+ # End FFmpeg
&& update-locale "LANG=fr_FR.UTF-8 UTF-8" \
&& dpkg-reconfigure --frontend noninteractive locales \
&& docker-php-ext-configure gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ \
@@ -54,6 +81,37 @@ RUN apt-get update \
&& docker-php-ext-enable redis amqp zmq imagick \
&& pecl clear-cache \
&& docker-php-source delete \
+ && mkdir /tmp/ffmpeg \
+ && curl -s https://ffmpeg.org/releases/ffmpeg-${FFMPEG_VERSION}.tar.bz2 | tar jxf - -C /tmp/ffmpeg \
+ && ( \
+ cd /tmp/ffmpeg/ffmpeg-${FFMPEG_VERSION} \
+ && ./configure \
+ --enable-gpl \
+ --enable-nonfree \
+ --enable-libfdk-aac \
+ --enable-libfdk_aac \
+ --enable-libgsm \
+ --enable-libmp3lame \
+ --enable-libtheora \
+ --enable-libvorbis \
+ --enable-libvpx \
+ --enable-libfreetype \
+ --enable-libopus \
+ --enable-libx264 \
+ --enable-libxvid \
+ --enable-zlib \
+ --enable-postproc \
+ --enable-swscale \
+ --enable-pthreads \
+ --enable-libdc1394 \
+ --enable-version3 \
+ --enable-libopencore-amrnb \
+ --enable-libopencore-amrwb \
+ && make \
+ && make install \
+ && make distclean \
+ ) \
+ && rm -rf /tmp/ffmpeg \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists \
&& mkdir /entrypoint /var/alchemy \
diff --git a/bin/console b/bin/console
index 2dcc02e275..c49fc89052 100755
--- a/bin/console
+++ b/bin/console
@@ -57,6 +57,7 @@ use Alchemy\Phrasea\Command\User\UserCreateCommand;
use Alchemy\Phrasea\Command\User\UserPasswordCommand;
use Alchemy\Phrasea\Command\User\UserListCommand;
use Alchemy\Phrasea\Command\UpgradeDBDatas;
+use Alchemy\Phrasea\Command\ApplyRightsCommand;
require_once __DIR__ . '/../lib/autoload.php';
@@ -93,6 +94,7 @@ $cli->command(new \module_console_aboutLicense('about:license'));
$cli->command(new CheckConfig('check:config'));
$cli->command(new UpgradeDBDatas('system:upgrade-datas'));
+$cli->command(new ApplyRightsCommand('system:apply-rights'));
$cli->command(new \module_console_systemMailCheck('system:mail-check'));
$cli->command(new \module_console_systemBackupDB('system:backup-db'));
@@ -160,9 +162,9 @@ $cli->command(new QueryParseCommand());
$cli->command(new QuerySampleCommand());
$cli->command(new FindConceptsCommand());
-$cli->command($cli['alchemy_worker.commands.run_dispatcher_command']);
-$cli->command($cli['alchemy_worker.commands.run_worker_command']);
-$cli->command($cli['alchemy_worker.commands.show_configuration']);
+//$cli->command($cli['alchemy_worker.commands.run_dispatcher_command']);
+//$cli->command($cli['alchemy_worker.commands.run_worker_command']);
+//$cli->command($cli['alchemy_worker.commands.show_configuration']);
$cli->loadPlugins();
diff --git a/cache/.gitkeep b/cache/.gitkeep
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/composer.json b/composer.json
index 46ff98203e..0fd22effb0 100644
--- a/composer.json
+++ b/composer.json
@@ -96,7 +96,7 @@
"league/flysystem": "^1.0",
"league/flysystem-aws-s3-v2": "^1.0",
"league/fractal": "dev-webgalleries#af1acc0275438571bc8c1d08a05a4b5af92c9f97 as 0.13.0",
- "media-alchemyst/media-alchemyst": "^0.5.5",
+ "media-alchemyst/media-alchemyst": "^0.5.6",
"monolog/monolog": "~1.3",
"mrclay/minify": "~2.1.6",
"neutron/process-manager": "2.0.x-dev@dev",
@@ -105,7 +105,7 @@
"neutron/silex-imagine-provider": "~0.1.0",
"neutron/temporary-filesystem": "~2.1",
"pagerfanta/pagerfanta": "^1.0",
- "php-ffmpeg/php-ffmpeg": "~0.5.0",
+ "php-ffmpeg/php-ffmpeg": "^v0.15",
"php-xpdf/php-xpdf": "~0.2.1",
"exiftool/exiftool": "^11",
"ramsey/uuid": "^3.0",
diff --git a/composer.lock b/composer.lock
index 41a8b05beb..112a4ecd0f 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": "5a4a0be62b13071a6b06893b7ce08372",
+ "content-hash": "008ff0b5d3d13b4f0ce5d34348ded83a",
"packages": [
{
"name": "alchemy-fr/tcpdf-clone",
@@ -4331,16 +4331,16 @@
},
{
"name": "media-alchemyst/media-alchemyst",
- "version": "0.5.5",
+ "version": "0.5.6",
"source": {
"type": "git",
"url": "https://github.com/alchemy-fr/Media-Alchemyst.git",
- "reference": "3bd3204b69882f495adfb617383a077face92ed0"
+ "reference": "2b9f7697997f7863bbc3d08344c559a1cba519c2"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/alchemy-fr/Media-Alchemyst/zipball/3bd3204b69882f495adfb617383a077face92ed0",
- "reference": "3bd3204b69882f495adfb617383a077face92ed0",
+ "url": "https://api.github.com/repos/alchemy-fr/Media-Alchemyst/zipball/2b9f7697997f7863bbc3d08344c559a1cba519c2",
+ "reference": "2b9f7697997f7863bbc3d08344c559a1cba519c2",
"shasum": ""
},
"require": {
@@ -4350,7 +4350,7 @@
"monolog/monolog": "~1.0",
"neutron/temporary-filesystem": "^2.1.1",
"php": ">=5.3.3",
- "php-ffmpeg/php-ffmpeg": ">=0.4.2,<0.6",
+ "php-ffmpeg/php-ffmpeg": "^v0.15",
"php-mp4box/php-mp4box": "~0.3.0",
"php-unoconv/php-unoconv": "~0.3.1",
"pimple/pimple": "~1.0",
@@ -4401,7 +4401,7 @@
"video",
"video processing"
],
- "time": "2019-12-11T07:20:45+00:00"
+ "time": "2020-04-01T08:51:55+00:00"
},
{
"name": "monolog/monolog",
@@ -5147,29 +5147,29 @@
},
{
"name": "php-ffmpeg/php-ffmpeg",
- "version": "0.5.1",
+ "version": "v0.15",
"source": {
"type": "git",
"url": "https://github.com/PHP-FFMpeg/PHP-FFMpeg.git",
- "reference": "c8949fe3df89edd7692368cc110a51a27971f28a"
+ "reference": "984dbd046b6d8c285f9e7419fc7645f197513bfa"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/PHP-FFMpeg/PHP-FFMpeg/zipball/c8949fe3df89edd7692368cc110a51a27971f28a",
- "reference": "c8949fe3df89edd7692368cc110a51a27971f28a",
+ "url": "https://api.github.com/repos/PHP-FFMpeg/PHP-FFMpeg/zipball/984dbd046b6d8c285f9e7419fc7645f197513bfa",
+ "reference": "984dbd046b6d8c285f9e7419fc7645f197513bfa",
"shasum": ""
},
"require": {
- "alchemy/binary-driver": "~1.5",
- "doctrine/cache": "~1.0",
- "evenement/evenement": "~1.0",
- "neutron/temporary-filesystem": "~2.1, >=2.1.1",
- "php": ">=5.3.3"
+ "alchemy/binary-driver": "^1.5 || ~2.0.0 || ^5.0",
+ "doctrine/cache": "^1.0",
+ "evenement/evenement": "^3.0 || ^2.0 || ^1.0",
+ "neutron/temporary-filesystem": "^2.1.1",
+ "php": "^5.3.9 || ^7.0"
},
"require-dev": {
- "phpunit/phpunit": "~3.7",
"sami/sami": "~1.0",
- "silex/silex": "~1.0"
+ "silex/silex": "~1.0",
+ "symfony/phpunit-bridge": "^5.0.4"
},
"suggest": {
"php-ffmpeg/extras": "A compilation of common audio & video drivers for PHP-FFMpeg"
@@ -5177,7 +5177,7 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "0.5-dev"
+ "dev-master": "0.7-dev"
}
},
"autoload": {
@@ -5199,6 +5199,21 @@
"name": "Phraseanet Team",
"email": "info@alchemy.fr",
"homepage": "http://www.phraseanet.com/"
+ },
+ {
+ "name": "Patrik Karisch",
+ "email": "patrik@karisch.guru",
+ "homepage": "http://www.karisch.guru"
+ },
+ {
+ "name": "Romain Biard",
+ "email": "romain.biard@gmail.com",
+ "homepage": "https://www.strime.io/"
+ },
+ {
+ "name": "Jens Hausdorf",
+ "email": "hello@jens-hausdorf.de",
+ "homepage": "https://jens-hausdorf.de"
}
],
"description": "FFMpeg PHP, an Object Oriented library to communicate with AVconv / ffmpeg",
@@ -5212,7 +5227,7 @@
"video",
"video processing"
],
- "time": "2014-08-26T08:46:56+00:00"
+ "time": "2020-03-23T09:32:09+00:00"
},
{
"name": "php-mp4box/php-mp4box",
@@ -7772,7 +7787,7 @@
],
"packages-dev": [
{
- "name": "mikey179/vfsStream",
+ "name": "mikey179/vfsstream",
"version": "v1.6.4",
"source": {
"type": "git",
diff --git a/docker/phraseanet/entrypoint.sh b/docker/phraseanet/entrypoint.sh
index 037fe0aa02..2e0f07519b 100755
--- a/docker/phraseanet/entrypoint.sh
+++ b/docker/phraseanet/entrypoint.sh
@@ -15,6 +15,7 @@ chown -R app:app \
FILE=config/configuration.yml
if [ -f "$FILE" ]; then
+ bin/setup system:config set registry.general.title $PHRASEANET_PROJECT_NAME
echo "$FILE exists, skip setup."
else
echo "$FILE doesn't exist, entering setup..."
diff --git a/lib/Alchemy/Phrasea/Account/AccountService.php b/lib/Alchemy/Phrasea/Account/AccountService.php
index 4c0e594e64..567a404a94 100644
--- a/lib/Alchemy/Phrasea/Account/AccountService.php
+++ b/lib/Alchemy/Phrasea/Account/AccountService.php
@@ -167,11 +167,11 @@ class AccountService
* @param string $login
* @throws AccountException
*/
- public function deleteAccount($login = null)
+ public function deleteAccount($login = null, array $grantedBaseIdList = array())
{
$user = $this->getUserOrCurrentUser($login);
- $this->userManipulator->delete($user);
+ $this->userManipulator->delete($user, $grantedBaseIdList);
}
/**
diff --git a/lib/Alchemy/Phrasea/Authentication/RegistrationService.php b/lib/Alchemy/Phrasea/Authentication/RegistrationService.php
index 7ffdf8c489..2ffe87f0cd 100644
--- a/lib/Alchemy/Phrasea/Authentication/RegistrationService.php
+++ b/lib/Alchemy/Phrasea/Authentication/RegistrationService.php
@@ -328,11 +328,9 @@ class RegistrationService
$autoReg = $acl->get_granted_base();
- $granted = [];
foreach ($autoReg as $baseId => $collection) {
$granted[$baseId] = $collection->get_label($this->app['locale']);
- }
- if(count($granted) > 0) {
+
$this->app['manipulator.webhook-event']->create(
WebhookEvent::USER_REGISTRATION_GRANTED,
WebhookEvent::USER_REGISTRATION_TYPE,
@@ -340,8 +338,11 @@ class RegistrationService
'user_id' => $user->getId(),
'granted' => $granted,
'rejected' => []
- ]
+ ],
+ [$baseId]
);
+
+ unset($granted);
}
diff --git a/lib/Alchemy/Phrasea/Border/Manager.php b/lib/Alchemy/Phrasea/Border/Manager.php
index d8abc9ac99..d017d206e6 100644
--- a/lib/Alchemy/Phrasea/Border/Manager.php
+++ b/lib/Alchemy/Phrasea/Border/Manager.php
@@ -14,6 +14,8 @@ namespace Alchemy\Phrasea\Border;
use Alchemy\Phrasea\Application;
use Alchemy\Phrasea\Border\Checker\CheckerInterface;
use Alchemy\Phrasea\Border\Attribute\AttributeInterface;
+use Alchemy\Phrasea\Core\Event\Record\RecordEvents;
+use Alchemy\Phrasea\Core\Event\Record\SubdefinitionCreateEvent;
use Alchemy\Phrasea\Exception\RuntimeException;
use Alchemy\Phrasea\Metadata\Tag\TfArchivedate;
use Alchemy\Phrasea\Metadata\Tag\TfQuarantine;
@@ -333,7 +335,7 @@ class Manager
$this->app['phraseanet.metadata-setter']->replaceMetadata($newMetadata, $element);
if(!$nosubdef) {
- $element->rebuild_subdefs();
+ $this->app['dispatcher']->dispatch(RecordEvents::SUBDEFINITION_CREATE, new SubdefinitionCreateEvent($element, true));
}
return $element;
diff --git a/lib/Alchemy/Phrasea/Command/ApplyRightsCommand.php b/lib/Alchemy/Phrasea/Command/ApplyRightsCommand.php
new file mode 100644
index 0000000000..bcf0b3d0e7
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Command/ApplyRightsCommand.php
@@ -0,0 +1,89 @@
+setDescription('Apply right on databox, inject appbox:basusr to dboxes:collusr')
+ ->addOption('user_id', null, InputOption::VALUE_REQUIRED, 'the user ID to apply rights')
+ ;
+
+ return $this;
+ }
+
+ protected function doExecute(InputInterface $input, OutputInterface $output)
+ {
+ $userId = $input->getOption('user_id');
+ $userRepository = $this->container['repo.users'];
+
+ if ($userId) {
+ if (($user = $userRepository->find($userId)) === null) {
+ $output->writeln('user not found!');
+
+ return 0;
+ }
+
+ $this->injectRightsSbas($user);
+ } else {
+ foreach ($userRepository->findAll() as $user) {
+ $this->injectRightsSbas($user);
+ }
+ }
+
+ $output->writeln('Apply right on databox finished!');
+
+ return 0;
+ }
+
+ private function injectRightsSbas(User $user)
+ {
+ $userAcl = $this->container->getAclForUser($user);
+
+ foreach ($userAcl->get_granted_sbas() as $databox) {
+
+ $userAcl->delete_injected_rights_sbas($databox);
+
+ $sql = "INSERT INTO collusr
+ (site, usr_id, coll_id, mask_and, mask_xor, ord)
+ VALUES (:site_id, :usr_id, :coll_id, :mask_and, :mask_xor, :ord)";
+ $stmt = $databox->get_connection()->prepare($sql);
+ $iord = 0;
+
+ // fix collusr if user has right on collection
+ foreach ($userAcl->get_granted_base([], [$databox->get_sbas_id()]) as $collection) {
+ try {
+ $stmt->execute([
+ ':site_id' => $this->container['conf']->get(['main', 'key']),
+ ':usr_id' => $user->getId(),
+ ':coll_id' => $collection->get_coll_id(),
+ ':mask_and' => $userAcl->get_mask_and($collection->get_base_id()),
+ ':mask_xor' => $userAcl->get_mask_xor($collection->get_base_id()),
+ ':ord' => $iord++
+ ]);
+ } catch (DBALException $e) {
+
+ }
+ }
+
+ $stmt->closeCursor();
+ }
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Controller/Admin/SubdefsController.php b/lib/Alchemy/Phrasea/Controller/Admin/SubdefsController.php
index 106a60b1d8..97896ac613 100644
--- a/lib/Alchemy/Phrasea/Controller/Admin/SubdefsController.php
+++ b/lib/Alchemy/Phrasea/Controller/Admin/SubdefsController.php
@@ -383,7 +383,7 @@ class SubdefsController extends Controller
Video::OPTION_GOPSIZE => "25",
Video::OPTION_SIZE => "480",
Video::OPTION_FRAMERATE => "25",
- Video::OPTION_VCODEC => "libtheora",
+ Video::OPTION_VCODEC => "libx264",
Video::OPTION_ACODEC => "libmp3lame",
Subdef::OPTION_DEVICE => ["all"]
],
@@ -449,7 +449,7 @@ class SubdefsController extends Controller
Video::OPTION_GOPSIZE => "25",
Video::OPTION_SIZE => "480",
Video::OPTION_FRAMERATE => "25",
- Video::OPTION_VCODEC => "libtheora",
+ Video::OPTION_VCODEC => "libx264",
Video::OPTION_ACODEC => "libfdk_aac",
Subdef::OPTION_DEVICE => ["all"]
],
diff --git a/lib/Alchemy/Phrasea/Controller/Admin/UserController.php b/lib/Alchemy/Phrasea/Controller/Admin/UserController.php
index 35ff646b8c..a1487603d8 100644
--- a/lib/Alchemy/Phrasea/Controller/Admin/UserController.php
+++ b/lib/Alchemy/Phrasea/Controller/Admin/UserController.php
@@ -515,9 +515,9 @@ class UserController extends Controller
$denyColl[] = $label;
$hookData['rejected'][$bas] = $label;
}
- }
- $this->app['manipulator.webhook-event']->create($hookName, $hookType, $hookData);
+ $this->app['manipulator.webhook-event']->create($hookName, $hookType, $hookData, [$bas]);
+ }
if ($user->hasMailNotificationsActivated() && (0 !== count($acceptColl) || 0 !== count($denyColl))) {
$message = '';
diff --git a/lib/Alchemy/Phrasea/Controller/Prod/ExportController.php b/lib/Alchemy/Phrasea/Controller/Prod/ExportController.php
index df6f2a9f19..f4692c99ac 100644
--- a/lib/Alchemy/Phrasea/Controller/Prod/ExportController.php
+++ b/lib/Alchemy/Phrasea/Controller/Prod/ExportController.php
@@ -15,6 +15,7 @@ use Alchemy\Phrasea\Application\Helper\FilesystemAware;
use Alchemy\Phrasea\Application\Helper\NotifierAware;
use Alchemy\Phrasea\Controller\Controller;
use Alchemy\Phrasea\Core\Event\ExportFailureEvent;
+use Alchemy\Phrasea\Core\Event\ExportMailEvent;
use Alchemy\Phrasea\Core\PhraseaEvents;
use Alchemy\Phrasea\Exception\InvalidArgumentException;
use Alchemy\Phrasea\Model\Manipulator\TokenManipulator;
@@ -193,42 +194,26 @@ class ExportController extends Controller
$token = $this->getTokenManipulator()->createEmailExportToken(serialize($list));
if (count($destMails) > 0) {
- //zip documents
- \set_export::build_zip(
- $this->app,
- $token,
- $list,
- $this->app['tmp.download.path'].'/'. $token->getValue() . '.zip'
- );
+ $emitterId = $this->getAuthenticatedUser()->getId();
- $remaingEmails = $destMails;
+ $tokenValue = $token->getValue();
$url = $this->app->url('prepare_download', ['token' => $token->getValue(), 'anonymous' => false, 'type' => \Session_Logger::EVENT_EXPORTMAIL]);
- $user = $this->getAuthenticatedUser();
- $emitter = new Emitter($user->getDisplayName(), $user->getEmail());
+ $params = [
+ 'url' => $url,
+ 'textmail' => $request->request->get('textmail'),
+ 'reading_confirm' => !!$request->request->get('reading_confirm', false),
+ 'ssttid' => $ssttid = $request->request->get('ssttid', ''),
+ 'lst' => $lst = $request->request->get('lst', ''),
+ ];
- foreach ($destMails as $key => $mail) {
- try {
- $receiver = new Receiver(null, trim($mail));
- } catch (InvalidArgumentException $e) {
- continue;
- }
-
- $mail = MailRecordsExport::create($this->app, $receiver, $emitter, $request->request->get('textmail'));
- $mail->setButtonUrl($url);
- $mail->setExpiration($token->getExpiration());
-
- $this->deliver($mail, !!$request->request->get('reading_confirm', false));
- unset($remaingEmails[$key]);
- }
-
- //some mails failed
- if (count($remaingEmails) > 0) {
- foreach ($remaingEmails as $mail) {
- $this->dispatch(PhraseaEvents::EXPORT_MAIL_FAILURE, new ExportFailureEvent($this->getAuthenticatedUser(), $ssttid, $lst, \eventsmanager_notify_downloadmailfail::MAIL_FAIL, $mail));
- }
- }
+ $this->dispatch(PhraseaEvents::EXPORT_MAIL_CREATE, new ExportMailEvent(
+ $emitterId,
+ $tokenValue,
+ $destMails,
+ $params
+ ));
}
return $this->app->json([
diff --git a/lib/Alchemy/Phrasea/Controller/Prod/PushController.php b/lib/Alchemy/Phrasea/Controller/Prod/PushController.php
index 6e650aefaa..c9726d5faf 100644
--- a/lib/Alchemy/Phrasea/Controller/Prod/PushController.php
+++ b/lib/Alchemy/Phrasea/Controller/Prod/PushController.php
@@ -98,7 +98,7 @@ class PushController extends Controller
$Basket->setUser($user_receiver);
$Basket->setPusher($this->getAuthenticatedUser());
$Basket->markUnread();
-
+
$manager->persist($Basket);
foreach ($pusher->get_elements() as $element) {
@@ -600,6 +600,38 @@ class PushController extends Controller
);
}
+ public function updateExpirationAction(Request $request)
+ {
+ $ret = [
+ 'success' => false,
+ 'message' => $this->app->trans('Unable to save the expiration date')
+ ];
+ if (is_null($request->request->get('date'))) {
+ $ret['message'] = $this->app->trans('The provided date is null!');
+ return $this->app->json($ret);
+ }
+ $repository = $this->app['repo.baskets'];
+ $manager = $this->getEntityManager();
+ $manager->beginTransaction();
+ try {
+ $basket = $repository->findUserBasket($request->request->get('basket_id'), $this->app->getAuthenticatedUser(), true);
+ $date = new \DateTime($request->request->get('date') . " 23:59:59");
+ $validation = $basket->getValidation();
+ if (is_null($validation)) {
+ return $this->app->json($ret);
+ }
+ $validation->setExpires($date);
+ $manager->persist($validation);
+ $manager->flush();
+ $manager->commit();
+ $ret['message'] = $this->app->trans('Expiration date successfully updated!');
+ } catch (\Exception $e) {
+ $ret['message'] = $e->getMessage();
+ $manager->rollback();
+ }
+ return $this->app->json($ret);
+ }
+
private function formatUser(User $user)
{
$subtitle = array_filter([$user->getJob(), $user->getCompany()]);
@@ -734,4 +766,5 @@ class PushController extends Controller
{
return $this->app['random.medium'];
}
+
}
diff --git a/lib/Alchemy/Phrasea/Controller/Prod/QueryController.php b/lib/Alchemy/Phrasea/Controller/Prod/QueryController.php
index 8afd22d2b3..d4830859c2 100644
--- a/lib/Alchemy/Phrasea/Controller/Prod/QueryController.php
+++ b/lib/Alchemy/Phrasea/Controller/Prod/QueryController.php
@@ -269,7 +269,7 @@ class QueryController extends Controller
$infoResult = '
'
. $this->app->trans('%number% documents
selectionnes', ['%number%' => '
'])
- . '


'
+ . '


'
. '
diff --git a/lib/Alchemy/Phrasea/Controller/Prod/RecordController.php b/lib/Alchemy/Phrasea/Controller/Prod/RecordController.php
index 0a948d6d1d..8e9a609811 100644
--- a/lib/Alchemy/Phrasea/Controller/Prod/RecordController.php
+++ b/lib/Alchemy/Phrasea/Controller/Prod/RecordController.php
@@ -14,6 +14,8 @@ use Alchemy\Phrasea\Application\Helper\EntityManagerAware;
use Alchemy\Phrasea\Application\Helper\SearchEngineAware;
use Alchemy\Phrasea\Controller\Controller;
use Alchemy\Phrasea\Controller\RecordsRequest;
+use Alchemy\Phrasea\Core\Event\Record\DeleteEvent;
+use Alchemy\Phrasea\Core\Event\Record\RecordEvents;
use Alchemy\Phrasea\Core\Event\RecordEdit;
use Alchemy\Phrasea\Core\PhraseaEvents;
use Alchemy\Phrasea\Model\Entities\BasketElement;
@@ -234,7 +236,7 @@ class RecordController extends Controller
if($trashCollectionsBySbasId[$sbasId] !== null) {
if($record->getCollection()->get_coll_id() == $trashCollectionsBySbasId[$sbasId]->get_coll_id()) {
// record is already in trash so delete it
- $record->delete();
+ $this->getEventDispatcher()->dispatch(RecordEvents::DELETE, new DeleteEvent($record));
} else {
// move to trash collection
$record->move_to_collection($trashCollectionsBySbasId[$sbasId], $this->getApplicationBox());
@@ -247,7 +249,7 @@ class RecordController extends Controller
}
} else {
// no trash collection, delete
- $record->delete();
+ $this->getEventDispatcher()->dispatch(RecordEvents::DELETE, new DeleteEvent($record));
}
} catch (\Exception $e) {
}
diff --git a/lib/Alchemy/Phrasea/Controller/Prod/StoryController.php b/lib/Alchemy/Phrasea/Controller/Prod/StoryController.php
index 9a65ed61fa..a90f0a0ed1 100644
--- a/lib/Alchemy/Phrasea/Controller/Prod/StoryController.php
+++ b/lib/Alchemy/Phrasea/Controller/Prod/StoryController.php
@@ -14,6 +14,8 @@ use Alchemy\Phrasea\Application\Helper\EntityManagerAware;
use Alchemy\Phrasea\Controller\Controller;
use Alchemy\Phrasea\Controller\RecordsRequest;
use Alchemy\Phrasea\Controller\Exception as ControllerException;
+use Alchemy\Phrasea\Core\Event\Record\RecordEvents;
+use Alchemy\Phrasea\Core\Event\Record\SubdefinitionCreateEvent;
use Alchemy\Phrasea\Core\Event\RecordEdit;
use Alchemy\Phrasea\Core\PhraseaEvents;
use Alchemy\Phrasea\Model\Entities\StoryWZ;
@@ -68,7 +70,9 @@ class StoryController extends Controller
break;
}
- $story->set_metadatas($metadatas)->rebuild_subdefs();
+ $recordAdapter = $story->set_metadatas($metadatas);
+ // tell phraseanet to rebuild subdef
+ $this->dispatch(RecordEvents::SUBDEFINITION_CREATE, new SubdefinitionCreateEvent($recordAdapter));
$storyWZ = new StoryWZ();
$storyWZ->setUser($this->getAuthenticatedUser());
diff --git a/lib/Alchemy/Phrasea/Controller/Prod/ToolsController.php b/lib/Alchemy/Phrasea/Controller/Prod/ToolsController.php
index 872f385455..65f72a8a36 100644
--- a/lib/Alchemy/Phrasea/Controller/Prod/ToolsController.php
+++ b/lib/Alchemy/Phrasea/Controller/Prod/ToolsController.php
@@ -16,6 +16,7 @@ use Alchemy\Phrasea\Application\Helper\SubDefinitionSubstituerAware;
use Alchemy\Phrasea\Controller\Controller;
use Alchemy\Phrasea\Controller\RecordsRequest;
use Alchemy\Phrasea\Core\Event\Record\RecordEvents;
+use Alchemy\Phrasea\Core\Event\Record\SubdefinitionCreateEvent;
use Alchemy\Phrasea\Exception\RuntimeException;
use Alchemy\Phrasea\Metadata\PhraseanetMetadataReader;
use Alchemy\Phrasea\Metadata\PhraseanetMetadataSetter;
@@ -156,7 +157,7 @@ class ToolsController extends Controller
}
if (!$substituted || $force) {
- $record->rebuild_subdefs();
+ $this->dispatch(RecordEvents::SUBDEFINITION_CREATE, new SubdefinitionCreateEvent($record));
}
}
diff --git a/lib/Alchemy/Phrasea/Controller/Root/AccountController.php b/lib/Alchemy/Phrasea/Controller/Root/AccountController.php
index 6bf2d11f17..b56d428cec 100644
--- a/lib/Alchemy/Phrasea/Controller/Root/AccountController.php
+++ b/lib/Alchemy/Phrasea/Controller/Root/AccountController.php
@@ -518,7 +518,9 @@ class AccountController extends Controller
$this->getApiApplicationManipulator()->deleteApiApplications($applications);
- // revoke access and delete phraseanet user account
+ // get list of old granted base_id then revoke access and delete phraseanet user account
+
+ $oldGrantedBaseIds = array_keys($this->app->getAclForUser($user)->get_granted_base());
$list = array_keys($this->app['repo.collections-registry']->getBaseIdMap());
@@ -542,8 +544,9 @@ class AccountController extends Controller
$mail = null;
}
- $this->app['manipulator.user']->delete($user);
+ $mail = MailSuccessAccountDelete::create($this->app, $receiver);
+ $this->app['manipulator.user']->delete($user, [$user->getId() => $oldGrantedBaseIds]);
if($mail) {
$this->deliver($mail);
}
diff --git a/lib/Alchemy/Phrasea/ControllerProvider/Prod/Push.php b/lib/Alchemy/Phrasea/ControllerProvider/Prod/Push.php
index 48352e1e39..40b93220c0 100644
--- a/lib/Alchemy/Phrasea/ControllerProvider/Prod/Push.php
+++ b/lib/Alchemy/Phrasea/ControllerProvider/Prod/Push.php
@@ -59,6 +59,9 @@ class Push implements ControllerProviderInterface, ServiceProviderInterface
$controllers->post('/validate/', 'controller.prod.push:validateAction')
->bind('prod_push_validate');
+ $controllers->post('/update-expiration/', 'controller.prod.push:updateExpirationAction')
+ ->bind('prod_push_do_update_expiration');
+
$controllers->get('/user/{usr_id}/', 'controller.prod.push:getUserAction')
->assert('usr_id', '\d+');
diff --git a/lib/Alchemy/Phrasea/Core/Configuration/RegistryFormManipulator.php b/lib/Alchemy/Phrasea/Core/Configuration/RegistryFormManipulator.php
index fbce72ccc7..9ebd69c829 100644
--- a/lib/Alchemy/Phrasea/Core/Configuration/RegistryFormManipulator.php
+++ b/lib/Alchemy/Phrasea/Core/Configuration/RegistryFormManipulator.php
@@ -102,6 +102,8 @@ class RegistryFormManipulator
'keywords' => null,
'description' => null,
'analytics' => null,
+ 'matomo-analytics-url' => null,
+ 'matomo-analytics-id' => null,
'allow-indexation' => true,
'home-presentation-mode' => 'GALLERIA',
'default-subdef-url-ttl' => 7200,
diff --git a/lib/Alchemy/Phrasea/Core/Event/ExportMailEvent.php b/lib/Alchemy/Phrasea/Core/Event/ExportMailEvent.php
new file mode 100644
index 0000000000..3acafb4c70
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Core/Event/ExportMailEvent.php
@@ -0,0 +1,53 @@
+emitterUserId = $emitterUserId;
+ $this->tokenValue = $tokenValue;
+ $this->destinationMails = $destMails;
+ $this->params = $params;
+ }
+
+ public function getTokenValue()
+ {
+ return $this->tokenValue;
+ }
+
+ public function getDestinationMails()
+ {
+ return $this->destinationMails;
+ }
+
+ public function getEmitterUserId()
+ {
+ return $this->emitterUserId;
+ }
+
+ public function getParams()
+ {
+ return $this->params;
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Core/Event/Record/DeleteEvent.php b/lib/Alchemy/Phrasea/Core/Event/Record/DeleteEvent.php
new file mode 100644
index 0000000000..8d8bfdb379
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Core/Event/Record/DeleteEvent.php
@@ -0,0 +1,7 @@
+isNewRecord = $isNewRecord;
+ }
+
+ /**
+ * @return bool
+ */
+ public function isNewRecord()
+ {
+ return $this->isNewRecord;
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Core/Event/Subscriber/ExportSubscriber.php b/lib/Alchemy/Phrasea/Core/Event/Subscriber/ExportSubscriber.php
index fca66dab4a..d3c05a64b6 100644
--- a/lib/Alchemy/Phrasea/Core/Event/Subscriber/ExportSubscriber.php
+++ b/lib/Alchemy/Phrasea/Core/Event/Subscriber/ExportSubscriber.php
@@ -12,7 +12,15 @@
namespace Alchemy\Phrasea\Core\Event\Subscriber;
use Alchemy\Phrasea\Core\Event\ExportFailureEvent;
+use Alchemy\Phrasea\Core\Event\ExportMailEvent;
use Alchemy\Phrasea\Core\PhraseaEvents;
+use Alchemy\Phrasea\Exception\InvalidArgumentException;
+use Alchemy\Phrasea\Model\Entities\Token;
+use Alchemy\Phrasea\Model\Repositories\TokenRepository;
+use Alchemy\Phrasea\Model\Repositories\UserRepository;
+use Alchemy\Phrasea\Notification\Emitter;
+use Alchemy\Phrasea\Notification\Mail\MailRecordsExport;
+use Alchemy\Phrasea\Notification\Receiver;
class ExportSubscriber extends AbstractNotificationSubscriber
{
@@ -39,10 +47,65 @@ class ExportSubscriber extends AbstractNotificationSubscriber
$this->app['event-manager']->notify($params['usr_id'], 'eventsmanager_notify_downloadmailfail', $datas, $mailed);
}
+ public function onCreateExportMail(ExportMailEvent $event)
+ {
+ $destMails = $event->getDestinationMails();
+
+ $params = $event->getParams();
+
+ /** @var UserRepository $userRepository */
+ $userRepository = $this->app['repo.users'];
+
+ $user = $userRepository->find($event->getEmitterUserId());
+
+ /** @var TokenRepository $tokenRepository */
+ $tokenRepository = $this->app['repo.tokens'];
+
+ /** @var Token $token */
+ $token = $tokenRepository->findValidToken($event->getTokenValue());
+
+ $list = unserialize($token->getData());
+
+ //zip documents
+ \set_export::build_zip(
+ $this->app,
+ $token,
+ $list,
+ $this->app['tmp.download.path'].'/'. $token->getValue() . '.zip'
+ );
+
+ $remaingEmails = $destMails;
+
+ $emitter = new Emitter($user->getDisplayName(), $user->getEmail());
+
+ foreach ($destMails as $key => $mail) {
+ try {
+ $receiver = new Receiver(null, trim($mail));
+ } catch (InvalidArgumentException $e) {
+ continue;
+ }
+
+ $mail = MailRecordsExport::create($this->app, $receiver, $emitter, $params['textmail']);
+ $mail->setButtonUrl($params['url']);
+ $mail->setExpiration($token->getExpiration());
+
+ $this->deliver($mail, $params['reading_confirm']);
+ unset($remaingEmails[$key]);
+ }
+
+ //some mails failed
+ if (count($remaingEmails) > 0) {
+ foreach ($remaingEmails as $mail) {
+ $this->app['dispatcher']->dispatch(PhraseaEvents::EXPORT_MAIL_FAILURE, new ExportFailureEvent($user, $params['ssttid'], $params['lst'], \eventsmanager_notify_downloadmailfail::MAIL_FAIL, $mail));
+ }
+ }
+ }
+
public static function getSubscribedEvents()
{
return [
- PhraseaEvents::EXPORT_MAIL_FAILURE => 'onMailExportFailure'
+ PhraseaEvents::EXPORT_MAIL_FAILURE => 'onMailExportFailure',
+ PhraseaEvents::EXPORT_MAIL_CREATE => 'onCreateExportMail',
];
}
}
diff --git a/lib/Alchemy/Phrasea/Core/Event/Subscriber/FeedEntrySubscriber.php b/lib/Alchemy/Phrasea/Core/Event/Subscriber/FeedEntrySubscriber.php
index 8bc7cb2b52..e229cc3f44 100644
--- a/lib/Alchemy/Phrasea/Core/Event/Subscriber/FeedEntrySubscriber.php
+++ b/lib/Alchemy/Phrasea/Core/Event/Subscriber/FeedEntrySubscriber.php
@@ -33,7 +33,8 @@ class FeedEntrySubscriber extends AbstractNotificationSubscriber
$this->app['manipulator.webhook-event']->create(
WebhookEvent::NEW_FEED_ENTRY,
WebhookEvent::FEED_ENTRY_TYPE,
- array_merge(array('feed_id' => $entry->getFeed()->getId()), $params)
+ array_merge(array('feed_id' => $entry->getFeed()->getId()), $params),
+ $entry->getFeed()->getBaseId() ? [$entry->getFeed()->getBaseId()] : []
);
$datas = json_encode($params);
diff --git a/lib/Alchemy/Phrasea/Core/Event/Subscriber/OrderSubscriber.php b/lib/Alchemy/Phrasea/Core/Event/Subscriber/OrderSubscriber.php
index 142537f418..01f7e7b235 100644
--- a/lib/Alchemy/Phrasea/Core/Event/Subscriber/OrderSubscriber.php
+++ b/lib/Alchemy/Phrasea/Core/Event/Subscriber/OrderSubscriber.php
@@ -41,13 +41,13 @@ class OrderSubscriber extends AbstractNotificationSubscriber
public function onCreate(OrderEvent $event)
{
- $base_ids = array_unique(array_map(function (OrderElement $element) {
+ $baseIds = array_unique(array_map(function (OrderElement $element) {
return $element->getBaseId();
}, iterator_to_array($event->getOrder()->getElements())));
$query = $this->app['phraseanet.user-query'];
/** @var User[] $users */
- $users = $query->on_base_ids($base_ids)
+ $users = $query->on_base_ids($baseIds)
->who_have_right([\ACL::ORDER_MASTER])
->execute()->get_results();
@@ -60,10 +60,12 @@ class OrderSubscriber extends AbstractNotificationSubscriber
'order_id' => $event->getOrder()->getId(),
]);
- $notifier = $this->notifierRegistry->getNotifier($event->getOrder()->getNotificationMethod());
+ // notify by webhook
+ $notifier = $this->notifierRegistry->getNotifier(Order::NOTIFY_WEBHOOK);
- $notifier->notifyCreation($event->getOrder(), $event->getOrder()->getUser());
+ $notifier->notifyCreation($event->getOrder(), $event->getOrder()->getUser(), $baseIds);
+ // notify by mail
$notifier = $this->notifierRegistry->getNotifier(Order::NOTIFY_MAIL);
foreach ($users as $user) {
@@ -85,7 +87,13 @@ class OrderSubscriber extends AbstractNotificationSubscriber
public function onDeliver(OrderDeliveryEvent $event)
{
+ // notify by webhook
+ $notifier = $this->notifierRegistry->getNotifier(Order::NOTIFY_WEBHOOK);
+ $notifier->notifyDelivery($event->getDelivery(), $event->getDelivery()->getPartialOrder()->getBaseIds());
+
$notified = false;
+
+ // actually NotificationMethod is always by mail
$notifier = $this->notifierRegistry->getNotifier($event->getOrder()->getNotificationMethod());
$notificationData = json_encode([
'from' => $event->getDelivery()->getAdmin()->getId(),
@@ -109,7 +117,13 @@ class OrderSubscriber extends AbstractNotificationSubscriber
public function onDeny(OrderDeliveryEvent $event)
{
+ // notify by webhook
+ $notifier = $this->notifierRegistry->getNotifier(Order::NOTIFY_WEBHOOK);
+ $notifier->notifyDenial($event->getDelivery(), $event->getDelivery()->getPartialOrder()->getBaseIds());
+
$notified = false;
+
+ // actually NotificationMethod is always by mail
$notifier = $this->notifierRegistry->getNotifier($event->getOrder()->getNotificationMethod());
$notificationData = json_encode([
'from' => $event->getDelivery()->getAdmin()->getId(),
diff --git a/lib/Alchemy/Phrasea/Core/Event/Subscriber/RecordEditSubscriber.php b/lib/Alchemy/Phrasea/Core/Event/Subscriber/RecordEditSubscriber.php
index d5c0c69f54..8307749eb5 100644
--- a/lib/Alchemy/Phrasea/Core/Event/Subscriber/RecordEditSubscriber.php
+++ b/lib/Alchemy/Phrasea/Core/Event/Subscriber/RecordEditSubscriber.php
@@ -11,8 +11,10 @@
namespace Alchemy\Phrasea\Core\Event\Subscriber;
use Alchemy\Phrasea\Core\Event\Record\CollectionChangedEvent;
+use Alchemy\Phrasea\Core\Event\Record\DeleteEvent;
use Alchemy\Phrasea\Core\Event\Record\RecordEvent;
use Alchemy\Phrasea\Core\Event\Record\RecordEvents;
+use Alchemy\Phrasea\Core\Event\Record\SubdefinitionCreateEvent;
use Alchemy\Phrasea\Core\Event\RecordEdit;
use Alchemy\Phrasea\Core\PhraseaEvents;
use Alchemy\Phrasea\Metadata\Tag\TfEditdate;
@@ -26,10 +28,12 @@ class RecordEditSubscriber implements EventSubscriberInterface
public static function getSubscribedEvents()
{
return array(
- PhraseaEvents::RECORD_EDIT => 'onEdit',
- PhraseaEvents::RECORD_UPLOAD => 'onEdit',
- RecordEvents::ROTATE => 'onRecordChange',
- RecordEvents::COLLECTION_CHANGED => 'onCollectionChanged',
+ PhraseaEvents::RECORD_EDIT => 'onEdit',
+ PhraseaEvents::RECORD_UPLOAD => 'onEdit',
+ RecordEvents::ROTATE => 'onRecordChange',
+ RecordEvents::COLLECTION_CHANGED => 'onCollectionChanged',
+ RecordEvents::SUBDEFINITION_CREATE => 'onSubdefinitionCreate',
+ RecordEvents::DELETE => 'onDelete',
);
}
@@ -49,6 +53,18 @@ class RecordEditSubscriber implements EventSubscriberInterface
$recordAdapter->clearStampCache();
}
+ public function onSubdefinitionCreate(SubdefinitionCreateEvent $event)
+ {
+ $recordAdapter = $this->convertToRecordAdapter($event->getRecord());
+ $recordAdapter->rebuild_subdefs();
+ }
+
+ public function onDelete(DeleteEvent $event)
+ {
+ $recordAdapter = $this->convertToRecordAdapter($event->getRecord());
+ $recordAdapter->delete();
+ }
+
public function onEdit(RecordEdit $event)
{
static $into = false;
diff --git a/lib/Alchemy/Phrasea/Core/Event/Subscriber/WebhookSubdefEventSubscriber.php b/lib/Alchemy/Phrasea/Core/Event/Subscriber/WebhookSubdefEventSubscriber.php
new file mode 100644
index 0000000000..ca5d88b2ab
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Core/Event/Subscriber/WebhookSubdefEventSubscriber.php
@@ -0,0 +1,75 @@
+app = $app;
+ }
+
+ public function onSubdefCreated(SubDefinitionCreatedEvent $event)
+ {
+ $eventData = [
+ 'databox_id' => $event->getRecord()->getDataboxId(),
+ 'record_id' => $event->getRecord()->getRecordId(),
+ 'subdef' => $event->getSubDefinitionName()
+ ];
+
+ $this->app['manipulator.webhook-event']->create(
+ WebhookEvent::RECORD_SUBDEF_CREATED,
+ WebhookEvent::RECORD_SUBDEF_TYPE,
+ $eventData
+ );
+ }
+
+ public function onSubdefCreationFailed(SubDefinitionCreationFailedEvent $event)
+ {
+ $eventData = [
+ 'databox_id' => $event->getRecord()->getDataboxId(),
+ 'record_id' => $event->getRecord()->getRecordId(),
+ 'subdef' => $event->getSubDefinitionName()
+ ];
+
+ $this->app['manipulator.webhook-event']->create(
+ WebhookEvent::RECORD_SUBDEF_FAILED,
+ WebhookEvent::RECORD_SUBDEF_TYPE,
+ $eventData
+ );
+ }
+
+ public function onSubdefsCreated(SubDefinitionsCreatedEvent $event)
+ {
+ $eventData = [
+ 'databox_id' => $event->getRecord()->getDataboxId(),
+ 'record_id' => $event->getRecord()->getRecordId(),
+ 'subdef_count' => count($event->getMedia())
+ ];
+
+ $this->app['manipulator.webhook-event']->create(
+ WebhookEvent::RECORD_SUBDEFS_CREATED,
+ WebhookEvent::RECORD_SUBDEF_TYPE,
+ $eventData
+ );
+ }
+
+ public static function getSubscribedEvents()
+ {
+ return [
+ RecordEvents::SUB_DEFINITION_CREATED => 'onSubdefCreated',
+ RecordEvents::SUB_DEFINITIONS_CREATED => 'onSubdefsCreated',
+ RecordEvents::SUB_DEFINITION_CREATION_FAILED => 'onSubdefCreationFailed'
+ ];
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Core/Event/Subscriber/WebhookUserEventSubscriber.php b/lib/Alchemy/Phrasea/Core/Event/Subscriber/WebhookUserEventSubscriber.php
index a2f935bf89..74c1c6c23a 100644
--- a/lib/Alchemy/Phrasea/Core/Event/Subscriber/WebhookUserEventSubscriber.php
+++ b/lib/Alchemy/Phrasea/Core/Event/Subscriber/WebhookUserEventSubscriber.php
@@ -45,7 +45,7 @@ class WebhookUserEventSubscriber implements EventSubscriberInterface
'user_id' => $event->getUserId(),
'email' => $event->getEmailAddress(),
'login' => $event->getLogin()
- ]);
+ ], $event->getGrantedBaseIds());
}
public static function getSubscribedEvents()
diff --git a/lib/Alchemy/Phrasea/Core/Event/User/DeletedEvent.php b/lib/Alchemy/Phrasea/Core/Event/User/DeletedEvent.php
index b67ba45b24..1528917245 100644
--- a/lib/Alchemy/Phrasea/Core/Event/User/DeletedEvent.php
+++ b/lib/Alchemy/Phrasea/Core/Event/User/DeletedEvent.php
@@ -36,4 +36,12 @@ class DeletedEvent extends UserEvent
{
return $this->args['email'];
}
+
+ /**
+ * @return array
+ */
+ public function getGrantedBaseIds()
+ {
+ return $this->args['grantedBaseIds'];
+ }
}
diff --git a/lib/Alchemy/Phrasea/Core/PhraseaEvents.php b/lib/Alchemy/Phrasea/Core/PhraseaEvents.php
index 97c9c10ba5..90adac69dd 100644
--- a/lib/Alchemy/Phrasea/Core/PhraseaEvents.php
+++ b/lib/Alchemy/Phrasea/Core/PhraseaEvents.php
@@ -48,7 +48,8 @@ final class PhraseaEvents
const BRIDGE_UPLOAD_FAILURE = 'bridge.upload-failure';
const EXPORT_MAIL_FAILURE = 'export.mail-failure';
- const EXPORT_CREATE = 'export.create';
+ const EXPORT_CREATE = 'export.create';
+ const EXPORT_MAIL_CREATE = 'export.mail-create';
const RECORD_EDIT = 'record.edit';
const RECORD_UPLOAD = 'record.upload';
diff --git a/lib/Alchemy/Phrasea/Core/Provider/SearchEngineServiceProvider.php b/lib/Alchemy/Phrasea/Core/Provider/SearchEngineServiceProvider.php
index e342bf8380..8968ef5f0a 100644
--- a/lib/Alchemy/Phrasea/Core/Provider/SearchEngineServiceProvider.php
+++ b/lib/Alchemy/Phrasea/Core/Provider/SearchEngineServiceProvider.php
@@ -43,6 +43,7 @@ use Silex\ServiceProviderInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpKernel\KernelEvents;
+
class SearchEngineServiceProvider implements ServiceProviderInterface
{
public function register(Application $app)
@@ -145,6 +146,7 @@ class SearchEngineServiceProvider implements ServiceProviderInterface
$app['elasticsearch.indexer.databox_fetcher_factory'] = $app->share(function ($app) {
return new DataboxFetcherFactory(
+ $app['conf'],
$app['elasticsearch.record_helper'],
$app['elasticsearch.options'],
$app,
diff --git a/lib/Alchemy/Phrasea/Core/Provider/WebhookServiceProvider.php b/lib/Alchemy/Phrasea/Core/Provider/WebhookServiceProvider.php
index 4b280c92f0..43a5aeafa4 100644
--- a/lib/Alchemy/Phrasea/Core/Provider/WebhookServiceProvider.php
+++ b/lib/Alchemy/Phrasea/Core/Provider/WebhookServiceProvider.php
@@ -2,6 +2,7 @@
namespace Alchemy\Phrasea\Core\Provider;
+use Alchemy\Phrasea\Core\Event\Subscriber\WebhookSubdefEventSubscriber;
use Alchemy\Phrasea\Webhook\EventProcessorFactory;
use Alchemy\Phrasea\Webhook\EventProcessorWorker;
use Alchemy\Phrasea\Webhook\WebhookInvoker;
@@ -10,6 +11,7 @@ use Alchemy\Worker\CallableWorkerFactory;
use Alchemy\Worker\TypeBasedWorkerResolver;
use Silex\Application;
use Silex\ServiceProviderInterface;
+use Symfony\Component\EventDispatcher\EventDispatcher;
class WebhookServiceProvider implements ServiceProviderInterface
{
@@ -58,6 +60,14 @@ class WebhookServiceProvider implements ServiceProviderInterface
return $resolver;
}
);
+
+ $app['dispatcher'] = $app->share(
+ $app->extend('dispatcher', function (EventDispatcher $dispatcher, Application $app) {
+ $dispatcher->addSubscriber(new WebhookSubdefEventSubscriber($app));
+
+ return $dispatcher;
+ })
+ );
}
private function createAlias(Application $app, $alias, $targetServiceKey)
@@ -69,6 +79,6 @@ class WebhookServiceProvider implements ServiceProviderInterface
public function boot(Application $app)
{
- // no-op
+
}
}
diff --git a/lib/Alchemy/Phrasea/Core/Provider/WorkerConfigurationServiceProvider.php b/lib/Alchemy/Phrasea/Core/Provider/WorkerConfigurationServiceProvider.php
index 1b2e15d4f0..9931d4649f 100644
--- a/lib/Alchemy/Phrasea/Core/Provider/WorkerConfigurationServiceProvider.php
+++ b/lib/Alchemy/Phrasea/Core/Provider/WorkerConfigurationServiceProvider.php
@@ -32,11 +32,12 @@ class WorkerConfigurationServiceProvider implements ServiceProviderInterface
$app['alchemy_queues.queues'] = $app->share(function (Application $app) {
$defaultConfiguration = [
'worker-queue' => [
- 'registry' => 'alchemy_worker.queue_registry',
- 'host' => 'localhost',
- 'port' => 5672,
- 'user' => 'guest',
- 'vhost' => '/'
+ 'registry' => 'alchemy_worker.queue_registry',
+ 'host' => 'localhost',
+ 'port' => 5672,
+ 'user' => 'guest',
+ 'password' => 'guest',
+ 'vhost' => '/'
]
];
@@ -46,19 +47,22 @@ class WorkerConfigurationServiceProvider implements ServiceProviderInterface
$queueConfigurations = $configuration->get(['workers', 'queue'], $defaultConfiguration);
- $queueConfiguration = reset($queueConfigurations);
- $queueKey = key($queueConfigurations);
+ $config = [];
- if (! isset($queueConfiguration['name'])) {
- if (! is_string($queueKey)) {
- throw new \RuntimeException('Invalid queue configuration: configuration has no key or name.');
+ foreach($queueConfigurations as $name => $queueConfiguration) {
+ $queueKey = $name;
+
+ if (! isset($queueConfiguration['name'])) {
+ if (! is_string($queueKey)) {
+ throw new \RuntimeException('Invalid queue configuration: configuration has no key or name.');
+ }
+
+ $queueConfiguration['name'] = $queueKey;
}
- $queueConfiguration['name'] = $queueKey;
+ $config[$queueConfiguration['name']] = $queueConfiguration ;
}
- $config = [ $queueConfiguration['name'] => $queueConfiguration ];
-
return $config;
}
catch (RuntimeException $exception) {
diff --git a/lib/Alchemy/Phrasea/Form/Configuration/GeneralFormType.php b/lib/Alchemy/Phrasea/Form/Configuration/GeneralFormType.php
index 5a1c6a645e..767c91a782 100644
--- a/lib/Alchemy/Phrasea/Form/Configuration/GeneralFormType.php
+++ b/lib/Alchemy/Phrasea/Form/Configuration/GeneralFormType.php
@@ -38,6 +38,12 @@ class GeneralFormType extends AbstractType
$builder->add('analytics', 'text', [
'label' => 'Google Analytics identifier',
]);
+ $builder->add('matomo-analytics-url', 'text', [
+ 'label' => 'Matomo Analytics url',
+ ]);
+ $builder->add('matomo-analytics-id', 'text', [
+ 'label' => 'Matomo Analytics identifier',
+ ]);
$builder->add('allow-indexation', 'checkbox', [
'label' => 'Allow the website to be indexed by search engines like Google',
]);
diff --git a/lib/Alchemy/Phrasea/Helper/User/Edit.php b/lib/Alchemy/Phrasea/Helper/User/Edit.php
index 2c7513fb3f..970b2ec8e1 100644
--- a/lib/Alchemy/Phrasea/Helper/User/Edit.php
+++ b/lib/Alchemy/Phrasea/Helper/User/Edit.php
@@ -73,10 +73,12 @@ class Edit extends \Alchemy\Phrasea\Helper\Helper
{
$list = array_keys($this->app->getAclForUser($this->app->getAuthenticatedUser())->get_granted_base([\ACL::CANADMIN]));
+ $oldGrantedBaseIds = array_keys($this->app->getAclForUser($user)->get_granted_base());
+
$this->app->getAclForUser($user)->revoke_access_from_bases($list);
if ($this->app->getAclForUser($user)->is_phantom()) {
- $this->app['manipulator.user']->delete($user);
+ $this->app['manipulator.user']->delete($user, [$user->getId() => $oldGrantedBaseIds]);
}
return $this;
@@ -583,8 +585,8 @@ class Edit extends \Alchemy\Phrasea\Helper\Helper
$user = $this->app['repo.users']->find($usr_id);
$this->app->getAclForUser($user)->revoke_access_from_bases($delete)
- ->give_access_to_base($create)
- ->give_access_to_sbas($create_sbas);
+ ->give_access_to_sbas($create_sbas) // give access to sbas before bas
+ ->give_access_to_base($create);
foreach ($update as $base_id => $rights) {
$this->app->getAclForUser($user)
diff --git a/lib/Alchemy/Phrasea/Media/SubdefGenerator.php b/lib/Alchemy/Phrasea/Media/SubdefGenerator.php
index 88867551a1..9612e3bc19 100644
--- a/lib/Alchemy/Phrasea/Media/SubdefGenerator.php
+++ b/lib/Alchemy/Phrasea/Media/SubdefGenerator.php
@@ -18,6 +18,7 @@ use Alchemy\Phrasea\Core\Event\Record\SubDefinitionCreationEvent;
use Alchemy\Phrasea\Core\Event\Record\SubDefinitionsCreationEvent;
use Alchemy\Phrasea\Core\Event\Record\SubDefinitionCreationFailedEvent;
use Alchemy\Phrasea\Core\Event\Record\RecordEvents;
+use Alchemy\Phrasea\Databox\Subdef\MediaSubdefRepository;
use Alchemy\Phrasea\Filesystem\FilesystemService;
use Alchemy\Phrasea\Media\Subdef\Specification\PdfSpecification;
use MediaAlchemyst\Alchemyst;
@@ -170,13 +171,53 @@ class SubdefGenerator
unset($this->tmpFilePath);
}
- $this->dispatch(
- RecordEvents::SUB_DEFINITIONS_CREATED,
- new SubDefinitionsCreatedEvent(
- $record,
- $mediaCreated
- )
- );
+ // if we created subdef one by one
+ if (count($wanted_subdefs) == 1) {
+ $mediaSubdefRepository = $this->getMediaSubdefRepository($record->getDataboxId());
+ $mediaSubdefs = $mediaSubdefRepository->findByRecordIdsAndNames([$record->getRecordId()]);
+ $medias = [];
+ foreach ($mediaSubdefs as $subdef) {
+ try {
+ $medias[$subdef->get_name()] = $this->mediavorus->guess($subdef->getRealPath());
+ } catch (MediaVorusFileNotFoundException $e) {
+
+ }
+ }
+
+ $this->dispatch(
+ RecordEvents::SUB_DEFINITIONS_CREATED,
+ new SubDefinitionsCreatedEvent(
+ $record,
+ $medias
+ )
+ );
+ } else {
+ $this->dispatch(
+ RecordEvents::SUB_DEFINITIONS_CREATED,
+ new SubDefinitionsCreatedEvent(
+ $record,
+ $mediaCreated
+ )
+ );
+ }
+ }
+
+ /**
+ * set a logger to use
+ * @param LoggerInterface $logger
+ */
+ public function setLogger(LoggerInterface $logger)
+ {
+ $this->logger = $logger;
+ }
+
+ /**
+ * to get the logger
+ * @return LoggerInterface
+ */
+ public function getLogger()
+ {
+ return $this->logger;
}
private function generateSubdef(\record_adapter $record, \databox_subdef $subdef_class, $pathdest)
@@ -276,4 +317,14 @@ class SubdefGenerator
$i = floor(log($bytes, 1024));
return round($bytes / pow(1024, $i), [0,0,2,2,3][$i]).['B','kB','MB','GB'][$i];
}
+
+ /**
+ * @param $databoxId
+ *
+ * @return MediaSubdefRepository|Object
+ */
+ private function getMediaSubdefRepository($databoxId)
+ {
+ return $this->app['provider.repo.media_subdef']->getRepositoryForDatabox($databoxId);
+ }
}
diff --git a/lib/Alchemy/Phrasea/Media/SubdefSubstituer.php b/lib/Alchemy/Phrasea/Media/SubdefSubstituer.php
index 2259fa572d..8f7aa61e90 100644
--- a/lib/Alchemy/Phrasea/Media/SubdefSubstituer.php
+++ b/lib/Alchemy/Phrasea/Media/SubdefSubstituer.php
@@ -13,6 +13,7 @@ namespace Alchemy\Phrasea\Media;
use Alchemy\Phrasea\Application;
use Alchemy\Phrasea\Core\Event\Record\MediaSubstitutedEvent;
use Alchemy\Phrasea\Core\Event\Record\RecordEvents;
+use Alchemy\Phrasea\Core\Event\Record\SubdefinitionCreateEvent;
use Alchemy\Phrasea\Filesystem\FilesystemService;
use MediaAlchemyst\Alchemyst;
use MediaAlchemyst\Exception\ExceptionInterface as MediaAlchemystException;
@@ -79,7 +80,7 @@ class SubdefSubstituer
$record->write_metas();
if ($shouldSubdefsBeRebuilt) {
- $record->rebuild_subdefs();
+ $this->dispatcher->dispatch(RecordEvents::SUBDEFINITION_CREATE, new SubdefinitionCreateEvent($record));
}
$this->dispatcher->dispatch(RecordEvents::MEDIA_SUBSTITUTED, new MediaSubstitutedEvent($record));
diff --git a/lib/Alchemy/Phrasea/Model/Entities/WebhookEvent.php b/lib/Alchemy/Phrasea/Model/Entities/WebhookEvent.php
index 275650f1b3..a1091f531f 100644
--- a/lib/Alchemy/Phrasea/Model/Entities/WebhookEvent.php
+++ b/lib/Alchemy/Phrasea/Model/Entities/WebhookEvent.php
@@ -68,6 +68,14 @@ class WebhookEvent
*/
private $created;
+ /**
+ * List of collection base_id concerned
+ * @var array
+ *
+ * @ORM\Column(name="collection_base_ids", type="json_array", nullable=true)
+ */
+ private $collectionBaseIds;
+
/**
* @param \DateTime $created
*
@@ -175,4 +183,24 @@ class WebhookEvent
return $this;
}
+
+ /**
+ * @param array $collectionBaseIds
+ *
+ * @return $this
+ */
+ public function setCollectionBaseIds(array $collectionBaseIds)
+ {
+ $this->collectionBaseIds = $collectionBaseIds;
+
+ return $this;
+ }
+
+ /**
+ * @return array
+ */
+ public function getCollectionBaseIds()
+ {
+ return $this->collectionBaseIds;
+ }
}
diff --git a/lib/Alchemy/Phrasea/Model/Manipulator/UserManipulator.php b/lib/Alchemy/Phrasea/Model/Manipulator/UserManipulator.php
index 3d0815370e..d36fbf527a 100644
--- a/lib/Alchemy/Phrasea/Model/Manipulator/UserManipulator.php
+++ b/lib/Alchemy/Phrasea/Model/Manipulator/UserManipulator.php
@@ -126,8 +126,9 @@ class UserManipulator implements ManipulatorInterface
* Deletes a user.
*
* @param User|User[] $users
+ * @param array $grantedBaseIdList List of the old granted base_id per userId [user_id => [base_id, ...] ]
*/
- public function delete($users)
+ public function delete($users, array $grantedBaseIdList = array())
{
/** @var User $user */
foreach ($this->makeTraversable($users) as $user) {
@@ -146,9 +147,10 @@ class UserManipulator implements ManipulatorInterface
new DeletedEvent(
null,
array(
- 'user_id'=>$old_id,
- 'login'=>$old_login,
- 'email'=>$old_email
+ 'user_id' => $old_id,
+ 'login' => $old_login,
+ 'email' => $old_email,
+ 'grantedBaseIds' => isset($grantedBaseIdList[$old_id]) ? $grantedBaseIdList[$old_id] : []
)
)
);
diff --git a/lib/Alchemy/Phrasea/Model/Manipulator/WebhookEventManipulator.php b/lib/Alchemy/Phrasea/Model/Manipulator/WebhookEventManipulator.php
index 40e7f812a7..90cbf66b65 100644
--- a/lib/Alchemy/Phrasea/Model/Manipulator/WebhookEventManipulator.php
+++ b/lib/Alchemy/Phrasea/Model/Manipulator/WebhookEventManipulator.php
@@ -12,7 +12,7 @@
namespace Alchemy\Phrasea\Model\Manipulator;
use Alchemy\Phrasea\Model\Entities\WebhookEvent;
-use Alchemy\Phrasea\Webhook\WebhookPublisher;
+use Alchemy\Phrasea\Webhook\WebhookPublisherInterface;
use Doctrine\Common\Persistence\ObjectManager;
use Doctrine\ORM\EntityRepository;
@@ -29,18 +29,18 @@ class WebhookEventManipulator implements ManipulatorInterface
private $repository;
/**
- * @var WebhookPublisher
+ * @var WebhookPublisherInterface
*/
private $publisher;
- public function __construct(ObjectManager $om, EntityRepository $repo, WebhookPublisher $publisher)
+ public function __construct(ObjectManager $om, EntityRepository $repo, WebhookPublisherInterface $publisher)
{
$this->om = $om;
$this->repository = $repo;
$this->publisher = $publisher;
}
- public function create($eventName, $type, array $data)
+ public function create($eventName, $type, array $data, array $collectionBaseIds = array())
{
$event = new WebhookEvent();
@@ -48,6 +48,10 @@ class WebhookEventManipulator implements ManipulatorInterface
$event->setType($type);
$event->setData($data);
+ if (count($collectionBaseIds) > 0) {
+ $event->setCollectionBaseIds($collectionBaseIds);
+ }
+
$this->update($event);
$this->publisher->publishWebhookEvent($event);
diff --git a/lib/Alchemy/Phrasea/Order/Controller/BaseOrderController.php b/lib/Alchemy/Phrasea/Order/Controller/BaseOrderController.php
index 5b2e90d4cb..cf3c84e981 100644
--- a/lib/Alchemy/Phrasea/Order/Controller/BaseOrderController.php
+++ b/lib/Alchemy/Phrasea/Order/Controller/BaseOrderController.php
@@ -172,7 +172,7 @@ class BaseOrderController extends Controller
$manager->persist($element);
}
- $delivery = new OrderDelivery($order, $acceptor, count($basketElements));
+ $delivery = new OrderDelivery($order, $acceptor, count($basketElements), $partialOrder);
$this->dispatch(PhraseaEvents::ORDER_DELIVER, new OrderDeliveryEvent($delivery));
}
@@ -198,11 +198,13 @@ class BaseOrderController extends Controller
$elements = $this->findRequestedElements($order_id, $elementIds, $acceptor);
$order = $this->findOr404($order_id);
+ $partialOrder = new PartialOrder($order, $elements);
+
$this->getOrderValidator()->deny($acceptor, new PartialOrder($order, $elements));
try {
if (!empty($elements)) {
- $delivery = new OrderDelivery($order, $acceptor, count($elements));
+ $delivery = new OrderDelivery($order, $acceptor, count($elements), $partialOrder);
$this->dispatch(PhraseaEvents::ORDER_DENY, new OrderDeliveryEvent($delivery));
}
diff --git a/lib/Alchemy/Phrasea/Order/OrderDelivery.php b/lib/Alchemy/Phrasea/Order/OrderDelivery.php
index 5d8f3313ff..f36a98525f 100644
--- a/lib/Alchemy/Phrasea/Order/OrderDelivery.php
+++ b/lib/Alchemy/Phrasea/Order/OrderDelivery.php
@@ -31,16 +31,23 @@ class OrderDelivery
*/
private $quantity;
+ /**
+ * @var PartialOrder
+ */
+ private $partialOrder;
+
/**
* @param Order $deliveredOrder
* @param User $manager
* @param int $quantity
+ * @param PartialOrder $partialOrder
*/
- public function __construct(Order $deliveredOrder, User $manager, $quantity)
+ public function __construct(Order $deliveredOrder, User $manager, $quantity, PartialOrder $partialOrder)
{
- $this->order = $deliveredOrder;
- $this->admin = $manager;
- $this->quantity = $quantity;
+ $this->order = $deliveredOrder;
+ $this->admin = $manager;
+ $this->quantity = $quantity;
+ $this->partialOrder = $partialOrder;
}
/**
@@ -66,4 +73,12 @@ class OrderDelivery
{
return $this->quantity;
}
+
+ /**
+ * @return PartialOrder
+ */
+ public function getPartialOrder()
+ {
+ return $this->partialOrder;
+ }
}
diff --git a/lib/Alchemy/Phrasea/Order/ValidationNotifier.php b/lib/Alchemy/Phrasea/Order/ValidationNotifier.php
index e79b9ef888..419f180520 100644
--- a/lib/Alchemy/Phrasea/Order/ValidationNotifier.php
+++ b/lib/Alchemy/Phrasea/Order/ValidationNotifier.php
@@ -19,20 +19,23 @@ interface ValidationNotifier
/**
* @param Order $order
* @param User $recipient
+ * @param array $baseIds
* @return void
*/
- public function notifyCreation(Order $order, User $recipient);
+ public function notifyCreation(Order $order, User $recipient, array $baseIds = array());
/**
* @param OrderDelivery $delivery
+ * @param array $baseIds
* @return void
*/
- public function notifyDelivery(OrderDelivery $delivery);
+ public function notifyDelivery(OrderDelivery $delivery, array $baseIds = array());
/**
* @param OrderDelivery $delivery
+ * @param array $baseIds
* @return void
*/
- public function notifyDenial(OrderDelivery $delivery);
+ public function notifyDenial(OrderDelivery $delivery, array $baseIds = array());
}
diff --git a/lib/Alchemy/Phrasea/Order/ValidationNotifier/CompositeNotifier.php b/lib/Alchemy/Phrasea/Order/ValidationNotifier/CompositeNotifier.php
index a3e75cebcb..b38d51a3ca 100644
--- a/lib/Alchemy/Phrasea/Order/ValidationNotifier/CompositeNotifier.php
+++ b/lib/Alchemy/Phrasea/Order/ValidationNotifier/CompositeNotifier.php
@@ -26,8 +26,9 @@ class CompositeNotifier implements ValidationNotifier
/**
* @param Order $order
* @param User $recipient
+ * @param array $baseIds
*/
- public function notifyCreation(Order $order, User $recipient)
+ public function notifyCreation(Order $order, User $recipient, array $baseIds = array())
{
foreach ($this->notifiers as $notifier) {
$notifier->notifyCreation($order, $recipient);
@@ -36,21 +37,23 @@ class CompositeNotifier implements ValidationNotifier
/**
* @param OrderDelivery $delivery
+ * @param array $baseIds
*/
- public function notifyDelivery(OrderDelivery $delivery)
+ public function notifyDelivery(OrderDelivery $delivery, array $baseIds = array())
{
foreach ($this->notifiers as $notifier) {
- $notifier->notifyDelivery($delivery);
+ $notifier->notifyDelivery($delivery, $baseIds);
}
}
/**
* @param OrderDelivery $delivery
+ * @param array $baseIds
*/
- public function notifyDenial(OrderDelivery $delivery)
+ public function notifyDenial(OrderDelivery $delivery, array $baseIds = array())
{
foreach ($this->notifiers as $notifier) {
- $notifier->notifyDenial($delivery);
+ $notifier->notifyDenial($delivery, $baseIds);
}
}
}
diff --git a/lib/Alchemy/Phrasea/Order/ValidationNotifier/MailNotifier.php b/lib/Alchemy/Phrasea/Order/ValidationNotifier/MailNotifier.php
index 8a6541793a..9934fafb08 100644
--- a/lib/Alchemy/Phrasea/Order/ValidationNotifier/MailNotifier.php
+++ b/lib/Alchemy/Phrasea/Order/ValidationNotifier/MailNotifier.php
@@ -46,8 +46,9 @@ class MailNotifier implements ValidationNotifier
/**
* @param Order $order
* @param User $recipient
+ * @param array $baseIds
*/
- public function notifyCreation(Order $order, User $recipient)
+ public function notifyCreation(Order $order, User $recipient, array $baseIds = array())
{
$mail = MailInfoNewOrder::create($this->application, Receiver::fromUser($recipient));
@@ -58,8 +59,9 @@ class MailNotifier implements ValidationNotifier
/**
* @param OrderDelivery $delivery
+ * @param array $baseIds
*/
- public function notifyDelivery(OrderDelivery $delivery)
+ public function notifyDelivery(OrderDelivery $delivery, array $baseIds = array())
{
$order = $delivery->getOrder();
@@ -85,8 +87,9 @@ class MailNotifier implements ValidationNotifier
/**
* @param OrderDelivery $delivery
+ * @param array $baseIds
*/
- public function notifyDenial(OrderDelivery $delivery)
+ public function notifyDenial(OrderDelivery $delivery, array $baseIds = array())
{
$sender = Emitter::fromUser($delivery->getAdmin());
$recipient = Receiver::fromUser($delivery->getOrder()->getUser());
diff --git a/lib/Alchemy/Phrasea/Order/ValidationNotifier/WebhookNotifier.php b/lib/Alchemy/Phrasea/Order/ValidationNotifier/WebhookNotifier.php
index 3634e8e907..4762ff43ec 100644
--- a/lib/Alchemy/Phrasea/Order/ValidationNotifier/WebhookNotifier.php
+++ b/lib/Alchemy/Phrasea/Order/ValidationNotifier/WebhookNotifier.php
@@ -47,21 +47,23 @@ class WebhookNotifier implements ValidationNotifier
/**
* @param Order $order
* @param User $recipient
+ * @param array $baseIds
*/
- public function notifyCreation(Order $order, User $recipient)
+ public function notifyCreation(Order $order, User $recipient, array $baseIds = array())
{
$eventData = [
'order_id' => $order->getId(),
'user_id' => $recipient->getId(),
];
- $this->getManipulator()->create(WebhookEvent::ORDER_CREATED, WebhookEvent::ORDER_TYPE, $eventData);
+ $this->getManipulator()->create(WebhookEvent::ORDER_CREATED, WebhookEvent::ORDER_TYPE, $eventData, $baseIds);
}
/**
* @param OrderDelivery $delivery
+ * @param array $baseIds
*/
- public function notifyDelivery(OrderDelivery $delivery)
+ public function notifyDelivery(OrderDelivery $delivery, array $baseIds = array())
{
$eventData = [
'order_id' => $delivery->getOrder()->getId(),
@@ -69,13 +71,14 @@ class WebhookNotifier implements ValidationNotifier
'quantity' => $delivery->getQuantity()
];
- $this->getManipulator()->create(WebhookEvent::ORDER_DELIVERED, WebhookEvent::ORDER_TYPE, $eventData);
+ $this->getManipulator()->create(WebhookEvent::ORDER_DELIVERED, WebhookEvent::ORDER_TYPE, $eventData, $baseIds);
}
/**
* @param OrderDelivery $delivery
+ * @param array $baseIds
*/
- public function notifyDenial(OrderDelivery $delivery)
+ public function notifyDenial(OrderDelivery $delivery, array $baseIds = array())
{
$eventData = [
'order_id' => $delivery->getOrder()->getId(),
@@ -83,6 +86,6 @@ class WebhookNotifier implements ValidationNotifier
'quantity' => $delivery->getQuantity()
];
- $this->getManipulator()->create(WebhookEvent::ORDER_DENIED, WebhookEvent::ORDER_TYPE, $eventData);
+ $this->getManipulator()->create(WebhookEvent::ORDER_DENIED, WebhookEvent::ORDER_TYPE, $eventData, $baseIds);
}
}
diff --git a/lib/Alchemy/Phrasea/SearchEngine/Elastic/DataboxFetcherFactory.php b/lib/Alchemy/Phrasea/SearchEngine/Elastic/DataboxFetcherFactory.php
index b4eb070896..469ef65fc9 100644
--- a/lib/Alchemy/Phrasea/SearchEngine/Elastic/DataboxFetcherFactory.php
+++ b/lib/Alchemy/Phrasea/SearchEngine/Elastic/DataboxFetcherFactory.php
@@ -2,6 +2,7 @@
namespace Alchemy\Phrasea\SearchEngine\Elastic;
+use Alchemy\Phrasea\Core\Configuration\PropertyAccess;
use Alchemy\Phrasea\SearchEngine\Elastic\Indexer\Record\Delegate\FetcherDelegateInterface;
use Alchemy\Phrasea\SearchEngine\Elastic\Indexer\Record\Fetcher;
use Alchemy\Phrasea\SearchEngine\Elastic\Indexer\Record\Hydrator\CoreHydrator;
@@ -13,8 +14,14 @@ use Alchemy\Phrasea\SearchEngine\Elastic\Indexer\Record\Hydrator\TitleHydrator;
use Alchemy\Phrasea\SearchEngine\Elastic\Structure\Structure;
use Alchemy\Phrasea\SearchEngine\Elastic\Thesaurus\CandidateTerms;
+
class DataboxFetcherFactory
{
+ /**
+ * @var PropertyAccess phraseanet configuration
+ */
+ private $conf;
+
/**
* @var \ArrayAccess
*/
@@ -39,14 +46,16 @@ class DataboxFetcherFactory
private $options;
/**
+ * @param PropertyAccess $conf
* @param RecordHelper $recordHelper
* @param ElasticsearchOptions $options
* @param \ArrayAccess $container
* @param string $structureKey
* @param string $thesaurusKey
*/
- public function __construct(RecordHelper $recordHelper, ElasticsearchOptions $options, \ArrayAccess $container, $structureKey, $thesaurusKey)
+ public function __construct(PropertyAccess $conf, RecordHelper $recordHelper, ElasticsearchOptions $options, \ArrayAccess $container, $structureKey, $thesaurusKey)
{
+ $this->conf = $conf;
$this->recordHelper = $recordHelper;
$this->options = $options;
$this->container = $container;
@@ -70,7 +79,7 @@ class DataboxFetcherFactory
[
new CoreHydrator($databox->get_sbas_id(), $databox->get_viewname(), $this->recordHelper),
new TitleHydrator($connection, $this->recordHelper),
- new MetadataHydrator($connection, $this->getStructure(), $this->recordHelper),
+ new MetadataHydrator($this->conf, $connection, $this->getStructure(), $this->recordHelper),
new FlagHydrator($this->getStructure(), $databox),
new ThesaurusHydrator($this->getStructure(), $this->getThesaurus(), $candidateTerms),
new SubDefinitionHydrator($connection)
diff --git a/lib/Alchemy/Phrasea/SearchEngine/Elastic/Indexer/Record/Hydrator/GpsPosition.php b/lib/Alchemy/Phrasea/SearchEngine/Elastic/Indexer/Record/Hydrator/GpsPosition.php
index ba0dcf573f..9d4f1aabf7 100644
--- a/lib/Alchemy/Phrasea/SearchEngine/Elastic/Indexer/Record/Hydrator/GpsPosition.php
+++ b/lib/Alchemy/Phrasea/SearchEngine/Elastic/Indexer/Record/Hydrator/GpsPosition.php
@@ -15,6 +15,7 @@ use Assert\Assertion;
class GpsPosition
{
+ const FULL_GEO_NOTATION = 'FullNotation';
const LONGITUDE_TAG_NAME = 'Longitude';
const LONGITUDE_REF_TAG_NAME = 'LongitudeRef';
const LONGITUDE_REF_WEST = 'W';
@@ -29,6 +30,16 @@ class GpsPosition
private $latitude;
private $latitude_ref;
+ public function __construct()
+ {
+ $this->clear();
+ }
+
+ public function clear()
+ {
+ $this->longitude = $this->longitude_ref = $this->latitude = $this->latitude_ref = null;
+ }
+
public function set($tag_name, $value)
{
switch ($tag_name) {
@@ -64,6 +75,53 @@ class GpsPosition
$this->latitude_ref = $normalized;
break;
+ case self::FULL_GEO_NOTATION:
+ $re = '/(-?\d+(?:\.\d+)?°?)\s*(\d+(?:\.\d+)?\')?\s*(\d+(?:\.\d+)?")?\s*(N|S|E|W)?/um';
+ $normalized = trim(strtoupper($value));
+ $matches = null;
+ preg_match_all($re, $normalized, $matches, PREG_SET_ORDER, 0);
+ if(count($matches) === 2) { // we need lat and lon
+ $lat = $lon = null;
+ foreach ($matches as $imatch => $match) {
+ if(count($match) != 5) {
+ continue;
+ }
+ $v = 0.0;
+ for($part=1, $div=1.0; $part<=3; $part++, $div*=60.0) {
+ $v += floatval($match[$part]) / $div;
+ }
+ switch($match[4]) { // N S E W
+ case 'N':
+ $lat = $v;
+ break;
+ case 'S':
+ $lat = -$v;
+ break;
+ case 'E':
+ $lon = $v;
+ break;
+ case 'W':
+ $lon = -$v;
+ break;
+ case '': // no ref -> lat lon (first=lat, second=lon)
+ if($imatch === 0) {
+ $lat = $v;
+ }
+ else {
+ $lon = $v;
+ }
+ break;
+ default:
+ throw new \InvalidArgumentException(sprintf('Unsupported reference "%s", should be N|S|E|W.', $match[4]));
+ }
+ }
+ if($lat !== null && $lon != null) {
+ $this->set(self::LATITUDE_TAG_NAME, $lat);
+ $this->set(self::LONGITUDE_TAG_NAME, $lon);
+ }
+ }
+ break;
+
default:
throw new \InvalidArgumentException(sprintf('Unsupported tag name "%s".', $tag_name));
}
@@ -95,19 +153,11 @@ class GpsPosition
public function getCompositeLongitude()
{
- if ($this->longitude === null) {
- return null;
- }
-
return $this->longitude ;
}
public function getCompositeLatitude()
{
- if ($this->latitude === null) {
- return null;
- }
-
return $this->latitude;
}
diff --git a/lib/Alchemy/Phrasea/SearchEngine/Elastic/Indexer/Record/Hydrator/MetadataHydrator.php b/lib/Alchemy/Phrasea/SearchEngine/Elastic/Indexer/Record/Hydrator/MetadataHydrator.php
index 5dc2471cb9..f930fce740 100644
--- a/lib/Alchemy/Phrasea/SearchEngine/Elastic/Indexer/Record/Hydrator/MetadataHydrator.php
+++ b/lib/Alchemy/Phrasea/SearchEngine/Elastic/Indexer/Record/Hydrator/MetadataHydrator.php
@@ -11,6 +11,7 @@
namespace Alchemy\Phrasea\SearchEngine\Elastic\Indexer\Record\Hydrator;
+use Alchemy\Phrasea\Core\Configuration\PropertyAccess;
use Alchemy\Phrasea\SearchEngine\Elastic\Exception\Exception;
use Alchemy\Phrasea\SearchEngine\Elastic\FieldMapping;
use Alchemy\Phrasea\SearchEngine\Elastic\Mapping;
@@ -24,28 +25,48 @@ use InvalidArgumentException;
class MetadataHydrator implements HydratorInterface
{
+ private $conf;
private $connection;
private $structure;
private $helper;
- private $gps_position_buffer = [];
+ private $position_fields_mapping; // get from conf
- public function __construct(DriverConnection $connection, Structure $structure, RecordHelper $helper)
+ private $caption_gps_position;
+ private $exif_gps_position;
+
+ public function __construct(PropertyAccess $conf, DriverConnection $connection, Structure $structure, RecordHelper $helper)
{
+ $this->conf = $conf;
$this->connection = $connection;
$this->structure = $structure;
$this->helper = $helper;
+
+ // get the fieldnames of source of lat / lon geo fields (defined in instance conf)
+ $this->position_fields_mapping = [];
+ foreach($conf->get(['geocoding-providers'], []) as $provider) {
+ if($provider['enabled'] && array_key_exists('position-fields', $provider)) {
+ foreach ($provider['position-fields'] as $position_field) {
+ $this->position_fields_mapping[$position_field['name']] = $position_field['type'];
+ }
+ }
+ }
+
+ $this->caption_gps_position = new GpsPosition();
+ $this->exif_gps_position = new GpsPosition();
}
public function hydrateRecords(array &$records)
{
- $sql = "(SELECT record_id, ms.name AS `key`, m.value AS value, 'caption' AS type, ms.business AS private\n"
+ $sql = "SELECT * FROM ("
+ . "(SELECT record_id, ms.name AS `key`, m.value AS value, 'caption' AS type, ms.business AS private\n"
. " FROM metadatas AS m INNER JOIN metadatas_structure AS ms ON (ms.id = m.meta_struct_id)\n"
. " WHERE record_id IN (?))\n"
. "UNION\n"
. "(SELECT record_id, t.name AS `key`, t.value AS value, 'exif' AS type, 0 AS private\n"
. " FROM technical_datas AS t\n"
- . " WHERE record_id IN (?))\n";
+ . " WHERE record_id IN (?))\n"
+ . ") AS t ORDER BY record_id";
$ids = array_keys($records);
$statement = $this->connection->executeQuery(
@@ -54,7 +75,17 @@ class MetadataHydrator implements HydratorInterface
array(Connection::PARAM_INT_ARRAY, Connection::PARAM_INT_ARRAY)
);
+ $record_id = -1;
while ($metadata = $statement->fetch()) {
+
+ if($metadata['record_id'] !== $record_id) {
+ // record has changed, don't mix with previous one
+ $this->caption_gps_position->clear();
+ $this->exif_gps_position->clear();
+
+ $record_id = $metadata['record_id'];
+ }
+
// Store metadata value
$key = $metadata['key'];
$value = trim($metadata['value']);
@@ -64,10 +95,10 @@ class MetadataHydrator implements HydratorInterface
continue;
}
- $id = $metadata['record_id'];
- if (isset($records[$id])) {
- $record =& $records[$id];
- } else {
+ if (isset($records[$record_id])) {
+ $record =& $records[$record_id];
+ }
+ else {
throw new Exception('Received metadata from unexpected record');
}
@@ -89,11 +120,31 @@ class MetadataHydrator implements HydratorInterface
$record[$field] = array();
}
$record[$field][] = $value;
+
+ if(array_key_exists($key, $this->position_fields_mapping)) {
+ // this field is mapped as a position part (lat, lon, latlon), push it
+ switch($this->position_fields_mapping[$key]) {
+ case 'lat':
+ $this->handleGpsPosition($this->caption_gps_position, $record, GpsPosition::LATITUDE_TAG_NAME, $value);
+ break;
+ case 'lng':
+ case 'lon':
+ $this->handleGpsPosition($this->caption_gps_position, $record, GpsPosition::LONGITUDE_TAG_NAME, $value);
+ break;
+ case 'latlng':
+ case 'latlon':
+ $this->handleGpsPosition($this->caption_gps_position, $record, GpsPosition::FULL_GEO_NOTATION, $value);
+ break;
+ }
+ }
+
break;
case 'exif':
- if (GpsPosition::isSupportedTagName($key)) {
- $this->handleGpsPosition($records, $id, $key, $value);
+ // exif gps is a first-chance if caption is not yet set
+ // anyway if caption is set later, it will override the exif values
+ if (GpsPosition::isSupportedTagName($key) && !$this->caption_gps_position->isCompleteComposite()) {
+ $this->handleGpsPosition($this->exif_gps_position, $record, $key, $value);
break;
}
$tag = $this->structure->getMetadataTagByName($key);
@@ -109,38 +160,25 @@ class MetadataHydrator implements HydratorInterface
break;
}
}
-
- $this->clearGpsPositionBuffer();
}
- private function handleGpsPosition(&$records, $id, $tag_name, $value)
+ private function handleGpsPosition(GpsPosition &$position, &$record, $tag_name, $value)
{
- // Get position object
- if (!isset($this->gps_position_buffer[$id])) {
- $this->gps_position_buffer[$id] = new GpsPosition();
- }
- $position = $this->gps_position_buffer[$id];
// Push this tag into object
$position->set($tag_name, $value);
+
// Try to output complete position
if ($position->isCompleteComposite()) {
$lon = $position->getCompositeLongitude();
$lat = $position->getCompositeLatitude();
- $records[$id]['metadata_tags']['Longitude'] = $lon;
- $records[$id]['metadata_tags']['Latitude'] = $lat;
+ $record['metadata_tags']['Longitude'] = $lon;
+ $record['metadata_tags']['Latitude'] = $lat;
- $records[$id]["location"] = [
+ $record["location"] = [
"lat" => $lat,
"lon" => $lon
];
-
- unset($this->gps_position_buffer[$id]);
}
}
-
- private function clearGpsPositionBuffer()
- {
- $this->gps_position_buffer = [];
- }
}
diff --git a/lib/Alchemy/Phrasea/SearchEngine/Elastic/RecordHelper.php b/lib/Alchemy/Phrasea/SearchEngine/Elastic/RecordHelper.php
index 74f3a297a8..6d19916e62 100644
--- a/lib/Alchemy/Phrasea/SearchEngine/Elastic/RecordHelper.php
+++ b/lib/Alchemy/Phrasea/SearchEngine/Elastic/RecordHelper.php
@@ -151,6 +151,7 @@ class RecordHelper
return (bool) $value;
case FieldMapping::TYPE_STRING:
+ $value = substr($value, 0, 32766); // for lucene limit, before a better solution
return str_replace("\0", '', $value);
default:
diff --git a/lib/Alchemy/Phrasea/TaskManager/Job/ArchiveJob.php b/lib/Alchemy/Phrasea/TaskManager/Job/ArchiveJob.php
index 532eb8bbb5..de418a4e71 100644
--- a/lib/Alchemy/Phrasea/TaskManager/Job/ArchiveJob.php
+++ b/lib/Alchemy/Phrasea/TaskManager/Job/ArchiveJob.php
@@ -12,6 +12,8 @@
namespace Alchemy\Phrasea\TaskManager\Job;
use Alchemy\Phrasea\Application;
+use Alchemy\Phrasea\Core\Event\Record\RecordEvents;
+use Alchemy\Phrasea\Core\Event\Record\SubdefinitionCreateEvent;
use Alchemy\Phrasea\Exception\RuntimeException;
use Alchemy\Phrasea\Border\File;
use Alchemy\Phrasea\Border\Manager as borderManager;
@@ -1001,7 +1003,7 @@ class ArchiveJob extends AbstractJob
{
// quick fix to reconnect if mysql is lost
$app->getApplicationBox()->get_connection();
- $databox->get_connection();
+ $collection->get_connection();
$status = \databox_status::operation_or($stat0, $stat1);
@@ -1032,7 +1034,8 @@ class ArchiveJob extends AbstractJob
}
$story->setStatus(\databox_status::operation_or($stat0, $stat1));
- $story->rebuild_subdefs();
+
+ $app['dispatcher']->dispatch(RecordEvents::SUBDEFINITION_CREATE, new SubdefinitionCreateEvent($story));
unset($media);
diff --git a/lib/Alchemy/Phrasea/Webhook/EventProcessorFactory.php b/lib/Alchemy/Phrasea/Webhook/EventProcessorFactory.php
index 90edefc10c..79109a624c 100644
--- a/lib/Alchemy/Phrasea/Webhook/EventProcessorFactory.php
+++ b/lib/Alchemy/Phrasea/Webhook/EventProcessorFactory.php
@@ -12,6 +12,7 @@ use Alchemy\Phrasea\Webhook\Processor\ProcessorFactory;
use Alchemy\Phrasea\Webhook\Processor\ProcessorInterface;
use Alchemy\Phrasea\Webhook\Processor\UserDeletedProcessorFactory;
use Alchemy\Phrasea\Webhook\Processor\UserRegistrationProcessorFactory;
+use Alchemy\Phrasea\Webhook\Processor\SubdefEventProcessor;
class EventProcessorFactory
{
@@ -29,7 +30,10 @@ class EventProcessorFactory
$this->registerFactory(WebhookEvent::FEED_ENTRY_TYPE, new FeedEntryProcessorFactory($app));
$this->registerFactory(WebhookEvent::USER_REGISTRATION_TYPE, new UserRegistrationProcessorFactory($app));
$this->registerFactory(WebhookEvent::ORDER_TYPE, new OrderNotificationProcessorFactory($app));
- $this->registerFactory(WebhookEvent::USER_DELETED_TYPE, new UserDeletedProcessorFactory($app));
+ $this->registerFactory(WebhookEvent::USER_DELETED_TYPE, new UserDeletedProcessorFactory());
+ $this->registerCallableFactory(WebhookEvent::RECORD_SUBDEF_TYPE, function () {
+ return new SubdefEventProcessor();
+ });
}
/**
@@ -43,7 +47,7 @@ class EventProcessorFactory
/**
* @param string $eventType
- * @param callback|callable $callable
+ * @param callback|callable|\Closure $callable
*/
public function registerCallableFactory($eventType, $callable)
{
diff --git a/lib/Alchemy/Phrasea/Webhook/Processor/FeedEntryProcessor.php b/lib/Alchemy/Phrasea/Webhook/Processor/FeedEntryProcessor.php
index 7fd4500c49..c873bbb680 100644
--- a/lib/Alchemy/Phrasea/Webhook/Processor/FeedEntryProcessor.php
+++ b/lib/Alchemy/Phrasea/Webhook/Processor/FeedEntryProcessor.php
@@ -34,17 +34,16 @@ class FeedEntryProcessor implements ProcessorInterface
{
$data = $event->getData();
- if (!isset($data->entry_id)) {
+ if (!isset($data['entry_id'])) {
return null;
}
- $entry = $this->entryRepository->find($data->entry_id);
+ $entry = $this->entryRepository->find($data['entry_id']);
if (null === $entry) {
return null;
}
- $data = $event->getData();
$feed = $entry->getFeed();
$query = $this->userQuery;
@@ -54,8 +53,8 @@ class FeedEntryProcessor implements ProcessorInterface
->include_templates(false)
->email_not_null(true);
- if ($feed->getCollection($this->app)) {
- $query->on_base_ids([$feed->getCollection($this->app)->get_base_id()]);
+ if ($feed->getCollection($this->application)) {
+ $query->on_base_ids([$feed->getCollection($this->application)->get_base_id()]);
}
$start = 0;
@@ -76,7 +75,7 @@ class FeedEntryProcessor implements ProcessorInterface
return [
'event' => $event->getName(),
- 'users_were_notified' => isset($data->notify_email) ?: (bool) $data->notify_email,
+ 'users_were_notified' => isset($data['notify_email']) ? (bool) $data['notify_email'] : false,
'feed' => [
'id' => $feed->getId(),
'title' => $feed->getTitle(),
diff --git a/lib/Alchemy/Phrasea/Webhook/Processor/SubdefEventProcessor.php b/lib/Alchemy/Phrasea/Webhook/Processor/SubdefEventProcessor.php
new file mode 100644
index 0000000000..4a442ed5db
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Webhook/Processor/SubdefEventProcessor.php
@@ -0,0 +1,17 @@
+ $event->getName(),
+ 'data' => $event->getData()
+ ];
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Webhook/WebhookPublisher.php b/lib/Alchemy/Phrasea/Webhook/WebhookPublisher.php
index 4304a5319f..2beaec292f 100644
--- a/lib/Alchemy/Phrasea/Webhook/WebhookPublisher.php
+++ b/lib/Alchemy/Phrasea/Webhook/WebhookPublisher.php
@@ -19,7 +19,7 @@ use Alchemy\Queue\MessageQueueRegistry;
* Class WebhookPublisher publishes webhook event notifications in message queues
* @package Alchemy\Phrasea\Webhook
*/
-class WebhookPublisher
+class WebhookPublisher implements WebhookPublisherInterface
{
/**
* @var MessageQueueRegistry
diff --git a/lib/Alchemy/Phrasea/Webhook/WebhookPublisherInterface.php b/lib/Alchemy/Phrasea/Webhook/WebhookPublisherInterface.php
new file mode 100644
index 0000000000..f8b4751ef0
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Webhook/WebhookPublisherInterface.php
@@ -0,0 +1,10 @@
+app['locale'] ?: 'en',
+ NULL, NULL, NULL, NULL, 'dd MMMM yyyy'
+ );
+
+ return $fmt->format($date);
+ }
+
/**
*
* @param DateTime $date
@@ -139,17 +149,25 @@ class phraseadate
*/
private function formatDate(DateTime $date, $locale, $format)
{
-
switch ($locale) {
default:
+ case 'de':
case 'fr':
switch ($format) {
default:
case 'DAY_MONTH':
- $date_formated = strftime("%e %B", $date->format('U'));
+ $formatM = new IntlDateFormatter(
+ $locale,
+ NULL, NULL, NULL, NULL, 'dd MMMM'
+ );
+ $date_formated = $formatM->format($date);
break;
case 'DAY_MONTH_YEAR':
- $date_formated = strftime("%e %B %Y", $date->format('U'));
+ $formatY = new IntlDateFormatter(
+ $locale,
+ NULL, NULL, NULL, NULL, 'dd MMMM yyyy'
+ );
+ $date_formated = $formatY->format($date);
break;
}
break;
@@ -157,26 +175,22 @@ class phraseadate
switch ($format) {
default:
case 'DAY_MONTH':
- $date_formated = strftime("%B %e", $date->format('U'));
+ $formatM = new IntlDateFormatter(
+ $locale,
+ NULL, NULL, NULL, NULL, 'MMMM dd'
+ );
+ $date_formated = $formatM->format($date);
break;
case 'DAY_MONTH_YEAR':
- $date_formated = strftime("%B %e %Y", $date->format('U'));
- break;
- }
- break;
- case 'de':
- switch ($format) {
- default:
- case 'DAY_MONTH':
- $date_formated = strftime("%e. %B", $date->format('U'));
- break;
- case 'DAY_MONTH_YEAR':
- $date_formated = strftime("%e. %B %Y", $date->format('U'));
+ $formatY = new IntlDateFormatter(
+ $locale,
+ NULL, NULL, NULL, NULL, 'MMMM dd yyyy'
+ );
+ $date_formated = $formatY->format($date);
break;
}
break;
}
-
return $date_formated;
}
diff --git a/lib/classes/record/adapter.php b/lib/classes/record/adapter.php
index 8e181c5d2e..9d94822566 100644
--- a/lib/classes/record/adapter.php
+++ b/lib/classes/record/adapter.php
@@ -19,6 +19,7 @@ use Alchemy\Phrasea\Core\Event\Record\OriginalNameChangedEvent;
use Alchemy\Phrasea\Core\Event\Record\RecordEvent;
use Alchemy\Phrasea\Core\Event\Record\RecordEvents;
use Alchemy\Phrasea\Core\Event\Record\StatusChangedEvent;
+use Alchemy\Phrasea\Core\Event\Record\SubdefinitionCreateEvent;
use Alchemy\Phrasea\Core\PhraseaTokens;
use Alchemy\Phrasea\Databox\Subdef\MediaSubdefRepository;
use Alchemy\Phrasea\Filesystem\FilesystemService;
@@ -284,7 +285,7 @@ class record_adapter implements RecordInterface, cache_cacheableInterface
$this->getDataboxConnection()->executeUpdate($sql, ['type' => $type, 'record_id' => $this->getRecordId()]);
if ($old_type !== $type) {
- $this->rebuild_subdefs();
+ $this->dispatch(RecordEvents::SUBDEFINITION_CREATE, new SubdefinitionCreateEvent($this));
}
$this->type = $type;
@@ -341,7 +342,8 @@ class record_adapter implements RecordInterface, cache_cacheableInterface
array(':mime' => $mime, ':record_id' => $this->getRecordId())
)) {
- $this->rebuild_subdefs();
+ $this->dispatch(RecordEvents::SUBDEFINITION_CREATE, new SubdefinitionCreateEvent($this));
+
$this->delete_data_from_cache();
}
@@ -1735,7 +1737,9 @@ class record_adapter implements RecordInterface, cache_cacheableInterface
throw new Exception('This record is not a grouping');
}
- $selections = $this->getDatabox()->getRecordRepository()->findChildren([$this->getRecordId()], null, $offset, $max_items);
+ $user = $this->getAuthenticatedUser();
+
+ $selections = $this->getDatabox()->getRecordRepository()->findChildren([$this->getRecordId()], $user, $offset, $max_items);
return reset($selections);
}
@@ -1745,7 +1749,9 @@ class record_adapter implements RecordInterface, cache_cacheableInterface
*/
public function get_grouping_parents()
{
- $selections = $this->getDatabox()->getRecordRepository()->findParents([$this->getRecordId()]);
+ $user = $this->getAuthenticatedUser();
+
+ $selections = $this->getDatabox()->getRecordRepository()->findParents([$this->getRecordId()], $user);
return reset($selections);
}
@@ -1948,4 +1954,15 @@ class record_adapter implements RecordInterface, cache_cacheableInterface
{
return $this->app['provider.repo.media_subdef']->getRepositoryForDatabox($this->getDataboxId());
}
+
+ /**
+ * @return User|null
+ */
+ protected function getAuthenticatedUser()
+ {
+ /** @var \Alchemy\Phrasea\Authentication\Authenticator $authenticator */
+ $authenticator = $this->app['authentication'];
+
+ return $authenticator->getUser();
+ }
}
diff --git a/lib/conf.d/data_templates/DublinCore.xml b/lib/conf.d/data_templates/DublinCore.xml
index eae43d5461..db3039df7f 100644
--- a/lib/conf.d/data_templates/DublinCore.xml
+++ b/lib/conf.d/data_templates/DublinCore.xml
@@ -79,7 +79,7 @@
748
video
yes
- libmp3lame
+ libfdk_aac
libx264
screen
1000
diff --git a/lib/conf.d/data_templates/en-simple.xml b/lib/conf.d/data_templates/en-simple.xml
index 44b0afa69f..81ce397169 100644
--- a/lib/conf.d/data_templates/en-simple.xml
+++ b/lib/conf.d/data_templates/en-simple.xml
@@ -79,7 +79,7 @@
748
video
yes
- libmp3lame
+ libfdk_aac
libx264
screen
1000
diff --git a/lib/conf.d/data_templates/fr-simple.xml b/lib/conf.d/data_templates/fr-simple.xml
index cc0a2b582c..48d628cedd 100644
--- a/lib/conf.d/data_templates/fr-simple.xml
+++ b/lib/conf.d/data_templates/fr-simple.xml
@@ -79,7 +79,7 @@
748
video
yes
- libmp3lame
+ libfdk_aac
libx264
screen
1000
diff --git a/package.json b/package.json
index 7ce6d01a66..232c24d86b 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.150-d",
+ "phraseanet-production-client": "0.34.160-d",
"requirejs": "^2.3.5",
"tinymce": "^4.0.28",
"underscore": "^1.8.3",
diff --git a/resources/hudson/_GV.php b/resources/hudson/_GV.php
index 284f4db6ba..808768544e 100644
--- a/resources/hudson/_GV.php
+++ b/resources/hudson/_GV.php
@@ -84,5 +84,7 @@ define('GV_homeTitle', 'SuperPhraseanet');
define('GV_metaKeywords', '');
define('GV_metaDescription', '');
define('GV_googleAnalytics', '');
+define('GV_matomoAnalyticsUrl', '');
+define('GV_matomoAnalyticsId', '');
define('GV_allow_search_engine', true);
define('GV_display_gcf', true);
diff --git a/resources/locales/messages.de.xlf b/resources/locales/messages.de.xlf
index 9ca23ed2d7..8972dae59c 100644
--- a/resources/locales/messages.de.xlf
+++ b/resources/locales/messages.de.xlf
@@ -1,6 +1,6 @@
-
+
The source node in most cases contains the sample message as written by the developer. If it looks like a dot-delimitted string such as "form.label.firstname", then the developer has not provided a default message.
@@ -192,8 +192,9 @@
%nb_records% records
%nb_records% Datensätze
+ prod/WorkZone/Basket.html.twig
prod/Tooltip/Story.html.twig
- prod/Tooltip/Basket.html.twig
+ prod/Tooltip/Basket.html.twig
%nb_view% vue
@@ -1689,7 +1690,7 @@
Certaines donnees du panier ont change
Einige Daten des Sammelkorbs wurden verändert
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
Certaines donnees du reportage ont change
@@ -3165,6 +3166,11 @@
Einstellungen von ausführbaren Programme
Form/Configuration/MainConfigurationFormType.php
+
+ Expiration date successfully updated!
+ Expiration date successfully updated!
+ Controller/Prod/PushController.php
+
Export
Exportieren
@@ -6685,6 +6691,11 @@
Die folgende Fehler wurden festgestellt
user/import/view.html.twig
+
+ The provided date is null!
+ The provided date is null!
+ Controller/Prod/PushController.php
+
The publication has been stopped
Veröffentlichung wurde gestoppt
@@ -7046,6 +7057,11 @@
Controller/Root/LoginController.php
Controller/Api/OAuth2Controller.php
+
+ Unable to save the expiration date
+ Unable to save the expiration date
+ Controller/Prod/PushController.php
+
Unable to send the documents
Es ist nicht möglich Dokumente zu senden
@@ -11718,7 +11734,13 @@
prod:workzone:basket:creation-date
prod:workzone:basket:creation-date
- prod/Tooltip/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
+ prod/Tooltip/Basket.html.twig
+
+
+ prod:workzone:basket:updated-message
+ prod:workzone:basket:updated-message
+ prod/WorkZone/Basket.html.twig
prod:workzone:facetstab:search_and_facets_sort_options
@@ -11826,7 +11848,7 @@
Aktualisieren
prod/WorkZone/Story.html.twig
prod/WorkZone/Macros.html.twig
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
prod/results/feeds.html.twig
prod/results/feeds.html.twig
@@ -13397,128 +13419,134 @@
workzone:datepicker:april
workzone:datepicker:april
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
workzone:datepicker:august
workzone:datepicker:august
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
workzone:datepicker:closeText
workzone:datepicker:closeText
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
workzone:datepicker:currentText
workzone:datepicker:currentText
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
workzone:datepicker:december
workzone:datepicker:december
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
workzone:datepicker:february
workzone:datepicker:february
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
workzone:datepicker:friday
workzone:datepicker:friday
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
workzone:datepicker:january
workzone:datepicker:january
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
workzone:datepicker:july
workzone:datepicker:july
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
workzone:datepicker:june
workzone:datepicker:june
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
workzone:datepicker:march
workzone:datepicker:march
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
workzone:datepicker:may
workzone:datepicker:may
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
workzone:datepicker:monday
workzone:datepicker:monday
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
workzone:datepicker:nextText
workzone:datepicker:nextText
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
workzone:datepicker:november
workzone:datepicker:november
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
workzone:datepicker:october
workzone:datepicker:october
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
workzone:datepicker:prevText
workzone:datepicker:prevText
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
workzone:datepicker:saturday
workzone:datepicker:saturday
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
workzone:datepicker:september
workzone:datepicker:september
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
workzone:datepicker:sunday
workzone:datepicker:sunday
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
workzone:datepicker:thursday
workzone:datepicker:thursday
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
workzone:datepicker:tuesday
workzone:datepicker:tuesday
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
workzone:datepicker:wednesday
workzone:datepicker:wednesday
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
-
- workzone:feedback:expiration
- workzone:feedback:expiration
- prod/WorkZone/Basket.html.twig
- prod/Tooltip/Basket.html.twig
+
+ workzone:feedback:expiration-closed
+ workzone:feedback:expiration-closed
+ prod/WorkZone/Basket.html.twig
+ prod/Tooltip/Basket.html.twig
+
+
+ workzone:feedback:expiration-open
+ workzone:feedback:expiration-open
+ prod/WorkZone/Basket.html.twig
+ prod/Tooltip/Basket.html.twig
workzone:feedback:update
workzone:feedback:update
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
yes
diff --git a/resources/locales/messages.en.xlf b/resources/locales/messages.en.xlf
index 301050bb79..4b5369e3e6 100644
--- a/resources/locales/messages.en.xlf
+++ b/resources/locales/messages.en.xlf
@@ -1,6 +1,6 @@
-
+
The source node in most cases contains the sample message as written by the developer. If it looks like a dot-delimitted string such as "form.label.firstname", then the developer has not provided a default message.
@@ -192,8 +192,9 @@
%nb_records% records
%nb_records% records
+ prod/WorkZone/Basket.html.twig
prod/Tooltip/Story.html.twig
- prod/Tooltip/Basket.html.twig
+ prod/Tooltip/Basket.html.twig
%nb_view% vue
@@ -1690,7 +1691,7 @@
Certaines donnees du panier ont change
This basket has been updated
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
Certaines donnees du reportage ont change
@@ -3168,6 +3169,11 @@
Executables setting
Form/Configuration/MainConfigurationFormType.php
+
+ Expiration date successfully updated!
+ Expiration date successfully updated!
+ Controller/Prod/PushController.php
+
Export
Export
@@ -6688,6 +6694,11 @@
The following errors have been detected
user/import/view.html.twig
+
+ The provided date is null!
+ The provided date is null!
+ Controller/Prod/PushController.php
+
The publication has been stopped
The publication has been stopped.
@@ -7049,6 +7060,11 @@
Controller/Root/LoginController.php
Controller/Api/OAuth2Controller.php
+
+ Unable to save the expiration date
+ Unable to save the expiration date
+ Controller/Prod/PushController.php
+
Unable to send the documents
Unable to send the documents
@@ -11721,10 +11737,16 @@ It is possible to place several search areas
Delete Selection
prod/actions/Push.html.twig
-
+
prod:workzone:basket:creation-date
Creation
- prod/Tooltip/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
+ prod/Tooltip/Basket.html.twig
+
+
+ prod:workzone:basket:updated-message
+ prod:workzone:basket:updated-message
+ prod/WorkZone/Basket.html.twig
prod:workzone:facetstab:search_and_facets_sort_options
@@ -11832,7 +11854,7 @@ It is possible to place several search areas
Refresh
prod/WorkZone/Story.html.twig
prod/WorkZone/Macros.html.twig
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
prod/results/feeds.html.twig
prod/results/feeds.html.twig
@@ -13403,128 +13425,134 @@ It is possible to place several search areas
workzone:datepicker:april
April
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
workzone:datepicker:august
August
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
-
+
workzone:datepicker:closeText
- workzone:datepicker:closeText
- prod/WorkZone/Basket.html.twig
+ Close
+ prod/WorkZone/Basket.html.twig
-
+
workzone:datepicker:currentText
- workzone:datepicker:currentText
- prod/WorkZone/Basket.html.twig
+ Current
+ prod/WorkZone/Basket.html.twig
workzone:datepicker:december
December
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
workzone:datepicker:february
February
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
workzone:datepicker:friday
Friday
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
workzone:datepicker:january
January
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
workzone:datepicker:july
July
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
workzone:datepicker:june
June
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
workzone:datepicker:march
March
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
workzone:datepicker:may
May
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
workzone:datepicker:monday
Monday
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
-
+
workzone:datepicker:nextText
Next
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
workzone:datepicker:november
November
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
workzone:datepicker:october
October
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
-
+
workzone:datepicker:prevText
Previous
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
workzone:datepicker:saturday
Saturday
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
workzone:datepicker:september
September
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
workzone:datepicker:sunday
Sunday
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
workzone:datepicker:thursday
Thursday
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
workzone:datepicker:tuesday
Tuesday
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
workzone:datepicker:wednesday
Wednesday
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
-
- workzone:feedback:expiration
- Feedback open Until
- prod/WorkZone/Basket.html.twig
- prod/Tooltip/Basket.html.twig
+
+ workzone:feedback:expiration-closed
+ workzone:feedback:expiration-closed
+ prod/WorkZone/Basket.html.twig
+ prod/Tooltip/Basket.html.twig
-
+
+ workzone:feedback:expiration-open
+ Feedback open until
+ prod/WorkZone/Basket.html.twig
+ prod/Tooltip/Basket.html.twig
+
+
workzone:feedback:update
Update Date
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
yes
diff --git a/resources/locales/messages.fr.xlf b/resources/locales/messages.fr.xlf
index a8166562ca..d830633044 100644
--- a/resources/locales/messages.fr.xlf
+++ b/resources/locales/messages.fr.xlf
@@ -1,6 +1,6 @@
-
+
The source node in most cases contains the sample message as written by the developer. If it looks like a dot-delimitted string such as "form.label.firstname", then the developer has not provided a default message.
@@ -192,8 +192,9 @@
%nb_records% records
%nb_records% enregistrement(s)
+ prod/WorkZone/Basket.html.twig
prod/Tooltip/Story.html.twig
- prod/Tooltip/Basket.html.twig
+ prod/Tooltip/Basket.html.twig
%nb_view% vue
@@ -1689,7 +1690,7 @@
Certaines donnees du panier ont change
Certaines données du panier ont changé
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
Certaines donnees du reportage ont change
@@ -3165,6 +3166,11 @@
Paramètres d'exécutables
Form/Configuration/MainConfigurationFormType.php
+
+ Expiration date successfully updated!
+ Expiration date successfully updated!
+ Controller/Prod/PushController.php
+
Export
Exporter
@@ -6687,6 +6693,11 @@ Pour les utilisateurs authentifiés, la demande de validation est également dis
Les erreurs suivantes ont été détectées.
user/import/view.html.twig
+
+ The provided date is null!
+ The provided date is null!
+ Controller/Prod/PushController.php
+
The publication has been stopped
La publication a été suspendue
@@ -7048,6 +7059,11 @@ Pour les utilisateurs authentifiés, la demande de validation est également dis
Controller/Root/LoginController.php
Controller/Api/OAuth2Controller.php
+
+ Unable to save the expiration date
+ Unable to save the expiration date
+ Controller/Prod/PushController.php
+
Unable to send the documents
Impossible d'envoyer les documents
@@ -11724,10 +11740,16 @@ Si vous recevez cet e-mail sans l'avoir sollicité, merci de l'ignorer ou de le
Supprimer la selection
prod/actions/Push.html.twig
-
+
prod:workzone:basket:creation-date
Date de création
- prod/Tooltip/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
+ prod/Tooltip/Basket.html.twig
+
+
+ prod:workzone:basket:updated-message
+ prod:workzone:basket:updated-message
+ prod/WorkZone/Basket.html.twig
prod:workzone:facetstab:search_and_facets_sort_options
@@ -11835,7 +11857,7 @@ Si vous recevez cet e-mail sans l'avoir sollicité, merci de l'ignorer ou de le
Rafraîchir
prod/WorkZone/Story.html.twig
prod/WorkZone/Macros.html.twig
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
prod/results/feeds.html.twig
prod/results/feeds.html.twig
@@ -13406,128 +13428,134 @@ Si vous recevez cet e-mail sans l'avoir sollicité, merci de l'ignorer ou de le
workzone:datepicker:april
Avril
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
workzone:datepicker:august
Aout
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
-
+
workzone:datepicker:closeText
Clore
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
-
+
workzone:datepicker:currentText
courant
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
workzone:datepicker:december
Décembre
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
workzone:datepicker:february
Février
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
workzone:datepicker:friday
Vendredi
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
workzone:datepicker:january
Janvier
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
workzone:datepicker:july
Juillet
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
workzone:datepicker:june
Juin
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
workzone:datepicker:march
Mars
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
workzone:datepicker:may
Mai
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
workzone:datepicker:monday
Lundi
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
-
+
workzone:datepicker:nextText
Suivant
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
workzone:datepicker:november
Novembre
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
workzone:datepicker:october
Octobre
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
-
+
workzone:datepicker:prevText
Précédent
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
workzone:datepicker:saturday
Samedi
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
workzone:datepicker:september
Septembre
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
workzone:datepicker:sunday
Dimanche
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
workzone:datepicker:thursday
Jeudi
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
workzone:datepicker:tuesday
Mardi
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
workzone:datepicker:wednesday
Mercredi
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
-
- workzone:feedback:expiration
- Validation ouverte jusqu'au
- prod/WorkZone/Basket.html.twig
- prod/Tooltip/Basket.html.twig
+
+ workzone:feedback:expiration-closed
+ Validation close depuis
+ prod/WorkZone/Basket.html.twig
+ prod/Tooltip/Basket.html.twig
-
+
+ workzone:feedback:expiration-open
+ validation ouverte jusqu'au
+ prod/WorkZone/Basket.html.twig
+ prod/Tooltip/Basket.html.twig
+
+
workzone:feedback:update
Valider
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
yes
diff --git a/resources/locales/messages.nl.xlf b/resources/locales/messages.nl.xlf
index c61524de6a..d5fff61be3 100644
--- a/resources/locales/messages.nl.xlf
+++ b/resources/locales/messages.nl.xlf
@@ -1,6 +1,6 @@
-
+
The source node in most cases contains the sample message as written by the developer. If it looks like a dot-delimitted string such as "form.label.firstname", then the developer has not provided a default message.
@@ -196,8 +196,9 @@
%nb_records% records
%nb_records% records
+ prod/WorkZone/Basket.html.twig
prod/Tooltip/Story.html.twig
- prod/Tooltip/Basket.html.twig
+ prod/Tooltip/Basket.html.twig
%nb_view% vue
@@ -1694,7 +1695,7 @@
Certaines donnees du panier ont change
Sommige gegevens in het mandje zijn veranderd
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
Certaines donnees du reportage ont change
@@ -3175,6 +3176,11 @@
Executables instellingen
Form/Configuration/MainConfigurationFormType.php
+
+ Expiration date successfully updated!
+ Expiration date successfully updated!
+ Controller/Prod/PushController.php
+
Export
Exporteer
@@ -6695,6 +6701,11 @@
De volgende fouten werden opgemerkt
user/import/view.html.twig
+
+ The provided date is null!
+ The provided date is null!
+ Controller/Prod/PushController.php
+
The publication has been stopped
Het programma is gestopt
@@ -7056,6 +7067,11 @@
Controller/Root/LoginController.php
Controller/Api/OAuth2Controller.php
+
+ Unable to save the expiration date
+ Unable to save the expiration date
+ Controller/Prod/PushController.php
+
Unable to send the documents
Documenten kunnen niet worden verstuurd
@@ -11728,7 +11744,13 @@
prod:workzone:basket:creation-date
prod:workzone:basket:creation-date
- prod/Tooltip/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
+ prod/Tooltip/Basket.html.twig
+
+
+ prod:workzone:basket:updated-message
+ prod:workzone:basket:updated-message
+ prod/WorkZone/Basket.html.twig
prod:workzone:facetstab:search_and_facets_sort_options
@@ -11836,7 +11858,7 @@
vernieuwen
prod/WorkZone/Story.html.twig
prod/WorkZone/Macros.html.twig
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
prod/results/feeds.html.twig
prod/results/feeds.html.twig
@@ -13407,128 +13429,134 @@
workzone:datepicker:april
workzone:datepicker:april
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
workzone:datepicker:august
workzone:datepicker:august
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
workzone:datepicker:closeText
workzone:datepicker:closeText
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
workzone:datepicker:currentText
workzone:datepicker:currentText
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
workzone:datepicker:december
workzone:datepicker:december
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
workzone:datepicker:february
workzone:datepicker:february
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
workzone:datepicker:friday
workzone:datepicker:friday
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
workzone:datepicker:january
workzone:datepicker:january
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
workzone:datepicker:july
workzone:datepicker:july
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
workzone:datepicker:june
workzone:datepicker:june
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
workzone:datepicker:march
workzone:datepicker:march
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
workzone:datepicker:may
workzone:datepicker:may
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
workzone:datepicker:monday
workzone:datepicker:monday
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
workzone:datepicker:nextText
workzone:datepicker:nextText
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
workzone:datepicker:november
workzone:datepicker:november
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
workzone:datepicker:october
workzone:datepicker:october
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
workzone:datepicker:prevText
workzone:datepicker:prevText
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
workzone:datepicker:saturday
workzone:datepicker:saturday
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
workzone:datepicker:september
workzone:datepicker:september
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
workzone:datepicker:sunday
workzone:datepicker:sunday
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
workzone:datepicker:thursday
workzone:datepicker:thursday
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
workzone:datepicker:tuesday
workzone:datepicker:tuesday
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
workzone:datepicker:wednesday
workzone:datepicker:wednesday
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
-
- workzone:feedback:expiration
- workzone:feedback:expiration
- prod/WorkZone/Basket.html.twig
- prod/Tooltip/Basket.html.twig
+
+ workzone:feedback:expiration-closed
+ workzone:feedback:expiration-closed
+ prod/WorkZone/Basket.html.twig
+ prod/Tooltip/Basket.html.twig
+
+
+ workzone:feedback:expiration-open
+ workzone:feedback:expiration-open
+ prod/WorkZone/Basket.html.twig
+ prod/Tooltip/Basket.html.twig
workzone:feedback:update
workzone:feedback:update
- prod/WorkZone/Basket.html.twig
+ prod/WorkZone/Basket.html.twig
yes
diff --git a/resources/locales/validators.de.xlf b/resources/locales/validators.de.xlf
index b7398ca514..4797cbcafd 100644
--- a/resources/locales/validators.de.xlf
+++ b/resources/locales/validators.de.xlf
@@ -1,6 +1,6 @@
-
+
The source node in most cases contains the sample message as written by the developer. If it looks like a dot-delimitted string such as "form.label.firstname", then the developer has not provided a default message.
diff --git a/resources/locales/validators.en.xlf b/resources/locales/validators.en.xlf
index f8d0a54e1d..398448e7d5 100644
--- a/resources/locales/validators.en.xlf
+++ b/resources/locales/validators.en.xlf
@@ -1,6 +1,6 @@
-
+
The source node in most cases contains the sample message as written by the developer. If it looks like a dot-delimitted string such as "form.label.firstname", then the developer has not provided a default message.
diff --git a/resources/locales/validators.fr.xlf b/resources/locales/validators.fr.xlf
index 6f350ddd76..8d7c716ebd 100644
--- a/resources/locales/validators.fr.xlf
+++ b/resources/locales/validators.fr.xlf
@@ -1,6 +1,6 @@
-
+
The source node in most cases contains the sample message as written by the developer. If it looks like a dot-delimitted string such as "form.label.firstname", then the developer has not provided a default message.
diff --git a/resources/locales/validators.nl.xlf b/resources/locales/validators.nl.xlf
index 2218c1ff00..0f8bf37ddc 100644
--- a/resources/locales/validators.nl.xlf
+++ b/resources/locales/validators.nl.xlf
@@ -1,6 +1,6 @@
-
+
The source node in most cases contains the sample message as written by the developer. If it looks like a dot-delimitted string such as "form.label.firstname", then the developer has not provided a default message.
diff --git a/templates/mobile/common/index.html.twig b/templates/mobile/common/index.html.twig
index 32a08cc094..c5b68592da 100644
--- a/templates/mobile/common/index.html.twig
+++ b/templates/mobile/common/index.html.twig
@@ -24,5 +24,6 @@
{% block content %}{% endblock %}
{% include 'common/analytics.html.twig' %}
+ {% include 'common/matomo_analytics.html.twig' %}