From e2b76cc81f8b483d134b926c529efeb63f410f65 Mon Sep 17 00:00:00 2001 From: jygaulier Date: Thu, 16 Nov 2023 16:16:14 +0100 Subject: [PATCH 1/4] PHRAS-3940 translator-group-jobs (#4409) * declare actions (work on fields) inside job (work on records) * fix invalid sql * fix invalid sql ; add sample --- .../Command/Thesaurus/Translator/Action.php | 303 +++++++++++ .../Translator/GlobalConfiguration.php | 55 +- .../Command/Thesaurus/Translator/Job.php | 478 ++++++------------ .../Thesaurus/Translator/TranslateCommand.php | 17 +- .../Translator/doc/configuration-sample.yml | 69 ++- 5 files changed, 546 insertions(+), 376 deletions(-) create mode 100644 lib/Alchemy/Phrasea/Command/Thesaurus/Translator/Action.php diff --git a/lib/Alchemy/Phrasea/Command/Thesaurus/Translator/Action.php b/lib/Alchemy/Phrasea/Command/Thesaurus/Translator/Action.php new file mode 100644 index 0000000000..dacf75727f --- /dev/null +++ b/lib/Alchemy/Phrasea/Command/Thesaurus/Translator/Action.php @@ -0,0 +1,303 @@ +job = $job; + $this->unicode = $unicode; + $this->output = $output; + $this->reportFormat = $this->job->getGlobalConfiguration()->getReportFormat(); + + if (array_key_exists('active', $action_conf) && $action_conf['active'] === false) { + $this->active = false; + return; + } + + + // get infos about the "source_field" + // + if (!($f = $job->getDataboxField($action_conf['source_field'])) ) { + $this->errors[] = sprintf("source field (%s) not found.", $action_conf['source_field']); + } + if (trim($f->get_tbranch()) === '') { + $this->errors[] = sprintf("source field (%s) not linked to thesaurus.", $action_conf['source_field']); + } + $this->tbranches = $job->getXpathTh()->query($f->get_tbranch()); + if (!$this->tbranches || $this->tbranches->length <= 0) { + $this->errors[] = sprintf("thesaurus branch(es) of source field (%s) not found.", $this->source_field['tbranch']); + } + $this->source_field = [ + 'id' => $f->get_id(), + 'name' => $f->get_name(), + 'tbranch' => $f->get_tbranch(), + 'lng' => array_key_exists('source_lng', $action_conf) ? $action_conf['source_lng'] : null + ]; + $this->selectRecordFieldIds[] = $this->source_field['id']; + + + // get infos about the "destination_fields" + // + $this->destination_fields = []; + foreach ($action_conf['destination_fields'] as $tf) { + list($lng, $fname) = explode(':', $tf); + if(!($f = $job->getDataboxField($fname)) ) { + $this->output->writeln(sprintf("undefined field (%s) (ignored).", $fname)); + continue; + } + $this->destination_fields[$lng] = [ + 'id' => $f->get_id(), + 'name' => $f->get_name(), + ]; + + $this->selectRecordFieldIds[] = $this->destination_fields[$lng]['id']; + } + + if (empty($this->destination_fields)) { + $this->errors[] = sprintf("no \"destination_field\" found."); + } + + // misc settings + $this->cleanupDestination = array_key_exists('cleanup_destination', $action_conf) && $action_conf['cleanup_destination'] === true; + $this->cleanupSource = array_key_exists('cleanup_source', $action_conf) ? $action_conf['cleanup_source'] : self::NEVER_CLEANUP_SOURCE; + } + + public function doAction(array $metas, array &$meta_to_delete, array&$meta_to_add) + { + if ($this->cleanupDestination) { + foreach ($this->destination_fields as $lng => $destination_field) { + $destination_field_id = $destination_field['id']; + if(array_key_exists($destination_field_id, $metas)) { + foreach ($metas[$destination_field_id] as $meta_id => $value) { + $meta_to_delete[$meta_id] = $value; + } + } + unset($meta_id, $value); + } + unset($lng, $destination_field, $destination_field_id); + } + + $source_field_id = $this->source_field['id']; + + if(!array_key_exists($source_field_id, $metas)) { + // no source field value for this record: nothing to do + return; + } + + // loop on every value of the "source_field" + // + foreach ($metas[$source_field_id] as $source_meta_id => $source_value) { + + $t = $this->splitTermAndContext($source_value); + $q = '@w=\'' . thesaurus::xquery_escape($this->unicode->remove_indexer_chars($t[0])) . '\''; + if ($t[1]) { + $q .= ' and @k=\'' . thesaurus::xquery_escape($this->unicode->remove_indexer_chars($t[1])) . '\''; + } + if(!is_null($this->source_field['lng'])) { + $q .= ' and @lng=\'' . thesaurus::xquery_escape($this->source_field['lng']) . '\''; + } + $q = '//sy[' . $q . ']/../sy'; + unset($t); + + // loop on every tbranch (one field may be linked to many branches) + // + $translations = []; // ONE translation per lng (first found in th) + /** @var DOMNode $tbranch */ + foreach ($this->tbranches as $tbranch) { + if (!($nodes = $this->job->getXpathTh()->query($q, $tbranch))) { + $this->output->writeln(sprintf("\t\t\t- \"%s\" xpath error on (%s), ignored.", $source_value, $q)); + continue; + } + + // loop on every synonym + // + /** @var DOMElement $node */ + foreach ($nodes as $node) { + $lng = $node->getAttribute('lng'); + + // ignore synonyms not in one of the "destination_field" languages + // + if (!array_key_exists($lng, $this->destination_fields)) { + continue; + } + + $translated_value = $node->getAttribute('v'); + + $destination_field_id = $this->destination_fields[$lng]['id']; + if (!array_key_exists($lng, $translations)) { + if ( + !array_key_exists($destination_field_id, $metas) + || ($destination_meta_id = array_search($translated_value, $metas[$destination_field_id])) === false + ) { + $translations[$lng] = [ + 'val' => $translated_value, + 'id' => null, + 'msg' => sprintf(" --> %s", $this->destination_fields[$lng]['name']) + ]; + $meta_to_add[$destination_field_id][] = $translated_value; + } + else { + $translations[$lng] = [ + 'val' => $translated_value, + 'id' => $destination_meta_id, + 'msg' => sprintf("already in %s", $this->destination_fields[$lng]['name']) + ]; + unset($meta_to_delete[$destination_meta_id]); + } + unset($destination_meta_id); + } + unset($lng, $destination_field_id, $translated_value); + } + unset($nodes, $node, $tbranch); + } + unset($q); + + // cleanup source + // + if (empty($translations)) { + if($this->reportFormat === GlobalConfiguration::REPORT_FORMAT_ALL) { + $this->output->writeln(sprintf("\t\t\t- \"%s\" : no translation found.", $source_value)); + } + $this->job->addToCondensedReport($source_value, job::CONDENSED_REPORT_NOT_TRANSLATED); + } + else if (count($translations) < count($this->destination_fields)) { + if(in_array($this->reportFormat, [GlobalConfiguration::REPORT_FORMAT_ALL, GlobalConfiguration::REPORT_FORMAT_TRANSLATED])) { + $this->output->writeln(sprintf("\t\t\t- \"%s\" : incomplete translation.", $source_value)); + } + $this->job->addToCondensedReport($source_value, job::CONDENSED_REPORT_INCOMPLETELY_TRANSLATED); + } + else { + // complete translation (all target lng) + if(in_array($this->reportFormat, [GlobalConfiguration::REPORT_FORMAT_ALL, GlobalConfiguration::REPORT_FORMAT_TRANSLATED])) { + $this->output->writeln(sprintf("\t\t\t- \"%s\" :", $source_value)); + } + $this->job->addToCondensedReport($source_value, job::CONDENSED_REPORT_FULLY_TRANSLATED); + + if ($this->cleanupSource === self::CLEANUP_SOURCE_IF_TRANSLATED) { + // do NOT delete the source term if one translation found it as already present as destination (possible if source=destination) + $used = false; + foreach($translations as $l => $t) { + if($t['id'] === $source_meta_id) { + $used = true; + break; + } + } + if(!$used) { + $meta_to_delete[$source_meta_id] = $metas[$source_field_id][$source_meta_id]; + } + } + } + + if(in_array($this->reportFormat, [GlobalConfiguration::REPORT_FORMAT_ALL, GlobalConfiguration::REPORT_FORMAT_TRANSLATED])) { + foreach ($translations as $lng => $translation) { + $this->output->writeln(sprintf("\t\t\t\t- [%s] \"%s\" %s", $lng, $translation['val'], $translation['msg'])); + } + } + + if ($this->cleanupSource === self::ALWAYS_CLEANUP_SOURCE) { + // do NOT delete the source term if one translation found it as already present as destination (possible if source=destination) + $used = false; + foreach($translations as $l => $t) { + if($t['id'] === $source_meta_id) { + $used = true; + break; + } + } + if(!$used) { + $meta_to_delete[$source_meta_id] = $metas[$source_field_id][$source_meta_id]; + } + } + + unset($lng, $translations, $translation); + } + } + + private function splitTermAndContext($word) + { + $term = trim($word); + $context = ''; + if (($po = strpos($term, '(')) !== false) { + if (($pc = strpos($term, ')', $po)) !== false) { + $context = trim(substr($term, $po + 1, $pc - $po - 1)); + $term = trim(substr($term, 0, $po)); + } + else { + $context = trim(substr($term, $po + 1)); + $term = trim(substr($term, 0, $po)); + } + } + + return [$term, $context]; + } + + /** + * @return bool + */ + public function isActive(): bool + { + return $this->active; + } + + /** + * @return array + */ + public function getErrors(): array + { + return $this->errors; + } + + /** + * @return array + */ + public function getSelectRecordFieldIds(): array + { + return $this->selectRecordFieldIds; + } + +} diff --git a/lib/Alchemy/Phrasea/Command/Thesaurus/Translator/GlobalConfiguration.php b/lib/Alchemy/Phrasea/Command/Thesaurus/Translator/GlobalConfiguration.php index c097c63634..e989c69380 100644 --- a/lib/Alchemy/Phrasea/Command/Thesaurus/Translator/GlobalConfiguration.php +++ b/lib/Alchemy/Phrasea/Command/Thesaurus/Translator/GlobalConfiguration.php @@ -5,6 +5,7 @@ use appbox; use collection; use databox; +use databox_field; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Yaml\Yaml; use Unicode; @@ -13,6 +14,8 @@ { const CONFIG_DIR = "/config/translator/"; const CONFIG_FILE = "configuration.yml"; + const REPORT_FORMAT_ALL = "all"; + const REPORT_FORMAT_TRANSLATED = "translated"; private $configuration = null; @@ -47,7 +50,8 @@ private function __construct($appBox, Unicode $unicode, $global_conf, bool $dryR $sbas_name = $databox->get_dbname(); $this->databoxes[$sbas_id] = [ 'dbox' => $databox, - 'collections' => [] + 'collections' => [], + 'fields' => [], ]; $this->databoxes[$sbas_name] = &$this->databoxes[$sbas_id]; // list all collections @@ -57,16 +61,44 @@ private function __construct($appBox, Unicode $unicode, $global_conf, bool $dryR $this->databoxes[$sbas_id]['collections'][$coll_id] = $collection; $this->databoxes[$sbas_id]['collections'][$coll_name] = &$this->databoxes[$sbas_id]['collections'][$coll_id]; } + // list all fields + /** @var databox_field $dbf */ + foreach($databox->get_meta_structure() as $dbf) { + $field_id = $dbf->get_id(); + $field_name = $dbf->get_name(); + $this->databoxes[$sbas_id]['fields'][$field_id] = $dbf; + $this->databoxes[$sbas_id]['fields'][$field_name] = &$this->databoxes[$sbas_id]['fields'][$field_id]; + } } foreach($global_conf['jobs'] as $job_name => $job_conf) { - $this->jobs[$job_name] = new Job($this, $job_conf, $unicode, $output); + $job = new Job($this, $job_name, $job_conf, $unicode, $output); + if($job->isActive()) { + if($job->isValid()) { + $this->jobs[$job_name] = $job; + } + else { + $output->writeln("Configuration error(s)... :"); + foreach ($job->getErrors() as $err) { + $output->writeln(sprintf(" - %s", $err)); + } + $output->writeln("...Job ignored"); + } + } + else { + unset($job); + $output->writeln(sprintf("job \"%s\" is inactive: ignored.", $job_name)); + } } } /** * @param appbox $appBox + * @param Unicode $unicode * @param string $root + * @param bool $dryRun + * @param string $reportFormat + * @param OutputInterface $output * @return GlobalConfiguration * @throws ConfigurationException */ @@ -109,6 +141,25 @@ public function getCollection($sbasIdOrName, $collIdOrName) return $this->databoxes[$sbasIdOrName]['collections'][$collIdOrName] ?? null; } + /** + * @param string|int $sbasIdOrName + * @return databox_field[]|null + */ + public function getFields($sbasIdOrName) + { + return $this->databoxes[$sbasIdOrName] ?? null; + } + + /** + * @param string|int $sbasIdOrName + * @param string|int $collIdOrName + * @return databox_field|null + */ + public function getField($sbasIdOrName, $fieldIdOrName) + { + return $this->databoxes[$sbasIdOrName]['fields'][$fieldIdOrName] ?? null; + } + /** * @return bool */ diff --git a/lib/Alchemy/Phrasea/Command/Thesaurus/Translator/Job.php b/lib/Alchemy/Phrasea/Command/Thesaurus/Translator/Job.php index e8d408d057..1d67e79db4 100644 --- a/lib/Alchemy/Phrasea/Command/Thesaurus/Translator/Job.php +++ b/lib/Alchemy/Phrasea/Command/Thesaurus/Translator/Job.php @@ -2,9 +2,8 @@ namespace Alchemy\Phrasea\Command\Thesaurus\Translator; +use collection; use databox; -use DOMElement; -use DOMNode; use DOMNodeList; use DOMXpath; use PDO; @@ -14,13 +13,20 @@ class Job { - const NEVER_CLEANUP_SOURCE = 'never'; - const ALWAYS_CLEANUP_SOURCE = 'always'; - const CLEANUP_SOURCE_IF_TRANSLATED = 'if_translated'; + const CONDENSED_REPORT_NOT_TRANSLATED = 'notTranslated'; + const CONDENSED_REPORT_INCOMPLETELY_TRANSLATED = 'incompletelyTranslated'; + const CONDENSED_REPORT_FULLY_TRANSLATED = 'fullyTranslated'; private $active = true; + /** @var array[] */ + private $condensedReportCounts = [ + self::CONDENSED_REPORT_NOT_TRANSLATED => [], + self::CONDENSED_REPORT_INCOMPLETELY_TRANSLATED => [], + self::CONDENSED_REPORT_FULLY_TRANSLATED => [] + ]; + /** @var string[] */ private $errors = []; // error messages while parsing conf @@ -32,188 +38,127 @@ class Job private $selectRecordsSql = null; - /** @var array list of field ids of "source_field" (unique) and "destination_fields" (many) */ - private $selectRecordFieldIds; - - /** - * @var OutputInterface - */ + /** @var OutputInterface */ private $output; - private $source_field; // infos about the "source_field" - private $destination_fields; // infos about the "destination_fields" (key=lng) - - /** - * @var Unicode - */ - private $unicode; - /** @var DOMXpath|false|thesaurus_xpath */ private $xpathTh; - /** - * @var DOMNodeList - * The thesaurus branch(es) linked to the "source_field" - */ - private $tbranches; - - /** @var bool */ - private $cleanupDestination; + /** @var int flush every n records */ + private $bulk = 10; - /** @var string */ - private $cleanupSource = self::NEVER_CLEANUP_SOURCE; - /** - * @var GlobalConfiguration - */ + /** @var GlobalConfiguration */ private $globalConfiguration; - /** - * @var array - */ - private $job_conf; - /** - * @var \collection|null - */ + + /** @var collection|null */ private $setCollection = null; - /** - * @var string - */ + /** @var string */ private $setStatus = null; // format 0xx1100xx01xxxx - /** - * @var array - */ - private $notTranslated; // for condensed report - /** - * @var array - */ - private $incompletelyTranslated; // for condensed report - /** - * @var array - */ - private $fullyTranslated; // for condensed report - /** - * @var int - */ + /** @var Action[] */ + private $actions; + + /** @var array */ + private $selectRecordFieldIds = []; // ids of fields required by actions + + /** @var int */ private $recordsDone; // for condensed report + /** * @param GlobalConfiguration $globalConfiguration + * @param string $job_name * @param array $job_conf + * @param Unicode $unicode + * @param OutputInterface $output */ - public function __construct($globalConfiguration, $job_conf, Unicode $unicode, OutputInterface $output) + public function __construct(GlobalConfiguration $globalConfiguration, string $job_name, array $job_conf, Unicode $unicode, OutputInterface $output) { $this->globalConfiguration = $globalConfiguration; - $this->job_conf = $job_conf; - $this->unicode = $unicode; $this->output = $output; + $this->actions = []; + $this->errors = []; + if (array_key_exists('active', $job_conf) && $job_conf['active'] === false) { $this->active = false; - return; } - $this->errors = []; - foreach (['active', 'databox', 'source_field', 'destination_fields'] as $mandatory) { + foreach (['active', 'databox', 'actions'] as $mandatory) { if (!isset($job_conf[$mandatory])) { $this->errors[] = sprintf("Missing mandatory setting (%s).", $mandatory); } } - if (!empty($this->errors)) { - return; - } - if (!($this->databox = $globalConfiguration->getDatabox($job_conf['databox']))) { $this->errors[] = sprintf("unknown databox (%s).", $job_conf['databox']); - - return; } - + $ifCollection = null; + if(array_key_exists('if_collection', $job_conf)) { + if(!($ifCollection = $globalConfiguration->getCollection($this->databox->get_sbas_id(), $job_conf['if_collection']))) { + $this->errors[] = sprintf("unknown setCollection (%s).", $job_conf['if_collection']); + } + } if(array_key_exists('set_collection', $job_conf)) { if(!($this->setCollection = $globalConfiguration->getCollection($this->databox->get_sbas_id(), $job_conf['set_collection']))) { $this->errors[] = sprintf("unknown setCollection (%s).", $job_conf['set_collection']); - - return; } } - if(array_key_exists('set_status', $job_conf)) { $this->setStatus = $job_conf['set_status']; } - - - $cnx = $this->databox->get_connection(); - - // get infos about the "source_field" - // - $sql = "SELECT `id`, `tbranch` FROM `metadatas_structure` WHERE `name` = :name AND `tbranch` != ''"; - $stmt = $cnx->executeQuery($sql, [':name' => $job_conf['source_field']]); - $this->source_field = $stmt->fetch(PDO::FETCH_ASSOC); - $stmt->closeCursor(); - if (!$this->source_field) { - $this->errors[] = sprintf("field (%s) not found or not linked to thesaurus.", $job_conf['source_field']); - - return; + if(array_key_exists('bulk', $job_conf)) { + if( ($this->bulk = (int) $job_conf['bulk']) < 1) { + $this->errors[] = sprintf("bulk should be >= 1."); + } } - $this->source_field['lng'] = array_key_exists('source_lng', $job_conf) ? $job_conf['source_lng'] : null; - $this->selectRecordFieldIds[] = $this->source_field['id']; + $this->xpathTh = $this->databox->get_xpath_thesaurus(); - $this->tbranches = $this->xpathTh->query($this->source_field['tbranch']); - if (!$this->tbranches || $this->tbranches->length <= 0) { - $this->errors[] = sprintf("thesaurus branch(es) (%s) not found.", $this->source_field['tbranch']); - return; - } - // get infos about the "destination_fields" + + // load actions // - $this->destination_fields = []; - $sql = "SELECT `id`, `name` FROM `metadatas_structure` WHERE `name` = :name "; - $stmt = $cnx->prepare($sql); - foreach ($job_conf['destination_fields'] as $tf) { - list($lng, $fname) = explode(':', $tf); - $stmt->execute([':name' => $fname]); - if (!($row = $stmt->fetch(PDO::FETCH_ASSOC))) { - $this->output->writeln(sprintf("undefined field (%s) (ignored).", $fname)); - continue; + $this->selectRecordFieldIds = []; + foreach($job_conf['actions'] as $action_name => $action_conf) { + $action = new Action($this, $action_conf, $unicode, $this->output); + if($action->isActive()) { + $this->selectRecordFieldIds = array_merge($this->selectRecordFieldIds, $action->getSelectRecordFieldIds()); + $this->errors = array_merge($this->errors, $action->getErrors()); + $this->actions[$action_name] = $action; + } + else { + unset($action); + $output->writeln(sprintf("action \"%s\" of job \"%s\" is inactive: ignored.", $action_name, $job_name)); } - $this->destination_fields[$lng] = $row; - $stmt->closeCursor(); - - $this->selectRecordFieldIds[] = $row['id']; } + $this->selectRecordFieldIds = array_unique($this->selectRecordFieldIds); - if (empty($this->destination_fields)) { - $this->errors[] = sprintf("no \"destination_field\" found."); - + if (!empty($this->errors)) { return; } - // misc settings - $this->cleanupDestination = array_key_exists('cleanup_destination', $job_conf) && $job_conf['cleanup_destination'] === true; - $this->cleanupSource = array_key_exists('cleanup_source', $job_conf) ? $job_conf['cleanup_source'] : self::NEVER_CLEANUP_SOURCE; // build records select sql // - $selectRecordClauses = []; - $this->selectRecordParams = []; - if (array_key_exists('if_collection', $job_conf)) { - if (!($coll = $globalConfiguration->getCollection($job_conf['databox'], $job_conf['if_collection']))) { - $this->errors[] = sprintf("unknown collection (%s)", $job_conf['if_collection']); - - return; - } - $selectRecordClauses[] = "`coll_id` = :coll_id"; - $this->selectRecordParams[':coll_id'] = $coll->get_coll_id(); + $selectRecordsClauses = [ + '`record_id` > :minrid' + ]; + $this->selectRecordParams = [ + ':minrid' => 0 + ]; + if ($ifCollection) { + $selectRecordsClauses[] = "`coll_id` = " . (int)($ifCollection->get_coll_id()); } if (array_key_exists('if_status', $job_conf)) { - $selectRecordClauses[] = "`status` & b:sb_and = b:sb_equ"; - $this->selectRecordParams[':sb_and'] = str_replace(['0', 'x'], ['1', '0'], $job_conf['if_status']); - $this->selectRecordParams[':sb_equ'] = str_replace('x', '0', $job_conf['if_status']); + $_and = '0b'.str_replace(['0', 'x'], ['1', '0'], $job_conf['if_status']); + $_equ = '0b'.str_replace('x', '0', $job_conf['if_status']); + $selectRecordsClauses[] = "`status` & " . $_and . " = " . $_equ; } - $selectRecordClauses[] = "`meta_struct_id` IN (" + $cnx = $this->databox->get_connection(); + $selectFieldsClause = "`meta_struct_id` IN (" . join( ',', array_map(function ($id) use ($cnx) { @@ -222,222 +167,109 @@ public function __construct($globalConfiguration, $job_conf, Unicode $unicode, O ) . ")"; - $sql = "SELECT `record_id`, `meta_struct_id`, `metadatas`.`id` AS meta_id, `value` FROM"; - $sql .= " `record` INNER JOIN `metadatas` USING(`record_id`)"; - $sql .= " WHERE " . join(" AND ", $selectRecordClauses); + $sql = "SELECT `r1`.`record_id`, `meta_struct_id`, `metadatas`.`id` AS meta_id, `value` FROM\n"; + $sql .= " (SELECT `record_id` FROM `record` WHERE ".join(" AND ", $selectRecordsClauses)." LIMIT ".$this->bulk.") AS `r1`\n"; + $sql .= " LEFT JOIN `metadatas` ON(`metadatas`.`record_id`=`r1`.`record_id`\n"; + $sql .= " AND " . $selectFieldsClause . ")\n"; $sql .= " ORDER BY `record_id` ASC"; $this->selectRecordsSql = $sql; } public function run() { - $cnx = $this->databox->get_connection(); - $stmt = $cnx->executeQuery($this->selectRecordsSql, $this->selectRecordParams); - - $currentRid = '?'; $this->recordsDone = 0; - $this->notTranslated = []; - $this->incompletelyTranslated = []; - $this->fullyTranslated = []; - - $metas = $emptyValues = array_map(function () { - return []; - }, array_flip($this->selectRecordFieldIds)); - while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { - if ($currentRid == '?') { - $currentRid = $row['record_id']; + + $stmt = $this->databox->get_connection()->prepare($this->selectRecordsSql); + + +// $metas = $emptyValues = array_map(function () { +// return []; +// }, array_flip($this->selectRecordFieldIds)); + + $minrid = 0; + do { + $nrows = 0; + $currentRid = '?'; + $metas = []; + + $this->selectRecordParams[':minrid'] = $minrid; + $stmt->execute($this->selectRecordParams); + while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { + $nrows++; + if ($currentRid == '?') { + $currentRid = $row['record_id']; + } + if ($row['record_id'] !== $currentRid) { + // change record + $this->doRecord($currentRid, $metas); // flush previous record + $currentRid = $row['record_id']; + // $metas = $emptyValues; + $metas = []; + } + if ($row['meta_struct_id'] !== null) { // left join : a record may not have any required field + if (!array_key_exists($row['meta_struct_id'], $metas)) { + $metas[$row['meta_struct_id']] = []; + } + $metas[$row['meta_struct_id']][$row['meta_id']] = $row['value']; + } } - if ($row['record_id'] !== $currentRid) { - // change record - $this->doRecord($currentRid, $metas); // flush previous record - $currentRid = $row['record_id']; - $metas = $emptyValues; + if ($currentRid !== '?') { + $this->doRecord($currentRid, $metas); // flush last record } - $metas[$row['meta_struct_id']][$row['meta_id']] = $row['value']; - } - if($currentRid !== '?') { - $this->doRecord($currentRid, $metas); // flush last record + $stmt->closeCursor(); + $minrid = $currentRid; } - - $stmt->closeCursor(); + while($nrows > 0); // condensed report // if($this->globalConfiguration->getReportFormat() === 'condensed') { $this->output->writeln(sprintf("%d records done.", $this->recordsDone)); - if(!empty($this->notTranslated)) { - ksort($this->notTranslated, SORT_STRING|SORT_FLAG_CASE); + if(!empty($this->condensedReportCounts[self::CONDENSED_REPORT_NOT_TRANSLATED])) { + ksort($this->condensedReportCounts[self::CONDENSED_REPORT_NOT_TRANSLATED], SORT_STRING|SORT_FLAG_CASE); $this->output->writeln("Not translated terms:"); - foreach ($this->notTranslated as $term => $n) { + foreach ($this->condensedReportCounts[self::CONDENSED_REPORT_NOT_TRANSLATED] as $term => $n) { $this->output->writeln(sprintf(" - \"%s\" (%d times)", $term, $n)); } } - if(!empty($this->incompletelyTranslated)) { - ksort($this->incompletelyTranslated, SORT_STRING|SORT_FLAG_CASE); + if(!empty($this->condensedReportCounts[self::CONDENSED_REPORT_INCOMPLETELY_TRANSLATED])) { + ksort($this->condensedReportCounts[self::CONDENSED_REPORT_INCOMPLETELY_TRANSLATED], SORT_STRING|SORT_FLAG_CASE); $this->output->writeln("Incompletely translated terms:"); - foreach ($this->incompletelyTranslated as $term => $n) { + foreach ($this->condensedReportCounts[self::CONDENSED_REPORT_INCOMPLETELY_TRANSLATED] as $term => $n) { $this->output->writeln(sprintf(" - \"%s\" (%d times)", $term, $n)); } } - if(!empty($this->fullyTranslated)) { - ksort($this->fullyTranslated, SORT_STRING|SORT_FLAG_CASE); + if(!empty($this->condensedReportCounts[self::CONDENSED_REPORT_FULLY_TRANSLATED])) { + ksort($this->condensedReportCounts[self::CONDENSED_REPORT_FULLY_TRANSLATED], SORT_STRING|SORT_FLAG_CASE); $this->output->writeln("Fully translated terms:"); - foreach ($this->fullyTranslated as $term => $n) { + foreach ($this->condensedReportCounts[self::CONDENSED_REPORT_FULLY_TRANSLATED] as $term => $n) { $this->output->writeln(sprintf(" - \"%s\" (%d times)", $term, $n)); } } } } - private function doRecord($record_id, $metas) + private function doRecord(string $record_id, array $metas) { $reportFormat = $this->globalConfiguration->getReportFormat(); - if($reportFormat !== 'condensed') { - $this->output->writeln(sprintf("record id: %s", $record_id)); + $this->output->writeln(sprintf("\trecord id: %s", $record_id)); } - $source_field_id = $this->source_field['id']; $meta_to_delete = []; // key = id, to easily keep unique $meta_to_add = []; - if ($this->cleanupDestination) { - foreach ($this->destination_fields as $lng => $destination_field) { - $destination_field_id = $destination_field['id']; - foreach ($metas[$destination_field_id] as $meta_id => $value) { - $meta_to_delete[$meta_id] = $value; - } - unset($meta_id, $value); - } - unset($lng, $destination_field, $destination_field_id); - } - - // loop on every value of the "source_field" + // play all actions // - foreach ($metas[$source_field_id] as $source_meta_id => $source_value) { - - $t = $this->splitTermAndContext($source_value); - $q = '@w=\'' . \thesaurus::xquery_escape($this->unicode->remove_indexer_chars($t[0])) . '\''; - if ($t[1]) { - $q .= ' and @k=\'' . \thesaurus::xquery_escape($this->unicode->remove_indexer_chars($t[1])) . '\''; - } - if(!is_null($this->source_field['lng'])) { - $q .= ' and @lng=\'' . \thesaurus::xquery_escape($this->source_field['lng']) . '\''; - } - $q = '//sy[' . $q . ']/../sy'; - unset($t); - - // loop on every tbranch (one field may be linked to many branches) - // - $translations = []; // ONE translation per lng (first found in th) - /** @var DOMNode $tbranch */ - foreach ($this->tbranches as $tbranch) { - if (!($nodes = $this->xpathTh->query($q, $tbranch))) { - $this->output->writeln(sprintf(" - \"%s\" xpath error on (%s), ignored.", $source_value, $q)); - continue; - } - - // loop on every synonym - // - /** @var DOMElement $node */ - foreach ($nodes as $node) { - $lng = $node->getAttribute('lng'); - - // ignore synonyms not in one of the "destination_field" languages - // - if (!array_key_exists($lng, $this->destination_fields)) { - continue; - } - - $translated_value = $node->getAttribute('v'); - - $destination_field_id = $this->destination_fields[$lng]['id']; - if (!array_key_exists($lng, $translations)) { - if (($destination_meta_id = array_search($translated_value, $metas[$destination_field_id])) === false) { - $translations[$lng] = [ - 'val' => $translated_value, - 'id' => null, - 'msg' => sprintf(" --> %s", $this->destination_fields[$lng]['name']) - ]; - $meta_to_add[$destination_field_id][] = $translated_value; - } - else { - $translations[$lng] = [ - 'val' => $translated_value, - 'id' => $destination_meta_id, - 'msg' => sprintf("already in %s", $this->destination_fields[$lng]['name']) - ]; - unset($meta_to_delete[$destination_meta_id]); - } - unset($destination_meta_id); - } - unset($lng, $destination_field_id, $translated_value); - } - unset($nodes, $node, $tbranch); - } - unset($q); - - // cleanup source - // - if (empty($translations)) { - if($reportFormat === 'all') { - $this->output->writeln(sprintf(" - \"%s\" : no translation found.", $source_value)); - } - $this->addToCondensedReport($source_value, $this->notTranslated); + foreach($this->actions as $action_name => $action) { + if($reportFormat !== 'condensed') { + $this->output->writeln(sprintf("\t\tplaying action \"%s\"", $action_name)); } - else if (count($translations) < count($this->destination_fields)) { - if(in_array($reportFormat, ['all', 'translated'])) { - $this->output->writeln(sprintf(" - \"%s\" : incomplete translation.", $source_value)); - } - $this->addToCondensedReport($source_value, $this->incompletelyTranslated); - } - else { - // complete translation (all target lng) - if(in_array($reportFormat, ['all', 'translated'])) { - $this->output->writeln(sprintf(" - \"%s\" :", $source_value)); - } - $this->addToCondensedReport($source_value, $this->fullyTranslated); - - if ($this->cleanupSource === self::CLEANUP_SOURCE_IF_TRANSLATED) { - // do NOT delete the source term if one translation found it as already present as destination (possible if source=destination) - $used = false; - foreach($translations as $l => $t) { - if($t['id'] === $source_meta_id) { - $used = true; - break; - } - } - if(!$used) { - $meta_to_delete[$source_meta_id] = $metas[$source_field_id][$source_meta_id]; - } - } - } - - if(in_array($reportFormat, ['all', 'translated'])) { - foreach ($translations as $lng => $translation) { - $this->output->writeln(sprintf(" - [%s] \"%s\" %s", $lng, $translation['val'], $translation['msg'])); - } - } - - if ($this->cleanupSource === self::ALWAYS_CLEANUP_SOURCE) { - // do NOT delete the source term if one translation found it as already present as destination (possible if source=destination) - $used = false; - foreach($translations as $l => $t) { - if($t['id'] === $source_meta_id) { - $used = true; - break; - } - } - if(!$used) { - $meta_to_delete[$source_meta_id] = $metas[$source_field_id][$source_meta_id]; - } - } - - unset($lng, $translations, $translation); + $action->doAction($metas, $meta_to_delete, $meta_to_add); } - unset($metas, $source_meta_id, $source_value); + unset($metas); $actions = []; @@ -494,36 +326,29 @@ private function doRecord($record_id, $metas) $this->recordsDone++; } - private function addToCondensedReport($term, &$where) + public function addToCondensedReport(string $term, string $where) { if($this->globalConfiguration->getReportFormat() !== 'condensed') { return; } - if(!array_key_exists($term, $where)) { - $where[$term] = 0; + if(!array_key_exists($where, $this->condensedReportCounts)) { + $this->condensedReportCounts[$where] = []; } - $where[$term]++; + if(!array_key_exists($term, $this->condensedReportCounts[$where])) { + $this->condensedReportCounts[$where][$term] = 0; + } + $this->condensedReportCounts[$where][$term]++; } - private function splitTermAndContext($word) - { - $term = trim($word); - $context = ''; - if (($po = strpos($term, '(')) !== false) { - if (($pc = strpos($term, ')', $po)) !== false) { - $context = trim(substr($term, $po + 1, $pc - $po - 1)); - $term = trim(substr($term, 0, $po)); - } - else { - $context = trim(substr($term, $po + 1)); - $term = trim(substr($term, 0, $po)); - } - } - return [$term, $context]; + /** + * @return GlobalConfiguration + */ + public function getGlobalConfiguration(): GlobalConfiguration + { + return $this->globalConfiguration; } - /** * @return string[] */ @@ -553,5 +378,16 @@ public function isActive(): bool return $this->active; } + public function getDataboxField(string $fieldIdOrName) + { + return $this->globalConfiguration->getField($this->databox->get_sbas_id(), $fieldIdOrName); + } + /** + * @return DOMXpath|false|thesaurus_xpath + */ + public function getXpathTh() + { + return $this->xpathTh; + } } diff --git a/lib/Alchemy/Phrasea/Command/Thesaurus/Translator/TranslateCommand.php b/lib/Alchemy/Phrasea/Command/Thesaurus/Translator/TranslateCommand.php index 1758d50c82..b5a43ac9cb 100644 --- a/lib/Alchemy/Phrasea/Command/Thesaurus/Translator/TranslateCommand.php +++ b/lib/Alchemy/Phrasea/Command/Thesaurus/Translator/TranslateCommand.php @@ -94,22 +94,7 @@ protected function doExecute(InputInterface $input, OutputInterface $output) */ foreach ($this->config->getJobs() as $jobName => $job) { $output->writeln(""); - $output->writeln(sprintf("======== Playing job %s ========", $jobName)); - - if(!$job->isValid()) { - $output->writeln("Configuration error(s)... :"); - foreach ($job->getErrors() as $err) { - $output->writeln(sprintf(" - %s", $err)); - } - $output->writeln("...Job ignored"); - - continue; - } - - if(!$job->isActive()) { - $output->writeln(sprintf("job is inactive, skipped.")); - continue; - } + $output->writeln(sprintf("Playing job \"%s\"", $jobName)); $job->run(); } diff --git a/lib/Alchemy/Phrasea/Command/Thesaurus/Translator/doc/configuration-sample.yml b/lib/Alchemy/Phrasea/Command/Thesaurus/Translator/doc/configuration-sample.yml index dd4dfe5224..96afbcb37a 100644 --- a/lib/Alchemy/Phrasea/Command/Thesaurus/Translator/doc/configuration-sample.yml +++ b/lib/Alchemy/Phrasea/Command/Thesaurus/Translator/doc/configuration-sample.yml @@ -3,45 +3,40 @@ translator: jobs: # - # first job : translate EN keywords to FR and DE - # then change status-bit to elect record for job 2 - keywords_EN_to_FR_DE: + # - translate EN keywords to FR and EN + # nb: since we clean the destination fields, we MUST (re)write EN to EN + # - add country to keywords, both FR/EN + keywords: active: true databox: my_databox if_collection: to_translate - if_status: xx1xxxx - source_field: KeywordsEN - source_lng: en - destination_fields: - - fr:keywordsFR - - de:keywordsDE - cleanup_source: if_translated - # job 1 cleans the destination fields - cleanup_destination: true - # do NOT change collection because job 2 looks here... - #-- set_collection: online - # ... but change status - set_status: 010xxxx + if_status: x1xxxx + actions: - # - # second (last) job : translate EN country to FR and DE, add also as keywords ! - # - country_EN_to_FR_DE: - active: true - databox: my_databox - # same collection as job 1 - if_collection: to_translate - # status was changet by job 1 - if_status: 010xxxx - source_field: CountryEN - source_lng: en - # add translated country to the keywords - destination_fields: - - fr:keywordsFR - - de:keywordsDE - cleanup_source: if_translated - # job 2 must NOT erase what job 1 did - cleanup_destination: false - # the last job will change collection + KeywordsENtoFREN: + active: true + source_field: KeywordsEN + source_lng: en + destination_fields: + - fr:keywordsFR + - en:keywordsEN + cleanup_source: if_translated + # action 1 cleans the destination fields + cleanup_destination: true + + CountryENtoKeywordsFREN: + active: true + source_field: CountryEN + source_lng: en + # add translated country to the keywords + destination_fields: + - fr:keywordsFR + - en:keywordsEN + cleanup_source: if_translated + # action 2 must NOT erase what action 1 did + cleanup_destination: false + + # end of job : change coll status + set_status: 10xxxx set_collection: online - set_status: 100xxxx + From b4b7ebf4ce0a9c5f789a5f576870152c6c814c42 Mon Sep 17 00:00:00 2001 From: Aina Sitraka <35221835+aynsix@users.noreply.github.com> Date: Thu, 16 Nov 2023 19:08:52 +0300 Subject: [PATCH 2/4] PHRAS-3917 Prod - home redirection when session end - catch more 403 (#4399) * redirect at home when session end * catch some error 403 --- Phraseanet-production-client/config/config.js | 2 +- .../dist/authenticate.js | 2 +- .../dist/authenticate.min.js | 2 +- Phraseanet-production-client/dist/commons.js | 2 +- .../dist/commons.min.js | 2 +- Phraseanet-production-client/dist/lightbox.js | 5 + .../dist/lightbox.min.js | 5 + .../dist/production.js | 109 +++++++++++++++++- .../dist/production.min.js | 109 +++++++++++++++++- .../src/components/basket/archive.js | 5 + .../src/components/basket/create.js | 8 +- .../src/components/basket/delete.js | 5 + .../src/components/basket/reorderContent.js | 8 +- .../src/components/basket/update.js | 7 +- .../src/components/lightbox/download.js | 5 + .../src/components/preferences/index.js | 5 + .../src/components/publication/index.js | 6 +- .../src/components/record/delete.js | 5 + .../src/components/record/edit.js | 7 +- .../src/components/record/export.js | 5 + .../src/components/record/move.js | 7 +- .../src/components/record/print.js | 5 + .../src/components/record/property.js | 5 + .../src/components/record/publish.js | 7 +- .../src/components/record/pushbasketModal.js | 7 +- .../src/components/record/sharebasketModal.js | 7 +- .../src/components/record/tools/index.js | 6 +- .../src/components/story/create.js | 5 + .../src/components/story/reorderContent.js | 8 +- .../src/components/ui/workzone/index.js | 10 ++ .../src/phraseanet-common/components/user.js | 7 +- .../Phrasea/Twig/PhraseanetExtension.php | 2 +- 32 files changed, 353 insertions(+), 27 deletions(-) diff --git a/Phraseanet-production-client/config/config.js b/Phraseanet-production-client/config/config.js index c3d24a407b..d04c1782b9 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: 97 + assetFileVersion: 98 }; diff --git a/Phraseanet-production-client/dist/authenticate.js b/Phraseanet-production-client/dist/authenticate.js index d084afdad9..fcdd066040 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=97"; +/******/ script.src = __webpack_require__.p + "lazy-" + ({}[chunkId]||chunkId) + ".js?v=98"; /******/ 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 7c597982c8..b3bab31079 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=97"; +/******/ script.src = __webpack_require__.p + "lazy-" + ({}[chunkId]||chunkId) + ".min.js?v=98"; /******/ 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 d55e728c02..bd3663400e 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=97"; +/******/ script.src = __webpack_require__.p + "lazy-" + ({}[chunkId]||chunkId) + ".js?v=98"; /******/ 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 6f271a5ec8..df88311e87 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=97"; +/******/ script.src = __webpack_require__.p + "lazy-" + ({}[chunkId]||chunkId) + ".min.js?v=98"; /******/ var timeout = setTimeout(onScriptComplete, 120000); /******/ script.onerror = script.onload = onScriptComplete; /******/ function onScriptComplete() { diff --git a/Phraseanet-production-client/dist/lightbox.js b/Phraseanet-production-client/dist/lightbox.js index aac0a3a98f..5f1b857913 100644 --- a/Phraseanet-production-client/dist/lightbox.js +++ b/Phraseanet-production-client/dist/lightbox.js @@ -1249,6 +1249,11 @@ var download = function download(services) { success: function success(data) { $dialog.setContent(data); _onDownloadReady($dialog, window.exportConfig); + }, + error: function error(data) { + if (data.status === 403 && data.getResponseHeader('x-phraseanet-end-session')) { + self.location.replace(self.location.href); // refresh will redirect to login + } } }); diff --git a/Phraseanet-production-client/dist/lightbox.min.js b/Phraseanet-production-client/dist/lightbox.min.js index aac0a3a98f..5f1b857913 100644 --- a/Phraseanet-production-client/dist/lightbox.min.js +++ b/Phraseanet-production-client/dist/lightbox.min.js @@ -1249,6 +1249,11 @@ var download = function download(services) { success: function success(data) { $dialog.setContent(data); _onDownloadReady($dialog, window.exportConfig); + }, + error: function error(data) { + if (data.status === 403 && data.getResponseHeader('x-phraseanet-end-session')) { + self.location.replace(self.location.href); // refresh will redirect to login + } } }); diff --git a/Phraseanet-production-client/dist/production.js b/Phraseanet-production-client/dist/production.js index 2241df118c..7bcf382bfa 100644 --- a/Phraseanet-production-client/dist/production.js +++ b/Phraseanet-production-client/dist/production.js @@ -1617,7 +1617,6 @@ function setPref(name, value) { }, dataType: 'json', timeout: _jquery2.default.data[prefName] = false, - error: _jquery2.default.data[prefName] = false, success: function success(data) { if (data.success) { humane.info(data.message); @@ -1626,6 +1625,12 @@ function setPref(name, value) { } _jquery2.default.data[prefName] = false; return data; + }, + error: function error(data) { + _jquery2.default.data[prefName] = false; + if (data.status === 403 && data.getResponseHeader('x-phraseanet-end-session')) { + self.location.replace(self.location.href); // refresh will redirect to login + } } }); return _jquery2.default.data[prefName]; @@ -3503,6 +3508,10 @@ var publication = function publication(services) { _jquery2.default.post(url + 'prod/feeds/requestavailable/', options, function (data) { return openModal(data); + }).fail(function (data) { + if (data.status === 403 && data.getResponseHeader('x-phraseanet-end-session')) { + self.location.replace(self.location.href); // refresh will redirect to login + } }); return; @@ -4391,6 +4400,10 @@ var sharebasketModal = function sharebasketModal(services, datas) { $dialog.setContent(data); _onDialogReady(); return; + }).fail(function (data) { + if (data.status === 403 && data.getResponseHeader('x-phraseanet-end-session')) { + self.location.replace(self.location.href); // refresh will redirect to login + } }); return true; @@ -5543,8 +5556,11 @@ var editRecord = function editRecord(services) { (0, _jquery2.default)('#tooltip').hide(); return; }, - error: function error(XHR, textStatus, errorThrown) { - if (XHR.status === 0) { + error: function error(data) { + if (data.status === 403 && data.getResponseHeader('x-phraseanet-end-session')) { + self.location.replace(self.location.href); // refresh will redirect to login + } + if (data.status === 0) { return false; } } @@ -7487,6 +7503,11 @@ var exportRecord = function exportRecord(services) { } else { _onExportReady($dialog, window.exportConfig); } + }, + error: function error(data) { + if (data.status === 403 && data.getResponseHeader('x-phraseanet-end-session')) { + self.location.replace(self.location.href); // refresh will redirect to login + } } }); @@ -7911,6 +7932,11 @@ var printRecord = function printRecord(services) { success: function success(data) { (0, _jquery2.default)('#DIALOG').removeClass('loading').empty().append(data); return; + }, + error: function error(data) { + if (data.status === 403 && data.getResponseHeader('x-phraseanet-end-session')) { + self.location.replace(self.location.href); // refresh will redirect to login + } } }); } @@ -10464,6 +10490,11 @@ var workzone = function workzone(services) { }, success: function success(data) { return; + }, + error: function error(data) { + if (data.status === 403 && data.getResponseHeader('x-phraseanet-end-session')) { + self.location.replace(self.location.href); // refresh will redirect to login + } } }); }); @@ -11100,6 +11131,11 @@ var workzone = function workzone(services) { if ('error' in data) { (0, _jquery2.default)('.publication-list').empty().html(data.error); } + }, + error: function error(data) { + if (data.status === 403 && data.getResponseHeader('x-phraseanet-end-session')) { + self.location.replace(self.location.href); // refresh will redirect to login + } } }); @@ -17919,6 +17955,11 @@ var deleteBasket = function deleteBasket(services) { } return false; + }, + error: function error(data) { + if (data.status === 403 && data.getResponseHeader('x-phraseanet-end-session')) { + self.location.replace(self.location.href); // refresh will redirect to login + } } }); }; @@ -19869,6 +19910,11 @@ var archiveBasket = function archiveBasket(services) { alert(data.message); } return; + }, + error: function error(data) { + if (data.status === 403 && data.getResponseHeader('x-phraseanet-end-session')) { + self.location.replace(self.location.href); // refresh will redirect to login + } } }); } @@ -19944,6 +19990,10 @@ var basketCreate = function basketCreate(services) { $dialog.setContent(data); _onDialogReady(); return; + }).fail(function (data) { + if (data.status === 403 && data.getResponseHeader('x-phraseanet-end-session')) { + self.location.replace(self.location.href); // refresh will redirect to login + } }); }; @@ -20076,6 +20126,11 @@ var storyCreate = function storyCreate(services) { _onDialogReady(); return; + }, + error: function error(data) { + if (data.status === 403 && data.getResponseHeader('x-phraseanet-end-session')) { + self.location.replace(self.location.href); // refresh will redirect to login + } } }); }; @@ -20283,6 +20338,10 @@ var basketUpdate = function basketUpdate(services) { $dialog.setContent(data); _onDialogReady(); return; + }).fail(function (data) { + if (data.status === 403 && data.getResponseHeader('x-phraseanet-end-session')) { + self.location.replace(self.location.href); // refresh will redirect to login + } }); }; @@ -20706,7 +20765,12 @@ var basketReorderContent = function basketReorderContent(services) { return _jquery2.default.get(url + 'prod/baskets/' + basketId + '/reorder/', function (data) { $dialog.setContent(data); _onDialogReady(); + return; + }).fail(function (data) { + if (data.status === 403 && data.getResponseHeader('x-phraseanet-end-session')) { + self.location.replace(self.location.href); // refresh will redirect to login + } }); }; @@ -20984,7 +21048,12 @@ var storyReorderContent = function storyReorderContent(services) { return _jquery2.default.get(url + 'prod/story/' + dbId + '/' + recordId + '/reorder/', function (data) { $dialog.setContent(data); _onDialogReady(); + return; + }).fail(function (data) { + if (data.status === 403 && data.getResponseHeader('x-phraseanet-end-session')) { + self.location.replace(self.location.href); // refresh will redirect to login + } }); }; @@ -22086,7 +22155,12 @@ var moveRecord = function moveRecord(services) { return _jquery2.default.ajax({ type: 'POST', url: url + 'prod/records/movecollection/', - data: datas + data: datas, + error: function error(data) { + if (data.status === 403 && data.getResponseHeader('x-phraseanet-end-session')) { + self.location.replace(self.location.href); // refresh will redirect to login + } + } }); }; @@ -62266,6 +62340,11 @@ var deleteRecord = function deleteRecord(services) { //reset top position of dialog $dialog.getDomElement().offsetParent().css('top', ((0, _jquery2.default)(window).height() - $dialog.getDomElement()[0].clientHeight) / 2); _onDialogReady(); + }, + error: function error(data) { + if (data.status === 403 && data.getResponseHeader('x-phraseanet-end-session')) { + self.location.replace(self.location.href); // refresh will redirect to login + } } }); @@ -62469,6 +62548,11 @@ var propertyRecord = function propertyRecord(services) { success: function success(data) { $dialog.setContent(data); _onPropertyReady($dialog); + }, + error: function error(data) { + if (data.status === 403 && data.getResponseHeader('x-phraseanet-end-session')) { + self.location.replace(self.location.href); // refresh will redirect to login + } } }); @@ -62627,6 +62711,10 @@ var pushbasketModal = function pushbasketModal(services, datas) { $dialog.setContent(data); _onDialogReady(); return; + }).fail(function (data) { + if (data.status === 403 && data.getResponseHeader('x-phraseanet-end-session')) { + self.location.replace(self.location.href); // refresh will redirect to login + } }); return true; @@ -62686,6 +62774,10 @@ var recordPublishModal = function recordPublishModal(services, datas) { _jquery2.default.post(url + 'prod/feeds/requestavailable/', datas, function (data) { return (0, _publication2.default)(services).openModal(data); + }).fail(function (data) { + if (data.status === 403 && data.getResponseHeader('x-phraseanet-end-session')) { + self.location.replace(self.location.href); // refresh will redirect to login + } }); return true; @@ -62761,6 +62853,10 @@ var recordToolsModal = function recordToolsModal(services, datas) { $dialog.setOption('contextArgs', datas); _onModalReady(data, window.toolsConfig, activeTab); return; + }).fail(function (data) { + if (data.status === 403 && data.getResponseHeader('x-phraseanet-end-session')) { + self.location.replace(self.location.href); // refresh will redirect to login + } }); }; @@ -65169,6 +65265,11 @@ var preferences = function preferences(services) { (0, _jquery2.default)('body').removeClass().addClass('PNB ' + color); /* console.log('saved:' + color);*/ return; + }, + error: function error(data) { + if (data.status === 403 && data.getResponseHeader('x-phraseanet-end-session')) { + self.location.replace(self.location.href); // refresh will redirect to login + } } }); }); diff --git a/Phraseanet-production-client/dist/production.min.js b/Phraseanet-production-client/dist/production.min.js index 2241df118c..7bcf382bfa 100644 --- a/Phraseanet-production-client/dist/production.min.js +++ b/Phraseanet-production-client/dist/production.min.js @@ -1617,7 +1617,6 @@ function setPref(name, value) { }, dataType: 'json', timeout: _jquery2.default.data[prefName] = false, - error: _jquery2.default.data[prefName] = false, success: function success(data) { if (data.success) { humane.info(data.message); @@ -1626,6 +1625,12 @@ function setPref(name, value) { } _jquery2.default.data[prefName] = false; return data; + }, + error: function error(data) { + _jquery2.default.data[prefName] = false; + if (data.status === 403 && data.getResponseHeader('x-phraseanet-end-session')) { + self.location.replace(self.location.href); // refresh will redirect to login + } } }); return _jquery2.default.data[prefName]; @@ -3503,6 +3508,10 @@ var publication = function publication(services) { _jquery2.default.post(url + 'prod/feeds/requestavailable/', options, function (data) { return openModal(data); + }).fail(function (data) { + if (data.status === 403 && data.getResponseHeader('x-phraseanet-end-session')) { + self.location.replace(self.location.href); // refresh will redirect to login + } }); return; @@ -4391,6 +4400,10 @@ var sharebasketModal = function sharebasketModal(services, datas) { $dialog.setContent(data); _onDialogReady(); return; + }).fail(function (data) { + if (data.status === 403 && data.getResponseHeader('x-phraseanet-end-session')) { + self.location.replace(self.location.href); // refresh will redirect to login + } }); return true; @@ -5543,8 +5556,11 @@ var editRecord = function editRecord(services) { (0, _jquery2.default)('#tooltip').hide(); return; }, - error: function error(XHR, textStatus, errorThrown) { - if (XHR.status === 0) { + error: function error(data) { + if (data.status === 403 && data.getResponseHeader('x-phraseanet-end-session')) { + self.location.replace(self.location.href); // refresh will redirect to login + } + if (data.status === 0) { return false; } } @@ -7487,6 +7503,11 @@ var exportRecord = function exportRecord(services) { } else { _onExportReady($dialog, window.exportConfig); } + }, + error: function error(data) { + if (data.status === 403 && data.getResponseHeader('x-phraseanet-end-session')) { + self.location.replace(self.location.href); // refresh will redirect to login + } } }); @@ -7911,6 +7932,11 @@ var printRecord = function printRecord(services) { success: function success(data) { (0, _jquery2.default)('#DIALOG').removeClass('loading').empty().append(data); return; + }, + error: function error(data) { + if (data.status === 403 && data.getResponseHeader('x-phraseanet-end-session')) { + self.location.replace(self.location.href); // refresh will redirect to login + } } }); } @@ -10464,6 +10490,11 @@ var workzone = function workzone(services) { }, success: function success(data) { return; + }, + error: function error(data) { + if (data.status === 403 && data.getResponseHeader('x-phraseanet-end-session')) { + self.location.replace(self.location.href); // refresh will redirect to login + } } }); }); @@ -11100,6 +11131,11 @@ var workzone = function workzone(services) { if ('error' in data) { (0, _jquery2.default)('.publication-list').empty().html(data.error); } + }, + error: function error(data) { + if (data.status === 403 && data.getResponseHeader('x-phraseanet-end-session')) { + self.location.replace(self.location.href); // refresh will redirect to login + } } }); @@ -17919,6 +17955,11 @@ var deleteBasket = function deleteBasket(services) { } return false; + }, + error: function error(data) { + if (data.status === 403 && data.getResponseHeader('x-phraseanet-end-session')) { + self.location.replace(self.location.href); // refresh will redirect to login + } } }); }; @@ -19869,6 +19910,11 @@ var archiveBasket = function archiveBasket(services) { alert(data.message); } return; + }, + error: function error(data) { + if (data.status === 403 && data.getResponseHeader('x-phraseanet-end-session')) { + self.location.replace(self.location.href); // refresh will redirect to login + } } }); } @@ -19944,6 +19990,10 @@ var basketCreate = function basketCreate(services) { $dialog.setContent(data); _onDialogReady(); return; + }).fail(function (data) { + if (data.status === 403 && data.getResponseHeader('x-phraseanet-end-session')) { + self.location.replace(self.location.href); // refresh will redirect to login + } }); }; @@ -20076,6 +20126,11 @@ var storyCreate = function storyCreate(services) { _onDialogReady(); return; + }, + error: function error(data) { + if (data.status === 403 && data.getResponseHeader('x-phraseanet-end-session')) { + self.location.replace(self.location.href); // refresh will redirect to login + } } }); }; @@ -20283,6 +20338,10 @@ var basketUpdate = function basketUpdate(services) { $dialog.setContent(data); _onDialogReady(); return; + }).fail(function (data) { + if (data.status === 403 && data.getResponseHeader('x-phraseanet-end-session')) { + self.location.replace(self.location.href); // refresh will redirect to login + } }); }; @@ -20706,7 +20765,12 @@ var basketReorderContent = function basketReorderContent(services) { return _jquery2.default.get(url + 'prod/baskets/' + basketId + '/reorder/', function (data) { $dialog.setContent(data); _onDialogReady(); + return; + }).fail(function (data) { + if (data.status === 403 && data.getResponseHeader('x-phraseanet-end-session')) { + self.location.replace(self.location.href); // refresh will redirect to login + } }); }; @@ -20984,7 +21048,12 @@ var storyReorderContent = function storyReorderContent(services) { return _jquery2.default.get(url + 'prod/story/' + dbId + '/' + recordId + '/reorder/', function (data) { $dialog.setContent(data); _onDialogReady(); + return; + }).fail(function (data) { + if (data.status === 403 && data.getResponseHeader('x-phraseanet-end-session')) { + self.location.replace(self.location.href); // refresh will redirect to login + } }); }; @@ -22086,7 +22155,12 @@ var moveRecord = function moveRecord(services) { return _jquery2.default.ajax({ type: 'POST', url: url + 'prod/records/movecollection/', - data: datas + data: datas, + error: function error(data) { + if (data.status === 403 && data.getResponseHeader('x-phraseanet-end-session')) { + self.location.replace(self.location.href); // refresh will redirect to login + } + } }); }; @@ -62266,6 +62340,11 @@ var deleteRecord = function deleteRecord(services) { //reset top position of dialog $dialog.getDomElement().offsetParent().css('top', ((0, _jquery2.default)(window).height() - $dialog.getDomElement()[0].clientHeight) / 2); _onDialogReady(); + }, + error: function error(data) { + if (data.status === 403 && data.getResponseHeader('x-phraseanet-end-session')) { + self.location.replace(self.location.href); // refresh will redirect to login + } } }); @@ -62469,6 +62548,11 @@ var propertyRecord = function propertyRecord(services) { success: function success(data) { $dialog.setContent(data); _onPropertyReady($dialog); + }, + error: function error(data) { + if (data.status === 403 && data.getResponseHeader('x-phraseanet-end-session')) { + self.location.replace(self.location.href); // refresh will redirect to login + } } }); @@ -62627,6 +62711,10 @@ var pushbasketModal = function pushbasketModal(services, datas) { $dialog.setContent(data); _onDialogReady(); return; + }).fail(function (data) { + if (data.status === 403 && data.getResponseHeader('x-phraseanet-end-session')) { + self.location.replace(self.location.href); // refresh will redirect to login + } }); return true; @@ -62686,6 +62774,10 @@ var recordPublishModal = function recordPublishModal(services, datas) { _jquery2.default.post(url + 'prod/feeds/requestavailable/', datas, function (data) { return (0, _publication2.default)(services).openModal(data); + }).fail(function (data) { + if (data.status === 403 && data.getResponseHeader('x-phraseanet-end-session')) { + self.location.replace(self.location.href); // refresh will redirect to login + } }); return true; @@ -62761,6 +62853,10 @@ var recordToolsModal = function recordToolsModal(services, datas) { $dialog.setOption('contextArgs', datas); _onModalReady(data, window.toolsConfig, activeTab); return; + }).fail(function (data) { + if (data.status === 403 && data.getResponseHeader('x-phraseanet-end-session')) { + self.location.replace(self.location.href); // refresh will redirect to login + } }); }; @@ -65169,6 +65265,11 @@ var preferences = function preferences(services) { (0, _jquery2.default)('body').removeClass().addClass('PNB ' + color); /* console.log('saved:' + color);*/ return; + }, + error: function error(data) { + if (data.status === 403 && data.getResponseHeader('x-phraseanet-end-session')) { + self.location.replace(self.location.href); // refresh will redirect to login + } } }); }); diff --git a/Phraseanet-production-client/src/components/basket/archive.js b/Phraseanet-production-client/src/components/basket/archive.js index 20b889264f..c80fb25318 100644 --- a/Phraseanet-production-client/src/components/basket/archive.js +++ b/Phraseanet-production-client/src/components/basket/archive.js @@ -45,6 +45,11 @@ const archiveBasket = (services) => { alert(data.message); } return; + }, + error: function (data) { + if (data.status === 403 && data.getResponseHeader('x-phraseanet-end-session')) { + self.location.replace(self.location.href); // refresh will redirect to login + } } }); } diff --git a/Phraseanet-production-client/src/components/basket/create.js b/Phraseanet-production-client/src/components/basket/create.js index f32d125b58..45a4438b50 100644 --- a/Phraseanet-production-client/src/components/basket/create.js +++ b/Phraseanet-production-client/src/components/basket/create.js @@ -40,7 +40,13 @@ const basketCreate = (services) => { $dialog.setContent(data); _onDialogReady(); return; - }); + }) + .fail(function (data) { + if (data.status === 403 && data.getResponseHeader('x-phraseanet-end-session')) { + self.location.replace(self.location.href); // refresh will redirect to login + } + }) + ; }; const _onDialogReady = () => { diff --git a/Phraseanet-production-client/src/components/basket/delete.js b/Phraseanet-production-client/src/components/basket/delete.js index f32261d247..6085bffa35 100644 --- a/Phraseanet-production-client/src/components/basket/delete.js +++ b/Phraseanet-production-client/src/components/basket/delete.js @@ -139,6 +139,11 @@ const deleteBasket = (services) => { } return false; + }, + error: function (data) { + if (data.status === 403 && data.getResponseHeader('x-phraseanet-end-session')) { + self.location.replace(self.location.href); // refresh will redirect to login + } } }); }; diff --git a/Phraseanet-production-client/src/components/basket/reorderContent.js b/Phraseanet-production-client/src/components/basket/reorderContent.js index bc99add63a..f30e1b60e3 100644 --- a/Phraseanet-production-client/src/components/basket/reorderContent.js +++ b/Phraseanet-production-client/src/components/basket/reorderContent.js @@ -41,8 +41,14 @@ const basketReorderContent = (services) => { return $.get(`${url}prod/baskets/${basketId}/reorder/`, function (data) { $dialog.setContent(data); _onDialogReady(); + return; - }); + }).fail(function (data) { + if (data.status === 403 && data.getResponseHeader('x-phraseanet-end-session')) { + self.location.replace(self.location.href); // refresh will redirect to login + } + }) + ; }; const _onDialogReady = () => { diff --git a/Phraseanet-production-client/src/components/basket/update.js b/Phraseanet-production-client/src/components/basket/update.js index a6d08e2750..ebbb1d666a 100644 --- a/Phraseanet-production-client/src/components/basket/update.js +++ b/Phraseanet-production-client/src/components/basket/update.js @@ -43,7 +43,12 @@ const basketUpdate = (services) => { $dialog.setContent(data); _onDialogReady(); return; - }); + }).fail(function (data) { + if (data.status === 403 && data.getResponseHeader('x-phraseanet-end-session')) { + self.location.replace(self.location.href); // refresh will redirect to login + } + }) + ; }; const _onDialogReady = () => { diff --git a/Phraseanet-production-client/src/components/lightbox/download.js b/Phraseanet-production-client/src/components/lightbox/download.js index 6c88005555..3059e7c1fb 100644 --- a/Phraseanet-production-client/src/components/lightbox/download.js +++ b/Phraseanet-production-client/src/components/lightbox/download.js @@ -32,6 +32,11 @@ const download = (services) => { success: function (data) { $dialog.setContent(data); _onDownloadReady($dialog, window.exportConfig); + }, + error: function (data) { + if (data.status === 403 && data.getResponseHeader('x-phraseanet-end-session')) { + self.location.replace(self.location.href); // refresh will redirect to login + } } }); diff --git a/Phraseanet-production-client/src/components/preferences/index.js b/Phraseanet-production-client/src/components/preferences/index.js index 05ea6ecb4c..40b20d8d4d 100644 --- a/Phraseanet-production-client/src/components/preferences/index.js +++ b/Phraseanet-production-client/src/components/preferences/index.js @@ -207,6 +207,11 @@ const preferences = services => { $('body').removeClass().addClass('PNB ' + color); /* console.log('saved:' + color);*/ return; + }, + error: function (data) { + if (data.status === 403 && data.getResponseHeader('x-phraseanet-end-session')) { + self.location.replace(self.location.href); // refresh will redirect to login + } } }); diff --git a/Phraseanet-production-client/src/components/publication/index.js b/Phraseanet-production-client/src/components/publication/index.js index 748032471b..9e57aca8ff 100644 --- a/Phraseanet-production-client/src/components/publication/index.js +++ b/Phraseanet-production-client/src/components/publication/index.js @@ -472,7 +472,11 @@ const publication = (services) => { , function (data) { return openModal(data); - }); + }).fail(function (data) { + if (data.status === 403 && data.getResponseHeader('x-phraseanet-end-session')) { + self.location.replace(self.location.href); // refresh will redirect to login + } + }); return; }; diff --git a/Phraseanet-production-client/src/components/record/delete.js b/Phraseanet-production-client/src/components/record/delete.js index e6ed7eddc3..827289b92e 100644 --- a/Phraseanet-production-client/src/components/record/delete.js +++ b/Phraseanet-production-client/src/components/record/delete.js @@ -24,6 +24,11 @@ const deleteRecord = (services) => { //reset top position of dialog $dialog.getDomElement().offsetParent().css('top', ($(window).height() - $dialog.getDomElement()[0].clientHeight) / 2); _onDialogReady(); + }, + error: function (data) { + if (data.status === 403 && data.getResponseHeader('x-phraseanet-end-session')) { + self.location.replace(self.location.href); // refresh will redirect to login + } } }); diff --git a/Phraseanet-production-client/src/components/record/edit.js b/Phraseanet-production-client/src/components/record/edit.js index 0a234334b1..7e060e60da 100644 --- a/Phraseanet-production-client/src/components/record/edit.js +++ b/Phraseanet-production-client/src/components/record/edit.js @@ -74,8 +74,11 @@ const editRecord = (services) => { $('#tooltip').hide(); return; }, - error: function (XHR, textStatus, errorThrown) { - if (XHR.status === 0) { + error: function (data) { + if (data.status === 403 && data.getResponseHeader('x-phraseanet-end-session')) { + self.location.replace(self.location.href); // refresh will redirect to login + } + if (data.status === 0) { return false; } } diff --git a/Phraseanet-production-client/src/components/record/export.js b/Phraseanet-production-client/src/components/record/export.js index 324d33e0df..11ce48e163 100644 --- a/Phraseanet-production-client/src/components/record/export.js +++ b/Phraseanet-production-client/src/components/record/export.js @@ -64,6 +64,11 @@ const exportRecord = services => { else { _onExportReady($dialog, window.exportConfig); } + }, + error: function (data) { + if (data.status === 403 && data.getResponseHeader('x-phraseanet-end-session')) { + self.location.replace(self.location.href); // refresh will redirect to login + } } }); diff --git a/Phraseanet-production-client/src/components/record/move.js b/Phraseanet-production-client/src/components/record/move.js index 3e2d840b34..1fe740cb0f 100644 --- a/Phraseanet-production-client/src/components/record/move.js +++ b/Phraseanet-production-client/src/components/record/move.js @@ -86,7 +86,12 @@ const moveRecord = (services) => { return $.ajax({ type: 'POST', url: `${url}prod/records/movecollection/`, - data: datas + data: datas, + error: function (data) { + if (data.status === 403 && data.getResponseHeader('x-phraseanet-end-session')) { + self.location.replace(self.location.href); // refresh will redirect to login + } + } }); }; diff --git a/Phraseanet-production-client/src/components/record/print.js b/Phraseanet-production-client/src/components/record/print.js index 720eb2f2d3..8eafcda41f 100644 --- a/Phraseanet-production-client/src/components/record/print.js +++ b/Phraseanet-production-client/src/components/record/print.js @@ -69,6 +69,11 @@ const printRecord = (services) => { $('#DIALOG').removeClass('loading').empty() .append(data); return; + }, + error: function (data) { + if (data.status === 403 && data.getResponseHeader('x-phraseanet-end-session')) { + self.location.replace(self.location.href); // refresh will redirect to login + } } }); } diff --git a/Phraseanet-production-client/src/components/record/property.js b/Phraseanet-production-client/src/components/record/property.js index 6d54af95b1..e069c6f08b 100644 --- a/Phraseanet-production-client/src/components/record/property.js +++ b/Phraseanet-production-client/src/components/record/property.js @@ -22,6 +22,11 @@ const propertyRecord = (services) => { success: function (data) { $dialog.setContent(data); _onPropertyReady($dialog); + }, + error: function (data) { + if (data.status === 403 && data.getResponseHeader('x-phraseanet-end-session')) { + self.location.replace(self.location.href); // refresh will redirect to login + } } }); diff --git a/Phraseanet-production-client/src/components/record/publish.js b/Phraseanet-production-client/src/components/record/publish.js index 049cf61918..f54d20716e 100644 --- a/Phraseanet-production-client/src/components/record/publish.js +++ b/Phraseanet-production-client/src/components/record/publish.js @@ -13,7 +13,12 @@ const recordPublishModal = (services, datas) => { , function (data) { return publication(services).openModal(data); - }); + }).fail(function (data) { + if (data.status === 403 && data.getResponseHeader('x-phraseanet-end-session')) { + self.location.replace(self.location.href); // refresh will redirect to login + } + }) + ; return true; }; diff --git a/Phraseanet-production-client/src/components/record/pushbasketModal.js b/Phraseanet-production-client/src/components/record/pushbasketModal.js index 70f762dd7a..4b0f151788 100644 --- a/Phraseanet-production-client/src/components/record/pushbasketModal.js +++ b/Phraseanet-production-client/src/components/record/pushbasketModal.js @@ -23,7 +23,12 @@ const pushbasketModal = (services, datas) => { $dialog.setContent(data); _onDialogReady(); return; - }); + }).fail(function (data) { + if (data.status === 403 && data.getResponseHeader('x-phraseanet-end-session')) { + self.location.replace(self.location.href); // refresh will redirect to login + } + }) + ; return true; }; diff --git a/Phraseanet-production-client/src/components/record/sharebasketModal.js b/Phraseanet-production-client/src/components/record/sharebasketModal.js index 3ea16070be..751f954331 100644 --- a/Phraseanet-production-client/src/components/record/sharebasketModal.js +++ b/Phraseanet-production-client/src/components/record/sharebasketModal.js @@ -25,7 +25,12 @@ const sharebasketModal = (services, datas) => { $dialog.setContent(data); _onDialogReady(); return; - }); + }).fail(function (data) { + if (data.status === 403 && data.getResponseHeader('x-phraseanet-end-session')) { + self.location.replace(self.location.href); // refresh will redirect to login + } + }) + ; return true; }; diff --git a/Phraseanet-production-client/src/components/record/tools/index.js b/Phraseanet-production-client/src/components/record/tools/index.js index 1793fe8f55..f44d53ee04 100644 --- a/Phraseanet-production-client/src/components/record/tools/index.js +++ b/Phraseanet-production-client/src/components/record/tools/index.js @@ -36,7 +36,11 @@ const recordToolsModal = (services, datas, activeTab = false) => { _onModalReady(data, window.toolsConfig, activeTab); return; } - ); + ).fail(function (data) { + if (data.status === 403 && data.getResponseHeader('x-phraseanet-end-session')) { + self.location.replace(self.location.href); // refresh will redirect to login + } + }); }; diff --git a/Phraseanet-production-client/src/components/story/create.js b/Phraseanet-production-client/src/components/story/create.js index aceb208888..48a7f92c34 100644 --- a/Phraseanet-production-client/src/components/story/create.js +++ b/Phraseanet-production-client/src/components/story/create.js @@ -50,6 +50,11 @@ const storyCreate = (services) => { _onDialogReady(); return; + }, + error: function (data) { + if (data.status === 403 && data.getResponseHeader('x-phraseanet-end-session')) { + self.location.replace(self.location.href); // refresh will redirect to login + } } }); }; diff --git a/Phraseanet-production-client/src/components/story/reorderContent.js b/Phraseanet-production-client/src/components/story/reorderContent.js index 7b60847975..26c6c3c3c0 100644 --- a/Phraseanet-production-client/src/components/story/reorderContent.js +++ b/Phraseanet-production-client/src/components/story/reorderContent.js @@ -36,8 +36,14 @@ const storyReorderContent = (services) => { return $.get(`${url}prod/story/${dbId}/${recordId}/reorder/`, function (data) { $dialog.setContent(data); _onDialogReady(); + return; - }); + }).fail(function (data) { + if (data.status === 403 && data.getResponseHeader('x-phraseanet-end-session')) { + self.location.replace(self.location.href); // refresh will redirect to login + } + }) + ; }; const _onDialogReady = () => { diff --git a/Phraseanet-production-client/src/components/ui/workzone/index.js b/Phraseanet-production-client/src/components/ui/workzone/index.js index 1eeacf5633..d9a8cc2f86 100644 --- a/Phraseanet-production-client/src/components/ui/workzone/index.js +++ b/Phraseanet-production-client/src/components/ui/workzone/index.js @@ -538,6 +538,11 @@ const workzone = (services) => { }, success: function (data) { return; + }, + error: function (data) { + if (data.status === 403 && data.getResponseHeader('x-phraseanet-end-session')) { + self.location.replace(self.location.href); // refresh will redirect to login + } } }); }); @@ -1186,6 +1191,11 @@ const workzone = (services) => { if ('error' in data) { $('.publication-list').empty().html(data.error); } + }, + error: function (data) { + if (data.status === 403 && data.getResponseHeader('x-phraseanet-end-session')) { + self.location.replace(self.location.href); // refresh will redirect to login + } } }); diff --git a/Phraseanet-production-client/src/phraseanet-common/components/user.js b/Phraseanet-production-client/src/phraseanet-common/components/user.js index 96880da53b..f987377934 100644 --- a/Phraseanet-production-client/src/phraseanet-common/components/user.js +++ b/Phraseanet-production-client/src/phraseanet-common/components/user.js @@ -22,7 +22,6 @@ function setPref(name, value) { }, dataType: 'json', timeout: $.data[prefName] = false, - error: $.data[prefName] = false, success: (data) => { if (data.success) { humane.info(data.message); @@ -31,6 +30,12 @@ function setPref(name, value) { } $.data[prefName] = false; return data; + }, + error: function (data) { + $.data[prefName] = false; + if (data.status === 403 && data.getResponseHeader('x-phraseanet-end-session')) { + self.location.replace(self.location.href); // refresh will redirect to login + } } }); return $.data[prefName]; diff --git a/lib/Alchemy/Phrasea/Twig/PhraseanetExtension.php b/lib/Alchemy/Phrasea/Twig/PhraseanetExtension.php index 1f84f0c4ec..f211cf8734 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' => 97 + 'assetFileVersion' => 98 ]; } From dc80246f56b6a627496aca176bab6737d42fe96e Mon Sep 17 00:00:00 2001 From: jygaulier Date: Thu, 16 Nov 2023 17:32:19 +0100 Subject: [PATCH 3/4] PHRAS-3770_quarantine-enhancement (#4352) * wip * add: quarantine: when "adding", can copy metadata from the selected record * fix failing test ; add test for "add & copy caption" (to be completed with field values...) * wip * add: quarantine: when "adding", can copy metadata from the selected record * fix failing test ; add test for "add & copy caption" (to be completed with field values...) * fix add button (did nothing when no doc selected at right) ; add clickable label on "copy meta" ckbox * fix missing sb-off icons --- .../Controller/Prod/LazaretController.php | 50 ++++- .../Phrasea/ControllerProvider/Api/V2.php | 3 + .../ControllerProvider/Prod/Lazaret.php | 8 + templates/web/prod/upload/lazaret.html.twig | 200 ++++++++++++------ .../Phrasea/Controller/Prod/LazaretTest.php | 125 ++++++++++- 5 files changed, 315 insertions(+), 71 deletions(-) diff --git a/lib/Alchemy/Phrasea/Controller/Prod/LazaretController.php b/lib/Alchemy/Phrasea/Controller/Prod/LazaretController.php index 85cf5195d0..29cfe6d77e 100644 --- a/lib/Alchemy/Phrasea/Controller/Prod/LazaretController.php +++ b/lib/Alchemy/Phrasea/Controller/Prod/LazaretController.php @@ -132,6 +132,38 @@ public function addElement(Request $request, $file_id) /** @var LazaretManipulator $lazaretManipulator */ $lazaretManipulator = $this->app['manipulator.lazaret']; + //Check if the chosen record is eligible to the substitution + $recordId = $request->request->get('record_id'); + /** @var LazaretFile $lazaretFile */ + $metadatasToSet = []; + if($recordId !== "" && !!$request->request->get('copy_meta', false)) { + + $substitutedRecord = null; + + $lazaretFile = $this->getLazaretFileRepository()->find($file_id); + foreach ($lazaretFile->getRecordsToSubstitute($this->app) as $r) { + if ($r->getRecordId() === (int)$recordId) { + $substitutedRecord = $r; + break; + } + } + if (!$substitutedRecord) { + $ret['message'] = $this->app->trans('The destination record provided is not allowed'); + + return $this->app->json($ret); + } + + $fieldsToCopy = []; + foreach ($substitutedRecord->getDatabox()->get_meta_structure() as $df) { + if(!$df->is_readonly()) { + $fieldsToCopy[] = $df->get_name(); + } + } + foreach ($substitutedRecord->getCaption($fieldsToCopy) as $k=>$v) { + $metadatasToSet[] = ['field_name' => $k, 'value' => $v]; + } + } + $ret = $lazaretManipulator->add($file_id, $keepAttributes, $attributesToKeep); try{ @@ -140,7 +172,13 @@ public function addElement(Request $request, $file_id) $postStatus = (array) $request->request->get('status'); // update status $this->updateRecordStatus($record, $postStatus); - }catch(\Exception $e){ + + if(!empty($metadatasToSet)) { + $actions = json_decode(json_encode(['metadatas' => $metadatasToSet])); + $record->setMetadatasByActions($actions); + } + } + catch(\Exception $e){ $ret['message'] = $this->app->trans('An error occured when wanting to change status!'); } @@ -229,16 +267,14 @@ public function acceptElement(Request $request, $file_id) return $this->app->json($ret); } - $found = false; //Check if the chosen record is eligible to the substitution + $found = false; foreach ($lazaretFile->getRecordsToSubstitute($this->app) as $record) { - if ($record->getRecordId() !== (int) $recordId) { - continue; + if ($record->getRecordId() === (int) $recordId) { + $found = true; + break; } - - $found = true; - break; } if (!$found) { diff --git a/lib/Alchemy/Phrasea/ControllerProvider/Api/V2.php b/lib/Alchemy/Phrasea/ControllerProvider/Api/V2.php index 7ff511332a..3ca74cac8d 100644 --- a/lib/Alchemy/Phrasea/ControllerProvider/Api/V2.php +++ b/lib/Alchemy/Phrasea/ControllerProvider/Api/V2.php @@ -104,13 +104,16 @@ public function connect(Application $app) $controllers->match('/search/', 'controller.api.v2.search:searchAction'); + /** @uses LazaretController::quarantineItemEmptyAction */ $controllers->delete('/quarantine/', 'controller.api.v2.lazaret:quarantineItemEmptyAction') ->bind('api_v2_quarantine_empty'); + /** @uses LazaretController::quarantineItemDeleteAction */ $controller = $controllers->delete('/quarantine/item/{lazaret_id}/', 'controller.api.v2.lazaret:quarantineItemDeleteAction') ->bind('api_v2_quarantine_item_delete'); $this->addQuarantineMiddleware($controller); + /** @uses LazaretController::quarantineItemAddAction */ $controller = $controllers->post('/quarantine/item/{lazaret_id}/add/', 'controller.api.v2.lazaret:quarantineItemAddAction') ->bind('api_v2_quarantine_item_add'); $this->addQuarantineMiddleware($controller); diff --git a/lib/Alchemy/Phrasea/ControllerProvider/Prod/Lazaret.php b/lib/Alchemy/Phrasea/ControllerProvider/Prod/Lazaret.php index 53c2804fce..4b9e1d1932 100644 --- a/lib/Alchemy/Phrasea/ControllerProvider/Prod/Lazaret.php +++ b/lib/Alchemy/Phrasea/ControllerProvider/Prod/Lazaret.php @@ -56,32 +56,40 @@ public function connect(Application $app) $firewall->requireRight(\ACL::CANADDRECORD); }); + /** @uses LazaretController::listElement */ $controllers->get('/', 'controller.prod.lazaret:listElement') ->bind('lazaret_elements'); + /** @uses LazaretController::getElement */ $controllers->get('/{file_id}/', 'controller.prod.lazaret:getElement') ->assert('file_id', '\d+') ->bind('lazaret_element'); + /** @uses LazaretController::addElement */ $controllers->post('/{file_id}/force-add/', 'controller.prod.lazaret:addElement') ->assert('file_id', '\d+') ->bind('lazaret_force_add'); + /** @uses LazaretController::denyElement */ $controllers->post('/{file_id}/deny/', 'controller.prod.lazaret:denyElement') ->assert('file_id', '\d+') ->bind('lazaret_deny_element'); + /** @uses LazaretController::emptyLazaret */ $controllers->post('/empty/', 'controller.prod.lazaret:emptyLazaret') ->bind('lazaret_empty'); + /** @uses LazaretController::acceptElement */ $controllers->post('/{file_id}/accept/', 'controller.prod.lazaret:acceptElement') ->assert('file_id', '\d+') ->bind('lazaret_accept'); + /** @uses LazaretController::thumbnailElement */ $controllers->get('/{file_id}/thumbnail/', 'controller.prod.lazaret:thumbnailElement') ->assert('file_id', '\d+') ->bind('lazaret_thumbnail'); + /** @uses LazaretController::getDestinationStatus */ $controllers->get('/{databox_id}/{record_id}/status', 'controller.prod.lazaret:getDestinationStatus') ->assert('databox_id', '\d+') ->assert('record_id', '\d+') diff --git a/templates/web/prod/upload/lazaret.html.twig b/templates/web/prod/upload/lazaret.html.twig index 74cdce9c19..62b84a5a0d 100644 --- a/templates/web/prod/upload/lazaret.html.twig +++ b/templates/web/prod/upload/lazaret.html.twig @@ -33,8 +33,9 @@