{{ 'export::mail: destinataire' | trans }}
@@ -189,10 +205,10 @@
{% set my_email = app.getAuthenticatedUser().getEmail() %}
{% if my_email != '' %}
-
+
{% else %}
+
From ebe915326beefacf13fb92b30b2867ff2e192a53 Mon Sep 17 00:00:00 2001
From: Aina Sitraka <35221835+aynsix@users.noreply.github.com>
Date: Mon, 30 Oct 2023 18:14:22 +0300
Subject: [PATCH 04/10] PHRAS-3900 Check TLS version use for email SMTP sending
- TLS 1.0 of 1.1 deprecation (#4382)
* swift tls 1.2
* add tls 1.1 and 1.2 option
* tlsv1.2 by default
---
lib/Alchemy/Phrasea/Application.php | 21 ++++++++++++++++---
.../Configuration/RegistryFormManipulator.php | 2 +-
.../Form/Configuration/EmailFormType.php | 2 +-
3 files changed, 20 insertions(+), 5 deletions(-)
diff --git a/lib/Alchemy/Phrasea/Application.php b/lib/Alchemy/Phrasea/Application.php
index 4906435e4f..7eab7c6592 100644
--- a/lib/Alchemy/Phrasea/Application.php
+++ b/lib/Alchemy/Phrasea/Application.php
@@ -687,9 +687,20 @@ private function setupSwiftMailer()
);
$encryption = null;
-
- if (in_array($app['conf']->get(['registry', 'email', 'smtp-secure-mode']), ['ssl', 'tls'])) {
- $encryption = $app['conf']->get(['registry', 'email', 'smtp-secure-mode']);
+ $secureMode = '';
+
+ if (in_array($app['conf']->get(['registry', 'email', 'smtp-secure-mode']), ['ssl', 'tls', 'tlsv1.1', 'tlsv1.2'])) {
+ $secureMode = $app['conf']->get(['registry', 'email', 'smtp-secure-mode']);
+
+ if ($secureMode == 'ssl') {
+ $encryption = 'ssl';
+ } else {
+ $encryption = 'tls';
+ if ($secureMode == 'tls') {
+ // by default use tlsv1.2
+ $secureMode = 'tlsv1.2';
+ }
+ }
}
$options = $app['swiftmailer.options'] = array_replace([
@@ -706,6 +717,10 @@ private function setupSwiftMailer()
// tls or ssl
$transport->setEncryption($options['encryption']);
+ if ($options['encryption'] == 'tls') {
+ $transport->setStreamOptions(['ssl' =>[$secureMode => true]]);
+ }
+
if ($app['conf']->get(['registry', 'email', 'smtp-auth-enabled'])) {
$transport->setUsername($options['username']);
$transport->setPassword($options['password']);
diff --git a/lib/Alchemy/Phrasea/Core/Configuration/RegistryFormManipulator.php b/lib/Alchemy/Phrasea/Core/Configuration/RegistryFormManipulator.php
index f832bbbdc0..b9b50172ef 100644
--- a/lib/Alchemy/Phrasea/Core/Configuration/RegistryFormManipulator.php
+++ b/lib/Alchemy/Phrasea/Core/Configuration/RegistryFormManipulator.php
@@ -178,7 +178,7 @@ private function getDefaultData(array $config)
'smtp-auth-enabled' => false,
'smtp-host' => null,
'smtp-port' => null,
- 'smtp-secure-mode' => 'tls',
+ 'smtp-secure-mode' => 'tlsv1.2',
'smtp-user' => null,
'smtp-password' => isset($config['email']['smtp-password']) ? $config['email']['smtp-password'] : null,
],
diff --git a/lib/Alchemy/Phrasea/Form/Configuration/EmailFormType.php b/lib/Alchemy/Phrasea/Form/Configuration/EmailFormType.php
index 37eadd1478..468b4ffb00 100644
--- a/lib/Alchemy/Phrasea/Form/Configuration/EmailFormType.php
+++ b/lib/Alchemy/Phrasea/Form/Configuration/EmailFormType.php
@@ -42,7 +42,7 @@ public function buildForm(FormBuilderInterface $builder, array $options)
]);
$builder->add('smtp-secure-mode', ChoiceType::class, [
'label' => 'SMTP encryption',
- 'choices' => ['none' => 'None', 'ssl' => 'SSL', 'tls' => 'TLS'],
+ 'choices' => ['none' => 'None', 'ssl' => 'SSL', 'tlsv1.1' => 'TLSV1.1', 'tlsv1.2' => 'TLSV1.2'],
]);
$builder->add('smtp-user', TextType::class, [
'label' => 'SMTP user',
From 179431d1b5fedd984d97ef6c2f8ed3317f3c32ec Mon Sep 17 00:00:00 2001
From: jygaulier
Date: Mon, 30 Oct 2023 17:15:04 +0100
Subject: [PATCH 05/10] PHRAS-3931_phraseanet_local_id_in_api (#4400)
* add "resource_id" to record / story ; formula defined into InstanceIdAware.php
* fix tests
* fix tests
* switch to main/instance_id
---
.../Controller/Api/InstanceIdAware.php | 31 +++++++++++++++++++
.../Phrasea/Controller/Api/V1Controller.php | 29 +++++++++--------
.../Controller/Api/V3/V3ResultHelpers.php | 4 +++
.../Controller/Api/V3/V3SearchController.php | 4 ++-
.../Phrasea/ControllerProvider/Api/V1.php | 6 +++-
.../Phrasea/ControllerProvider/Api/V3.php | 8 +++--
.../Phrasea/Search/RecordTransformer.php | 17 +++++++++-
.../Phrasea/Search/StoryTransformer.php | 1 +
8 files changed, 81 insertions(+), 19 deletions(-)
create mode 100644 lib/Alchemy/Phrasea/Controller/Api/InstanceIdAware.php
diff --git a/lib/Alchemy/Phrasea/Controller/Api/InstanceIdAware.php b/lib/Alchemy/Phrasea/Controller/Api/InstanceIdAware.php
new file mode 100644
index 0000000000..6d93a27921
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Controller/Api/InstanceIdAware.php
@@ -0,0 +1,31 @@
+instanceId = $conf->get(
+ ['main', 'instance_id'],
+ md5($conf->get(['main', 'key'], ''))
+ );
+
+ return $this;
+ }
+
+ public function getResourceIdResolver()
+ {
+ return function(\record_adapter $record): string {
+ return $this->instanceId . '_' . $record->getDataboxId() . '_' . $record->getRecordId();
+ };
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Controller/Api/V1Controller.php b/lib/Alchemy/Phrasea/Controller/Api/V1Controller.php
index 4b2737e01d..728cb743a3 100644
--- a/lib/Alchemy/Phrasea/Controller/Api/V1Controller.php
+++ b/lib/Alchemy/Phrasea/Controller/Api/V1Controller.php
@@ -113,6 +113,7 @@ class V1Controller extends Controller
use DispatcherAware;
use FilesystemAware;
use JsonBodyAware;
+ use InstanceIdAware;
const OBJECT_TYPE_USER = 'http://api.phraseanet.com/api/objects/user';
const OBJECT_TYPE_STORY = 'http://api.phraseanet.com/api/objects/story';
@@ -1216,7 +1217,7 @@ public function searchAction(Request $request)
{
$subdefTransformer = new SubdefTransformer($this->app['acl'], $this->getAuthenticatedUser(), new PermalinkTransformer());
$technicalDataTransformer = new TechnicalDataTransformer();
- $recordTransformer = new RecordTransformer($subdefTransformer, $technicalDataTransformer);
+ $recordTransformer = new RecordTransformer($subdefTransformer, $technicalDataTransformer, $this->getResourceIdResolver());
$storyTransformer = new StoryTransformer($subdefTransformer, $recordTransformer);
$compositeTransformer = new V1SearchCompositeResultTransformer($recordTransformer, $storyTransformer);
$searchTransformer = new V1SearchResultTransformer($compositeTransformer);
@@ -1275,7 +1276,7 @@ public function searchRecordsAction(Request $request)
{
$subdefTransformer = new SubdefTransformer($this->app['acl'], $this->getAuthenticatedUser(), new PermalinkTransformer());
$technicalDataTransformer = new TechnicalDataTransformer();
- $recordTransformer = new RecordTransformer($subdefTransformer, $technicalDataTransformer);
+ $recordTransformer = new RecordTransformer($subdefTransformer, $technicalDataTransformer, $this->getResourceIdResolver());
$searchTransformer = new V1SearchRecordsResultTransformer($recordTransformer);
$transformerResolver = new SearchResultTransformerResolver([
@@ -1667,6 +1668,7 @@ private function listRecord(Request $request, record_adapter $record)
$data = [
'databox_id' => $record->getDataboxId(),
'record_id' => $record->getRecordId(),
+ 'resource_id' => ($this->getResourceIdResolver())($record),
'mime_type' => $record->getMimeType(),
'title' => $record->get_title(['encode'=> record_adapter::ENCODE_NONE]),
'original_name' => $record->get_original_name(),
@@ -1721,17 +1723,18 @@ private function listStory(Request $request, record_adapter $story)
};
return [
- '@entity@' => self::OBJECT_TYPE_STORY,
- 'databox_id' => $story->getDataboxId(),
- 'story_id' => $story->getRecordId(),
+ '@entity@' => self::OBJECT_TYPE_STORY,
+ 'databox_id' => $story->getDataboxId(),
+ 'story_id' => $story->getRecordId(),
+ 'resource_id' => ($this->getResourceIdResolver())($story),
'cover_record_id' => $story->getCoverRecordId(),
- 'updated_on' => $story->getUpdated()->format(DATE_ATOM),
- 'created_on' => $story->getCreated()->format(DATE_ATOM),
- 'collection_id' => $story->getCollectionId(),
- 'base_id' => $story->getBaseId(),
- 'thumbnail' => $this->listEmbeddableMedia($request, $story, $story->get_thumbnail()),
- 'uuid' => $story->getUuid(),
- 'metadatas' => [
+ 'updated_on' => $story->getUpdated()->format(DATE_ATOM),
+ 'created_on' => $story->getCreated()->format(DATE_ATOM),
+ 'collection_id' => $story->getCollectionId(),
+ 'base_id' => $story->getBaseId(),
+ 'thumbnail' => $this->listEmbeddableMedia($request, $story, $story->get_thumbnail()),
+ 'uuid' => $story->getUuid(),
+ 'metadatas' => [
'@entity@' => self::OBJECT_TYPE_STORY_METADATA_BAG,
'dc:contributor' => $format($caption, \databox_Field_DCESAbstract::Contributor),
'dc:coverage' => $format($caption, \databox_Field_DCESAbstract::Coverage),
@@ -1749,7 +1752,7 @@ private function listStory(Request $request, record_adapter $story)
'dc:title' => $format($caption, \databox_Field_DCESAbstract::Title),
'dc:type' => $format($caption, \databox_Field_DCESAbstract::Type),
],
- 'records' => $this->listRecords($request, array_values($story->getChildren()->get_elements())),
+ 'records' => $this->listRecords($request, array_values($story->getChildren()->get_elements())),
];
}
diff --git a/lib/Alchemy/Phrasea/Controller/Api/V3/V3ResultHelpers.php b/lib/Alchemy/Phrasea/Controller/Api/V3/V3ResultHelpers.php
index 1a4849fc22..f2cc28e482 100644
--- a/lib/Alchemy/Phrasea/Controller/Api/V3/V3ResultHelpers.php
+++ b/lib/Alchemy/Phrasea/Controller/Api/V3/V3ResultHelpers.php
@@ -5,6 +5,7 @@
use ACL;
use Alchemy\Phrasea\Authentication\Authenticator;
+use Alchemy\Phrasea\Controller\Api\InstanceIdAware;
use Alchemy\Phrasea\Core\Configuration\PropertyAccess;
use Alchemy\Phrasea\Media\MediaSubDefinitionUrlGenerator;
use databox_status;
@@ -17,6 +18,8 @@
class V3ResultHelpers
{
+ use InstanceIdAware;
+
/** @var PropertyAccess */
private $conf;
@@ -151,6 +154,7 @@ public function listRecord(Request $request, record_adapter $record, ACL $aclfor
$data = [
'databox_id' => $record->getDataboxId(),
'record_id' => $record->getRecordId(),
+ 'resource_id' => ($this->getResourceIdResolver())($record),
'mime_type' => $record->getMimeType(),
'title' => $record->get_title(['encode'=> record_adapter::ENCODE_NONE]),
'original_name' => $record->get_original_name(),
diff --git a/lib/Alchemy/Phrasea/Controller/Api/V3/V3SearchController.php b/lib/Alchemy/Phrasea/Controller/Api/V3/V3SearchController.php
index c16b13fa82..6cfd6bd682 100644
--- a/lib/Alchemy/Phrasea/Controller/Api/V3/V3SearchController.php
+++ b/lib/Alchemy/Phrasea/Controller/Api/V3/V3SearchController.php
@@ -5,6 +5,7 @@
use Alchemy\Phrasea\Application\Helper\DispatcherAware;
use Alchemy\Phrasea\Application\Helper\JsonBodyAware;
use Alchemy\Phrasea\Collection\Reference\CollectionReference;
+use Alchemy\Phrasea\Controller\Api\InstanceIdAware;
use Alchemy\Phrasea\Controller\Api\Result;
use Alchemy\Phrasea\Controller\Controller;
use Alchemy\Phrasea\Databox\DataboxGroupable;
@@ -51,6 +52,7 @@ class V3SearchController extends Controller
{
use JsonBodyAware;
use DispatcherAware;
+ use InstanceIdAware;
/**
* Search for results
@@ -65,7 +67,7 @@ public function searchAction(Request $request)
$subdefTransformer = new SubdefTransformer($this->app['acl'], $this->getAuthenticatedUser(), new PermalinkTransformer());
$technicalDataTransformer = new TechnicalDataTransformer();
- $recordTransformer = new RecordTransformer($subdefTransformer, $technicalDataTransformer);
+ $recordTransformer = new RecordTransformer($subdefTransformer, $technicalDataTransformer, $this->getResourceIdResolver());
$storyTransformer = new V3StoryTransformer($recordTransformer);
$compositeTransformer = new V3SearchCompositeResultTransformer($recordTransformer, $storyTransformer);
$searchTransformer = new V3SearchResultTransformer($compositeTransformer);
diff --git a/lib/Alchemy/Phrasea/ControllerProvider/Api/V1.php b/lib/Alchemy/Phrasea/ControllerProvider/Api/V1.php
index bdc7a8cd50..a7cc39aa8e 100644
--- a/lib/Alchemy/Phrasea/ControllerProvider/Api/V1.php
+++ b/lib/Alchemy/Phrasea/ControllerProvider/Api/V1.php
@@ -38,7 +38,9 @@ public function register(Application $app)
->setDataboxLoggerLocator($app['phraseanet.logger'])
->setDispatcher($app['dispatcher'])
->setFileSystemLocator(new LazyLocator($app, 'filesystem'))
- ->setJsonBodyHelper(new LazyLocator($app, 'json.body_helper'));
+ ->setJsonBodyHelper(new LazyLocator($app, 'json.body_helper'))
+ ->setInstanceId($app['conf'])
+ ;
});
}
@@ -186,6 +188,7 @@ public function connect(Application $app)
->assert('databox_id', '\d+')
->assert('record_id', '\d+');
+ /** @uses \Alchemy\Phrasea\Controller\Api\V1Controller::getRecordAction */
$controllers->get('/records/{databox_id}/{record_id}/', 'controller.api.v1:getRecordAction')
->before('controller.api.v1:ensureCanAccessToRecord')
->assert('databox_id', '\d+')
@@ -238,6 +241,7 @@ public function connect(Application $app)
->assert('record_id', '\d+');
$controllers->get('/stories/{any_id}/{anyother_id}/embed/', 'controller.api.v1:getBadRequestAction');
+ /** @uses \Alchemy\Phrasea\Controller\Api\V1Controller::getStoryAction */
$controllers->get('/stories/{databox_id}/{record_id}/', 'controller.api.v1:getStoryAction')
->before('controller.api.v1:ensureCanAccessToRecord')
->assert('databox_id', '\d+')
diff --git a/lib/Alchemy/Phrasea/ControllerProvider/Api/V3.php b/lib/Alchemy/Phrasea/ControllerProvider/Api/V3.php
index e7797eaf92..dafff86a2f 100644
--- a/lib/Alchemy/Phrasea/ControllerProvider/Api/V3.php
+++ b/lib/Alchemy/Phrasea/ControllerProvider/Api/V3.php
@@ -30,8 +30,8 @@ public function register(Application $app)
$app['conf'],
$app['media_accessor.subdef_url_generator'],
$app['authentication'],
- $app['url_generator']
- ));
+ $app['url_generator']))
+ ->setInstanceId($app['conf']);
});
$app['controller.api.v3.subdefs_service'] = $app->share(function (PhraseaApplication $app) {
return (new V3SubdefsServiceController($app))
@@ -46,7 +46,9 @@ public function register(Application $app)
;
});
$app['controller.api.v3.search'] = $app->share(function (PhraseaApplication $app) {
- return (new V3SearchController($app));
+ return (new V3SearchController($app))
+ ->setInstanceId($app['conf'])
+ ;
});
$app['controller.api.v3.searchraw'] = $app->share(function (PhraseaApplication $app) {
return (new V3SearchRawController($app));
diff --git a/lib/Alchemy/Phrasea/Search/RecordTransformer.php b/lib/Alchemy/Phrasea/Search/RecordTransformer.php
index 7dd6cd5a5e..9f6a715e93 100644
--- a/lib/Alchemy/Phrasea/Search/RecordTransformer.php
+++ b/lib/Alchemy/Phrasea/Search/RecordTransformer.php
@@ -41,10 +41,16 @@ class RecordTransformer extends TransformerAbstract
*/
private $technicalDataTransformer;
- public function __construct(SubdefTransformer $subdefTransformer, TechnicalDataTransformer $technicalDataTransformer)
+ /**
+ * @var callable
+ */
+ private $resourceIdResolver;
+
+ public function __construct(SubdefTransformer $subdefTransformer, TechnicalDataTransformer $technicalDataTransformer, callable $resourceIdResolver)
{
$this->subdefTransformer = $subdefTransformer;
$this->technicalDataTransformer = $technicalDataTransformer;
+ $this->resourceIdResolver = $resourceIdResolver;
}
public function transform($recordView)
@@ -55,6 +61,7 @@ public function transform($recordView)
return [
'databox_id' => $record->getDataboxId(),
'record_id' => $record->getRecordId(),
+ 'resource_id' => ($this->resourceIdResolver)($record),
'mime_type' => $record->getMimeType(),
'title' => $record->get_title(['encode'=> record_adapter::ENCODE_NONE]),
'original_name' => $record->get_original_name(),
@@ -160,4 +167,12 @@ public function includeCaption(RecordView $recordView)
];
});
}
+
+ /**
+ * @return callable
+ */
+ public function getResourceIdResolver(): callable
+ {
+ return $this->resourceIdResolver;
+ }
}
diff --git a/lib/Alchemy/Phrasea/Search/StoryTransformer.php b/lib/Alchemy/Phrasea/Search/StoryTransformer.php
index 17f67f0e64..4fb17274b6 100644
--- a/lib/Alchemy/Phrasea/Search/StoryTransformer.php
+++ b/lib/Alchemy/Phrasea/Search/StoryTransformer.php
@@ -53,6 +53,7 @@ public function transform(StoryView $storyView)
'@entity@' => 'http://api.phraseanet.com/api/objects/story',
'databox_id' => $story->getDataboxId(),
'story_id' => $story->getRecordId(),
+ 'resource_id' => ($this->recordTransformer->getResourceIdResolver())($story),
'cover_record_id' => $story->getCoverRecordId(),
'updated_on' => NullableDateTime::format($story->getUpdated()),
'created_on' => NullableDateTime::format($story->getUpdated()),
From bf76b30670f656d177f152f1ba1c6c7056fab30d Mon Sep 17 00:00:00 2001
From: Aina Sitraka <35221835+aynsix@users.noreply.github.com>
Date: Mon, 30 Oct 2023 19:35:00 +0300
Subject: [PATCH 06/10] PHRAS-3934 fix videotools subtitle timeline (#4398)
* fix videotools subtitle timeline digit issue
* fix videotools when text of subtitle clip is empty
* bump front version
---
Phraseanet-production-client/config/config.js | 2 +-
Phraseanet-production-client/dist/authenticate.js | 2 +-
.../dist/authenticate.min.js | 2 +-
Phraseanet-production-client/dist/commons.js | 2 +-
Phraseanet-production-client/dist/commons.min.js | 2 +-
Phraseanet-production-client/dist/production.js | 11 ++++++-----
Phraseanet-production-client/dist/production.min.js | 11 ++++++-----
.../record/videoEditor/videoSubtitleCapture.js | 13 +++++++------
lib/Alchemy/Phrasea/Twig/PhraseanetExtension.php | 2 +-
9 files changed, 25 insertions(+), 22 deletions(-)
diff --git a/Phraseanet-production-client/config/config.js b/Phraseanet-production-client/config/config.js
index 4d14f1e9e7..c3d24a407b 100644
--- a/Phraseanet-production-client/config/config.js
+++ b/Phraseanet-production-client/config/config.js
@@ -13,5 +13,5 @@ module.exports = {
setupDir: _root + 'tests/setup/node.js',
karmaConf: _root + 'config/karma.conf.js',
// change this version when you change JS file for lazy loading
- assetFileVersion: 96
+ assetFileVersion: 97
};
diff --git a/Phraseanet-production-client/dist/authenticate.js b/Phraseanet-production-client/dist/authenticate.js
index d0cca781fb..d084afdad9 100644
--- a/Phraseanet-production-client/dist/authenticate.js
+++ b/Phraseanet-production-client/dist/authenticate.js
@@ -96,7 +96,7 @@ return /******/ (function(modules) { // webpackBootstrap
/******/ if (__webpack_require__.nc) {
/******/ script.setAttribute("nonce", __webpack_require__.nc);
/******/ }
-/******/ script.src = __webpack_require__.p + "lazy-" + ({}[chunkId]||chunkId) + ".js?v=96";
+/******/ script.src = __webpack_require__.p + "lazy-" + ({}[chunkId]||chunkId) + ".js?v=97";
/******/ var timeout = setTimeout(onScriptComplete, 120000);
/******/ script.onerror = script.onload = onScriptComplete;
/******/ function onScriptComplete() {
diff --git a/Phraseanet-production-client/dist/authenticate.min.js b/Phraseanet-production-client/dist/authenticate.min.js
index 52711bcba4..7c597982c8 100644
--- a/Phraseanet-production-client/dist/authenticate.min.js
+++ b/Phraseanet-production-client/dist/authenticate.min.js
@@ -96,7 +96,7 @@ return /******/ (function(modules) { // webpackBootstrap
/******/ if (__webpack_require__.nc) {
/******/ script.setAttribute("nonce", __webpack_require__.nc);
/******/ }
-/******/ script.src = __webpack_require__.p + "lazy-" + ({}[chunkId]||chunkId) + ".min.js?v=96";
+/******/ script.src = __webpack_require__.p + "lazy-" + ({}[chunkId]||chunkId) + ".min.js?v=97";
/******/ var timeout = setTimeout(onScriptComplete, 120000);
/******/ script.onerror = script.onload = onScriptComplete;
/******/ function onScriptComplete() {
diff --git a/Phraseanet-production-client/dist/commons.js b/Phraseanet-production-client/dist/commons.js
index 4cc1a64f11..d55e728c02 100644
--- a/Phraseanet-production-client/dist/commons.js
+++ b/Phraseanet-production-client/dist/commons.js
@@ -91,7 +91,7 @@
/******/ if (__webpack_require__.nc) {
/******/ script.setAttribute("nonce", __webpack_require__.nc);
/******/ }
-/******/ script.src = __webpack_require__.p + "lazy-" + ({}[chunkId]||chunkId) + ".js?v=96";
+/******/ script.src = __webpack_require__.p + "lazy-" + ({}[chunkId]||chunkId) + ".js?v=97";
/******/ var timeout = setTimeout(onScriptComplete, 120000);
/******/ script.onerror = script.onload = onScriptComplete;
/******/ function onScriptComplete() {
diff --git a/Phraseanet-production-client/dist/commons.min.js b/Phraseanet-production-client/dist/commons.min.js
index 75d2c8e4f3..6f271a5ec8 100644
--- a/Phraseanet-production-client/dist/commons.min.js
+++ b/Phraseanet-production-client/dist/commons.min.js
@@ -91,7 +91,7 @@
/******/ if (__webpack_require__.nc) {
/******/ script.setAttribute("nonce", __webpack_require__.nc);
/******/ }
-/******/ script.src = __webpack_require__.p + "lazy-" + ({}[chunkId]||chunkId) + ".min.js?v=96";
+/******/ script.src = __webpack_require__.p + "lazy-" + ({}[chunkId]||chunkId) + ".min.js?v=97";
/******/ var timeout = setTimeout(onScriptComplete, 120000);
/******/ script.onerror = script.onload = onScriptComplete;
/******/ function onScriptComplete() {
diff --git a/Phraseanet-production-client/dist/production.js b/Phraseanet-production-client/dist/production.js
index f1d762def3..f4f94a4b83 100644
--- a/Phraseanet-production-client/dist/production.js
+++ b/Phraseanet-production-client/dist/production.js
@@ -64615,7 +64615,7 @@ var videoSubtitleCapture = function videoSubtitleCapture(services, datas) {
minutes = minutes < 10 ? "0" + minutes : minutes;
seconds = seconds < 10 ? "0" + seconds : seconds;
// if(isNaN(hours) && isNaN(minutes) && isNaN(seconds) && isNaN(milliseconds) ) {
- return hours + ":" + minutes + ":" + seconds + "." + milliseconds;
+ return hours + ":" + minutes + ":" + seconds + "." + ('000' + milliseconds).slice(-3);
//}
}
@@ -64652,11 +64652,14 @@ var videoSubtitleCapture = function videoSubtitleCapture(services, datas) {
var captionText = "WEBVTT - with cue identifier\n\n";
while (i <= countSubtitle * 3) {
j = j + 1;
- captionText += j + "\n" + allData[i].value + " --> " + allData[i + 1].value + "\n" + allData[i + 2].value + "\n\n";
+ // save only wich with value not empty
+ if (allData[i + 2].value.length != 0) {
+ captionText += j + "\n" + allData[i].value + " --> " + allData[i + 1].value + "\n" + allData[i + 2].value + "\n\n";
+ }
+
i = i + 3;
if (i == countSubtitle * 3 - 3) {
(0, _jquery2.default)('#record-vtt').val(captionText);
- console.log(captionText);
if (btn == 'save') {
//send data
_jquery2.default.ajax({
@@ -64761,7 +64764,6 @@ var videoSubtitleCapture = function videoSubtitleCapture(services, datas) {
ResValue = fieldvalue.split("WEBVTT - with cue identifier\n\n");
captionValue = ResValue[1].split("\n\n");
captionLength = captionValue.length;
- console.log(captionValue);
for (var i = 0; i <= captionLength - 1; i++) {
// Regex blank line
@@ -64863,7 +64865,6 @@ var videoSubtitleCapture = function videoSubtitleCapture(services, datas) {
try {
var requestData = (0, _jquery2.default)('#video-subtitle-request').serializeArray();
requestData = JSON.parse(JSON.stringify(requestData));
- console.log(requestData);
} catch (err) {
return;
}
diff --git a/Phraseanet-production-client/dist/production.min.js b/Phraseanet-production-client/dist/production.min.js
index f1d762def3..f4f94a4b83 100644
--- a/Phraseanet-production-client/dist/production.min.js
+++ b/Phraseanet-production-client/dist/production.min.js
@@ -64615,7 +64615,7 @@ var videoSubtitleCapture = function videoSubtitleCapture(services, datas) {
minutes = minutes < 10 ? "0" + minutes : minutes;
seconds = seconds < 10 ? "0" + seconds : seconds;
// if(isNaN(hours) && isNaN(minutes) && isNaN(seconds) && isNaN(milliseconds) ) {
- return hours + ":" + minutes + ":" + seconds + "." + milliseconds;
+ return hours + ":" + minutes + ":" + seconds + "." + ('000' + milliseconds).slice(-3);
//}
}
@@ -64652,11 +64652,14 @@ var videoSubtitleCapture = function videoSubtitleCapture(services, datas) {
var captionText = "WEBVTT - with cue identifier\n\n";
while (i <= countSubtitle * 3) {
j = j + 1;
- captionText += j + "\n" + allData[i].value + " --> " + allData[i + 1].value + "\n" + allData[i + 2].value + "\n\n";
+ // save only wich with value not empty
+ if (allData[i + 2].value.length != 0) {
+ captionText += j + "\n" + allData[i].value + " --> " + allData[i + 1].value + "\n" + allData[i + 2].value + "\n\n";
+ }
+
i = i + 3;
if (i == countSubtitle * 3 - 3) {
(0, _jquery2.default)('#record-vtt').val(captionText);
- console.log(captionText);
if (btn == 'save') {
//send data
_jquery2.default.ajax({
@@ -64761,7 +64764,6 @@ var videoSubtitleCapture = function videoSubtitleCapture(services, datas) {
ResValue = fieldvalue.split("WEBVTT - with cue identifier\n\n");
captionValue = ResValue[1].split("\n\n");
captionLength = captionValue.length;
- console.log(captionValue);
for (var i = 0; i <= captionLength - 1; i++) {
// Regex blank line
@@ -64863,7 +64865,6 @@ var videoSubtitleCapture = function videoSubtitleCapture(services, datas) {
try {
var requestData = (0, _jquery2.default)('#video-subtitle-request').serializeArray();
requestData = JSON.parse(JSON.stringify(requestData));
- console.log(requestData);
} catch (err) {
return;
}
diff --git a/Phraseanet-production-client/src/components/record/videoEditor/videoSubtitleCapture.js b/Phraseanet-production-client/src/components/record/videoEditor/videoSubtitleCapture.js
index 1e5c1d0a00..28d0b25364 100644
--- a/Phraseanet-production-client/src/components/record/videoEditor/videoSubtitleCapture.js
+++ b/Phraseanet-production-client/src/components/record/videoEditor/videoSubtitleCapture.js
@@ -134,7 +134,7 @@ const videoSubtitleCapture = (services, datas, activeTab = false) => {
minutes = (minutes < 10) ? "0" + minutes : minutes;
seconds = (seconds < 10) ? "0" + seconds : seconds;
// if(isNaN(hours) && isNaN(minutes) && isNaN(seconds) && isNaN(milliseconds) ) {
- return hours + ":" + minutes + ":" + seconds + "." + milliseconds;
+ return hours + ":" + minutes + ":" + seconds + "." + ('000' + milliseconds).slice(-3);
//}
}
@@ -172,11 +172,14 @@ const videoSubtitleCapture = (services, datas, activeTab = false) => {
var captionText = "WEBVTT - with cue identifier\n\n";
while (i <= countSubtitle * 3) {
j= j +1;
- captionText += j + "\n" + allData[i].value + " --> " + allData[i + 1].value + "\n" + allData[i + 2].value + "\n\n";
+ // save only wich with value not empty
+ if (allData[i + 2].value.length != 0) {
+ captionText += j + "\n" + allData[i].value + " --> " + allData[i + 1].value + "\n" + allData[i + 2].value + "\n\n";
+ }
+
i = i + 3;
if (i == (countSubtitle * 3) - 3) {
$('#record-vtt').val(captionText);
- console.log(captionText);
if (btn == 'save') {
//send data
$.ajax({
@@ -286,7 +289,6 @@ const videoSubtitleCapture = (services, datas, activeTab = false) => {
ResValue = fieldvalue.split("WEBVTT - with cue identifier\n\n");
captionValue = ResValue[1].split("\n\n");
captionLength = captionValue.length;
- console.log(captionValue);
for (var i = 0; i <= captionLength - 1; i++) {
// Regex blank line
@@ -317,7 +319,7 @@ const videoSubtitleCapture = (services, datas, activeTab = false) => {
startVal = stringToseconde(timeValue[0]);
//Re-Build EndTime
- timeValue = timeValue [1].split("\n")
+ timeValue = timeValue[1].split("\n")
$('.item_' + i + ' .video-subtitle-item ').closest('.editing').find('.end-label').text(timeValue[0]);
$('.item_' + i + ' .video-subtitle-item ').find('.endTime').val(timeValue[0]);
endVal = stringToseconde(timeValue[0]);
@@ -393,7 +395,6 @@ const videoSubtitleCapture = (services, datas, activeTab = false) => {
try {
var requestData = $('#video-subtitle-request').serializeArray();
requestData = JSON.parse(JSON.stringify(requestData));
- console.log(requestData)
} catch (err) {
return;
diff --git a/lib/Alchemy/Phrasea/Twig/PhraseanetExtension.php b/lib/Alchemy/Phrasea/Twig/PhraseanetExtension.php
index a2a2391d84..1f84f0c4ec 100644
--- a/lib/Alchemy/Phrasea/Twig/PhraseanetExtension.php
+++ b/lib/Alchemy/Phrasea/Twig/PhraseanetExtension.php
@@ -62,7 +62,7 @@ public function getGlobals()
{
return [
// change this version when you change JS file to force the navigation to reload js file
- 'assetFileVersion' => 96
+ 'assetFileVersion' => 97
];
}
From 23bb5382465bca368eee9f6de54909ee7c6d176f Mon Sep 17 00:00:00 2001
From: Aina Sitraka <35221835+aynsix@users.noreply.github.com>
Date: Mon, 30 Oct 2023 23:14:07 +0300
Subject: [PATCH 07/10] PHRAS-3935 : phraseanet_local_id became instance_id
(#4396)
* phraseanet_local_id patch
* display phraseanet_local_id
* phraseanet_local_id to instance_id
* update embed bumdle
---
composer.json | 2 +-
composer.lock | 14 +--
.../Controller/PSAdminController.php | 9 --
.../Controller/PSExposeController.php | 4 +-
lib/Alchemy/Phrasea/Setup/Installer.php | 3 +
.../Worker/ExposeUploadWorker.php | 10 +--
lib/classes/patch/418RC7PHRAS3935.php | 85 +++++++++++++++++++
.../admin/phraseanet-service/index.html.twig | 4 +
8 files changed, 107 insertions(+), 24 deletions(-)
create mode 100644 lib/classes/patch/418RC7PHRAS3935.php
diff --git a/composer.json b/composer.json
index cbfd54d9b0..4908038d4e 100644
--- a/composer.json
+++ b/composer.json
@@ -59,7 +59,7 @@
"php": ">=7.0 <7.1",
"ext-intl": "*",
"alchemy-fr/tcpdf-clone": "~6.0",
- "alchemy/embed-bundle": "^2.0.15",
+ "alchemy/embed-bundle": "^2.0.16",
"alchemy/geonames-api-consumer": "~0.1.0",
"alchemy/mediavorus": "^0.4.4",
"alchemy/oauth2php": "1.1.0",
diff --git a/composer.lock b/composer.lock
index 3e813cdb76..38a205b024 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": "9e7490439544a7184da9a64bd9f3012f",
+ "content-hash": "08ce5acbe4eab8cb20d1881545d60071",
"packages": [
{
"name": "alchemy-fr/tcpdf-clone",
@@ -131,16 +131,16 @@
},
{
"name": "alchemy/embed-bundle",
- "version": "2.0.15",
+ "version": "2.0.16",
"source": {
"type": "git",
"url": "https://github.com/alchemy-fr/embed-bundle.git",
- "reference": "835a6b0cfce6b966df1b73c3a651a742ebe9449a"
+ "reference": "81b67d90d1720f9ff49ee4033f6d1b61b56e4809"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/alchemy-fr/embed-bundle/zipball/835a6b0cfce6b966df1b73c3a651a742ebe9449a",
- "reference": "835a6b0cfce6b966df1b73c3a651a742ebe9449a",
+ "url": "https://api.github.com/repos/alchemy-fr/embed-bundle/zipball/81b67d90d1720f9ff49ee4033f6d1b61b56e4809",
+ "reference": "81b67d90d1720f9ff49ee4033f6d1b61b56e4809",
"shasum": ""
},
"require-dev": {
@@ -178,10 +178,10 @@
],
"description": "Embed resources bundle",
"support": {
- "source": "https://github.com/alchemy-fr/embed-bundle/tree/2.0.15",
+ "source": "https://github.com/alchemy-fr/embed-bundle/tree/2.0.16",
"issues": "https://github.com/alchemy-fr/embed-bundle/issues"
},
- "time": "2023-10-14T10:42:19+00:00"
+ "time": "2023-10-19T11:28:29+00:00"
},
{
"name": "alchemy/geonames-api-consumer",
diff --git a/lib/Alchemy/Phrasea/PhraseanetService/Controller/PSAdminController.php b/lib/Alchemy/Phrasea/PhraseanetService/Controller/PSAdminController.php
index 1220c8f14a..7188fb8d24 100644
--- a/lib/Alchemy/Phrasea/PhraseanetService/Controller/PSAdminController.php
+++ b/lib/Alchemy/Phrasea/PhraseanetService/Controller/PSAdminController.php
@@ -35,15 +35,6 @@ public function exposeAction(PhraseaApplication $app, Request $request)
if ($form->isValid()) {
$app['conf']->set(['phraseanet-service', 'expose-service'], $form->getData());
- // generate a uniq key between phraseanet service and the phraseanet instance if not exist
- if(!$app['conf']->has(['phraseanet-service', 'phraseanet_local_id'])) {
- $instanceKey = $this->app['conf']->get(['main', 'key']);
-
- $phraseanetLocalId = md5($instanceKey);
-
- $app['conf']->set(['phraseanet-service', 'phraseanet_local_id'], $phraseanetLocalId);
- }
-
return $app->redirectPath('ps_admin', ['_fragment'=>'expose']);
}
diff --git a/lib/Alchemy/Phrasea/PhraseanetService/Controller/PSExposeController.php b/lib/Alchemy/Phrasea/PhraseanetService/Controller/PSExposeController.php
index 63f4d25cac..7da8e5e3cf 100644
--- a/lib/Alchemy/Phrasea/PhraseanetService/Controller/PSExposeController.php
+++ b/lib/Alchemy/Phrasea/PhraseanetService/Controller/PSExposeController.php
@@ -1195,9 +1195,9 @@ private function getClientAnnotationProfile(Client $exposeClient, $exposeName, $
*/
private function getExposeMappingName($mappingContext)
{
- $phraseanetLocalId = $this->app['conf']->get(['phraseanet-service', 'phraseanet_local_id']);
+ $instanceId = $this->app['conf']->get(['main', 'instance_id']);
- return $phraseanetLocalId.'_'. $mappingContext . '_mapping';
+ return $instanceId . '_' . $mappingContext . '_mapping';
}
/**
diff --git a/lib/Alchemy/Phrasea/Setup/Installer.php b/lib/Alchemy/Phrasea/Setup/Installer.php
index a26cc9d3cc..be17854cc1 100644
--- a/lib/Alchemy/Phrasea/Setup/Installer.php
+++ b/lib/Alchemy/Phrasea/Setup/Installer.php
@@ -192,6 +192,9 @@ private function createConfigFile(Connection $abConn, $serverName, $binaryData,
$config['servername'] = $serverName;
$config['main']['key'] = $this->app['random.medium']->generateString(16);
+ // generate from the random instance key
+ $config['main']['instance_id'] = md5($config['main']['key']);
+
// define storage config
$defaultStoragePaths = [
'subdefs' => __DIR__ . '/../../../../datas',
diff --git a/lib/Alchemy/Phrasea/WorkerManager/Worker/ExposeUploadWorker.php b/lib/Alchemy/Phrasea/WorkerManager/Worker/ExposeUploadWorker.php
index bd72160259..5d417cff42 100644
--- a/lib/Alchemy/Phrasea/WorkerManager/Worker/ExposeUploadWorker.php
+++ b/lib/Alchemy/Phrasea/WorkerManager/Worker/ExposeUploadWorker.php
@@ -106,12 +106,12 @@ public function process(array $payload)
$helpers = new PhraseanetExtension($this->app);
// the identification of phraseanet instance in expose
- $phraseanetLocalId = $this->app['conf']->get(['phraseanet-service', 'phraseanet_local_id']);
+ $instanceId = $this->app['conf']->get(['main', 'instance_id']);
// get mapping if exist
$clientAnnotationProfile = $this->getClientAnnotationProfile($exposeClient, $payload['publicationId']);
- $exposeFieldMappingName = $phraseanetLocalId . '_field_mapping';
+ $exposeFieldMappingName = $instanceId . '_field_mapping';
$fieldMapping = !empty($clientAnnotationProfile[$exposeFieldMappingName]) ? $clientAnnotationProfile[$exposeFieldMappingName] : [];
$fieldListToUpload = !empty($fieldMapping['fields']) ? $fieldMapping['fields'] : [];
@@ -199,7 +199,7 @@ public function process(array $payload)
}
}
- $exposeSubdefMappingName = $phraseanetLocalId . '_subdef_mapping';
+ $exposeSubdefMappingName = $instanceId . '_subdef_mapping';
$actualSubdefMapping = !empty($clientAnnotationProfile[$exposeSubdefMappingName]) ? $clientAnnotationProfile[$exposeSubdefMappingName] : [];
$documentType = $record->getType();
@@ -217,8 +217,8 @@ public function process(array $payload)
unset($mapping[$phraseanetSubdefAsDocument]);
// this is the unique reference for record in phraseanet and assets in expose
- // phraseanetLocalKey_basedID_record_id
- $assetId = $phraseanetLocalId.'_'.$record->getId();
+ // instanceId_basedID_record_id
+ $assetId = $instanceId . '_' . $record->getId();
if ($record->has_subdef($phraseanetSubdefAsDocument) && $record->get_subdef($phraseanetSubdefAsDocument)->is_physically_present()) {
$requestBody = [
diff --git a/lib/classes/patch/418RC7PHRAS3935.php b/lib/classes/patch/418RC7PHRAS3935.php
new file mode 100644
index 0000000000..03e35445ed
--- /dev/null
+++ b/lib/classes/patch/418RC7PHRAS3935.php
@@ -0,0 +1,85 @@
+release;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getDoctrineMigrations()
+ {
+ return [];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function require_all_upgrades()
+ {
+ return false;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function concern()
+ {
+ return $this->concern;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function apply(base $base, Application $app)
+ {
+ if ($base->get_base_type() === base::DATA_BOX) {
+ $this->patch_databox($base, $app);
+ } elseif ($base->get_base_type() === base::APPLICATION_BOX) {
+ $this->patch_appbox($base, $app);
+ }
+
+ return true;
+ }
+
+ private function patch_databox(databox $databox, Application $app)
+ {
+ }
+
+ private function patch_appbox(base $appbox, Application $app)
+ {
+ /** @var PropertyAccess $conf */
+ $conf = $app['conf'];
+
+ if (!$conf->has(['main', 'instance_id'])) {
+ if ($conf->has(['phraseanet-service', 'phraseanet_local_id'])) {
+ // get phraseanet_local_id if exist
+ $conf->set(['main', 'instance_id'], $conf->get(['phraseanet-service', 'phraseanet_local_id']));
+ $conf->remove(['phraseanet-service', 'phraseanet_local_id']);
+ } else {
+ // instance key is already a random value
+ $instanceKey = $conf->get(['main', 'key']);
+
+ $instanceId = md5($instanceKey);
+
+ $conf->set(['main', 'instance_id'], $instanceId);
+ }
+ }
+ }
+}
diff --git a/templates/web/admin/phraseanet-service/index.html.twig b/templates/web/admin/phraseanet-service/index.html.twig
index 4aa829dea3..dc98032498 100644
--- a/templates/web/admin/phraseanet-service/index.html.twig
+++ b/templates/web/admin/phraseanet-service/index.html.twig
@@ -1,5 +1,9 @@
{{ 'admin:phrasea-service-setting:tab-title:: Page title' | trans }}
+
+
+
+
- {{ 'admin:phrasea-service-setting:tab:: Auth' | trans }}
From 65732343eeea585af9295dadefa965d917b37138 Mon Sep 17 00:00:00 2001
From: jygaulier
Date: Tue, 31 Oct 2023 15:52:45 +0100
Subject: [PATCH 08/10] PHRAS-3918_subdef-substituable-setting (#4381)
* add checkbox "substituable" to admin/subdef ; bump to 4.1.8-rc7 and bump production-client ; migrate and remove conf 'registry/modules/thumb-substitution'
* bump production-client to 94
* fix test ; add test ; move "substituable" node to all subdefs (attribute)
* fix test
* fix test
* set "flatten layers"=true for new subdefs (PHRAS-3852 fix it)
---
.env | 2 +-
.../dist/production.js | 1 +
.../dist/production.min.js | 1 +
.../src/components/record/tools/index.js | 1 +
cache/.gitkeep | 0
.../Controller/Admin/SubdefsController.php | 10 +-
.../Controller/Prod/ToolsController.php | 128 ++++---
.../ControllerProvider/Admin/Subdefs.php | 2 +
.../Phrasea/ControllerProvider/Prod/Tools.php | 2 +
lib/Alchemy/Phrasea/Core/Version.php | 2 +-
lib/Alchemy/Phrasea/Media/Subdef/Image.php | 2 +-
lib/classes/databox/subdef.php | 21 +-
lib/classes/databox/subdefsStructure.php | 3 +-
lib/classes/patch/418RC7.php | 92 +++++
logs/.gitkeep | 0
templates/web/admin/subdefs.html.twig | 7 +
.../web/prod/actions/Tools/index.html.twig | 338 +++++++++---------
.../Phrasea/Controller/Prod/ToolsTest.php | 2 +-
tests/classes/databox/subdefTest.php | 31 ++
19 files changed, 406 insertions(+), 239 deletions(-)
mode change 100644 => 100755 cache/.gitkeep
create mode 100644 lib/classes/patch/418RC7.php
mode change 100644 => 100755 logs/.gitkeep
diff --git a/.env b/.env
index 529bbcce4e..a72c96c854 100644
--- a/.env
+++ b/.env
@@ -134,7 +134,7 @@ PHRASEANET_DOCKER_REGISTRY=local
# Docker images tag.
# @run
-PHRASEANET_DOCKER_TAG=4.1.8-rc6
+PHRASEANET_DOCKER_TAG=4.1.8-rc7
# Stack Name
# An optionnal Name for the stack
diff --git a/Phraseanet-production-client/dist/production.js b/Phraseanet-production-client/dist/production.js
index f4f94a4b83..2241df118c 100644
--- a/Phraseanet-production-client/dist/production.js
+++ b/Phraseanet-production-client/dist/production.js
@@ -62775,6 +62775,7 @@ var recordToolsModal = function recordToolsModal(services, datas) {
(0, _jquery2.default)('.iframe_submiter', $scope).bind('click', function () {
var form = (0, _jquery2.default)(this).closest('form');
form.submit();
+ form.find('.resultAction').empty();
form.find('.load').empty().html(localeService.t('loading') + ' ...');
(0, _jquery2.default)('#uploadHdsub').contents().find('.content').empty();
(0, _jquery2.default)('#uploadHdsub').load(function () {
diff --git a/Phraseanet-production-client/dist/production.min.js b/Phraseanet-production-client/dist/production.min.js
index f4f94a4b83..2241df118c 100644
--- a/Phraseanet-production-client/dist/production.min.js
+++ b/Phraseanet-production-client/dist/production.min.js
@@ -62775,6 +62775,7 @@ var recordToolsModal = function recordToolsModal(services, datas) {
(0, _jquery2.default)('.iframe_submiter', $scope).bind('click', function () {
var form = (0, _jquery2.default)(this).closest('form');
form.submit();
+ form.find('.resultAction').empty();
form.find('.load').empty().html(localeService.t('loading') + ' ...');
(0, _jquery2.default)('#uploadHdsub').contents().find('.content').empty();
(0, _jquery2.default)('#uploadHdsub').load(function () {
diff --git a/Phraseanet-production-client/src/components/record/tools/index.js b/Phraseanet-production-client/src/components/record/tools/index.js
index 8343e2a825..1793fe8f55 100644
--- a/Phraseanet-production-client/src/components/record/tools/index.js
+++ b/Phraseanet-production-client/src/components/record/tools/index.js
@@ -51,6 +51,7 @@ const recordToolsModal = (services, datas, activeTab = false) => {
$('.iframe_submiter', $scope).bind('click', function () {
var form = $(this).closest('form');
form.submit();
+ form.find('.resultAction').empty();
form.find('.load').empty().html(localeService.t('loading') + ' ...');
$('#uploadHdsub').contents().find('.content').empty();
$('#uploadHdsub').load(function () {
diff --git a/cache/.gitkeep b/cache/.gitkeep
old mode 100644
new mode 100755
diff --git a/lib/Alchemy/Phrasea/Controller/Admin/SubdefsController.php b/lib/Alchemy/Phrasea/Controller/Admin/SubdefsController.php
index 65f72b6da3..52ec210100 100644
--- a/lib/Alchemy/Phrasea/Controller/Admin/SubdefsController.php
+++ b/lib/Alchemy/Phrasea/Controller/Admin/SubdefsController.php
@@ -14,12 +14,14 @@
use Alchemy\Phrasea\Databox\SubdefGroup;
use Alchemy\Phrasea\Media\Subdef\Subdef;
use Alchemy\Phrasea\Media\Type\Type;
+use Exception;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Alchemy\Phrasea\Media\Subdef\Image;
use Alchemy\Phrasea\Media\Subdef\Video;
use Alchemy\Phrasea\Media\Subdef\Audio;
use Alchemy\Phrasea\Media\Subdef\Gif;
+use unicode;
class SubdefsController extends Controller
{
@@ -44,7 +46,7 @@ function indexAction($sbas_id) {
* @param Request $request
* @param int $sbas_id
* @return Response
- * @throws \Exception
+ * @throws Exception
*/
function changeSubdefsAction(Request $request, $sbas_id) {
$delete_subdef = $request->request->get('delete_subdef');
@@ -84,7 +86,7 @@ function changeSubdefsAction(Request $request, $sbas_id) {
$subdefs = $databox->get_subdef_structure();
$group = $add_subdef['group'];
- /** @var \unicode $unicode */
+ /** @var unicode $unicode */
$unicode = $this->app['unicode'];
$name = $unicode->remove_nonazAZ09($add_subdef['name'], false);
$class = $add_subdef['class'];
@@ -111,6 +113,7 @@ function changeSubdefsAction(Request $request, $sbas_id) {
$options[Image::OPTION_FLATTEN] = $config["image"]["definitions"][$preset][Image::OPTION_FLATTEN];
$options[Image::OPTION_QUALITY] = $config["image"]["definitions"][$preset][Image::OPTION_QUALITY];
$options[Image::OPTION_ICODEC] = $config["image"]["definitions"][$preset][Image::OPTION_ICODEC];
+ $options[Image::OPTION_BACKGROUNDCOLOR] = $config["image"]["definitions"][$preset][Image::OPTION_BACKGROUNDCOLOR];
foreach ($config["image"]["definitions"][$preset][Subdef::OPTION_DEVICE] as $devices) {
$options[Subdef::OPTION_DEVICE][] = $devices;
}
@@ -182,6 +185,7 @@ function changeSubdefsAction(Request $request, $sbas_id) {
$class = $request->request->get($post_sub . '_class');
$downloadable = $request->request->get($post_sub . '_downloadable');
$orderable = $request->request->get($post_sub . '_orderable');
+ $substituable = $request->request->get($post_sub . '_substituable');
$toBuild = $request->request->get($post_sub . '_tobuild');
$defaults = ['path', 'meta', 'mediatype'];
@@ -208,7 +212,7 @@ function changeSubdefsAction(Request $request, $sbas_id) {
}
$labels = $request->request->get($post_sub . '_label', []);
- $subdefs->set_subdef($group, $name, $class, $downloadable, $options, $labels, $orderable, $preset, $toBuild);
+ $subdefs->set_subdef($group, $name, $class, $downloadable, $options, $labels, $orderable, $preset, $toBuild, $substituable);
}
}
diff --git a/lib/Alchemy/Phrasea/Controller/Prod/ToolsController.php b/lib/Alchemy/Phrasea/Controller/Prod/ToolsController.php
index 6a30a0c055..9c522a6ee5 100644
--- a/lib/Alchemy/Phrasea/Controller/Prod/ToolsController.php
+++ b/lib/Alchemy/Phrasea/Controller/Prod/ToolsController.php
@@ -44,14 +44,14 @@ public function indexAction(Request $request)
$metadatas = false;
$record = null;
- $recordAccessibleSubdefs = array();
- $listsubdef= null;
+ $recordAccessibleSubdefs = [];
+ $listsubdef = null;
if (count($records) == 1) {
/** @var record_adapter $record */
$record = $records->first();
/**Array list of subdefs**/
- $listsubdef = array_keys($record-> get_subdefs());
+ $listsubdef = array_keys($record->get_subdefs());
// fetch subdef list:
$subdefs = $record->get_subdefs();
@@ -73,18 +73,19 @@ public function indexAction(Request $request)
continue;
}
$label = $this->app->trans('prod::tools: document');
- } elseif ($databoxSubdefs !== null && $databoxSubdefs->hasSubdef($subdefName)) {
+ }
+ elseif ($databoxSubdefs !== null && $databoxSubdefs->hasSubdef($subdefName)) {
if (!$acl->has_access_to_subdef($record, $subdefName)) {
continue;
}
$label = $databoxSubdefs->getSubdef($subdefName)->get_label($this->app['locale']);
}
- $recordAccessibleSubdefs[] = array(
- 'name' => $subdef->get_name(),
+ $recordAccessibleSubdefs[] = [
+ 'name' => $subdef->get_name(),
'state' => $permalink->get_is_activated(),
'label' => $label,
- );
+ ];
}
}
if (!$record->isStory()) {
@@ -95,8 +96,13 @@ public function indexAction(Request $request)
$availableSubdefLabel = [];
$countSubdefTodo = [];
+ $substituables = [];
+ if ($this->getConf()->get(['registry', 'modules', 'doc-substitution'])) {
+ $substituables[] = 'document';
+ }
/** @var record_adapter $rec */
foreach ($records as $rec) {
+
$databoxSubdefs = $rec->getDatabox()->get_subdef_structure()->getSubdefGroup($rec->getType());
if ($databoxSubdefs !== null) {
foreach ($databoxSubdefs as $sub) {
@@ -104,29 +110,38 @@ public function indexAction(Request $request)
$label = trim($sub->get_label($this->app['locale']));
$availableSubdefLabel[] = $label;
if (isset($countSubdefTodo[$label])) {
- $countSubdefTodo[$label] ++;
- } else {
+ $countSubdefTodo[$label]++;
+ }
+ else {
$countSubdefTodo[$label] = 1;
}
}
+ if ($sub->isSubstituable()) {
+ $substituables[] = $sub->get_name();
+ }
}
}
}
+ if (count($records) > 1) {
+ $substituables = [];
+ }
+
$this->setSessionFormToken('prodToolsSubdef');
$this->setSessionFormToken('prodToolsRotate');
$this->setSessionFormToken('prodToolsHDSubstitution');
$this->setSessionFormToken('prodToolsThumbSubstitution');
return $this->render('prod/actions/Tools/index.html.twig', [
- 'records' => $records,
- 'record' => $record,
- 'recordSubdefs' => $recordAccessibleSubdefs,
- 'metadatas' => $metadatas,
- 'listsubdef' => $listsubdef,
+ 'records' => $records,
+ 'record' => $record,
+ 'recordSubdefs' => $recordAccessibleSubdefs,
+ 'metadatas' => $metadatas,
+ 'listsubdef' => $listsubdef,
'availableSubdefLabel' => array_unique($availableSubdefLabel),
- 'nbRecords' => count($records),
- 'countSubdefTodo' => $countSubdefTodo
+ 'nbRecords' => count($records),
+ 'countSubdefTodo' => $countSubdefTodo,
+ 'substituables' => $substituables,
]);
}
@@ -161,7 +176,8 @@ public function rotateAction(Request $request)
try {
$subdef->rotate($rotation, $this->getMediaAlchemyst(), $this->getMediaVorus());
- } catch (\Exception $e) {
+ }
+ catch (\Exception $e) {
// ignore exception
}
}
@@ -256,31 +272,34 @@ public function hddocAction(Request $request)
$this->getSubDefinitionSubstituer()->substituteDocument($record, $media);
$record->insertTechnicalDatas($this->getMediaVorus());
- $this->getMetadataSetter()->replaceMetadata($this->getMetadataReader() ->read($media), $record);
+ $this->getMetadataSetter()->replaceMetadata($this->getMetadataReader()->read($media), $record);
$this->getDataboxLogger($record->getDatabox())
- ->log($record, \Session_Logger::EVENT_SUBSTITUTE, 'HD', '' );
+ ->log($record, \Session_Logger::EVENT_SUBSTITUTE, 'HD', '');
- if ((int) $request->request->get('ccfilename') === 1) {
+ if ((int)$request->request->get('ccfilename') === 1) {
$record->set_original_name($fileName);
}
unlink($tempoFile);
rmdir($tempoDir);
$success = true;
$message = $this->app->trans('Document has been successfully substitued');
- } catch (\Exception $e) {
+ }
+ catch (\Exception $e) {
$message = $this->app->trans('file is not valid');
}
- } else {
+ }
+ else {
$message = $this->app->trans('file is not valid');
}
- } else {
+ }
+ else {
$this->app->abort(400, 'Missing file parameter');
}
return $this->render('prod/actions/Tools/iframeUpload.html.twig', [
- 'success' => $success,
- 'message' => $message,
+ 'success' => $success,
+ 'message' => $message,
]);
}
@@ -296,10 +315,10 @@ public function changeThumbnailAction(Request $request)
$this->app->abort(400, 'Missing file parameter');
}
- if (! $file->isValid()) {
+ if (!$file->isValid()) {
return $this->render('prod/actions/Tools/iframeUpload.html.twig', [
- 'success' => false,
- 'message' => $this->app->trans('file is not valid'),
+ 'success' => false,
+ 'message' => $this->app->trans('file is not valid'),
]);
}
@@ -318,22 +337,28 @@ public function changeThumbnailAction(Request $request)
$media = $this->app->getMediaFromUri($tempoFile);
- $this->getSubDefinitionSubstituer()->substituteSubdef($record, 'thumbnail', $media);
+ // no BC break before PHRAS-3918 when only "thumbnail" was substituable
+ if(($subdef = $request->get('subdef')) === null) {
+ $subdef = 'thumbnail';
+ }
+
+ $this->getSubDefinitionSubstituer()->substituteSubdef($record, $subdef, $media);
$this->getDataboxLogger($record->getDatabox())
- ->log($record, \Session_Logger::EVENT_SUBSTITUTE, 'thumbnail', '');
+ ->log($record, \Session_Logger::EVENT_SUBSTITUTE, $subdef, '');
unlink($tempoFile);
rmdir($tempoDir);
$success = true;
- $message = $this->app->trans('Thumbnail has been successfully substitued');
- } catch (\Exception $e) {
+ $message = sprintf($this->app->trans('Subdef "%s" has been successfully substitued'), $subdef);
+ }
+ catch (\Exception $e) {
$success = false;
$message = $this->app->trans('file is not valid');
}
return $this->render('prod/actions/Tools/iframeUpload.html.twig', [
- 'success' => $success,
- 'message' => $message,
+ 'success' => $success,
+ 'message' => $message,
]);
}
@@ -344,14 +369,15 @@ public function submitConfirmBoxAction(Request $request)
try {
$record = new record_adapter($this->app, $request->request->get('sbas_id'), $request->request->get('record_id'));
$var = [
- 'video_title' => $record->get_title(['encode'=> record_adapter::ENCODE_NONE]),
+ 'video_title' => $record->get_title(['encode' => record_adapter::ENCODE_NONE]),
'image' => $request->request->get('image', ''),
];
$return = [
'error' => false,
'datas' => $this->render($template, $var),
];
- } catch (\Exception $e) {
+ }
+ catch (\Exception $e) {
$return = [
'error' => true,
'datas' => $this->app->trans('an error occured'),
@@ -378,7 +404,8 @@ public function applyThumbnailExtractionAction(Request $request)
}
$return = ['success' => true, 'message' => ''];
- } catch (\Exception $e) {
+ }
+ catch (\Exception $e) {
$return = ['success' => false, 'message' => $e->getMessage()];
}
@@ -418,7 +445,8 @@ public function editRecordSharing(Request $request, $base_id, $record_id)
try {
$permalink->set_is_activated($state);
$return = ['success' => true, 'state' => $permalink->get_is_activated()];
- } catch (\Exception $e) {
+ }
+ catch (\Exception $e) {
$return = ['success' => false, 'state' => $permalink->get_is_activated()];
}
@@ -474,13 +502,14 @@ private function substituteMedia(record_adapter $record, $subDefName, $subDefDat
$media = $this->app->getMediaFromUri($fileName);
- if($subDefName == 'document') {
+ if ($subDefName == 'document') {
$this->getSubDefinitionSubstituer()->substituteDocument($record, $media);
- } else {
+ }
+ else {
$this->getSubDefinitionSubstituer()->substituteSubdef($record, $subDefName, $media);
}
$this->getDataboxLogger($record->getDatabox())
- ->log($record, \Session_Logger::EVENT_SUBSTITUTE, $subDefName, '');
+ ->log($record, \Session_Logger::EVENT_SUBSTITUTE, $subDefName, '');
unset($media);
$this->getFilesystem()->remove($fileName);
@@ -574,7 +603,7 @@ public function videoEditorAction(Request $request)
'_value' => $record->getCaption([$meta->get_name()]),
];
- if (preg_match('/^VideoTextTrack(.*)$/iu', $meta->get_name(), $matches) && !empty($matches[1]) && strlen($matches[1]) == 2 ) {
+ if (preg_match('/^VideoTextTrack(.*)$/iu', $meta->get_name(), $matches) && !empty($matches[1]) && strlen($matches[1]) == 2) {
$field['label'] = $matches[1];
$field['meta_struct_id'] = $meta->get_id();
$field['value'] = '';
@@ -595,12 +624,12 @@ public function videoEditorAction(Request $request)
$conf = $this->getConf();
return $this->render('prod/actions/Tools/videoEditor.html.twig', [
- 'records' => $records,
- 'record' => $record,
- 'videoEditorConfig' => $conf->get(['video-editor']),
- 'metadatas' => $metadatas,
- 'JSonFields' => json_encode($JSFields),
- 'videoTextTrackFields' => $videoTextTrackFields
+ 'records' => $records,
+ 'record' => $record,
+ 'videoEditorConfig' => $conf->get(['video-editor']),
+ 'metadatas' => $metadatas,
+ 'JSonFields' => json_encode($JSFields),
+ 'videoTextTrackFields' => $videoTextTrackFields
]);
}
@@ -608,7 +637,8 @@ private function isPhysicallyPresent(record_adapter $record, $subdefName)
{
try {
return $record->get_subdef($subdefName)->is_physically_present();
- } catch (\Exception $e) {
+ }
+ catch (\Exception $e) {
unset($e);
}
diff --git a/lib/Alchemy/Phrasea/ControllerProvider/Admin/Subdefs.php b/lib/Alchemy/Phrasea/ControllerProvider/Admin/Subdefs.php
index 91d8eb07c2..2bdcd22686 100644
--- a/lib/Alchemy/Phrasea/ControllerProvider/Admin/Subdefs.php
+++ b/lib/Alchemy/Phrasea/ControllerProvider/Admin/Subdefs.php
@@ -44,10 +44,12 @@ public function connect(Application $app)
->requireRightOnSbas($request->attributes->get('sbas_id'), \ACL::BAS_MODIFY_STRUCT);
});
+ /** @uses SubdefsController::indexAction */
$controllers->get('/{sbas_id}/', 'controller.admin.subdefs:indexAction')
->bind('admin_subdefs_subdef')
->assert('sbas_id', '\d+');
+ /** @uses SubdefsController::changeSubdefsAction */
$controllers->post('/{sbas_id}/', 'controller.admin.subdefs:changeSubdefsAction')
->bind('admin_subdefs_subdef_update')
->assert('sbas_id', '\d+');
diff --git a/lib/Alchemy/Phrasea/ControllerProvider/Prod/Tools.php b/lib/Alchemy/Phrasea/ControllerProvider/Prod/Tools.php
index 07f8810ad4..dd7913a317 100644
--- a/lib/Alchemy/Phrasea/ControllerProvider/Prod/Tools.php
+++ b/lib/Alchemy/Phrasea/ControllerProvider/Prod/Tools.php
@@ -57,9 +57,11 @@ public function connect(Application $app)
$controllers->post('/image/', 'controller.prod.tools:imageAction')
->bind('prod_tools_image');
+ /** @uses \Alchemy\Phrasea\Controller\Prod\ToolsController::hddocAction */
$controllers->post('/hddoc/', 'controller.prod.tools:hddocAction')
->bind('prod_tools_hd_substitution');
+ /** @uses \Alchemy\Phrasea\Controller\Prod\ToolsController::changeThumbnailAction */
$controllers->post('/chgthumb/', 'controller.prod.tools:changeThumbnailAction')
->bind('prod_tools_thumbnail_substitution');
diff --git a/lib/Alchemy/Phrasea/Core/Version.php b/lib/Alchemy/Phrasea/Core/Version.php
index ed7b9b1805..e695c846ab 100644
--- a/lib/Alchemy/Phrasea/Core/Version.php
+++ b/lib/Alchemy/Phrasea/Core/Version.php
@@ -17,7 +17,7 @@ class Version
* @var string
*/
- private $number = '4.1.8-rc6';
+ private $number = '4.1.8-rc7';
/**
* @var string
diff --git a/lib/Alchemy/Phrasea/Media/Subdef/Image.php b/lib/Alchemy/Phrasea/Media/Subdef/Image.php
index 144c3033a8..4a881696b1 100644
--- a/lib/Alchemy/Phrasea/Media/Subdef/Image.php
+++ b/lib/Alchemy/Phrasea/Media/Subdef/Image.php
@@ -36,7 +36,7 @@ public function __construct(TranslatorInterface $translator)
$this->registerOption(new OptionType\Range($this->translator->trans('Dimension'), self::OPTION_SIZE, 20, 5000, 800));
$this->registerOption(new OptionType\Range($this->translator->trans('Resolution'), self::OPTION_RESOLUTION, 50, 1000, 72));
$this->registerOption(new OptionType\Boolean($this->translator->trans('Remove ICC Profile'), self::OPTION_STRIP, false));
- $this->registerOption(new OptionType\Boolean($this->translator->trans('Flatten layers'), self::OPTION_FLATTEN, false));
+ $this->registerOption(new OptionType\Boolean($this->translator->trans('Flatten layers'), self::OPTION_FLATTEN, true));
$this->registerOption(new OptionType\Range($this->translator->trans('Quality'), self::OPTION_QUALITY, 0, 100, 75));
$this->registerOption(new OptionType\Enum('Image Codec', self::OPTION_ICODEC, array('jpeg', 'png', 'tiff'), 'jpeg'));
$this->registerOption(new OptionType\EnumButton($this->translator->trans('Watermark'), self::OPTION_WATERMARK, array('no' => 'no', 'yes' => 'yes'), 'no'));
diff --git a/lib/classes/databox/subdef.php b/lib/classes/databox/subdef.php
index 9ae75c6878..2bc32f4361 100644
--- a/lib/classes/databox/subdef.php
+++ b/lib/classes/databox/subdef.php
@@ -43,6 +43,8 @@ class databox_subdef
*/
private $requiresMetadataUpdate;
protected $downloadable;
+ protected $orderable;
+ protected $substituable;
protected $tobuild;
protected $translator;
protected static $mediaTypeToSubdefTypes = [
@@ -67,8 +69,8 @@ class databox_subdef
*
* @param SubdefType $type
* @param SimpleXMLElement $sd
- *
- * @return databox_subdef
+ * @param TranslatorInterface $translator
+ * @param databox|null $databox
*/
public function __construct(SubdefType $type, SimpleXMLElement $sd, TranslatorInterface $translator, databox $databox = null)
{
@@ -81,8 +83,9 @@ public function __construct(SubdefType $type, SimpleXMLElement $sd, TranslatorIn
}
$this->name = strtolower($sd->attributes()->name);
$this->downloadable = p4field::isyes($sd->attributes()->downloadable);
- $this->orderable = isset($sd->attributes()->orderable) ? p4field::isyes($sd->attributes()->orderable) : true;
- $this->tobuild = isset($sd->attributes()->tobuild) ? p4field::isyes($sd->attributes()->tobuild) : true;
+ $this->substituable = isset($sd->attributes()->substituable) && p4field::isyes($sd->attributes()->substituable);
+ $this->orderable = !isset($sd->attributes()->orderable) || p4field::isyes($sd->attributes()->orderable);
+ $this->tobuild = !isset($sd->attributes()->tobuild) || p4field::isyes($sd->attributes()->tobuild);
$this->path = trim($sd->path) !== '' ? p4string::addEndSlash(trim($sd->path)) : '';
$this->preset = $sd->attributes()->presets;
$this->requiresMetadataUpdate = p4field::isyes((string)$sd->meta);
@@ -368,8 +371,6 @@ public function isOrderable()
}
/**
- * boolean
- *
* @return bool
*/
public function isTobuild()
@@ -377,6 +378,14 @@ public function isTobuild()
return $this->tobuild;
}
+ /**
+ * @return bool
+ */
+ public function isSubstituable()
+ {
+ return $this->substituable;
+ }
+
/**
* Get an array of Alchemy\Phrasea\Media\Subdef\Subdef available for the current Media Type
*
diff --git a/lib/classes/databox/subdefsStructure.php b/lib/classes/databox/subdefsStructure.php
index ffd6927b8a..69261f1bd1 100644
--- a/lib/classes/databox/subdefsStructure.php
+++ b/lib/classes/databox/subdefsStructure.php
@@ -246,7 +246,7 @@ public function add_subdef($groupname, $name, $class, $mediatype, $preset, $path
* @return databox_subdefsStructure
* @throws Exception
*/
- public function set_subdef($group, $name, $class, $downloadable, $options, $labels, $orderable = true, $preset = "Custom", $toBuild = true)
+ public function set_subdef($group, $name, $class, $downloadable, $options, $labels, $orderable = true, $preset = "Custom", $toBuild = true, $substituable = false)
{
$dom_struct = $this->databox->get_dom_structure();
@@ -256,6 +256,7 @@ public function set_subdef($group, $name, $class, $downloadable, $options, $labe
$subdef->setAttribute('downloadable', ($downloadable ? 'true' : 'false'));
$subdef->setAttribute('orderable', ($orderable ? 'true' : 'false'));
$subdef->setAttribute('tobuild', ($toBuild ? 'true' : 'false'));
+ $subdef->setAttribute('substituable', ($substituable ? 'true' : 'false'));
$subdef->setAttribute('presets', $preset);
foreach ($labels as $code => $label) {
diff --git a/lib/classes/patch/418RC7.php b/lib/classes/patch/418RC7.php
new file mode 100644
index 0000000000..71a2a4db9c
--- /dev/null
+++ b/lib/classes/patch/418RC7.php
@@ -0,0 +1,92 @@
+release;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getDoctrineMigrations()
+ {
+ return [];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function require_all_upgrades()
+ {
+ return false;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function concern()
+ {
+ return $this->concern;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function apply(base $base, Application $app)
+ {
+ if ($base->get_base_type() === base::DATA_BOX) {
+ $this->patch_databox($base, $app);
+ }
+ elseif ($base->get_base_type() === base::APPLICATION_BOX) {
+ $this->patch_appbox($base, $app);
+ }
+
+ return true;
+ }
+
+ private $thumbSubstitution = null;
+
+ public function patch_databox(databox $databox, Application $app)
+ {
+ /** @var PropertyAccess $conf */
+ $conf = $app['conf'];
+
+ if ($this->thumbSubstitution === null) {
+ // first db
+ $this->thumbSubstitution = $conf->get(['registry', 'modules', 'thumb-substitution']);
+ $conf->remove(['registry', 'modules', 'thumb-substitution']);
+ }
+
+ if ($this->thumbSubstitution) {
+ $dom_struct = $databox->get_dom_structure();
+ $dom_xp = $databox->get_xpath_structure();
+
+ $nodes = $dom_xp->query('//record/subdefs/subdefgroup/subdef[@name="thumbnail"]');
+ for ($i = 0; $i < $nodes->length; $i++) {
+ /** @var \DOMElement $node */
+ $node = $nodes->item($i);
+ $node->setAttribute('substituable', 'true');
+ }
+
+ $databox->saveStructure($dom_struct);
+ }
+ }
+
+ private function patch_appbox(base $appbox, Application $app)
+ {
+ }
+}
diff --git a/logs/.gitkeep b/logs/.gitkeep
old mode 100644
new mode 100755
diff --git a/templates/web/admin/subdefs.html.twig b/templates/web/admin/subdefs.html.twig
index b94e27c043..0fb71104b3 100644
--- a/templates/web/admin/subdefs.html.twig
+++ b/templates/web/admin/subdefs.html.twig
@@ -456,6 +456,13 @@
|
+
+ {{ 'subdef.substituable' | trans }} |
+
+ |
+ |
+
{{ 'classe' | trans }}
diff --git a/templates/web/prod/actions/Tools/index.html.twig b/templates/web/prod/actions/Tools/index.html.twig
index 2df6f62224..9740756b84 100644
--- a/templates/web/prod/actions/Tools/index.html.twig
+++ b/templates/web/prod/actions/Tools/index.html.twig
@@ -4,23 +4,23 @@
{% set nbSubdefSubstitute = 0 %}
{% for record in records %}
- {% set subdefs = record.get_subdefs() %}
- {% if subdefs|length > 0 %}
- {% for key, subdef in subdefs if subdef.is_substituted() %}
- {% if key == 'document' %}
- {% set nbHdSubstitute = nbHdSubstitute + 1 %}
- {% else %}
- {% set nbSubdefSubstitute = nbSubdefSubstitute + 1 %}
- {% endif%}
+ {% set subdefs = record.get_subdefs() %}
+ {% if subdefs|length > 0 %}
+ {% for key, subdef in subdefs if subdef.is_substituted() %}
+ {% if key == 'document' %}
+ {% set nbHdSubstitute = nbHdSubstitute + 1 %}
+ {% else %}
+ {% set nbSubdefSubstitute = nbSubdefSubstitute + 1 %}
+ {% endif %}
- {% endfor %}
- {% endif %}
+ {% endfor %}
+ {% endif %}
{% endfor %}
|