+
+ 0
+
+
+EOT;
+ $this->logo = '';
+ $this->labels = array(
+ 'en' => '',
+ 'fr' => '',
+ 'de' => '',
+ 'nl' => ''
+ );
+ $this->publicWatermark = '';
+ }
+
+ /**
+ * @return int
+ */
+ public function getDataboxId()
+ {
+ return $this->databoxId;
+ }
+
+ /**
+ * @return int
+ */
+ public function getCollectionId()
+ {
+ return $this->collectionId;
+ }
+
+ /**
+ * @param $collectionId
+ */
+ public function setCollectionId($collectionId)
+ {
+ if ($this->collectionId > 0) {
+ throw new LogicException('Cannot change the ID of an existing collection.');
+ }
+
+ $this->collectionId = (int) $collectionId;
+ }
+
+ /**
+ * @return string
+ */
+ public function getName()
+ {
+ return $this->name;
+ }
+
+ /**
+ * @param string $name
+ */
+ public function setName($name)
+ {
+ $name = trim(strip_tags($name));
+
+ if ($name === '') {
+ throw new \InvalidArgumentException();
+ }
+
+ $this->name = $name;
+ }
+
+ /**
+ * @return \string[]
+ */
+ public function getLabels()
+ {
+ return $this->labels;
+ }
+
+ /**
+ * @param \string[] $labels
+ */
+ public function setLabels($labels)
+ {
+ $this->labels = $labels;
+ }
+
+ /**
+ * @param $lang
+ * @param bool $substitute
+ * @return string
+ */
+ public function getLabel($lang, $substitute = true)
+ {
+ if (!array_key_exists($lang, $this->labels)) {
+ throw new \InvalidArgumentException(sprintf('Code %s is not defined', $lang));
+ }
+
+ if ($substitute) {
+ return isset($this->labels[$lang]) ? $this->labels[$lang] : $this->name;
+ } else {
+ return $this->labels[$lang];
+ }
+ }
+
+ /**
+ * @param $lang
+ * @param $label
+ */
+ public function setLabel($lang, $label)
+ {
+ if (!array_key_exists($lang, $this->labels)) {
+ throw new \InvalidArgumentException(sprintf("Language '%s' is not defined.", $lang));
+ }
+
+ $this->labels[$lang] = $label;
+ }
+
+ /**
+ * @return \int[]|string|null
+ */
+ public function getLogo()
+ {
+ return $this->logo;
+ }
+
+ /**
+ * @param \int[]|string $logo
+ */
+ public function setLogo($logo)
+ {
+ $this->logo = $logo;
+ }
+
+ /**
+ * @return \DateTimeInterface
+ */
+ public function getLogoUpdatedAt()
+ {
+ return $this->logoUpdatedAt;
+ }
+
+ /**
+ * @return string
+ */
+ public function getPublicWatermark()
+ {
+ return $this->publicWatermark;
+ }
+
+ /**
+ * @param string $publicWatermark
+ */
+ public function setPublicWatermark($publicWatermark)
+ {
+ if (! in_array($publicWatermark, ['none', 'wm', 'stamp'])) {
+ return;
+ }
+
+ $this->publicWatermark = $publicWatermark;
+ }
+
+ /**
+ * @return string
+ */
+ public function getPreferences()
+ {
+ return $this->preferences;
+ }
+
+ /**
+ * @param string $preferences
+ */
+ public function setPreferences($preferences)
+ {
+ $this->preferences = $preferences;
+ }
+
+ /**
+ * @return CollectionReference
+ */
+ public function getCollectionReference()
+ {
+ return $this->collectionReference;
+ }
+
+ /**
+ * @param CollectionReference $collectionReference
+ */
+ public function setCollectionReference($collectionReference)
+ {
+ $this->collectionReference = $collectionReference;
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Collection/CollectionFactory.php b/lib/Alchemy/Phrasea/Collection/CollectionFactory.php
new file mode 100644
index 0000000000..34c99c19b0
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Collection/CollectionFactory.php
@@ -0,0 +1,72 @@
+app = $application;
+ }
+
+ /**
+ * @param int $databoxId
+ * @param CollectionReference $reference
+ * @param array $row
+ * @return \collection
+ */
+ public function create($databoxId, CollectionReference $reference, array $row)
+ {
+ if ($databoxId != $reference->getDataboxId()) {
+ throw new \InvalidArgumentException('Reference does not belong to given databoxId.');
+ }
+
+ $collection = new Collection($databoxId, $row['coll_id'], $row['asciiname']);
+
+ $collection->setLabel('en', $row['label_en']);
+ $collection->setLabel('fr', $row['label_fr']);
+ $collection->setLabel('de', $row['label_de']);
+ $collection->setLabel('nl', $row['label_nl']);
+ $collection->setLogo($row['logo']);
+ $collection->setPreferences($row['prefs']);
+ $collection->setPublicWatermark($row['pub_wm']);
+
+ return new \collection($this->app, $collection, $reference, $row);
+ }
+
+ /**
+ * @param int $databoxId
+ * @param CollectionReference[] $collectionReferences
+ * @param array $rows
+ * @return array
+ */
+ public function createMany($databoxId, $collectionReferences, array $rows)
+ {
+ Assertion::allIsInstanceOf($collectionReferences, CollectionReference::class);
+
+ $collections = [];
+ $indexedReferences = [];
+
+ foreach ($collectionReferences as $reference) {
+ $indexedReferences[$reference->getCollectionId()] = $reference;
+ }
+
+ foreach ($rows as $row) {
+ $collections[$row['coll_id']] = $this->create($databoxId, $indexedReferences[$row['coll_id']], $row);
+ }
+
+ return $collections;
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Collection/CollectionRepository.php b/lib/Alchemy/Phrasea/Collection/CollectionRepository.php
new file mode 100644
index 0000000000..865ece521d
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Collection/CollectionRepository.php
@@ -0,0 +1,30 @@
+application = $app;
+ $this->repositoryFactory = $collectionRepositoryFactory;
+ $this->referenceRepository = $referenceRepository;
+ }
+
+ /**
+ * @param $databoxId
+ * @return CollectionRepository
+ */
+ public function getRepositoryByDatabox($databoxId)
+ {
+ if (!isset($this->repositories[$databoxId])) {
+ $this->repositories[$databoxId] = $this->repositoryFactory->createRepositoryForDatabox($databoxId);
+ }
+
+ return $this->repositories[$databoxId];
+ }
+
+ /**
+ * @param int $baseId
+ * @return CollectionRepository
+ * @throws \OutOfBoundsException if no repository was found for the given baseId.
+ */
+ public function getRepositoryByBase($baseId)
+ {
+ if ($this->baseIdMap === null) {
+ $this->loadBaseIdMap();
+ }
+
+ if (isset($this->baseIdMap[$baseId])) {
+ return $this->getRepositoryByDatabox($this->baseIdMap[$baseId]);
+ }
+
+ throw new \OutOfBoundsException('No repository available for given base [baseId: ' . $baseId . ' ].');
+ }
+
+ public function purgeRegistry()
+ {
+ $this->baseIdMap = null;
+
+ $appBox = $this->application->getApplicationBox();
+
+ \phrasea::reset_baseDatas($appBox);
+ \phrasea::reset_sbasDatas($appBox);
+ }
+
+ private function loadBaseIdMap()
+ {
+ $references = $this->referenceRepository->findAll();
+
+ $this->baseIdMap = [];
+
+ foreach ($references as $reference) {
+ $this->baseIdMap[$reference->getBaseId()] = $reference->getDataboxId();
+ }
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Collection/CollectionService.php b/lib/Alchemy/Phrasea/Collection/CollectionService.php
new file mode 100644
index 0000000000..0cb33fc4fe
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Collection/CollectionService.php
@@ -0,0 +1,266 @@
+app = $application;
+ $this->connection = $connection;
+ $this->connectionProvider = $connectionProvider;
+ }
+
+ /**
+ * @param Collection $collection
+ * @return int|null
+ * @throws \Doctrine\DBAL\DBALException
+ */
+ public function getRecordCount(Collection $collection)
+ {
+ $connection = $this->connectionProvider->getConnection($collection->getDataboxId());
+
+ $sql = "SELECT COALESCE(COUNT(record_id), 0) AS recordCount FROM record WHERE coll_id = :coll_id";
+ $stmt = $connection->prepare($sql);
+ $stmt->execute([':coll_id' => $collection->getCollectionId()]);
+ $rowbas = $stmt->fetch(\PDO::FETCH_ASSOC);
+ $stmt->closeCursor();
+
+ $amount = $rowbas ? (int) $rowbas["recordCount"] : null;
+
+ return $amount;
+ }
+
+ /**
+ * @param Collection $collection
+ * @return array
+ */
+ public function getRecordDetails(Collection $collection)
+ {
+ $sql = "SELECT record.coll_id,name,COALESCE(asciiname, CONCAT('_',record.coll_id)) AS asciiname,
+ SUM(1) AS n, SUM(size) AS size
+ FROM record NATURAL JOIN subdef
+ INNER JOIN coll ON record.coll_id=coll.coll_id AND coll.coll_id = :coll_id
+ GROUP BY record.coll_id, subdef.name";
+
+ $connection = $this->connectionProvider->getConnection($collection->getDataboxId());
+
+ $stmt = $connection->prepare($sql);
+ $stmt->execute([':coll_id' => $collection->getCollectionId()]);
+ $rs = $stmt->fetchAll(\PDO::FETCH_ASSOC);
+ $stmt->closeCursor();
+
+ $ret = [];
+ foreach ($rs as $row) {
+ $ret[] = [
+ "coll_id" => (int) $row["coll_id"],
+ "name" => $row["name"],
+ "amount" => (int) $row["n"],
+ "size" => (int) $row["size"]];
+ }
+
+ return $ret;
+ }
+
+ /**
+ * @param Collection $collection
+ * @return $this
+ * @throws \Doctrine\DBAL\DBALException
+ */
+ public function resetWatermark(Collection $collection)
+ {
+ $sql = 'SELECT path, file FROM record r INNER JOIN subdef s USING(record_id)
+ WHERE r.coll_id = :coll_id AND r.type="image" AND s.name="preview"';
+
+ $connection = $this->connectionProvider->getConnection($collection->getDataboxId());
+
+ $stmt = $connection->prepare($sql);
+ $stmt->execute([':coll_id' => $collection->getCollectionId()]);
+
+ while ($row2 = $stmt->fetch(\PDO::FETCH_ASSOC)) {
+ @unlink(\p4string::addEndSlash($row2['path']) . 'watermark_' . $row2['file']);
+ }
+ $stmt->closeCursor();
+
+ return $this;
+ }
+
+ /**
+ * @param Collection $collection
+ * @param int|null $record_id
+ * @return $this
+ * @throws \Doctrine\DBAL\DBALException
+ */
+ public function resetStamp(Collection $collection, $record_id = null)
+ {
+ $sql = 'SELECT path, file FROM record r INNER JOIN subdef s USING(record_id)
+ WHERE r.coll_id = :coll_id
+ AND r.type="image" AND s.name IN ("preview", "document")';
+
+
+ $params = [':coll_id' => $collection->getCollectionId()];
+
+ if ($record_id) {
+ $sql .= ' AND record_id = :record_id';
+ $params[':record_id'] = $record_id;
+ }
+
+ $connection = $this->connectionProvider->getConnection($collection->getDataboxId());
+
+ $stmt = $connection->prepare($sql);
+ $stmt->execute($params);
+
+ while ($row2 = $stmt->fetch(\PDO::FETCH_ASSOC)) {
+ @unlink(\p4string::addEndSlash($row2['path']) . 'stamp_' . $row2['file']);
+ }
+ $stmt->closeCursor();
+
+ return $this;
+ }
+
+ /**
+ * @param \databox $databox
+ * @param Collection $collection
+ * @param CollectionReference $reference
+ * @throws \Doctrine\DBAL\DBALException
+ */
+ public function delete(\databox $databox, Collection $collection, CollectionReference $reference)
+ {
+ while ($this->getRecordCount($collection) > 0) {
+ $this->emptyCollection($databox, $collection);
+ }
+
+ $connection = $this->connectionProvider->getConnection($collection->getDataboxId());
+
+ $sql = "DELETE FROM coll WHERE coll_id = :coll_id";
+ $stmt = $connection->prepare($sql);
+ $stmt->execute([':coll_id' => $collection->getCollectionId()]);
+ $stmt->closeCursor();
+
+ $sql = "DELETE FROM bas WHERE base_id = :base_id";
+ $stmt = $this->connection->prepare($sql);
+ $stmt->execute([':base_id' => $reference->getBaseId()]);
+ $stmt->closeCursor();
+
+ $sql = "DELETE FROM basusr WHERE base_id = :base_id";
+ $stmt = $this->connection->prepare($sql);
+ $stmt->execute([':base_id' => $reference->getBaseId()]);
+ $stmt->closeCursor();
+
+ return;
+ }
+
+ /**
+ * @param \databox $databox
+ * @param Collection $collection
+ * @param int $pass_quantity
+ * @return $this
+ * @throws \Doctrine\DBAL\DBALException
+ */
+ public function emptyCollection(\databox $databox, Collection $collection, $pass_quantity = 100)
+ {
+ $pass_quantity = (int) $pass_quantity > 200 ? 200 : (int) $pass_quantity;
+ $pass_quantity = (int) $pass_quantity < 10 ? 10 : (int) $pass_quantity;
+
+ $sql = "SELECT record_id FROM record WHERE coll_id = :coll_id
+ ORDER BY record_id DESC LIMIT 0, " . $pass_quantity;
+
+ $connection = $this->connectionProvider->getConnection($collection->getDataboxId());
+
+ $stmt = $connection->prepare($sql);
+ $stmt->execute([':coll_id' => $collection->getCollectionId()]);
+ $rs = $stmt->fetchAll(\PDO::FETCH_ASSOC);
+ $stmt->closeCursor();
+
+ foreach ($rs as $row) {
+ $record = $databox->get_record($row['record_id']);
+ $record->delete();
+ unset($record);
+ }
+
+ return $this;
+ }
+
+ /**
+ * @param CollectionReference $reference
+ * @return $this
+ * @throws \Doctrine\DBAL\DBALException
+ */
+ public function unmountCollection(CollectionReference $reference)
+ {
+ $params = [':base_id' => $reference->getBaseId()];
+
+ $query = $this->app['phraseanet.user-query'];
+ $total = $query->on_base_ids([$reference->getBaseId()])
+ ->include_phantoms(false)
+ ->include_special_users(true)
+ ->include_invite(true)
+ ->include_templates(true)->get_total();
+ $n = 0;
+
+ while ($n < $total) {
+ $results = $query->limit($n, 50)->execute()->get_results();
+
+ foreach ($results as $user) {
+ $this->app->getAclForUser($user)->delete_data_from_cache(\ACL::CACHE_RIGHTS_SBAS);
+ $this->app->getAclForUser($user)->delete_data_from_cache(\ACL::CACHE_RIGHTS_BAS);
+ }
+
+ $n+=50;
+ }
+
+ $sql = "DELETE FROM basusr WHERE base_id = :base_id";
+ $stmt = $this->connection->prepare($sql);
+ $stmt->execute($params);
+ $stmt->closeCursor();
+
+ $sql = "DELETE FROM bas WHERE base_id = :base_id";
+ $stmt = $this->connection->prepare($sql);
+ $stmt->execute($params);
+ $stmt->closeCursor();
+ }
+
+ /**
+ * @param CollectionReference $reference
+ * @param User $user
+ */
+ public function grantAdminRights(CollectionReference $reference, User $user)
+ {
+ $rights = [
+ "canputinalbum" => "1",
+ "candwnldhd" => "1",
+ "nowatermark" => "1",
+ "candwnldpreview" => "1",
+ "cancmd" => "1",
+ "canadmin" => "1",
+ "actif" => "1",
+ "canreport" => "1",
+ "canpush" => "1",
+ "basusr_infousr" => "",
+ "canaddrecord" => "1",
+ "canmodifrecord" => "1",
+ "candeleterecord" => "1",
+ "chgstatus" => "1",
+ "imgtools" => "1",
+ "manage" => "1",
+ "modify_struct" => "1"
+ ];
+
+ $this->app->getAclForUser($user)->update_rights_to_base($reference->getBaseId(), $rights);
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Collection/Factory/ArrayCachedCollectionRepositoryFactory.php b/lib/Alchemy/Phrasea/Collection/Factory/ArrayCachedCollectionRepositoryFactory.php
new file mode 100644
index 0000000000..bec14d37e9
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Collection/Factory/ArrayCachedCollectionRepositoryFactory.php
@@ -0,0 +1,34 @@
+collectionRepositoryFactory = $collectionRepositoryFactory;
+ }
+
+ /**
+ * @param int $databoxId
+ * @return CollectionRepository
+ */
+ public function createRepositoryForDatabox($databoxId)
+ {
+ $repository = $this->collectionRepositoryFactory->createRepositoryForDatabox($databoxId);
+
+ return new ArrayCacheCollectionRepository($repository);
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Collection/Factory/CachedCollectionRepositoryFactory.php b/lib/Alchemy/Phrasea/Collection/Factory/CachedCollectionRepositoryFactory.php
new file mode 100644
index 0000000000..601bd9671b
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Collection/Factory/CachedCollectionRepositoryFactory.php
@@ -0,0 +1,66 @@
+application = $application;
+ $this->collectionRepositoryFactory = $collectionRepositoryFactory;
+ $this->cache = $cache;
+ $this->baseCacheKey = (string)$baseCacheKey;
+ }
+
+ /**
+ * @param int $databoxId
+ * @return CollectionRepository
+ */
+ public function createRepositoryForDatabox($databoxId)
+ {
+ $repository = $this->collectionRepositoryFactory->createRepositoryForDatabox($databoxId);
+
+ return new CachedCollectionRepository(
+ $this->application,
+ $repository,
+ $this->cache,
+ $this->baseCacheKey . '.' . $databoxId
+ );
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Collection/Factory/DbalCollectionRepositoryFactory.php b/lib/Alchemy/Phrasea/Collection/Factory/DbalCollectionRepositoryFactory.php
new file mode 100644
index 0000000000..415c401851
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Collection/Factory/DbalCollectionRepositoryFactory.php
@@ -0,0 +1,60 @@
+databoxConnectionProvider = $connectionProvider;
+ $this->collectionFactory = $collectionFactory;
+ $this->collectionReferenceRepository = $referenceRepository;
+ }
+
+ /**
+ * @param int $databoxId
+ * @return CollectionRepository
+ */
+ public function createRepositoryForDatabox($databoxId)
+ {
+ $databoxConnection = $this->databoxConnectionProvider->getConnection($databoxId);
+
+ return new DbalCollectionRepository(
+ $databoxId,
+ $databoxConnection,
+ $this->collectionReferenceRepository,
+ $this->collectionFactory
+ );
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Collection/Reference/ArrayCacheCollectionReferenceRepository.php b/lib/Alchemy/Phrasea/Collection/Reference/ArrayCacheCollectionReferenceRepository.php
new file mode 100644
index 0000000000..1ec30411c6
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Collection/Reference/ArrayCacheCollectionReferenceRepository.php
@@ -0,0 +1,110 @@
+repository = $referenceRepository;
+ }
+
+ /**
+ * @return CollectionReference[]
+ */
+ public function findAll()
+ {
+ if ($this->referenceCache === null) {
+ $this->referenceCache = $this->repository->findAll();
+ }
+
+ return $this->referenceCache;
+ }
+
+ /**
+ * @param int $databoxId
+ * @return CollectionReference[]
+ */
+ public function findAllByDatabox($databoxId)
+ {
+ $references = $this->findAll();
+ $found = array();
+
+ foreach ($references as $reference) {
+ if ($reference->getDataboxId() == $databoxId) {
+ $found[$reference->getBaseId()] = $reference;
+ }
+ }
+
+ return $found;
+ }
+
+ /**
+ * @param int $baseId
+ * @return CollectionReference|null
+ */
+ public function find($baseId)
+ {
+ $references = $this->findAll();
+
+ if (isset($references[$baseId])) {
+ return $references[$baseId];
+ }
+
+ return null;
+ }
+
+ /**
+ * @param int $databoxId
+ * @param int $collectionId
+ * @return CollectionReference|null
+ */
+ public function findByCollectionId($databoxId, $collectionId)
+ {
+ $references = $this->findAll();
+
+ foreach ($references as $reference) {
+ if ($reference->getDataboxId() == $databoxId && $reference->getCollectionId() == $collectionId) {
+ return $reference;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * @param CollectionReference $reference
+ * @return void
+ */
+ public function save(CollectionReference $reference)
+ {
+ $this->repository->save($reference);
+
+ if ($this->referenceCache !== null) {
+ $this->referenceCache[$reference->getBaseId()] = $reference;
+ }
+ }
+
+ /**
+ * @param CollectionReference $reference
+ * @return void
+ */
+ public function delete(CollectionReference $reference)
+ {
+ $this->repository->delete($reference);
+
+ if ($this->referenceCache !== null) {
+ unset($this->referenceCache[$reference->getBaseId()]);
+ }
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Collection/Reference/CollectionReference.php b/lib/Alchemy/Phrasea/Collection/Reference/CollectionReference.php
new file mode 100644
index 0000000000..5f0f74b3c8
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Collection/Reference/CollectionReference.php
@@ -0,0 +1,156 @@
+baseId = (int) $baseId;
+ $this->databoxId = (int) $databoxId;
+ $this->collectionId = (int) $collectionId;
+ $this->displayIndex = (int) $displayIndex;
+ $this->isActive = (bool) $isActive;
+ $this->alias = (string) $alias;
+ }
+
+ /**
+ * @return int
+ */
+ public function getDataboxId()
+ {
+ return $this->databoxId;
+ }
+
+ /**
+ * @return int
+ */
+ public function getBaseId()
+ {
+ return $this->baseId;
+ }
+
+ /**
+ * @param int $baseId
+ */
+ public function setBaseId($baseId)
+ {
+ if ($this->baseId > 0) {
+ throw new \LogicException('Cannot change the baseId of an existing collection reference.');
+ }
+
+ $this->baseId = $baseId;
+ }
+
+ /**
+ * @return int
+ */
+ public function getCollectionId()
+ {
+ return $this->collectionId;
+ }
+
+ /**
+ * @return int
+ */
+ public function getDisplayIndex()
+ {
+ return $this->displayIndex;
+ }
+
+ /**
+ * @param int $index
+ * @return $this
+ */
+ public function setDisplayIndex($index)
+ {
+ $this->displayIndex = (int) $index;
+
+ return $this;
+ }
+
+ /**
+ * @return boolean
+ */
+ public function isActive()
+ {
+ return $this->isActive;
+ }
+
+ /**
+ * @return $this
+ */
+ public function disable()
+ {
+ $this->isActive = false;
+
+ return $this;
+ }
+
+ /**
+ * @return $this
+ */
+ public function enable()
+ {
+ $this->isActive = true;
+
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function getAlias()
+ {
+ return $this->alias;
+ }
+
+ /**
+ * @param string $alias
+ * @return $this
+ */
+ public function setAlias($alias)
+ {
+ $this->alias = (string) $alias;
+
+ return $this;
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Collection/Reference/CollectionReferenceRepository.php b/lib/Alchemy/Phrasea/Collection/Reference/CollectionReferenceRepository.php
new file mode 100644
index 0000000000..48dbfc94f5
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Collection/Reference/CollectionReferenceRepository.php
@@ -0,0 +1,42 @@
+ 'baseId',
+ 'sbas_id' => 'databoxId',
+ 'server_coll_id' => 'collectionId',
+ 'ord' => 'displayIndex',
+ 'active' => 'isActive',
+ 'aliases' => 'alias'
+ ];
+
+ private static $selectQuery = 'SELECT base_id AS baseId, sbas_id AS databoxId, server_coll_id AS collectionId,
+ ord AS displayIndex, active AS isActive, aliases AS alias
+ FROM bas';
+
+ private static $insertQuery = 'INSERT INTO bas (sbas_id, server_coll_id, ord, active, aliases)
+ VALUES (:databoxId, :collectionId,
+ (SELECT COALESCE(MAX(b.ord), 0) + 1 AS ord FROM bas b WHERE b.sbas_id = :databoxId),
+ :isActive, :alias)';
+
+ private static $updateQuery = 'UPDATE bas SET ord = :displayIndex, active = :isActive, aliases = :alias
+ WHERE base_id = :baseId';
+
+ private static $deleteQuery = 'DELETE FROM bas WHERE base_id = :baseId';
+
+ /**
+ * @var Connection
+ */
+ private $connection;
+
+ /**
+ * @param Connection $connection
+ */
+ public function __construct(Connection $connection)
+ {
+ $this->connection = $connection;
+ }
+
+ /**
+ * @return CollectionReference[]
+ */
+ public function findAll()
+ {
+ return $this->createManyReferences($this->connection->fetchAll(self::$selectQuery));
+ }
+
+ /**
+ * @param int $databoxId
+ * @return CollectionReference[]
+ */
+ public function findAllByDatabox($databoxId)
+ {
+ $query = self::$selectQuery . ' WHERE sbas_id = :databoxId';
+ $rows = $this->connection->fetchAll($query, [ ':databoxId' => $databoxId ]);
+
+ return $this->createManyReferences($rows);
+ }
+
+ /**
+ * @param int $baseId
+ * @return CollectionReference|null
+ */
+ public function find($baseId)
+ {
+ $query = self::$selectQuery . ' WHERE base_id = :baseId';
+ $row = $this->connection->fetchAssoc($query, [ ':baseId' => $baseId ]);
+
+ if ($row !== false) {
+ return $this->createReference($row);
+ }
+
+ return null;
+ }
+
+ /**
+ * @param int $databoxId
+ * @param int $collectionId
+ * @return CollectionReference|null
+ */
+ public function findByCollectionId($databoxId, $collectionId)
+ {
+ $query = self::$selectQuery . ' WHERE sbas_id = :databoxId AND server_coll_id = :collectionId';
+ $row = $this->connection->fetchAssoc($query, [ ':databoxId' => $databoxId, ':collectionId' => $collectionId ]);
+
+ if ($row !== false) {
+ return $this->createReference($row);
+ }
+
+ return null;
+ }
+
+ public function save(CollectionReference $collectionReference)
+ {
+ $query = self::$insertQuery;
+ $isInsert = true;
+
+ $parameters = [
+ 'isActive' => $collectionReference->isActive(),
+ 'alias' => $collectionReference->getAlias()
+ ];
+
+ if ($collectionReference->getBaseId() > 0) {
+ $query = self::$updateQuery;
+ $isInsert = false;
+
+ $parameters['baseId'] = $collectionReference->getBaseId();
+ $parameters['displayIndex'] = $collectionReference->getDisplayIndex();
+ }
+ else {
+ $parameters['databoxId'] = $collectionReference->getDataboxId();
+ $parameters['collectionId'] = $collectionReference->getCollectionId();
+ }
+
+ $this->connection->executeQuery($query, $parameters);
+
+ if ($isInsert) {
+ $collectionReference->setBaseId($this->connection->lastInsertId());
+ }
+ }
+
+ /**
+ * @param CollectionReference $collectionReference
+ * @throws \Doctrine\DBAL\DBALException
+ */
+ public function delete(CollectionReference $collectionReference)
+ {
+ $parameters = [
+ 'baseId' => $collectionReference->getBaseId()
+ ];
+
+ $this->connection->executeQuery(self::$deleteQuery, $parameters);
+ }
+
+ /**
+ * @param array $row
+ * @return CollectionReference
+ */
+ private function createReference(array $row)
+ {
+ return new CollectionReference(
+ $row['baseId'],
+ $row['databoxId'],
+ $row['collectionId'],
+ $row['displayIndex'],
+ $row['isActive'],
+ $row['alias']
+ );
+ }
+
+ /**
+ * @param $rows
+ * @return array
+ */
+ private function createManyReferences($rows)
+ {
+ $references = [];
+
+ foreach ($rows as $row) {
+ $references[$row['baseId']] = $this->createReference($row);
+ }
+
+ return $references;
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Collection/Repository/ArrayCacheCollectionRepository.php b/lib/Alchemy/Phrasea/Collection/Repository/ArrayCacheCollectionRepository.php
new file mode 100644
index 0000000000..3a5bc69951
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Collection/Repository/ArrayCacheCollectionRepository.php
@@ -0,0 +1,69 @@
+collectionRepository = $collectionRepository;
+ }
+
+ /**
+ * @return \collection[]
+ */
+ public function findAll()
+ {
+ if ($this->collectionCache === null) {
+ $this->collectionCache = $this->collectionRepository->findAll();
+ }
+
+ return $this->collectionCache;
+ }
+
+ /**
+ * @param int $collectionId
+ * @return \collection|null
+ */
+ public function find($collectionId)
+ {
+ $collections = $this->findAll();
+
+ if (isset($collections[$collectionId])) {
+ return $collections[$collectionId];
+ }
+
+ return null;
+ }
+
+ public function save(Collection $collection)
+ {
+ $this->collectionRepository->save($collection);
+
+ if ($this->collectionCache !== null) {
+ $this->collectionCache = null;
+ }
+ }
+
+ public function delete(Collection $collection)
+ {
+ $this->collectionRepository->delete($collection);
+
+ if (isset($this->collectionCache[$collection->getCollectionId()])) {
+ unset($this->collectionCache[$collection->getCollectionId()]);
+ }
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Collection/Repository/CachedCollectionRepository.php b/lib/Alchemy/Phrasea/Collection/Repository/CachedCollectionRepository.php
new file mode 100644
index 0000000000..71e68aed02
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Collection/Repository/CachedCollectionRepository.php
@@ -0,0 +1,122 @@
+app = $application;
+ $this->repository = $repository;
+ $this->cache = $cache;
+ $this->cacheKey = $cacheKey;
+ }
+
+ /**
+ * @return \collection[]
+ */
+ public function findAll()
+ {
+ $cacheKey = $this->getCacheKey();
+ /** @var \collection[] $collections */
+ $collections = $this->cache->fetch($cacheKey);
+
+ if ($collections === false) {
+ $collections = $this->repository->findAll();
+ $this->putInCache($cacheKey, $collections);
+ } else {
+ foreach ($collections as $collection) {
+ $collection->hydrate($this->app);
+ }
+ }
+
+ return $collections;
+ }
+
+ /**
+ * @param int $collectionId
+ * @return \collection|null
+ */
+ public function find($collectionId)
+ {
+ $collections = $this->findAll();
+
+ if (isset($collections[$collectionId])) {
+ return $collections[$collectionId];
+ }
+
+ return null;
+ }
+
+ public function save(Collection $collection)
+ {
+ $this->repository->save($collection);
+
+ $cacheKey = $this->getCacheKey();
+
+ $this->cache->delete($cacheKey);
+ }
+
+ public function delete(Collection $collection)
+ {
+ $this->repository->delete($collection);
+
+ $cacheKey = $this->getCacheKey();
+
+ $this->cache->delete($cacheKey);
+ }
+
+ private function putInCache($key, $value)
+ {
+ $this->cache->save($key, $value);
+ }
+
+ /**
+ * @return string
+ */
+ private function getCacheKey()
+ {
+ $cacheKey = 'collections:' . hash('sha256', $this->cacheKey);
+
+ return $cacheKey;
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Collection/Repository/DbalCollectionRepository.php b/lib/Alchemy/Phrasea/Collection/Repository/DbalCollectionRepository.php
new file mode 100644
index 0000000000..24736a7adc
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Collection/Repository/DbalCollectionRepository.php
@@ -0,0 +1,172 @@
+databoxId = (int) $databoxId;
+ $this->databoxConnection = $databoxConnection;
+ $this->referenceRepository = $referenceRepository;
+ $this->collectionFactory = $collectionFactory;
+ }
+
+ /**
+ * @return \collection[]
+ */
+ public function findAll()
+ {
+ $references = $this->referenceRepository->findAllByDatabox($this->databoxId);
+
+ if (empty($references)) {
+ return [];
+ }
+
+ $parameters = [];
+
+ foreach ($references as $reference) {
+ $parameters[] = $reference->getCollectionId();
+ }
+
+ $query = self::$selectQuery . ' WHERE coll_id IN (:collectionIds)';
+ $parameters = [ 'collectionIds' => $parameters ];
+ $parameterTypes = [ 'collectionIds' => Connection::PARAM_INT_ARRAY ];
+
+ $rows = $this->databoxConnection->fetchAll($query, $parameters, $parameterTypes);
+
+ return $this->collectionFactory->createMany($this->databoxId, $references, $rows);
+ }
+
+ /**
+ * @param int $baseId
+ * @return \collection|null
+ */
+ public function find($baseId)
+ {
+ $reference = $this->referenceRepository->find($baseId);
+
+ if ($reference === null) {
+ return null;
+ }
+
+ $query = self::$selectQuery . ' WHERE coll_id = :collectionId';
+ $row = $this->databoxConnection->fetchAssoc($query, [ ':collectionId' => $reference->getCollectionId() ]);
+
+ if ($row !== false) {
+ return $this->collectionFactory->create($this->databoxId, $reference, $row);
+ }
+
+ return null;
+ }
+
+ /**
+ * @param int $databoxId
+ * @param int $collectionId
+ * @return \collection|null
+ */
+ public function findByCollectionId($databoxId, $collectionId)
+ {
+ $reference = $this->referenceRepository->findByCollectionId($databoxId, $collectionId);
+
+ if ($reference === null) {
+ return null;
+ }
+
+ $query = self::$selectQuery . ' WHERE coll_id = :collectionId';
+ $row = $this->databoxConnection->fetchAssoc($query, [ ':collectionId' => $reference->getCollectionId() ]);
+
+ if ($row !== false) {
+ return $this->collectionFactory->create($this->databoxId, $reference, $row);
+ }
+
+ return null;
+ }
+
+ public function save(Collection $collection)
+ {
+ $isInsert = true;
+ $query = self::$insertQuery;
+ $parameters = array(
+ 'name' => $collection->getName(),
+ 'preferences' => $collection->getPreferences(),
+ 'logo' => $collection->getLogo()
+ );
+
+ if ($collection->getCollectionId() > 0) {
+ $parameters['collectionId'] = $collection->getCollectionId();
+ $parameters['labelEn'] = $collection->getLabel('en', false);
+ $parameters['labelFr'] = $collection->getLabel('fr', false);
+ $parameters['labelDe'] = $collection->getLabel('de', false);
+ $parameters['labelNl'] = $collection->getLabel('nl', false);
+ $parameters['logoTimestamp'] = $collection->getLogoUpdatedAt();
+ $parameters['publicWatermark'] = $collection->getPublicWatermark();
+
+ $query = self::$updateQuery;
+ $isInsert = false;
+ }
+
+ $this->databoxConnection->executeQuery($query, $parameters);
+
+ if ($isInsert) {
+ $collection->setCollectionId($this->databoxConnection->lastInsertId());
+ }
+ }
+
+ public function delete(Collection $collection)
+ {
+ $parameters = [
+ 'collectionId' => $collection->getCollectionId()
+ ];
+
+ $this->databoxConnection->executeQuery(self::$deleteQuery, $parameters);
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Command/Developer/IniReset.php b/lib/Alchemy/Phrasea/Command/Developer/IniReset.php
index 75edd32fa5..9cf58faec9 100644
--- a/lib/Alchemy/Phrasea/Command/Developer/IniReset.php
+++ b/lib/Alchemy/Phrasea/Command/Developer/IniReset.php
@@ -1,5 +1,4 @@
getHelperSet()->get('dialog');
$dbName = $dialog->ask(
$output,
- _('Please enter the databox name to reset or create')
+ $this->container['translator']->trans('Please enter the databox name to reset or create')
);
}
} else if ($input->getOption('db-name')) {
@@ -75,7 +72,7 @@ class IniReset extends Command
}
$continue = 'y';
- if (count($dbs['dbs']) > 1 && in_array($dbName, array_map(function($db) { return $db->get_dbname();}, $dbs['dbs']))) {
+ if (count($dbs['dbs']) > 1 && in_array($dbName, array_map(function(\base $db) { return $db->get_dbname();}, $dbs['dbs']))) {
if ($interactive) {
do {
$continue = mb_strtolower($dialog->ask($output, '' .$dbName.' database is going to be truncated, do you want to continue ? (Y/n)', 'Y'));
@@ -87,7 +84,7 @@ class IniReset extends Command
return;
}
- $unmountedDbs = $dbToMount = array_diff(array_map(function($db) { return $db->get_dbname();}, $dbs['dbs']), array($dbName));
+ $unmountedDbs = $dbToMount = array_diff(array_map(function(\base $db) { return $db->get_dbname();}, $dbs['dbs']), array($dbName));
if (count($unmountedDbs) > 1 && $interactive) {
array_unshift($unmountedDbs, 'all');
diff --git a/lib/Alchemy/Phrasea/Command/Developer/Uninstaller.php b/lib/Alchemy/Phrasea/Command/Developer/Uninstaller.php
index 43c5aa9517..a5c3c91659 100644
--- a/lib/Alchemy/Phrasea/Command/Developer/Uninstaller.php
+++ b/lib/Alchemy/Phrasea/Command/Developer/Uninstaller.php
@@ -31,60 +31,45 @@ class Uninstaller extends Command
protected function doExecute(InputInterface $input, OutputInterface $output)
{
$root = $this->container['root.path'];
+ $path = $this->container['cache.path'];
- foreach ([
- $root.'/config/configuration.yml',
- $root.'/config/services.yml',
- $root.'/config/connexions.yml',
- $root.'/config/config.yml',
- $root.'/config/config.inc',
- $root.'/config/connexion.inc',
- $root.'/config/_GV.php',
- $root.'/config/_GV.php.old',
- $root.'/config/configuration-compiled.php',
- ] as $file) {
- if ($this->container['filesystem']->exists($file)) {
- unlink($file);
- }
- }
-
- foreach ([
+ $paths = [
+ $root . '/config/configuration.yml',
+ $root . '/config/services.yml',
+ $root . '/config/connexions.yml',
+ $root . '/config/config.yml',
+ $root . '/config/config.inc',
+ $root . '/config/connexion.inc',
+ $root . '/config/_GV.php',
+ $root . '/config/_GV.php.old',
+ $root . '/config/configuration-compiled.php',
$this->container['tmp.download.path'],
$this->container['tmp.lazaret.path'],
$this->container['tmp.caption.path'],
- $this->container['tmp.path'].'/sessions',
- $this->container['tmp.path'].'/locks',
- ] as $resource) {
- if (is_dir($resource)) {
- $finder = new Finder();
- foreach ($finder->files()->in($resource) as $file) {
- $this->container['filesystem']->remove($file);
- }
- } elseif (is_file($resource)) {
- $this->container['filesystem']->remove($resource);
+ $this->container['tmp.path'] . '/sessions',
+ $this->container['tmp.path'] . '/locks',
+ $path . '/cache_registry.php',
+ $path . '/cache_registry.yml',
+ $path . '/serializer',
+ $path . '/doctrine',
+ $path . '/twig',
+ $path . '/translations',
+ $path . '/minify',
+ $path . '/profiler',
+ ];
+
+ $files = $directories = [];
+
+ foreach ($paths as $path) {
+ if (is_dir($path)) {
+ $directories[] = $path;
+ } elseif (is_file($path)) {
+ $files[] = $path;
}
}
- $path = $this->container['cache.path'];
- foreach ([
- $path.'/cache_registry.php',
- $path.'/cache_registry.yml',
- $path.'/serializer',
- $path.'/doctrine',
- $path.'/twig',
- $path.'/translations',
- $path.'/minify',
- $path.'/profiler',
- ] as $resource) {
- if (is_dir($resource)) {
- $finder = new Finder();
- foreach ($finder->files()->in($resource) as $file) {
- $this->container['filesystem']->remove($file);
- }
- } elseif (is_file($resource)) {
- $this->container['filesystem']->remove($resource);
- }
- }
+ $this->container['filesystem']->remove($files);
+ $this->container['filesystem']->remove(Finder::create()->in($directories));
return 0;
}
diff --git a/lib/Alchemy/Phrasea/Command/RecordAdd.php b/lib/Alchemy/Phrasea/Command/RecordAdd.php
index e42a79ee77..e40514ac4c 100644
--- a/lib/Alchemy/Phrasea/Command/RecordAdd.php
+++ b/lib/Alchemy/Phrasea/Command/RecordAdd.php
@@ -43,7 +43,7 @@ class RecordAdd extends Command
protected function doExecute(InputInterface $input, OutputInterface $output)
{
try {
- $collection = \collection::get_from_base_id($this->container, $input->getArgument('base_id'));
+ $collection = \collection::getByBaseId($this->container, $input->getArgument('base_id'));
} catch (\Exception $e) {
throw new \InvalidArgumentException(sprintf('Collection %s is invalid', $input->getArgument('base_id')));
}
diff --git a/lib/Alchemy/Phrasea/Command/Setup/H264MappingGenerator.php b/lib/Alchemy/Phrasea/Command/Setup/H264MappingGenerator.php
index 6626f27cd9..3b4bd93ab2 100644
--- a/lib/Alchemy/Phrasea/Command/Setup/H264MappingGenerator.php
+++ b/lib/Alchemy/Phrasea/Command/Setup/H264MappingGenerator.php
@@ -12,6 +12,7 @@
namespace Alchemy\Phrasea\Command\Setup;
use Alchemy\Phrasea\Command\Command;
+use Alchemy\Phrasea\Databox\DataboxPathExtractor;
use Alchemy\Phrasea\Http\H264PseudoStreaming\H264Factory;
use Alchemy\Phrasea\Model\Manipulator\TokenManipulator;
use Symfony\Component\Console\Input\InputArgument;
@@ -36,7 +37,8 @@ class H264MappingGenerator extends Command
*/
protected function doExecute(InputInterface $input, OutputInterface $output)
{
- $paths = $this->extractPath($this->container->getApplicationBox());
+ $extractor = new DataboxPathExtractor($this->container->getApplicationBox());
+ $paths = $extractor->extractPaths();
foreach ($paths as $path) {
$this->container['filesystem']->mkdir($path);
}
@@ -95,22 +97,4 @@ class H264MappingGenerator extends Command
return ['mount-point' => 'mp4-videos-'.$n, 'directory' => $path, 'passphrase' => $this->container['random.low']->generateString(32, TokenManipulator::LETTERS_AND_NUMBERS)];
}
-
- private function extractPath(\appbox $appbox)
- {
- $paths = [];
-
- foreach ($appbox->get_databoxes() as $databox) {
- foreach ($databox->get_subdef_structure() as $group => $subdefs) {
- if ('video' !== $group) {
- continue;
- }
- foreach ($subdefs as $subdef) {
- $paths[] = $subdef->get_path();
- }
- }
- }
-
- return array_filter(array_unique($paths));
- }
}
diff --git a/lib/Alchemy/Phrasea/Command/Setup/XSendFileMappingGenerator.php b/lib/Alchemy/Phrasea/Command/Setup/XSendFileMappingGenerator.php
index 8b0a618932..fd56a6765b 100644
--- a/lib/Alchemy/Phrasea/Command/Setup/XSendFileMappingGenerator.php
+++ b/lib/Alchemy/Phrasea/Command/Setup/XSendFileMappingGenerator.php
@@ -12,6 +12,7 @@
namespace Alchemy\Phrasea\Command\Setup;
use Alchemy\Phrasea\Command\Command;
+use Alchemy\Phrasea\Databox\DataboxPathExtractor;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
@@ -35,7 +36,8 @@ class XSendFileMappingGenerator extends Command
*/
protected function doExecute(InputInterface $input, OutputInterface $output)
{
- $paths = $this->extractPath($this->container->getApplicationBox());
+ $extractor = new DataboxPathExtractor($this->container->getApplicationBox());
+ $paths = $extractor->extractPaths();
foreach ($paths as $path) {
$this->container['filesystem']->mkdir($path);
}
@@ -84,20 +86,4 @@ class XSendFileMappingGenerator extends Command
return ['mount-point' => 'protected_dir_'.$n, 'directory' => $path];
}
-
- private function extractPath(\appbox $appbox)
- {
- $paths = [];
-
- foreach ($appbox->get_databoxes() as $databox) {
- $paths[] = (string) $databox->get_sxml_structure()->path;
- foreach ($databox->get_subdef_structure() as $group => $subdefs) {
- foreach ($subdefs as $subdef) {
- $paths[] = $subdef->get_path();
- }
- }
- }
-
- return array_filter(array_unique($paths));
- }
}
diff --git a/lib/Alchemy/Phrasea/Command/Upgrade/Step31.php b/lib/Alchemy/Phrasea/Command/Upgrade/Step31.php
index 32ea0f775e..a7f42db231 100644
--- a/lib/Alchemy/Phrasea/Command/Upgrade/Step31.php
+++ b/lib/Alchemy/Phrasea/Command/Upgrade/Step31.php
@@ -125,7 +125,7 @@ class Step31 implements DatasUpgraderInterface
$uuid = Uuid::uuid4();
try {
$media = $this->app->getMediaFromUri($pathfile);
- $collection = \collection::get_from_coll_id($this->$app, $databox, (int) $record['coll_id']);
+ $collection = \collection::getByCollectionId($this->$app, $databox, (int) $record['coll_id']);
$file = new File($this->app, $media, $collection);
$uuid = $file->getUUID(true, true);
diff --git a/lib/Alchemy/Phrasea/Controller/AbstractDelivery.php b/lib/Alchemy/Phrasea/Controller/AbstractDelivery.php
index dcb0f07dd2..e550c21912 100644
--- a/lib/Alchemy/Phrasea/Controller/AbstractDelivery.php
+++ b/lib/Alchemy/Phrasea/Controller/AbstractDelivery.php
@@ -16,7 +16,6 @@ use Alchemy\Phrasea\Application\Helper\DataboxLoggerAware;
use Alchemy\Phrasea\Application\Helper\DelivererAware;
use Alchemy\Phrasea\Http\DeliverDataInterface;
use Symfony\Component\HttpFoundation\Request;
-use Symfony\Component\HttpFoundation\Response;
abstract class AbstractDelivery
{
@@ -33,26 +32,20 @@ abstract class AbstractDelivery
public function deliverContent(Request $request, \record_adapter $record, $subdef, $watermark, $stamp)
{
- $file = $record->get_subdef($subdef);
- $pathOut = $file->get_pathfile();
+ $mediaSubdefinition = $record->get_subdef($subdef);
- if ($watermark === true && $file->get_type() === \media_subdef::TYPE_IMAGE) {
- $pathOut = \recordutils_image::watermark($this->app, $file);
- } elseif ($stamp === true && $file->get_type() === \media_subdef::TYPE_IMAGE) {
- $pathOut = \recordutils_image::stamp($this->app, $file);
- }
+ $pathOut = $this->tamperProofSubDefinition($mediaSubdefinition, $watermark, $stamp);
$disposition = $request->query->get('download') ? DeliverDataInterface::DISPOSITION_ATTACHMENT : DeliverDataInterface::DISPOSITION_INLINE;
- /** @var Response $response */
- $response = $this->deliverFile($pathOut, $file->get_file(), $disposition, $file->get_mime());
+ $response = $this->deliverFile($pathOut, $mediaSubdefinition->get_file(), $disposition, $mediaSubdefinition->get_mime());
if (in_array($subdef, array('document', 'preview'))) {
$response->setPrivate();
$this->logView($record, $request);
} elseif ($subdef !== 'thumbnail') {
try {
- if ($file->getDataboxSubdef()->get_class() != \databox_subdef::CLASS_THUMBNAIL) {
+ if ($mediaSubdefinition->getDataboxSubdef()->get_class() != \databox_subdef::CLASS_THUMBNAIL) {
$response->setPrivate();
$this->logView($record, $request);
}
@@ -81,4 +74,23 @@ abstract class AbstractDelivery
// Ignore exception
}
}
+
+ /**
+ * @param \media_subdef $mediaSubdefinition
+ * @param bool $watermark
+ * @param bool $stamp
+ * @return string
+ */
+ private function tamperProofSubDefinition(\media_subdef $mediaSubdefinition, $watermark, $stamp)
+ {
+ $pathOut = $mediaSubdefinition->getRealPath();
+
+ if ($watermark === true && $mediaSubdefinition->get_type() === \media_subdef::TYPE_IMAGE) {
+ $pathOut = \recordutils_image::watermark($this->app, $mediaSubdefinition);
+ } elseif ($stamp === true && $mediaSubdefinition->get_type() === \media_subdef::TYPE_IMAGE) {
+ $pathOut = \recordutils_image::stamp($this->app, $mediaSubdefinition);
+ }
+
+ return $pathOut;
+ }
}
diff --git a/lib/Alchemy/Phrasea/Controller/Admin/CollectionController.php b/lib/Alchemy/Phrasea/Controller/Admin/CollectionController.php
index a8254560fb..ac082bb3ca 100644
--- a/lib/Alchemy/Phrasea/Controller/Admin/CollectionController.php
+++ b/lib/Alchemy/Phrasea/Controller/Admin/CollectionController.php
@@ -33,7 +33,7 @@ class CollectionController extends Controller
*/
public function getCollection(Request $request, $bas_id)
{
- $collection = \collection::get_from_base_id($this->app, $bas_id);
+ $collection = \collection::getByBaseId($this->app, $bas_id);
$admins = [];
@@ -144,7 +144,7 @@ class CollectionController extends Controller
$success = false;
$msg = $this->app->trans('An error occurred');
- $collection = \collection::get_from_base_id($this->app, $bas_id);
+ $collection = \collection::getByBaseId($this->app, $bas_id);
try {
if ($collection->get_record_amount() <= 500) {
$collection->empty_collection(500);
@@ -184,7 +184,7 @@ class CollectionController extends Controller
{
$success = false;
- $collection = \collection::get_from_base_id($this->app, $bas_id);
+ $collection = \collection::getByBaseId($this->app, $bas_id);
try {
$this->app->getApplicationBox()->write_collection_pic(
@@ -224,7 +224,7 @@ class CollectionController extends Controller
{
$success = false;
- $collection = \collection::get_from_base_id($this->app, $bas_id);
+ $collection = \collection::getByBaseId($this->app, $bas_id);
try {
$this->app->getApplicationBox()->write_collection_pic(
@@ -264,7 +264,7 @@ class CollectionController extends Controller
{
$success = false;
- $collection = \collection::get_from_base_id($this->app, $bas_id);
+ $collection = \collection::getByBaseId($this->app, $bas_id);
try {
$collection->update_logo(null);
@@ -323,7 +323,7 @@ class CollectionController extends Controller
]);
}
- $collection = \collection::get_from_base_id($this->app, $bas_id);
+ $collection = \collection::getByBaseId($this->app, $bas_id);
try {
$this->app->getApplicationBox()->write_collection_pic(
@@ -378,7 +378,7 @@ class CollectionController extends Controller
]);
}
- $collection = \collection::get_from_base_id($this->app, $bas_id);
+ $collection = \collection::getByBaseId($this->app, $bas_id);
try {
$this->app->getApplicationBox()->write_collection_pic(
@@ -432,7 +432,7 @@ class CollectionController extends Controller
]);
}
- $collection = \collection::get_from_base_id($this->app, $bas_id);
+ $collection = \collection::getByBaseId($this->app, $bas_id);
try {
$this->app->getApplicationBox()->write_collection_pic(
@@ -468,13 +468,13 @@ class CollectionController extends Controller
$success = false;
$msg = $this->app->trans('An error occured');
- $collection = \collection::get_from_base_id($this->app, $bas_id);
+ $collection = \collection::getByBaseId($this->app, $bas_id);
try {
if ($collection->get_record_amount() > 0) {
$msg = $this->app->trans('Empty the collection before removing');
} else {
- $collection->unmount_collection($this->app);
+ $collection->unmount();
$collection->delete();
$success = true;
$msg = $this->app->trans('Successful removal');
@@ -522,10 +522,10 @@ class CollectionController extends Controller
{
$success = false;
- $collection = \collection::get_from_base_id($this->app, $bas_id);
+ $collection = \collection::getByBaseId($this->app, $bas_id);
try {
- $collection->unmount_collection($this->app);
+ $collection->unmount();
$success = true;
} catch (\Exception $e) {
@@ -562,7 +562,7 @@ class CollectionController extends Controller
$success = false;
- $collection = \collection::get_from_base_id($this->app, $bas_id);
+ $collection = \collection::getByBaseId($this->app, $bas_id);
try {
$collection->set_name($name);
@@ -594,7 +594,7 @@ class CollectionController extends Controller
$this->app->abort(400, $this->app->trans('Invalid labels parameter'));
}
- $collection = \collection::get_from_base_id($this->app, $bas_id);
+ $collection = \collection::getByBaseId($this->app, $bas_id);
$success = true;
try {
@@ -638,7 +638,7 @@ class CollectionController extends Controller
$success = false;
- $collection = \collection::get_from_base_id($this->app, $bas_id);
+ $collection = \collection::getByBaseId($this->app, $bas_id);
try {
$collection->set_public_presentation($watermark);
@@ -671,7 +671,7 @@ class CollectionController extends Controller
{
$success = false;
- $collection = \collection::get_from_base_id($this->app, $bas_id);
+ $collection = \collection::getByBaseId($this->app, $bas_id);
try {
$collection->enable($this->app->getApplicationBox());
@@ -704,7 +704,7 @@ class CollectionController extends Controller
{
$success = false;
- $collection = \collection::get_from_base_id($this->app, $bas_id);
+ $collection = \collection::getByBaseId($this->app, $bas_id);
try {
$collection->disable($this->app->getApplicationBox());
@@ -736,7 +736,7 @@ class CollectionController extends Controller
{
/** @var \databox $databox */
$databox = $this->app->findDataboxById(\phrasea::sbasFromBas($this->app, $bas_id));
- $collection = \collection::get_from_base_id($this->app, $bas_id);
+ $collection = \collection::getByBaseId($this->app, $bas_id);
$structFields = $suggestedValues = $basePrefs = [];
/** @var \databox_field $meta */
@@ -806,7 +806,7 @@ class CollectionController extends Controller
{
$success = false;
- $collection = \collection::get_from_base_id($this->app, $bas_id);
+ $collection = \collection::getByBaseId($this->app, $bas_id);
$prefs = $request->request->get('str');
try {
@@ -843,7 +843,7 @@ class CollectionController extends Controller
*/
public function getDetails($bas_id)
{
- $collection = \collection::get_from_base_id($this->app, $bas_id);
+ $collection = \collection::getByBaseId($this->app, $bas_id);
$out = ['total' => ['totobj' => 0, 'totsiz' => 0, 'mega' => '0', 'giga' => '0'], 'result' => []];
diff --git a/lib/Alchemy/Phrasea/Controller/Admin/DashboardController.php b/lib/Alchemy/Phrasea/Controller/Admin/DashboardController.php
index 82abeb8543..86dfb14d98 100644
--- a/lib/Alchemy/Phrasea/Controller/Admin/DashboardController.php
+++ b/lib/Alchemy/Phrasea/Controller/Admin/DashboardController.php
@@ -124,6 +124,10 @@ class DashboardController extends Controller
public function addAdmins(Request $request)
{
$admins = $request->request->get('admins', []);
+
+ // Remove empty values
+ $admins = array_filter($admins);
+
if (!is_array($admins) || count($admins) === 0) {
$this->app->abort(400, '"admins" parameter must contains at least one value.');
}
@@ -134,6 +138,15 @@ class DashboardController extends Controller
}
$userRepository = $this->getUserRepository();
+
+ $demotedAdmins = [];
+
+ foreach ($userRepository->findAdmins() as $admin) {
+ if (!in_array($admin->getId(), $admins)) {
+ $demotedAdmins[$admin->getId()] = $admin;
+ }
+ }
+
$userRepository->findBy(['id' => $admins]);
$admins = array_map(function ($usrId) use ($userRepository) {
if (null === $user = $userRepository->find($usrId)) {
@@ -145,7 +158,10 @@ class DashboardController extends Controller
/** @var UserManipulator $userManipulator */
$userManipulator = $this->app['manipulator.user'];
+
+ $userManipulator->demote($demotedAdmins);
$userManipulator->promote($admins);
+
/** @var ACLManipulator $aclManipulator */
$aclManipulator = $this->app['manipulator.acl'];
$aclManipulator->resetAdminRights($admins);
diff --git a/lib/Alchemy/Phrasea/Controller/Admin/DataboxController.php b/lib/Alchemy/Phrasea/Controller/Admin/DataboxController.php
index 0cc7529682..f1e8e5e6a0 100644
--- a/lib/Alchemy/Phrasea/Controller/Admin/DataboxController.php
+++ b/lib/Alchemy/Phrasea/Controller/Admin/DataboxController.php
@@ -633,7 +633,7 @@ class DataboxController extends Controller
{
try {
foreach ($request->request->get('order', []) as $data) {
- $collection = \collection::get_from_base_id($this->app, $data['id']);
+ $collection = \collection::getByBaseId($this->app, $data['id']);
$collection->set_ord($data['offset']);
}
$success = true;
@@ -712,7 +712,7 @@ class DataboxController extends Controller
} catch (\Exception $e) {
return $this->app->redirectPath('admin_database_submit_collection', [
'databox_id' => $databox_id,
- 'error' => 'error',
+ 'error' => $e->getMessage(),
]);
}
}
diff --git a/lib/Alchemy/Phrasea/Controller/Admin/FeedController.php b/lib/Alchemy/Phrasea/Controller/Admin/FeedController.php
index 59ce41c97b..4180cb1d84 100644
--- a/lib/Alchemy/Phrasea/Controller/Admin/FeedController.php
+++ b/lib/Alchemy/Phrasea/Controller/Admin/FeedController.php
@@ -56,7 +56,7 @@ class FeedController extends Controller
if ($request->request->get('public') == '1') {
$feed->setIsPublic(true);
} elseif ($request->request->get('base_id')) {
- $feed->setCollection(\collection::get_from_base_id($this->app, $request->request->get('base_id')));
+ $feed->setCollection(\collection::getByBaseId($this->app, $request->request->get('base_id')));
}
$publisher->setFeed($feed);
@@ -106,7 +106,7 @@ class FeedController extends Controller
}
try {
- $collection = \collection::get_from_base_id($this->app, $request->request->get('base_id'));
+ $collection = \collection::getByBaseId($this->app, $request->request->get('base_id'));
} catch (\Exception $e) {
$collection = null;
}
diff --git a/lib/Alchemy/Phrasea/Controller/Admin/RootController.php b/lib/Alchemy/Phrasea/Controller/Admin/RootController.php
index 6433b02c4e..901d2efc95 100644
--- a/lib/Alchemy/Phrasea/Controller/Admin/RootController.php
+++ b/lib/Alchemy/Phrasea/Controller/Admin/RootController.php
@@ -32,8 +32,7 @@ class RootController extends Controller
'module' => 'admin',
'events' => $this->app['events-manager'],
'module_name' => 'Admin',
- 'notice' => $request->query->get("notice"),
- 'tree' => $this->render('admin/tree.html.twig', $params),
+ 'notice' => $request->query->get("notice")
], $params));
}
diff --git a/lib/Alchemy/Phrasea/Controller/Admin/UserController.php b/lib/Alchemy/Phrasea/Controller/Admin/UserController.php
index eabcbc9a6f..07d8bc8674 100644
--- a/lib/Alchemy/Phrasea/Controller/Admin/UserController.php
+++ b/lib/Alchemy/Phrasea/Controller/Admin/UserController.php
@@ -455,7 +455,7 @@ class UserController extends Controller
$registrationRepository->getUserRegistrations(
$user,
array_map(function ($baseId) {
- return \collection::get_from_base_id($this->app, $baseId);
+ return \collection::getByBaseId($this->app, $baseId);
}, $bases)
) as $registration) {
$registrationManipulator->rejectRegistration($registration);
@@ -473,7 +473,7 @@ class UserController extends Controller
foreach ($registrationRepository->getUserRegistrations(
$user,
array_map(function ($baseId) {
- return \collection::get_from_base_id($this->app, $baseId);
+ return \collection::getByBaseId($this->app, $baseId);
}, $bases)
) as $registration) {
$done[$usr][$registration->getBaseId()] = true;
@@ -503,7 +503,7 @@ class UserController extends Controller
];
foreach ($bases as $bas => $isok) {
- $collection = \collection::get_from_base_id($this->app, $bas);
+ $collection = \collection::getByBaseId($this->app, $bas);
$label = $collection->get_label($this->app['locale']);
if ($isok) {
diff --git a/lib/Alchemy/Phrasea/Controller/Api/V1Controller.php b/lib/Alchemy/Phrasea/Controller/Api/V1Controller.php
index 778e0c1df5..3eaabe8f3b 100644
--- a/lib/Alchemy/Phrasea/Controller/Api/V1Controller.php
+++ b/lib/Alchemy/Phrasea/Controller/Api/V1Controller.php
@@ -837,7 +837,7 @@ class V1Controller extends Controller
return $this->getBadRequestAction($request, 'Missing base_id parameter');
}
- $collection = \collection::get_from_base_id($this->app, $request->get('base_id'));
+ $collection = \collection::getByBaseId($this->app, $request->get('base_id'));
if (!$this->getAclForUser()->has_right_on_base($request->get('base_id'), 'canaddrecord')) {
return Result::createError($request, 403, sprintf(
@@ -935,7 +935,7 @@ class V1Controller extends Controller
$media = $this->app->getMediaFromUri($file->getPathname());
$record = $this->findDataboxById($request->get('databox_id'))->get_record($request->get('record_id'));
$base_id = $record->getBaseId();
- $collection = \collection::get_from_base_id($this->app, $base_id);
+ $collection = \collection::getByBaseId($this->app, $base_id);
if (!$this->getAclForUser()->has_right_on_base($base_id, 'canaddrecord')) {
return Result::createError($request, 403, sprintf(
'You do not have access to collection %s', $collection->get_label($this->app['locale.I18n'])
@@ -943,7 +943,7 @@ class V1Controller extends Controller
}
$adapt = ($request->get('adapt')===null || !(\p4field::isno($request->get('adapt'))));
$ret['adapt'] = $adapt;
- $record->substitute_subdef($request->get('name'), $media, $this->app, $adapt);
+ $this->getSubdefSubstituer()->substitute($record, $request->get('name'), $media, $adapt);
foreach ($record->get_embedable_medias() as $name => $media) {
if ($name == $request->get('name') &&
null !== ($subdef = $this->listEmbeddableMedia($request, $record, $media))) {
@@ -1611,7 +1611,7 @@ class V1Controller extends Controller
$record = $databox->get_record($record_id);
try {
- $collection = \collection::get_from_base_id($this->app, $request->get('base_id'));
+ $collection = \collection::getByBaseId($this->app, $request->get('base_id'));
$record->move_to_collection($collection, $this->getApplicationBox());
return Result::create($request, ["record" => $this->listRecord($request, $record)])->createResponse();
@@ -2067,7 +2067,7 @@ class V1Controller extends Controller
*/
protected function createStory($data)
{
- $collection = \collection::get_from_base_id($this->app, $data->{'base_id'});
+ $collection = \collection::getByBaseId($this->app, $data->{'base_id'});
if (!$this->getAclForUser()->has_right_on_base($collection->get_base_id(), 'canaddrecord')) {
$this->app->abort(403, sprintf('You can not create a story on this collection %s', $collection->get_base_id()));
@@ -2235,8 +2235,8 @@ class V1Controller extends Controller
if (!in_array($name, array('thumbnail', 'preview'))) {
continue;
}
- $media = $this->app->getMediaFromUri($value->get_pathfile());
- $story->substitute_subdef($name, $media, $this->app);
+ $media = $this->app->getMediaFromUri($value->getRealPath());
+ $this->getSubdefSubstituer()->substitute($story, $name, $media);
$this->getDataboxLogger($story->getDatabox())->log(
$story,
\Session_Logger::EVENT_SUBSTITUTE,
@@ -2304,7 +2304,7 @@ class V1Controller extends Controller
$ret = [ 'success' => true ];
}
catch (AccountException $exception) {
- $ret = [ 'success' => false, 'message' => _($exception->getMessage()) ];
+ $ret = [ 'success' => false, 'message' => $this->app->trans($exception->getMessage()) ];
}
return Result::create($request, $ret)->createResponse();
@@ -2327,7 +2327,7 @@ class V1Controller extends Controller
$service->updatePassword($command, null);
$ret = ['success' => true];
} catch (AccountException $exception) {
- $ret = [ 'success' => false, 'message' => _($exception->getMessage()) ];
+ $ret = [ 'success' => false, 'message' => $this->app->trans($exception->getMessage()) ];
}
} else {
$ret = [ 'success' => false, 'message' => (string) $form->getErrorsAsString() ];
@@ -2567,4 +2567,12 @@ class V1Controller extends Controller
{
return $this->app['phraseanet.SE.logger'];
}
+
+ /**
+ * @return \Alchemy\Phrasea\Media\SubdefSubstituer
+ */
+ private function getSubdefSubstituer()
+ {
+ return $this->app['subdef.substituer'];
+ }
}
diff --git a/lib/Alchemy/Phrasea/Controller/MediaAccessorController.php b/lib/Alchemy/Phrasea/Controller/MediaAccessorController.php
index 43f105685c..6ca16d9491 100644
--- a/lib/Alchemy/Phrasea/Controller/MediaAccessorController.php
+++ b/lib/Alchemy/Phrasea/Controller/MediaAccessorController.php
@@ -52,18 +52,7 @@ class MediaAccessorController extends Controller
public function showAction(Request $request, $token)
{
- try {
- $token = JWT::decode($token, $this->keyStorage, $this->allowedAlgorithms);
- } catch (\UnexpectedValueException $exception) {
- throw new NotFoundHttpException('Resource not found', $exception);
- } catch (\Exception $exception) {
- throw new BadRequestHttpException('Invalid token', $exception);
- }
-
- if (! isset($token->sdef) || !is_array($token->sdef) || count($token->sdef) !== 3) {
- throw new BadRequestHttpException('sdef should be a sub-definition identifier.');
- }
- list ($sbas_id, $record_id, $subdef) = $token->sdef;
+ list($sbas_id, $record_id, $subdef) = $this->validateToken($token);
try {
$databox = $this->findDataboxById($sbas_id);
@@ -93,4 +82,39 @@ class MediaAccessorController extends Controller
return $response;
}
+
+ /**
+ * @param string $token
+ * @return object
+ */
+ public function decodeToken($token)
+ {
+ try {
+ return JWT::decode($token, $this->keyStorage, $this->allowedAlgorithms);
+ } catch (\UnexpectedValueException $exception) {
+ throw new NotFoundHttpException('Resource not found', $exception);
+ } catch (\Exception $exception) {
+ throw new BadRequestHttpException('Invalid token', $exception);
+ }
+ }
+
+ /**
+ * Validate token and returns triplet containing sbas_id, record_id and subdef.
+ *
+ * @param string|object $token
+ * @return array
+ */
+ public function validateToken($token)
+ {
+ if (is_string($token)) {
+ $token = $this->decodeToken($token);
+ }
+
+ if (!isset($token->sdef) || !is_array($token->sdef) || count($token->sdef) !== 3) {
+ throw new BadRequestHttpException('sdef should be a sub-definition identifier.');
+ }
+ list ($sbas_id, $record_id, $subdef) = $token->sdef;
+
+ return array($sbas_id, $record_id, $subdef);
+ }
}
diff --git a/lib/Alchemy/Phrasea/Controller/PermalinkController.php b/lib/Alchemy/Phrasea/Controller/PermalinkController.php
index 12379c9be7..04aed5ec2c 100644
--- a/lib/Alchemy/Phrasea/Controller/PermalinkController.php
+++ b/lib/Alchemy/Phrasea/Controller/PermalinkController.php
@@ -11,11 +11,12 @@
namespace Alchemy\Phrasea\Controller;
use Alchemy\Embed\Media\Media;
+use Alchemy\Embed\Media\MediaInformation;
use Alchemy\Phrasea\Application;
+use Alchemy\Phrasea\Application\Helper\ApplicationBoxAware;
use Alchemy\Phrasea\Authentication\ACLProvider;
use Alchemy\Phrasea\Authentication\Authenticator;
use Alchemy\Phrasea\Model\Repositories\BasketElementRepository;
-use Alchemy\Phrasea\Model\Repositories\FeedItemRepository;
use Alchemy\Phrasea\Model\Serializer\CaptionSerializer;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
@@ -23,20 +24,19 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
class PermalinkController extends AbstractDelivery
{
+ use ApplicationBoxAware;
+
/** @var ACLProvider */
private $acl;
- /** @var \appbox */
- private $appbox;
/** @var Authenticator */
private $authentication;
/** @var Media */
private $mediaService;
- public function __construct(Application $app, \appbox $appbox, ACLProvider $acl, Authenticator $authenticator, Media $mediaService)
+ public function __construct(Application $app, ACLProvider $acl, Authenticator $authenticator, Media $mediaService)
{
parent::__construct($app);
- $this->appbox = $appbox;
$this->acl = $acl;
$this->authentication = $authenticator;
$this->mediaService = $mediaService;
@@ -44,9 +44,9 @@ class PermalinkController extends AbstractDelivery
public function getOptionsResponse(Request $request, $sbas_id, $record_id)
{
- $databox = $this->mediaService->getDatabox($sbas_id);
+ $databox = $this->findDataboxById($sbas_id);
$token = $request->query->get('token');
- $record = $this->mediaService->retrieveRecord($databox, $token, $record_id, $request->get('subdef', 'thumbnail'));
+ $record = $this->retrieveRecord($databox, $token, $record_id, $request->get('subdef', 'thumbnail'));
if (null === $record) {
throw new NotFoundHttpException("Record not found");
@@ -57,9 +57,9 @@ class PermalinkController extends AbstractDelivery
public function deliverCaption(Request $request, $sbas_id, $record_id)
{
- $databox = $this->mediaService->getDatabox($sbas_id);
+ $databox = $this->findDataboxById($sbas_id);
$token = $request->query->get('token');
- $record = $this->mediaService->retrieveRecord($databox, $token, $record_id, \databox_subdef::CLASS_THUMBNAIL);
+ $record = $this->retrieveRecord($databox, $token, $record_id, \databox_subdef::CLASS_THUMBNAIL);
if (null === $record) {
throw new NotFoundHttpException("Caption not found");
@@ -74,7 +74,38 @@ class PermalinkController extends AbstractDelivery
return $this->doDeliverPermaview($sbas_id, $record_id, $request->query->get('token'), $subdef);
}
- public function deliverPermaviewOldWay($sbas_id, $record_id, $token, $subdef)
+ private function doDeliverPermaview($sbas_id, $record_id, $token, $subdefName)
+ {
+ $databox = $this->findDataboxById($sbas_id);
+ $record = $this->retrieveRecord($databox, $token, $record_id, $subdefName);
+ $subdef = $record->get_subdef($subdefName);
+
+ $information = $this->mediaService->createMediaInformationFromResourceAndRoute(
+ $subdef,
+ 'permalinks_permalink',
+ [
+ 'sbas_id' => $sbas_id,
+ 'record_id' => $record_id,
+ 'subdef' => $subdefName,
+ 'label' => $record->get_title(),
+ 'token' => $token,
+ ]
+ );
+ $metaData = $this->mediaService->getMetaData($information);
+
+ return $this->app['twig']->render('overview.html.twig', [
+ 'ogMetaData' => $metaData['ogMetaData'],
+ 'subdef' => $subdef,
+ 'module_name' => 'overview',
+ 'module' => 'overview',
+ 'view' => 'overview',
+ 'token' => $token,
+ 'record' => $record,
+ 'recordUrl' => $information->getUrl(),
+ ]);
+ }
+
+ public function deliverPermaviewOldWay(Request $request, $sbas_id, $record_id, $token, $subdef)
{
return $this->doDeliverPermaview($sbas_id, $record_id, $token, $subdef);
}
@@ -84,35 +115,10 @@ class PermalinkController extends AbstractDelivery
return $this->doDeliverPermalink($request, $sbas_id, $record_id, $request->query->get('token'), $subdef);
}
- public function deliverPermalinkOldWay(Request $request, $sbas_id, $record_id, $token, $subdef)
- {
- return $this->doDeliverPermalink($request, $sbas_id, $record_id, $token, $subdef);
- }
-
- private function doDeliverPermaview($sbas_id, $record_id, $token, $subdefName)
- {
-
- $databox = $this->mediaService->getDatabox($sbas_id);
- $record = $this->mediaService->retrieveRecord($databox, $token, $record_id, $subdefName);
- $metaDatas = $this->mediaService->getMetaDatas($record, $subdefName);
- $subdef = $record->get_subdef($subdefName);
-
- return $this->app['twig']->render('overview.html.twig', [
- 'ogMetaDatas' => $metaDatas['ogMetaDatas'],
- 'subdef' => $subdef,
- 'module_name' => 'overview',
- 'module' => 'overview',
- 'view' => 'overview',
- 'token' => $token,
- 'record' => $record,
- ]);
- }
-
private function doDeliverPermalink(Request $request, $sbas_id, $record_id, $token, $subdef)
{
- $databox = $this->mediaService->getDatabox($sbas_id);
- // $record = $this->retrieveRecord($databox, $token, $record_id, $subdef);
- $record = $this->mediaService->retrieveRecord($databox, $token, $record_id, $subdef);
+ $databox = $this->findDataboxById($sbas_id);
+ $record = $this->retrieveRecord($databox, $token, $record_id, $subdef);
$watermark = $stamp = false;
if ($this->authentication->isAuthenticated()) {
@@ -132,7 +138,7 @@ class PermalinkController extends AbstractDelivery
return $this->deliverContentWithCaptionLink($request, $record, $subdef, $watermark, $stamp, $token);
}
- $collection = \collection::get_from_base_id($this->app, $record->get_base_id());
+ $collection = \collection::getByBaseId($this->app, $record->get_base_id());
switch ($collection->get_pub_wm()) {
default:
case 'none':
@@ -170,4 +176,42 @@ class PermalinkController extends AbstractDelivery
return $response;
}
+
+ public function deliverPermalinkOldWay(Request $request, $sbas_id, $record_id, $token, $subdef)
+ {
+ return $this->doDeliverPermalink($request, $sbas_id, $record_id, $token, $subdef);
+ }
+
+ /**
+ * @param \databox $databox
+ * @param string $token
+ * @param int $record_id
+ * @param string $subdef
+ * @return \record_adapter
+ */
+ private function retrieveRecord(\databox $databox, $token, $record_id, $subdef)
+ {
+ try {
+ $record = $databox->get_record($record_id);
+ $subDefinition = $record->get_subdef($subdef);
+ $permalink = $subDefinition->get_permalink();
+ } catch (\Exception $exception) {
+ throw new NotFoundHttpException('Wrong token.', $exception);
+ }
+
+ if (null === $permalink || !$permalink->get_is_activated()) {
+ throw new NotFoundHttpException('This token has been disabled.');
+ }
+
+ $feedItemsRepository = $this->app['repo.feed-items'];
+ if (in_array($subdef, [\databox_subdef::CLASS_PREVIEW, \databox_subdef::CLASS_THUMBNAIL])
+ && $feedItemsRepository->isRecordInPublicFeed($databox->get_sbas_id(), $record_id)
+ ) {
+ return $record;
+ } elseif ($permalink->get_token() == (string)$token) {
+ return $record;
+ }
+
+ throw new NotFoundHttpException('Wrong token.');
+ }
}
diff --git a/lib/Alchemy/Phrasea/Controller/Prod/EditController.php b/lib/Alchemy/Phrasea/Controller/Prod/EditController.php
index ffbb76b386..d935211685 100644
--- a/lib/Alchemy/Phrasea/Controller/Prod/EditController.php
+++ b/lib/Alchemy/Phrasea/Controller/Prod/EditController.php
@@ -297,7 +297,7 @@ class EditController extends Controller
continue;
}
- $media = $this->app->getMediaFromUri($value->get_pathfile());
+ $media = $this->app->getMediaFromUri($value->getRealPath());
$this->getSubDefinitionSubstituer()->substitute($reg_record, $name, $media);
$this->getDispatcher()->dispatch(PhraseaEvents::RECORD_EDIT, new RecordEdit($reg_record));
$this->getDataboxLogger($reg_record->get_databox())->log(
diff --git a/lib/Alchemy/Phrasea/Controller/Prod/MoveCollectionController.php b/lib/Alchemy/Phrasea/Controller/Prod/MoveCollectionController.php
index c6daefa300..74c8378e84 100644
--- a/lib/Alchemy/Phrasea/Controller/Prod/MoveCollectionController.php
+++ b/lib/Alchemy/Phrasea/Controller/Prod/MoveCollectionController.php
@@ -58,7 +58,7 @@ class MoveCollectionController extends Controller
}
try {
- $collection = \collection::get_from_base_id($this->app, $request->request->get('base_id'));
+ $collection = \collection::getByBaseId($this->app, $request->request->get('base_id'));
} catch (\Exception_Databox_CollectionNotFound $e) {
$datas['message'] = $this->app->trans('Invalid target collection');
diff --git a/lib/Alchemy/Phrasea/Controller/Prod/PropertyController.php b/lib/Alchemy/Phrasea/Controller/Prod/PropertyController.php
index e4e832521c..f368b24046 100644
--- a/lib/Alchemy/Phrasea/Controller/Prod/PropertyController.php
+++ b/lib/Alchemy/Phrasea/Controller/Prod/PropertyController.php
@@ -30,13 +30,14 @@ class PropertyController extends Controller
$records = RecordsRequest::fromRequest($this->app, $request, false, ['chgstatus']);
- if (count($records->databoxes()) > 1) {
+ $databoxes = $records->databoxes();
+ if (count($databoxes) > 1) {
return new Response($this->render('prod/actions/Property/index.html.twig', [
'records' => $records,
]));
}
- $databox = current($records->databoxes());
+ $databox = reset($databoxes);
$statusStructure = $databox->getStatusStructure();
$recordsStatuses = [];
diff --git a/lib/Alchemy/Phrasea/Controller/Prod/QueryController.php b/lib/Alchemy/Phrasea/Controller/Prod/QueryController.php
index 86b121ef46..839fa0f2dc 100644
--- a/lib/Alchemy/Phrasea/Controller/Prod/QueryController.php
+++ b/lib/Alchemy/Phrasea/Controller/Prod/QueryController.php
@@ -156,7 +156,11 @@ class QueryController extends Controller
. $this->app->trans('%total% reponses', ['%total%' => ''.$result->getTotal().'']) . '';
$json['infos'] = $infoResult;
- $json['navigation'] = $string;
+ $json['navigationTpl'] = $string;
+ $json['navigation'] = [
+ 'page' => $page,
+ 'perPage' => $perPage
+ ];
$prop = null;
diff --git a/lib/Alchemy/Phrasea/Controller/Prod/ShareController.php b/lib/Alchemy/Phrasea/Controller/Prod/ShareController.php
index 207d64f4d5..85df3351cf 100644
--- a/lib/Alchemy/Phrasea/Controller/Prod/ShareController.php
+++ b/lib/Alchemy/Phrasea/Controller/Prod/ShareController.php
@@ -41,35 +41,13 @@ class ShareController extends Controller
$preview = $record->get_preview();
- if ($preview->get_permalink() !== null) {
+ if (null !== $previewLink = $preview->get_permalink()) {
+ $permalinkUrl = $previewLink->get_url();
+ $permaviewUrl = $previewLink->get_page();
+ $previewWidth = $preview->get_width();
+ $previewHeight = $preview->get_height();
-
- $subdefName = $preview->get_name();
- $subdef = $record->get_subdef($subdefName);
-
- switch ($record->getType()) {
-
- case 'flexpaper':
- case 'document':
- case 'audio':
- case 'video':
- default:
- $token = $preview->get_permalink()->get_token();
- $permalinkUrl = $preview->get_permalink()->get_url();
- $permaviewUrl = $preview->get_permalink()->get_page();
- $previewWidth = $preview->get_width();
- $previewHeight = $preview->get_height();
- break;
- }
-
-
- $sbas_id = $record->getDataboxId();
- $embedUrl = $this->app->url('alchemy_embed_view', [
- 'sbas_id' => $sbas_id,
- 'record_id' => $record_id,
- 'subdefName' => $subdefName,
- 'token' => $token,
- ]);
+ $embedUrl = $this->app->url('alchemy_embed_view', ['url' => (string)$permalinkUrl]);
$outputVars = [
'isAvailable' => true,
diff --git a/lib/Alchemy/Phrasea/Controller/Prod/StoryController.php b/lib/Alchemy/Phrasea/Controller/Prod/StoryController.php
index c74b6ddce4..e8efc59e47 100644
--- a/lib/Alchemy/Phrasea/Controller/Prod/StoryController.php
+++ b/lib/Alchemy/Phrasea/Controller/Prod/StoryController.php
@@ -33,7 +33,7 @@ class StoryController extends Controller
public function postCreateFormAction(Request $request)
{
- $collection = \collection::get_from_base_id($this->app, $request->request->get('base_id'));
+ $collection = \collection::getByBaseId($this->app, $request->request->get('base_id'));
if (!$this->getAclForUser()->has_right_on_base($collection->get_base_id(), 'canaddrecord')) {
throw new AccessDeniedHttpException('You can not create a story on this collection');
diff --git a/lib/Alchemy/Phrasea/Controller/Prod/ToolsController.php b/lib/Alchemy/Phrasea/Controller/Prod/ToolsController.php
index 4632c197f6..a0784a46eb 100644
--- a/lib/Alchemy/Phrasea/Controller/Prod/ToolsController.php
+++ b/lib/Alchemy/Phrasea/Controller/Prod/ToolsController.php
@@ -83,7 +83,7 @@ class ToolsController extends Controller
if (!$record->isStory()) {
try {
$metadata = $this->getExifToolReader()
- ->files($record->get_subdef('document')->get_pathfile())
+ ->files($record->get_subdef('document')->getRealPath())
->first()->getMetadatas();
} catch (PHPExiftoolException $e) {
// ignore
diff --git a/lib/Alchemy/Phrasea/Controller/Prod/UploadController.php b/lib/Alchemy/Phrasea/Controller/Prod/UploadController.php
index 9ca3c5f9d9..1c04bfd251 100644
--- a/lib/Alchemy/Phrasea/Controller/Prod/UploadController.php
+++ b/lib/Alchemy/Phrasea/Controller/Prod/UploadController.php
@@ -133,7 +133,7 @@ class UploadController extends Controller
$this->getFilesystem()->rename($uploadedFilename, $renamedFilename);
$media = $this->app->getMediaFromUri($renamedFilename);
- $collection = \collection::get_from_base_id($this->app, $base_id);
+ $collection = \collection::getByBaseId($this->app, $base_id);
$lazaretSession = new LazaretSession();
$lazaretSession->setUser($this->getAuthenticatedUser());
diff --git a/lib/Alchemy/Phrasea/Controller/Root/AccountController.php b/lib/Alchemy/Phrasea/Controller/Root/AccountController.php
index 7acbc0d5d8..87bcbdc721 100644
--- a/lib/Alchemy/Phrasea/Controller/Root/AccountController.php
+++ b/lib/Alchemy/Phrasea/Controller/Root/AccountController.php
@@ -316,7 +316,7 @@ class AccountController extends Controller
if (0 !== count($registrations)) {
foreach ($registrations as $baseId) {
$this->getRegistrationManipulator()
- ->createRegistration($user, \collection::get_from_base_id($this->app, $baseId));
+ ->createRegistration($user, \collection::getByBaseId($this->app, $baseId));
}
$this->app->addFlash('success', $this->app->trans('Your registration requests have been taken into account.'));
}
diff --git a/lib/Alchemy/Phrasea/Controller/Root/SessionController.php b/lib/Alchemy/Phrasea/Controller/Root/SessionController.php
index fcd23652bf..ece103e59c 100644
--- a/lib/Alchemy/Phrasea/Controller/Root/SessionController.php
+++ b/lib/Alchemy/Phrasea/Controller/Root/SessionController.php
@@ -82,7 +82,7 @@ class SessionController extends Controller
if (in_array($this->getSession()->get('phraseanet.message'), ['1', null])) {
if ($this->app['phraseanet.configuration']['main']['maintenance']) {
- $ret['message'] .= _('The application is going down for maintenance, please logout.');
+ $ret['message'] .= $this->app->trans('The application is going down for maintenance, please logout.');
}
if ($this->getConf()->get(['registry', 'maintenance', 'enabled'], false)) {
diff --git a/lib/Alchemy/Phrasea/ControllerProvider/ControllerProviderServiceProvider.php b/lib/Alchemy/Phrasea/ControllerProvider/ControllerProviderServiceProvider.php
new file mode 100644
index 0000000000..92e35853ab
--- /dev/null
+++ b/lib/Alchemy/Phrasea/ControllerProvider/ControllerProviderServiceProvider.php
@@ -0,0 +1,106 @@
+loadProviders();
+
+ foreach ($this->controllerProviders as $class => $values) {
+ $app->register(new $class, $values);
+ }
+ }
+
+ /**
+ * Bootstraps the application.
+ *
+ * This method is called after all services are registered
+ * and should be used for "dynamic" configuration (whenever
+ * a service must be requested).
+ */
+ public function boot(Application $app)
+ {
+ // Nothing to do here
+ }
+
+ public function loadProviders()
+ {
+ $this->controllerProviders = [
+ Admin\Collection::class => [],
+ Admin\ConnectedUsers::class => [],
+ Admin\Dashboard::class => [],
+ Admin\Databox::class => [],
+ Admin\Databoxes::class => [],
+ Admin\Feeds::class => [],
+ Admin\Fields::class => [],
+ Admin\Plugins::class => [],
+ Admin\Root::class => [],
+ Admin\SearchEngine::class => [],
+ Admin\Setup::class => [],
+ Admin\Subdefs::class => [],
+ Admin\TaskManager::class => [],
+ Admin\Users::class => [],
+ Client\Root::class => [],
+ Datafiles::class => [],
+ Lightbox::class => [],
+ MediaAccessor::class => [],
+ Minifier::class => [],
+ Permalink::class => [],
+ Prod\BasketProvider::class => [],
+ Prod\Bridge::class => [],
+ Prod\DoDownload::class => [],
+ Prod\Download::class => [],
+ Prod\Edit::class => [],
+ Prod\Export::class => [],
+ Prod\Feed::class => [],
+ Prod\Language::class => [],
+ Prod\Lazaret::class => [],
+ Prod\MoveCollection::class => [],
+ Prod\Order::class => [],
+ Prod\Printer::class => [],
+ Prod\Property::class => [],
+ Prod\Push::class => [],
+ Prod\Query::class => [],
+ Prod\Record::class => [],
+ Prod\Root::class => [],
+ Prod\Share::class => [],
+ Prod\Story::class => [],
+ Prod\Tools::class => [],
+ Prod\Tooltip::class => [],
+ Prod\TOU::class => [],
+ Prod\Upload::class => [],
+ Prod\UsrLists::class => [],
+ Prod\WorkZone::class => [],
+ Report\Activity::class => [],
+ Report\Information::class => [],
+ Report\Root::class => [],
+ Root\Account::class => [],
+ Root\Developers::class => [],
+ Root\Login::class => [],
+ Root\Root::class => [],
+ Root\RSSFeeds::class => [],
+ Root\Session::class => [],
+ Setup::class => [],
+ Thesaurus\Thesaurus::class => [],
+ Thesaurus\Xmlhttp::class => [],
+ User\Notifications::class => [],
+ User\Preferences::class => [],
+ EmbedServiceProvider::class => [],
+ ];
+ }
+}
diff --git a/lib/Alchemy/Phrasea/ControllerProvider/Permalink.php b/lib/Alchemy/Phrasea/ControllerProvider/Permalink.php
index dac57361d1..619c7526c9 100644
--- a/lib/Alchemy/Phrasea/ControllerProvider/Permalink.php
+++ b/lib/Alchemy/Phrasea/ControllerProvider/Permalink.php
@@ -24,9 +24,15 @@ class Permalink implements ControllerProviderInterface, ServiceProviderInterface
public function register(Application $app)
{
$app['controller.permalink'] = $app->share(function (PhraseaApplication $app) {
- return (new PermalinkController($app, $app->getApplicationBox(), $app['acl'], $app->getAuthenticator(), $app['alchemy_embed.service.media']))
+ return (new PermalinkController(
+ $app,
+ $app['acl'],
+ $app->getAuthenticator(),
+ $app['alchemy_embed.service.media']
+ ))
->setDataboxLoggerLocator($app['phraseanet.logger'])
->setDelivererLocator(new LazyLocator($app, 'phraseanet.file-serve'))
+ ->setApplicationBox($app['phraseanet.appbox'])
;
});
}
diff --git a/lib/Alchemy/Phrasea/Core/Configuration/AccessRestriction.php b/lib/Alchemy/Phrasea/Core/Configuration/AccessRestriction.php
index ef76784639..ff5ccd6a8a 100644
--- a/lib/Alchemy/Phrasea/Core/Configuration/AccessRestriction.php
+++ b/lib/Alchemy/Phrasea/Core/Configuration/AccessRestriction.php
@@ -1,9 +1,8 @@
conf = $conf;
+ $this->propertyAccess = $propertyAccess;
$this->appbox = $appbox;
$this->logger = $logger;
- $this->cache = $cache;
}
/**
* Returns true if a configuration is set.
*
- * @return Boolean
+ * @return bool
*/
public function isRestricted()
{
$this->load();
- return $this->cache->fetch('restricted');
+ return $this->restricted;
}
/**
@@ -49,7 +74,7 @@ class AccessRestriction
*
* @param \databox $databox
*
- * @return Boolean
+ * @return bool
*/
public function isDataboxAvailable(\databox $databox)
{
@@ -57,7 +82,7 @@ class AccessRestriction
return true;
}
- return in_array($databox->get_sbas_id(), $this->cache->fetch('available_databoxes'), true);
+ return in_array($databox->get_sbas_id(), $this->availableDataboxes, true);
}
/**
@@ -72,7 +97,7 @@ class AccessRestriction
return $databoxes;
}
- $available = array_flip($this->cache->fetch('available_databoxes'));
+ $available = array_flip($this->availableDataboxes);
return array_filter($databoxes, function (\databox $databox) use ($available) {
return isset($available[$databox->get_sbas_id()]);
@@ -84,7 +109,7 @@ class AccessRestriction
*
* @param \collection $collection
*
- * @return Boolean
+ * @return bool
*/
public function isCollectionAvailable(\collection $collection)
{
@@ -92,30 +117,31 @@ class AccessRestriction
return true;
}
- $availableCollections = $this->cache->fetch('available_collections_'.$collection->get_databox()->get_sbas_id()) ?: [];
+ $availableCollections = isset($this->availableCollections[$collection->get_sbas_id()])
+ ? $this->availableCollections[$collection->get_sbas_id()] : [];
return in_array($collection->get_base_id(), $availableCollections, true);
}
private function load()
{
- if ($this->cache->fetch('loaded')) {
+ if ($this->loaded) {
return;
}
- $this->cache->save('loaded', true);
+ $this->loaded = true;
$allowedDataboxIds = array_map(function ($dbConf) {
return $dbConf['id'];
- }, $this->conf->get('databoxes', []));
+ }, $this->propertyAccess->get('databoxes', []));
if (count($allowedDataboxIds) === 0) {
- $this->cache->save('restricted', false);
+ $this->restricted = false;
return;
}
- $this->cache->save('restricted', true);
+ $this->restricted = true;
$databoxIds = array_map(function (\databox $databox) { return $databox->get_sbas_id(); }, $this->appbox->get_databoxes());
$errors = array_diff($allowedDataboxIds, $databoxIds);
@@ -124,18 +150,15 @@ class AccessRestriction
$this->logger->error(sprintf('Misconfiguration for allowed databoxes : ids %s do not exist', implode(', ', $errors)));
}
- $allowedDataboxIds = array_intersect($allowedDataboxIds, $databoxIds);
- $this->cache->save('available_databoxes', $allowedDataboxIds);
+ $this->availableDataboxes = array_intersect($allowedDataboxIds, $databoxIds);
$this->loadCollections();
}
private function loadCollections()
{
- $allowedDataboxIds = $this->cache->fetch('available_databoxes');
-
- foreach ($this->conf->get('databoxes') as $databox) {
- if (!in_array($databox['id'], $allowedDataboxIds, true)) {
+ foreach ($this->propertyAccess->get('databoxes') as $databox) {
+ if (!in_array($databox['id'], $this->availableDataboxes, true)) {
continue;
}
@@ -148,9 +171,7 @@ class AccessRestriction
$this->logger->error(sprintf('Misconfiguration for allowed collections : ids %s do not exist', implode(', ', $errors)));
}
- $collections = array_intersect($collections, $availableBaseIds);
-
- $this->cache->save('available_collections_'.$databox['id'], $collections);
+ $this->availableCollections[$databox['id']] = array_intersect($collections, $availableBaseIds);
}
}
}
diff --git a/lib/Alchemy/Phrasea/Core/Configuration/DisplaySettingService.php b/lib/Alchemy/Phrasea/Core/Configuration/DisplaySettingService.php
index 751945ffed..b82b3501e0 100644
--- a/lib/Alchemy/Phrasea/Core/Configuration/DisplaySettingService.php
+++ b/lib/Alchemy/Phrasea/Core/Configuration/DisplaySettingService.php
@@ -33,7 +33,7 @@ class DisplaySettingService
'warning_on_delete_story' => 'true',
'client_basket_status' => '1',
'css' => '000000',
- 'start_page_query' => 'last',
+ 'start_page_query' => '',
'start_page' => 'QUERY',
'rollover_thumbnail' => 'caption',
'technical_display' => '1',
diff --git a/lib/Alchemy/Phrasea/Core/Event/Subscriber/CacheStatisticsSubscriber.php b/lib/Alchemy/Phrasea/Core/Event/Subscriber/CacheStatisticsSubscriber.php
new file mode 100644
index 0000000000..c0f05b6def
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Core/Event/Subscriber/CacheStatisticsSubscriber.php
@@ -0,0 +1,120 @@
+cache = $cache;
+ $this->cacheType = $cache->getName();
+ }
+
+ public function getCacheNamespace()
+ {
+ if ($this->cache instanceof TraceableCache) {
+ return $this->cache->getNamespace();
+ }
+
+ return '[ root ]';
+ }
+
+ public function getCallSummary()
+ {
+ if ($this->cache instanceof TraceableCache) {
+ return $this->cache->getSummary();
+ }
+
+ return [
+ 'calls' => 0,
+ 'hits' => 0,
+ 'misses' => 0,
+ 'calls_by_type' => [],
+ 'calls_by_key' => []
+ ];
+ }
+
+ public function getCalls()
+ {
+ if ($this->cache instanceof TraceableCache) {
+ return $this->cache->getCalls();
+ }
+
+ return [];
+ }
+
+ public function getTimeSpent()
+ {
+ if ($this->cache instanceof TraceableCache) {
+ return $this->cache->getTotalTime();
+ }
+
+ return 0;
+ }
+
+ public function getCacheType()
+ {
+ return $this->cacheType;
+ }
+
+ public function getInitialStats()
+ {
+ return $this->stats;
+ }
+
+ public function getCurrentStats()
+ {
+ return $this->cache->getStats();
+ }
+
+ public function onKernelRequest()
+ {
+ $this->stats = $this->cache->getStats();
+ }
+
+ /**
+ * Returns an array of event names this subscriber wants to listen to.
+ *
+ * The array keys are event names and the value can be:
+ *
+ * * The method name to call (priority defaults to 0)
+ * * An array composed of the method name to call and the priority
+ * * An array of arrays composed of the method names to call and respective
+ * priorities, or 0 if unset
+ *
+ * For instance:
+ *
+ * * array('eventName' => 'methodName')
+ * * array('eventName' => array('methodName', $priority))
+ * * array('eventName' => array(array('methodName1', $priority), array('methodName2'))
+ *
+ * @return array The event names to listen to
+ */
+ public static function getSubscribedEvents()
+ {
+ return [ KernelEvents::REQUEST => [ 'onKernelRequest', 2048 ] ];
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Core/Event/Subscriber/DebuggerSubscriber.php b/lib/Alchemy/Phrasea/Core/Event/Subscriber/DebuggerSubscriber.php
index 1a13e6fccf..696546f7dd 100644
--- a/lib/Alchemy/Phrasea/Core/Event/Subscriber/DebuggerSubscriber.php
+++ b/lib/Alchemy/Phrasea/Core/Event/Subscriber/DebuggerSubscriber.php
@@ -37,10 +37,6 @@ class DebuggerSubscriber implements EventSubscriberInterface
public function checkIp(GetResponseEvent $event)
{
- if (Application::ENV_DEV !== $this->app->getEnvironment()) {
- return;
- }
-
if ($this->app['configuration.store']->isSetup() && $this->app['conf']->has(['debugger', 'allowed-ips'])) {
$allowedIps = $this->app['conf']->get(['debugger', 'allowed-ips']);
$allowedIps = is_array($allowedIps) ? $allowedIps : [$allowedIps];
diff --git a/lib/Alchemy/Phrasea/Core/Event/Subscriber/RegistrationSubscriber.php b/lib/Alchemy/Phrasea/Core/Event/Subscriber/RegistrationSubscriber.php
index 4aa96e0392..6266983a18 100644
--- a/lib/Alchemy/Phrasea/Core/Event/Subscriber/RegistrationSubscriber.php
+++ b/lib/Alchemy/Phrasea/Core/Event/Subscriber/RegistrationSubscriber.php
@@ -1,9 +1,8 @@
getLogin());
- $body .= sprintf("%s : %s\n", _('admin::compte-utilisateur nom'), $registeredUser->getFirstName());
- $body .= sprintf("%s : %s\n", _('admin::compte-utilisateur prenom'), $registeredUser->getLastName());
- $body .= sprintf("%s : %s\n", _('admin::compte-utilisateur email'), $registeredUser->getEmail());
+ $body .= sprintf("%s : %s\n", $this->app->trans('admin::compte-utilisateur nom'), $registeredUser->getFirstName());
+ $body .= sprintf("%s : %s\n", $this->app->trans('admin::compte-utilisateur prenom'), $registeredUser->getLastName());
+ $body .= sprintf("%s : %s\n", $this->app->trans('admin::compte-utilisateur email'), $registeredUser->getEmail());
$body .= sprintf("%s/%s\n", $registeredUser->get_job(), $registeredUser->getCompany());
$readyToSend = false;
diff --git a/lib/Alchemy/Phrasea/Core/Event/Subscriber/TrustedProxySubscriber.php b/lib/Alchemy/Phrasea/Core/Event/Subscriber/TrustedProxySubscriber.php
index a993d64533..faafd01efb 100644
--- a/lib/Alchemy/Phrasea/Core/Event/Subscriber/TrustedProxySubscriber.php
+++ b/lib/Alchemy/Phrasea/Core/Event/Subscriber/TrustedProxySubscriber.php
@@ -29,7 +29,7 @@ class TrustedProxySubscriber implements EventSubscriberInterface
public static function getSubscribedEvents()
{
return [
- KernelEvents::REQUEST => ['setProxyConf', 0],
+ KernelEvents::REQUEST => ['setProxyConf', 1024],
];
}
diff --git a/lib/Alchemy/Phrasea/Core/MetaProvider/DatabaseMetaProvider.php b/lib/Alchemy/Phrasea/Core/MetaProvider/DatabaseMetaProvider.php
new file mode 100644
index 0000000000..84ecea5311
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Core/MetaProvider/DatabaseMetaProvider.php
@@ -0,0 +1,102 @@
+register(new DoctrineServiceProvider());
+ $this->setupDBAL($app);
+ $app->register(new DoctrineOrmServiceProvider());
+ $this->setupOrms($app);
+ $app->register(new ORMServiceProvider());
+ }
+
+ private function setupDBAL(PhraseaApplication $app)
+ {
+ $app['dbs.config'] = $app->share($app->extend('dbs.config', function ($configs, $app) {
+ if (! $app->isDebug()) {
+ return $configs;
+ }
+
+ foreach($configs->keys() as $service) {
+ $app['dbal.config.register.loggers']($configs[$service]);
+ }
+
+ return $configs;
+ }));
+
+ $app['dbs.event_manager'] = $app->share($app->extend('dbs.event_manager', function ($eventManagers, $app) {
+ foreach ($eventManagers->keys() as $name) {
+ /** @var EventManager $eventManager */
+ $eventManager = $eventManagers[$name];
+ $app['dbal.evm.register.listeners']($eventManager);
+
+ $eventManager->addEventListener(Events::postConnect, $app);
+ }
+
+ return $eventManagers;
+ }));
+ }
+
+ private function setupOrms(PhraseaApplication $app)
+ {
+ // Override "orm.cache.configurer" service provided for benefiting
+ // of "phraseanet.cache-service"
+ $app['orm.cache.configurer'] = $app->protect(function($name, Configuration $config, $options) use ($app) {
+ /** @var Manager $service */
+ $service = $app['phraseanet.cache-service'];
+
+ $config->setMetadataCacheImpl(
+ $service->factory('ORM_metadata', $app['orm.cache.driver'], $app['orm.cache.options'])
+ );
+ $config->setQueryCacheImpl(
+ $service->factory('ORM_query', $app['orm.cache.driver'], $app['orm.cache.options'])
+ );
+ $config->setResultCacheImpl(
+ $service->factory('ORM_result', $app['orm.cache.driver'], $app['orm.cache.options'])
+ );
+ $config->setHydrationCacheImpl(
+ $service->factory('ORM_hydration', $app['orm.cache.driver'], $app['orm.cache.options'])
+ );
+ });
+
+ $app['orm.proxies_dir'] = $app['root.path'].'/resources/proxies';
+ $app['orm.auto_generate_proxies'] = $app['debug'];
+ $app['orm.proxies_namespace'] = 'Alchemy\Phrasea\Model\Proxies';
+
+ $app['orm.ems'] = $app->share($app->extend('orm.ems', function (\Pimple $ems, $app) {
+ GedmoExtension::registerAnnotations();
+
+ foreach ($ems->keys() as $key) {
+ $app['orm.annotation.register']($key);
+ $connection = $ems[$key]->getConnection();
+
+ $app['connection.pool.manager']->add($connection);
+
+ $types = $app['orm.ems.options'][$key]['types'];
+ $app['dbal.type.register']($connection, $types);
+ }
+
+ return $ems;
+ }));
+ }
+
+ public function boot(Application $app)
+ {
+ // no-op
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Core/MetaProvider/HttpStackMetaProvider.php b/lib/Alchemy/Phrasea/Core/MetaProvider/HttpStackMetaProvider.php
new file mode 100644
index 0000000000..b3a308fc18
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Core/MetaProvider/HttpStackMetaProvider.php
@@ -0,0 +1,107 @@
+register(new HttpFragmentServiceProvider());
+ $app->register(new UrlGeneratorServiceProvider());
+
+ $this->setupRequestContext($app);
+
+ $app->register(new SessionHandlerServiceProvider());
+ $app->register(new SessionServiceProvider(), [
+ 'session.test' => $app->getEnvironment() === PhraseaApplication::ENV_TEST,
+ 'session.storage.options' => ['cookie_lifetime' => 0]
+ ]);
+
+ $app['session.storage.test'] = $app->share(function () {
+ return new MockArraySessionStorage();
+ });
+
+ $app['session.storage.handler'] = $app->share(function (Application $app) {
+ if (!$app['phraseanet.configuration-tester']->isInstalled()) {
+ return new NullSessionHandler();
+ }
+ return $app['session.storage.handler.factory']->create($app['conf']);
+ });
+
+ $app->register(new ControllerProviderServiceProvider());
+
+ $this->registerCors($app);
+ }
+
+ public function setupRequestContext(Application $app)
+ {
+ $app['request_context'] = $app->share($app->extend('request_context', function (RequestContext $context, Application $app) {
+ if ($app['configuration.store']->isSetup()) {
+ $data = parse_url($app['conf']->get('servername'));
+
+ if (isset($data['scheme'])) {
+ $context->setScheme($data['scheme']);
+ }
+ if (isset($data['host'])) {
+ $context->setHost($data['host']);
+ }
+ }
+
+ return $context;
+ }));
+ }
+
+ public function registerCors(Application $app)
+ {
+ $app->register(new ContentNegotiationServiceProvider());
+ $app->register(new CorsServiceProvider(), [
+ 'alchemy_cors.debug' => $app['debug'],
+ 'alchemy_cors.cache_path' => function (Application $app) {
+ return rtrim($app['cache.path'], '/\\') . '/alchemy_cors.cache.php';
+ },
+ ]);
+
+ $app['phraseanet.api_cors.options_provider'] = function (Application $app) {
+ $paths = [];
+
+ if (isset($app['phraseanet.configuration']['api_cors'])) {
+ $config = $app['phraseanet.configuration']['api_cors'];
+
+ if (isset($config['enabled']) && $config['enabled']) {
+ unset($config['enabled']);
+
+ $paths['/api/v\d+/'] = $config;
+ }
+ }
+
+ return new DefaultProvider($paths, []);
+ };
+
+ $app['alchemy_cors.options_providers'][] = 'phraseanet.api_cors.options_provider';
+ }
+
+ public function boot(Application $app)
+ {
+ // no-op
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Core/MetaProvider/MediaUtilitiesMetaServiceProvider.php b/lib/Alchemy/Phrasea/Core/MetaProvider/MediaUtilitiesMetaServiceProvider.php
new file mode 100644
index 0000000000..5092c6c77f
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Core/MetaProvider/MediaUtilitiesMetaServiceProvider.php
@@ -0,0 +1,53 @@
+register(new ImagineServiceProvider());
+ $app->register(new FFMpegServiceProvider());
+ $app->register(new MediaAlchemystServiceProvider());
+ $app->register(new PhraseanetMediaAlchemystServiceProvider());
+ $app->register(new MediaVorusServiceProvider());
+ $app->register(new MP4BoxServiceProvider());
+ $app->register(new PHPExiftoolServiceProvider());
+
+ $app['imagine.factory'] = $app->share(function (Application $app) {
+ if ($app['conf']->get(['registry', 'executables', 'imagine-driver']) != '') {
+ return $app['conf']->get(['registry', 'executables', 'imagine-driver']);
+ }
+
+ if (class_exists('\Gmagick')) {
+ return 'gmagick';
+ }
+
+ if (class_exists('\Imagick')) {
+ return 'imagick';
+ }
+
+ if (extension_loaded('gd')) {
+ return 'gd';
+ }
+
+ throw new \RuntimeException('No Imagine driver available');
+ });
+ }
+
+ public function boot(Application $app)
+ {
+ // no-op
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Core/MetaProvider/TemplateEngineMetaProvider.php b/lib/Alchemy/Phrasea/Core/MetaProvider/TemplateEngineMetaProvider.php
new file mode 100644
index 0000000000..15a2768f9e
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Core/MetaProvider/TemplateEngineMetaProvider.php
@@ -0,0 +1,23 @@
+register(new TwigServiceProvider());
+ $app->register(new PhraseanetTwigServiceProvider());
+ }
+
+ public function boot(Application $app)
+ {
+ // no-op
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Core/MetaProvider/TranslationMetaProvider.php b/lib/Alchemy/Phrasea/Core/MetaProvider/TranslationMetaProvider.php
new file mode 100644
index 0000000000..a94ce2ad0b
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Core/MetaProvider/TranslationMetaProvider.php
@@ -0,0 +1,39 @@
+register(new TranslationServiceProvider(), [
+ 'locale_fallbacks' => ['fr'],
+ 'translator.resources' => [
+ [ 'xlf', __DIR__.'/../../../../../resources/locales/messages.fr.xlf', 'fr', 'messages' ],
+ [ 'xlf', __DIR__.'/../../../../../resources/locales/validators.fr.xlf', 'fr', 'validators' ],
+ [ 'xlf', __DIR__.'/../../../../../resources/locales/messages.en.xlf', 'en', 'messages' ],
+ [ 'xlf', __DIR__.'/../../../../../resources/locales/validators.en.xlf', 'en', 'validators' ],
+ [ 'xlf', __DIR__.'/../../../../../resources/locales/messages.de.xlf', 'de', 'messages' ],
+ [ 'xlf', __DIR__.'/../../../../../resources/locales/validators.de.xlf', 'de', 'validators' ],
+ [ 'xlf', __DIR__.'/../../../../../resources/locales/messages.nl.xlf', 'nl', 'messages' ],
+ [ 'xlf', __DIR__.'/../../../../../resources/locales/validators.nl.xlf', 'nl', 'validators' ]
+ ],
+ 'translator.cache-options' => [
+ 'debug' => $app['debug'],
+ 'cache_dir' => $app->share(function($app) {
+ return $app['cache.path'] . '/translations';
+ }),
+ ],
+ ]);
+ }
+
+ public function boot(Application $app)
+ {
+ // no-op
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Core/Middleware/SetupMiddlewareProvider.php b/lib/Alchemy/Phrasea/Core/Middleware/SetupMiddlewareProvider.php
new file mode 100644
index 0000000000..fb4151bc42
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Core/Middleware/SetupMiddlewareProvider.php
@@ -0,0 +1,53 @@
+protect(function (Request $request) use ($app) {
+ if (0 === strpos($request->getPathInfo(), '/setup')) {
+ if (!$app['phraseanet.configuration-tester']->isInstalled()) {
+ if (!$app['phraseanet.configuration-tester']->isBlank()) {
+ if ('setup_upgrade_instructions' !== $app['request']->attributes->get('_route')) {
+ return $app->redirectPath('setup_upgrade_instructions');
+ }
+ }
+ } elseif (!$app['phraseanet.configuration-tester']->isBlank()) {
+ return $app->redirectPath('homepage');
+ }
+ } else {
+ if (false === strpos($request->getPathInfo(), '/include/minify')) {
+ $app['firewall']->requireSetup();
+ }
+ }
+ });
+ }
+
+ /**
+ * Bootstraps the application.
+ *
+ * This method is called after all services are registered
+ * and should be used for "dynamic" configuration (whenever
+ * a service must be requested).
+ */
+ public function boot(Application $app)
+ {
+ // no-op
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Core/Profiler/CacheDataCollector.php b/lib/Alchemy/Phrasea/Core/Profiler/CacheDataCollector.php
new file mode 100644
index 0000000000..893c6bceb6
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Core/Profiler/CacheDataCollector.php
@@ -0,0 +1,132 @@
+statsListener = $cacheStatisticsSubscriber;
+ }
+
+ /**
+ * Returns the name of the collector.
+ *
+ * @return string The collector name
+ */
+ public function getName()
+ {
+ return 'cache';
+ }
+
+ /**
+ * Collects data for the given Request and Response.
+ *
+ * @param Request $request A Request instance
+ * @param Response $response A Response instance
+ * @param \Exception $exception An Exception instance
+ */
+ public function collect(Request $request, Response $response, \Exception $exception = null)
+ {
+ $this->startProfile = new CacheProfile($this->statsListener->getInitialStats() ?: []);
+ $this->endProfile = new CacheProfile($this->statsListener->getCurrentStats() ?: []);
+
+ $this->timeSpent = $this->statsListener->getTimeSpent();
+ $this->calls = $this->statsListener->getCalls();
+ $this->callSummary = $this->statsListener->getCallSummary();
+
+ $this->summary = new CacheProfileSummary(
+ $this->statsListener->getCacheType(),
+ $this->statsListener->getCacheNamespace(),
+ $this->startProfile,
+ $this->endProfile,
+ $this->callSummary
+ );
+ }
+
+ /**
+ * @return CacheProfile
+ */
+ public function getInitialProfile()
+ {
+ return $this->startProfile;
+ }
+
+ /**
+ * @return CacheProfile
+ */
+ public function getCurrentProfile()
+ {
+ return $this->endProfile;
+ }
+
+ /**
+ * @return int
+ */
+ public function getTotalTime()
+ {
+ return $this->timeSpent;
+ }
+
+ public function getCalls()
+ {
+ return $this->calls;
+ }
+
+ public function getCallSummary()
+ {
+ return $this->callSummary;
+ }
+
+ /**
+ * @return CacheProfileSummary
+ */
+ public function getSummary()
+ {
+ return $this->summary;
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Core/Profiler/CacheProfile.php b/lib/Alchemy/Phrasea/Core/Profiler/CacheProfile.php
new file mode 100644
index 0000000000..6544c88ba7
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Core/Profiler/CacheProfile.php
@@ -0,0 +1,74 @@
+serverStats = array_replace([
+ 'hits' => 0,
+ 'misses' => 0,
+ 'uptime' => 0,
+ 'memory_usage' => 0,
+ 'memory_available' => 0
+ ], $serverStats);
+ }
+
+ /**
+ * @return int
+ */
+ public function getHits()
+ {
+ return (int) $this->serverStats['hits'];
+ }
+
+ /**
+ * @return int
+ */
+ public function getMisses()
+ {
+ return (int) $this->serverStats['misses'];
+ }
+
+ /**
+ * @return int
+ */
+ public function getUptime()
+ {
+ return (int) $this->serverStats['uptime'];
+ }
+
+ /**
+ * @return int
+ */
+ public function getMemUsage()
+ {
+ return (int) $this->serverStats['memory_usage'];
+ }
+
+ /**
+ * @return int
+ */
+ public function getMemAvailable()
+ {
+ return (int) $this->serverStats['memory_available'];
+ }
+
+ /**
+ * @return array
+ */
+ public function getServerStats()
+ {
+ return $this->serverStats;
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Core/Profiler/CacheProfileSummary.php b/lib/Alchemy/Phrasea/Core/Profiler/CacheProfileSummary.php
new file mode 100644
index 0000000000..80ab9d864e
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Core/Profiler/CacheProfileSummary.php
@@ -0,0 +1,153 @@
+cacheType = (string)$cacheType;
+ $this->cacheNamespace = (string)$namespace;
+ $this->initialProfile = $initialProfile;
+ $this->finalProfile = $finalProfile;
+ $this->callSummaryData = $callSummaryData;
+ }
+
+ /**
+ * @return string
+ */
+ public function getCacheType()
+ {
+ return $this->cacheType;
+ }
+
+ /**
+ * @return string
+ */
+ public function getNamespace()
+ {
+ return $this->cacheNamespace;
+ }
+
+ /**
+ * @return int
+ */
+ public function getHits()
+ {
+ if (isset($this->callSummaryData['hits'])) {
+ return (int) $this->callSummaryData['hits'];
+ }
+
+ return (int)max(0, $this->finalProfile->getHits() - $this->initialProfile->getHits());
+ }
+
+ /**
+ * @return int
+ */
+ public function getMisses()
+ {
+ if (isset($this->callSummaryData['misses'])) {
+ return (int) $this->callSummaryData['misses'];
+ }
+
+ return (int)max(0, $this->finalProfile->getMisses() - $this->initialProfile->getMisses());
+ }
+
+ /**
+ * @return int
+ */
+ public function getCalls()
+ {
+ if (isset($this->callSummaryData['calls'])) {
+ return (int) $this->callSummaryData['calls'];
+ }
+
+ return $this->getHits() + $this->getMisses();
+ }
+
+ /**
+ * @return float
+ */
+ public function getHitRatio()
+ {
+ $calls = $this->getCalls();
+
+ if ($calls == 0) {
+ return (float)0;
+ }
+
+ return $this->getHits() / $calls;
+ }
+
+ /**
+ * @return float
+ */
+ public function getMissRatio()
+ {
+ $calls = $this->getCalls();
+
+ if ($calls == 0) {
+ return (float)0;
+ }
+
+ return $this->getMisses() / $calls;
+ }
+
+ /**
+ * @return int
+ */
+ public function getMemUsageDelta()
+ {
+ return $this->finalProfile->getMemUsage() - $this->initialProfile->getMemUsage();
+ }
+
+ /**
+ * @return int
+ */
+ public function getMemAvailableDelta()
+ {
+ return $this->finalProfile->getMemAvailable() - $this->initialProfile->getMemAvailable();
+ }
+
+ public function getUptimeDelta()
+ {
+ return $this->finalProfile->getUptime() - $this->initialProfile->getUptime();
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Core/Profiler/TraceableCache.php b/lib/Alchemy/Phrasea/Core/Profiler/TraceableCache.php
new file mode 100644
index 0000000000..d465220a17
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Core/Profiler/TraceableCache.php
@@ -0,0 +1,341 @@
+ 0,
+ 'hits' => 0,
+ 'misses' => 0,
+ 'calls_by_type' => [],
+ 'calls_by_key' => [],
+ ];
+
+ private $stopWatch;
+
+ /*s*
+ * @param PhraseaCache $cache
+ */
+ public function __construct(PhraseaCache $cache, Stopwatch $stopwatch = null)
+ {
+ $this->cache = $cache;
+ $this->stopWatch = $stopwatch ?: new Stopwatch();
+ }
+
+ private function collect($type, $id, $hit = true, $result = null)
+ {
+ $this->summary['calls']++;
+ $this->summary['hits'] += $hit ? 1 : 0;
+ $this->summary['misses'] += $hit ? 0 : 1;
+
+ if (! array_key_exists($type, $this->summary['calls_by_type'])) {
+ $this->summary['calls_by_type'][$type] = 0;
+ }
+
+ $this->summary['calls_by_type'][$type]++;
+
+ if (! array_key_exists($id, $this->summary['calls_by_key'])) {
+ $this->summary['calls_by_key'][$id] = [
+ 'total' => 0,
+ 'reads'=> 0,
+ 'writes' => 0,
+ 'hits' => 0,
+ 'misses' => 0
+ ];
+ }
+
+ if (! array_key_exists($type, $this->summary['calls_by_key'][$id])) {
+ $this->summary['calls_by_key'][$id][$type] = 0;
+ }
+ $this->summary['calls_by_key'][$id]['hits'] += $hit ? 1 : 0;
+ $this->summary['calls_by_key'][$id]['misses'] += $hit ? 0 : 1;
+
+ $this->summary['calls_by_key'][$id]['reads'] += ($type == 'fetch' || $type == 'contains') ? 1 : 0;
+ $this->summary['calls_by_key'][$id]['writes'] += ($type == 'fetch' || $type == 'contains') ? 0 : 1;
+
+ $this->summary['calls_by_key'][$id]['total']++;
+
+ $lastCall = end($this->calls);
+ $callData = [
+ 'type' => $type,
+ 'key' => $id,
+ 'result' => $result,
+ 'hit' => (bool) $hit,
+ 'count' => 1
+ ];
+
+ if (is_array($lastCall) && $this->compareCalls($lastCall, $callData)) {
+ $lastCall['count']++;
+ $callData = $lastCall;
+
+ array_pop($this->calls);
+ }
+
+ $this->calls[] = $callData;
+ }
+
+ private function compareCalls(array $previousCall, array $currentCall)
+ {
+ $keys = [ 'type', 'key', 'result', 'hit'];
+
+ foreach ($keys as $key) {
+ if ($previousCall[$key] != $currentCall[$key]) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * @return string
+ */
+ public function getNamespace()
+ {
+ return $this->namespace;
+ }
+
+ /**
+ * @return int
+ */
+ public function getTotalTime()
+ {
+ return $this->stopWatch->getEvent('cache')->getDuration();
+ }
+
+ /**
+ * @return array
+ */
+ public function getCalls()
+ {
+ return $this->calls;
+ }
+
+ /**
+ * @return array
+ */
+ public function getSummary()
+ {
+ return $this->summary;
+ }
+
+ /**
+ * Fetches an entry from the cache.
+ *
+ * @param string $id The id of the cache entry to fetch.
+ *
+ * @return mixed The cached data or FALSE, if no cache entry exists for the given id.
+ */
+ public function fetch($id)
+ {
+ try {
+ $this->stopWatch->start('cache');
+ $value = $this->cache->fetch($id);
+ $this->stopWatch->stop('cache');
+ }
+ catch (\Exception $ex) {
+ $value = false;
+ }
+
+ $this->collect('fetch', $id, $value != false, $value);
+
+ return $value;
+ }
+
+ /**
+ * Tests if an entry exists in the cache.
+ *
+ * @param string $id The cache id of the entry to check for.
+ *
+ * @return bool TRUE if a cache entry exists for the given cache id, FALSE otherwise.
+ */
+ public function contains($id)
+ {
+ $this->collect('contains', $id);
+
+ $this->stopWatch->start('cache');
+ $result = $this->cache->contains($id);
+ $this->stopWatch->stop('cache');
+
+ return $result;
+ }
+
+ /**
+ * Puts data into the cache.
+ *
+ * If a cache entry with the given id already exists, its data will be replaced.
+ *
+ * @param string $id The cache id.
+ * @param mixed $data The cache entry/data.
+ * @param int $lifeTime The lifetime in number of seconds for this cache entry.
+ * If zero (the default), the entry never expires (although it may be deleted from the cache
+ * to make place for other entries).
+ *
+ * @return bool TRUE if the entry was successfully stored in the cache, FALSE otherwise.
+ */
+ public function save($id, $data, $lifeTime = 0)
+ {
+ $this->collect('save', $id);
+
+ $this->stopWatch->start('cache');
+ $result = $this->cache->save($id, $data, $lifeTime);
+ $this->stopWatch->stop('cache');
+
+ return $result;
+ }
+
+ /**
+ * Deletes a cache entry.
+ *
+ * @param string $id The cache id.
+ *
+ * @return bool TRUE if the cache entry was successfully deleted, FALSE otherwise.
+ * Deleting a non-existing entry is considered successful.
+ */
+ public function delete($id)
+ {
+ $this->collect('delete', $id);
+
+ $this->stopWatch->start('cache');
+ $result = $this->cache->delete($id);
+ $this->stopWatch->stop('cache');
+
+ return $result;
+ }
+
+ /**
+ * Retrieves cached information from the data store.
+ *
+ * The server's statistics array has the following values:
+ *
+ * - hits
+ * Number of keys that have been requested and found present.
+ *
+ * - misses
+ * Number of items that have been requested and not found.
+ *
+ * - uptime
+ * Time that the server is running.
+ *
+ * - memory_usage
+ * Memory used by this server to store items.
+ *
+ * - memory_available
+ * Memory allowed to use for storage.
+ *
+ * @since 2.2
+ *
+ * @return array|null An associative array with server's statistics if available, NULL otherwise.
+ */
+ public function getStats()
+ {
+ $this->stopWatch->start('cache');
+ $result = $this->cache->getStats();
+ $this->stopWatch->stop('cache');
+
+ return $result;
+ }
+
+ /**
+ * Sets the namespace
+ *
+ * @param string $namespace
+ */
+ public function setNamespace($namespace)
+ {
+ $this->namespace = $namespace;
+ $this->cache->setNamespace($namespace);
+ }
+
+ /**
+ * Flushes all data contained in the adapter
+ */
+ public function flushAll()
+ {
+ $this->collect('flush-all', null);
+ $this->stopWatch->start('cache');
+ $this->cache->flushAll();
+ $this->stopWatch->stop('cache');
+ }
+
+ /**
+ * Name of the cache driver
+ * @return string
+ */
+ public function getName()
+ {
+ return 'traceable-' . $this->cache->getName();
+ }
+
+ /**
+ * Tell whether the caching system use a server or not
+ * @return boolean
+ */
+ public function isServer()
+ {
+ return $this->cache->isServer();
+ }
+
+ /**
+ * Tell if the cache system is online
+ * @return boolean
+ */
+ public function isOnline()
+ {
+ return $this->cache->isOnline();
+ }
+
+ /**
+ * Get an entry from the cache.
+ *
+ * @param string $key cache id The id of the cache entry to fetch.
+ *
+ * @return string The cached data.
+ * @return FALSE, if no cache entry exists for the given id.
+ *
+ * @throws Exception if provided key does not exist
+ */
+ public function get($key)
+ {
+ if ( ! $this->contains($key)) {
+ throw new Exception('Unable to retrieve the value');
+ }
+
+ return $this->fetch($key);
+ }
+
+ /**
+ * Delete multi cache entries
+ *
+ * @param array $keys contains all keys to delete
+ * @return PhraseaCache
+ */
+ public function deleteMulti(array $keys)
+ {
+ foreach ($keys as $key) {
+ $this->delete($key);
+ }
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Core/Provider/BorderManagerServiceProvider.php b/lib/Alchemy/Phrasea/Core/Provider/BorderManagerServiceProvider.php
index 496c56fde5..36f0ed0e7e 100644
--- a/lib/Alchemy/Phrasea/Core/Provider/BorderManagerServiceProvider.php
+++ b/lib/Alchemy/Phrasea/Core/Provider/BorderManagerServiceProvider.php
@@ -70,7 +70,7 @@ class BorderManagerServiceProvider implements ServiceProviderInterface
$collections = [];
foreach ($checker['collections'] as $base_id) {
try {
- $collections[] = \collection::get_from_base_id($app, $base_id);
+ $collections[] = \collection::getByBaseId($app, $base_id);
} catch (\Exception $e) {
throw new \InvalidArgumentException('Invalid collection option');
}
diff --git a/lib/Alchemy/Phrasea/Core/Provider/ConfigurationServiceProvider.php b/lib/Alchemy/Phrasea/Core/Provider/ConfigurationServiceProvider.php
index a684fa6412..55623e0081 100644
--- a/lib/Alchemy/Phrasea/Core/Provider/ConfigurationServiceProvider.php
+++ b/lib/Alchemy/Phrasea/Core/Provider/ConfigurationServiceProvider.php
@@ -69,7 +69,7 @@ class ConfigurationServiceProvider implements ServiceProviderInterface
});
$app['conf.restrictions'] = $app->share(function (SilexApplication $app) {
- return new AccessRestriction($app['cache'], $app['conf'], $app->getApplicationBox(), $app['monolog']);
+ return new AccessRestriction($app['conf'], $app->getApplicationBox(), $app['monolog']);
});
}
diff --git a/lib/Alchemy/Phrasea/Core/Provider/ConfigurationTesterServiceProvider.php b/lib/Alchemy/Phrasea/Core/Provider/ConfigurationTesterServiceProvider.php
index 57e0ee583f..188e39eda8 100644
--- a/lib/Alchemy/Phrasea/Core/Provider/ConfigurationTesterServiceProvider.php
+++ b/lib/Alchemy/Phrasea/Core/Provider/ConfigurationTesterServiceProvider.php
@@ -15,6 +15,7 @@ use Alchemy\Phrasea\Setup\ConfigurationTester;
use Alchemy\Phrasea\Application;
use Alchemy\Phrasea\Setup\Version\PreSchemaUpgrade\PreSchemaUpgradeCollection;
use Alchemy\Phrasea\Setup\Version\PreSchemaUpgrade\Upgrade39Feeds;
+use Alchemy\Phrasea\Setup\Version\PreSchemaUpgrade\Upgrade39Sessions;
use Alchemy\Phrasea\Setup\Version\PreSchemaUpgrade\Upgrade39Tokens;
use Alchemy\Phrasea\Setup\Version\PreSchemaUpgrade\Upgrade39Users;
use Silex\Application as SilexApplication;
@@ -30,7 +31,7 @@ class ConfigurationTesterServiceProvider implements ServiceProviderInterface
});
$app['phraseanet.pre-schema-upgrader.upgrades'] = $app->share(function () {
- return [new Upgrade39Feeds(), new Upgrade39Users(), new Upgrade39Tokens()];
+ return [new Upgrade39Feeds(), new Upgrade39Users(), new Upgrade39Tokens(), new Upgrade39Sessions()];
});
$app['phraseanet.pre-schema-upgrader'] = $app->share(function (Application $app) {
diff --git a/lib/Alchemy/Phrasea/Core/Provider/FileServeServiceProvider.php b/lib/Alchemy/Phrasea/Core/Provider/FileServeServiceProvider.php
index 3333b45090..ab651780bd 100644
--- a/lib/Alchemy/Phrasea/Core/Provider/FileServeServiceProvider.php
+++ b/lib/Alchemy/Phrasea/Core/Provider/FileServeServiceProvider.php
@@ -14,12 +14,11 @@ namespace Alchemy\Phrasea\Core\Provider;
use Alchemy\Phrasea\Core\Event\Subscriber\XSendFileSubscriber;
use Alchemy\Phrasea\Http\H264PseudoStreaming\H264Factory;
use Alchemy\Phrasea\Http\ServeFileResponseFactory;
-use Alchemy\Phrasea\Http\StaticFile\StaticFileFactory;
use Alchemy\Phrasea\Http\StaticFile\StaticMode;
-use Alchemy\Phrasea\Http\StaticFile\Symlink\SymLinker;
use Alchemy\Phrasea\Http\XSendFile\XSendFileFactory;
use Silex\Application;
use Silex\ServiceProviderInterface;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
class FileServeServiceProvider implements ServiceProviderInterface
{
@@ -41,11 +40,11 @@ class FileServeServiceProvider implements ServiceProviderInterface
});
$app['phraseanet.static-file'] = $app->share(function (Application $app) {
- return new StaticMode(SymLinker::create($app));
+ return new StaticMode($app['phraseanet.thumb-symlinker']);
});
$app['phraseanet.file-serve'] = $app->share(function (Application $app) {
- return ServeFileResponseFactory::create($app);
+ return new ServeFileResponseFactory($app['unicode']);
});
}
@@ -55,7 +54,7 @@ class FileServeServiceProvider implements ServiceProviderInterface
public function boot(Application $app)
{
$app['dispatcher'] = $app->share(
- $app->extend('dispatcher', function ($dispatcher, Application $app) {
+ $app->extend('dispatcher', function (EventDispatcherInterface $dispatcher, Application $app) {
$dispatcher->addSubscriber(new XSendFileSubscriber($app));
return $dispatcher;
diff --git a/lib/Alchemy/Phrasea/Core/Provider/MediaAlchemystServiceProvider.php b/lib/Alchemy/Phrasea/Core/Provider/MediaAlchemystServiceProvider.php
new file mode 100644
index 0000000000..dd4e970b6f
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Core/Provider/MediaAlchemystServiceProvider.php
@@ -0,0 +1,53 @@
+share(function (Application $app) {
+ $configuration = [];
+ $parameters = [
+ 'swftools.pdf2swf.binaries' => 'pdf2swf_binary',
+ 'swftools.swfrender.binaries' => 'swf_render_binary',
+ 'swftools.swfextract.binaries' => 'swf_extract_binary',
+ 'unoconv.binaries' => 'unoconv_binary',
+ 'mp4box.binaries' => 'mp4box_binary',
+ 'gs.binaries' => 'ghostscript_binary',
+ 'ffmpeg.ffmpeg.binaries' => 'ffmpeg_binary',
+ 'ffmpeg.ffprobe.binaries' => 'ffprobe_binary',
+ 'ffmpeg.ffmpeg.timeout' => 'ffmpeg_timeout',
+ 'ffmpeg.ffprobe.timeout' => 'ffprobe_timeout',
+ 'gs.timeout' => 'gs_timeout',
+ 'mp4box.timeout' => 'mp4box_timeout',
+ 'swftools.timeout' => 'swftools_timeout',
+ 'unoconv.timeout' => 'unoconv_timeout',
+ ];
+
+ foreach ($parameters as $parameter => $key) {
+ if ($app['conf']->has(['main', 'binaries', $key])) {
+ $configuration[$parameter] = $app['conf']->get(['main', 'binaries', $key]);
+ }
+ }
+
+ $configuration['ffmpeg.threads'] = $app['conf']->get(['registry', 'executables', 'ffmpeg-threads']) ?: null;
+ $configuration['imagine.driver'] = $app['conf']->get(['registry', 'executables', 'imagine-driver']) ?: null;
+
+ return $configuration;
+ });
+
+ $app['media-alchemyst.logger'] = $app->share(function (Application $app) {
+ return $app['monolog'];
+ });
+ }
+
+ public function boot(Application $app)
+ {
+ // no-op
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Core/Provider/PhraseanetServiceProvider.php b/lib/Alchemy/Phrasea/Core/Provider/PhraseanetServiceProvider.php
index 988c1a850d..e0f477f17a 100644
--- a/lib/Alchemy/Phrasea/Core/Provider/PhraseanetServiceProvider.php
+++ b/lib/Alchemy/Phrasea/Core/Provider/PhraseanetServiceProvider.php
@@ -41,11 +41,15 @@ class PhraseanetServiceProvider implements ServiceProviderInterface
});
$app['phraseanet.thumb-symlinker'] = $app->share(function (SilexApplication $app) {
- return SymLinker::create($app);
+ return new SymLinker(
+ $app['phraseanet.thumb-symlinker-encoder'],
+ $app['filesystem'],
+ $app['thumbnail.path']
+ );
});
$app['phraseanet.thumb-symlinker-encoder'] = $app->share(function (SilexApplication $app) {
- return SymLinkerEncoder::create($app);
+ return new SymLinkerEncoder($app['phraseanet.configuration']['main']['key']);
});
$app['acl'] = $app->share(function (SilexApplication $app) {
diff --git a/lib/Alchemy/Phrasea/Core/Provider/RepositoriesServiceProvider.php b/lib/Alchemy/Phrasea/Core/Provider/RepositoriesServiceProvider.php
index fe40727b0c..5e50367f09 100644
--- a/lib/Alchemy/Phrasea/Core/Provider/RepositoriesServiceProvider.php
+++ b/lib/Alchemy/Phrasea/Core/Provider/RepositoriesServiceProvider.php
@@ -1,9 +1,8 @@
getRepository('Phraseanet:Registration');
});
$app['repo.baskets'] = $app->share(function (PhraseaApplication $app) {
- return $app['orm.em']->getRepository('Phraseanet:Basket');
+ /** @var BasketRepository $repository */
+ $repository = $app['orm.em']->getRepository('Phraseanet:Basket');
+ $repository->setTranslator($app['translator']);
+
+ return $repository;
});
$app['repo.basket-elements'] = $app->share(function (PhraseaApplication $app) {
return $app['orm.em']->getRepository('Phraseanet:BasketElement');
@@ -133,11 +146,21 @@ class RepositoriesServiceProvider implements ServiceProviderInterface
});
$app['repo.databoxes'] = $app->share(function (PhraseaApplication $app) {
- $factory = new DataboxFactory($app);
$appbox = $app->getApplicationBox();
- $repository = new DbalDataboxRepository($appbox->get_connection(), $factory);
- return new CachingDataboxRepositoryDecorator($repository, $app['cache'], $appbox->get_cache_key($appbox::CACHE_LIST_BASES), $factory);
+ $factory = new DataboxFactory($app);
+ $repository = new CachingDataboxRepositoryDecorator(
+ new DbalDataboxRepository($appbox->get_connection(), $factory),
+ $app['cache'],
+ $appbox->get_cache_key($appbox::CACHE_LIST_BASES),
+ $factory
+ );
+
+ $repository = new ArrayCacheDataboxRepository($repository);
+
+ $factory->setDataboxRepository($repository);
+
+ return $repository;
});
$app['repo.fields.factory'] = $app->protect(function (\databox $databox) use ($app) {
@@ -147,9 +170,37 @@ class RepositoriesServiceProvider implements ServiceProviderInterface
$app['repo.records.factory'] = $app->protect(function (\databox $databox) use ($app) {
return new LegacyRecordRepository($app, $databox);
});
+
+ $app['repo.collection-references'] = $app->share(function (PhraseaApplication $app) {
+ $repository = new DbalCollectionReferenceRepository($app->getApplicationBox()->get_connection());
+
+ return new ArrayCacheCollectionReferenceRepository($repository);
+ });
+ $app['repo.collections-registry'] = $app->share(function (PhraseaApplication $app) {
+ $factory = new CollectionFactory($app);
+ $connectionProvider = new DataboxConnectionProvider($app->getApplicationBox());
+
+ $repositoryFactory = new DbalCollectionRepositoryFactory(
+ $connectionProvider,
+ $factory,
+ $app['repo.collection-references']
+ );
+
+ $repositoryFactory = new CachedCollectionRepositoryFactory(
+ $app,
+ $repositoryFactory,
+ $app['cache'],
+ 'phrasea.collections'
+ );
+
+ $repositoryFactory = new ArrayCachedCollectionRepositoryFactory($repositoryFactory);
+
+ return new CollectionRepositoryRegistry($app, $repositoryFactory, $app['repo.collection-references']);
+ });
}
public function boot(Application $app)
{
+ // no-op
}
}
diff --git a/lib/Alchemy/Phrasea/Core/Provider/SubdefServiceProvider.php b/lib/Alchemy/Phrasea/Core/Provider/SubdefServiceProvider.php
index 6210c72e5e..1ef91b9bba 100644
--- a/lib/Alchemy/Phrasea/Core/Provider/SubdefServiceProvider.php
+++ b/lib/Alchemy/Phrasea/Core/Provider/SubdefServiceProvider.php
@@ -21,11 +21,14 @@ class SubdefServiceProvider implements ServiceProviderInterface
{
public function register(SilexApplication $app)
{
- $app['subdef.generator'] = $app->share(function (SilexApplication $app) {
- return new SubdefGenerator($app, $app['media-alchemyst'], $app['filesystem'], $app['mediavorus'], isset($app['task-manager.logger']) ? $app['task-manager.logger'] : $app['monolog']);
+ $app['subdef.generator'] = $app->share(function (Application $app) {
+ $generator = new SubdefGenerator($app, $app['media-alchemyst'], $app['phraseanet.filesystem'], $app['mediavorus'], isset($app['task-manager.logger']) ? $app['task-manager.logger'] : $app['monolog']);
+ $generator->setDispatcher($app['dispatcher']);
+
+ return $generator;
});
- $app['subdef.substituer'] = $app->share(function (SilexApplication $app) {
- return new SubdefSubstituer($app, $app['filesystem'], $app['media-alchemyst'], $app['mediavorus'], $app['dispatcher']);
+ $app['subdef.substituer'] = $app->share(function (Application $app) {
+ return new SubdefSubstituer($app, $app['phraseanet.filesystem'], $app['media-alchemyst'], $app['mediavorus'], $app['dispatcher']);
});
}
diff --git a/lib/Alchemy/Phrasea/Core/Provider/TranslationServiceProvider.php b/lib/Alchemy/Phrasea/Core/Provider/TranslationServiceProvider.php
index c50cd00bf6..f87c421728 100644
--- a/lib/Alchemy/Phrasea/Core/Provider/TranslationServiceProvider.php
+++ b/lib/Alchemy/Phrasea/Core/Provider/TranslationServiceProvider.php
@@ -5,6 +5,7 @@ namespace Alchemy\Phrasea\Core\Provider;
use Alchemy\Phrasea\Utilities\CachedTranslator;
use Silex\Application;
use Silex\ServiceProviderInterface;
+use Symfony\Component\Translation\Loader\PoFileLoader;
use Symfony\Component\Translation\MessageSelector;
use Symfony\Component\Translation\Loader\ArrayLoader;
use JMS\TranslationBundle\Translation\Loader\Symfony\XliffLoader;
@@ -36,6 +37,7 @@ class TranslationServiceProvider implements ServiceProviderInterface
$translator->addLoader('xliff', new XliffLoader());
// to load Phraseanet resources
$translator->addLoader('xlf', new XliffLoader());
+ $translator->addLoader('po', new PoFileLoader());
foreach ($app['translator.domains'] as $domain => $data) {
foreach ($data as $locale => $messages) {
@@ -43,6 +45,15 @@ class TranslationServiceProvider implements ServiceProviderInterface
}
}
+ foreach ($app['translator.resources'] as $resource) {
+ $translator->addResource(
+ $resource[0],
+ $resource[1],
+ $resource[2],
+ isset($resource[3]) ? $resource[3] : null
+ );
+ }
+
return $translator;
});
diff --git a/lib/Alchemy/Phrasea/Core/Provider/TwigServiceProvider.php b/lib/Alchemy/Phrasea/Core/Provider/TwigServiceProvider.php
new file mode 100644
index 0000000000..6fa067a84c
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Core/Provider/TwigServiceProvider.php
@@ -0,0 +1,159 @@
+share($app->extend('twig', function (\Twig_Environment $twig, $app) {
+ $twig->setCache($app['cache.path'] . '/twig');
+
+ $paths = [];
+
+ if (file_exists($app['plugin.path'] . '/twig-paths.php')) {
+ $paths = require $app['plugin.path'] . '/twig-paths.php';
+ }
+
+ if ($app['browser']->isTablet() || $app['browser']->isMobile()) {
+ $paths[] = $app['root.path'] . '/config/templates/mobile';
+ $paths[] = $app['root.path'] . '/templates/mobile';
+ $paths['phraseanet'] = $app['root.path'] . '/config/templates/mobile';
+ $paths['phraseanet'] = $app['root.path'] . '/templates/mobile';
+ }
+
+ $paths[] = $app['root.path'] . '/config/templates/web';
+ $paths[] = $app['root.path'] . '/templates/web';
+ $paths['phraseanet'] = $app['root.path'] . '/config/templates/web';
+ $paths['phraseanet'] = $app['root.path'] . '/templates/web';
+
+ foreach ($paths as $namespace => $path) {
+ if (!is_int($namespace)) {
+ $app['twig.loader.filesystem']->addPath($path, $namespace);
+ } else {
+ $app['twig.loader.filesystem']->addPath($path);
+ }
+ }
+
+ $twig->addGlobal('current_date', new \DateTime());
+
+ $this->registerExtensions($twig, $app);
+ $this->registerFilters($twig, $app);
+
+ return $twig;
+ }));
+ }
+
+ private function registerExtensions(\Twig_Environment $twig, Application $app)
+ {
+ $twig->addExtension(new \Twig_Extension_Core());
+ $twig->addExtension(new \Twig_Extension_Optimizer());
+ $twig->addExtension(new \Twig_Extension_Escaper());
+ if ($app['debug']) {
+ $twig->addExtension(new \Twig_Extension_Debug());
+ }
+
+ // add filter trans
+ $twig->addExtension(new TranslationExtension($app['translator']));
+ // add filter localizeddate
+ $twig->addExtension(new \Twig_Extensions_Extension_Intl());
+ // add filters truncate, wordwrap, nl2br
+ $twig->addExtension(new \Twig_Extensions_Extension_Text());
+ $twig->addExtension(new JSUniqueID());
+ $twig->addExtension(new Fit());
+ $twig->addExtension(new Camelize());
+ $twig->addExtension(new BytesConverter());
+ $twig->addExtension(new PhraseanetExtension($app));
+ }
+
+ private function registerFilters(\Twig_Environment $twig, Application $app)
+ {
+ $twig->addFilter('serialize', new \Twig_Filter_Function('serialize'));
+ $twig->addFilter('stristr', new \Twig_Filter_Function('stristr'));
+ $twig->addFilter('get_class', new \Twig_Filter_Function('get_class'));
+ $twig->addFilter('stripdoublequotes', new \Twig_Filter_Function('stripdoublequotes'));
+ $twig->addFilter('get_collection_logo', new \Twig_Filter_Function('collection::getLogo'));
+ $twig->addFilter('floor', new \Twig_Filter_Function('floor'));
+ $twig->addFilter('ceil', new \Twig_Filter_Function('ceil'));
+ $twig->addFilter('max', new \Twig_Filter_Function('max'));
+ $twig->addFilter('min', new \Twig_Filter_Function('min'));
+ $twig->addFilter('bas_labels', new \Twig_Filter_Function('phrasea::bas_labels'));
+ $twig->addFilter('sbas_names', new \Twig_Filter_Function('phrasea::sbas_names'));
+ $twig->addFilter('sbas_labels', new \Twig_Filter_Function('phrasea::sbas_labels'));
+ $twig->addFilter('sbas_from_bas', new \Twig_Filter_Function('phrasea::sbasFromBas'));
+ $twig->addFilter('key_exists', new \Twig_Filter_Function('array_key_exists'));
+ $twig->addFilter('round', new \Twig_Filter_Function('round'));
+ $twig->addFilter('count', new \Twig_Filter_Function('count'));
+ $twig->addFilter('formatOctets', new \Twig_Filter_Function('p4string::format_octets'));
+ $twig->addFilter('base_from_coll', new \Twig_Filter_Function('phrasea::baseFromColl'));
+ $twig->addFilter(new \Twig_SimpleFilter('escapeSimpleQuote', function ($value) {
+ return str_replace("'", "\\'", $value);
+ }));
+
+ $twig->addFilter(new \Twig_SimpleFilter('highlight', function (\Twig_Environment $twig, $string) {
+ return str_replace(['[[em]]', '[[/em]]'], ['', ''], $string);
+ }, ['needs_environment' => true, 'is_safe' => ['html']]));
+
+ $twig->addFilter(new \Twig_SimpleFilter('linkify', function (\Twig_Environment $twig, $string) use ($app) {
+ return preg_replace(
+ "(([^']{1})((https?|file):((/{2,4})|(\\{2,4}))[\w:#%/;$()~_?/\-=\\\.&]*)([^']{1}))"
+ ,
+ '$1 $2 $7'
+ , $string
+ );
+ }, ['needs_environment' => true, 'is_safe' => ['html']]));
+
+ $twig->addFilter(new \Twig_SimpleFilter('bounce',
+ function (\Twig_Environment $twig, $fieldValue, $fieldName, $searchRequest, $sbasId) {
+ // bounce value if it is present in thesaurus as well
+ return ""
+ . $fieldValue
+ . "";
+
+ }, ['needs_environment' => true, 'is_safe' => ['html']]));
+
+ $twig->addFilter(new \Twig_SimpleFilter('escapeDoubleQuote', function ($value) {
+ return str_replace('"', '\"', $value);
+ }));
+ }
+
+ /**
+ * Bootstraps the application.
+ *
+ * This method is called after all services are registered
+ * and should be used for "dynamic" configuration (whenever
+ * a service must be requested).
+ */
+ public function boot(Application $app)
+ {
+ // no-op
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Core/Provider/WebProfilerServiceProvider.php b/lib/Alchemy/Phrasea/Core/Provider/WebProfilerServiceProvider.php
new file mode 100644
index 0000000000..d6fd0d25f8
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Core/Provider/WebProfilerServiceProvider.php
@@ -0,0 +1,133 @@
+share(
+ $app->extend('web_profiler.toolbar.listener', function () use ($app) {
+ return new WebDebugToolbarListener(
+ $app['twig'],
+ $app['web_profiler.debug_toolbar.intercept_redirects'],
+ 2,
+ $app['web_profiler.debug_toolbar.position'],
+ $app['url_generator']
+ );
+ })
+ );
+
+ if (class_exists(AjaxDataCollector::class)) {
+ $app['data_collector.templates'] = $app->share($app->extend('data_collector.templates', function (array $templates) {
+ $templates[] = array('ajax', '@WebProfiler/Collector/ajax.html.twig');
+
+ return $templates;
+ }));
+
+ $app['data_collectors'] = $app->share($app->extend('data_collectors', function ($collectors) use ($app) {
+ $collectors['ajax'] = function () {
+ return new AjaxDataCollector();
+ };
+
+ return $collectors;
+ }));
+ }
+
+ $app['data_collectors.doctrine'] = $app->share(function ($app) {
+ /** @var Connection $db */
+ $db = $app['db'];
+ return new DoctrineDataCollector($db);
+ });
+
+ $app['cache'] = $app->share($app->extend('cache', function (Cache $cache, $app) {
+ $namespace = $app['conf']->get(['main', 'cache', 'options', 'namespace']);
+ $cache = new TraceableCache($cache);
+
+ $cache->setNamespace($namespace);
+
+ return $cache;
+ }));
+
+ $app['data_collector.cache_subscriber'] = $app->share(function ($app) {
+ return new CacheStatisticsSubscriber($app['cache']);
+ });
+
+ $app['data_collectors'] = $app->share($app->extend('data_collectors', function ($collectors) use ($app) {
+ $collectors['db'] = $app->share(function ($app) {
+ /** @var DoctrineDataCollector $collector */
+ $collector = $app['data_collectors.doctrine'];
+
+ $loggerChain = new LoggerChain();
+ $logger = new DebugStack();
+
+ $loggerChain->addLogger($logger);
+ $loggerChain->addLogger(new DbalLogger($app['logger'], $app['stopwatch']));
+
+ /** @var Connection $db */
+ $db = $app['db'];
+ $db->getConfiguration()->setSQLLogger($loggerChain);
+
+ $collector->addLogger($logger);
+
+ return $collector;
+ });
+
+ $collectors['cache'] = $app->share(function ($app) {
+ return new CacheDataCollector($app['data_collector.cache_subscriber']);
+ });
+
+ return $collectors;
+ }));
+
+ $app['data_collector.templates'] = $app->share($app->extend('data_collector.templates', function (array $templates) {
+ $templates[] = array('db', '@DoctrineBundle/Collector/db.html.twig');
+ $templates[] = array('cache', '@PhraseaProfiler/cache.html.twig');
+
+ return $templates;
+ }));
+
+ $app['twig.loader.filesystem'] = $app->share($app->extend('twig.loader.filesystem', function ($loader, $app) {
+ $loader->addPath(
+ $app['root.path'] . '/vendor/sorien/silex-dbal-profiler/src/Sorien/Resources/views',
+ 'DoctrineBundle'
+ );
+ $loader->addPath($app['root.path'] . '/templates-profiler/', 'PhraseaProfiler');
+ return $loader;
+ }));
+ }
+
+ /**
+ * Bootstraps the application.
+ *
+ * This method is called after all services are registered
+ * and should be used for "dynamic" configuration (whenever
+ * a service must be requested).
+ */
+ public function boot(Application $app)
+ {
+ $app['dispatcher']->addSubscriber($app['data_collector.cache_subscriber']);
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Core/Provider/WebhookServiceProvider.php b/lib/Alchemy/Phrasea/Core/Provider/WebhookServiceProvider.php
new file mode 100644
index 0000000000..e224d8a43d
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Core/Provider/WebhookServiceProvider.php
@@ -0,0 +1,23 @@
+share(function ($app) {
+ return new EventProcessorFactory($app);
+ });
+ }
+
+ public function boot(Application $app)
+ {
+ // no-op
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Databox/ArrayCacheDataboxRepository.php b/lib/Alchemy/Phrasea/Databox/ArrayCacheDataboxRepository.php
new file mode 100644
index 0000000000..7660771460
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Databox/ArrayCacheDataboxRepository.php
@@ -0,0 +1,136 @@
+repository = $repository;
+ }
+
+ /**
+ * @param int $id
+ * @return \databox
+ */
+ public function find($id)
+ {
+ $this->load();
+
+ if (! isset($this->databoxes[$id])) {
+ return null;
+ }
+
+ return $this->databoxes[$id];
+ }
+
+ /**
+ * @return \databox[]
+ */
+ public function findAll()
+ {
+ $this->load();
+
+ return $this->databoxes;
+ }
+
+ /**
+ * @param \databox $databox
+ */
+ public function save(\databox $databox)
+ {
+ $this->clear();
+
+ return $this->repository->save($databox);
+ }
+
+ /**
+ * @param \databox $databox
+ */
+ public function delete(\databox $databox)
+ {
+ $this->clear();
+
+ return $this->repository->delete($databox);
+ }
+
+ /**
+ * @param \databox $databox
+ */
+ public function unmount(\databox $databox)
+ {
+ $this->clear();
+
+ return $this->repository->unmount($databox);
+ }
+
+ /**
+ * @param $host
+ * @param $port
+ * @param $user
+ * @param $password
+ * @param $dbname
+ *
+ * @return \databox
+ */
+ public function mount($host, $port, $user, $password, $dbname)
+ {
+ $this->clear();
+
+ return $this->repository->mount($host, $port, $user, $password, $dbname);
+ }
+
+ /**
+ * @param $host
+ * @param $port
+ * @param $user
+ * @param $password
+ * @param $dbname
+ *
+ * @return \databox
+ */
+ public function create($host, $port, $user, $password, $dbname)
+ {
+ $this->clear();
+
+ return $this->repository->create($host, $port, $user, $password, $dbname);
+ }
+
+ /**
+ * Initializes the memory cache if needed.
+ */
+ private function load()
+ {
+ if (! $this->loaded) {
+ $this->databoxes = $this->repository->findAll();
+ $this->loaded = true;
+ }
+ }
+
+ /**
+ * Clears the memory cache.
+ */
+ private function clear()
+ {
+ $this->loaded = false;
+ $this->databoxes = [];
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Databox/CachingDataboxRepositoryDecorator.php b/lib/Alchemy/Phrasea/Databox/CachingDataboxRepositoryDecorator.php
index 6f94cedb22..14dcf824e5 100644
--- a/lib/Alchemy/Phrasea/Databox/CachingDataboxRepositoryDecorator.php
+++ b/lib/Alchemy/Phrasea/Databox/CachingDataboxRepositoryDecorator.php
@@ -22,6 +22,12 @@ final class CachingDataboxRepositoryDecorator implements DataboxRepository
/** @var DataboxFactory */
private $factory;
+ /**
+ * @param DataboxRepository $repository
+ * @param Cache $cache
+ * @param string $cacheKey
+ * @param DataboxFactory $factory
+ */
public function __construct(DataboxRepository $repository, Cache $cache, $cacheKey, DataboxFactory $factory)
{
$this->repository = $repository;
@@ -56,6 +62,63 @@ final class CachingDataboxRepositoryDecorator implements DataboxRepository
return $databoxes;
}
+ public function save(\databox $databox)
+ {
+ $this->clearCache();
+
+ return $this->repository->save($databox);
+ }
+
+ public function delete(\databox $databox)
+ {
+ $this->clearCache();
+
+ return $this->repository->delete($databox);
+ }
+
+ public function unmount(\databox $databox)
+ {
+ $this->clearCache();
+
+ return $this->repository->unmount($databox);
+ }
+
+ /**
+ * @param $host
+ * @param $port
+ * @param $user
+ * @param $password
+ * @param $dbname
+ *
+ * @return \databox
+ */
+ public function mount($host, $port, $user, $password, $dbname)
+ {
+ $databox = $this->repository->mount($host, $port, $user, $password, $dbname);
+
+ $this->clearCache();
+
+ return $databox;
+ }
+
+ /**
+ * @param $host
+ * @param $port
+ * @param $user
+ * @param $password
+ * @param $dbname
+ *
+ * @return \databox
+ */
+ public function create($host, $port, $user, $password, $dbname)
+ {
+ $databox = $this->repository->create($host, $port, $user, $password, $dbname);
+
+ $this->clearCache();
+
+ return $databox;
+ }
+
/**
* @param \databox[] $databoxes
*/
@@ -69,4 +132,9 @@ final class CachingDataboxRepositoryDecorator implements DataboxRepository
$this->cache->save($this->cacheKey, $rows);
}
+
+ private function clearCache()
+ {
+ $this->cache->delete($this->cacheKey);
+ }
}
diff --git a/lib/Alchemy/Phrasea/Databox/DataboxConnectionProvider.php b/lib/Alchemy/Phrasea/Databox/DataboxConnectionProvider.php
new file mode 100644
index 0000000000..a1601dd00d
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Databox/DataboxConnectionProvider.php
@@ -0,0 +1,23 @@
+applicationBox = $applicationBox;
+ }
+
+ /**
+ * @param $databoxId
+ * @return \Doctrine\DBAL\Connection
+ */
+ public function getConnection($databoxId)
+ {
+ return $this->applicationBox->get_databox($databoxId)->get_connection();
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Databox/DataboxFactory.php b/lib/Alchemy/Phrasea/Databox/DataboxFactory.php
index 15b38281c6..522dd989f7 100644
--- a/lib/Alchemy/Phrasea/Databox/DataboxFactory.php
+++ b/lib/Alchemy/Phrasea/Databox/DataboxFactory.php
@@ -17,20 +17,33 @@ class DataboxFactory
/** @var Application */
private $app;
+ /** @var DataboxRepository */
+ private $databoxRepository;
+
+ /**
+ * @param Application $app
+ */
public function __construct(Application $app)
{
$this->app = $app;
}
/**
- * @param int $id
+ * @param DataboxRepository $databoxRepository
+ */
+ public function setDataboxRepository(DataboxRepository $databoxRepository)
+ {
+ $this->databoxRepository = $databoxRepository;
+ }
+
+ /**
+ * @param int $id
* @param array $raw
- * @throws NotFoundHttpException when Databox could not be retrieved from Persistence layer
- * @return \databox
+ * @return \databox when Databox could not be retrieved from Persistence layer
*/
public function create($id, array $raw)
{
- return new \databox($this->app, $id, $raw);
+ return new \databox($this->app, $id, $this->databoxRepository, $raw);
}
/**
@@ -43,7 +56,7 @@ class DataboxFactory
$databoxes = [];
foreach ($rows as $id => $raw) {
- $databoxes[$id] = new \databox($this->app, $id, $raw);
+ $databoxes[$id] = new \databox($this->app, $id, $this->databoxRepository, $raw);
}
return $databoxes;
diff --git a/lib/Alchemy/Phrasea/Databox/DataboxPathExtractor.php b/lib/Alchemy/Phrasea/Databox/DataboxPathExtractor.php
new file mode 100644
index 0000000000..f3e4a82e95
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Databox/DataboxPathExtractor.php
@@ -0,0 +1,37 @@
+appbox = $appbox;
+ }
+
+ public function extractPaths()
+ {
+ $paths = [];
+
+ foreach ($this->appbox->get_databoxes() as $databox) {
+ foreach ($databox->get_subdef_structure()->getSubdefGroup('video') as $subdef) {
+ $paths[] = $subdef->get_path();
+ }
+ }
+
+ return array_filter(array_unique($paths));
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Databox/DataboxRepository.php b/lib/Alchemy/Phrasea/Databox/DataboxRepository.php
index 73a4ccbb5d..a8d84f5fc9 100644
--- a/lib/Alchemy/Phrasea/Databox/DataboxRepository.php
+++ b/lib/Alchemy/Phrasea/Databox/DataboxRepository.php
@@ -21,4 +21,41 @@ interface DataboxRepository
* @return \databox[]
*/
public function findAll();
+
+ /**
+ * @param \databox $databox
+ */
+ public function save(\databox $databox);
+
+ /**
+ * @param \databox $databox
+ */
+ public function delete(\databox $databox);
+
+ /**
+ * @param \databox $databox
+ */
+ public function unmount(\databox $databox);
+
+ /**
+ * @param $host
+ * @param $port
+ * @param $user
+ * @param $password
+ * @param $dbname
+ *
+ * @return \databox
+ */
+ public function mount($host, $port, $user, $password, $dbname);
+
+ /**
+ * @param $host
+ * @param $port
+ * @param $user
+ * @param $password
+ * @param $dbname
+ *
+ * @return \databox
+ */
+ public function create($host, $port, $user, $password, $dbname);
}
diff --git a/lib/Alchemy/Phrasea/Databox/DbalDataboxRepository.php b/lib/Alchemy/Phrasea/Databox/DbalDataboxRepository.php
index 45efe87112..5ce27cb846 100644
--- a/lib/Alchemy/Phrasea/Databox/DbalDataboxRepository.php
+++ b/lib/Alchemy/Phrasea/Databox/DbalDataboxRepository.php
@@ -47,6 +47,25 @@ final class DbalDataboxRepository implements DataboxRepository
return $this->factory->createMany($this->fetchRows());
}
+ /**
+ * @param \databox $databox
+ * @return bool
+ */
+ public function save(\databox $databox)
+ {
+ return true;
+ }
+
+ public function delete(\databox $databox)
+ {
+ return true;
+ }
+
+ public function unmount(\databox $databox)
+ {
+ return true;
+ }
+
/**
* @param int $id
* @return false|array
@@ -82,4 +101,73 @@ final class DbalDataboxRepository implements DataboxRepository
return $rows;
}
+
+ /**
+ * @param $host
+ * @param $port
+ * @param $user
+ * @param $password
+ * @param $dbname
+ *
+ * @return \databox
+ */
+ public function mount($host, $port, $user, $password, $dbname)
+ {
+ $query = 'INSERT INTO sbas (ord, host, port, dbname, sqlengine, user, pwd)
+ SELECT COALESCE(MAX(ord), 0) + 1 AS ord, :host AS host, :port AS port, :dbname AS dbname,
+ "MYSQL" AS sqlengine, :user AS user, :password AS pwd FROM sbas';
+
+ $statement = $this->connection->prepare($query);
+ $statement->execute([
+ ':host' => $host,
+ ':port' => $port,
+ ':dbname' => $dbname,
+ ':user' => $user,
+ ':password' => $password
+ ]);
+
+ $statement->closeCursor();
+
+ return $this->find((int) $this->connection->lastInsertId());
+ }
+
+ /**
+ * @param $host
+ * @param $port
+ * @param $user
+ * @param $password
+ * @param $dbname
+ *
+ * @return \databox
+ */
+ public function create($host, $port, $user, $password, $dbname)
+ {
+ $params = [
+ ':host' => $host,
+ ':port' => $port,
+ ':user' => $user,
+ ':password' => $password,
+ ':dbname' => $dbname
+ ];
+
+ $query = 'SELECT sbas_id FROM sbas
+ WHERE host = :host AND port = :port AND `user` = :user AND pwd = :password AND dbname = :dbname';
+ $statement = $this->connection->executeQuery($query, $params);
+
+ if ($row = $statement->fetch(\PDO::FETCH_ASSOC)) {
+ return $this->find((int) $row['sbas_id']);
+ }
+
+ $query = 'INSERT INTO sbas (ord, host, port, dbname, sqlengine, user, pwd)
+ SELECT COALESCE(MAX(ord), 0) + 1 AS ord, :host AS host, :port AS port, :dbname AS dbname,
+ "MYSQL" AS sqlengine, :user AS user, :password AS pwd FROM sbas';
+
+ $stmt = $this->connection->prepare($query);
+ $stmt->execute($params);
+
+ $stmt->closeCursor();
+
+ return $this->find((int) $this->connection->lastInsertId());
+
+ }
}
diff --git a/lib/Alchemy/Phrasea/Filesystem/ApplicationPathServiceGenerator.php b/lib/Alchemy/Phrasea/Filesystem/ApplicationPathServiceGenerator.php
new file mode 100644
index 0000000000..7609a36187
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Filesystem/ApplicationPathServiceGenerator.php
@@ -0,0 +1,38 @@
+isSetup())
+ ? $app['conf']->get($key)
+ : null;
+
+ if (null === $path) {
+ $path = $default($app);
+ }
+
+ // ensure path is created
+ $app['filesystem']->mkdir($path);
+ }
+
+ return $path;
+ };
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Filesystem/FilesystemService.php b/lib/Alchemy/Phrasea/Filesystem/FilesystemService.php
new file mode 100644
index 0000000000..b6b34894ce
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Filesystem/FilesystemService.php
@@ -0,0 +1,208 @@
+filesystem = $filesystem;
+ }
+
+ /**
+ * @param string $repository_path
+ * @return string
+ */
+ public function directorySpread($repository_path)
+ {
+ $repository_path = \p4string::addEndSlash($repository_path);
+
+ $timestamp = strtotime(date('Y-m-d'));
+ $year = date('Y', $timestamp);
+ $month = date('m', $timestamp);
+ $day = date('d', $timestamp);
+
+ $comp = $year . DIRECTORY_SEPARATOR . $month . DIRECTORY_SEPARATOR . $day . DIRECTORY_SEPARATOR;
+
+ $n = 0;
+ do {
+ $pathout = sprintf('%s%s%05d', $repository_path, $comp, $n++);
+ } while (is_dir($pathout) && iterator_count(new \DirectoryIterator($pathout)) > 100);
+
+ $this->filesystem->mkdir($pathout, 0750);
+
+ return $pathout . DIRECTORY_SEPARATOR;
+ }
+
+ public function exists($path)
+ {
+ return $this->filesystem->exists($path);
+ }
+
+ public function generateSubdefPathname(\record_adapter $record, \databox_subdef $subdef, $oldVersion)
+ {
+ if ($oldVersion) {
+ $pathdest = \p4string::addEndSlash(pathinfo($oldVersion, PATHINFO_DIRNAME));
+ } else {
+ $pathdest = $this->directorySpread($subdef->get_path());
+ }
+
+ return $pathdest . $this->generateSubdefFilename($record, $subdef);
+ }
+
+ /**
+ * @param \record_adapter $record
+ * @param string|\SplFileInfo $source
+ * @return string
+ */
+ public function generateDocumentFilename(\record_adapter $record, $source)
+ {
+ if (!$source instanceof \SplFileInfo) {
+ $source = new \SplFileInfo($source);
+ }
+
+ return $record->getRecordId() . '_document' . strtolower($source->getExtension());
+ }
+
+ /**
+ * @param \record_adapter $record
+ * @param \databox_subdef $subdef
+ * @param string $marker
+ * @return string
+ */
+ public function generateSubdefFilename(\record_adapter $record, \databox_subdef $subdef, $marker = '')
+ {
+ return $record->getRecordId() . '_' . $marker . $subdef->get_name() . '.' . $this->getExtensionFromSpec($subdef->getSpecs());
+ }
+
+ public function generateSubdefSubstitutionPathname(\record_adapter $record, \databox_subdef $subdef)
+ {
+ $pathdest = $this->directorySpread($subdef->get_path());
+
+ return $pathdest . $this->generateSubdefFilename($record, $subdef, '0_');
+ }
+
+ /**
+ * @param \databox $databox
+ * @return string
+ */
+ public function generateDataboxDocumentBasePath(\databox $databox)
+ {
+ $baseprefs = $databox->get_sxml_structure();
+
+ return $this->directorySpread(\p4string::addEndSlash((string)($baseprefs->path)));
+ }
+
+ /**
+ * Write Media source file with given filename
+ *
+ * @param \databox $databox
+ * @param string $source
+ * @param string $filename
+ */
+ public function writeMediaSourceFile(\databox $databox, $source, $filename)
+ {
+ $realPath = $this->generateDataboxDocumentBasePath($databox) . $filename;
+
+ $this->filesystem->copy($source, $realPath, true);
+ $this->filesystem->chmod($realPath, 0760);
+ }
+
+ /**
+ * Copy file from source to target
+ *
+ * @param string $source
+ * @param string $target
+ */
+ public function copy($source, $target)
+ {
+ $this->filesystem->copy($source, $target, true);
+ }
+
+ public function chmod($files, $mode, $umask = 0000, $recursive = false)
+ {
+ $this->filesystem->chmod($files, $mode, $umask, $recursive);
+ }
+
+ /**
+ * Get the extension from MediaAlchemyst specs
+ *
+ * @param SpecificationInterface $spec
+ *
+ * @return string
+ */
+ private function getExtensionFromSpec(SpecificationInterface $spec)
+ {
+ switch ($spec->getType()) {
+ case SpecificationInterface::TYPE_IMAGE:
+ return 'jpg';
+ case SpecificationInterface::TYPE_ANIMATION:
+ return 'gif';
+ case SpecificationInterface::TYPE_AUDIO:
+ return $this->getExtensionFromAudioCodec($spec->getAudioCodec());
+ case SpecificationInterface::TYPE_VIDEO:
+ return $this->getExtensionFromVideoCodec($spec->getVideoCodec());
+ case SpecificationInterface::TYPE_SWF:
+ return 'swf';
+ }
+
+ return null;
+ }
+
+ /**
+ * Get the extension from audiocodec
+ *
+ * @param string $audioCodec
+ *
+ * @return string
+ */
+ private function getExtensionFromAudioCodec($audioCodec)
+ {
+ switch ($audioCodec) {
+ case 'flac':
+ return 'flac';
+ case 'libvorbis':
+ return 'ogg';
+ case 'libmp3lame':
+ return 'mp3';
+ }
+
+ return null;
+ }
+
+ /**
+ * Get the extension from videocodec
+ *
+ * @param string $videoCodec
+ *
+ * @return string
+ */
+ private function getExtensionFromVideoCodec($videoCodec)
+ {
+ switch ($videoCodec) {
+ case 'libtheora':
+ return 'ogv';
+ case 'libvpx':
+ return 'webm';
+ case 'libx264':
+ return 'mp4';
+ }
+
+ return null;
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Core/Provider/TemporaryFilesystemServiceProvider.php b/lib/Alchemy/Phrasea/Filesystem/FilesystemServiceProvider.php
similarity index 56%
rename from lib/Alchemy/Phrasea/Core/Provider/TemporaryFilesystemServiceProvider.php
rename to lib/Alchemy/Phrasea/Filesystem/FilesystemServiceProvider.php
index 1dacecf881..2b181f6ef4 100644
--- a/lib/Alchemy/Phrasea/Core/Provider/TemporaryFilesystemServiceProvider.php
+++ b/lib/Alchemy/Phrasea/Filesystem/FilesystemServiceProvider.php
@@ -1,6 +1,5 @@
share(function () {
+ return new Filesystem();
+ });
+
$app['temporary-filesystem.temporary-fs'] = $app->share(function (Application $app) {
return new TemporaryFilesystem($app['filesystem']);
});
$app['temporary-filesystem'] = $app->share(function (Application $app) {
return new Manager($app['temporary-filesystem.temporary-fs'], $app['filesystem']);
});
+
+ $app['phraseanet.filesystem'] = $app->share(function (Application $app) {
+ return new FilesystemService($app['filesystem']);
+ });
+
+ $app['phraseanet.lazaret_filesystem'] = $app->share(function (Application $app) {
+ return new LazaretFilesystemService($app['filesystem'], $app['tmp.lazaret.path'], $app['media-alchemyst']);
+ });
}
public function boot(Application $app)
{
+ // no-op
}
}
diff --git a/lib/Alchemy/Phrasea/Filesystem/LazaretFilesystemService.php b/lib/Alchemy/Phrasea/Filesystem/LazaretFilesystemService.php
new file mode 100644
index 0000000000..113bf6839b
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Filesystem/LazaretFilesystemService.php
@@ -0,0 +1,77 @@
+filesystem = $filesystem;
+ $this->alchemyst = $alchemyst;
+
+ $this->booker = new LazaretPathBooker($filesystem, $tmpPath);
+ }
+
+ /**
+ * Write a file in storage and mark it lazaret
+ * @param File $file
+ * @return PersistedLazaretInformation
+ */
+ public function writeLazaret(File $file)
+ {
+ $lazaretPathname = $this->booker->bookFile($file->getOriginalName());
+ $this->filesystem->copy($file->getFile()->getRealPath(), $lazaretPathname, true);
+
+ $lazaretPathnameThumb = $this->booker->bookFile($file->getOriginalName(), 'thumb');
+ try {
+ $this->alchemyst->turnInto($file->getFile()->getPathname(), $lazaretPathnameThumb, $this->createThumbnailSpecification());
+ } catch (ExceptionInterface $e) {
+ // Ignore, an empty file should be present
+ }
+
+ return new PersistedLazaretInformation($lazaretPathname, $lazaretPathnameThumb);
+ }
+
+ /**
+ * @return ImageSpecification
+ */
+ private function createThumbnailSpecification()
+ {
+ $spec = new ImageSpecification();
+
+ $spec->setResizeMode(ImageSpecification::RESIZE_MODE_INBOUND_FIXEDRATIO);
+ $spec->setDimensions(375, 275);
+
+ return $spec;
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Filesystem/LazaretPathBooker.php b/lib/Alchemy/Phrasea/Filesystem/LazaretPathBooker.php
new file mode 100644
index 0000000000..824ed965cc
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Filesystem/LazaretPathBooker.php
@@ -0,0 +1,87 @@
+filesystem = $filesystem;
+ $this->tmpPath = $tmpPath;
+
+ if (!is_callable($pathResolver)) {
+ throw new \LogicException('pathResolver should be callable');
+ }
+
+ $this->pathResolver = $pathResolver;
+ }
+
+ /**
+ * @param string $filename
+ * @param string $suffix
+ * @return string
+ */
+ public function bookFile($filename, $suffix = '')
+ {
+ $output = $this->tmpPath .'/lzrt_' . substr($filename, 0, 3) . '_' . $suffix . '.' . pathinfo($filename, PATHINFO_EXTENSION);
+ $infos = pathinfo($output);
+ $n = 0;
+
+ while (true) {
+ $output = sprintf('%s/%s-%d%s', $infos['dirname'], $infos['filename'], ++ $n, (isset($infos['extension']) ? '.' . $infos['extension'] : ''));
+
+ try {
+ if (! $this->filesystem->exists($output)) {
+ $this->filesystem->touch($output);
+ break;
+ }
+ } catch (IOException $e) {
+
+ }
+ }
+
+ return $this->resolvePath($output);
+ }
+
+ /**
+ * @param string $path
+ * @return string
+ */
+ private function resolvePath($path)
+ {
+ $callable = $this->pathResolver;
+
+ return $callable($path);
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Filesystem/PersistedLazaretInformation.php b/lib/Alchemy/Phrasea/Filesystem/PersistedLazaretInformation.php
new file mode 100644
index 0000000000..e9bd0a5c45
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Filesystem/PersistedLazaretInformation.php
@@ -0,0 +1,46 @@
+fileCopyRealPath = $fileCopyRealPath;
+ $this->thumbnailRealPath = $thumbnailRealPath;
+ }
+
+ /**
+ * @return string
+ */
+ public function getFilename()
+ {
+ return pathinfo($this->fileCopyRealPath, PATHINFO_BASENAME);
+ }
+
+ /**
+ * @return string
+ */
+ public function getThumbnailFilename()
+ {
+ return pathinfo($this->thumbnailRealPath, PATHINFO_BASENAME);
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Helper/User/Edit.php b/lib/Alchemy/Phrasea/Helper/User/Edit.php
index 95b0973e44..e8d0381dd2 100644
--- a/lib/Alchemy/Phrasea/Helper/User/Edit.php
+++ b/lib/Alchemy/Phrasea/Helper/User/Edit.php
@@ -227,7 +227,7 @@ class Edit extends \Alchemy\Phrasea\Helper\Helper
'users' => $this->users,
'users_serial' => implode(';', $this->users),
'base_id' => $this->base_id,
- 'collection' => \collection::get_from_base_id($this->app, $this->base_id),
+ 'collection' => \collection::getByBaseId($this->app, $this->base_id),
];
}
@@ -331,7 +331,7 @@ class Edit extends \Alchemy\Phrasea\Helper\Helper
'users' => $this->users,
'users_serial' => implode(';', $this->users),
'base_id' => $this->base_id,
- 'collection' => \collection::get_from_base_id($this->app, $this->base_id),
+ 'collection' => \collection::getByBaseId($this->app, $this->base_id),
];
}
@@ -391,7 +391,7 @@ class Edit extends \Alchemy\Phrasea\Helper\Helper
'users' => $this->users,
'users_serial' => implode(';', $this->users),
'base_id' => $this->base_id,
- 'collection' => \collection::get_from_base_id($this->app, $this->base_id),
+ 'collection' => \collection::getByBaseId($this->app, $this->base_id),
];
}
diff --git a/lib/Alchemy/Phrasea/Http/BinaryFileResponse.php b/lib/Alchemy/Phrasea/Http/BinaryFileResponse.php
new file mode 100644
index 0000000000..0e19e5a9c3
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Http/BinaryFileResponse.php
@@ -0,0 +1,66 @@
+headers->has('X-Sendfile-Type'))
+ && $request->headers->has('Range') && $request->headers->has('If-Range')
+ && $this->hasValidIfRangeHeader($request->headers->get('If-Range'))
+ ) {
+ $range = $request->headers->get('Range');
+ $fileSize = $this->file->getSize();
+
+ list($start, $end) = explode('-', substr($range, 6), 2) + array(0);
+
+ $end = ('' === $end) ? $fileSize - 1 : (int) $end;
+
+ if ('' === $start) {
+ $start = $fileSize - $end;
+ $end = $fileSize - 1;
+ } else {
+ $start = (int) $start;
+ }
+
+ if ($start <= $end) {
+ if ($start < 0 || $end > $fileSize - 1) {
+ $this->setStatusCode(416);
+ } elseif ($start !== 0 || $end !== $fileSize - 1) {
+ $this->maxlen = $end < $fileSize ? $end - $start + 1 : -1;
+ $this->offset = $start;
+
+ $this->setStatusCode(206);
+ $this->headers->set('Content-Range', sprintf('bytes %s-%s/%s', $start, $end, $fileSize));
+ $this->headers->set('Content-Length', $end - $start + 1);
+ }
+ }
+
+ }
+
+ return $this;
+ }
+
+ private function hasValidIfRangeHeader($header)
+ {
+ if (null === $lastModified = $this->getLastModified()) {
+ return false;
+ }
+
+ return $lastModified->format('D, d M Y H:i:s').' GMT' == $header;
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Http/DeliverDataInterface.php b/lib/Alchemy/Phrasea/Http/DeliverDataInterface.php
index af37b05a13..f9cfaf00f3 100644
--- a/lib/Alchemy/Phrasea/Http/DeliverDataInterface.php
+++ b/lib/Alchemy/Phrasea/Http/DeliverDataInterface.php
@@ -11,6 +11,7 @@
namespace Alchemy\Phrasea\Http;
+use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
interface DeliverDataInterface
@@ -26,17 +27,7 @@ interface DeliverDataInterface
* @param string $disposition
* @param string|null $mimeType
* @param integer $cacheDuration
+ * @return Response
*/
public function deliverFile($file, $filename = null, $disposition = self::DISPOSITION_INLINE, $mimeType = null, $cacheDuration = null);
-
- /**
- * Return a HTTP Response ready to deliver data
- *
- * @param string $data
- * @param string $filename
- * @param string $mimeType
- * @param string $disposition
- * @param integer $cacheDuration
- */
- public function deliverData($data, $filename, $mimeType, $disposition = self::DISPOSITION_INLINE, $cacheDuration = null);
}
diff --git a/lib/Alchemy/Phrasea/Http/ServeFileResponseFactory.php b/lib/Alchemy/Phrasea/Http/ServeFileResponseFactory.php
index 636f9e211a..639dfbd378 100644
--- a/lib/Alchemy/Phrasea/Http/ServeFileResponseFactory.php
+++ b/lib/Alchemy/Phrasea/Http/ServeFileResponseFactory.php
@@ -11,10 +11,6 @@
namespace Alchemy\Phrasea\Http;
-use Alchemy\Phrasea\Application;
-use Symfony\Component\HttpFoundation\Response;
-use Symfony\Component\HttpFoundation\BinaryFileResponse;
-
class ServeFileResponseFactory implements DeliverDataInterface
{
private $unicode;
@@ -24,20 +20,6 @@ class ServeFileResponseFactory implements DeliverDataInterface
$this->unicode = $unicode;
}
- /**
- * @param Application $app
- * @return self
- */
- public static function create(Application $app)
- {
- return new self(
- $app['unicode']
- );
- }
-
- /**
- * {@inheritdoc}
- */
public function deliverFile($file, $filename = '', $disposition = self::DISPOSITION_INLINE, $mimeType = null, $cacheDuration = null)
{
$response = new BinaryFileResponse($file);
@@ -53,25 +35,6 @@ class ServeFileResponseFactory implements DeliverDataInterface
return $response;
}
- /**
- * {@inheritdoc}
- */
- public function deliverData($data, $filename, $mimeType, $disposition = self::DISPOSITION_INLINE, $cacheDuration = null)
- {
- $response = new Response($data);
- $response->headers->set('Content-Disposition', $response->headers->makeDisposition(
- $disposition,
- $this->sanitizeFilename($filename),
- $this->sanitizeFilenameFallback($filename
- )));
- $response->headers->set('Content-Type', $mimeType);
- if (null !== $cacheDuration) {
- $response->setMaxAge($cacheDuration);
- }
-
- return $response;
- }
-
private function sanitizeFilename($filename)
{
return str_replace(['/', '\\'], '', $filename);
diff --git a/lib/Alchemy/Phrasea/Http/StaticFile/StaticMode.php b/lib/Alchemy/Phrasea/Http/StaticFile/StaticMode.php
index de5a1b4b7d..ca357da0a5 100644
--- a/lib/Alchemy/Phrasea/Http/StaticFile/StaticMode.php
+++ b/lib/Alchemy/Phrasea/Http/StaticFile/StaticMode.php
@@ -11,7 +11,6 @@
namespace Alchemy\Phrasea\Http\StaticFile;
-use Alchemy\Phrasea\Http\AbstractServerMode;
use Alchemy\Phrasea\Http\StaticFile\Symlink\SymLinker;
use Guzzle\Http\Url;
@@ -26,10 +25,10 @@ class StaticMode
/**
* @param $pathFile
- * @param null $etag
+ * @param null|string $etag
* @return Url
*/
- public function getUrl($pathFile, $etag=null)
+ public function getUrl($pathFile, $etag = null)
{
$this->ensureSymlink($pathFile);
diff --git a/lib/Alchemy/Phrasea/Http/StaticFile/Symlink/SymLinker.php b/lib/Alchemy/Phrasea/Http/StaticFile/Symlink/SymLinker.php
index 60603fc16b..221fa68131 100644
--- a/lib/Alchemy/Phrasea/Http/StaticFile/Symlink/SymLinker.php
+++ b/lib/Alchemy/Phrasea/Http/StaticFile/Symlink/SymLinker.php
@@ -11,10 +11,8 @@
namespace Alchemy\Phrasea\Http\StaticFile\Symlink;
-use Alchemy\Phrasea\Exception\InvalidArgumentException;
use Silex\Application;
use Symfony\Component\Filesystem\Filesystem;
-use Guzzle\Http\Url;
/**
* Create & retrieve symlinks
@@ -25,15 +23,6 @@ class SymLinker
protected $fs;
protected $symlinkDir;
- public static function create(Application $app)
- {
- return new SymLinker(
- $app['phraseanet.thumb-symlinker-encoder'],
- $app['filesystem'],
- $app['thumbnail.path']
- );
- }
-
public function __construct(SymLinkerEncoder $encoder, Filesystem $fs, $symlinkDir)
{
$this->encoder = $encoder;
@@ -51,6 +40,11 @@ class SymLinker
$this->fs->symlink($pathFile, $this->getSymlinkPath($pathFile)) ;
}
+ public function unlink($pathFile)
+ {
+ $this->fs->remove($this->getSymlinkPath($pathFile));
+ }
+
public function getSymlink($pathFile)
{
return $this->encoder->encode($pathFile);
diff --git a/lib/Alchemy/Phrasea/Http/StaticFile/Symlink/SymLinkerEncoder.php b/lib/Alchemy/Phrasea/Http/StaticFile/Symlink/SymLinkerEncoder.php
index 8139ec89eb..170032d40d 100644
--- a/lib/Alchemy/Phrasea/Http/StaticFile/Symlink/SymLinkerEncoder.php
+++ b/lib/Alchemy/Phrasea/Http/StaticFile/Symlink/SymLinkerEncoder.php
@@ -11,21 +11,10 @@
namespace Alchemy\Phrasea\Http\StaticFile\Symlink;
-use Silex\Application;
-use Symfony\Component\Filesystem\Filesystem;
-use Guzzle\Http\Url;
-
class SymLinkerEncoder
{
protected $key;
- public static function create(Application $app)
- {
- return new self(
- $app['phraseanet.configuration']['main']['key']
- );
- }
-
public function __construct($key)
{
$this->key = $key;
diff --git a/lib/Alchemy/Phrasea/Media/DatafilesResolver.php b/lib/Alchemy/Phrasea/Media/DatafilesResolver.php
new file mode 100644
index 0000000000..66fa3ec490
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Media/DatafilesResolver.php
@@ -0,0 +1,40 @@
+appbox = $appbox;
+ }
+
+ public function resolve(Request $request, $routeName, array $routeParameters)
+ {
+ $parameters = array_intersect_key($routeParameters, [
+ 'sbas_id' => null,
+ 'record_id' => null,
+ 'subdef' => null,
+ ]);
+
+ $databox = $this->appbox->get_databox((int) $parameters['sbas_id']);
+ $record = $databox->get_record((int)$parameters['record_id']);
+ $subdef = $record->get_subdef($parameters['subdef']);
+
+ return new MediaInformation($subdef, $request, $routeName, $routeParameters);
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Media/MediaAccessorResolver.php b/lib/Alchemy/Phrasea/Media/MediaAccessorResolver.php
new file mode 100644
index 0000000000..e5bcd8fff8
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Media/MediaAccessorResolver.php
@@ -0,0 +1,44 @@
+appbox = $appbox;
+ $this->controller = $controller;
+ }
+
+ public function resolve(Request $request, $routeName, array $routeParameters)
+ {
+ $parameters = array_intersect_key($routeParameters, [
+ 'token' => null,
+ ]);
+
+ list ($sbas_id, $record_id, $subdefName) = $this->controller->validateToken($parameters['token']);
+
+ $databox = $this->appbox->get_databox($sbas_id);
+ $record = $databox->get_record($record_id);
+ $subdef = $record->get_subdef($subdefName);
+
+ return new MediaInformation($subdef, $request, $routeName, $routeParameters);
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Media/PermalinkMediaResolver.php b/lib/Alchemy/Phrasea/Media/PermalinkMediaResolver.php
new file mode 100644
index 0000000000..5854987085
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Media/PermalinkMediaResolver.php
@@ -0,0 +1,44 @@
+appbox = $appbox;
+ }
+
+ public function resolve(Request $request, $routeName, array $routeParameters)
+ {
+ $parameters = array_replace(
+ $request->query->all(),
+ array_intersect_key($routeParameters, [
+ 'sbas_id' => null,
+ 'record_id' => null,
+ 'subdef' => null,
+ 'label' => null,
+ ])
+ );
+
+ $databox = $this->appbox->get_databox((int) $parameters['sbas_id']);
+ $record = $databox->get_record((int)$parameters['record_id']);
+ $subdef = $record->get_subdef($parameters['subdef']);
+
+ return new MediaInformation($subdef, $request, $routeName, $routeParameters);
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Media/Subdef/Subdef.php b/lib/Alchemy/Phrasea/Media/Subdef/Subdef.php
index 96e6632449..db42b01c84 100644
--- a/lib/Alchemy/Phrasea/Media/Subdef/Subdef.php
+++ b/lib/Alchemy/Phrasea/Media/Subdef/Subdef.php
@@ -11,6 +11,8 @@
namespace Alchemy\Phrasea\Media\Subdef;
+use MediaAlchemyst\Specification\SpecificationInterface;
+
interface Subdef
{
const TYPE_IMAGE = 'image';
@@ -19,9 +21,20 @@ interface Subdef
const TYPE_AUDIO = 'audio';
const TYPE_FLEXPAPER = 'flexpaper';
+ /**
+ * One of Subdef Type const
+ *
+ * @return string
+ */
public function getType();
+ /**
+ * @return string
+ */
public function getDescription();
+ /**
+ * @return SpecificationInterface
+ */
public function getMediaAlchemystSpec();
}
diff --git a/lib/Alchemy/Phrasea/Media/SubdefGenerator.php b/lib/Alchemy/Phrasea/Media/SubdefGenerator.php
index 27e1cd5b09..3168417c49 100644
--- a/lib/Alchemy/Phrasea/Media/SubdefGenerator.php
+++ b/lib/Alchemy/Phrasea/Media/SubdefGenerator.php
@@ -1,5 +1,4 @@
app = $app;
$this->alchemyst = $alchemyst;
@@ -43,17 +46,10 @@ class SubdefGenerator
$this->mediavorus = $mediavorus;
}
- private function dispatch($eventName, RecordEvent $event)
- {
- $this->app['dispatcher']->dispatch($eventName, $event);
- }
-
public function generateSubdefs(\record_adapter $record, array $wanted_subdefs = null)
{
if (null === $subdefs = $record->getDatabox()->get_subdef_structure()->getSubdefGroup($record->getType())) {
$this->logger->info(sprintf('Nothing to do for %s', $record->getType()));
-
- return $this;
}
$this->dispatch(
@@ -75,13 +71,13 @@ class SubdefGenerator
$pathdest = null;
if ($record->has_subdef($subdefname) && $record->get_subdef($subdefname)->is_physically_present()) {
- $pathdest = $record->get_subdef($subdefname)->get_pathfile();
+ $pathdest = $record->get_subdef($subdefname)->getRealPath();
$record->get_subdef($subdefname)->remove_file();
$this->logger->info(sprintf('Removed old file for %s', $subdefname));
$record->clearSubdefCache($subdefname);
}
- $pathdest = $this->generateSubdefPathname($record, $subdef, $pathdest);
+ $pathdest = $this->filesystem->generateSubdefPathname($record, $subdef, $pathdest);
$this->dispatch(
RecordEvents::SUB_DEFINITION_CREATION,
@@ -91,7 +87,7 @@ class SubdefGenerator
)
);
- $this->logger->addInfo(sprintf('Generating subdef %s to %s', $subdefname, $pathdest));
+ $this->logger->info(sprintf('Generating subdef %s to %s', $subdefname, $pathdest));
$this->generateSubdef($record, $subdef, $pathdest);
if ($this->filesystem->exists($pathdest)) {
@@ -128,15 +124,13 @@ class SubdefGenerator
$mediaCreated
)
);
-
- return $this;
}
private function generateSubdef(\record_adapter $record, \databox_subdef $subdef_class, $pathdest)
{
try {
if (null === $record->get_hd_file()) {
- $this->logger->addInfo('No HD file found, aborting');
+ $this->logger->info('No HD file found, aborting');
return;
}
@@ -146,99 +140,4 @@ class SubdefGenerator
$this->logger->error(sprintf('Subdef generation failed for record %d with message %s', $record->getRecordId(), $e->getMessage()));
}
}
-
- private function generateSubdefPathname(\record_adapter $record, \databox_subdef $subdef, $oldVersion = null)
- {
- if ($oldVersion) {
- $pathdest = \p4string::addEndSlash(pathinfo($oldVersion, PATHINFO_DIRNAME));
- } else {
- $pathdest = \databox::dispatch($this->filesystem, $subdef->get_path());
- }
-
- return $pathdest . $record->getRecordId() . '_' . $subdef->get_name() . '.' . $this->getExtensionFromSpec($subdef->getSpecs());
- }
-
- /**
- * Get the extension from MediaAlchemyst specs
- *
- * @param SpecificationInterface $spec
- *
- * @return string
- */
- private function getExtensionFromSpec(SpecificationInterface $spec)
- {
- $extension = null;
-
- switch (true) {
- case $spec->getType() === SpecificationInterface::TYPE_IMAGE:
- $extension = 'jpg';
- break;
- case $spec->getType() === SpecificationInterface::TYPE_ANIMATION:
- $extension = 'gif';
- break;
- case $spec->getType() === SpecificationInterface::TYPE_AUDIO:
- $extension = $this->getExtensionFromAudioCodec($spec->getAudioCodec());
- break;
- case $spec->getType() === SpecificationInterface::TYPE_VIDEO:
- $extension = $this->getExtensionFromVideoCodec($spec->getVideoCodec());
- break;
- case $spec->getType() === SpecificationInterface::TYPE_SWF:
- $extension = 'swf';
- break;
- }
-
- return $extension;
- }
-
- /**
- * Get the extension from audiocodec
- *
- * @param string $audioCodec
- *
- * @return string
- */
- private function getExtensionFromAudioCodec($audioCodec)
- {
- $extension = null;
-
- switch ($audioCodec) {
- case 'flac':
- $extension = 'flac';
- break;
- case 'libvorbis':
- $extension = 'ogg';
- break;
- case 'libmp3lame':
- $extension = 'mp3';
- break;
- }
-
- return $extension;
- }
-
- /**
- * Get the extension from videocodec
- *
- * @param string $videoCodec
- *
- * @return string
- */
- private function getExtensionFromVideoCodec($videoCodec)
- {
- $extension = null;
-
- switch ($videoCodec) {
- case 'libtheora':
- $extension = 'ogv';
- break;
- case 'libvpx':
- $extension = 'webm';
- break;
- case 'libx264':
- $extension = 'mp4';
- break;
- }
-
- return $extension;
- }
}
diff --git a/lib/Alchemy/Phrasea/Media/SubdefSubstituer.php b/lib/Alchemy/Phrasea/Media/SubdefSubstituer.php
index 0eb43f4eb7..e16045f8f1 100644
--- a/lib/Alchemy/Phrasea/Media/SubdefSubstituer.php
+++ b/lib/Alchemy/Phrasea/Media/SubdefSubstituer.php
@@ -1,9 +1,8 @@
alchemyst = $alchemyst;
$this->app = $app;
@@ -36,67 +35,117 @@ class SubdefSubstituer
$this->dispatcher = $dispatcher;
}
- public function substitute(\record_adapter $record, $name, MediaInterface $media)
+ /**
+ * @param \record_adapter $record
+ * @param string $name
+ * @param MediaInterface $media
+ * @param bool $adapt
+ *
+ * @deprecated use {@link self::substituteDocument} or {@link self::substituteSubdef} instead
+ */
+ public function substitute(\record_adapter $record, $name, MediaInterface $media, $adapt = true)
{
- $newfilename = $record->get_record_id() . '_0_' . $name . '.' . $media->getFile()->getExtension();
-
- $subdef_def = false;
-
if ($name == 'document') {
- $baseprefs = $record->get_databox()->get_sxml_structure();
- $pathhd = \p4string::addEndSlash((string) ($baseprefs->path));
+ $this->substituteDocument($record, $media);
- $filehd = $record->get_record_id() . "_document." . strtolower($media->getFile()->getExtension());
- $pathhd = \databox::dispatch($this->fs, $pathhd);
+ return;
+ }
- $this->fs->copy($media->getFile()->getRealPath(), $pathhd . $filehd, true);
+ $this->substituteSubdef($record, $name, $media, $adapt);
+ }
- $subdefFile = $pathhd . $filehd;
- $meta_writable = true;
+ public function substituteDocument(\record_adapter $record, MediaInterface $media)
+ {
+ /** @var \SplFileInfo $file */
+ $file = $media->getFile();
+
+ $source = $file->getRealPath();
+ $target = $this->fs->generateDocumentFilename($record, $file);
+
+ $this->fs->writeMediaSourceFile($record->getDatabox(), $source, $target);
+
+ $media = $this->mediavorus->guess($source);
+
+ $this->createMediaSubdef($record, 'document', $media);
+
+ $record->write_metas();
+
+ $record->rebuild_subdefs();
+
+ $this->dispatcher->dispatch(RecordEvents::MEDIA_SUBSTITUTED, new MediaSubstitutedEvent($record));
+ }
+
+ /**
+ * @param \record_adapter $record
+ * @param string $name
+ * @param MediaInterface $media
+ * @param bool $adapt
+ */
+ public function substituteSubdef(\record_adapter $record, $name, MediaInterface $media, $adapt = true)
+ {
+ if ($name == 'document') {
+ throw new \RuntimeException('Cannot substitute documents, only subdefs allowed');
+ }
+
+ $type = $record->isStory() ? 'image' : $record->getType();
+ $databox_subdef = $record->getDatabox()->get_subdef_structure()->get_subdef($type, $name);
+
+ if ($this->isOldSubdefPresent($record, $name)) {
+ $path_file_dest = $record->get_subdef($name)->getRealPath();
+ $record->get_subdef($name)->remove_file();
+ $record->clearSubdefCache($name);
} else {
- $type = $record->isStory() ? 'image' : $record->get_type();
- $subdef_def = $record->get_databox()->get_subdef_structure()->get_subdef($type, $name);
-
- if ($record->has_subdef($name) && $record->get_subdef($name)->is_physically_present()) {
- $path_file_dest = $record->get_subdef($name)->get_pathfile();
- $record->get_subdef($name)->remove_file();
- $record->clearSubdefCache($name);
- } else {
- $path = \databox::dispatch($this->fs, $subdef_def->get_path());
- $this->fs->mkdir($path, 0750);
- $path_file_dest = $path . $newfilename;
- }
+ $path_file_dest = $this->fs->generateSubdefSubstitutionPathname($record, $databox_subdef);
+ }
+ if($adapt) {
try {
$this->alchemyst->turnInto(
$media->getFile()->getRealPath(),
$path_file_dest,
- $subdef_def->getSpecs()
+ $databox_subdef->getSpecs()
);
} catch (MediaAlchemystException $e) {
return;
}
-
- $subdefFile = $path_file_dest;
-
- $meta_writable = $subdef_def->meta_writeable();
+ } else {
+ $this->fs->copy($media->getFile()->getRealPath(), $path_file_dest);
}
- $this->fs->chmod($subdefFile, 0760);
- $media = $this->mediavorus->guess($subdefFile);
+ $this->fs->chmod($path_file_dest, 0760);
+ $media = $this->mediavorus->guess($path_file_dest);
- \media_subdef::create($this->app, $record, $name, $media);
+ $this->createMediaSubdef($record, $name, $media);
- $record->delete_data_from_cache(\record_adapter::CACHE_SUBDEFS);
-
- if ($meta_writable) {
+ if ($databox_subdef->isMetadataUpdateRequired()) {
$record->write_metas();
}
- if ($name == 'document') {
- $record->rebuild_subdefs();
- }
+ $record->rebuild_subdefs();
$this->dispatcher->dispatch(RecordEvents::MEDIA_SUBSTITUTED, new MediaSubstitutedEvent($record));
}
+
+ /**
+ * @param \record_adapter $record
+ * @param string $name
+ * @return bool
+ */
+ private function isOldSubdefPresent(\record_adapter $record, $name)
+ {
+ return $record->has_subdef($name) && $record->get_subdef($name)->is_physically_present();
+ }
+
+ /**
+ * @param \record_adapter $record
+ * @param string $name
+ * @param MediaInterface $media
+ */
+ private function createMediaSubdef(\record_adapter $record, $name, MediaInterface $media)
+ {
+ $subdef = \media_subdef::create($this->app, $record, $name, $media);
+ $subdef->set_substituted(true);
+
+ $record->delete_data_from_cache(\record_adapter::CACHE_SUBDEFS);
+ }
}
diff --git a/lib/Alchemy/Phrasea/Model/Entities/Feed.php b/lib/Alchemy/Phrasea/Model/Entities/Feed.php
index 6f1c5a8a93..55a2156128 100644
--- a/lib/Alchemy/Phrasea/Model/Entities/Feed.php
+++ b/lib/Alchemy/Phrasea/Model/Entities/Feed.php
@@ -302,7 +302,7 @@ class Feed implements FeedInterface
public function getCollection(Application $app)
{
if ($this->getBaseId() !== null) {
- return \collection::get_from_base_id($app, $this->getBaseId());
+ return \collection::getByBaseId($app, $this->getBaseId());
}
}
diff --git a/lib/Alchemy/Phrasea/Model/Entities/LazaretFile.php b/lib/Alchemy/Phrasea/Model/Entities/LazaretFile.php
index 1eabdf1fd7..c9a43ec845 100644
--- a/lib/Alchemy/Phrasea/Model/Entities/LazaretFile.php
+++ b/lib/Alchemy/Phrasea/Model/Entities/LazaretFile.php
@@ -211,7 +211,7 @@ class LazaretFile
*/
public function getCollection(Application $app)
{
- return \collection::get_from_base_id($app, $this->getBaseId());
+ return \collection::getByBaseId($app, $this->getBaseId());
}
/**
diff --git a/lib/Alchemy/Phrasea/Model/Entities/Registration.php b/lib/Alchemy/Phrasea/Model/Entities/Registration.php
index e00b82455f..6d7e08052f 100644
--- a/lib/Alchemy/Phrasea/Model/Entities/Registration.php
+++ b/lib/Alchemy/Phrasea/Model/Entities/Registration.php
@@ -141,7 +141,7 @@ class Registration
*/
public function getCollection(Application $app)
{
- return \collection::get_from_base_id($app, $this->baseId);
+ return \collection::getByBaseId($app, $this->baseId);
}
/**
diff --git a/lib/Alchemy/Phrasea/Model/Entities/Session.php b/lib/Alchemy/Phrasea/Model/Entities/Session.php
index f6a6813506..6653599b68 100644
--- a/lib/Alchemy/Phrasea/Model/Entities/Session.php
+++ b/lib/Alchemy/Phrasea/Model/Entities/Session.php
@@ -15,7 +15,7 @@ use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation as Gedmo;
/**
- * @ORM\Table(name="Sessions", indexes={@ORM\index(name="session_user_id", columns={"user_id"})})
+ * @ORM\Table(name="Sessions")
* @ORM\Entity(repositoryClass="Alchemy\Phrasea\Model\Repositories\SessionRepository")
*/
class Session
diff --git a/lib/Alchemy/Phrasea/Model/Entities/WebhookEvent.php b/lib/Alchemy/Phrasea/Model/Entities/WebhookEvent.php
index d8c5f959b3..3017239acf 100644
--- a/lib/Alchemy/Phrasea/Model/Entities/WebhookEvent.php
+++ b/lib/Alchemy/Phrasea/Model/Entities/WebhookEvent.php
@@ -21,6 +21,9 @@ class WebhookEvent
const USER_DELETED = 'user.deleted';
const USER_DELETED_TYPE = 'user.deleted';
+ const RECORD_SUBDEF_CREATED = 'record.subdef.created';
+ const RECORD_SUBDEF_TYPE = 'record.subdef';
+
/**
* @ORM\Column(type="integer")
* @ORM\Id
@@ -58,25 +61,6 @@ class WebhookEvent
*/
private $created;
- public static function types()
- {
- return [
- self::FEED_ENTRY_TYPE,
- self::USER_REGISTRATION_TYPE,
- self::USER_DELETED_TYPE
- ];
- }
-
- public static function events()
- {
- return [
- self::NEW_FEED_ENTRY,
- self::USER_REGISTRATION_REJECTED,
- self::USER_REGISTRATION_GRANTED,
- self::USER_DELETED
- ];
- }
-
/**
* @param \DateTime $created
*
@@ -126,17 +110,12 @@ class WebhookEvent
}
/**
- * @param $name
+ * @param string $name
*
- * @return WebhookEvent
- * @throws \InvalidArgumentException
+ * @return $this
*/
public function setName($name)
{
- if (!in_array($name, self::events())) {
- throw new \InvalidArgumentException("Invalid event name");
- }
-
$this->name = $name;
return $this;
@@ -179,16 +158,12 @@ class WebhookEvent
}
/**
- * @param $type
+ * @param string $type
*
* @return $this
- * @throws \InvalidArgumentException
*/
public function setType($type)
{
- if (!in_array($type, self::types())) {
- throw new \InvalidArgumentException("Invalid event name");
- }
$this->type = $type;
return $this;
diff --git a/lib/Alchemy/Phrasea/Model/Manipulator/UserManipulator.php b/lib/Alchemy/Phrasea/Model/Manipulator/UserManipulator.php
index e8a5fc8128..adfebb6f4f 100644
--- a/lib/Alchemy/Phrasea/Model/Manipulator/UserManipulator.php
+++ b/lib/Alchemy/Phrasea/Model/Manipulator/UserManipulator.php
@@ -347,9 +347,9 @@ class UserManipulator implements ManipulatorInterface
/**
* Makes given variable traversable.
*
- * @param mixed $var
+ * @param User|User[] $var
*
- * @return array
+ * @return array|\Traversable|User[]
*/
private function makeTraversable($var)
{
diff --git a/lib/Alchemy/Phrasea/Model/Repositories/BasketRepository.php b/lib/Alchemy/Phrasea/Model/Repositories/BasketRepository.php
index 6f081d98b3..5db12de16e 100644
--- a/lib/Alchemy/Phrasea/Model/Repositories/BasketRepository.php
+++ b/lib/Alchemy/Phrasea/Model/Repositories/BasketRepository.php
@@ -1,9 +1,8 @@
translator = $translator;
+ }
+
+ /**
+ * @param string $id
+ * @param array $parameters
+ * @param string $domain
+ * @param string $locale
+ * @return string
+ */
+ private function trans($id, $parameters = [], $domain = null, $locale = null)
+ {
+ if ($this->translator) {
+ return $this->translator->trans($id, $parameters, $domain, $locale);
+ }
+
+ return $id;
+ }
+
/**
* Returns all basket for a given user that are not marked as archived
*
@@ -135,11 +161,11 @@ class BasketRepository extends EntityRepository
$basket = $query->getOneOrNullResult();
- /* @var $basket Basket */
if (null === $basket) {
- throw new NotFoundHttpException(_('Basket is not found'));
+ throw new NotFoundHttpException($this->trans('Basket is not found'));
}
+ /* @var Basket $basket */
if ($basket->getUser()->getId() != $user->getId()) {
$participant = false;
@@ -152,7 +178,7 @@ class BasketRepository extends EntityRepository
}
}
if (!$participant) {
- throw new AccessDeniedHttpException(_('You have not access to this basket'));
+ throw new AccessDeniedHttpException($this->trans('You have not access to this basket'));
}
}
diff --git a/lib/Alchemy/Phrasea/Model/Repositories/UserRepository.php b/lib/Alchemy/Phrasea/Model/Repositories/UserRepository.php
index 7e14289f03..9d66c73285 100644
--- a/lib/Alchemy/Phrasea/Model/Repositories/UserRepository.php
+++ b/lib/Alchemy/Phrasea/Model/Repositories/UserRepository.php
@@ -25,7 +25,7 @@ class UserRepository extends EntityRepository
/**
* Finds admins.
*
- * @return array
+ * @return User[]
*/
public function findAdmins()
{
diff --git a/lib/Alchemy/Phrasea/Out/Module/PDF.php b/lib/Alchemy/Phrasea/Out/Module/PDF.php
index a7ef976057..8fe925d8b3 100644
--- a/lib/Alchemy/Phrasea/Out/Module/PDF.php
+++ b/lib/Alchemy/Phrasea/Out/Module/PDF.php
@@ -146,7 +146,7 @@ class PDF
$irow = $ipage = 0;
$icol = -1;
foreach ($this->records as $rec) {
- /* @var $rec record_adapter */
+ /* @var \record_adapter $rec */
if (++$icol >= $NDiapoW) {
$icol = 0;
if (++$irow >= $NDiapoH) {
@@ -164,7 +164,7 @@ class PDF
$subdef = $rec->get_thumbnail();
}
- $fimg = $subdef->get_pathfile();
+ $fimg = $subdef->getRealPath();
if (!$this->app->getAclForUser($this->app->getAuthenticatedUser())->has_right_on_base($rec->get_base_id(), "nowatermark")
&& $subdef->get_type() == \media_subdef::TYPE_IMAGE) {
@@ -226,10 +226,10 @@ class PDF
$ndoc = 0;
foreach ($this->records as $rec) {
- /* @var $rec record_adapter */
+ /* @var \record_adapter $rec */
$subdef = $rec->get_subdef('thumbnail');
- $fimg = $subdef->get_pathfile();
+ $fimg = $subdef->getRealPath();
$wimg = $himg = 50;
// 1px = 3.77952 mm
@@ -322,7 +322,7 @@ class PDF
}
foreach ($this->records as $krec => $rec) {
- /* @var $rec record_adapter */
+ /* @var \record_adapter $rec */
$this->pdf->AddPage();
@@ -350,7 +350,7 @@ class PDF
}
}
- $collection = \collection::get_from_base_id($this->app, $rec->get_base_id());
+ $collection = \collection::getByBaseId($this->app, $rec->get_base_id());
$vn = "";
if (false !== $str = simplexml_load_string($collection->get_prefs())) {
@@ -436,7 +436,7 @@ class PDF
$subdef = $rec->get_thumbnail();
}
- $f = $subdef->get_pathfile();
+ $f = $subdef->getRealPath();
if (!$this->app->getAclForUser($this->app->getAuthenticatedUser())->has_right_on_base($rec->get_base_id(), "nowatermark")
&& $subdef->get_type() == \media_subdef::TYPE_IMAGE)
diff --git a/lib/Alchemy/Phrasea/SearchEngine/Elastic/Search/QueryVisitor.php b/lib/Alchemy/Phrasea/SearchEngine/Elastic/Search/QueryVisitor.php
index 36815ac3ec..9c5f77f9f6 100644
--- a/lib/Alchemy/Phrasea/SearchEngine/Elastic/Search/QueryVisitor.php
+++ b/lib/Alchemy/Phrasea/SearchEngine/Elastic/Search/QueryVisitor.php
@@ -4,6 +4,7 @@ namespace Alchemy\Phrasea\SearchEngine\Elastic\Search;
use Alchemy\Phrasea\SearchEngine\Elastic\AST;
use Alchemy\Phrasea\SearchEngine\Elastic\Exception\Exception;
+use Alchemy\Phrasea\SearchEngine\Elastic\Mapping;
use Alchemy\Phrasea\SearchEngine\Elastic\Structure\Structure;
use Hoa\Compiler\Llk\TreeNode;
use Hoa\Visitor\Element;
@@ -207,7 +208,7 @@ class QueryVisitor implements Visit
if ($key instanceof AST\KeyValue\TimestampKey) {
return true;
} elseif ($key instanceof AST\KeyValue\FieldKey) {
- return $this->structure->get($key->getName()) !== null;
+ return $this->structure->typeOf($key->getName()) === Mapping::TYPE_DATE;
}
return false;
}
diff --git a/lib/Alchemy/Phrasea/SearchEngine/SearchEngineOptions.php b/lib/Alchemy/Phrasea/SearchEngine/SearchEngineOptions.php
index 6bd3bcf00f..0b1dad1798 100644
--- a/lib/Alchemy/Phrasea/SearchEngine/SearchEngineOptions.php
+++ b/lib/Alchemy/Phrasea/SearchEngine/SearchEngineOptions.php
@@ -481,7 +481,7 @@ class SearchEngineOptions
break;
case in_array($key, ['collections', 'business_fields']):
$value = array_map(function ($base_id) use ($app) {
- return \collection::get_from_base_id($app, $base_id);
+ return \collection::getByBaseId($app, $base_id);
}, $value);
break;
}
@@ -572,7 +572,7 @@ class SearchEngineOptions
$bas = [];
foreach ($selected_bases as $bas_id) {
try {
- $bas[$bas_id] = \collection::get_from_base_id($app, $bas_id);
+ $bas[$bas_id] = \collection::getByBaseId($app, $bas_id);
} catch (\Exception_Databox_CollectionNotFound $e) {
// Ignore
}
diff --git a/lib/Alchemy/Phrasea/Setup/DoctrineMigrations/SessionMigration.php b/lib/Alchemy/Phrasea/Setup/DoctrineMigrations/SessionMigration.php
index 8993dcb4eb..c9760728c7 100644
--- a/lib/Alchemy/Phrasea/Setup/DoctrineMigrations/SessionMigration.php
+++ b/lib/Alchemy/Phrasea/Setup/DoctrineMigrations/SessionMigration.php
@@ -17,15 +17,17 @@ class SessionMigration extends AbstractMigration
{
public function isAlreadyApplied()
{
- return $this->tableExists('Sessions');
+ return false;
}
public function doUpSql(Schema $schema)
{
- $this->addSql("CREATE TABLE SessionModules (id INT AUTO_INCREMENT NOT NULL, session_id INT DEFAULT NULL, module_id INT NOT NULL, created DATETIME NOT NULL, updated DATETIME NOT NULL, INDEX IDX_BA36EF49613FECDF (session_id), UNIQUE INDEX unique_module (session_id, module_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB");
- $this->addSql("CREATE TABLE Sessions (id INT AUTO_INCREMENT NOT NULL, user_id INT NOT NULL, user_agent VARCHAR(512) NOT NULL, ip_address VARCHAR(40) DEFAULT NULL, platform VARCHAR(128) DEFAULT NULL, browser_name VARCHAR(128) DEFAULT NULL, browser_version VARCHAR(32) DEFAULT NULL, screen_width INT DEFAULT NULL, screen_height INT DEFAULT NULL, token VARCHAR(128) DEFAULT NULL, nonce VARCHAR(16) DEFAULT NULL, created DATETIME NOT NULL, updated DATETIME NOT NULL, UNIQUE INDEX UNIQ_6316FF455F37A13B (token), INDEX IDX_6316FF45A76ED395 (user_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB");
- $this->addSql("ALTER TABLE SessionModules ADD CONSTRAINT FK_BA36EF49613FECDF FOREIGN KEY (session_id) REFERENCES Sessions (id)");
- $this->addSql("ALTER TABLE Sessions ADD CONSTRAINT FK_6316FF45A76ED395 FOREIGN KEY (user_id) REFERENCES Users (id)");
+ if (! $schema->hasTable('Sessions')) {
+ $this->addSql("CREATE TABLE Sessions (id INT AUTO_INCREMENT NOT NULL, user_id INT NOT NULL, user_agent VARCHAR(512) NOT NULL, ip_address VARCHAR(40) DEFAULT NULL, platform VARCHAR(128) DEFAULT NULL, browser_name VARCHAR(128) DEFAULT NULL, browser_version VARCHAR(32) DEFAULT NULL, screen_width INT DEFAULT NULL, screen_height INT DEFAULT NULL, token VARCHAR(128) DEFAULT NULL, nonce VARCHAR(16) DEFAULT NULL, created DATETIME NOT NULL, updated DATETIME NOT NULL, UNIQUE INDEX UNIQ_6316FF455F37A13B (token), INDEX IDX_6316FF45A76ED395 (user_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB");
+ $this->addSql("CREATE TABLE SessionModules (id INT AUTO_INCREMENT NOT NULL, session_id INT DEFAULT NULL, module_id INT NOT NULL, created DATETIME NOT NULL, updated DATETIME NOT NULL, INDEX IDX_BA36EF49613FECDF (session_id), UNIQUE INDEX unique_module (session_id, module_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB");
+ $this->addSql("ALTER TABLE SessionModules ADD CONSTRAINT FK_BA36EF49613FECDF FOREIGN KEY (session_id) REFERENCES Sessions (id)");
+ $this->addSql("ALTER TABLE Sessions ADD CONSTRAINT FK_6316FF45A76ED395 FOREIGN KEY (user_id) REFERENCES Users (id)");
+ }
}
public function doDownSql(Schema $schema)
diff --git a/lib/Alchemy/Phrasea/Setup/DoctrineMigrations/Version20150519173347.php b/lib/Alchemy/Phrasea/Setup/DoctrineMigrations/Version20150519173347.php
index 3cfe5f9212..f50aab94f6 100644
--- a/lib/Alchemy/Phrasea/Setup/DoctrineMigrations/Version20150519173347.php
+++ b/lib/Alchemy/Phrasea/Setup/DoctrineMigrations/Version20150519173347.php
@@ -14,6 +14,8 @@ class Version20150519173347 extends BaseMigration
{
$this->abortIf($this->connection->getDatabasePlatform()->getName() != 'mysql', 'Migration can only be executed safely on \'mysql\'.');
+ $this->skipIf($schema->hasTable('Secrets'), 'Table already exists');
+
$this->addSql('CREATE TABLE Secrets (id INT AUTO_INCREMENT NOT NULL, creator_id INT NOT NULL, created DATETIME NOT NULL, token VARCHAR(40) COLLATE utf8_bin NOT NULL, INDEX IDX_48F428861220EA6 (creator_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB');
$this->addSql('ALTER TABLE Secrets ADD CONSTRAINT FK_48F428861220EA6 FOREIGN KEY (creator_id) REFERENCES Users (id)');
}
diff --git a/lib/Alchemy/Phrasea/Setup/DoctrineMigrations/Version20151209101524.php b/lib/Alchemy/Phrasea/Setup/DoctrineMigrations/Version20151209101524.php
index d080457172..c7d0766698 100644
--- a/lib/Alchemy/Phrasea/Setup/DoctrineMigrations/Version20151209101524.php
+++ b/lib/Alchemy/Phrasea/Setup/DoctrineMigrations/Version20151209101524.php
@@ -2,13 +2,13 @@
namespace Alchemy\Phrasea\Setup\DoctrineMigrations;
-use Doctrine\DBAL\Migrations\AbstractMigration;
+use Doctrine\DBAL\Migrations\AbstractMigration as BaseMigration;
use Doctrine\DBAL\Schema\Schema;
/**
* Auto-generated Migration: Please modify to your needs!
*/
-class Version20151209101524 extends AbstractMigration
+class Version20151209101524 extends BaseMigration
{
/**
* @param Schema $schema
diff --git a/lib/Alchemy/Phrasea/Setup/Version/PreSchemaUpgrade/Upgrade39Sessions.php b/lib/Alchemy/Phrasea/Setup/Version/PreSchemaUpgrade/Upgrade39Sessions.php
new file mode 100644
index 0000000000..79d57d9d38
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Setup/Version/PreSchemaUpgrade/Upgrade39Sessions.php
@@ -0,0 +1,72 @@
+tableExists($em, 'Sessions')) {
+ $this->dropTable($em, 'SessionModules');
+ $this->dropTable($em, 'Sessions');
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function isApplyable(Application $app)
+ {
+ return $this->tableExists($app['orm.em'], 'Sessions');
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function rollback(EntityManager $em, \appbox $appbox, Configuration $conf)
+ {
+
+ }
+
+ /**
+ * Checks whether the table exists or not.
+ *
+ * @param EntityManager $em
+ * @param string $table
+ *
+ * @return boolean
+ */
+ private function tableExists(EntityManager $em, $table)
+ {
+ return (Boolean) $em->createNativeQuery(
+ 'SHOW TABLE STATUS WHERE Name="'.$table.'" COLLATE utf8_bin ', (new ResultSetMapping())->addScalarResult('Name', 'Name')
+ )->getOneOrNullResult();
+ }
+
+ /**
+ * @param EntityManager $em
+ * @param $tableName
+ */
+ private function dropTable(EntityManager $em, $tableName)
+ {
+ $em->getConnection()->getSchemaManager()->dropTable($tableName);
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Setup/Version/PreSchemaUpgrade/Upgrade39Users.php b/lib/Alchemy/Phrasea/Setup/Version/PreSchemaUpgrade/Upgrade39Users.php
index 1ffde761ab..f3b58fcb96 100644
--- a/lib/Alchemy/Phrasea/Setup/Version/PreSchemaUpgrade/Upgrade39Users.php
+++ b/lib/Alchemy/Phrasea/Setup/Version/PreSchemaUpgrade/Upgrade39Users.php
@@ -297,15 +297,19 @@ class Upgrade39Users implements PreSchemaUpgradeInterface
$meta = $em->getClassMetadata('Alchemy\Phrasea\Model\Entities\User');
$connection = $em->getConnection();
$dbPlatform = $connection->getDatabasePlatform();
- $connection->beginTransaction();
- try {
- $connection->query('SET FOREIGN_KEY_CHECKS=0');
- $connection->executeUpdate($dbPlatform->getTruncateTableSql($meta->getTableName()));
- $connection->query('SET FOREIGN_KEY_CHECKS=1');
- $connection->commit();
- } catch (\Exception $e) {
- $connection->rollback();
- throw $e;
+
+ if ($connection->getSchemaManager()->tablesExist([ $meta->getTableName() ])) {
+ $connection->beginTransaction();
+
+ try {
+ $connection->query('SET FOREIGN_KEY_CHECKS=0');
+ $connection->executeUpdate($dbPlatform->getTruncateTableSql($meta->getTableName()));
+ $connection->query('SET FOREIGN_KEY_CHECKS=1');
+ $connection->commit();
+ } catch (\Exception $e) {
+ $connection->rollback();
+ throw $e;
+ }
}
}
diff --git a/lib/Alchemy/Phrasea/TaskManager/Job/ArchiveJob.php b/lib/Alchemy/Phrasea/TaskManager/Job/ArchiveJob.php
index ce80c5f814..8372a6f3b1 100644
--- a/lib/Alchemy/Phrasea/TaskManager/Job/ArchiveJob.php
+++ b/lib/Alchemy/Phrasea/TaskManager/Job/ArchiveJob.php
@@ -868,7 +868,7 @@ class ArchiveJob extends AbstractJob
}
try {
- $collection = \collection::get_from_coll_id($app, $databox, (int) $cid);
+ $collection = \collection::getByCollectionId($app, $databox, (int) $cid);
if ($captionFileName === null) {
$story = $this->createStory($app, $collection, $path . '/' . $representationFileName, null, $stat0, $stat1);
} else {
@@ -1203,7 +1203,7 @@ class ArchiveJob extends AbstractJob
}
try {
- $collection = \collection::get_from_coll_id($app, $databox, (int) $cid);
+ $collection = \collection::getByCollectionId($app, $databox, (int) $cid);
if ($captionFileName === null) {
$this->createRecord($app, $collection, $path . '/' . $file, null, $grp_rid, null, $stat0, $stat1);
diff --git a/lib/Alchemy/Phrasea/TaskManager/Job/EmptyCollectionJob.php b/lib/Alchemy/Phrasea/TaskManager/Job/EmptyCollectionJob.php
index a3756c3161..3e361ba549 100644
--- a/lib/Alchemy/Phrasea/TaskManager/Job/EmptyCollectionJob.php
+++ b/lib/Alchemy/Phrasea/TaskManager/Job/EmptyCollectionJob.php
@@ -61,7 +61,7 @@ class EmptyCollectionJob extends AbstractJob
$baseId = (string) $settings->base_id;
- $collection = \collection::get_from_base_id($app, $baseId);
+ $collection = \collection::getByBaseId($app, $baseId);
$collection->empty_collection(200);
if (0 === $collection->get_record_amount()) {
diff --git a/lib/Alchemy/Phrasea/TaskManager/Job/FtpJob.php b/lib/Alchemy/Phrasea/TaskManager/Job/FtpJob.php
index 52ec5fc75c..a5eabfc08f 100644
--- a/lib/Alchemy/Phrasea/TaskManager/Job/FtpJob.php
+++ b/lib/Alchemy/Phrasea/TaskManager/Job/FtpJob.php
@@ -212,13 +212,13 @@ class FtpJob extends AbstractJob
throw new \Exception('Impossible de creer un fichier temporaire');
}
} else {
- $sd = $record->get_subdefs();
-
- if (!$sd || !isset($sd[$subdef])) {
+ try {
+ $sd = $record->get_subdef($subdef);
+ } catch (\Exception_Media_SubdefNotFound $notFount) {
continue;
}
- $localfile = $sd[$subdef]->get_pathfile();
+ $localfile = $sd->getRealPath();
if (!file_exists($localfile)) {
throw new \Exception('Le fichier local n\'existe pas');
}
diff --git a/lib/Alchemy/Phrasea/TaskManager/Job/RecordMoverJob.php b/lib/Alchemy/Phrasea/TaskManager/Job/RecordMoverJob.php
index 71922028ba..8ac3f3064a 100644
--- a/lib/Alchemy/Phrasea/TaskManager/Job/RecordMoverJob.php
+++ b/lib/Alchemy/Phrasea/TaskManager/Job/RecordMoverJob.php
@@ -82,7 +82,7 @@ class RecordMoverJob extends AbstractJob
case 'UPDATE':
// change collection ?
if (array_key_exists('coll', $row)) {
- $coll = \collection::get_from_coll_id($app, $databox, $row['coll']);
+ $coll = \collection::getByCollectionId($app, $databox, $row['coll']);
$rec->move_to_collection($coll, $app['phraseanet.appbox']);
if ($logsql) {
$this->log('debug', sprintf("on sbas %s move rid %s to coll %s \n", $row['sbas_id'], $row['record_id'], $coll->get_coll_id()));
diff --git a/lib/Alchemy/Phrasea/TaskManager/Job/SubdefsJob.php b/lib/Alchemy/Phrasea/TaskManager/Job/SubdefsJob.php
index 01c66d6b14..22303c9111 100644
--- a/lib/Alchemy/Phrasea/TaskManager/Job/SubdefsJob.php
+++ b/lib/Alchemy/Phrasea/TaskManager/Job/SubdefsJob.php
@@ -1,9 +1,8 @@
translator->trans('task::subdef:creation des sous definitions');
}
- /**
- * {@inheritdoc}
- */
public function getJobId()
{
return 'Subdefs';
}
- /**
- * {@inheritdoc}
- */
public function getDescription()
{
return $this->translator->trans("task::subdef:creation des sous definitions des documents d'origine");
}
- /**
- * {@inheritdoc}
- */
public function getEditor()
{
return new SubdefsEditor($this->translator);
}
- /**
- * {@inheritdoc}
- */
protected function doJob(JobData $data)
{
$app = $data->getApplication();
$settings = simplexml_load_string($data->getTask()->getSettings());
- $thumbnailExtraction = (Boolean) (string) $settings->embedded;
+ $thumbnailExtraction = (bool) (string) $settings->embedded;
Image2Image::$lookForEmbeddedPreview = $thumbnailExtraction;
- $sqlqmark = array();
- $sqlparms = array();
- foreach(array('image',
- 'video',
- 'audio',
- 'document',
- 'flash',
- 'unknown') as $type) {
+ $documentTypes = [
+ 'image',
+ 'video',
+ 'audio',
+ 'document',
+ 'flash',
+ 'unknown'
+ ];
+
+ $sqlParameters = [];
+
+ foreach($documentTypes as $type) {
if (!isset($settings->{"type_" . $type}) || !\p4field::isno($settings->{"type_" . $type})) {
- $sqlqmark[] = '?';
- $sqlparms[] = $type;
+ $sqlParameters[] = $type;
}
}
- if(count($sqlqmark) == 0) {
+
+ if(empty($sqlParameters)) {
return;
}
@@ -90,16 +80,22 @@ class SubdefsJob extends AbstractJob
$conn = $databox->get_connection();
$sql = 'SELECT coll_id, record_id FROM record'
- . ' WHERE jeton & ' . PhraseaTokens::MAKE_SUBDEF . ' > 0'
- . ' AND type IN(' . implode(',', $sqlqmark) . ')'
+ . ' WHERE jeton & :token > 0 AND type IN(:types)'
. ' ORDER BY record_id DESC LIMIT 0, 30';
- $stmt = $conn->prepare($sql);
- $stmt->execute($sqlparms);
- $rs = $stmt->fetchAll(\PDO::FETCH_ASSOC);
- $stmt->closeCursor();
+ $resultSet = $conn->fetchAll(
+ $sql,
+ [
+ 'token' => PhraseaTokens::MAKE_SUBDEF,
+ 'types' => $sqlParameters,
+ ],
+ [
+ 'token' => \PDO::PARAM_INT,
+ 'types' => Connection::PARAM_STR_ARRAY,
+ ]
+ );
$i = 0;
- foreach ($rs as $row) {
+ foreach ($resultSet as $row) {
if (!$this->isStarted()) {
break;
}
@@ -107,9 +103,7 @@ class SubdefsJob extends AbstractJob
try {
$record = $databox->get_record($row['record_id']);
- /** @var SubdefGenerator $sg */
- $sg = $app['subdef.generator'];
- $sg->generateSubdefs($record);
+ $this->getSubdefGenerator($app)->generateSubdefs($record);
} catch (\Exception $e) {
$this->log('warning', sprintf("Generate subdefs failed for : sbasid=%s / databox=%s / recordid=%s : %s", $databox->get_sbas_id(), $databox->get_dbname() , $row['record_id'], $e->getMessage()));
}
@@ -119,24 +113,35 @@ class SubdefsJob extends AbstractJob
. ' SET jeton=(jeton & ~(:flag_and)) | :flag_or, moddate=NOW()'
. ' WHERE record_id=:record_id';
- $stmt = $conn->prepare($sql);
- $stmt->execute([
+ $conn->executeUpdate($sql, [
':record_id' => $row['record_id'],
':flag_and' => PhraseaTokens::MAKE_SUBDEF,
':flag_or' => (PhraseaTokens::WRITE_META_SUBDEF | PhraseaTokens::TO_INDEX)
]);
- $stmt->closeCursor();
unset($record);
$i++;
if ($i % 5 === 0) {
- $app['elasticsearch.indexer']->flushQueue();
+ $this->flushIndexerQueue($app);
}
}
}
- $app['elasticsearch.indexer']->flushQueue();
+ $this->flushIndexerQueue($app);
}
+ /**
+ * @param Application $app
+ * @return SubdefGenerator
+ */
+ private function getSubdefGenerator(Application $app)
+ {
+ return $app['subdef.generator'];
+ }
+
+ private function flushIndexerQueue(Application $app)
+ {
+ $app['elasticsearch.indexer']->flushQueue();
+ }
}
diff --git a/lib/Alchemy/Phrasea/TaskManager/Job/WebhookJob.php b/lib/Alchemy/Phrasea/TaskManager/Job/WebhookJob.php
index 38ac65d1e7..8616477469 100644
--- a/lib/Alchemy/Phrasea/TaskManager/Job/WebhookJob.php
+++ b/lib/Alchemy/Phrasea/TaskManager/Job/WebhookJob.php
@@ -121,7 +121,8 @@ class WebhookJob extends AbstractJob
)
));
- $eventFactory = new EventProcessorFactory($app);
+ /** @var EventProcessorFactory $eventFactory */
+ $eventFactory = $app['webhook.processor_factory'];
foreach ($app['repo.webhook-event']->findUnprocessedEvents() as $event) {
// set event as processed
diff --git a/lib/Alchemy/Phrasea/TaskManager/Job/WriteMetadataJob.php b/lib/Alchemy/Phrasea/TaskManager/Job/WriteMetadataJob.php
index 2165f2c5ea..1fab12b7a7 100644
--- a/lib/Alchemy/Phrasea/TaskManager/Job/WriteMetadataJob.php
+++ b/lib/Alchemy/Phrasea/TaskManager/Job/WriteMetadataJob.php
@@ -11,6 +11,7 @@
namespace Alchemy\Phrasea\TaskManager\Job;
+use Alchemy\Phrasea\Application;
use Alchemy\Phrasea\Core\PhraseaTokens;
use Alchemy\Phrasea\Metadata\TagFactory;
use Alchemy\Phrasea\TaskManager\Editor\WriteMetadataEditor;
@@ -20,6 +21,7 @@ use PHPExiftool\Driver\Tag;
use PHPExiftool\Exception\ExceptionInterface as PHPExiftoolException;
use PHPExiftool\Writer as ExifWriter;
use PHPExiftool\Exception\TagUnknown;
+use PHPExiftool\Writer;
class WriteMetadataJob extends AbstractJob
{
@@ -60,34 +62,17 @@ class WriteMetadataJob extends AbstractJob
*/
protected function doJob(JobData $data)
{
- $app = $data->getApplication();
$settings = simplexml_load_string($data->getTask()->getSettings());
$clearDoc = (Boolean) (string) $settings->cleardoc;
$MWG = (Boolean) (string) $settings->mwg;
- // move this in service provider configuration
- // $app['exiftool.writer']->setModule(Writer::MODULE_MWG, true);
+ foreach ($data->getApplication()->getDataboxes() as $databox) {
+ $connection = $databox->get_connection();
- foreach ($app->getDataboxes() as $databox) {
-
- $conn = $databox->get_connection();
- $metaSubdefs = [];
-
- foreach ($databox->get_subdef_structure() as $type => $definitions) {
- foreach ($definitions as $sub) {
- $name = $sub->get_name();
- if ($sub->meta_writeable()) {
- $metaSubdefs[$name . '_' . $type] = true;
- }
- }
- }
-
- $sql = 'SELECT record_id, coll_id, jeton FROM record WHERE (jeton & ' . PhraseaTokens::WRITE_META . ' > 0)';
-
- $stmt = $conn->prepare($sql);
- $stmt->execute();
- $rs = $stmt->fetchAll(\PDO::FETCH_ASSOC);
- $stmt->closeCursor();
+ $statement = $connection->prepare('SELECT record_id, coll_id, jeton FROM record WHERE (jeton & :token > 0)');
+ $statement->execute(['token' => PhraseaTokens::WRITE_META]);
+ $rs = $statement->fetchAll(\PDO::FETCH_ASSOC);
+ $statement->closeCursor();
foreach ($rs as $row) {
$record_id = $row['record_id'];
@@ -99,10 +84,10 @@ class WriteMetadataJob extends AbstractJob
$subdefs = [];
foreach ($record->get_subdefs() as $name => $subdef) {
$write_document = (($token & PhraseaTokens::WRITE_META_DOC) && $name == 'document');
- $write_subdef = (($token & PhraseaTokens::WRITE_META_SUBDEF) && isset($metaSubdefs[$name . '_' . $type]));
+ $write_subdef = (($token & PhraseaTokens::WRITE_META_SUBDEF) && $this->isSubdefMetadataUpdateRequired($databox, $type, $name));
if (($write_document || $write_subdef) && $subdef->is_physically_present()) {
- $subdefs[$name] = $subdef->get_pathfile();
+ $subdefs[$name] = $subdef->getRealPath();
}
}
@@ -179,16 +164,17 @@ class WriteMetadataJob extends AbstractJob
);
}
- $app['exiftool.writer']->reset();
+ $writer = $this->getMetadataWriter($data->getApplication());
+ $writer->reset();
if($MWG) {
- $app['exiftool.writer']->setModule(ExifWriter::MODULE_MWG, true);
+ $writer->setModule(ExifWriter::MODULE_MWG, true);
}
foreach ($subdefs as $name => $file) {
- $app['exiftool.writer']->erase($name != 'document' || $clearDoc, true);
+ $writer->erase($name != 'document' || $clearDoc, true);
try {
- $app['exiftool.writer']->write($file, $metadata);
+ $writer->write($file, $metadata);
$this->log('info',sprintf('meta written for sbasid=%1$d - recordid=%2$d (%3$s)', $databox->get_sbas_id(), $record_id, $name));
} catch (PHPExiftoolException $e) {
@@ -196,11 +182,33 @@ class WriteMetadataJob extends AbstractJob
}
}
- $sql = 'UPDATE record SET jeton=jeton & ~' . PhraseaTokens::WRITE_META . ' WHERE record_id = :record_id';
- $stmt = $conn->prepare($sql);
- $stmt->execute([':record_id' => $record_id]);
- $stmt->closeCursor();
+ $statement = $connection->prepare('UPDATE record SET jeton=jeton & ~:token WHERE record_id = :record_id');
+ $statement->execute([
+ 'record_id' => $record_id,
+ 'token' => PhraseaTokens::WRITE_META,
+ ]);
+ $statement->closeCursor();
}
}
}
+
+ /**
+ * @param Application $app
+ * @return Writer
+ */
+ private function getMetadataWriter(Application $app)
+ {
+ return $app['exiftool.writer'];
+ }
+
+ /**
+ * @param \databox $databox
+ * @param string $subdefType
+ * @param string $subdefName
+ * @return bool
+ */
+ private function isSubdefMetadataUpdateRequired(\databox $databox, $subdefType, $subdefName)
+ {
+ return $databox->get_subdef_structure()->get_subdef($subdefType, $subdefName)->isMetadataUpdateRequired();
+ }
}
diff --git a/lib/Alchemy/Phrasea/Twig/PhraseanetExtension.php b/lib/Alchemy/Phrasea/Twig/PhraseanetExtension.php
index 57f0f83a3a..0424b09a72 100644
--- a/lib/Alchemy/Phrasea/Twig/PhraseanetExtension.php
+++ b/lib/Alchemy/Phrasea/Twig/PhraseanetExtension.php
@@ -42,10 +42,35 @@ class PhraseanetExtension extends \Twig_Extension
new \Twig_SimpleFunction('record_flags', array($this, 'getRecordFlags')),
new \Twig_SimpleFunction('border_checker_from_fqcn', array($this, 'getCheckerFromFQCN')),
new \Twig_SimpleFunction('caption_field', array($this, 'getCaptionField')),
+ new \Twig_SimpleFunction('caption_field_label', array($this, 'getCaptionFieldLabel')),
new \Twig_SimpleFunction('caption_field_order', array($this, 'getCaptionFieldOrder')),
);
}
+ /**
+ * get localized field's label
+ * @param RecordInterface $record
+ * @param $fieldName
+ * @return string - the name label
+ */
+ public function getCaptionFieldLabel(RecordInterface $record, $fieldName)
+ {
+ if ($record) {
+ /** @var \appbox $appbox */
+ $appbox = $this->app['phraseanet.appbox'];
+ $databox = $appbox->get_databox($record->getDataboxId());
+
+ foreach ($databox->get_meta_structure() as $meta) {
+ /** @var \databox_field $meta */
+ if ($meta->get_name() === $fieldName) {
+ return $meta->get_label($this->app['locale']);
+ }
+ }
+ }
+
+ return '';
+ }
+
public function getCaptionField(RecordInterface $record, $field, $value)
{
if ($record instanceof ElasticsearchRecord) {
@@ -248,7 +273,7 @@ class PhraseanetExtension extends \Twig_Extension
}
} elseif ($record instanceof \record_adapter) {
if (null !== $thumbnail = $record->get_subdef($subdefName)) {
- if ('' !== $path = $thumbnail->get_pathfile()) {
+ if ('' !== $path = $thumbnail->getRealPath()) {
$etag = $thumbnail->getEtag();
return $staticMode->getUrl($path, $etag);
}
diff --git a/lib/Alchemy/Phrasea/Webhook/EventProcessorFactory.php b/lib/Alchemy/Phrasea/Webhook/EventProcessorFactory.php
index cbb3de6c44..0eec23ed16 100644
--- a/lib/Alchemy/Phrasea/Webhook/EventProcessorFactory.php
+++ b/lib/Alchemy/Phrasea/Webhook/EventProcessorFactory.php
@@ -2,33 +2,68 @@
namespace Alchemy\Phrasea\Webhook;
+use Alchemy\Phrasea\Exception\InvalidArgumentException;
use Alchemy\Phrasea\Model\Entities\WebhookEvent;
use Alchemy\Phrasea\Application;
-use Alchemy\Phrasea\Webhook\Processor\FeedEntryProcessor;
-use Alchemy\Phrasea\Webhook\Processor\UserRegistrationProcessor;
+use Alchemy\Phrasea\Webhook\Processor\CallableProcessorFactory;
+use Alchemy\Phrasea\Webhook\Processor\FeedEntryProcessorFactory;
+use Alchemy\Phrasea\Webhook\Processor\ProcessorFactory;
+use Alchemy\Phrasea\Webhook\Processor\UserRegistrationProcessorFactory;
class EventProcessorFactory
{
- private $app;
+ /**
+ * @var ProcessorFactory
+ */
+ private $processorFactories = [];
+
+ /**
+ * @param Application $app
+ */
public function __construct(Application $app)
{
- $this->app = $app;
+ $this->registerFactory(WebhookEvent::FEED_ENTRY_TYPE, new FeedEntryProcessorFactory($app));
+ $this->registerFactory(WebhookEvent::USER_REGISTRATION_TYPE, new UserRegistrationProcessorFactory($app));
}
+ /**
+ * @param string $eventType
+ * @param ProcessorFactory $processorFactory
+ */
+ public function registerFactory($eventType, ProcessorFactory $processorFactory)
+ {
+ $this->processorFactories[$eventType] = $processorFactory;
+ }
+
+ /**
+ * @param string $eventType
+ * @param callback|callable $callable
+ */
+ public function registerCallableFactory($eventType, $callable)
+ {
+ if (! is_callable($callable)) {
+ throw new InvalidArgumentException(sprintf(
+ 'Expected a callable, got "%s" instead',
+ is_object($callable) ? get_class($callable) : gettype($callable)
+ ));
+ }
+
+ $this->processorFactories[$eventType] = new CallableProcessorFactory($callable);
+ }
+
+ /**
+ * @param WebhookEvent $event
+ * @return Processor\ProcessorInterface
+ */
public function get(WebhookEvent $event)
{
- switch ($event->getType()) {
- case WebhookEvent::FEED_ENTRY_TYPE:
- return new FeedEntryProcessor(
- $this->app,
- $this->app['repo.feed-entries'],
- $this->app['phraseanet.user-query']
- );
- case WebhookEvent::USER_REGISTRATION_TYPE:
- return new UserRegistrationProcessor($this->app['repo.users']);
- default:
- throw new \RuntimeException(sprintf('No processor found for %s', $event->getType()));
+ if (! isset($this->processorFactories[$event->getType()])) {
+ throw new \RuntimeException(sprintf('No processor found for %s', $event->getType()));
}
+
+ $factory = $this->processorFactories[$event->getType()];
+
+ return $factory->createProcessor();
}
}
diff --git a/lib/Alchemy/Phrasea/Webhook/Processor/CallableProcessorFactor.php b/lib/Alchemy/Phrasea/Webhook/Processor/CallableProcessorFactor.php
new file mode 100644
index 0000000000..bc57c7c41d
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Webhook/Processor/CallableProcessorFactor.php
@@ -0,0 +1,21 @@
+factoryMethod = $callable;
+ }
+
+ public function createProcessor()
+ {
+ $factoryMethod = $this->factoryMethod;
+
+ return $factoryMethod();
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Webhook/Processor/FeedEntryProcessorFactory.php b/lib/Alchemy/Phrasea/Webhook/Processor/FeedEntryProcessorFactory.php
new file mode 100644
index 0000000000..69b2b1bf94
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Webhook/Processor/FeedEntryProcessorFactory.php
@@ -0,0 +1,27 @@
+app = $application;
+ }
+
+ public function createProcessor()
+ {
+ return new FeedEntryProcessor(
+ $this->app,
+ $this->app['repo.feed-entries'],
+ $this->app['phraseanet.user-query']
+ );
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Webhook/Processor/ProcessorFactory.php b/lib/Alchemy/Phrasea/Webhook/Processor/ProcessorFactory.php
new file mode 100644
index 0000000000..cdb3e90fd2
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Webhook/Processor/ProcessorFactory.php
@@ -0,0 +1,11 @@
+app = $application;
+ }
+
+ /**
+ * @return UserRegistrationProcessor
+ */
+ public function createProcessor()
+ {
+ return new UserRegistrationProcessor($this->app['repo.users']);
+ }
+}
diff --git a/lib/classes/ACL.php b/lib/classes/ACL.php
index 65b805daff..b28c134af6 100644
--- a/lib/classes/ACL.php
+++ b/lib/classes/ACL.php
@@ -730,11 +730,7 @@ class ACL implements cache_cacheableInterface
continue;
}
- try {
- $ret[$base_id] = collection::get_from_base_id($this->app, $base_id);
- } catch (\Exception $e) {
-
- }
+ $ret[$base_id] = $collection;
}
}
@@ -1780,7 +1776,7 @@ class ACL implements cache_cacheableInterface
$collections = [];
foreach ($rs as $row) {
- $collections[] = \collection::get_from_base_id($this->app, $row['base_id']);
+ $collections[] = \collection::getByBaseId($this->app, $row['base_id']);
}
return $collections;
diff --git a/lib/classes/Bridge/Api.php b/lib/classes/Bridge/Api.php
index dcd0886652..fa7d4553c9 100644
--- a/lib/classes/Bridge/Api.php
+++ b/lib/classes/Bridge/Api.php
@@ -99,16 +99,16 @@ class Bridge_Api
}
/**
- *
- * @return boolean
+ * @param null|DateTime $checkDate
+ * @return bool
*/
- public function is_disabled()
+ public function is_disabled(DateTime $checkDate = null)
{
if ($this->disable_time === null) {
return false;
}
- $date_obj = new DateTime();
+ $date_obj = $checkDate ?: new DateTime();
if ($date_obj > $this->disable_time) {
$this->enable();
diff --git a/lib/classes/Bridge/Element.php b/lib/classes/Bridge/Element.php
index cdf6673f21..84f0184446 100644
--- a/lib/classes/Bridge/Element.php
+++ b/lib/classes/Bridge/Element.php
@@ -14,85 +14,71 @@ use Alchemy\Phrasea\Application;
class Bridge_Element
{
/**
- *
* @var appbox
*/
protected $app;
/**
- *
* @var account
*/
protected $account;
/**
- *
* @var int
*/
protected $id;
/**
- *
* @var record_adapter
*/
protected $record;
/**
- *
* @var string
*/
protected $dist_id;
/**
- *
* @var string
*/
protected $status;
/**
- *
* @var string
*/
protected $connector_status;
/**
- *
* @var string
*/
protected $title;
/**
- *
* @var string
*/
protected $type;
/**
- *
* @var array
*/
protected $datas;
/**
- *
* @var DateTime
*/
protected $created_on;
/**
- *
* @var DateTime
*/
protected $uploaded_on;
/**
- *
* @var DateTime
*/
protected $updated_on;
/**
- *
* @var Bridge_Api_ElementInterface
*/
protected $connector_element;
@@ -410,7 +396,6 @@ class Bridge_Element
}
/**
- *
* @return DateTime
*/
public function get_updated_on()
diff --git a/lib/classes/appbox.php b/lib/classes/appbox.php
index 8d01767ed8..6309532b82 100644
--- a/lib/classes/appbox.php
+++ b/lib/classes/appbox.php
@@ -10,13 +10,14 @@
*/
use Alchemy\Phrasea\Application;
+use Alchemy\Phrasea\Collection\CollectionService;
use Alchemy\Phrasea\Core\Configuration\AccessRestriction;
use Alchemy\Phrasea\Core\Connection\ConnectionSettings;
use Alchemy\Phrasea\Core\Version\AppboxVersionRepository;
+use Alchemy\Phrasea\Databox\DataboxConnectionProvider;
use Alchemy\Phrasea\Databox\DataboxRepository;
use Doctrine\ORM\Tools\SchemaTool;
use MediaAlchemyst\Alchemyst;
-use MediaAlchemyst\Specification\Image as ImageSpecification;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\HttpFoundation\File\File as SymfoFile;
use Symfony\Component\Finder\Finder;
@@ -42,6 +43,10 @@ class appbox extends base
* @var \databox[]
*/
protected $databoxes;
+ /**
+ * @var CollectionService
+ */
+ protected $collectionService;
public function __construct(Application $app)
{
@@ -233,11 +238,11 @@ class appbox extends base
protected function post_upgrade(Application $app)
{
- $this->apply_patches($this->get_version(), $app['phraseanet.version']->getNumber(), true, $app);
+ $this->apply_patches($this->get_version(), $app['phraseanet.version']->getNumber(), true);
$this->setVersion($app['phraseanet.version']);
foreach ($this->get_databoxes() as $databox) {
- $databox->apply_patches($databox->get_version(), $app['phraseanet.version']->getNumber(), true, $app);
+ $databox->apply_patches($databox->get_version(), $app['phraseanet.version']->getNumber(), true);
$databox->setVersion($app['phraseanet.version']);
}
@@ -258,12 +263,12 @@ class appbox extends base
}
/**
- * @param $sbas_id
+ * @param int $sbas_id
* @return databox
*/
public function get_databox($sbas_id)
{
- $databoxes = $this->get_databoxes();
+ $databoxes = $this->getDataboxRepository()->findAll();
if (!isset($databoxes[$sbas_id]) && !array_key_exists($sbas_id, $databoxes)) {
throw new NotFoundHttpException('Databox `' . $sbas_id . '` not found');
@@ -314,10 +319,23 @@ class appbox extends base
parent::delete_data_from_cache($option);
}
+ public function getCollectionService()
+ {
+ if ($this->collectionService === null) {
+ $this->collectionService = new CollectionService(
+ $this->app,
+ $this->connection,
+ new DataboxConnectionProvider($this)
+ );
+ }
+
+ return $this->collectionService;
+ }
+
/**
* @return AccessRestriction
*/
- private function getAccessRestriction()
+ public function getAccessRestriction()
{
return $this->app['conf.restrictions'];
}
diff --git a/lib/classes/base.php b/lib/classes/base.php
index 618e8d1325..4ffe971e60 100644
--- a/lib/classes/base.php
+++ b/lib/classes/base.php
@@ -263,10 +263,10 @@ abstract class base implements cache_cacheableInterface
return $this;
}
- public function apply_patches($from, $to, $post_process, Application $app)
+ public function apply_patches($from, $to, $post_process)
{
$service = new DatabaseMaintenanceService($this->app, $this->connection);
- return $service->applyPatches($this, $from, $to, $post_process, $app);
+ return $service->applyPatches($this, $from, $to, $post_process);
}
}
diff --git a/lib/classes/caption/Field/Value.php b/lib/classes/caption/Field/Value.php
index 28e259d713..d216de6f78 100644
--- a/lib/classes/caption/Field/Value.php
+++ b/lib/classes/caption/Field/Value.php
@@ -15,6 +15,9 @@ use Alchemy\Phrasea\Vocabulary;
class caption_Field_Value implements cache_cacheableInterface
{
+ const RETRIEVE_VALUES = true;
+ const DONT_RETRIEVE_VALUES = false;
+
/** @var int */
protected $id;
@@ -55,16 +58,19 @@ class caption_Field_Value implements cache_cacheableInterface
* @param databox_field $databox_field
* @param record_adapter $record
* @param mixed $id
+ * @param bool $retrieveValues
* @return \caption_Field_Value
*/
- public function __construct(Application $app, databox_field $databox_field, record_adapter $record, $id)
+ public function __construct(Application $app, databox_field $databox_field, record_adapter $record, $id, $retrieveValues = self::RETRIEVE_VALUES)
{
$this->id = (int) $id;
$this->databox_field = $databox_field;
$this->record = $record;
$this->app = $app;
- $this->retrieveValues();
+ if($retrieveValues == self::RETRIEVE_VALUES) {
+ $this->retrieveValues();
+ }
}
public function getQjs()
@@ -72,6 +78,53 @@ class caption_Field_Value implements cache_cacheableInterface
return $this->qjs;
}
+ public function injectValues($value, $VocabularyType, $VocabularyId)
+ {
+ $this->value = StringHelper::crlfNormalize($value);
+
+ try {
+ $this->VocabularyType = $VocabularyType ? Vocabulary\Controller::get($this->app, $VocabularyType) : null;
+ $this->VocabularyId = $VocabularyId;
+ } catch (\InvalidArgumentException $e) {
+
+ }
+
+ if ($this->VocabularyType) {
+ /**
+ * Vocabulary Control has been deactivated
+ */
+ if ( ! $this->databox_field->getVocabularyControl()) {
+ $this->removeVocabulary();
+ }
+ /**
+ * Vocabulary Control has changed
+ */
+ elseif ($this->databox_field->getVocabularyControl()->getType() !== $this->VocabularyType->getType()) {
+ $this->removeVocabulary();
+ }
+ /**
+ * Current Id is not available anymore
+ */
+ elseif ( ! $this->VocabularyType->validate($this->VocabularyId)) {
+ $this->removeVocabulary();
+ }
+ /**
+ * String equivalence has changed
+ */
+ elseif ($this->VocabularyType->getValue($this->VocabularyId) !== $this->value) {
+ $this->set_value($this->VocabularyType->getValue($this->VocabularyId));
+ }
+ }
+
+ $datas = [
+ 'value' => $this->value,
+ 'vocabularyId' => $this->VocabularyId,
+ 'vocabularyType' => $this->VocabularyType ? $this->VocabularyType->getType() : null,
+ ];
+
+ $this->set_data_to_cache($datas);
+ }
+
protected function retrieveValues()
{
try {
@@ -95,47 +148,13 @@ class caption_Field_Value implements cache_cacheableInterface
$row = $stmt->fetch(PDO::FETCH_ASSOC);
$stmt->closeCursor();
- $this->value = $row ? StringHelper::crlfNormalize($row['value']) : null;
-
- try {
- $this->VocabularyType = $row['VocabularyType'] ? Vocabulary\Controller::get($this->app, $row['VocabularyType']) : null;
- $this->VocabularyId = $row['VocabularyId'];
- } catch (\InvalidArgumentException $e) {
-
+ if($row) {
+ $this->injectValues($row['value'], $row['VocabularyType'], $row['VocabularyId']);
}
-
- if ($this->VocabularyType) {
- /**
- * Vocabulary Control has been deactivated
- */
- if ( ! $this->databox_field->getVocabularyControl()) {
- $this->removeVocabulary();
- }
- /**
- * Vocabulary Control has changed
- */ elseif ($this->databox_field->getVocabularyControl()->getType() !== $this->VocabularyType->getType()) {
- $this->removeVocabulary();
- }
- /**
- * Current Id is not available anymore
- */ elseif ( ! $this->VocabularyType->validate($this->VocabularyId)) {
- $this->removeVocabulary();
- }
- /**
- * String equivalence has changed
- */ elseif ($this->VocabularyType->getValue($this->VocabularyId) !== $this->value) {
- $this->set_value($this->VocabularyType->getValue($this->VocabularyId));
- }
+ else {
+ $this->injectValues(null, null, null);
}
- $datas = [
- 'value' => $this->value,
- 'vocabularyId' => $this->VocabularyId,
- 'vocabularyType' => $this->VocabularyType ? $this->VocabularyType->getType() : null,
- ];
-
- $this->set_data_to_cache($datas);
-
return $this;
}
diff --git a/lib/classes/caption/field.php b/lib/classes/caption/field.php
index fdd2bd658d..ecd4ac9bf2 100644
--- a/lib/classes/caption/field.php
+++ b/lib/classes/caption/field.php
@@ -14,6 +14,9 @@ use Doctrine\DBAL\Driver\Statement;
class caption_field implements cache_cacheableInterface
{
+ const RETRIEVE_VALUES = true;
+ const DONT_RETRIEVE_VALUES = false;
+
/**
* @var databox_field
*/
@@ -33,33 +36,44 @@ class caption_field implements cache_cacheableInterface
protected static $localCache = [];
/**
- * @param Application $app
- * @param databox_field $databox_field
+ * @param Application $app
+ * @param databox_field $databox_field
* @param record_adapter $record
+ * @param bool $retrieveValues
*
* @return caption_field
*/
- public function __construct(Application $app, databox_field $databox_field, \record_adapter $record)
+ public function __construct(Application $app, databox_field $databox_field, \record_adapter $record, $retrieveValues = self::RETRIEVE_VALUES)
{
$this->app = $app;
$this->record = $record;
$this->databox_field = $databox_field;
$this->values = [];
- $rs = $this->get_metadatas_ids();
+ if($retrieveValues == self::RETRIEVE_VALUES) {
+ $rs = $this->get_metadatas_ids();
- foreach ($rs as $row) {
- $this->values[$row['id']] = new caption_Field_Value($this->app, $databox_field, $record, $row['id']);
+ foreach ($rs as $row) {
+ $this->values[$row['id']] = new caption_Field_Value($this->app, $databox_field, $record, $row['id']);
- // Inconsistent, should not happen
- if ( ! $databox_field->is_multi()) {
- break;
+ // Inconsistent, should not happen
+ if (!$databox_field->is_multi()) {
+ break;
+ }
}
}
return $this;
}
+ /**
+ * @param caption_Field_Value[] $values
+ */
+ public function injectValues($values)
+ {
+ $this->values = $values;
+ }
+
protected function get_metadatas_ids()
{
try {
@@ -73,7 +87,7 @@ class caption_field implements cache_cacheableInterface
$sql = 'SELECT id FROM metadatas WHERE record_id = :record_id AND meta_struct_id = :meta_struct_id';
$params = [
- ':record_id' => $this->record->get_record_id()
+ ':record_id' => $this->record->getRecordId()
, ':meta_struct_id' => $this->databox_field->get_id()
];
diff --git a/lib/classes/caption/record.php b/lib/classes/caption/record.php
index 6dfeac7f92..740e21ee2b 100644
--- a/lib/classes/caption/record.php
+++ b/lib/classes/caption/record.php
@@ -47,7 +47,7 @@ class caption_record implements caption_interface, cache_cacheableInterface
public function __construct(Application $app, \record_adapter $record, databox $databox)
{
$this->app = $app;
- $this->sbas_id = $record->get_sbas_id();
+ $this->sbas_id = $record->getDataboxId();
$this->record = $record;
$this->databox = $databox;
}
@@ -78,12 +78,12 @@ class caption_record implements caption_interface, cache_cacheableInterface
try {
$fields = $this->get_data_from_cache();
} catch (\Exception $e) {
- $sql = "SELECT m.id as meta_id, s.id as structure_id
- FROM metadatas m, metadatas_structure s
- WHERE m.record_id = :record_id AND s.id = m.meta_struct_id
- ORDER BY s.sorter ASC";
+ $sql = "SELECT m.id AS meta_id, s.id AS structure_id, value, VocabularyType, VocabularyId"
+ . " FROM metadatas m, metadatas_structure s"
+ . " WHERE m.record_id = :record_id AND s.id = m.meta_struct_id"
+ . " ORDER BY s.sorter ASC";
$stmt = $this->databox->get_connection()->prepare($sql);
- $stmt->execute([':record_id' => $this->record->get_record_id()]);
+ $stmt->execute([':record_id' => $this->record->getRecordId()]);
$fields = $stmt->fetchAll(PDO::FETCH_ASSOC);
$stmt->closeCursor();
if ($fields) {
@@ -95,11 +95,55 @@ class caption_record implements caption_interface, cache_cacheableInterface
if ($fields) {
$databox_descriptionStructure = $this->databox->get_meta_structure();
- foreach ($fields as $row) {
- $databox_field = $databox_descriptionStructure->get_element($row['structure_id']);
- $metadata = new caption_field($this->app, $databox_field, $this->record);
- $rec_fields[$databox_field->get_id()] = $metadata;
+ // first group values by field
+ $caption_fields = [];
+ foreach ($fields as $row) {
+ $structure_id = $row['structure_id'];
+ if(!array_key_exists($structure_id, $caption_fields)) {
+ $caption_fields[$structure_id] = [
+ 'db_field' => $databox_descriptionStructure->get_element($structure_id),
+ 'values' => []
+ ];
+ }
+
+ if (count($caption_fields[$structure_id]['values']) > 0 && !$caption_fields[$structure_id]['db_field']->is_multi()) {
+ // Inconsistent, should not happen
+ continue;
+ }
+
+ // build an EMPTY caption_Field_Value
+ $cfv = new caption_Field_Value(
+ $this->app,
+ $caption_fields[$structure_id]['db_field'],
+ $this->record,
+ $row['meta_id'],
+ caption_Field_Value::DONT_RETRIEVE_VALUES // ask caption_Field_Value "no n+1 sql"
+ );
+
+ // inject the value we already know
+ $cfv->injectValues($row['value'], $row['VocabularyType'], $row['VocabularyId']);
+
+ // add the value to the field
+ $caption_fields[$structure_id]['values'][] = $cfv;
+ }
+
+ // now build a "caption_field" with already known "caption_Field_Value"s
+ foreach($caption_fields as $structure_id => $caption_field) {
+
+ // build an EMPTY caption_field
+ $cf = new caption_field(
+ $this->app,
+ $caption_field['db_field'],
+ $this->record,
+ caption_field::DONT_RETRIEVE_VALUES // ask caption_field "no n+1 sql"
+ );
+
+ // inject the value we already know
+ $cf->injectValues($caption_field['values']);
+
+ // add the field to the fields
+ $rec_fields[$structure_id] = $cf;
}
}
$this->fields = $rec_fields;
@@ -248,7 +292,7 @@ class caption_record implements caption_interface, cache_cacheableInterface
*/
public function get_data_from_cache($option = null)
{
- return $this->record->get_databox()->get_data_from_cache($this->get_cache_key($option));
+ return $this->record->getDatabox()->get_data_from_cache($this->get_cache_key($option));
}
/**
@@ -261,7 +305,7 @@ class caption_record implements caption_interface, cache_cacheableInterface
*/
public function set_data_to_cache($value, $option = null, $duration = 0)
{
- return $this->record->get_databox()->set_data_to_cache($value, $this->get_cache_key($option), $duration);
+ return $this->record->getDatabox()->set_data_to_cache($value, $this->get_cache_key($option), $duration);
}
/**
@@ -274,6 +318,6 @@ class caption_record implements caption_interface, cache_cacheableInterface
{
$this->fields = null;
- return $this->record->get_databox()->delete_data_from_cache($this->get_cache_key($option));
+ return $this->record->getDatabox()->delete_data_from_cache($this->get_cache_key($option));
}
}
diff --git a/lib/classes/collection.php b/lib/classes/collection.php
index c37e5de801..7ac76177bb 100644
--- a/lib/classes/collection.php
+++ b/lib/classes/collection.php
@@ -10,11 +10,15 @@
*/
use Alchemy\Phrasea\Application;
+use Alchemy\Phrasea\Collection\Collection as CollectionVO;
+use Alchemy\Phrasea\Collection\CollectionRepository;
+use Alchemy\Phrasea\Collection\CollectionRepositoryRegistry;
+use Alchemy\Phrasea\Collection\CollectionService;
+use Alchemy\Phrasea\Collection\Reference\CollectionReference;
+use Alchemy\Phrasea\Collection\Reference\CollectionReferenceRepository;
use Alchemy\Phrasea\Core\Thumbnail\ThumbnailedElement;
use Alchemy\Phrasea\Core\Thumbnail\ThumbnailManager;
-use Alchemy\Phrasea\Exception\InvalidArgumentException;
use Alchemy\Phrasea\Model\Entities\User;
-use Doctrine\DBAL\Driver\Connection;
use Symfony\Component\HttpFoundation\File\File;
use Alchemy\Phrasea\Core\Event\Collection\CollectionEvent;
@@ -29,205 +33,347 @@ use Alchemy\Phrasea\Core\Event\Collection\UnmountedEvent;
use Alchemy\Phrasea\Core\Event\Collection\SettingsChangedEvent;
use Alchemy\Phrasea\Core\Event\Collection\LabelChangedEvent;
-class collection implements cache_cacheableInterface, ThumbnailedElement
+class collection implements ThumbnailedElement, cache_cacheableInterface
{
- protected $base_id;
- protected $sbas_id;
- protected $coll_id;
- protected $available = false;
- protected $name;
- protected $prefs;
- protected $pub_wm;
- protected $labels = [];
- private static $_logos = [];
- private static $_stamps = [];
- private static $_watermarks = [];
- private static $_presentations = [];
- private static $_collections = [];
- protected $databox;
- protected $is_active;
- protected $binary_logo;
- protected $ord;
- protected $app;
const PIC_LOGO = 'minilogos';
const PIC_WM = 'wm';
const PIC_STAMP = 'stamp';
const PIC_PRESENTATION = 'presentation';
- protected function __construct(Application $app, $coll_id, databox $databox)
- {
- $this->app = $app;
- $this->databox = $databox;
- $this->sbas_id = (int) $databox->get_sbas_id();
- $this->coll_id = (int) $coll_id;
- $this->load();
+ private static $_logos = [];
+ private static $_stamps = [];
+ private static $_watermarks = [];
+ private static $_presentations = [];
- return $this;
+ /**
+ * @param Application $app
+ * @param $databoxId
+ * @return CollectionRepository
+ */
+ private static function getRepository(Application $app, $databoxId)
+ {
+ /** @var CollectionRepositoryRegistry $registry */
+ $registry = $app['repo.collections-registry'];
+
+ return $registry->getRepositoryByDatabox($databoxId);
}
+ public static function create(Application $app, databox $databox, appbox $appbox, $name, User $user = null)
+ {
+ $databoxId = $databox->get_sbas_id();
+
+ $repository = self::getRepository($app, $databoxId);
+ $collection = new CollectionVO($databoxId, 0, $name);
+
+ $repository->save($collection);
+
+ $repository = $app['repo.collection-references'];
+ $collectionReference = new CollectionReference(0, $databoxId, $collection->getCollectionId(), 0, true, '');
+
+ $repository->save($collectionReference);
+
+ $app['repo.collections-registry']->purgeRegistry();
+
+ $collection = new self($app, $collection, $collectionReference);
+
+ if (null !== $user) {
+ $collection->collectionService->grantAdminRights($collectionReference, $user);
+ }
+
+ $app['dispatcher']->dispatch(
+ CollectionEvents::CREATED,
+ new CreatedEvent(
+ $collection
+ )
+ );
+
+ return $collection;
+ }
+
+ public static function mount_collection(Application $app, databox $databox, $coll_id, User $user)
+ {
+ $reference = new CollectionReference(0, $databox->get_sbas_id(), $coll_id, 0, true, '');
+
+ $app['repo.collection-references']->save($reference);
+ $app['repo.collections-registry']->purgeRegistry();
+
+ $collection = self::getByBaseId($app, $reference->getBaseId());
+ $collection->collectionService->grantAdminRights($collection->reference, $user);
+
+ $app['dispatcher']->dispatch(
+ CollectionEvents::MOUNTED,
+ new MountedEvent(
+ $collection
+ )
+ );
+
+ return $reference->getBaseId();
+ }
+
+ public static function getLogo($base_id, Application $app, $printname = false)
+ {
+ $base_id_key = $base_id . '_' . ($printname ? '1' : '0');
+
+ if (!isset(self::$_logos[$base_id_key])) {
+
+ if (is_file($app['root.path'] . '/config/minilogos/' . $base_id)) {
+ $name = phrasea::bas_labels($base_id, $app);
+ self::$_logos[$base_id_key] = '
';
+ } elseif ($printname) {
+ self::$_logos[$base_id_key] = phrasea::bas_labels($base_id, $app);
+ }
+ }
+
+ return isset(self::$_logos[$base_id_key]) ? self::$_logos[$base_id_key] : '';
+ }
+
+ public static function getWatermark($base_id)
+ {
+ if (!isset(self::$_watermarks['base_id'])) {
+
+ if (is_file(__DIR__ . '/../../config/wm/' . $base_id)) {
+ self::$_watermarks['base_id'] = '
';
+ }
+ }
+
+ return isset(self::$_watermarks['base_id']) ? self::$_watermarks['base_id'] : '';
+ }
+
+ public static function getPresentation($base_id)
+ {
+ if (!isset(self::$_presentations['base_id'])) {
+
+ if (is_file(__DIR__ . '/../../config/presentation/' . $base_id)) {
+ self::$_presentations['base_id'] = '
';
+ }
+ }
+
+ return isset(self::$_presentations['base_id']) ? self::$_presentations['base_id'] : '';
+ }
+
+ public static function getStamp($base_id)
+ {
+ if (!isset(self::$_stamps['base_id'])) {
+
+ if (is_file(__DIR__ . '/../../config/stamp/' . $base_id)) {
+ self::$_stamps['base_id'] = '
';
+ }
+ }
+
+ return isset(self::$_stamps['base_id']) ? self::$_stamps['base_id'] : '';
+ }
+
+ public static function purge()
+ {
+ // BC only
+ }
+
+ /**
+ * @param Application $app
+ * @param int $base_id
+ * @return collection
+ */
+ public static function getByBaseId(Application $app, $base_id)
+ {
+ /** @var CollectionReferenceRepository $referenceRepository */
+ $referenceRepository = $app['repo.collection-references'];
+ $reference = $referenceRepository->find($base_id);
+
+ if (!$reference) {
+ throw new Exception_Databox_CollectionNotFound(sprintf(
+ "Collection with base_id %s could not be found",
+ $base_id
+ ));
+ }
+
+ return self::getAvailableCollection($app, $reference->getDataboxId(), $reference->getCollectionId());
+ }
+
+ /**
+ * @param Application $app
+ * @param databox $databox
+ * @param int $collectionId
+ * @return collection
+ */
+ public static function getByCollectionId(Application $app, databox $databox, $collectionId)
+ {
+ assert(is_int($collectionId));
+
+ return self::getAvailableCollection($app, $databox->get_sbas_id(), $collectionId);
+ }
+
+ /**
+ * @param Application $app
+ * @return \Alchemy\Phrasea\Core\Configuration\AccessRestriction
+ */
+ private static function getAccessRestriction(Application $app)
+ {
+ return $app['conf.restrictions'];
+ }
+
+ private static function assertCollectionIsAvailable(Application $app, collection $collection)
+ {
+ if (!self::getAccessRestriction($app)->isCollectionAvailable($collection)) {
+ throw new Exception_Databox_CollectionNotFound(sprintf(
+ 'Collection `%d` is not available here.',
+ $collection->get_base_id()
+ ));
+ }
+ }
+
+ /**
+ * @param Application $app
+ * @param int $databoxId
+ * @param int $collectionId
+ * @return collection
+ */
+ private static function getByDataboxIdAndCollectionId(Application $app, $databoxId, $collectionId)
+ {
+ $repository = self::getRepository($app, $databoxId);
+ $collection = $repository->find($collectionId);
+
+ if (!$collection) {
+ throw new Exception_Databox_CollectionNotFound(sprintf(
+ "Collection '%d' on databox '%d' could not be found",
+ $collectionId,
+ $databoxId
+ ));
+ }
+
+ return $collection;
+ }
+
+ /**
+ * @param Application $app
+ * @param int $databoxId
+ * @param int $collectionId
+ * @return collection
+ */
+ private static function getAvailableCollection(Application $app, $databoxId, $collectionId)
+ {
+ $collection = self::getByDataboxIdAndCollectionId($app, $databoxId, $collectionId);
+ self::assertCollectionIsAvailable($app, $collection);
+
+ return $collection;
+ }
+
+ /**
+ * @var Application
+ */
+ protected $app;
+
+ /**
+ * @var CollectionService
+ */
+ protected $collectionService;
+
+ /**
+ * @var databox
+ */
+ protected $databox;
+
+ /**
+ * @var CollectionVO
+ */
+ protected $collectionVO;
+
+ /**
+ * @var CollectionRepositoryRegistry
+ */
+ protected $collectionRepositoryRegistry;
+
+ /**
+ * @var CollectionReference
+ */
+ protected $reference;
+
+
+ /**
+ * @param Application $app
+ * @param CollectionVO $collection
+ * @param CollectionReference $reference
+ * @internal param $baseId
+ * @internal param array $row
+ */
+ public function __construct(Application $app, CollectionVO $collection, CollectionReference $reference)
+ {
+ $this->collectionVO = $collection;
+ $this->reference = $reference;
+
+ $this->fetchInternalServices($app);
+ }
+
+ /**
+ * @param $eventName
+ * @param CollectionEvent $event
+ */
private function dispatch($eventName, CollectionEvent $event)
{
$this->app['dispatcher']->dispatch($eventName, $event);
}
- protected function load()
+ /**
+ * @return CollectionRepository
+ */
+ private function getCollectionRepository()
{
- try {
- $datas = $this->get_data_from_cache();
- if (!is_array($datas)) {
- throw new \Exception('Collection could not be retrieved from cache');
- }
- $this->is_active = $datas['is_active'];
- $this->base_id = $datas['base_id'];
- $this->available = $datas['available'];
- $this->pub_wm = $datas['pub_wm'];
- $this->name = $datas['name'];
- $this->ord = $datas['ord'];
- $this->prefs = $datas['prefs'];
- $this->labels = $datas['labels'];
+ return self::getRepository($this->app, $this->reference->getDataboxId());
+ }
- return $this;
- } catch (\Exception $e) {
+ /**
+ * @return CollectionReferenceRepository
+ */
+ private function getReferenceRepository()
+ {
+ return $this->app['repo.collection-references'];
+ }
- }
+ public function hydrate(Application $app)
+ {
+ $this->fetchInternalServices($app);
+ }
- $connbas = $this->databox->get_connection();
- $sql = 'SELECT
- asciiname, prefs, pub_wm, coll_id,
- label_en, label_fr, label_de, label_nl
- FROM coll WHERE coll_id = :coll_id';
- $stmt = $connbas->prepare($sql);
- $stmt->execute([':coll_id' => $this->coll_id]);
- $row = $stmt->fetch(PDO::FETCH_ASSOC);
- $stmt->closeCursor();
-
- if ( ! $row)
- throw new Exception('Unknown collection ' . $this->coll_id . ' on ' . $this->databox->get_dbname());
-
- $this->available = true;
- $this->pub_wm = $row['pub_wm'];
- $this->name = $row['asciiname'];
- $this->prefs = $row['prefs'];
- $this->labels = [
- 'fr' => $row['label_fr'],
- 'en' => $row['label_en'],
- 'de' => $row['label_de'],
- 'nl' => $row['label_nl'],
+ public function __sleep()
+ {
+ return [
+ 'collectionVO',
+ 'reference'
];
+ }
- $conn = $this->app->getApplicationBox()->get_connection();
-
- $sql = 'SELECT server_coll_id, sbas_id, base_id, active, ord FROM bas
- WHERE server_coll_id = :coll_id AND sbas_id = :sbas_id';
-
- $stmt = $conn->prepare($sql);
- $stmt->execute([':coll_id' => $this->coll_id, ':sbas_id' => $this->databox->get_sbas_id()]);
- $row = $stmt->fetch(PDO::FETCH_ASSOC);
- $stmt->closeCursor();
-
- $this->is_active = false;
-
- if ($row) {
- $this->is_active = ! ! $row['active'];
- $this->base_id = (int) $row['base_id'];
- $this->ord = (int) $row['ord'];
- }
-
- $stmt->closeCursor();
-
- $datas = [
- 'is_active' => $this->is_active
- , 'base_id' => $this->base_id
- , 'available' => $this->available
- , 'pub_wm' => $this->pub_wm
- , 'name' => $this->name
- , 'ord' => $this->ord
- , 'prefs' => $this->prefs
- , 'labels' => $this->labels
+ public function __debugInfo()
+ {
+ return [
+ 'reference' => $this->reference,
+ 'databox' => $this->databox,
+ 'collectionVO' => $this->collectionVO
];
-
- $this->set_data_to_cache($datas);
-
- return $this;
}
- public function enable(appbox $appbox)
+ /**
+ * @return CollectionVO
+ */
+ public function getCollection()
{
- $sql = 'UPDATE bas SET active = "1" WHERE base_id = :base_id';
- $stmt = $appbox->get_connection()->prepare($sql);
- $stmt->execute([':base_id' => $this->get_base_id()]);
- $stmt->closeCursor();
-
- $this->is_active = true;
- $this->delete_data_from_cache();
- $appbox->delete_data_from_cache(appbox::CACHE_LIST_BASES);
- $this->databox->delete_data_from_cache(databox::CACHE_COLLECTIONS);
- cache_databox::update($this->app, $this->databox->get_sbas_id(), 'structure');
-
- $this->dispatch(CollectionEvents::ENABLED, new EnabledEvent($this));
-
- return $this;
+ return $this->collectionVO;
}
- public function get_ord()
+ /**
+ * @return CollectionReference
+ */
+ public function getReference()
{
- return $this->ord;
- }
-
- public function set_ord($ord)
- {
- $this->app->getApplicationBox()->set_collection_order($this, $ord);
- $this->delete_data_from_cache();
- $this->app->getApplicationBox()->delete_data_from_cache(appbox::CACHE_LIST_BASES);
-
- return $this;
- }
-
- public function disable(appbox $appbox)
- {
- $sql = 'UPDATE bas SET active=0 WHERE base_id = :base_id';
- $stmt = $appbox->get_connection()->prepare($sql);
- $stmt->execute([':base_id' => $this->get_base_id()]);
- $stmt->closeCursor();
- $this->is_active = false;
- $this->delete_data_from_cache();
- $appbox->delete_data_from_cache(appbox::CACHE_LIST_BASES);
- $this->databox->delete_data_from_cache(databox::CACHE_COLLECTIONS);
- cache_databox::update($this->app, $this->databox->get_sbas_id(), 'structure');
-
- $this->dispatch(CollectionEvents::DISABLED, new DisabledEvent($this));
-
- return $this;
- }
-
- public function empty_collection($pass_quantity = 100)
- {
- $pass_quantity = (int) $pass_quantity > 200 ? 200 : (int) $pass_quantity;
- $pass_quantity = (int) $pass_quantity < 10 ? 10 : (int) $pass_quantity;
-
- $sql = "SELECT record_id FROM record WHERE coll_id = :coll_id
- ORDER BY record_id DESC LIMIT 0, " . $pass_quantity;
-
- $stmt = $this->databox->get_connection()->prepare($sql);
- $stmt->execute([':coll_id' => $this->get_coll_id()]);
- $rs = $stmt->fetchAll(PDO::FETCH_ASSOC);
- $stmt->closeCursor();
-
- foreach ($rs as $row) {
- $record = $this->databox->get_record($row['record_id']);
- $record->delete();
- unset($record);
- }
-
- $this->dispatch(CollectionEvents::EMPTIED, new EmptiedEvent($this));
-
- return $this;
+ return $this->reference;
}
+ /**
+ * @return bool
+ */
public function is_active()
{
- return $this->is_active;
+ return $this->reference->isActive();
}
/**
@@ -239,16 +385,257 @@ class collection implements cache_cacheableInterface, ThumbnailedElement
return $this->databox;
}
+ /**
+ * @return \Doctrine\DBAL\Connection
+ */
public function get_connection()
{
return $this->databox->get_connection();
}
- public function getRootIdentifier()
+ /**
+ * @param $publi
+ * @return $this
+ */
+ public function set_public_presentation($publi)
{
- return $this->base_id;
+ $this->collectionVO->setPublicWatermark($publi);
+ $this->getCollectionRepository()->save($this->collectionVO);
+ $this->app['repo.collections-registry']->purgeRegistry();
+
+ return $this;
}
+ /**
+ * @param $name
+ * @return $this
+ * @throws Exception_InvalidArgument
+ */
+ public function set_name($name)
+ {
+ $old_name = $this->get_name();
+
+ try {
+ $this->collectionVO->setName($name);
+ } catch (\InvalidArgumentException $e) {
+ throw new Exception_InvalidArgument();
+ }
+
+ $this->getCollectionRepository()->save($this->collectionVO);
+ $this->app['repo.collections-registry']->purgeRegistry();
+
+ $this->dispatch(CollectionEvents::NAME_CHANGED,
+ new NameChangedEvent(
+ $this,
+ array("name_before"=>$old_name)
+ )
+ );
+
+ return $this;
+ }
+
+ /**
+ * @param $code
+ * @param $label
+ * @return $this
+ */
+ public function set_label($code, $label)
+ {
+ $old_label = $this->collectionVO->getLabel($code);
+
+ $this->collectionVO->setLabel($code, $label);
+
+ $this->getCollectionRepository()->save($this->collectionVO);
+ $this->app['repo.collections-registry']->purgeRegistry();
+
+ $this->dispatch(CollectionEvents::LABEL_CHANGED, new LabelChangedEvent($this, array(
+ "lng"=>$code,
+ "label_before"=>$old_label,
+ )));
+
+ return $this;
+ }
+
+ /**
+ * @param $code
+ * @param bool $substitute
+ * @return string
+ */
+ public function get_label($code, $substitute = true)
+ {
+ return $this->collectionVO->getLabel($code, $substitute);
+ }
+
+ /**
+ * @return int
+ */
+ public function get_ord()
+ {
+ return $this->reference->getDisplayIndex();
+ }
+
+ /**
+ * @param $ord
+ * @return $this
+ */
+ public function set_ord($ord)
+ {
+ $this->reference->setDisplayIndex($ord);
+
+ $this->getReferenceRepository()->save($this->reference);
+ $this->app['repo.collections-registry']->purgeRegistry();
+
+ return $this;
+ }
+
+ /**
+ * @return int[]|null|string
+ */
+ public function get_binary_minilogos()
+ {
+ return $this->collectionVO->getLogo();
+ }
+
+ /**
+ * @return int
+ */
+ public function get_base_id()
+ {
+ return (int) $this->reference->getBaseId();
+ }
+
+ /**
+ * @return int
+ */
+ public function get_sbas_id()
+ {
+ return (int) $this->reference->getDataboxId();
+ }
+
+ /**
+ * @return int
+ */
+ public function get_coll_id()
+ {
+ return (int) $this->reference->getCollectionId();
+ }
+
+ /**
+ * @return string
+ */
+ public function get_prefs()
+ {
+ return $this->collectionVO->getPreferences();
+ }
+
+ /**
+ * @param DOMDocument $dom
+ * @return string
+ */
+ public function set_prefs(DOMDocument $dom)
+ {
+ $oldPreferences = $this->collectionVO->getPreferences();
+
+ $this->collectionVO->setPreferences($dom->saveXML());
+ $this->getCollectionRepository()->save($this->collectionVO);
+
+ $this->app['repo.collections-registry']->purgeRegistry();
+
+ $this->dispatch(
+ CollectionEvents::SETTINGS_CHANGED,
+ new SettingsChangedEvent(
+ $this,
+ array(
+ 'settings_before' => $oldPreferences
+ )
+ )
+ );
+
+ return $this->collectionVO->getPreferences();
+ }
+
+ /**
+ * @return string
+ */
+ public function get_name()
+ {
+ return $this->collectionVO->getName();
+ }
+
+ /**
+ * @return string
+ */
+ public function get_pub_wm()
+ {
+ return $this->collectionVO->getName();
+ }
+
+ /**
+ * @return bool
+ */
+ public function is_available()
+ {
+ return true;
+ }
+
+ /**
+ * @return int
+ */
+ public function getRootIdentifier()
+ {
+ return $this->reference->getBaseId();
+ }
+
+ /**
+ * @return $this
+ */
+ public function disable()
+ {
+ $this->reference->disable();
+
+ $this->getReferenceRepository()->save($this->reference);
+ $this->collectionRepositoryRegistry->purgeRegistry();
+
+ cache_databox::update($this->app, $this->databox->get_sbas_id(), 'structure');
+
+ $this->dispatch(CollectionEvents::DISABLED, new DisabledEvent($this));
+
+ return $this;
+ }
+
+ /**
+ * @return $this
+ */
+ public function enable()
+ {
+ $this->reference->enable();
+
+ $this->getReferenceRepository()->save($this->reference);
+ $this->collectionRepositoryRegistry->purgeRegistry();
+
+ cache_databox::update($this->app, $this->databox->get_sbas_id(), 'structure');
+
+ $this->dispatch(CollectionEvents::ENABLED, new EnabledEvent($this));
+
+ return $this;
+ }
+
+ /**
+ * @param int $pass_quantity
+ * @return $this
+ * @throws \Doctrine\DBAL\DBALException
+ */
+ public function empty_collection($pass_quantity = 100)
+ {
+ $this->collectionService->emptyCollection($this->databox, $this->collectionVO, $pass_quantity);
+ $this->dispatch(CollectionEvents::EMPTIED, new EmptiedEvent($this));
+ return $this;
+ }
+
+ /**
+ * @param string $thumbnailType
+ * @param File $file
+ */
public function updateThumbnail($thumbnailType, File $file = null)
{
switch ($thumbnailType) {
@@ -268,378 +655,109 @@ class collection implements cache_cacheableInterface, ThumbnailedElement
}
}
-
- public function set_public_presentation($publi)
- {
- if (in_array($publi, ['none', 'wm', 'stamp'])) {
- $sql = 'UPDATE coll SET pub_wm = :pub_wm WHERE coll_id = :coll_id';
- $stmt = $this->get_connection()->prepare($sql);
- $stmt->execute([':pub_wm' => $publi, ':coll_id' => $this->get_coll_id()]);
- $stmt->closeCursor();
-
- $this->pub_wm = $publi;
-
- $this->delete_data_from_cache();
- }
-
- return $this;
- }
-
- public function set_name($name)
- {
- $old_name = $this->get_name();
-
- $name = trim(strip_tags($name));
-
- if ($name === '')
- throw new Exception_InvalidArgument ();
-
- $sql = "UPDATE coll SET asciiname = :asciiname
- WHERE coll_id = :coll_id";
- $stmt = $this->get_connection()->prepare($sql);
- $stmt->execute([':asciiname' => $name, ':coll_id' => $this->get_coll_id()]);
- $stmt->closeCursor();
-
- $this->name = $name;
-
- $this->delete_data_from_cache();
-
- phrasea::reset_baseDatas($this->databox->get_appbox());
-
- $this->dispatch(
- CollectionEvents::NAME_CHANGED,
- new NameChangedEvent(
- $this,
- array("name_before"=>$old_name)
- )
- );
-
- return $this;
- }
-
- public function set_label($code, $label)
- {
- if (!array_key_exists($code, $this->labels)) {
- throw new InvalidArgumentException(sprintf('Code %s is not defined', $code));
- }
-
- $old_label = $this->labels[$code];
-
- $sql = "UPDATE coll SET label_$code = :label
- WHERE coll_id = :coll_id";
- $stmt = $this->get_connection()->prepare($sql);
- $stmt->execute([':label' => $label, ':coll_id' => $this->get_coll_id()]);
- $stmt->closeCursor();
-
- $this->labels[$code] = $label;
-
- $this->delete_data_from_cache();
-
- phrasea::reset_baseDatas($this->databox->get_appbox());
-
- $this->dispatch(CollectionEvents::LABEL_CHANGED, new LabelChangedEvent($this, array(
- "lng"=>$code,
- "label_before"=>$old_label,
- )));
-
- return $this;
- }
-
- public function get_label($code, $substitute = true)
- {
- if (!array_key_exists($code, $this->labels)) {
- throw new InvalidArgumentException(sprintf('Code %s is not defined', $code));
- }
-
- if ($substitute) {
- return isset($this->labels[$code]) ? $this->labels[$code] : $this->name;
- } else {
- return $this->labels[$code];
- }
- }
-
+ /**
+ * @return int|null
+ * @throws \Doctrine\DBAL\DBALException
+ */
public function get_record_amount()
{
- $sql = "SELECT COUNT(record_id) AS n FROM record WHERE coll_id = :coll_id";
- $stmt = $this->get_connection()->prepare($sql);
- $stmt->execute([':coll_id' => $this->get_coll_id()]);
- $rowbas = $stmt->fetch(PDO::FETCH_ASSOC);
- $stmt->closeCursor();
-
- $amount = $rowbas ? (int) $rowbas["n"] : null;
-
- return $amount;
+ return $this->collectionService->getRecordCount($this->collectionVO);
}
+ /**
+ * @return array
+ * @throws \Doctrine\DBAL\DBALException
+ */
public function get_record_details()
{
-
- $sql = "SELECT record.coll_id,name,COALESCE(asciiname, CONCAT('_',record.coll_id)) AS asciiname,
- SUM(1) AS n, SUM(size) AS size
- FROM record NATURAL JOIN subdef
- INNER JOIN coll ON record.coll_id=coll.coll_id AND coll.coll_id = :coll_id
- GROUP BY record.coll_id, subdef.name";
-
- $stmt = $this->get_connection()->prepare($sql);
- $stmt->execute([':coll_id' => $this->get_coll_id()]);
- $rs = $stmt->fetchAll(PDO::FETCH_ASSOC);
- $stmt->closeCursor();
-
- $ret = [];
- foreach ($rs as $row) {
- $ret[] = [
- "coll_id" => (int) $row["coll_id"],
- "name" => $row["name"],
- "amount" => (int) $row["n"],
- "size" => (int) $row["size"]];
- }
-
- return $ret;
+ return $this->collectionService->getRecordDetails($this->collectionVO);
}
+ /**
+ * @param SplFileInfo $pathfile
+ * @return $this
+ */
public function update_logo(\SplFileInfo $pathfile = null)
{
- if (is_null($pathfile)) {
- $this->binary_logo = null;
- } else {
- $this->binary_logo = file_get_contents($pathfile->getPathname());
+ $fileContents = null;
+
+ if (!is_null($pathfile)) {
+ $fileContents = file_get_contents($pathfile->getPathname());
}
- $sql = "UPDATE coll SET logo = :logo, majLogo=NOW() WHERE coll_id = :coll_id";
- $stmt = $this->get_connection()->prepare($sql);
- $stmt->execute([':logo' => $this->binary_logo, ':coll_id' => $this->get_coll_id()]);
- $stmt->closeCursor();
+ $this->collectionVO->setLogo($fileContents);
+
+ $this->getCollectionRepository()->save($this->collectionVO);
+ $this->collectionRepositoryRegistry->purgeRegistry();
return $this;
}
+ /**
+ * @return $this
+ * @throws \Doctrine\DBAL\DBALException
+ */
public function reset_watermark()
{
+ $this->collectionService->resetWatermark($this->collectionVO);
- $sql = 'SELECT path, file FROM record r INNER JOIN subdef s USING(record_id)
- WHERE r.coll_id = :coll_id AND r.type="image" AND s.name="preview"';
-
- $stmt = $this->get_connection()->prepare($sql);
- $stmt->execute([':coll_id' => $this->get_coll_id()]);
-
- while ($row2 = $stmt->fetch(PDO::FETCH_ASSOC)) {
- @unlink(p4string::addEndSlash($row2['path']) . 'watermark_' . $row2['file']);
- }
- $stmt->closeCursor();
+ $this->getCollectionRepository()->save($this->collectionVO);
+ $this->collectionRepositoryRegistry->purgeRegistry();
return $this;
}
+ /**
+ * @param null $record_id
+ * @return $this
+ * @throws \Doctrine\DBAL\DBALException
+ */
public function reset_stamp($record_id = null)
{
+ $this->collectionService->resetStamp($this->collectionVO, $record_id);
- $sql = 'SELECT path, file FROM record r INNER JOIN subdef s USING(record_id)
- WHERE r.coll_id = :coll_id
- AND r.type="image" AND s.name IN ("preview", "document")';
-
- $params = [':coll_id' => $this->get_coll_id()];
-
- if ($record_id) {
- $sql .= ' AND record_id = :record_id';
- $params[':record_id'] = $record_id;
- }
-
- $stmt = $this->get_connection()->prepare($sql);
- $stmt->execute($params);
-
- while ($row2 = $stmt->fetch(PDO::FETCH_ASSOC)) {
- @unlink(p4string::addEndSlash($row2['path']) . 'stamp_' . $row2['file']);
- }
- $stmt->closeCursor();
+ $this->getCollectionRepository()->save($this->collectionVO);
+ $this->collectionRepositoryRegistry->purgeRegistry();
return $this;
}
+ /**
+ * @throws \Doctrine\DBAL\DBALException
+ */
public function delete()
{
- while ($this->get_record_amount() > 0) {
- $this->empty_collection();
- }
+ $this->collectionService->delete($this->databox, $this->collectionVO, $this->reference);
- $sql = "DELETE FROM coll WHERE coll_id = :coll_id";
- $stmt = $this->get_connection()->prepare($sql);
- $stmt->execute([':coll_id' => $this->get_coll_id()]);
- $stmt->closeCursor();
-
- $appbox = $this->databox->get_appbox();
-
- $sql = "DELETE FROM bas WHERE base_id = :base_id";
- $stmt = $appbox->get_connection()->prepare($sql);
- $stmt->execute([':base_id' => $this->get_base_id()]);
- $stmt->closeCursor();
-
- $sql = "DELETE FROM basusr WHERE base_id = :base_id";
- $stmt = $appbox->get_connection()->prepare($sql);
- $stmt->execute([':base_id' => $this->get_base_id()]);
- $stmt->closeCursor();
+ $this->getCollectionRepository()->delete($this->collectionVO);
$this->app['manipulator.registration']->deleteRegistrationsOnCollection($this);
-
- $this->get_databox()->delete_data_from_cache(databox::CACHE_COLLECTIONS);
- $appbox->delete_data_from_cache(appbox::CACHE_LIST_BASES);
- phrasea::reset_baseDatas($appbox);
-
- return;
- }
-
- public function get_binary_minilogos()
- {
- return $this->binary_logo;
+ $this->collectionRepositoryRegistry->purgeRegistry();
}
/**
- *
- * @param Application $app
- * @param int $base_id
- * @return collection
+ * @return $this
+ * @throws \Doctrine\DBAL\DBALException
*/
- public static function get_from_base_id(Application $app, $base_id)
- {
- $coll_id = phrasea::collFromBas($app, $base_id);
- $sbas_id = phrasea::sbasFromBas($app, $base_id);
- if (! $sbas_id || ! $coll_id) {
- throw new Exception_Databox_CollectionNotFound(sprintf("Collection with base_id %s could not be found", $base_id));
- }
- $databox = $app->findDataboxById($sbas_id);
-
- return self::get_from_coll_id($app, $databox, $coll_id);
- }
-
- /**
- *
- * @param Application $app
- * @param databox $databox
- * @param int $coll_id
- * @return collection
- */
- public static function get_from_coll_id(Application $app, databox $databox, $coll_id)
- {
- assert(is_int($coll_id));
-
- $key = sprintf('%d_%d', $databox->get_sbas_id(), $coll_id);
- if (!isset(self::$_collections[$key])) {
- $collection = new self($app, $coll_id, $databox);
-
- if (!$app['conf.restrictions']->isCollectionAvailable($collection)) {
- throw new Exception_Databox_CollectionNotFound('Collection `' . $collection->get_base_id() . '` is not available here.');
- }
-
- self::$_collections[$key] = $collection;
- }
-
- return self::$_collections[$key];
- }
-
- public function get_base_id()
- {
- return $this->base_id;
- }
-
- public function get_sbas_id()
- {
- return $this->sbas_id;
- }
-
- public function get_coll_id()
- {
- return $this->coll_id;
- }
-
- public function get_prefs()
- {
- return $this->prefs;
- }
-
- public function set_prefs(DOMDocument $dom)
- {
- $old_prefs = $this->get_prefs();
-
- $this->prefs = $dom->saveXML();
-
- $sql = "UPDATE coll SET prefs = :prefs WHERE coll_id = :coll_id";
- $stmt = $this->get_connection()->prepare($sql);
- $stmt->execute([':prefs' => $this->prefs, ':coll_id' => $this->get_coll_id()]);
- $stmt->closeCursor();
-
- $this->delete_data_from_cache();
-
- $this->dispatch(
- CollectionEvents::SETTINGS_CHANGED,
- new SettingsChangedEvent(
- $this,
- array(
- 'settings_before'=>$old_prefs
- )
- )
- );
-
- return $this->prefs;
- }
-
- public function get_name()
- {
- return $this->name;
- }
-
- public function get_pub_wm()
- {
- return $this->pub_wm;
- }
-
- public function is_available()
- {
- return $this->available;
- }
-
- public function unmount_collection(Application $app)
+ public function unmount()
{
$old_coll_id = $this->get_coll_id();
$old_name = $this->get_name();
- $params = [':base_id' => $this->get_base_id()];
+ $this->collectionService->unmountCollection($this->reference);
- $query = $app['phraseanet.user-query'];
- $total = $query->on_base_ids([$this->get_base_id()])
- ->include_phantoms(false)
- ->include_special_users(true)
- ->include_invite(true)
- ->include_templates(true)->get_total();
- $n = 0;
- while ($n < $total) {
- $results = $query->limit($n, 50)->execute()->get_results();
- foreach ($results as $user) {
- $app->getAclForUser($user)->delete_data_from_cache(ACL::CACHE_RIGHTS_SBAS);
- $app->getAclForUser($user)->delete_data_from_cache(ACL::CACHE_RIGHTS_BAS);
- }
- $n+=50;
- }
-
- $sql = "DELETE FROM basusr WHERE base_id = :base_id";
- $stmt = $app->getApplicationBox()->get_connection()->prepare($sql);
- $stmt->execute($params);
- $stmt->closeCursor();
-
- $sql = "DELETE FROM bas WHERE base_id = :base_id";
- $stmt = $app->getApplicationBox()->get_connection()->prepare($sql);
- $stmt->execute($params);
- $stmt->closeCursor();
+ $this->getReferenceRepository()->delete($this->reference);
$this->app['manipulator.registration']->deleteRegistrationsOnCollection($this);
+ $this->collectionRepositoryRegistry->purgeRegistry();
- phrasea::reset_baseDatas($app['phraseanet.appbox']);
-
- $app['dispatcher']->dispatch(
+ $this->dispatch(
CollectionEvents::UNMOUNTED,
new UnmountedEvent(
null, // the coll is not available anymore
array(
- 'coll_id'=>$old_coll_id,
- 'coll_name'=>$old_name
+ 'coll_id' => $old_coll_id,
+ 'coll_name' => $old_name
)
)
);
@@ -647,218 +765,6 @@ class collection implements cache_cacheableInterface, ThumbnailedElement
return $this;
}
- private static function getNewOrder(Connection $conn, $sbas_id)
- {
- $sql = "SELECT GREATEST(0, MAX(ord)) + 1 AS ord FROM bas WHERE sbas_id = :sbas_id";
- $stmt = $conn->prepare($sql);
- $stmt->execute([':sbas_id' => $sbas_id]);
- $ord = $stmt->fetch(\PDO::FETCH_ASSOC);
- $stmt->closeCursor();
-
- return $ord['ord'] ?: 1;
- }
-
- public static function create(Application $app, databox $databox, appbox $appbox, $name, User $user = null)
- {
- $sbas_id = $databox->get_sbas_id();
- $connbas = $databox->get_connection();
- $conn = $appbox->get_connection();
- $new_bas = false;
-
- $prefs = '
-
- 0
-
-
- ';
-
- $sql = "INSERT INTO coll (coll_id, asciiname, prefs, logo)
- VALUES (null, :name, :prefs, '')";
-
- $params = [
- ':name' => $name,
- 'prefs' => $prefs,
- ];
-
- $stmt = $connbas->prepare($sql);
- $stmt->execute($params);
- $stmt->closeCursor();
-
- $new_id = (int) $connbas->lastInsertId();
-
- $sql = "INSERT INTO bas (base_id, active, ord, server_coll_id, sbas_id, aliases)
- VALUES
- (null, 1, :ord, :server_coll_id, :sbas_id, '')";
- $stmt = $conn->prepare($sql);
- $stmt->execute([
- ':server_coll_id' => $new_id,
- ':sbas_id' => $sbas_id,
- ':ord' => self::getNewOrder($conn, $sbas_id),
- ]);
- $stmt->closeCursor();
-
- $new_bas = $conn->lastInsertId();
- $databox->delete_data_from_cache(databox::CACHE_COLLECTIONS);
- $appbox->delete_data_from_cache(appbox::CACHE_LIST_BASES);
-
- phrasea::reset_baseDatas($appbox);
-
- $collection = self::get_from_coll_id($app, $databox, $new_id);
-
- if (null !== $user) {
- $collection->set_admin($new_bas, $user);
- }
-
- $app['dispatcher']->dispatch(
- CollectionEvents::CREATED,
- new CreatedEvent(
- $collection
- )
- );
-
- return $collection;
- }
-
- public function set_admin($base_id, User $user)
- {
-
- $rights = [
- "canputinalbum" => "1",
- "candwnldhd" => "1",
- "nowatermark" => "1",
- "candwnldpreview" => "1",
- "cancmd" => "1",
- "canadmin" => "1",
- "actif" => "1",
- "canreport" => "1",
- "canpush" => "1",
- "basusr_infousr" => "",
- "canaddrecord" => "1",
- "canmodifrecord" => "1",
- "candeleterecord" => "1",
- "chgstatus" => "1",
- "imgtools" => "1",
- "manage" => "1",
- "modify_struct" => "1"
- ];
-
- $this->app->getAclForUser($user)->update_rights_to_base($base_id, $rights);
-
- return true;
- }
-
- public static function mount_collection(Application $app, databox $databox, $coll_id, User $user)
- {
-
- $sql = "INSERT INTO bas (base_id, active, server_coll_id, sbas_id, aliases, ord)
- VALUES
- (null, 1, :server_coll_id, :sbas_id, '', :ord)";
- $stmt = $databox->get_appbox()->get_connection()->prepare($sql);
- $stmt->execute([
- ':server_coll_id' => $coll_id,
- ':sbas_id' => $databox->get_sbas_id(),
- ':ord' => self::getNewOrder($databox->get_appbox()->get_connection(), $databox->get_sbas_id()),
- ]);
- $stmt->closeCursor();
-
- $new_bas = $databox->get_appbox()->get_connection()->lastInsertId();
- $databox->get_appbox()->delete_data_from_cache(appbox::CACHE_LIST_BASES);
-
- $databox->delete_data_from_cache(databox::CACHE_COLLECTIONS);
-
- cache_databox::update($app, $databox->get_sbas_id(), 'structure');
-
- phrasea::reset_baseDatas($databox->get_appbox());
-
- $collection = self::get_from_base_id($app, $new_bas);
- $collection->set_admin($new_bas, $user);
-
- $app['dispatcher']->dispatch(
- CollectionEvents::MOUNTED,
- new MountedEvent(
- $collection
- )
- );
-
- return $new_bas;
- }
-
- public static function getLogo($base_id, Application $app, $printname = false)
- {
- $base_id_key = $base_id . '_' . ($printname ? '1' : '0');
-
- if ( ! isset(self::$_logos[$base_id_key])) {
-
- if (is_file($app['root.path'] . '/config/minilogos/' . $base_id)) {
- $name = phrasea::bas_labels($base_id, $app);
- self::$_logos[$base_id_key] = '
';
- } elseif ($printname) {
- self::$_logos[$base_id_key] = phrasea::bas_labels($base_id, $app);
- }
- }
-
- return isset(self::$_logos[$base_id_key]) ? self::$_logos[$base_id_key] : '';
- }
-
- public static function getWatermark($base_id)
- {
- if ( ! isset(self::$_watermarks['base_id'])) {
-
- if (is_file(__DIR__ . '/../../config/wm/' . $base_id))
- self::$_watermarks['base_id'] = '
';
- }
-
- return isset(self::$_watermarks['base_id']) ? self::$_watermarks['base_id'] : '';
- }
-
- public static function getPresentation($base_id)
- {
- if ( ! isset(self::$_presentations['base_id'])) {
-
- if (is_file(__DIR__ . '/../../config/presentation/' . $base_id))
- self::$_presentations['base_id'] = '
';
- }
-
- return isset(self::$_presentations['base_id']) ? self::$_presentations['base_id'] : '';
- }
-
- public static function getStamp($base_id)
- {
- if ( ! isset(self::$_stamps['base_id'])) {
-
- if (is_file(__DIR__ . '/../../config/stamp/' . $base_id))
- self::$_stamps['base_id'] = '
';
- }
-
- return isset(self::$_stamps['base_id']) ? self::$_stamps['base_id'] : '';
- }
-
- public function get_cache_key($option = null)
- {
- return 'collection_' . $this->coll_id . ($option ? '_' . $option : '');
- }
-
- public function get_data_from_cache($option = null)
- {
- return $this->databox->get_data_from_cache($this->get_cache_key($option));
- }
-
- public function set_data_to_cache($value, $option = null, $duration = 0)
- {
- return $this->databox->set_data_to_cache($value, $this->get_cache_key($option), $duration);
- }
-
- public function delete_data_from_cache($option = null)
- {
- return $this->databox->delete_data_from_cache($this->get_cache_key($option));
- }
-
- public static function purge()
- {
- self::$_collections = [];
- }
-
/**
* Tells whether registration is activated for provided collection or not.
*
@@ -877,7 +783,7 @@ class collection implements cache_cacheableInterface, ThumbnailedElement
}
foreach ($element as $caninscript) {
- if (false !== (Boolean) (string) $caninscript) {
+ if (false !== (bool)(string)$caninscript) {
return true;
}
}
@@ -888,18 +794,47 @@ class collection implements cache_cacheableInterface, ThumbnailedElement
/**
* Gets terms of use.
*
- * @param \collection $collection
- *
* @return null|string
*/
public function getTermsOfUse()
{
if (false === $xml = simplexml_load_string($this->get_prefs())) {
- return;
+ return null;
}
foreach ($xml->xpath('/baseprefs/cgu') as $sbpcgu) {
return $sbpcgu->saveXML();
}
}
+
+ public function get_cache_key($option = null)
+ {
+ return 'collection_' . $this->collectionVO->getCollectionId() . ($option ? '_' . $option : '');
+ }
+
+ public function get_data_from_cache($option = null)
+ {
+ return $this->databox->get_data_from_cache($this->get_cache_key($option));
+ }
+
+ public function set_data_to_cache($value, $option = null, $duration = 0)
+ {
+ return $this->databox->set_data_to_cache($value, $this->get_cache_key($option), $duration);
+ }
+
+ public function delete_data_from_cache($option = null)
+ {
+ $this->databox->delete_data_from_cache($this->get_cache_key($option));
+ }
+
+ /**
+ * @param Application $app
+ */
+ private function fetchInternalServices(Application $app)
+ {
+ $this->app = $app;
+ $this->databox = $app->getApplicationBox()->get_databox($this->reference->getDataboxId());
+ $this->collectionService = $app->getApplicationBox()->getCollectionService();
+ $this->collectionRepositoryRegistry = $app['repo.collections-registry'];
+ }
}
diff --git a/lib/classes/databox.php b/lib/classes/databox.php
index ecf510463d..94f07ff5ed 100644
--- a/lib/classes/databox.php
+++ b/lib/classes/databox.php
@@ -10,10 +10,12 @@
*/
use Alchemy\Phrasea\Application;
+use Alchemy\Phrasea\Collection\CollectionRepositoryRegistry;
use Alchemy\Phrasea\Core\Connection\ConnectionSettings;
use Alchemy\Phrasea\Core\PhraseaTokens;
use Alchemy\Phrasea\Core\Thumbnail\ThumbnailedElement;
use Alchemy\Phrasea\Core\Version\DataboxVersionRepository;
+use Alchemy\Phrasea\Databox\DataboxRepository;
use Alchemy\Phrasea\Databox\Record\RecordRepository;
use Alchemy\Phrasea\Exception\InvalidArgumentException;
use Alchemy\Phrasea\Model\Entities\User;
@@ -46,6 +48,7 @@ class databox extends base implements ThumbnailedElement
const CACHE_COLLECTIONS = 'collections';
const CACHE_STRUCTURE = 'structure';
const PIC_PDF = 'logopdf';
+ const CACHE_CGUS = 'cgus';
/** @var array */
protected static $_xpath_thesaurus = [];
@@ -53,9 +56,189 @@ class databox extends base implements ThumbnailedElement
protected static $_dom_thesaurus = [];
/** @var array */
protected static $_thesaurus = [];
+
/** @var SimpleXMLElement */
protected static $_sxml_thesaurus = [];
+ /**
+ * @param Application $app
+ * @param Connection $databoxConnection
+ * @param SplFileInfo $data_template
+ * @return databox
+ * @throws \Doctrine\DBAL\DBALException
+ */
+ public static function create(Application $app, Connection $databoxConnection, \SplFileInfo $data_template)
+ {
+ if ( ! file_exists($data_template->getRealPath())) {
+ throw new \InvalidArgumentException($data_template->getRealPath() . " does not exist");
+ }
+
+ $host = $databoxConnection->getHost();
+ $port = $databoxConnection->getPort();
+ $dbname = $databoxConnection->getDatabase();
+ $user = $databoxConnection->getUsername();
+ $password = $databoxConnection->getPassword();
+
+ $appbox = $app->getApplicationBox();
+
+ try {
+ $sql = 'CREATE DATABASE `' . $dbname . '` CHARACTER SET utf8 COLLATE utf8_unicode_ci';
+ $stmt = $databoxConnection->prepare($sql);
+ $stmt->execute();
+ $stmt->closeCursor();
+ } catch (\Exception $e) {
+
+ }
+
+ $sql = 'USE `' . $dbname . '`';
+ $stmt = $databoxConnection->prepare($sql);
+ $stmt->execute();
+ $stmt->closeCursor();
+
+ $app['orm.add']([
+ 'host' => $host,
+ 'port' => $port,
+ 'dbname' => $dbname,
+ 'user' => $user,
+ 'password' => $password
+ ]);
+
+ phrasea::reset_sbasDatas($app['phraseanet.appbox']);
+
+ /** @var DataboxRepository $databoxRepository */
+ $databoxRepository = $app['repo.databoxes'];
+ $databox = $databoxRepository->create($host, $port, $user, $password, $dbname);
+
+ $appbox->delete_data_from_cache(appbox::CACHE_LIST_BASES);
+
+ $databox->insert_datas();
+ $databox->setNewStructure(
+ $data_template, $app['conf']->get(['main', 'storage', 'subdefs'])
+ );
+
+ $app['dispatcher']->dispatch(DataboxEvents::CREATED, new CreatedEvent($databox));
+
+ return $databox;
+ }
+
+ /**
+ *
+ * @param Application $app
+ * @param string $host
+ * @param int $port
+ * @param string $user
+ * @param string $password
+ * @param string $dbname
+ * @return databox
+ */
+ public static function mount(Application $app, $host, $port, $user, $password, $dbname)
+ {
+ $app['db.provider']([
+ 'host' => $host,
+ 'port' => $port,
+ 'user' => $user,
+ 'password' => $password,
+ 'dbname' => $dbname,
+ ])->connect();
+
+ /** @var DataboxRepository $databoxRepository */
+ $databoxRepository = $app['repo.databoxes'];
+ $databox = $databoxRepository->mount($host, $port, $user, $password, $dbname);
+
+ $databox->delete_data_from_cache(databox::CACHE_COLLECTIONS);
+ $app->getApplicationBox()->delete_data_from_cache(appbox::CACHE_LIST_BASES);
+
+ phrasea::reset_sbasDatas($app['phraseanet.appbox']);
+ cache_databox::update($app, $databox->get_sbas_id(), 'structure');
+
+ $app['dispatcher']->dispatch(DataboxEvents::MOUNTED, new MountedEvent($databox));
+
+ return $databox;
+ }
+
+ public static function get_available_dcfields()
+ {
+ return [
+ databox_Field_DCESAbstract::Contributor => new databox_Field_DCES_Contributor(),
+ databox_Field_DCESAbstract::Coverage => new databox_Field_DCES_Coverage(),
+ databox_Field_DCESAbstract::Creator => new databox_Field_DCES_Creator(),
+ databox_Field_DCESAbstract::Date => new databox_Field_DCES_Date(),
+ databox_Field_DCESAbstract::Description => new databox_Field_DCES_Description(),
+ databox_Field_DCESAbstract::Format => new databox_Field_DCES_Format(),
+ databox_Field_DCESAbstract::Identifier => new databox_Field_DCES_Identifier(),
+ databox_Field_DCESAbstract::Language => new databox_Field_DCES_Language(),
+ databox_Field_DCESAbstract::Publisher => new databox_Field_DCES_Publisher(),
+ databox_Field_DCESAbstract::Relation => new databox_Field_DCES_Relation(),
+ databox_Field_DCESAbstract::Rights => new databox_Field_DCES_Rights(),
+ databox_Field_DCESAbstract::Source => new databox_Field_DCES_Source(),
+ databox_Field_DCESAbstract::Subject => new databox_Field_DCES_Subject(),
+ databox_Field_DCESAbstract::Title => new databox_Field_DCES_Title(),
+ databox_Field_DCESAbstract::Type => new databox_Field_DCES_Type()
+ ];
+ }
+
+ /**
+ *
+ * @param int $sbas_id
+ * @return string
+ */
+ public static function getPrintLogo($sbas_id)
+ {
+ $out = '';
+ if (is_file(($filename = __DIR__ . '/../../config/minilogos/'.\databox::PIC_PDF.'_' . $sbas_id . '.jpg')))
+ $out = file_get_contents($filename);
+
+ return $out;
+ }
+
+ /**
+ * @param TranslatorInterface $translator
+ * @param string $structure
+ * @return Array
+ */
+ public static function get_structure_errors(TranslatorInterface $translator, $structure)
+ {
+ $sx_structure = simplexml_load_string($structure);
+
+ $subdefgroup = $sx_structure->subdefs[0];
+ $AvSubdefs = [];
+
+ $errors = [];
+
+ foreach ($subdefgroup as $k => $subdefs) {
+ $subdefgroup_name = trim((string) $subdefs->attributes()->name);
+
+ if ($subdefgroup_name == '') {
+ $errors[] = $translator->trans('ERREUR : TOUTES LES BALISES subdefgroup necessitent un attribut name');
+ continue;
+ }
+
+ if ( ! isset($AvSubdefs[$subdefgroup_name]))
+ $AvSubdefs[$subdefgroup_name] = [];
+
+ foreach ($subdefs as $sd) {
+ $sd_name = trim(mb_strtolower((string) $sd->attributes()->name));
+ $sd_class = trim(mb_strtolower((string) $sd->attributes()->class));
+ if ($sd_name == '' || isset($AvSubdefs[$subdefgroup_name][$sd_name])) {
+ $errors[] = $translator->trans('ERREUR : Les name de subdef sont uniques par groupe de subdefs et necessaire');
+ continue;
+ }
+ if ( ! in_array($sd_class, ['thumbnail', 'preview', 'document'])) {
+ $errors[] = $translator->trans('ERREUR : La classe de subdef est necessaire et egal a "thumbnail","preview" ou "document"');
+ continue;
+ }
+ $AvSubdefs[$subdefgroup_name][$sd_name] = $sd;
+ }
+ }
+
+ return $errors;
+ }
+
+ public static function purge()
+ {
+ self::$_xpath_thesaurus = self::$_dom_thesaurus = self::$_thesaurus = self::$_sxml_thesaurus = [];
+ }
+
/** @var int */
protected $id;
/** @var string */
@@ -72,24 +255,32 @@ class databox extends base implements ThumbnailedElement
protected $meta_struct;
/** @var databox_subdefsStructure */
protected $subdef_struct;
-
+ protected $thesaurus;
+ protected $cterms;
+ protected $cgus;
+ /** @var DataboxRepository */
+ private $databoxRepository;
/** @var RecordRepository */
private $recordRepository;
/** @var string[] */
private $labels = [];
+ /** @var int */
private $ord;
+ /** @var string */
private $viewname;
/**
* @param Application $app
- * @param int $sbas_id
- * @param array $row
+ * @param int $sbas_id
+ * @param DataboxRepository $databoxRepository
+ * @param array $row
*/
- public function __construct(Application $app, $sbas_id, array $row)
+ public function __construct(Application $app, $sbas_id, DataboxRepository $databoxRepository, array $row)
{
assert(is_int($sbas_id));
assert($sbas_id > 0);
+ $this->databoxRepository = $databoxRepository;
$this->id = $sbas_id;
$connectionConfigs = phrasea::sbas_params($app);
@@ -116,6 +307,212 @@ class databox extends base implements ThumbnailedElement
$this->loadFromRow($row);
}
+ public function setNewStructure(\SplFileInfo $data_template, $path_doc)
+ {
+ if ( ! file_exists($data_template->getPathname())) {
+ throw new \InvalidArgumentException(sprintf('File %s does not exists'));
+ }
+
+ $contents = file_get_contents($data_template->getPathname());
+
+ $contents = str_replace(
+ ["{{basename}}", "{{datapathnoweb}}"]
+ , [$this->connectionSettings->getDatabaseName(), rtrim($path_doc, '/').'/']
+ , $contents
+ );
+
+ $dom_doc = new DOMDocument();
+ $dom_doc->loadXML($contents);
+ $this->saveStructure($dom_doc);
+
+ $this->feed_meta_fields();
+
+ return $this;
+ }
+
+ /**
+ *
+ * @param DOMDocument $dom_struct
+ * @return databox
+ */
+ public function saveStructure(DOMDocument $dom_struct)
+ {
+ $old_structure = $this->get_dom_structure();
+
+ $dom_struct->documentElement
+ ->setAttribute("modification_date", $now = date("YmdHis"));
+
+ $sql = "UPDATE pref SET value= :structure, updated_on= :now WHERE prop='structure'";
+
+ $this->structure = $dom_struct->saveXML();
+
+ $stmt = $this->get_connection()->prepare($sql);
+ $stmt->execute(
+ [
+ ':structure' => $this->structure,
+ ':now' => $now
+ ]
+ );
+ $stmt->closeCursor();
+
+ $this->_sxml_structure = $this->_dom_structure = $this->_xpath_structure = null;
+
+ $this->meta_struct = null;
+
+ $this->get_appbox()->delete_data_from_cache(appbox::CACHE_LIST_BASES);
+ $this->delete_data_from_cache(self::CACHE_STRUCTURE);
+ $this->delete_data_from_cache(self::CACHE_META_STRUCT);
+
+ cache_databox::update($this->app, $this->id, 'structure');
+
+ $this->databoxRepository->save($this);
+
+ $this->app['dispatcher']->dispatch(
+ DataboxEvents::STRUCTURE_CHANGED,
+ new StructureChangedEvent(
+ $this,
+ array(
+ 'dom_before'=>$old_structure
+ )
+ )
+ );
+
+ return $this;
+ }
+
+ /**
+ * @return DOMDocument
+ */
+ public function get_dom_structure()
+ {
+ if ($this->_dom_structure) {
+ return $this->_dom_structure;
+ }
+
+ $structure = $this->get_structure();
+
+ $dom = new DOMDocument();
+
+ $dom->standalone = true;
+ $dom->preserveWhiteSpace = false;
+ $dom->formatOutput = true;
+
+ if ($structure && $dom->loadXML($structure) !== false)
+ $this->_dom_structure = $dom;
+ else
+ $this->_dom_structure = false;
+
+ return $this->_dom_structure;
+ }
+
+ /**
+ * @return string
+ */
+ public function get_structure()
+ {
+ if ($this->structure) {
+ return $this->structure;
+ }
+
+ $this->structure = $this->retrieve_structure();
+
+ return $this->structure;
+ }
+
+ public function feed_meta_fields()
+ {
+ $sxe = $this->get_sxml_structure();
+
+ foreach ($sxe->description->children() as $fname => $field) {
+ $dom_struct = $this->get_dom_structure();
+ $xp_struct = $this->get_xpath_structure();
+ $fname = (string) $fname;
+ $src = trim(isset($field['src']) ? str_replace('/rdf:RDF/rdf:Description/', '', $field['src']) : '');
+
+ $meta_id = isset($field['meta_id']) ? $field['meta_id'] : null;
+ if ( ! is_null($meta_id))
+ continue;
+
+ $nodes = $xp_struct->query('/record/description/' . $fname);
+ if ($nodes->length > 0) {
+ $nodes->item(0)->parentNode->removeChild($nodes->item(0));
+ }
+ $this->saveStructure($dom_struct);
+
+ $type = isset($field['type']) ? $field['type'] : 'string';
+ $type = in_array($type
+ , [
+ databox_field::TYPE_DATE
+ , databox_field::TYPE_NUMBER
+ , databox_field::TYPE_STRING
+ , databox_field::TYPE_TEXT
+ ]
+ ) ? $type : databox_field::TYPE_STRING;
+
+ $multi = isset($field['multi']) ? (Boolean) (string) $field['multi'] : false;
+
+ $meta_struct_field = databox_field::create($this->app, $this, $fname, $multi);
+ $meta_struct_field
+ ->set_readonly(isset($field['readonly']) ? (string) $field['readonly'] : 0)
+ ->set_indexable(isset($field['index']) ? (string) $field['index'] : '1')
+ ->set_separator(isset($field['separator']) ? (string) $field['separator'] : '')
+ ->set_required((isset($field['required']) && (string) $field['required'] == 1))
+ ->set_business((isset($field['business']) && (string) $field['business'] == 1))
+ ->set_aggregable((isset($field['aggregable']) ? (string) $field['aggregable'] : 0))
+ ->set_type($type)
+ ->set_tbranch(isset($field['tbranch']) ? (string) $field['tbranch'] : '')
+ ->set_thumbtitle(isset($field['thumbtitle']) ? (string) $field['thumbtitle'] : (isset($field['thumbTitle']) ? $field['thumbTitle'] : '0'))
+ ->set_report(isset($field['report']) ? (string) $field['report'] : '1')
+ ->save();
+
+ try {
+ $meta_struct_field->set_tag(\databox_field::loadClassFromTagName($src))->save();
+ } catch (\Exception $e) {
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ *
+ * @return SimpleXMLElement
+ */
+ public function get_sxml_structure()
+ {
+ if ($this->_sxml_structure) {
+ return $this->_sxml_structure;
+ }
+
+ $structure = $this->get_structure();
+
+ if ($structure && false !== $tmp = simplexml_load_string($structure))
+ $this->_sxml_structure = $tmp;
+ else
+ $this->_sxml_structure = false;
+
+ return $this->_sxml_structure;
+ }
+
+ /**
+ * @return DOMXpath
+ */
+ public function get_xpath_structure()
+ {
+ if ($this->_xpath_structure) {
+ return $this->_xpath_structure;
+ }
+
+ $dom_doc = $this->get_dom_structure();
+
+ if ($dom_doc && ($tmp = new DOMXpath($dom_doc)) !== false)
+ $this->_xpath_structure = $tmp;
+ else
+ $this->_xpath_structure = false;
+
+ return $this->_xpath_structure;
+ }
+
/**
* @return RecordRepository
*/
@@ -128,118 +525,79 @@ class databox extends base implements ThumbnailedElement
return $this->recordRepository;
}
- public function get_viewname()
- {
- return $this->viewname ? : $this->connectionSettings->getDatabaseName();
- }
-
- public function set_viewname($viewname)
- {
- $sql = 'UPDATE sbas SET viewname = :viewname WHERE sbas_id = :sbas_id';
-
- $stmt = $this->get_appbox()->get_connection()->prepare($sql);
- $stmt->execute([':viewname' => $viewname, ':sbas_id' => $this->id]);
- $stmt->closeCursor();
-
- $this->get_appbox()->delete_data_from_cache(appbox::CACHE_LIST_BASES);
- cache_databox::update($this->app, $this->id, 'structure');
-
- $this->viewname = $viewname;
-
- return $this;
- }
-
public function get_ord()
{
return $this->ord;
}
- /**
- * @return appbox
- */
- public function get_appbox()
- {
- return $this->app->getApplicationBox();
- }
-
public function getRootIdentifier()
{
return $this->get_sbas_id();
}
+ /**
+ * Returns current sbas_id
+ *
+ * @return int
+ */
+ public function get_sbas_id()
+ {
+ return $this->id;
+ }
+
public function updateThumbnail($thumbnailType, File $file = null)
{
$this->delete_data_from_cache('printLogo');
}
+ public function delete_data_from_cache($option = null)
+ {
+ switch ($option) {
+ case self::CACHE_CGUS:
+ $this->cgus = null;
+ break;
+ case self::CACHE_META_STRUCT:
+ $this->meta_struct = null;
+ break;
+ case self::CACHE_STRUCTURE:
+ $this->_dom_structure = $this->_xpath_structure = $this->structure = $this->_sxml_structure = null;
+ break;
+ case self::CACHE_THESAURUS:
+ $this->thesaurus = null;
+ unset(self::$_dom_thesaurus[$this->id]);
+ break;
+ default:
+ break;
+ }
+ parent::delete_data_from_cache($option);
+ }
+
+ /**
+ * @return int[]
+ */
+ public function get_collection_unique_ids()
+ {
+ $collectionsIds = [];
+
+ foreach ($this->get_collections() as $collection) {
+ $collectionsIds[] = $collection->get_base_id();
+ }
+
+ return $collectionsIds;
+ }
+
/**
* @return collection[]
*/
public function get_collections()
{
- $ret = [];
+ /** @var CollectionRepositoryRegistry $repositoryRegistry */
+ $repositoryRegistry = $this->app['repo.collections-registry'];
+ $repository = $repositoryRegistry->getRepositoryByDatabox($this->get_sbas_id());
- foreach ($this->get_available_collections() as $coll_id) {
- $ret[] = collection::get_from_coll_id($this->app, $this, $coll_id);
- }
-
- return $ret;
- }
-
- public function get_collection_unique_ids()
- {
- static $base_ids_cache = [];
-
- if (isset($base_ids_cache[$this->id])) {
- return $base_ids_cache[$this->id];
- }
-
- $conn = $this->get_appbox()->get_connection();
- $sql = "SELECT b.base_id FROM bas b WHERE b.sbas_id = :sbas_id AND b.active = '1' ORDER BY b.ord ASC";
- $stmt = $conn->prepare($sql);
- $stmt->execute([':sbas_id' => $this->id]);
- $rs = $stmt->fetchAll(PDO::FETCH_ASSOC);
- $stmt->closeCursor();
-
- $base_ids = [];
- foreach ($rs as $row) {
- $base_ids[] = (int) $row['base_id'];
- }
-
- return $base_ids_cache[$this->id] = $base_ids;
- }
-
- protected function get_available_collections()
- {
- try {
- $data = $this->get_data_from_cache(self::CACHE_COLLECTIONS);
- if (is_array($data)) {
- return $data;
- }
- } catch (\Exception $e) {
-
- }
-
- $conn = $this->get_appbox()->get_connection();
-
- $sql = "SELECT b.server_coll_id FROM sbas s, bas b
- WHERE s.sbas_id = b.sbas_id AND b.sbas_id = :sbas_id
- AND b.active = '1'
- ORDER BY s.ord ASC, b.ord,b.base_id ASC";
- $stmt = $conn->prepare($sql);
- $stmt->execute([':sbas_id' => $this->id]);
- $rs = $stmt->fetchAll(PDO::FETCH_ASSOC);
- $stmt->closeCursor();
-
- $ret = [];
-
- foreach ($rs as $row) {
- $ret[] = (int) $row['server_coll_id'];
- }
-
- $this->set_data_to_cache($ret, self::CACHE_COLLECTIONS);
-
- return $ret;
+ return array_filter($repository->findAll(), function (collection $collection) {
+ return $collection->is_active();
+ });
}
/**
@@ -266,6 +624,36 @@ class databox extends base implements ThumbnailedElement
}
}
+ public function get_viewname()
+ {
+ return $this->viewname ? : $this->connectionSettings->getDatabaseName();
+ }
+
+ public function set_viewname($viewname)
+ {
+ $sql = 'UPDATE sbas SET viewname = :viewname WHERE sbas_id = :sbas_id';
+
+ $stmt = $this->get_appbox()->get_connection()->prepare($sql);
+ $stmt->execute([':viewname' => $viewname, ':sbas_id' => $this->id]);
+ $stmt->closeCursor();
+
+ $this->get_appbox()->delete_data_from_cache(appbox::CACHE_LIST_BASES);
+ cache_databox::update($this->app, $this->id, 'structure');
+
+ $this->viewname = $viewname;
+ $this->databoxRepository->save($this);
+
+ return $this;
+ }
+
+ /**
+ * @return appbox
+ */
+ public function get_appbox()
+ {
+ return $this->app->getApplicationBox();
+ }
+
public function set_label($code, $label)
{
if (!array_key_exists($code, $this->labels)) {
@@ -280,6 +668,8 @@ class databox extends base implements ThumbnailedElement
$this->labels[$code] = $label;
+ $this->databoxRepository->save($this);
+
phrasea::reset_sbasDatas($this->app['phraseanet.appbox']);
return $this;
@@ -292,17 +682,8 @@ class databox extends base implements ThumbnailedElement
{
/** @var StatusStructureFactory $structureFactory */
$structureFactory = $this->app['factory.status-structure'];
- return $structureFactory->getStructure($this);
- }
- /**
- * Returns current sbas_id
- *
- * @return int
- */
- public function get_sbas_id()
- {
- return $this->id;
+ return $structureFactory->getStructure($this);
}
public function get_record_details($sort)
@@ -415,7 +796,7 @@ class databox extends base implements ThumbnailedElement
$old_dbname = $this->get_dbname();
foreach ($this->get_collections() as $collection) {
- $collection->unmount_collection($this->app);
+ $collection->unmount();
}
$query = $this->app['phraseanet.user-query'];
@@ -468,6 +849,7 @@ class databox extends base implements ThumbnailedElement
$stmt->execute([':sbas_id' => $this->id]);
$stmt->closeCursor();
+ $this->databoxRepository->unmount($this);
$this->get_appbox()->delete_data_from_cache(appbox::CACHE_LIST_BASES);
$this->app['dispatcher']->dispatch(
@@ -483,176 +865,6 @@ class databox extends base implements ThumbnailedElement
return;
}
- /**
- * @param Application $app
- * @param Connection $connection
- * @param SplFileInfo $data_template
- * @return databox
- * @throws \Doctrine\DBAL\DBALException
- */
- public static function create(Application $app, Connection $connection, \SplFileInfo $data_template)
- {
- if ( ! file_exists($data_template->getRealPath())) {
- throw new \InvalidArgumentException($data_template->getRealPath() . " does not exist");
- }
-
- $sql = 'SELECT sbas_id
- FROM sbas
- WHERE host = :host AND port = :port AND dbname = :dbname
- AND user = :user AND pwd = :password';
-
- $host = $connection->getHost();
- $port = $connection->getPort();
- $dbname = $connection->getDatabase();
- $user = $connection->getUsername();
- $password = $connection->getPassword();
-
- $params = [
- ':host' => $host,
- ':port' => $port,
- ':dbname' => $dbname,
- ':user' => $user,
- ':password' => $password
- ];
-
- /** @var appbox $appbox */
- $appbox = $app['phraseanet.appbox'];
- $stmt = $appbox->get_connection()->prepare($sql);
- $stmt->execute($params);
- $row = $stmt->fetch(PDO::FETCH_ASSOC);
- $stmt->closeCursor();
-
- if ($row) {
- return $appbox->get_databox((int) $row['sbas_id']);
- }
-
- try {
- $sql = 'CREATE DATABASE `' . $dbname . '`
- CHARACTER SET utf8 COLLATE utf8_unicode_ci';
- $stmt = $connection->prepare($sql);
- $stmt->execute();
- $stmt->closeCursor();
- } catch (\Exception $e) {
-
- }
-
- $sql = 'USE `' . $dbname . '`';
- $stmt = $connection->prepare($sql);
- $stmt->execute();
- $stmt->closeCursor();
-
- $sql = 'SELECT MAX(ord) as ord FROM sbas';
- $stmt = $appbox->get_connection()->prepare($sql);
- $stmt->execute();
- $row = $stmt->fetch(PDO::FETCH_ASSOC);
- $stmt->closeCursor();
-
- if ($row) {
- $ord = $row['ord'] + 1;
- }
-
- $params[':ord'] = $ord;
-
- $sql = 'INSERT INTO sbas (sbas_id, ord, host, port, dbname, sqlengine, user, pwd)
- VALUES (null, :ord, :host, :port, :dbname, "MYSQL", :user, :password)';
- $stmt = $appbox->get_connection()->prepare($sql);
- $stmt->execute($params);
- $stmt->closeCursor();
- $sbas_id = (int) $appbox->get_connection()->lastInsertId();
-
- $app['orm.add']([
- 'host' => $host,
- 'port' => $port,
- 'dbname' => $dbname,
- 'user' => $user,
- 'password' => $password
- ]);
-
- $appbox->delete_data_from_cache(appbox::CACHE_LIST_BASES);
-
- $databox = $appbox->get_databox($sbas_id);
- $databox->insert_datas();
-
- $databox->setNewStructure(
- $data_template, $app['conf']->get(['main', 'storage', 'subdefs'])
- );
-
- $app['dispatcher']->dispatch(
- DataboxEvents::CREATED,
- new CreatedEvent(
- $databox
- )
- );
-
- return $databox;
- }
-
- /**
- *
- * @param Application $app
- * @param string $host
- * @param int $port
- * @param string $user
- * @param string $password
- * @param string $dbname
- * @return databox
- */
- public static function mount(Application $app, $host, $port, $user, $password, $dbname)
- {
- $conn = $app['db.provider']([
- 'host' => $host,
- 'port' => $port,
- 'user' => $user,
- 'password' => $password,
- 'dbname' => $dbname,
- ]);
-
- $conn->connect();
-
- $conn = $app->getApplicationBox()->get_connection();
- $sql = 'SELECT MAX(ord) as ord FROM sbas';
- $stmt = $conn->prepare($sql);
- $stmt->execute();
- $row = $stmt->fetch(PDO::FETCH_ASSOC);
- $stmt->closeCursor();
- if ($row)
- $ord = $row['ord'] + 1;
-
- $sql = 'INSERT INTO sbas (sbas_id, ord, host, port, dbname, sqlengine, user, pwd)
- VALUES (null, :ord, :host, :port, :dbname, "MYSQL", :user, :password)';
- $stmt = $conn->prepare($sql);
- $stmt->execute([
- ':ord' => $ord,
- ':host' => $host,
- ':port' => $port,
- ':dbname' => $dbname,
- ':user' => $user,
- ':password' => $password
- ]);
-
- $stmt->closeCursor();
- $sbas_id = (int) $conn->lastInsertId();
-
- $app->getApplicationBox()->delete_data_from_cache(appbox::CACHE_LIST_BASES);
-
- $databox = $app->findDataboxById($sbas_id);
-
- $databox->delete_data_from_cache(databox::CACHE_COLLECTIONS);
-
- phrasea::reset_sbasDatas($app['phraseanet.appbox']);
-
- cache_databox::update($app, $databox->get_sbas_id(), 'structure');
-
- $app['dispatcher']->dispatch(
- DataboxEvents::MOUNTED,
- new MountedEvent(
- $databox
- )
- );
-
- return $databox;
- }
-
public function get_base_type()
{
return self::BASE_TYPE;
@@ -681,8 +893,7 @@ class databox extends base implements ThumbnailedElement
}
/**
- *
- * @return databox_subdefsStructure
+ * @return databox_subdefsStructure|databox_subdef[][]
*/
public function get_subdef_structure()
{
@@ -693,30 +904,6 @@ class databox extends base implements ThumbnailedElement
return $this->subdef_struct;
}
- public static function dispatch(Filesystem $filesystem, $repository_path, $date = false)
- {
- if (! $date) {
- $date = date('Y-m-d H:i:s');
- }
-
- $repository_path = p4string::addEndSlash($repository_path);
-
- $year = date('Y', strtotime($date));
- $month = date('m', strtotime($date));
- $day = date('d', strtotime($date));
-
- $n = 0;
- $comp = $year . DIRECTORY_SEPARATOR . $month . DIRECTORY_SEPARATOR . $day . DIRECTORY_SEPARATOR;
-
- while (($pathout = $repository_path . $comp . self::addZeros($n)) && is_dir($pathout) && iterator_count(new \DirectoryIterator($pathout)) > 100) {
- $n ++;
- }
-
- $filesystem->mkdir($pathout, 0750);
-
- return $pathout . DIRECTORY_SEPARATOR;
- }
-
public function delete()
{
$old_dbname = $this->get_dbname();
@@ -728,6 +915,8 @@ class databox extends base implements ThumbnailedElement
$this->get_appbox()->delete_data_from_cache(appbox::CACHE_LIST_BASES);
+ $this->databoxRepository->delete($this);
+
$this->app['dispatcher']->dispatch(
DataboxEvents::DELETED,
new DeletedEvent(
@@ -741,11 +930,6 @@ class databox extends base implements ThumbnailedElement
return;
}
- private static function addZeros($n, $length = 5)
- {
- return str_pad($n, $length, '0');
- }
-
public function get_serialized_server_info()
{
return sprintf("%s@%s:%s (MySQL %s)",
@@ -756,27 +940,6 @@ class databox extends base implements ThumbnailedElement
);
}
- public static function get_available_dcfields()
- {
- return [
- databox_Field_DCESAbstract::Contributor => new databox_Field_DCES_Contributor(),
- databox_Field_DCESAbstract::Coverage => new databox_Field_DCES_Coverage(),
- databox_Field_DCESAbstract::Creator => new databox_Field_DCES_Creator(),
- databox_Field_DCESAbstract::Date => new databox_Field_DCES_Date(),
- databox_Field_DCESAbstract::Description => new databox_Field_DCES_Description(),
- databox_Field_DCESAbstract::Format => new databox_Field_DCES_Format(),
- databox_Field_DCESAbstract::Identifier => new databox_Field_DCES_Identifier(),
- databox_Field_DCESAbstract::Language => new databox_Field_DCES_Language(),
- databox_Field_DCESAbstract::Publisher => new databox_Field_DCES_Publisher(),
- databox_Field_DCESAbstract::Relation => new databox_Field_DCES_Relation(),
- databox_Field_DCESAbstract::Rights => new databox_Field_DCES_Rights(),
- databox_Field_DCESAbstract::Source => new databox_Field_DCES_Source(),
- databox_Field_DCESAbstract::Subject => new databox_Field_DCES_Subject(),
- databox_Field_DCESAbstract::Title => new databox_Field_DCES_Title(),
- databox_Field_DCESAbstract::Type => new databox_Field_DCES_Type()
- ];
- }
-
/**
*
* @return Array
@@ -843,54 +1006,6 @@ class databox extends base implements ThumbnailedElement
return $base_ids;
}
- /**
- *
- * @param DOMDocument $dom_struct
- * @return databox
- */
- public function saveStructure(DOMDocument $dom_struct)
- {
- $old_structure = $this->get_dom_structure();
-
- $dom_struct->documentElement
- ->setAttribute("modification_date", $now = date("YmdHis"));
-
- $sql = "UPDATE pref SET value= :structure, updated_on= :now WHERE prop='structure'";
-
- $this->structure = $dom_struct->saveXML();
-
- $stmt = $this->get_connection()->prepare($sql);
- $stmt->execute(
- [
- ':structure' => $this->structure,
- ':now' => $now
- ]
- );
- $stmt->closeCursor();
-
- $this->_sxml_structure = $this->_dom_structure = $this->_xpath_structure = null;
-
- $this->meta_struct = null;
-
- $this->get_appbox()->delete_data_from_cache(appbox::CACHE_LIST_BASES);
- $this->delete_data_from_cache(self::CACHE_STRUCTURE);
- $this->delete_data_from_cache(self::CACHE_META_STRUCT);
-
- cache_databox::update($this->app, $this->id, 'structure');
-
- $this->app['dispatcher']->dispatch(
- DataboxEvents::STRUCTURE_CHANGED,
- new StructureChangedEvent(
- $this,
- array(
- 'dom_before'=>$old_structure
- )
- )
- );
-
- return $this;
- }
-
public function saveCterms(DOMDocument $dom_cterms)
{
$dom_cterms->documentElement->setAttribute("modification_date", $now = date("YmdHis"));
@@ -908,9 +1023,10 @@ class databox extends base implements ThumbnailedElement
$stmt->execute($params);
$stmt->closeCursor();
+ $this->databoxRepository->save($this);
+
return $this;
}
- protected $thesaurus;
public function saveThesaurus(DOMDocument $dom_thesaurus)
{
@@ -925,6 +1041,8 @@ class databox extends base implements ThumbnailedElement
$stmt->closeCursor();
$this->delete_data_from_cache(databox::CACHE_THESAURUS);
+ $this->databoxRepository->save($this);
+
$this->app['dispatcher']->dispatch(
DataboxEvents::THESAURUS_CHANGED,
new ThesaurusChangedEvent(
@@ -938,82 +1056,56 @@ class databox extends base implements ThumbnailedElement
return $this;
}
- public function setNewStructure(\SplFileInfo $data_template, $path_doc)
+ /**
+ * @return DOMDocument
+ */
+ public function get_dom_thesaurus()
{
- if ( ! file_exists($data_template->getPathname())) {
- throw new \InvalidArgumentException(sprintf('File %s does not exists'));
+ $sbas_id = $this->id;
+ if (isset(self::$_dom_thesaurus[$sbas_id])) {
+ return self::$_dom_thesaurus[$sbas_id];
}
- $contents = file_get_contents($data_template->getPathname());
+ $thesaurus = $this->get_thesaurus();
- $contents = str_replace(
- ["{{basename}}", "{{datapathnoweb}}"]
- , [$this->connectionSettings->getDatabaseName(), rtrim($path_doc, '/').'/']
- , $contents
- );
+ $dom = new DOMDocument();
- $dom_doc = new DOMDocument();
- $dom_doc->loadXML($contents);
- $this->saveStructure($dom_doc);
+ if ($thesaurus && false !== $dom->loadXML($thesaurus)) {
+ self::$_dom_thesaurus[$sbas_id] = $dom;
+ } else {
+ self::$_dom_thesaurus[$sbas_id] = false;
+ unset($dom);
+ }
- $this->feed_meta_fields();
-
- return $this;
+ return self::$_dom_thesaurus[$sbas_id];
}
- public function feed_meta_fields()
+ /**
+ * @return string
+ */
+ public function get_thesaurus()
{
- $sxe = $this->get_sxml_structure();
+ try {
+ $this->thesaurus = $this->get_data_from_cache(self::CACHE_THESAURUS);
- foreach ($sxe->description->children() as $fname => $field) {
- $dom_struct = $this->get_dom_structure();
- $xp_struct = $this->get_xpath_structure();
- $fname = (string) $fname;
- $src = trim(isset($field['src']) ? str_replace('/rdf:RDF/rdf:Description/', '', $field['src']) : '');
-
- $meta_id = isset($field['meta_id']) ? $field['meta_id'] : null;
- if ( ! is_null($meta_id))
- continue;
-
- $nodes = $xp_struct->query('/record/description/' . $fname);
- if ($nodes->length > 0) {
- $nodes->item(0)->parentNode->removeChild($nodes->item(0));
- }
- $this->saveStructure($dom_struct);
-
- $type = isset($field['type']) ? $field['type'] : 'string';
- $type = in_array($type
- , [
- databox_field::TYPE_DATE
- , databox_field::TYPE_NUMBER
- , databox_field::TYPE_STRING
- , databox_field::TYPE_TEXT
- ]
- ) ? $type : databox_field::TYPE_STRING;
-
- $multi = isset($field['multi']) ? (Boolean) (string) $field['multi'] : false;
-
- $meta_struct_field = databox_field::create($this->app, $this, $fname, $multi);
- $meta_struct_field
- ->set_readonly(isset($field['readonly']) ? (string) $field['readonly'] : 0)
- ->set_indexable(isset($field['index']) ? (string) $field['index'] : '1')
- ->set_separator(isset($field['separator']) ? (string) $field['separator'] : '')
- ->set_required((isset($field['required']) && (string) $field['required'] == 1))
- ->set_business((isset($field['business']) && (string) $field['business'] == 1))
- ->set_aggregable((isset($field['aggregable']) ? (string) $field['aggregable'] : 0))
- ->set_type($type)
- ->set_tbranch(isset($field['tbranch']) ? (string) $field['tbranch'] : '')
- ->set_thumbtitle(isset($field['thumbtitle']) ? (string) $field['thumbtitle'] : (isset($field['thumbTitle']) ? $field['thumbTitle'] : '0'))
- ->set_report(isset($field['report']) ? (string) $field['report'] : '1')
- ->save();
-
- try {
- $meta_struct_field->set_tag(\databox_field::loadClassFromTagName($src))->save();
- } catch (\Exception $e) {
- }
+ return $this->thesaurus;
+ } catch (\Exception $e) {
+ unset($e);
}
- return $this;
+ try {
+ $sql = 'SELECT value AS thesaurus FROM pref WHERE prop="thesaurus" LIMIT 1;';
+ $stmt = $this->get_connection()->prepare($sql);
+ $stmt->execute();
+ $row = $stmt->fetch(PDO::FETCH_ASSOC);
+ $stmt->closeCursor();
+ $this->thesaurus = $row['thesaurus'];
+ $this->set_data_to_cache($this->thesaurus, self::CACHE_THESAURUS);
+ } catch (\Exception $e) {
+ unset($e);
+ }
+
+ return $this->thesaurus;
}
/**
@@ -1075,20 +1167,6 @@ class databox extends base implements ThumbnailedElement
return $this;
}
- /**
- *
- * @param int $sbas_id
- * @return string
- */
- public static function getPrintLogo($sbas_id)
- {
- $out = '';
- if (is_file(($filename = __DIR__ . '/../../config/minilogos/'.\databox::PIC_PDF.'_' . $sbas_id . '.jpg')))
- $out = file_get_contents($filename);
-
- return $out;
- }
-
public function clear_logs()
{
foreach (['log', 'log_colls', 'log_docs', 'log_search', 'log_view', 'log_thumb'] as $table) {
@@ -1118,30 +1196,6 @@ class databox extends base implements ThumbnailedElement
return $this;
}
- /**
- * @return DOMDocument
- */
- public function get_dom_thesaurus()
- {
- $sbas_id = $this->id;
- if (isset(self::$_dom_thesaurus[$sbas_id])) {
- return self::$_dom_thesaurus[$sbas_id];
- }
-
- $thesaurus = $this->get_thesaurus();
-
- $dom = new DOMDocument();
-
- if ($thesaurus && false !== $dom->loadXML($thesaurus)) {
- self::$_dom_thesaurus[$sbas_id] = $dom;
- } else {
- self::$_dom_thesaurus[$sbas_id] = false;
- unset($dom);
- }
-
- return self::$_dom_thesaurus[$sbas_id];
- }
-
/**
* @return DOMXpath
*/
@@ -1182,121 +1236,6 @@ class databox extends base implements ThumbnailedElement
return self::$_sxml_thesaurus[$sbas_id];
}
- /**
- * @return string
- */
- public function get_thesaurus()
- {
- try {
- $this->thesaurus = $this->get_data_from_cache(self::CACHE_THESAURUS);
-
- return $this->thesaurus;
- } catch (\Exception $e) {
- unset($e);
- }
-
- try {
- $sql = 'SELECT value AS thesaurus FROM pref WHERE prop="thesaurus" LIMIT 1;';
- $stmt = $this->get_connection()->prepare($sql);
- $stmt->execute();
- $row = $stmt->fetch(PDO::FETCH_ASSOC);
- $stmt->closeCursor();
- $this->thesaurus = $row['thesaurus'];
- $this->set_data_to_cache($this->thesaurus, self::CACHE_THESAURUS);
- } catch (\Exception $e) {
- unset($e);
- }
-
- return $this->thesaurus;
- }
-
- /**
- * @return string
- */
- public function get_structure()
- {
- if ($this->structure) {
- return $this->structure;
- }
-
- $this->structure = $this->retrieve_structure();
-
- return $this->structure;
- }
-
- protected function retrieve_structure()
- {
- try {
- $data = $this->get_data_from_cache(self::CACHE_STRUCTURE);
- if (is_array($data)) {
- return $data;
- }
- } catch (\Exception $e) {
-
- }
-
- $structure = null;
- $sql = "SELECT value FROM pref WHERE prop='structure'";
- $stmt = $this->get_connection()->prepare($sql);
- $stmt->execute();
- $row = $stmt->fetch(PDO::FETCH_ASSOC);
- $stmt->closeCursor();
-
- if ($row)
- $structure = $row['value'];
- $this->set_data_to_cache($structure, self::CACHE_STRUCTURE);
-
- return $structure;
- }
- protected $cterms;
-
- /**
- *
- * @return string
- */
- public function get_cterms()
- {
- if ($this->cterms) {
- return $this->cterms;
- }
-
- $sql = "SELECT value FROM pref WHERE prop='cterms'";
- $stmt = $this->get_connection()->prepare($sql);
- $stmt->execute();
- $row = $stmt->fetch(PDO::FETCH_ASSOC);
- $stmt->closeCursor();
-
- if ($row)
- $this->cterms = $row['value'];
-
- return $this->cterms;
- }
-
- /**
- * @return DOMDocument
- */
- public function get_dom_structure()
- {
- if ($this->_dom_structure) {
- return $this->_dom_structure;
- }
-
- $structure = $this->get_structure();
-
- $dom = new DOMDocument();
-
- $dom->standalone = true;
- $dom->preserveWhiteSpace = false;
- $dom->formatOutput = true;
-
- if ($structure && $dom->loadXML($structure) !== false)
- $this->_dom_structure = $dom;
- else
- $this->_dom_structure = false;
-
- return $this->_dom_structure;
- }
-
/**
* @return DOMDocument
*/
@@ -1324,87 +1263,57 @@ class databox extends base implements ThumbnailedElement
/**
*
- * @return SimpleXMLElement
+ * @return string
*/
- public function get_sxml_structure()
+ public function get_cterms()
{
- if ($this->_sxml_structure) {
- return $this->_sxml_structure;
+ if ($this->cterms) {
+ return $this->cterms;
}
- $structure = $this->get_structure();
+ $sql = "SELECT value FROM pref WHERE prop='cterms'";
+ $stmt = $this->get_connection()->prepare($sql);
+ $stmt->execute();
+ $row = $stmt->fetch(PDO::FETCH_ASSOC);
+ $stmt->closeCursor();
- if ($structure && false !== $tmp = simplexml_load_string($structure))
- $this->_sxml_structure = $tmp;
- else
- $this->_sxml_structure = false;
+ if ($row)
+ $this->cterms = $row['value'];
- return $this->_sxml_structure;
+ return $this->cterms;
}
- /**
- * @return DOMXpath
- */
- public function get_xpath_structure()
+ public function update_cgus($locale, $terms, $reset_date)
{
- if ($this->_xpath_structure) {
- return $this->_xpath_structure;
- }
+ $old_tou = $this->get_cgus();
- $dom_doc = $this->get_dom_structure();
+ $terms = str_replace(["\r\n", "\n", "\r"], ['', '', ''], strip_tags($terms, ''));
+ $sql = 'UPDATE pref SET value = :terms ';
- if ($dom_doc && ($tmp = new DOMXpath($dom_doc)) !== false)
- $this->_xpath_structure = $tmp;
- else
- $this->_xpath_structure = false;
+ if ($reset_date)
+ $sql .= ', updated_on=NOW() ';
- return $this->_xpath_structure;
+ $sql .= ' WHERE prop="ToU" AND locale = :locale';
+
+ $stmt = $this->get_connection()->prepare($sql);
+ $stmt->execute([':terms' => $terms, ':locale' => $locale]);
+ $stmt->closeCursor();
+ $this->cgus = null;
+ $this->delete_data_from_cache(self::CACHE_CGUS);
+
+ $this->app['dispatcher']->dispatch(
+ DataboxEvents::TOU_CHANGED,
+ new TouChangedEvent(
+ $this,
+ array(
+ 'tou_before'=>$old_tou,
+ )
+ )
+ );
+
+ return $this;
}
- /**
- * @param TranslatorInterface $translator
- * @param string $structure
- * @return Array
- */
- public static function get_structure_errors(TranslatorInterface $translator, $structure)
- {
- $sx_structure = simplexml_load_string($structure);
-
- $subdefgroup = $sx_structure->subdefs[0];
- $AvSubdefs = [];
-
- $errors = [];
-
- foreach ($subdefgroup as $k => $subdefs) {
- $subdefgroup_name = trim((string) $subdefs->attributes()->name);
-
- if ($subdefgroup_name == '') {
- $errors[] = $translator->trans('ERREUR : TOUTES LES BALISES subdefgroup necessitent un attribut name');
- continue;
- }
-
- if ( ! isset($AvSubdefs[$subdefgroup_name]))
- $AvSubdefs[$subdefgroup_name] = [];
-
- foreach ($subdefs as $sd) {
- $sd_name = trim(mb_strtolower((string) $sd->attributes()->name));
- $sd_class = trim(mb_strtolower((string) $sd->attributes()->class));
- if ($sd_name == '' || isset($AvSubdefs[$subdefgroup_name][$sd_name])) {
- $errors[] = $translator->trans('ERREUR : Les name de subdef sont uniques par groupe de subdefs et necessaire');
- continue;
- }
- if ( ! in_array($sd_class, ['thumbnail', 'preview', 'document'])) {
- $errors[] = $translator->trans('ERREUR : La classe de subdef est necessaire et egal a "thumbnail","preview" ou "document"');
- continue;
- }
- $AvSubdefs[$subdefgroup_name][$sd_name] = $sd;
- }
- }
-
- return $errors;
- }
- protected $cgus;
-
public function get_cgus()
{
if ($this->cgus) {
@@ -1416,6 +1325,88 @@ class databox extends base implements ThumbnailedElement
return $this->cgus;
}
+ public function __sleep()
+ {
+ $this->_sxml_structure = $this->_dom_structure = $this->_xpath_structure = null;
+
+ $vars = [];
+
+ foreach ($this as $key => $value) {
+ if (in_array($key, ['app', 'meta_struct'])) {
+ continue;
+ }
+
+ $vars[] = $key;
+ }
+
+ return $vars;
+ }
+
+ public function hydrate(Application $app)
+ {
+ $this->app = $app;
+ }
+
+ /**
+ * Tells whether the registration is enable or not.
+ *
+ * @return boolean
+ */
+ public function isRegistrationEnabled()
+ {
+ if (false !== $xml = $this->get_sxml_structure()) {
+ foreach ($xml->xpath('/record/caninscript') as $canRegister) {
+ if (false !== (Boolean) (string) $canRegister) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Return an array that can be used to restore databox.
+ *
+ * @return array
+ */
+ public function getRawData()
+ {
+ return [
+ 'ord' => $this->ord,
+ 'viewname' => $this->viewname,
+ 'label_en' => $this->labels['en'],
+ 'label_fr' => $this->labels['fr'],
+ 'label_de' => $this->labels['de'],
+ 'label_nl' => $this->labels['nl'],
+ ];
+ }
+
+ protected function retrieve_structure()
+ {
+ try {
+ $data = $this->get_data_from_cache(self::CACHE_STRUCTURE);
+ if (is_array($data)) {
+ return $data;
+ }
+ } catch (\Exception $e) {
+
+ }
+
+ $structure = null;
+ $sql = "SELECT value FROM pref WHERE prop='structure'";
+ $stmt = $this->get_connection()->prepare($sql);
+ $stmt->execute();
+ $row = $stmt->fetch(PDO::FETCH_ASSOC);
+ $stmt->closeCursor();
+
+ if ($row)
+ $structure = $row['value'];
+ $this->set_data_to_cache($structure, self::CACHE_STRUCTURE);
+
+ return $structure;
+ }
+
protected function load_cgus()
{
try {
@@ -1463,105 +1454,6 @@ class databox extends base implements ThumbnailedElement
return $this;
}
- const CACHE_CGUS = 'cgus';
-
- public function update_cgus($locale, $terms, $reset_date)
- {
- $old_tou = $this->get_cgus();
-
- $terms = str_replace(["\r\n", "\n", "\r"], ['', '', ''], strip_tags($terms, '
'));
- $sql = 'UPDATE pref SET value = :terms ';
-
- if ($reset_date)
- $sql .= ', updated_on=NOW() ';
-
- $sql .= ' WHERE prop="ToU" AND locale = :locale';
-
- $stmt = $this->get_connection()->prepare($sql);
- $stmt->execute([':terms' => $terms, ':locale' => $locale]);
- $stmt->closeCursor();
- $this->cgus = null;
- $this->delete_data_from_cache(self::CACHE_CGUS);
-
- $this->app['dispatcher']->dispatch(
- DataboxEvents::TOU_CHANGED,
- new TouChangedEvent(
- $this,
- array(
- 'tou_before'=>$old_tou,
- )
- )
- );
-
- return $this;
- }
-
- public function __sleep()
- {
- $this->_sxml_structure = $this->_dom_structure = $this->_xpath_structure = null;
-
- $vars = [];
-
- foreach ($this as $key => $value) {
- if (in_array($key, ['app', 'meta_struct'])) {
- continue;
- }
-
- $vars[] = $key;
- }
-
- return $vars;
- }
-
- public function hydrate(Application $app)
- {
- $this->app = $app;
- }
-
- public function delete_data_from_cache($option = null)
- {
- switch ($option) {
- case self::CACHE_CGUS:
- $this->cgus = null;
- break;
- case self::CACHE_META_STRUCT:
- $this->meta_struct = null;
- break;
- case self::CACHE_STRUCTURE:
- $this->_dom_structure = $this->_xpath_structure = $this->structure = $this->_sxml_structure = null;
- break;
- case self::CACHE_THESAURUS:
- $this->thesaurus = null;
- unset(self::$_dom_thesaurus[$this->id]);
- break;
- default:
- break;
- }
- parent::delete_data_from_cache($option);
- }
-
- public static function purge()
- {
- self::$_xpath_thesaurus = self::$_dom_thesaurus = self::$_thesaurus = self::$_sxml_thesaurus = [];
- }
-
- /**
- * Tells whether the registration is enable or not.
- *
- * @return boolean
- */
- public function isRegistrationEnabled()
- {
- if (false !== $xml = $this->get_sxml_structure()) {
- foreach ($xml->xpath('/record/caninscript') as $canRegister) {
- if (false !== (Boolean) (string) $canRegister) {
- return true;
- }
- }
- }
-
- return false;
- }
/**
* @param array $row
@@ -1575,21 +1467,4 @@ class databox extends base implements ThumbnailedElement
$this->labels['de'] = $row['label_de'];
$this->labels['nl'] = $row['label_nl'];
}
-
- /**
- * Return an array that can be used to restore databox.
- *
- * @return array
- */
- public function getRawData()
- {
- return [
- 'ord' => $this->ord,
- 'viewname' => $this->viewname,
- 'label_en' => $this->labels['en'],
- 'label_fr' => $this->labels['fr'],
- 'label_de' => $this->labels['de'],
- 'label_nl' => $this->labels['nl'],
- ];
- }
}
diff --git a/lib/classes/databox/subdef.php b/lib/classes/databox/subdef.php
index 3dc36a17ad..653ac83a1e 100644
--- a/lib/classes/databox/subdef.php
+++ b/lib/classes/databox/subdef.php
@@ -16,6 +16,7 @@ use Alchemy\Phrasea\Media\Subdef\FlexPaper;
use Alchemy\Phrasea\Media\Subdef\Gif;
use Alchemy\Phrasea\Media\Subdef\Subdef as SubdefSpecs;
use Alchemy\Phrasea\Media\Type\Type as SubdefType;
+use MediaAlchemyst\Specification\SpecificationInterface;
use Symfony\Component\Translation\TranslatorInterface;
class databox_subdef
@@ -32,7 +33,11 @@ class databox_subdef
protected $path;
protected $subdef_group;
protected $labels = [];
- protected $write_meta;
+
+ /**
+ * @var bool
+ */
+ private $requiresMetadataUpdate;
protected $downloadable;
protected $translator;
protected static $mediaTypeToSubdefTypes = [
@@ -74,7 +79,7 @@ class databox_subdef
$this->downloadable = p4field::isyes($sd->attributes()->downloadable);
$this->path = trim($sd->path) !== '' ? p4string::addEndSlash(trim($sd->path)) : '';
- $this->write_meta = p4field::isyes((string) $sd->meta);
+ $this->requiresMetadataUpdate = p4field::isyes((string) $sd->meta);
foreach ($sd->label as $label) {
$lang = trim((string) $label->attributes()->lang);
@@ -242,13 +247,13 @@ class databox_subdef
}
/**
- * Tells us if we have to write meta datas in the subdef
+ * Tells us if we have to write meta data in the subdef
*
- * @return boolean
+ * @return bool
*/
- public function meta_writeable()
+ public function isMetadataUpdateRequired()
{
- return $this->write_meta;
+ return $this->requiresMetadataUpdate;
}
/**
@@ -264,7 +269,7 @@ class databox_subdef
/**
* Get the MediaAlchemyst specs for the current subdef
*
- * @return type
+ * @return SpecificationInterface
*/
public function getSpecs()
{
diff --git a/lib/classes/databox/subdefsStructure.php b/lib/classes/databox/subdefsStructure.php
index 085137f473..58b0556451 100644
--- a/lib/classes/databox/subdefsStructure.php
+++ b/lib/classes/databox/subdefsStructure.php
@@ -1,5 +1,4 @@
translator = $translator;
$this->load_subdefs();
-
- return $this->AvSubdefs;
}
/**
@@ -75,10 +74,6 @@ class databox_subdefsStructure implements IteratorAggregate, Countable
return null;
}
- /**
- *
- * @return databox_subdefsStructure
- */
protected function load_subdefs()
{
$sx_struct = $this->databox->get_sxml_structure();
@@ -92,7 +87,7 @@ class databox_subdefsStructure implements IteratorAggregate, Countable
];
if (! $sx_struct) {
- return $this;
+ return;
}
$subdefgroup = $sx_struct->subdefs[0];
@@ -132,15 +127,13 @@ class databox_subdefsStructure implements IteratorAggregate, Countable
}
}
$this->AvSubdefs = $avSubdefs;
-
- return $this;
}
/**
- * @param $subdef_type
- * @param $subdef_name
+ * @param string $subdef_type
+ * @param string $subdef_name
*
- * @return mixed
+ * @return databox_subdef
* @throws Exception_Databox_SubdefNotFound
*/
public function get_subdef($subdef_type, $subdef_name)
@@ -224,13 +217,14 @@ class databox_subdefsStructure implements IteratorAggregate, Countable
}
/**
- *
- * @param string $group
- * @param string $name
- * @param string $class
- * @param boolean $downloadable
- * @param Array $options
+ * @param string $group
+ * @param string $name
+ * @param string $class
+ * @param boolean $downloadable
+ * @param array $options
+ * @param array $labels
* @return databox_subdefsStructure
+ * @throws Exception
*/
public function set_subdef($group, $name, $class, $downloadable, $options, $labels)
{
diff --git a/lib/classes/media/abstract.php b/lib/classes/media/abstract.php
index 58c941eb5e..381b3ea7b4 100644
--- a/lib/classes/media/abstract.php
+++ b/lib/classes/media/abstract.php
@@ -13,45 +13,37 @@ use Guzzle\Http\Url;
abstract class media_abstract
{
+ const PORTRAIT = 'PORTRAIT';
+ const LANDSCAPE = 'LANDSCAPE';
+
/**
- *
* @var Url
*/
protected $url;
/**
- *
* @var int
*/
protected $height;
/**
- *
* @var int
*/
protected $width;
- const PORTRAIT = 'PORTRAIT';
- const PAYSAGE = 'LANDSCAPE';
-
/**
- *
- * @param string $url
- * @param int $width
- * @param int $height
- * @return media
+ * @param int $width
+ * @param int $height
+ * @param Url|null $url
*/
- public function __construct(Url $url, $width, $height)
+ public function __construct($width, $height, Url $url = null)
{
$this->url = $url;
$this->height = (int) $height;
$this->width = (int) $width;
-
- return $this;
}
/**
- *
* @return Url
*/
public function get_url()
@@ -60,7 +52,6 @@ abstract class media_abstract
}
/**
- *
* @return int
*/
public function get_width()
@@ -69,7 +60,6 @@ abstract class media_abstract
}
/**
- *
* @return int
*/
public function get_height()
@@ -78,7 +68,6 @@ abstract class media_abstract
}
/**
- *
* @return string
*/
public function get_type()
@@ -87,33 +76,30 @@ abstract class media_abstract
}
/**
- *
* @return string
*/
- public function get_orientation()
+ public function getOrientation()
{
if ($this->width > $this->height) {
- return self::PAYSAGE;
+ return self::LANDSCAPE;
} else {
return self::PORTRAIT;
}
}
/**
- *
- * @return boolean
+ * @return bool
*/
- public function is_paysage()
+ public function isLandscape()
{
- return $this->get_orientation() == self::PAYSAGE;
+ return $this->getOrientation() == self::LANDSCAPE;
}
/**
- *
- * @return boolean
+ * @return bool
*/
- public function is_portrait()
+ public function isPortrait()
{
- return $this->get_orientation() == self::PORTRAIT;
+ return $this->getOrientation() == self::PORTRAIT;
}
}
diff --git a/lib/classes/media/adapter.php b/lib/classes/media/adapter.php
index 42c9efffba..ff7ffc9866 100644
--- a/lib/classes/media/adapter.php
+++ b/lib/classes/media/adapter.php
@@ -1,5 +1,4 @@
load($substitute);
$this->generate_url();
+
+ parent::__construct($this->width, $this->height, $this->url);
}
/**
- *
- * @param boolean $substitute
- * @return media_subdef
+ * @param bool $substitute
+ * @return $this
*/
protected function load($substitute)
{
@@ -209,7 +174,7 @@ class media_subdef extends media_abstract implements cache_cacheableInterface
$this->mime = $row['mime'];
$this->file = $row['file'];
$this->path = p4string::addEndSlash($row['path']);
- $this->is_physically_present = file_exists($this->get_pathfile());
+ $this->is_physically_present = file_exists($this->getRealPath());
$this->etag = $row['etag'];
$this->is_substituted = ! ! $row['substit'];
$this->subdef_id = (int) $row['subdef_id'];
@@ -228,19 +193,19 @@ class media_subdef extends media_abstract implements cache_cacheableInterface
}
$datas = [
- 'mime' => $this->mime
- , 'width' => $this->width
- , 'size' => $this->size
- , 'height' => $this->height
- , 'etag' => $this->etag
- , 'path' => $this->path
- , 'url' => $this->url
- , 'file' => $this->file
- , 'physically_present' => $this->is_physically_present
- , 'is_substituted' => $this->is_substituted
- , 'subdef_id' => $this->subdef_id
- , 'modification_date' => $this->modification_date
- , 'creation_date' => $this->creation_date
+ 'mime' => $this->mime,
+ 'width' => $this->width,
+ 'size' => $this->size,
+ 'height' => $this->height,
+ 'etag' => $this->etag,
+ 'path' => $this->path,
+ 'url' => $this->url,
+ 'file' => $this->file,
+ 'physically_present' => $this->is_physically_present,
+ 'is_substituted' => $this->is_substituted,
+ 'subdef_id' => $this->subdef_id,
+ 'modification_date' => $this->modification_date,
+ 'creation_date' => $this->creation_date,
];
$this->set_data_to_cache($datas);
@@ -255,8 +220,8 @@ class media_subdef extends media_abstract implements cache_cacheableInterface
*/
public function remove_file()
{
- if ($this->is_physically_present() && is_writable($this->get_pathfile())) {
- unlink($this->get_pathfile());
+ if ($this->is_physically_present() && is_writable($this->getRealPath())) {
+ unlink($this->getRealPath());
$this->delete_data_from_cache();
@@ -308,8 +273,7 @@ class media_subdef extends media_abstract implements cache_cacheableInterface
}
/**
- *
- * @return boolean
+ * @return bool
*/
public function is_physically_present()
{
@@ -317,7 +281,6 @@ class media_subdef extends media_abstract implements cache_cacheableInterface
}
/**
- *
* @return record_adapter
*/
public function get_record()
@@ -326,7 +289,6 @@ class media_subdef extends media_abstract implements cache_cacheableInterface
}
/**
- *
* @return media_Permalink_Adapter
*/
public function get_permalink()
@@ -338,7 +300,6 @@ class media_subdef extends media_abstract implements cache_cacheableInterface
}
/**
- *
* @return int
*/
public function get_record_id()
@@ -349,7 +310,7 @@ class media_subdef extends media_abstract implements cache_cacheableInterface
public function getEtag()
{
if (!$this->etag && $this->is_physically_present()) {
- $file = new SplFileInfo($this->get_pathfile());
+ $file = new SplFileInfo($this->getRealPath());
if ($file->isFile()) {
$this->setEtag(md5($file->getMTime()));
}
@@ -388,7 +349,6 @@ class media_subdef extends media_abstract implements cache_cacheableInterface
}
/**
- *
* @return int
*/
public function get_sbas_id()
@@ -397,7 +357,6 @@ class media_subdef extends media_abstract implements cache_cacheableInterface
}
/**
- *
* @return string
*/
public function get_type()
@@ -430,7 +389,6 @@ class media_subdef extends media_abstract implements cache_cacheableInterface
}
/**
- *
* @return string
*/
public function get_mime()
@@ -439,7 +397,6 @@ class media_subdef extends media_abstract implements cache_cacheableInterface
}
/**
- *
* @return string
*/
public function get_path()
@@ -448,7 +405,6 @@ class media_subdef extends media_abstract implements cache_cacheableInterface
}
/**
- *
* @return string
*/
public function get_file()
@@ -457,7 +413,6 @@ class media_subdef extends media_abstract implements cache_cacheableInterface
}
/**
- *
* @return int
*/
public function get_size()
@@ -466,7 +421,6 @@ class media_subdef extends media_abstract implements cache_cacheableInterface
}
/**
- *
* @return string
*/
public function get_name()
@@ -475,7 +429,6 @@ class media_subdef extends media_abstract implements cache_cacheableInterface
}
/**
- *
* @return int
*/
public function get_subdef_id()
@@ -484,8 +437,7 @@ class media_subdef extends media_abstract implements cache_cacheableInterface
}
/**
- *
- * @return boolean
+ * @return bool
*/
public function is_substituted()
{
@@ -493,16 +445,15 @@ class media_subdef extends media_abstract implements cache_cacheableInterface
}
/**
- *
* @return string
+ * @deprecated use {@link self::getRealPath} instead
*/
public function get_pathfile()
{
- return $this->path . $this->file;
+ return $this->getRealPath();
}
/**
- *
* @return DateTime
*/
public function get_modification_date()
@@ -511,7 +462,6 @@ class media_subdef extends media_abstract implements cache_cacheableInterface
}
/**
- *
* @return DateTime
*/
public function get_creation_date()
@@ -520,7 +470,6 @@ class media_subdef extends media_abstract implements cache_cacheableInterface
}
/**
- *
* @return string
*/
public function renew_url()
@@ -561,7 +510,6 @@ class media_subdef extends media_abstract implements cache_cacheableInterface
}
/**
- *
* @param int $angle
* @param Alchemyst $alchemyst
* @param MediaVorus $mediavorus
@@ -578,12 +526,12 @@ class media_subdef extends media_abstract implements cache_cacheableInterface
$specs->setRotationAngle($angle);
try {
- $alchemyst->turnInto($this->get_pathfile(), $this->get_pathfile(), $specs);
+ $alchemyst->turnInto($this->getRealPath(), $this->getRealPath(), $specs);
} catch (\MediaAlchemyst\Exception\ExceptionInterface $e) {
return $this;
}
- $media = $mediavorus->guess($this->get_pathfile());
+ $media = $mediavorus->guess($this->getRealPath());
$sql = "UPDATE subdef SET height = :height , width = :width, updated_on = NOW()"
. " WHERE record_id = :record_id AND name = :name";
@@ -621,7 +569,7 @@ class media_subdef extends media_abstract implements cache_cacheableInterface
return [];
}
- $media = $mediavorus->guess($this->get_pathfile());
+ $media = $mediavorus->guess($this->getRealPath());
$datas = [];
@@ -718,7 +666,7 @@ class media_subdef extends media_abstract implements cache_cacheableInterface
if ($name === 'thumbnail') {
/** @var SymLinker $symlinker */
$symlinker = $app['phraseanet.thumb-symlinker'];
- $symlinker->symlink($subdef->get_pathfile());
+ $symlinker->symlink($subdef->getRealPath());
}
unset($media);
@@ -726,9 +674,6 @@ class media_subdef extends media_abstract implements cache_cacheableInterface
return $subdef;
}
- /**
- *
- */
protected function generate_url()
{
if (!$this->is_physically_present()) {
@@ -737,24 +682,28 @@ class media_subdef extends media_abstract implements cache_cacheableInterface
// serve thumbnails using static file service
if ($this->get_name() === 'thumbnail') {
- if (null !== $url = $this->app['phraseanet.static-file']->getUrl($this->get_pathfile())) {
- $this->url = $url. "?etag=".$this->getEtag();
+ if (null !== $url = $this->app['phraseanet.static-file']->getUrl($this->getRealPath())) {
+ $url->getQuery()->offsetSet('etag', $this->getEtag());
+ $this->url = $url;
return;
}
}
if ($this->app['phraseanet.h264-factory']->isH264Enabled() && in_array($this->mime, ['video/mp4'])) {
- if (null !== $url = $this->app['phraseanet.h264']->getUrl($this->get_pathfile())) {
+ if (null !== $url = $this->app['phraseanet.h264']->getUrl($this->getRealPath())) {
$this->url = $url;
return;
}
}
- $this->url = Url::factory("/datafiles/" . $this->record->getDataboxId()
- . "/" . $this->record->getRecordId() . "/"
- . $this->get_name() . "/?etag=".$this->getEtag());
+ $this->url = Url::factory($this->app->path('datafile', [
+ 'sbas_id' => $this->record->getDataboxId(),
+ 'record_id' => $this->record->getRecordId(),
+ 'subdef' => $this->get_name(),
+ 'etag' => $this->getEtag(),
+ ]));
return;
}
@@ -786,4 +735,28 @@ class media_subdef extends media_abstract implements cache_cacheableInterface
$databox->delete_data_from_cache($this->get_cache_key($option));
}
+
+ /**
+ * @return string
+ */
+ public function getRealPath()
+ {
+ return $this->path . $this->file;
+ }
+
+ /**
+ * @return string
+ */
+ public function getWatermarkRealPath()
+ {
+ return $this->path . 'watermark_' . $this->file;
+ }
+
+ /**
+ * @return string
+ */
+ public function getStampRealPath()
+ {
+ return $this->path . 'stamp_' . $this->file;
+ }
}
diff --git a/lib/classes/module/console/systemExport.php b/lib/classes/module/console/systemExport.php
index f64f7580f4..272124fb75 100644
--- a/lib/classes/module/console/systemExport.php
+++ b/lib/classes/module/console/systemExport.php
@@ -242,12 +242,12 @@ class module_console_systemExport extends Command
protected function processRecords(\record_adapter $record, $outfile, $caption)
{
- if ( ! file_exists($record->get_subdef('document')->get_pathfile())) {
+ if ( ! file_exists($record->get_subdef('document')->getRealPath())) {
return false;
}
$this->getService('filesystem')
- ->copy($record->get_subdef('document')->get_pathfile(), $outfile);
+ ->copy($record->get_subdef('document')->getRealPath(), $outfile);
$dest_file = new \SplFileInfo($outfile);
diff --git a/lib/classes/module/console/systemUpgrade.php b/lib/classes/module/console/systemUpgrade.php
index 4652aa0f93..33bdb7f3b4 100644
--- a/lib/classes/module/console/systemUpgrade.php
+++ b/lib/classes/module/console/systemUpgrade.php
@@ -35,6 +35,16 @@ class module_console_systemUpgrade extends Command
protected function doExecute(InputInterface $input, OutputInterface $output)
{
+ $pluginAutoloader = rtrim($this->container['root.path'], '\\/') . '/plugins/autoload.php';
+
+ if (file_exists($pluginAutoloader)) {
+ $serviceProvider = new \Alchemy\Phrasea\Core\Provider\PluginServiceProvider();
+ $serviceProvider->register($this->getContainer());
+ $serviceProvider->boot($this->getContainer());
+ }
+
+ $this->getContainer()->loadPlugins();
+
$interactive = !$input->getOption('yes');
while ($migrations = $this->container['phraseanet.configuration-tester']->getMigrations()) {
diff --git a/lib/classes/patch/370alpha6a.php b/lib/classes/patch/370alpha6a.php
index 478e55bd15..7f3472c260 100644
--- a/lib/classes/patch/370alpha6a.php
+++ b/lib/classes/patch/370alpha6a.php
@@ -48,6 +48,9 @@ class patch_370alpha6a extends patchAbstract
*/
public function apply(base $databox, Application $app)
{
+ /**
+ * @var databox $databox
+ */
$structure = $databox->get_structure();
$DOM = new DOMDocument();
@@ -90,7 +93,7 @@ class patch_370alpha6a extends patchAbstract
return true;
}
- protected function addScreenDeviceOption($root, $subdef, $groupname)
+ protected function addScreenDeviceOption($root, databox_subdef $subdef, $groupname)
{
$optionsSubdef = $subdef->getOptions();
@@ -102,7 +105,7 @@ class patch_370alpha6a extends patchAbstract
$options['path'] = $subdef->get_path();
$options['mediatype'] = $subdef->getSubdefType()->getType();
- $options['meta'] = $subdef->meta_writeable() ? 'yes' : 'no';
+ $options['meta'] = $subdef->isMetadataUpdateRequired() ? 'yes' : 'no';
$options['devices'] = [databox_subdef::DEVICE_SCREEN];
$root->set_subdef($groupname, $subdef->get_name(), $subdef->get_class(), $subdef->is_downloadable(), $options, []);
diff --git a/lib/classes/patch/370alpha7a.php b/lib/classes/patch/370alpha7a.php
index d6c7fb0be0..ea48b90430 100644
--- a/lib/classes/patch/370alpha7a.php
+++ b/lib/classes/patch/370alpha7a.php
@@ -110,7 +110,7 @@ class patch_370alpha7a extends patchAbstract
$media = $app->getMediaFromUri($filePath);
- $collection = \collection::get_from_base_id($app, $row['base_id']);
+ $collection = \collection::getByBaseId($app, $row['base_id']);
$borderFile = new \Alchemy\Phrasea\Border\File($app, $media, $collection);
diff --git a/lib/classes/patch/390alpha13a.php b/lib/classes/patch/390alpha13a.php
index d0b7ca7118..3b96e50b46 100644
--- a/lib/classes/patch/390alpha13a.php
+++ b/lib/classes/patch/390alpha13a.php
@@ -91,7 +91,7 @@ class patch_390alpha13a implements patchInterface
}
try {
- $collection = \collection::get_from_base_id($app, $row['base_id']);
+ $collection = \collection::getByBaseId($app, $row['base_id']);
} catch (\Exception $e) {
$app['monolog']->addInfo(sprintf(
'Patch %s : Registration for user (%s) could not be turn into doctrine entity as base with id (%s) could not be found.',
diff --git a/lib/classes/phrasea.php b/lib/classes/phrasea.php
index 93c732243a..211a150e44 100644
--- a/lib/classes/phrasea.php
+++ b/lib/classes/phrasea.php
@@ -10,16 +10,14 @@
*/
use Alchemy\Phrasea\Application;
+use Alchemy\Phrasea\Collection\CollectionRepositoryRegistry;
+use Alchemy\Phrasea\Collection\Reference\CollectionReferenceRepository;
use Symfony\Component\Translation\TranslatorInterface;
class phrasea
{
- private static $_bas2sbas = false;
private static $_sbas_names = false;
private static $_sbas_labels = false;
- private static $_coll2bas = false;
- private static $_bas2coll = false;
- private static $_bas_labels = false;
private static $_sbas_params = false;
const CACHE_BAS_2_SBAS = 'bas_2_sbas';
@@ -98,97 +96,60 @@ class phrasea
public static function sbasFromBas(Application $app, $base_id)
{
- if (!self::$_bas2sbas) {
- try {
- $data = $app->getApplicationBox()->get_data_from_cache(self::CACHE_SBAS_FROM_BAS);
- if (!$data) {
- throw new \Exception('Could not get sbas from cache');
- }
- self::$_bas2sbas = $data;
- } catch (\Exception $e) {
- $sql = 'SELECT base_id, sbas_id FROM bas';
- $stmt = $app->getApplicationBox()->get_connection()->prepare($sql);
- $stmt->execute();
- $rs = $stmt->fetchAll(PDO::FETCH_ASSOC);
- $stmt->closeCursor();
+ $reference = self::getCollectionReferenceRepository($app)->find($base_id);
- foreach ($rs as $row) {
- self::$_bas2sbas[$row['base_id']] = (int) $row['sbas_id'];
- }
-
- $app->getApplicationBox()->set_data_to_cache(self::$_bas2sbas, self::CACHE_SBAS_FROM_BAS);
- }
+ if ($reference) {
+ return $reference->getDataboxId();
}
- return isset(self::$_bas2sbas[$base_id]) ? self::$_bas2sbas[$base_id] : false;
+ return false;
}
public static function baseFromColl($sbas_id, $coll_id, Application $app)
{
- if (!self::$_coll2bas) {
- $conn = $app->getApplicationBox()->get_connection();
- $sql = 'SELECT base_id, server_coll_id, sbas_id FROM bas';
- $stmt = $conn->prepare($sql);
- $stmt->execute();
- $rs = $stmt->fetchAll(PDO::FETCH_ASSOC);
- $stmt->closeCursor();
+ $reference = self::getCollectionReferenceRepository($app)->findByCollectionId($sbas_id, $coll_id);
- foreach ($rs as $row) {
- if (!isset(self::$_coll2bas[$row['sbas_id']]))
- self::$_coll2bas[$row['sbas_id']] = [];
- self::$_coll2bas[$row['sbas_id']][$row['server_coll_id']] = (int) $row['base_id'];
- }
+ if ($reference) {
+ return $reference->getBaseId();
}
- return isset(self::$_coll2bas[$sbas_id][$coll_id]) ? self::$_coll2bas[$sbas_id][$coll_id] : false;
+ return false;
}
public static function reset_baseDatas(appbox $appbox)
{
- self::$_coll2bas = self::$_bas2coll = self::$_bas_labels = self::$_bas2sbas = null;
- $appbox->delete_data_from_cache(
- [
- self::CACHE_BAS_2_COLL
- , self::CACHE_BAS_2_COLL
- , self::CACHE_BAS_LABELS
- , self::CACHE_SBAS_FROM_BAS
- ]
- );
+ $appbox->delete_data_from_cache([
+ self::CACHE_BAS_2_COLL,
+ self::CACHE_BAS_2_COLL,
+ self::CACHE_BAS_LABELS,
+ self::CACHE_SBAS_FROM_BAS,
+ ]);
return;
}
public static function reset_sbasDatas(appbox $appbox)
{
- self::$_sbas_names = self::$_sbas_labels = self::$_sbas_params = self::$_bas2sbas = null;
- $appbox->delete_data_from_cache(
- [
- self::CACHE_SBAS_NAMES,
- self::CACHE_SBAS_LABELS,
- self::CACHE_SBAS_FROM_BAS,
- self::CACHE_SBAS_PARAMS,
- ]
- );
+ self::$_sbas_names = self::$_sbas_labels = self::$_sbas_params = null;
+ $appbox->delete_data_from_cache([
+ self::CACHE_SBAS_NAMES,
+ self::CACHE_SBAS_LABELS,
+ self::CACHE_SBAS_FROM_BAS,
+ self::CACHE_SBAS_PARAMS,
+ ]);
return;
}
public static function collFromBas(Application $app, $base_id)
{
- if (!self::$_bas2coll) {
- $conn = $app->getApplicationBox()->get_connection();
- $sql = 'SELECT base_id, server_coll_id FROM bas';
- $stmt = $conn->prepare($sql);
- $stmt->execute();
- $rs = $stmt->fetchAll(PDO::FETCH_ASSOC);
- $stmt->closeCursor();
+ $reference = self::getCollectionReferenceRepository($app)->find($base_id);
- foreach ($rs as $row) {
- self::$_bas2coll[$row['base_id']] = (int) $row['server_coll_id'];
- }
+ if ($reference) {
+ return $reference->getCollectionId();
}
- return isset(self::$_bas2coll[$base_id]) ? self::$_bas2coll[$base_id] : false;
+ return false;
}
public static function sbas_names($sbas_id, Application $app)
@@ -212,6 +173,9 @@ class phrasea
if (!self::$_sbas_labels) {
try {
self::$_sbas_labels = $app->getApplicationBox()->get_data_from_cache(self::CACHE_SBAS_LABELS);
+ if (!is_array(self::$_sbas_labels)) {
+ throw new \Exception('Invalid data retrieved from cache');
+ }
} catch (\Exception $e) {
foreach ($app->getDataboxes() as $databox) {
self::$_sbas_labels[$databox->get_sbas_id()] = [
@@ -234,29 +198,45 @@ class phrasea
public static function bas_labels($base_id, Application $app)
{
- if (!self::$_bas_labels) {
- try {
- self::$_bas_labels = $app->getApplicationBox()->get_data_from_cache(self::CACHE_BAS_LABELS);
- } catch (\Exception $e) {
- foreach ($app->getDataboxes() as $databox) {
- foreach ($databox->get_collections() as $collection) {
- self::$_bas_labels[$collection->get_base_id()] = [
- 'fr' => $collection->get_label('fr'),
- 'en' => $collection->get_label('en'),
- 'de' => $collection->get_label('de'),
- 'nl' => $collection->get_label('nl'),
- ];
- }
- }
+ $reference = self::getCollectionReferenceRepository($app)->find($base_id);
- $app->getApplicationBox()->set_data_to_cache(self::$_bas_labels, self::CACHE_BAS_LABELS);
- }
+ if (! $reference) {
+ return $app->trans('collection.label.unknown');
}
- if (isset(self::$_bas_labels[$base_id]) && isset(self::$_bas_labels[$base_id][$app['locale']])) {
- return self::$_bas_labels[$base_id][$app['locale']];
+ $collectionRepository = self::getCollectionRepositoryRegistry($app)
+ ->getRepositoryByDatabox($reference->getDataboxId());
+
+ $collection = $collectionRepository->find($reference->getCollectionId());
+
+ if (! $collection) {
+ throw new \RuntimeException('Missing collection ' . $base_id . '.');
}
- return 'Unknown collection';
+ $labels = $collection->getCollection()->getLabels();
+
+ if (isset($labels[$app['locale']])) {
+ return $labels[$app['locale']];
+ }
+
+ return $collection->getCollection()->getName();
+ }
+
+ /**
+ * @param Application $app
+ * @return CollectionReferenceRepository
+ */
+ private static function getCollectionReferenceRepository(Application $app)
+ {
+ return $app['repo.collection-references'];
+ }
+
+ /**
+ * @param Application $app
+ * @return CollectionRepositoryRegistry
+ */
+ private static function getCollectionRepositoryRegistry(Application $app)
+ {
+ return $app['repo.collections-registry'];
}
}
diff --git a/lib/classes/record/adapter.php b/lib/classes/record/adapter.php
index de7335a83f..840bb1711e 100644
--- a/lib/classes/record/adapter.php
+++ b/lib/classes/record/adapter.php
@@ -1,6 +1,5 @@
app, $this->databox, $this->collection_id);
+ return \collection::getByCollectionId($this->app, $this->databox, $this->collection_id);
}
/**
@@ -894,82 +901,6 @@ class record_adapter implements RecordInterface, cache_cacheableInterface
return $this->getDatabox()->get_sbas_id();
}
- public function substitute_subdef($name, MediaInterface $media, Application $app, $adapt=true)
- {
- $newfilename = $this->record_id . '_0_' . $name . '.' . $media->getFile()->getExtension();
-
- /** @var Filesystem $filesystem */
- $filesystem = $app['filesystem'];
-
- if ($name == 'document') {
- $baseprefs = $this->getDatabox()->get_sxml_structure();
-
- $pathhd = p4string::addEndSlash((string) ($baseprefs->path));
-
- $filehd = $this->getRecordId() . "_document." . strtolower($media->getFile()->getExtension());
- $pathhd = databox::dispatch($filesystem, $pathhd);
-
- $filesystem->copy($media->getFile()->getRealPath(), $pathhd . $filehd, true);
-
- $subdefFile = $pathhd . $filehd;
-
- $meta_writable = true;
- } else {
- $type = $this->isStory() ? 'image' : $this->getType();
-
- $subdef_def = $this->getDatabox()->get_subdef_structure()->get_subdef($type, $name);
-
- if ($this->has_subdef($name) && $this->get_subdef($name)->is_physically_present()) {
-
- $path_file_dest = $this->get_subdef($name)->get_pathfile();
- $this->get_subdef($name)->remove_file();
- $this->clearSubdefCache($name);
- } else {
- $path = databox::dispatch($filesystem, $subdef_def->get_path());
- $filesystem->mkdir($path, 0750);
- $path_file_dest = $path . $newfilename;
- }
-
- if($adapt) {
- try {
- $app['media-alchemyst']->turnInto(
- $media->getFile()->getRealPath(),
- $path_file_dest,
- $subdef_def->getSpecs()
- );
- } catch (\MediaAlchemyst\Exception\ExceptionInterface $e) {
- return $this;
- }
-
- $subdefFile = $path_file_dest;
- }
- else{
- $filesystem->copy($media->getFile()->getRealPath(), $path_file_dest);
-
- $subdefFile = $path_file_dest;
- }
-
- $meta_writable = $subdef_def->meta_writeable();
- }
-
- $filesystem->chmod($subdefFile, 0760);
- $media = $app->getMediaFromUri($subdefFile);
- $subdef = media_subdef::create($app, $this, $name, $media);
- $subdef->set_substituted(true);
-
- $this->delete_data_from_cache(self::CACHE_SUBDEFS);
-
- if ($meta_writable) {
- $this->write_metas();
- }
-
- if ($name == 'document' && $adapt) {
- $this->rebuild_subdefs();
- }
-
- return $this;
- }
-
/**
* @param DOMDocument $dom_doc
* @return record_adapter
@@ -1228,7 +1159,6 @@ class record_adapter implements RecordInterface, cache_cacheableInterface
}
/**
- *
* @param File $file
* @param Application $app
*
@@ -1276,13 +1206,12 @@ class record_adapter implements RecordInterface, cache_cacheableInterface
unset($e);
}
- /** @var Filesystem $filesystem */
- $filesystem = $app['filesystem'];
+ $filesystem = self::getFilesystem($app);
- $pathhd = databox::dispatch($filesystem, trim($databox->get_sxml_structure()->path));
- $newname = $record->getRecordId() . "_document." . pathinfo($file->getOriginalName(), PATHINFO_EXTENSION);
+ $pathhd = $filesystem->generateDataboxDocumentBasePath($databox);
+ $newname = $filesystem->generateDocumentFilename($record, $file->getFile());
- $filesystem->copy($file->getFile()->getRealPath(), $pathhd . $newname, true);
+ $filesystem->copy($file->getFile()->getRealPath(), $pathhd . $newname);
$media = $app->getMediaFromUri($pathhd . $newname);
media_subdef::create($app, $record, 'document', $media);
@@ -1398,14 +1327,14 @@ class record_adapter implements RecordInterface, cache_cacheableInterface
$hd = $this->get_subdef('document');
if ($hd->is_physically_present()) {
- return new SymfoFile($hd->get_pathfile());
+ return new SymfoFile($hd->getRealPath());
}
return null;
}
/**
- * @return Array : list of deleted files;
+ * @return array[] list of deleted files real paths
*/
public function delete()
{
@@ -1417,14 +1346,14 @@ class record_adapter implements RecordInterface, cache_cacheableInterface
continue;
if ($subdef->get_name() === 'thumbnail') {
- $this->app['filesystem']->remove($this->app['phraseanet.thumb-symlinker']->getSymlinkPath($subdef->get_pathfile()));
+ $this->app['phraseanet.thumb-symlinker']->unlink($subdef->getRealPath());
}
- $ftodel[] = $subdef->get_pathfile();
- $watermark = $subdef->get_path() . 'watermark_' . $subdef->get_file();
+ $ftodel[] = $subdef->getRealPath();
+ $watermark = $subdef->getWatermarkRealPath();
if (file_exists($watermark))
$ftodel[] = $watermark;
- $stamp = $subdef->get_path() . 'stamp_' . $subdef->get_file();
+ $stamp = $subdef->getStampRealPath();
if (file_exists($stamp))
$ftodel[] = $stamp;
}
@@ -1623,7 +1552,8 @@ class record_adapter implements RecordInterface, cache_cacheableInterface
$offset_start = (int) ($offset_start < 0 ? 0 : $offset_start);
$how_many = (int) (($how_many > 20 || $how_many < 1) ? 10 : $how_many);
- $sql = sprintf('SELECT record_id FROM record WHERE originalname = :original_name COLLATE %s LIMIT %d, %d',
+ $sql = sprintf(
+ 'SELECT record_id FROM record WHERE originalname = :original_name COLLATE %s LIMIT %d, %d',
$caseSensitive ? 'utf8_bin' : 'utf8_unicode_ci',
$offset_start,
$how_many
diff --git a/lib/classes/record/exportElement.php b/lib/classes/record/exportElement.php
index c368c27c15..fe225f145d 100644
--- a/lib/classes/record/exportElement.php
+++ b/lib/classes/record/exportElement.php
@@ -127,7 +127,7 @@ class record_exportElement extends record_adapter
$orderable['document'] = false;
$downloadable['document'] = false;
- if (isset($sd['document']) && is_file($sd['document']->get_pathfile())) {
+ if (isset($sd['document']) && is_file($sd['document']->getRealPath())) {
if ($go_dl['document'] === true) {
if ($this->app->getAclForUser($this->app->getAuthenticatedUser())->is_restricted_download($this->get_base_id())) {
$this->remain_hd --;
diff --git a/lib/classes/record/preview.php b/lib/classes/record/preview.php
index 2af5fdfcd2..805b9ba120 100644
--- a/lib/classes/record/preview.php
+++ b/lib/classes/record/preview.php
@@ -474,7 +474,7 @@ class record_preview extends record_adapter
. '|2:|min|average|max' .
'&chxp=2,' . $min . ',' . $average . ',' . $max);
- $this->view_popularity = new media_adapter($url, $width, $height);
+ $this->view_popularity = new media_adapter($width, $height, $url);
return $this->view_popularity;
}
@@ -547,7 +547,7 @@ class record_preview extends record_adapter
. '&chl='
. urlencode(implode('|', array_keys($referrers))));
- $this->refferer_popularity = new media_adapter($url, $width, $height);
+ $this->refferer_popularity = new media_adapter($width, $height, $url);
return $this->refferer_popularity;
}
@@ -627,7 +627,7 @@ class record_preview extends record_adapter
. date_format(new DateTime(), 'd M') . '|1:|0|'
. round($top / 2) . '|' . $top);
- $ret = new media_adapter($url, $width, $height);
+ $ret = new media_adapter($width, $height, $url);
$this->download_popularity = $ret;
return $this->download_popularity;
diff --git a/lib/classes/recordutils/image.php b/lib/classes/recordutils/image.php
index e030fdea6a..1c0fa411e9 100644
--- a/lib/classes/recordutils/image.php
+++ b/lib/classes/recordutils/image.php
@@ -52,16 +52,16 @@ class recordutils_image
$base_id = $subdef->get_record()->get_base_id();
if ($subdef->get_type() !== \media_subdef::TYPE_IMAGE) {
- return $subdef->get_pathfile();
+ return $subdef->getRealPath();
}
if (!$subdef->is_physically_present()) {
- return $subdef->get_pathfile();
+ return $subdef->getRealPath();
}
$rotation = null;
try {
- $image = $app->getMediaFromUri($subdef->get_pathfile());
+ $image = $app->getMediaFromUri($subdef->getRealPath());
if (MediaInterface::TYPE_IMAGE === $image->getType()) {
$rotation = $image->getOrientation();
}
@@ -72,21 +72,21 @@ class recordutils_image
$domprefs = new DOMDocument();
if (false === $domprefs->loadXML($subdef->get_record()->get_collection()->get_prefs())) {
- return $subdef->get_pathfile();
+ return $subdef->getRealPath();
}
if (false === $sxxml = simplexml_load_string($app['serializer.caption']->serialize($subdef->get_record()->get_caption(), CaptionSerializer::SERIALIZE_XML))) {
- return $subdef->get_pathfile();
+ return $subdef->getRealPath();
}
$xpprefs = new DOMXPath($domprefs);
$stampNodes = $xpprefs->query('/baseprefs/stamp');
if ($stampNodes->length == 0) {
- return $subdef->get_pathfile();
+ return $subdef->getRealPath();
}
- $pathIn = $subdef->get_path() . $subdef->get_file();
- $pathOut = $subdef->get_path() . 'stamp_' . $subdef->get_file();
+ $pathIn = $subdef->getRealPath();
+ $pathOut = $subdef->getStampRealPath();
$vars = $xpprefs->query('/baseprefs/stamp/*/var');
@@ -355,14 +355,15 @@ class recordutils_image
$image_out->save($pathOut);
if (is_file($pathOut)) {
- // copy metadatas to the stamped file if we can
- if(method_exists($app['exiftool.writer'], "copy")) {
- $app['exiftool.writer']->copy($subdef->get_pathfile(), $pathOut);
+ $writer = $app['exiftool.writer'];
+ // copy metadata to the stamped file if we can
+ if(method_exists($writer, "copy")) {
+ $writer->copy($subdef->getRealPath(), $pathOut);
}
return $pathOut;
}
- return $subdef->get_pathfile();
+ return $subdef->getRealPath();
}
/**
@@ -383,24 +384,24 @@ class recordutils_image
$base_id = $subdef->get_record()->get_base_id();
if ($subdef->get_name() !== 'preview') {
- return $subdef->get_pathfile();
+ return $subdef->getRealPath();
}
if ($subdef->get_type() !== \media_subdef::TYPE_IMAGE) {
- return $subdef->get_pathfile();
+ return $subdef->getRealPath();
}
if (!$subdef->is_physically_present()) {
return false;
}
- $pathIn = $subdef->get_path() . $subdef->get_file();
+ $pathIn = $subdef->getRealPath();
if (!is_file($pathIn)) {
return false;
}
- $pathOut = $subdef->get_path() . 'watermark_' . $subdef->get_file();
+ $pathOut = $subdef->getWatermarkRealPath();
// cache
if (is_file($pathOut)) {
diff --git a/lib/classes/set/export.php b/lib/classes/set/export.php
index 08ad4ea7eb..90a1ef3115 100644
--- a/lib/classes/set/export.php
+++ b/lib/classes/set/export.php
@@ -1,6 +1,5 @@
findUserBasket($sstid, $app->getAuthenticatedUser(), false);
$this->exportName = str_replace([' ', '\\', '/'], '_', $Basket->getName()) . "_" . date("Y-n-d");
diff --git a/resources/locales/messages.de.xlf b/resources/locales/messages.de.xlf
index c484c345ce..f114e45bea 100644
--- a/resources/locales/messages.de.xlf
+++ b/resources/locales/messages.de.xlf
@@ -1,6 +1,6 @@
-
+
The source node in most cases contains the sample message as written by the developer. If it looks like a dot-delimitted string such as "form.label.firstname", then the developer has not provided a default message.
@@ -181,13 +181,13 @@
%quantity% records added
%quantity% records added
- Controller/Prod/BasketController.php
+ Controller/Prod/BasketController.php
Controller/Prod/StoryController.php
%quantity% records moved
%quantity% records moved
- Controller/Prod/BasketController.php
+ Controller/Prod/BasketController.php
%quantity% selected files
@@ -742,7 +742,7 @@
Ajouter un publisher
einen Veröffentlicher hinzufügen
- admin/publications/fiche.html.twig
+ admin/publications/fiche.html.twig
Ajouter une publication
@@ -859,7 +859,7 @@
Controller/Admin/DataboxController.php
Controller/Admin/DataboxController.php
Controller/Admin/DataboxesController.php
- Controller/Prod/BasketController.php
+ Controller/Prod/BasketController.php
Controller/Prod/LazaretController.php
Controller/Prod/LazaretController.php
Controller/Prod/LazaretController.php
@@ -910,9 +910,9 @@
Ein Fehler ist aufgetreten
Controller/Admin/CollectionController.php
Controller/Admin/DataboxController.php
- Controller/Api/V1Controller.php
- Controller/Api/V1Controller.php
- Controller/Prod/BasketController.php
+ Controller/Api/V1Controller.php
+ Controller/Api/V1Controller.php
+ Controller/Prod/BasketController.php
web/admin/statusbit.html.twig
@@ -1301,32 +1301,32 @@
Basket created
Sammelkorb wurde erstellt
- Controller/Prod/BasketController.php
+ Controller/Prod/BasketController.php
Basket has been archived
Sammelkorb wurde archiviert
- Controller/Prod/BasketController.php
+ Controller/Prod/BasketController.php
Basket has been deleted
Sammelkorb wurde gelöscht
- Controller/Prod/BasketController.php
+ Controller/Prod/BasketController.php
Basket has been unarchived
Sammelkorb wurde nicht mehr archiviert
- Controller/Prod/BasketController.php
+ Controller/Prod/BasketController.php
Basket has been updated
Sammelkorb wurde aktualisiert
- Controller/Prod/BasketController.php
+ Controller/Prod/BasketController.php
Basket updated
Sammelkorb wurde aktualisiert
- Controller/Prod/BasketController.php
+ Controller/Prod/BasketController.php
Bitrate
@@ -1562,7 +1562,7 @@
Changes for rotation will be applied only on the sub-definitions of "image" type.
Changes for rotation will be applied only on the sub-definitions of "image" type.
- actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
Channels
@@ -1588,7 +1588,7 @@
Choisir
wählen
web/admin/subdefs.html.twig
- web/admin/subdefs.html.twig
+ web/admin/subdefs.html.twig
prod/Baskets/Reorder.html.twig
prod/Story/Reorder.html.twig
@@ -1616,7 +1616,8 @@
Clear
Klar
- admin/task-manager/log.html.twig
+ admin/task-manager/log_scheduler.html.twig
+ admin/task-manager/log_task.html.twig
Clear list
@@ -2272,7 +2273,7 @@
Disable document type sharing
Disable document type sharing
- actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
Disabled
@@ -2413,17 +2414,17 @@
ERREUR : La classe de subdef est necessaire et egal a "thumbnail","preview" ou "document"
ERREUR : La classe de subdef est necessaire et egal a "thumbnail","preview" ou "document"
- lib/classes/databox.php
+ lib/classes/databox.php
ERREUR : Les name de subdef sont uniques par groupe de subdefs et necessaire
FEHLER : das "name" Attribut des Knotens "subdef" ist zwingend und einzigartig durch Gruppe von subdef
- lib/classes/databox.php
+ lib/classes/databox.php
ERREUR : TOUTES LES BALISES subdefgroup necessitent un attribut name
FEHLER : Alle "subdefgroup" tags brauchen ein "name" Attribut
- lib/classes/databox.php
+ lib/classes/databox.php
Edit
@@ -2498,7 +2499,7 @@
Email
Email
web/admin/dashboard.html.twig
- admin/publications/fiche.html.twig
+ admin/publications/fiche.html.twig
Email '%email%' for login '%login%' already exists in database
@@ -2633,7 +2634,7 @@
Enable document type sharing
Enable document type sharing
- actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
Enable maintenance message broadcast
@@ -2834,7 +2835,7 @@
Etendue de la publication
Erweiterung der Veröffentlichung
- admin/publications/fiche.html.twig
+ admin/publications/fiche.html.twig
admin/publications/list.html.twig
@@ -3314,7 +3315,7 @@
Id
ID
- admin/publications/fiche.html.twig
+ admin/publications/fiche.html.twig
If request is bigger, then mail is still available
@@ -3401,12 +3402,12 @@
Indexation task
Indexation task
- TaskManager/Job/IndexerJob.php
+ TaskManager/Job/IndexerJob.php
Indexing Batch (collections/databox)
Indexing Batch (collections/databox)
- TaskManager/Job/IndexerJob.php
+ TaskManager/Job/IndexerJob.php
Informations
@@ -3472,7 +3473,7 @@
Invalid file type, only (%supported_file_types%) file formats are supported
ungültiger Dateityp, nur (%supported_file_types%) Dateitypen werden unterstützt
admin/databox/databox.html.twig
- admin/statusbit/edit.html.twig
+ admin/statusbit/edit.html.twig
user/import/file.html.twig
@@ -3818,7 +3819,7 @@
Liste des personnes habilitees a publier sur ce fil
Liste der Personen, die auf diesen Thread veröffentlichen dürfen
- admin/publications/fiche.html.twig
+ admin/publications/fiche.html.twig
Lists
@@ -4122,7 +4123,7 @@
Name or email
Name oder Email
- admin/publications/fiche.html.twig
+ admin/publications/fiche.html.twig
Ne pas autoriser
@@ -4287,7 +4288,7 @@
Non-Restreinte (publique)
Nicht eingeschränkt (öffentlich)
- admin/publications/fiche.html.twig
+ admin/publications/fiche.html.twig
admin/publications/list.html.twig
@@ -4488,7 +4489,7 @@
Owner
Besitzer
- admin/publications/fiche.html.twig
+ admin/publications/fiche.html.twig
Owner removed from list
@@ -4825,7 +4826,7 @@
Publique
öffentliche
- admin/publications/fiche.html.twig
+ admin/publications/fiche.html.twig
admin/publications/list.html.twig
@@ -5149,12 +5150,12 @@
Record Not Found
Datensatz wurde nicht gefunden
- Controller/Api/V1Controller.php
+ Controller/Api/V1Controller.php
Record removed from basket
Datensatz wurde aus dem Sammelkorb gelöscht
- Controller/Prod/BasketController.php
+ Controller/Prod/BasketController.php
Record removed from story
@@ -5422,11 +5423,6 @@
Zurück zur Bestellung
prod/orders/order_item.html.twig
-
- Return
- Return
- admin/task-manager/log.html.twig
-
Review order on %website%
Review order on %website%
@@ -5744,7 +5740,7 @@
Short description
Kurzbeschreibung
- admin/publications/fiche.html.twig
+ admin/publications/fiche.html.twig
admin/publications/list.html.twig
@@ -5858,7 +5854,7 @@
Sous-titre
Untertitel
- admin/publications/fiche.html.twig
+ admin/publications/fiche.html.twig
admin/publications/list.html.twig
@@ -5963,7 +5959,7 @@
Story Not Found
Bericht wurde nicht gefunden
- Controller/Api/V1Controller.php
+ Controller/Api/V1Controller.php
Story created
@@ -6024,7 +6020,7 @@
Substitution is not possible for this kind of record
Für diesen Typ von Aufnahme ist der Ersatz nicht verfügbar
- actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
Success
@@ -6322,7 +6318,7 @@
The requested basket does not exist
Der erforderte Sammelkorb existiert nicht
- Controller/Prod/BasketController.php
+ Controller/Prod/BasketController.php
The task status
@@ -6485,7 +6481,7 @@
Titre
Titel
- admin/publications/fiche.html.twig
+ admin/publications/fiche.html.twig
admin/publications/list.html.twig
admin/publications/list.html.twig
Bridge/Dailymotion/upload.html.twig
@@ -7380,7 +7376,7 @@
You are not the feed owner
Sie sind nicht der Besitzer von diesem Feed
- admin/publications/fiche.html.twig
+ admin/publications/fiche.html.twig
You are not the owner of this feed, you can not edit it
@@ -7443,7 +7439,7 @@
You do not have access to this basket
Sie haben keinen Zugriff auf diesen Sammelkorb
- Controller/Prod/BasketController.php
+ Controller/Prod/BasketController.php
You do not have enough rights to access quarantine
@@ -7609,18 +7605,18 @@
Your install might need to build some sub-definitions
Ihre Installation könnte benötigen, einige Unterauflösungen zu erstellen.
- lib/classes/appbox.php
+ lib/classes/appbox.php
Your install might need to re-read technical datas
Ihre Installation erfordert vielleicht, technische Daten nochmal zu lesen.
- lib/classes/appbox.php
+ lib/classes/appbox.php
Your install requires data migration, please execute the following command
Ihre Installation erfordert Datenmigration, bitte führen Sie folgenden Befehl aus.
- lib/classes/appbox.php
- lib/classes/appbox.php
+ lib/classes/appbox.php
+ lib/classes/appbox.php
Your medias and their subdefinitions (previews, thumbnails..) will be stored in these directories.
@@ -7642,7 +7638,7 @@
action : ajouter au panier
Zum Sammelkorb hinzufügen
prod/preview/tools.html.twig
- prod/results/record.html.twig
+ prod/results/record.html.twig
action : bridge
@@ -7677,7 +7673,7 @@
web/lightbox/validate.html.twig
web/prod/index.html.twig
prod/preview/tools.html.twig
- prod/results/record.html.twig
+ prod/results/record.html.twig
web/prod/toolbar.html.twig
prod/WorkZone/Basket.html.twig
prod/WorkZone/Story.html.twig
@@ -7693,7 +7689,7 @@
action : print
Drucken
prod/preview/tools.html.twig
- prod/results/record.html.twig
+ prod/results/record.html.twig
web/prod/toolbar.html.twig
prod/WorkZone/Basket.html.twig
prod/WorkZone/Story.html.twig
@@ -8430,7 +8426,7 @@
admin::monitor: module admin
Admin
Controller/Admin/ConnectedUsersController.php
- lib/classes/phrasea.php
+ lib/classes/phrasea.php
web/admin/connected-users.html.twig
web/common/menubar.html.twig
@@ -8438,7 +8434,7 @@
admin::monitor: module client
Client
Controller/Admin/ConnectedUsersController.php
- lib/classes/phrasea.php
+ lib/classes/phrasea.php
classes/record/preview.php
web/admin/connected-users.html.twig
@@ -8446,7 +8442,7 @@
admin::monitor: module comparateur
Lightbox
Controller/Admin/ConnectedUsersController.php
- lib/classes/phrasea.php
+ lib/classes/phrasea.php
web/admin/connected-users.html.twig
@@ -8458,7 +8454,7 @@
admin::monitor: module production
Prod
Controller/Admin/ConnectedUsersController.php
- lib/classes/phrasea.php
+ lib/classes/phrasea.php
classes/record/preview.php
web/admin/connected-users.html.twig
@@ -8466,7 +8462,7 @@
admin::monitor: module report
Report
Controller/Admin/ConnectedUsersController.php
- lib/classes/phrasea.php
+ lib/classes/phrasea.php
web/admin/connected-users.html.twig
web/common/menubar.html.twig
@@ -8474,7 +8470,7 @@
admin::monitor: module thesaurus
Thesaurus
Controller/Admin/ConnectedUsersController.php
- lib/classes/phrasea.php
+ lib/classes/phrasea.php
web/admin/connected-users.html.twig
web/common/menubar.html.twig
@@ -8482,7 +8478,7 @@
admin::monitor: module upload
Upload
Controller/Admin/ConnectedUsersController.php
- lib/classes/phrasea.php
+ lib/classes/phrasea.php
web/common/menubar.html.twig
@@ -8492,7 +8488,7 @@
Phrasea/Controller/LightboxController.php
Phrasea/Controller/LightboxController.php
Phrasea/Controller/LightboxController.php
- lib/classes/phrasea.php
+ lib/classes/phrasea.php
web/common/menubar.html.twig
@@ -8528,6 +8524,7 @@
admin::plugins: plugins
admin::plugins: plugins
+ admin/plugins/index.html.twig
web/admin/tree.html.twig
@@ -8673,7 +8670,7 @@
alert
Vorsicht
- actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
all caches services have been flushed
@@ -8684,7 +8681,7 @@
an error occured
Ein Fehler ist aufgetreten
Controller/Prod/ToolsController.php
- actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
an error occured : %message%
@@ -8714,7 +8711,7 @@
audio
Audio
- Phrasea/Twig/PhraseanetExtension.php
+ Phrasea/Twig/PhraseanetExtension.php
task-manager/task-editor/subdefs.html.twig
@@ -8782,7 +8779,7 @@
web/account/reset-email.html.twig
admin/collection/create.html.twig
web/admin/index.html.twig
- admin/publications/fiche.html.twig
+ admin/publications/fiche.html.twig
web/common/dialog_export.html.twig
web/common/dialog_export.html.twig
web/common/dialog_export.html.twig
@@ -8974,7 +8971,7 @@
admin/collection/details.html.twig
admin/databox/details.html.twig
web/admin/editusers.html.twig
- admin/publications/fiche.html.twig
+ admin/publications/fiche.html.twig
admin/statusbit/edit.html.twig
user/import/file.html.twig
web/developers/application.html.twig
@@ -9023,7 +9020,7 @@
admin/collection/collection.html.twig
admin/collection/collection.html.twig
admin/collection/suggested_value.html.twig
- admin/publications/fiche.html.twig
+ admin/publications/fiche.html.twig
admin/publications/list.html.twig
web/admin/subdefs.html.twig
Bridge/Dailymotion/actioncontainers.html.twig
@@ -9074,14 +9071,14 @@
admin/collection/suggested_value.html.twig
web/admin/dashboard.html.twig
web/admin/editusers.html.twig
- admin/publications/fiche.html.twig
+ admin/publications/fiche.html.twig
admin/publications/list.html.twig
admin/search-engine/phrasea.html.twig
admin/search-engine/sphinx-search.html.twig
web/admin/setup.html.twig
admin/statusbit/edit.html.twig
web/admin/structure.html.twig
- web/admin/subdefs.html.twig
+ web/admin/subdefs.html.twig
user/import/view.html.twig
admin/user/registrations.html.twig
web/developers/application_form.html.twig
@@ -9144,9 +9141,9 @@
cancel
abbrechen
actions/Tools/index.html.twig
- actions/Tools/index.html.twig
- actions/Tools/index.html.twig
- actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
cannes SAUF festival
@@ -9243,6 +9240,11 @@
Alles aktivieren
web/report/listColumn.html.twig
+
+ collection.label.unknown
+ collection.label.unknown
+ lib/classes/phrasea.php
+
commande::deadline
Termin
@@ -9305,7 +9307,7 @@
document
Dokument
- Phrasea/Twig/PhraseanetExtension.php
+ Phrasea/Twig/PhraseanetExtension.php
web/admin/subdefs.html.twig
web/admin/subdefs.html.twig
task-manager/task-editor/subdefs.html.twig
@@ -9477,7 +9479,7 @@
flash
flash
- Phrasea/Twig/PhraseanetExtension.php
+ Phrasea/Twig/PhraseanetExtension.php
task-manager/task-editor/subdefs.html.twig
@@ -9551,13 +9553,13 @@
image
Bild
- Phrasea/Twig/PhraseanetExtension.php
+ Phrasea/Twig/PhraseanetExtension.php
task-manager/task-editor/subdefs.html.twig
image rotation
Bilddrehung
- actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
image tool
@@ -9756,7 +9758,7 @@
mettre a jour le nom original de fichier apres substitution
ursprüngliche Dateiname nach Ersetzung aktualisieren
- actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
name
@@ -9772,14 +9774,14 @@
no
Nein
web/account/sessions.html.twig
- web/admin/subdefs.html.twig
+ web/admin/subdefs.html.twig
user/import/view.html.twig
web/common/technical_datas.html.twig
no image selected
Kein Bild wurde ausgewählt
- actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
non
@@ -10318,6 +10320,7 @@
previewLinkLabel
prod/Share/record.html.twig
prod/Share/record.html.twig
+ prod/Share/record.html.twig
print:: image de choix et description
@@ -10364,7 +10367,7 @@
processing
verarbeitend
- actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
prod::Les enregistrements ne provienent pas tous de la meme base et ne peuvent donc etre traites ensemble
@@ -10771,7 +10774,7 @@
reponses:: partager
Teilen
- prod/results/record.html.twig
+ prod/results/record.html.twig
reponses:: selectionner rien
@@ -10791,7 +10794,7 @@
reponses::document sans titre
ohne Titel
- classes/record/adapter.php
+ classes/record/adapter.php
report :: aucun resultat trouve
@@ -11625,8 +11628,8 @@
reportage
Reportage
- Phrasea/Twig/PhraseanetExtension.php
- Phrasea/Twig/PhraseanetExtension.php
+ Phrasea/Twig/PhraseanetExtension.php
+ Phrasea/Twig/PhraseanetExtension.php
resultat numero %number%
@@ -11676,12 +11679,12 @@
rotation 90 degres anti-horaires
90° entgegen dem Uhrzeigersinn drehen
- actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
rotation 90 degres horaire
90° im Uhrzeigersinn drehen
- actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
scheduled status
@@ -11792,18 +11795,17 @@
substitution HD
HD Ersetzung
- actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
substitution SD
SD Ersetzung
- actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
take a screenshot
einen Screenshot erstellen
actions/Tools/index.html.twig
- actions/Tools/index.html.twig
task::_common_:hotfolder
@@ -11818,12 +11820,12 @@
task::archive:Archivage
Archivierung auf Kollektion
- TaskManager/Job/ArchiveJob.php
+ TaskManager/Job/ArchiveJob.php
task::archive:Archiving files found into a 'hotfolder'
gefundenen Dateien nach einem Hotfolder archivieren
- TaskManager/Job/ArchiveJob.php
+ TaskManager/Job/ArchiveJob.php
task::archive:archivage sur base/collection/
@@ -12522,7 +12524,7 @@
thumbnail validation
Miniaturansicht Bestätigung
- actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
to
@@ -12592,9 +12594,10 @@
validate
bestätigen
actions/Tools/index.html.twig
- actions/Tools/index.html.twig
- actions/Tools/index.html.twig
- actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
validation:: NON
@@ -12659,7 +12662,7 @@
yes
Ja
web/account/sessions.html.twig
- web/admin/subdefs.html.twig
+ web/admin/subdefs.html.twig
web/common/technical_datas.html.twig
diff --git a/resources/locales/messages.en.xlf b/resources/locales/messages.en.xlf
index b58a0a0a5f..27af3e59a1 100644
--- a/resources/locales/messages.en.xlf
+++ b/resources/locales/messages.en.xlf
@@ -1,14 +1,14 @@
-
+
-
+
The source node in most cases contains the sample message as written by the developer. If it looks like a dot-delimitted string such as "form.label.firstname", then the developer has not provided a default message.
-
-
+
+
Form/Configuration/EmailFormType.php
Form/Login/PhraseaAuthenticationForm.php
@@ -177,13 +177,13 @@
%quantity% records added
%quantity% records added
- Controller/Prod/BasketController.php
+ Controller/Prod/BasketController.php
Controller/Prod/StoryController.php
%quantity% records moved
%quantity% records moved
- Controller/Prod/BasketController.php
+ Controller/Prod/BasketController.php
%quantity% selected files
@@ -738,7 +738,7 @@
Ajouter un publisher
Add a Publisher
- admin/publications/fiche.html.twig
+ admin/publications/fiche.html.twig
Ajouter une publication
@@ -855,7 +855,7 @@
Controller/Admin/DataboxController.php
Controller/Admin/DataboxController.php
Controller/Admin/DataboxesController.php
- Controller/Prod/BasketController.php
+ Controller/Prod/BasketController.php
Controller/Prod/LazaretController.php
Controller/Prod/LazaretController.php
Controller/Prod/LazaretController.php
@@ -906,9 +906,9 @@
An error occurred
Controller/Admin/CollectionController.php
Controller/Admin/DataboxController.php
- Controller/Api/V1Controller.php
- Controller/Api/V1Controller.php
- Controller/Prod/BasketController.php
+ Controller/Api/V1Controller.php
+ Controller/Api/V1Controller.php
+ Controller/Prod/BasketController.php
web/admin/statusbit.html.twig
@@ -1297,32 +1297,32 @@
Basket created
Basket created
- Controller/Prod/BasketController.php
+ Controller/Prod/BasketController.php
Basket has been archived
Basket has been archived
- Controller/Prod/BasketController.php
+ Controller/Prod/BasketController.php
Basket has been deleted
Basket has been deleted
- Controller/Prod/BasketController.php
+ Controller/Prod/BasketController.php
Basket has been unarchived
Basket has been unarchived
- Controller/Prod/BasketController.php
+ Controller/Prod/BasketController.php
Basket has been updated
Basket has been updated
- Controller/Prod/BasketController.php
+ Controller/Prod/BasketController.php
Basket updated
Basket updated
- Controller/Prod/BasketController.php
+ Controller/Prod/BasketController.php
Bitrate
@@ -1558,7 +1558,7 @@
Changes for rotation will be applied only on the sub-definitions of "image" type.
Rotations are applied only on subviews of "image" type documents.
- actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
Channels
@@ -1584,7 +1584,7 @@
Choisir
Choose
web/admin/subdefs.html.twig
- web/admin/subdefs.html.twig
+ web/admin/subdefs.html.twig
prod/Baskets/Reorder.html.twig
prod/Story/Reorder.html.twig
@@ -1612,7 +1612,8 @@
Clear
Clear
- admin/task-manager/log.html.twig
+ admin/task-manager/log_scheduler.html.twig
+ admin/task-manager/log_task.html.twig
Clear list
@@ -2268,7 +2269,7 @@
Disable document type sharing
Disable document type sharing
- actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
Disabled
@@ -2409,17 +2410,17 @@
ERREUR : La classe de subdef est necessaire et egal a "thumbnail","preview" ou "document"
Error: Class attribute is mandatory and must be Thumbnail, Preview or Document
- lib/classes/databox.php
+ lib/classes/databox.php
ERREUR : Les name de subdef sont uniques par groupe de subdefs et necessaire
Error : Attribute "name" from node "subdef" is required and single
- lib/classes/databox.php
+ lib/classes/databox.php
ERREUR : TOUTES LES BALISES subdefgroup necessitent un attribut name
Error : All "subdefgroup" tags must have an attribute "name"
- lib/classes/databox.php
+ lib/classes/databox.php
Edit
@@ -2494,7 +2495,7 @@
Email
E-mail
web/admin/dashboard.html.twig
- admin/publications/fiche.html.twig
+ admin/publications/fiche.html.twig
Email '%email%' for login '%login%' already exists in database
@@ -2534,7 +2535,7 @@
Emails
- E-mails
+ E-mail configuration
Form/Configuration/MainConfigurationFormType.php
@@ -2629,7 +2630,7 @@
Enable document type sharing
Enable document type sharing
- actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
Enable maintenance message broadcast
@@ -2830,7 +2831,7 @@
Etendue de la publication
Publication scope
- admin/publications/fiche.html.twig
+ admin/publications/fiche.html.twig
admin/publications/list.html.twig
@@ -3310,7 +3311,7 @@
Id
Id
- admin/publications/fiche.html.twig
+ admin/publications/fiche.html.twig
If request is bigger, then mail is still available
@@ -3397,12 +3398,12 @@
Indexation task
Indexation task
- TaskManager/Job/IndexerJob.php
+ TaskManager/Job/IndexerJob.php
Indexing Batch (collections/databox)
Indexing batch (Collections/Databox)
- TaskManager/Job/IndexerJob.php
+ TaskManager/Job/IndexerJob.php
Informations
@@ -3468,7 +3469,7 @@
Invalid file type, only (%supported_file_types%) file formats are supported
Invalid file type. Only %supported_file_types% file formats are supported.
admin/databox/databox.html.twig
- admin/statusbit/edit.html.twig
+ admin/statusbit/edit.html.twig
user/import/file.html.twig
@@ -3814,7 +3815,7 @@
Liste des personnes habilitees a publier sur ce fil
List of users allowed to publish on this feed
- admin/publications/fiche.html.twig
+ admin/publications/fiche.html.twig
Lists
@@ -4118,7 +4119,7 @@
Name or email
Name or E-mail
- admin/publications/fiche.html.twig
+ admin/publications/fiche.html.twig
Ne pas autoriser
@@ -4283,7 +4284,7 @@
Non-Restreinte (publique)
Unrestricted (public)
- admin/publications/fiche.html.twig
+ admin/publications/fiche.html.twig
admin/publications/list.html.twig
@@ -4484,7 +4485,7 @@
Owner
Owner
- admin/publications/fiche.html.twig
+ admin/publications/fiche.html.twig
Owner removed from list
@@ -4821,7 +4822,7 @@
Publique
Public
- admin/publications/fiche.html.twig
+ admin/publications/fiche.html.twig
admin/publications/list.html.twig
@@ -5149,12 +5150,12 @@ Pushed documents are also available in a received basket within Phraseanet Produ
Record Not Found
Record not found
- Controller/Api/V1Controller.php
+ Controller/Api/V1Controller.php
Record removed from basket
Record removed from basket
- Controller/Prod/BasketController.php
+ Controller/Prod/BasketController.php
Record removed from story
@@ -5422,11 +5423,6 @@ Pushed documents are also available in a received basket within Phraseanet Produ
Back to orders
prod/orders/order_item.html.twig
-
- Return
- Return
- admin/task-manager/log.html.twig
-
Review order on %website%
Review this order on %website%
@@ -5744,7 +5740,7 @@ Pushed documents are also available in a received basket within Phraseanet Produ
Short description
Short description
- admin/publications/fiche.html.twig
+ admin/publications/fiche.html.twig
admin/publications/list.html.twig
@@ -5858,7 +5854,7 @@ Pushed documents are also available in a received basket within Phraseanet Produ
Sous-titre
Subtitle
- admin/publications/fiche.html.twig
+ admin/publications/fiche.html.twig
admin/publications/list.html.twig
@@ -5963,7 +5959,7 @@ Pushed documents are also available in a received basket within Phraseanet Produ
Story Not Found
Story not found
- Controller/Api/V1Controller.php
+ Controller/Api/V1Controller.php
Story created
@@ -6024,7 +6020,7 @@ Pushed documents are also available in a received basket within Phraseanet Produ
Substitution is not possible for this kind of record
Substitution is not possible for this record type
- actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
Success
@@ -6322,7 +6318,7 @@ Pushed documents are also available in a received basket within Phraseanet Produ
The requested basket does not exist
The requested basket does not exist
- Controller/Prod/BasketController.php
+ Controller/Prod/BasketController.php
The task status
@@ -6485,7 +6481,7 @@ Pushed documents are also available in a received basket within Phraseanet Produ
Titre
Title
- admin/publications/fiche.html.twig
+ admin/publications/fiche.html.twig
admin/publications/list.html.twig
admin/publications/list.html.twig
Bridge/Dailymotion/upload.html.twig
@@ -7380,7 +7376,7 @@ Pushed documents are also available in a received basket within Phraseanet Produ
You are not the feed owner
You are not the feed owner
- admin/publications/fiche.html.twig
+ admin/publications/fiche.html.twig
You are not the owner of this feed, you can not edit it
@@ -7443,7 +7439,7 @@ Pushed documents are also available in a received basket within Phraseanet Produ
You do not have access to this basket
You do not have access to this basket
- Controller/Prod/BasketController.php
+ Controller/Prod/BasketController.php
You do not have enough rights to access quarantine
@@ -7609,18 +7605,18 @@ Pushed documents are also available in a received basket within Phraseanet Produ
Your install might need to build some sub-definitions
Your install might need to build some subviews.
- lib/classes/appbox.php
+ lib/classes/appbox.php
Your install might need to re-read technical datas
Your install might need to re-read technical data
- lib/classes/appbox.php
+ lib/classes/appbox.php
Your install requires data migration, please execute the following command
Your install requires data migration, please execute the following command
- lib/classes/appbox.php
- lib/classes/appbox.php
+ lib/classes/appbox.php
+ lib/classes/appbox.php
Your medias and their subdefinitions (previews, thumbnails..) will be stored in these directories.
@@ -7642,7 +7638,7 @@ Pushed documents are also available in a received basket within Phraseanet Produ
action : ajouter au panier
Add to Basket
prod/preview/tools.html.twig
- prod/results/record.html.twig
+ prod/results/record.html.twig
action : bridge
@@ -7677,7 +7673,7 @@ Pushed documents are also available in a received basket within Phraseanet Produ
web/lightbox/validate.html.twig
web/prod/index.html.twig
prod/preview/tools.html.twig
- prod/results/record.html.twig
+ prod/results/record.html.twig
web/prod/toolbar.html.twig
prod/WorkZone/Basket.html.twig
prod/WorkZone/Story.html.twig
@@ -7693,7 +7689,7 @@ Pushed documents are also available in a received basket within Phraseanet Produ
action : print
Print
prod/preview/tools.html.twig
- prod/results/record.html.twig
+ prod/results/record.html.twig
web/prod/toolbar.html.twig
prod/WorkZone/Basket.html.twig
prod/WorkZone/Story.html.twig
@@ -8430,7 +8426,7 @@ Pushed documents are also available in a received basket within Phraseanet Produ
admin::monitor: module admin
Admin
Controller/Admin/ConnectedUsersController.php
- lib/classes/phrasea.php
+ lib/classes/phrasea.php
web/admin/connected-users.html.twig
web/common/menubar.html.twig
@@ -8438,7 +8434,7 @@ Pushed documents are also available in a received basket within Phraseanet Produ
admin::monitor: module client
Client
Controller/Admin/ConnectedUsersController.php
- lib/classes/phrasea.php
+ lib/classes/phrasea.php
classes/record/preview.php
web/admin/connected-users.html.twig
@@ -8446,7 +8442,7 @@ Pushed documents are also available in a received basket within Phraseanet Produ
admin::monitor: module comparateur
Lightbox
Controller/Admin/ConnectedUsersController.php
- lib/classes/phrasea.php
+ lib/classes/phrasea.php
web/admin/connected-users.html.twig
@@ -8458,7 +8454,7 @@ Pushed documents are also available in a received basket within Phraseanet Produ
admin::monitor: module production
Production
Controller/Admin/ConnectedUsersController.php
- lib/classes/phrasea.php
+ lib/classes/phrasea.php
classes/record/preview.php
web/admin/connected-users.html.twig
@@ -8466,7 +8462,7 @@ Pushed documents are also available in a received basket within Phraseanet Produ
admin::monitor: module report
Report
Controller/Admin/ConnectedUsersController.php
- lib/classes/phrasea.php
+ lib/classes/phrasea.php
web/admin/connected-users.html.twig
web/common/menubar.html.twig
@@ -8474,7 +8470,7 @@ Pushed documents are also available in a received basket within Phraseanet Produ
admin::monitor: module thesaurus
Thesaurus
Controller/Admin/ConnectedUsersController.php
- lib/classes/phrasea.php
+ lib/classes/phrasea.php
web/admin/connected-users.html.twig
web/common/menubar.html.twig
@@ -8482,7 +8478,7 @@ Pushed documents are also available in a received basket within Phraseanet Produ
admin::monitor: module upload
Upload
Controller/Admin/ConnectedUsersController.php
- lib/classes/phrasea.php
+ lib/classes/phrasea.php
web/common/menubar.html.twig
@@ -8492,7 +8488,7 @@ Pushed documents are also available in a received basket within Phraseanet Produ
Phrasea/Controller/LightboxController.php
Phrasea/Controller/LightboxController.php
Phrasea/Controller/LightboxController.php
- lib/classes/phrasea.php
+ lib/classes/phrasea.php
web/common/menubar.html.twig
@@ -8528,6 +8524,7 @@ Pushed documents are also available in a received basket within Phraseanet Produ
admin::plugins: plugins
Plugins
+ admin/plugins/index.html.twig
web/admin/tree.html.twig
@@ -8673,7 +8670,7 @@ Pushed documents are also available in a received basket within Phraseanet Produ
alert
Warning
- actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
all caches services have been flushed
@@ -8684,7 +8681,7 @@ Pushed documents are also available in a received basket within Phraseanet Produ
an error occured
an error occured
Controller/Prod/ToolsController.php
- actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
an error occured : %message%
@@ -8714,7 +8711,7 @@ Pushed documents are also available in a received basket within Phraseanet Produ
audio
audio
- Phrasea/Twig/PhraseanetExtension.php
+ Phrasea/Twig/PhraseanetExtension.php
task-manager/task-editor/subdefs.html.twig
@@ -8782,7 +8779,7 @@ Pushed documents are also available in a received basket within Phraseanet Produ
web/account/reset-email.html.twig
admin/collection/create.html.twig
web/admin/index.html.twig
- admin/publications/fiche.html.twig
+ admin/publications/fiche.html.twig
web/common/dialog_export.html.twig
web/common/dialog_export.html.twig
web/common/dialog_export.html.twig
@@ -8974,7 +8971,7 @@ Pushed documents are also available in a received basket within Phraseanet Produ
admin/collection/details.html.twig
admin/databox/details.html.twig
web/admin/editusers.html.twig
- admin/publications/fiche.html.twig
+ admin/publications/fiche.html.twig
admin/statusbit/edit.html.twig
user/import/file.html.twig
web/developers/application.html.twig
@@ -9023,7 +9020,7 @@ Pushed documents are also available in a received basket within Phraseanet Produ
admin/collection/collection.html.twig
admin/collection/collection.html.twig
admin/collection/suggested_value.html.twig
- admin/publications/fiche.html.twig
+ admin/publications/fiche.html.twig
admin/publications/list.html.twig
web/admin/subdefs.html.twig
Bridge/Dailymotion/actioncontainers.html.twig
@@ -9074,14 +9071,14 @@ Pushed documents are also available in a received basket within Phraseanet Produ
admin/collection/suggested_value.html.twig
web/admin/dashboard.html.twig
web/admin/editusers.html.twig
- admin/publications/fiche.html.twig
+ admin/publications/fiche.html.twig
admin/publications/list.html.twig
admin/search-engine/phrasea.html.twig
admin/search-engine/sphinx-search.html.twig
web/admin/setup.html.twig
admin/statusbit/edit.html.twig
web/admin/structure.html.twig
- web/admin/subdefs.html.twig
+ web/admin/subdefs.html.twig
user/import/view.html.twig
admin/user/registrations.html.twig
web/developers/application_form.html.twig
@@ -9144,9 +9141,9 @@ Pushed documents are also available in a received basket within Phraseanet Produ
cancel
Cancel
actions/Tools/index.html.twig
- actions/Tools/index.html.twig
- actions/Tools/index.html.twig
- actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
cannes SAUF festival
@@ -9243,6 +9240,11 @@ Pushed documents are also available in a received basket within Phraseanet Produ
Select all
web/report/listColumn.html.twig
+
+ collection.label.unknown
+ Unknown collection
+ lib/classes/phrasea.php
+
commande::deadline
Deadline
@@ -9305,7 +9307,7 @@ Pushed documents are also available in a received basket within Phraseanet Produ
document
document
- Phrasea/Twig/PhraseanetExtension.php
+ Phrasea/Twig/PhraseanetExtension.php
web/admin/subdefs.html.twig
web/admin/subdefs.html.twig
task-manager/task-editor/subdefs.html.twig
@@ -9477,7 +9479,7 @@ Pushed documents are also available in a received basket within Phraseanet Produ
flash
flash
- Phrasea/Twig/PhraseanetExtension.php
+ Phrasea/Twig/PhraseanetExtension.php
task-manager/task-editor/subdefs.html.twig
@@ -9551,13 +9553,13 @@ Pushed documents are also available in a received basket within Phraseanet Produ
image
Image
- Phrasea/Twig/PhraseanetExtension.php
+ Phrasea/Twig/PhraseanetExtension.php
task-manager/task-editor/subdefs.html.twig
image rotation
Image rotation
- actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
image tool
@@ -9756,7 +9758,7 @@ Pushed documents are also available in a received basket within Phraseanet Produ
mettre a jour le nom original de fichier apres substitution
Update the filename
- actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
name
@@ -9772,14 +9774,14 @@ Pushed documents are also available in a received basket within Phraseanet Produ
no
No
web/account/sessions.html.twig
- web/admin/subdefs.html.twig
+ web/admin/subdefs.html.twig
user/import/view.html.twig
web/common/technical_datas.html.twig
no image selected
No Document selected
- actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
non
@@ -10313,11 +10315,12 @@ Pushed documents are also available in a received basket within Phraseanet Produ
View statistics
prod/preview/popularity.html.twig
-
+
previewLinkLabel
Preview
prod/Share/record.html.twig
prod/Share/record.html.twig
+ prod/Share/record.html.twig
print:: image de choix et description
@@ -10364,7 +10367,7 @@ Pushed documents are also available in a received basket within Phraseanet Produ
processing
Processing...
- actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
prod::Les enregistrements ne provienent pas tous de la meme base et ne peuvent donc etre traites ensemble
@@ -10771,7 +10774,7 @@ Pushed documents are also available in a received basket within Phraseanet Produ
reponses:: partager
Share
- prod/results/record.html.twig
+ prod/results/record.html.twig
reponses:: selectionner rien
@@ -10791,7 +10794,7 @@ Pushed documents are also available in a received basket within Phraseanet Produ
reponses::document sans titre
Untitled
- classes/record/adapter.php
+ classes/record/adapter.php
report :: aucun resultat trouve
@@ -11625,8 +11628,8 @@ Pushed documents are also available in a received basket within Phraseanet Produ
reportage
Feature
- Phrasea/Twig/PhraseanetExtension.php
- Phrasea/Twig/PhraseanetExtension.php
+ Phrasea/Twig/PhraseanetExtension.php
+ Phrasea/Twig/PhraseanetExtension.php
resultat numero %number%
@@ -11676,12 +11679,12 @@ Pushed documents are also available in a received basket within Phraseanet Produ
rotation 90 degres anti-horaires
Rotate 90 degrees counter clockwise
- actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
rotation 90 degres horaire
Rotate 90 degrees clockwise
- actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
scheduled status
@@ -11792,18 +11795,17 @@ Pushed documents are also available in a received basket within Phraseanet Produ
substitution HD
Document substitution
- actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
substitution SD
Thumbnail substitution
- actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
take a screenshot
Take a screenshoot
actions/Tools/index.html.twig
- actions/Tools/index.html.twig
task::_common_:hotfolder
@@ -11818,12 +11820,12 @@ Pushed documents are also available in a received basket within Phraseanet Produ
task::archive:Archivage
Archive in collection
- TaskManager/Job/ArchiveJob.php
+ TaskManager/Job/ArchiveJob.php
task::archive:Archiving files found into a 'hotfolder'
Archiving file(s) from hotfolder
- TaskManager/Job/ArchiveJob.php
+ TaskManager/Job/ArchiveJob.php
task::archive:archivage sur base/collection/
@@ -12522,7 +12524,7 @@ Pushed documents are also available in a received basket within Phraseanet Produ
thumbnail validation
Confirm thumbnail
- actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
to
@@ -12592,9 +12594,10 @@ Pushed documents are also available in a received basket within Phraseanet Produ
validate
Validate
actions/Tools/index.html.twig
- actions/Tools/index.html.twig
- actions/Tools/index.html.twig
- actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
validation:: NON
@@ -12659,7 +12662,7 @@ Pushed documents are also available in a received basket within Phraseanet Produ
yes
Yes
web/account/sessions.html.twig
- web/admin/subdefs.html.twig
+ web/admin/subdefs.html.twig
web/common/technical_datas.html.twig
diff --git a/resources/locales/messages.fr.xlf b/resources/locales/messages.fr.xlf
index 9efed3f92b..ae19da1f6d 100644
--- a/resources/locales/messages.fr.xlf
+++ b/resources/locales/messages.fr.xlf
@@ -1,14 +1,14 @@
-
+
-
+
The source node in most cases contains the sample message as written by the developer. If it looks like a dot-delimitted string such as "form.label.firstname", then the developer has not provided a default message.
-
-
+
+
Form/Configuration/EmailFormType.php
Form/Login/PhraseaAuthenticationForm.php
@@ -177,13 +177,13 @@
%quantity% records added
%quantity% enregistrements ajoutés
- Controller/Prod/BasketController.php
+ Controller/Prod/BasketController.php
Controller/Prod/StoryController.php
%quantity% records moved
%quantity% enregistrements déplacés
- Controller/Prod/BasketController.php
+ Controller/Prod/BasketController.php
%quantity% selected files
@@ -738,7 +738,7 @@
Ajouter un publisher
Ajouter un utilisateur
- admin/publications/fiche.html.twig
+ admin/publications/fiche.html.twig
Ajouter une publication
@@ -855,7 +855,7 @@
Controller/Admin/DataboxController.php
Controller/Admin/DataboxController.php
Controller/Admin/DataboxesController.php
- Controller/Prod/BasketController.php
+ Controller/Prod/BasketController.php
Controller/Prod/LazaretController.php
Controller/Prod/LazaretController.php
Controller/Prod/LazaretController.php
@@ -906,9 +906,9 @@
Un erreur est survenue
Controller/Admin/CollectionController.php
Controller/Admin/DataboxController.php
- Controller/Api/V1Controller.php
- Controller/Api/V1Controller.php
- Controller/Prod/BasketController.php
+ Controller/Api/V1Controller.php
+ Controller/Api/V1Controller.php
+ Controller/Prod/BasketController.php
web/admin/statusbit.html.twig
@@ -1297,32 +1297,32 @@
Basket created
Panier créé
- Controller/Prod/BasketController.php
+ Controller/Prod/BasketController.php
Basket has been archived
Le panier a été archivé
- Controller/Prod/BasketController.php
+ Controller/Prod/BasketController.php
Basket has been deleted
Le panier a été supprimé
- Controller/Prod/BasketController.php
+ Controller/Prod/BasketController.php
Basket has been unarchived
Le panier est disponible dans la fenêtre des paniers
- Controller/Prod/BasketController.php
+ Controller/Prod/BasketController.php
Basket has been updated
Le panier a été mis à jour
- Controller/Prod/BasketController.php
+ Controller/Prod/BasketController.php
Basket updated
Paniers mis à jour
- Controller/Prod/BasketController.php
+ Controller/Prod/BasketController.php
Bitrate
@@ -1558,7 +1558,7 @@
Changes for rotation will be applied only on the sub-definitions of "image" type.
Les rotations ne sont appliquées qu'aux sous-définitions des documents de type image.
- actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
Channels
@@ -1584,7 +1584,7 @@
Choisir
Choisir
web/admin/subdefs.html.twig
- web/admin/subdefs.html.twig
+ web/admin/subdefs.html.twig
prod/Baskets/Reorder.html.twig
prod/Story/Reorder.html.twig
@@ -1612,7 +1612,8 @@
Clear
Effacer
- admin/task-manager/log.html.twig
+ admin/task-manager/log_scheduler.html.twig
+ admin/task-manager/log_task.html.twig
Clear list
@@ -2268,7 +2269,7 @@
Disable document type sharing
Désactiver le lien de partage
- actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
Disabled
@@ -2409,17 +2410,17 @@
ERREUR : La classe de subdef est necessaire et egal a "thumbnail","preview" ou "document"
Erreur : Une classe est nécessaire et peut être égale à "Tout le monde","Sous-résolution" ou "Document"
- lib/classes/databox.php
+ lib/classes/databox.php
ERREUR : Les name de subdef sont uniques par groupe de subdefs et necessaire
L'attribut "name" du noeud "subdef" est obligatoire et unique par groupe de subdefs
- lib/classes/databox.php
+ lib/classes/databox.php
ERREUR : TOUTES LES BALISES subdefgroup necessitent un attribut name
ERREUR : Toutes les balises "subdefgroup" nécessitent un attribut "name"
- lib/classes/databox.php
+ lib/classes/databox.php
Edit
@@ -2494,7 +2495,7 @@
Email
E-mail
web/admin/dashboard.html.twig
- admin/publications/fiche.html.twig
+ admin/publications/fiche.html.twig
Email '%email%' for login '%login%' already exists in database
@@ -2629,7 +2630,7 @@
Enable document type sharing
Activer le lien de partage
- actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
Enable maintenance message broadcast
@@ -2830,7 +2831,7 @@
Etendue de la publication
Etendue de la publication
- admin/publications/fiche.html.twig
+ admin/publications/fiche.html.twig
admin/publications/list.html.twig
@@ -3310,7 +3311,7 @@
Id
Identifiant
- admin/publications/fiche.html.twig
+ admin/publications/fiche.html.twig
If request is bigger, then mail is still available
@@ -3397,12 +3398,12 @@
Indexation task
Tâche d'indexation
- TaskManager/Job/IndexerJob.php
+ TaskManager/Job/IndexerJob.php
Indexing Batch (collections/databox)
Lot d'indexation (Collections/Databox)
- TaskManager/Job/IndexerJob.php
+ TaskManager/Job/IndexerJob.php
Informations
@@ -3468,7 +3469,7 @@
Invalid file type, only (%supported_file_types%) file formats are supported
Type de fichier non supportés. Seuls les types de fichiers %supported_file_types% sont supportés.
admin/databox/databox.html.twig
- admin/statusbit/edit.html.twig
+ admin/statusbit/edit.html.twig
user/import/file.html.twig
@@ -3814,7 +3815,7 @@
Liste des personnes habilitees a publier sur ce fil
Liste des utilisateurs autorisés à publier sur ce fil
- admin/publications/fiche.html.twig
+ admin/publications/fiche.html.twig
Lists
@@ -4118,7 +4119,7 @@
Name or email
Nom ou e-mail
- admin/publications/fiche.html.twig
+ admin/publications/fiche.html.twig
Ne pas autoriser
@@ -4283,7 +4284,7 @@
Non-Restreinte (publique)
Non restreinte (publique)
- admin/publications/fiche.html.twig
+ admin/publications/fiche.html.twig
admin/publications/list.html.twig
@@ -4484,7 +4485,7 @@
Owner
Propriétaire
- admin/publications/fiche.html.twig
+ admin/publications/fiche.html.twig
Owner removed from list
@@ -4821,7 +4822,7 @@
Publique
Publique
- admin/publications/fiche.html.twig
+ admin/publications/fiche.html.twig
admin/publications/list.html.twig
@@ -5147,12 +5148,12 @@ Pour les utilisateurs authentifiés, la demande de validation est également dis
Record Not Found
Enregistrement non trouvé
- Controller/Api/V1Controller.php
+ Controller/Api/V1Controller.php
Record removed from basket
L'enregistrement a été supprimé du panier
- Controller/Prod/BasketController.php
+ Controller/Prod/BasketController.php
Record removed from story
@@ -5420,11 +5421,6 @@ Pour les utilisateurs authentifiés, la demande de validation est également dis
Retour aux commandes
prod/orders/order_item.html.twig
-
- Return
- Retour
- admin/task-manager/log.html.twig
-
Review order on %website%
Consulter la commande sur %website%
@@ -5742,7 +5738,7 @@ Pour les utilisateurs authentifiés, la demande de validation est également dis
Short description
Description brève
- admin/publications/fiche.html.twig
+ admin/publications/fiche.html.twig
admin/publications/list.html.twig
@@ -5856,7 +5852,7 @@ Pour les utilisateurs authentifiés, la demande de validation est également dis
Sous-titre
Sous-titre
- admin/publications/fiche.html.twig
+ admin/publications/fiche.html.twig
admin/publications/list.html.twig
@@ -5961,7 +5957,7 @@ Pour les utilisateurs authentifiés, la demande de validation est également dis
Story Not Found
Reportage inconnu
- Controller/Api/V1Controller.php
+ Controller/Api/V1Controller.php
Story created
@@ -6022,7 +6018,7 @@ Pour les utilisateurs authentifiés, la demande de validation est également dis
Substitution is not possible for this kind of record
La substitution de ce type d'enregistrement n'est pas possible
- actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
Success
@@ -6320,7 +6316,7 @@ Pour les utilisateurs authentifiés, la demande de validation est également dis
The requested basket does not exist
La panier demandé n'existe pas
- Controller/Prod/BasketController.php
+ Controller/Prod/BasketController.php
The task status
@@ -6483,7 +6479,7 @@ Pour les utilisateurs authentifiés, la demande de validation est également dis
Titre
Titre
- admin/publications/fiche.html.twig
+ admin/publications/fiche.html.twig
admin/publications/list.html.twig
admin/publications/list.html.twig
Bridge/Dailymotion/upload.html.twig
@@ -7378,7 +7374,7 @@ Pour les utilisateurs authentifiés, la demande de validation est également dis
You are not the feed owner
Vous n'êtes pas le propriétaire de ce flux
- admin/publications/fiche.html.twig
+ admin/publications/fiche.html.twig
You are not the owner of this feed, you can not edit it
@@ -7441,7 +7437,7 @@ Pour les utilisateurs authentifiés, la demande de validation est également dis
You do not have access to this basket
Vous n'avez pas accès à ce panier
- Controller/Prod/BasketController.php
+ Controller/Prod/BasketController.php
You do not have enough rights to access quarantine
@@ -7607,18 +7603,18 @@ Pour les utilisateurs authentifiés, la demande de validation est également dis
Your install might need to build some sub-definitions
Votre installation semble nécessiter la génération de sous-définitions.
- lib/classes/appbox.php
+ lib/classes/appbox.php
Your install might need to re-read technical datas
Votre installation nécessite probablement une relecture de données techniques
- lib/classes/appbox.php
+ lib/classes/appbox.php
Your install requires data migration, please execute the following command
Votre installation requiert une mise à jour, veuillez exécuter la commande suivante
- lib/classes/appbox.php
- lib/classes/appbox.php
+ lib/classes/appbox.php
+ lib/classes/appbox.php
Your medias and their subdefinitions (previews, thumbnails..) will be stored in these directories.
@@ -7640,7 +7636,7 @@ Pour les utilisateurs authentifiés, la demande de validation est également dis
action : ajouter au panier
Ajouter au panier
prod/preview/tools.html.twig
- prod/results/record.html.twig
+ prod/results/record.html.twig
action : bridge
@@ -7675,7 +7671,7 @@ Pour les utilisateurs authentifiés, la demande de validation est également dis
web/lightbox/validate.html.twig
web/prod/index.html.twig
prod/preview/tools.html.twig
- prod/results/record.html.twig
+ prod/results/record.html.twig
web/prod/toolbar.html.twig
prod/WorkZone/Basket.html.twig
prod/WorkZone/Story.html.twig
@@ -7691,7 +7687,7 @@ Pour les utilisateurs authentifiés, la demande de validation est également dis
action : print
Imprimer
prod/preview/tools.html.twig
- prod/results/record.html.twig
+ prod/results/record.html.twig
web/prod/toolbar.html.twig
prod/WorkZone/Basket.html.twig
prod/WorkZone/Story.html.twig
@@ -8429,7 +8425,7 @@ Si vous recevez cet e-mail sans l'avoir sollicité, merci de l'ignorer ou de le
admin::monitor: module admin
Admin
Controller/Admin/ConnectedUsersController.php
- lib/classes/phrasea.php
+ lib/classes/phrasea.php
web/admin/connected-users.html.twig
web/common/menubar.html.twig
@@ -8437,7 +8433,7 @@ Si vous recevez cet e-mail sans l'avoir sollicité, merci de l'ignorer ou de le
admin::monitor: module client
Classic
Controller/Admin/ConnectedUsersController.php
- lib/classes/phrasea.php
+ lib/classes/phrasea.php
classes/record/preview.php
web/admin/connected-users.html.twig
@@ -8445,7 +8441,7 @@ Si vous recevez cet e-mail sans l'avoir sollicité, merci de l'ignorer ou de le
admin::monitor: module comparateur
Lightbox
Controller/Admin/ConnectedUsersController.php
- lib/classes/phrasea.php
+ lib/classes/phrasea.php
web/admin/connected-users.html.twig
@@ -8457,7 +8453,7 @@ Si vous recevez cet e-mail sans l'avoir sollicité, merci de l'ignorer ou de le
admin::monitor: module production
Production
Controller/Admin/ConnectedUsersController.php
- lib/classes/phrasea.php
+ lib/classes/phrasea.php
classes/record/preview.php
web/admin/connected-users.html.twig
@@ -8465,7 +8461,7 @@ Si vous recevez cet e-mail sans l'avoir sollicité, merci de l'ignorer ou de le
admin::monitor: module report
Report
Controller/Admin/ConnectedUsersController.php
- lib/classes/phrasea.php
+ lib/classes/phrasea.php
web/admin/connected-users.html.twig
web/common/menubar.html.twig
@@ -8473,7 +8469,7 @@ Si vous recevez cet e-mail sans l'avoir sollicité, merci de l'ignorer ou de le
admin::monitor: module thesaurus
Thesaurus
Controller/Admin/ConnectedUsersController.php
- lib/classes/phrasea.php
+ lib/classes/phrasea.php
web/admin/connected-users.html.twig
web/common/menubar.html.twig
@@ -8481,7 +8477,7 @@ Si vous recevez cet e-mail sans l'avoir sollicité, merci de l'ignorer ou de le
admin::monitor: module upload
Upload
Controller/Admin/ConnectedUsersController.php
- lib/classes/phrasea.php
+ lib/classes/phrasea.php
web/common/menubar.html.twig
@@ -8491,7 +8487,7 @@ Si vous recevez cet e-mail sans l'avoir sollicité, merci de l'ignorer ou de le
Phrasea/Controller/LightboxController.php
Phrasea/Controller/LightboxController.php
Phrasea/Controller/LightboxController.php
- lib/classes/phrasea.php
+ lib/classes/phrasea.php
web/common/menubar.html.twig
@@ -8527,6 +8523,7 @@ Si vous recevez cet e-mail sans l'avoir sollicité, merci de l'ignorer ou de le
admin::plugins: plugins
Plugins
+ admin/plugins/index.html.twig
web/admin/tree.html.twig
@@ -8672,7 +8669,7 @@ Si vous recevez cet e-mail sans l'avoir sollicité, merci de l'ignorer ou de le
alert
Alerte
- actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
all caches services have been flushed
@@ -8683,7 +8680,7 @@ Si vous recevez cet e-mail sans l'avoir sollicité, merci de l'ignorer ou de le
an error occured
une erreur est survenue
Controller/Prod/ToolsController.php
- actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
an error occured : %message%
@@ -8713,7 +8710,7 @@ Si vous recevez cet e-mail sans l'avoir sollicité, merci de l'ignorer ou de le
audio
audios
- Phrasea/Twig/PhraseanetExtension.php
+ Phrasea/Twig/PhraseanetExtension.php
task-manager/task-editor/subdefs.html.twig
@@ -8781,7 +8778,7 @@ Si vous recevez cet e-mail sans l'avoir sollicité, merci de l'ignorer ou de le
web/account/reset-email.html.twig
admin/collection/create.html.twig
web/admin/index.html.twig
- admin/publications/fiche.html.twig
+ admin/publications/fiche.html.twig
web/common/dialog_export.html.twig
web/common/dialog_export.html.twig
web/common/dialog_export.html.twig
@@ -8973,7 +8970,7 @@ Si vous recevez cet e-mail sans l'avoir sollicité, merci de l'ignorer ou de le
admin/collection/details.html.twig
admin/databox/details.html.twig
web/admin/editusers.html.twig
- admin/publications/fiche.html.twig
+ admin/publications/fiche.html.twig
admin/statusbit/edit.html.twig
user/import/file.html.twig
web/developers/application.html.twig
@@ -9022,7 +9019,7 @@ Si vous recevez cet e-mail sans l'avoir sollicité, merci de l'ignorer ou de le
admin/collection/collection.html.twig
admin/collection/collection.html.twig
admin/collection/suggested_value.html.twig
- admin/publications/fiche.html.twig
+ admin/publications/fiche.html.twig
admin/publications/list.html.twig
web/admin/subdefs.html.twig
Bridge/Dailymotion/actioncontainers.html.twig
@@ -9073,14 +9070,14 @@ Si vous recevez cet e-mail sans l'avoir sollicité, merci de l'ignorer ou de le
admin/collection/suggested_value.html.twig
web/admin/dashboard.html.twig
web/admin/editusers.html.twig
- admin/publications/fiche.html.twig
+ admin/publications/fiche.html.twig
admin/publications/list.html.twig
admin/search-engine/phrasea.html.twig
admin/search-engine/sphinx-search.html.twig
web/admin/setup.html.twig
admin/statusbit/edit.html.twig
web/admin/structure.html.twig
- web/admin/subdefs.html.twig
+ web/admin/subdefs.html.twig
user/import/view.html.twig
admin/user/registrations.html.twig
web/developers/application_form.html.twig
@@ -9143,9 +9140,9 @@ Si vous recevez cet e-mail sans l'avoir sollicité, merci de l'ignorer ou de le
cancel
Annuler
actions/Tools/index.html.twig
- actions/Tools/index.html.twig
- actions/Tools/index.html.twig
- actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
cannes SAUF festival
@@ -9242,6 +9239,11 @@ Si vous recevez cet e-mail sans l'avoir sollicité, merci de l'ignorer ou de le
Tout cocher
web/report/listColumn.html.twig
+
+ collection.label.unknown
+ Collection inconnue
+ lib/classes/phrasea.php
+
commande::deadline
Date limite
@@ -9304,7 +9306,7 @@ Si vous recevez cet e-mail sans l'avoir sollicité, merci de l'ignorer ou de le
document
documents
- Phrasea/Twig/PhraseanetExtension.php
+ Phrasea/Twig/PhraseanetExtension.php
web/admin/subdefs.html.twig
web/admin/subdefs.html.twig
task-manager/task-editor/subdefs.html.twig
@@ -9476,7 +9478,7 @@ Si vous recevez cet e-mail sans l'avoir sollicité, merci de l'ignorer ou de le
flash
Flash
- Phrasea/Twig/PhraseanetExtension.php
+ Phrasea/Twig/PhraseanetExtension.php
task-manager/task-editor/subdefs.html.twig
@@ -9550,13 +9552,13 @@ Si vous recevez cet e-mail sans l'avoir sollicité, merci de l'ignorer ou de le
image
images
- Phrasea/Twig/PhraseanetExtension.php
+ Phrasea/Twig/PhraseanetExtension.php
task-manager/task-editor/subdefs.html.twig
image rotation
Rotation d'images
- actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
image tool
@@ -9755,7 +9757,7 @@ Si vous recevez cet e-mail sans l'avoir sollicité, merci de l'ignorer ou de le
mettre a jour le nom original de fichier apres substitution
Mettre à jour le nom original du fichier après substitution
- actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
name
@@ -9771,14 +9773,14 @@ Si vous recevez cet e-mail sans l'avoir sollicité, merci de l'ignorer ou de le
no
Non
web/account/sessions.html.twig
- web/admin/subdefs.html.twig
+ web/admin/subdefs.html.twig
user/import/view.html.twig
web/common/technical_datas.html.twig
no image selected
Aucune image sélectionnée
- actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
non
@@ -10317,6 +10319,7 @@ Si vous recevez cet e-mail sans l'avoir sollicité, merci de l'ignorer ou de le
Prévisualiser
prod/Share/record.html.twig
prod/Share/record.html.twig
+ prod/Share/record.html.twig
print:: image de choix et description
@@ -10363,7 +10366,7 @@ Si vous recevez cet e-mail sans l'avoir sollicité, merci de l'ignorer ou de le
processing
En cours...
- actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
prod::Les enregistrements ne provienent pas tous de la meme base et ne peuvent donc etre traites ensemble
@@ -10770,7 +10773,7 @@ Si vous recevez cet e-mail sans l'avoir sollicité, merci de l'ignorer ou de le
reponses:: partager
Partager
- prod/results/record.html.twig
+ prod/results/record.html.twig
reponses:: selectionner rien
@@ -10790,7 +10793,7 @@ Si vous recevez cet e-mail sans l'avoir sollicité, merci de l'ignorer ou de le
reponses::document sans titre
Sans titre
- classes/record/adapter.php
+ classes/record/adapter.php
report :: aucun resultat trouve
@@ -11624,8 +11627,8 @@ Si vous recevez cet e-mail sans l'avoir sollicité, merci de l'ignorer ou de le
reportage
Reportage
- Phrasea/Twig/PhraseanetExtension.php
- Phrasea/Twig/PhraseanetExtension.php
+ Phrasea/Twig/PhraseanetExtension.php
+ Phrasea/Twig/PhraseanetExtension.php
resultat numero %number%
@@ -11675,12 +11678,12 @@ Si vous recevez cet e-mail sans l'avoir sollicité, merci de l'ignorer ou de le
rotation 90 degres anti-horaires
Rotation de 90 degrés anti-horaires
- actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
rotation 90 degres horaire
Rotation de 90 degrés horaire
- actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
scheduled status
@@ -11791,18 +11794,17 @@ Si vous recevez cet e-mail sans l'avoir sollicité, merci de l'ignorer ou de le
substitution HD
Substitution de document
- actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
substitution SD
Substitution de la vignette thumbnail
- actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
take a screenshot
Faire une capture
actions/Tools/index.html.twig
- actions/Tools/index.html.twig
task::_common_:hotfolder
@@ -11817,12 +11819,12 @@ Si vous recevez cet e-mail sans l'avoir sollicité, merci de l'ignorer ou de le
task::archive:Archivage
Archive dans la collection
- TaskManager/Job/ArchiveJob.php
+ TaskManager/Job/ArchiveJob.php
task::archive:Archiving files found into a 'hotfolder'
Archiver les fichiers déposés dans le dossier
- TaskManager/Job/ArchiveJob.php
+ TaskManager/Job/ArchiveJob.php
task::archive:archivage sur base/collection/
@@ -12521,7 +12523,7 @@ Si vous recevez cet e-mail sans l'avoir sollicité, merci de l'ignorer ou de le
thumbnail validation
Validation de la vignette
- actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
to
@@ -12591,9 +12593,10 @@ Si vous recevez cet e-mail sans l'avoir sollicité, merci de l'ignorer ou de le
validate
Valider
actions/Tools/index.html.twig
- actions/Tools/index.html.twig
- actions/Tools/index.html.twig
- actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
validation:: NON
@@ -12658,7 +12661,7 @@ Si vous recevez cet e-mail sans l'avoir sollicité, merci de l'ignorer ou de le
yes
Oui
web/account/sessions.html.twig
- web/admin/subdefs.html.twig
+ web/admin/subdefs.html.twig
web/common/technical_datas.html.twig
diff --git a/resources/locales/messages.nl.xlf b/resources/locales/messages.nl.xlf
index 2aa5e6c4e3..af54fa8c68 100644
--- a/resources/locales/messages.nl.xlf
+++ b/resources/locales/messages.nl.xlf
@@ -1,6 +1,6 @@
-
+
The source node in most cases contains the sample message as written by the developer. If it looks like a dot-delimitted string such as "form.label.firstname", then the developer has not provided a default message.
@@ -181,13 +181,13 @@
%quantity% records added
%quantity% records added
- Controller/Prod/BasketController.php
+ Controller/Prod/BasketController.php
Controller/Prod/StoryController.php
%quantity% records moved
%quantity% records moved
- Controller/Prod/BasketController.php
+ Controller/Prod/BasketController.php
%quantity% selected files
@@ -742,7 +742,7 @@
Ajouter un publisher
Een publisher toevoegen
- admin/publications/fiche.html.twig
+ admin/publications/fiche.html.twig
Ajouter une publication
@@ -859,7 +859,7 @@
Controller/Admin/DataboxController.php
Controller/Admin/DataboxController.php
Controller/Admin/DataboxesController.php
- Controller/Prod/BasketController.php
+ Controller/Prod/BasketController.php
Controller/Prod/LazaretController.php
Controller/Prod/LazaretController.php
Controller/Prod/LazaretController.php
@@ -910,9 +910,9 @@
Er is een fout opgetreden
Controller/Admin/CollectionController.php
Controller/Admin/DataboxController.php
- Controller/Api/V1Controller.php
- Controller/Api/V1Controller.php
- Controller/Prod/BasketController.php
+ Controller/Api/V1Controller.php
+ Controller/Api/V1Controller.php
+ Controller/Prod/BasketController.php
web/admin/statusbit.html.twig
@@ -1301,32 +1301,32 @@
Basket created
Mandje werd aangemaakt
- Controller/Prod/BasketController.php
+ Controller/Prod/BasketController.php
Basket has been archived
Mandje werd geachriveerd
- Controller/Prod/BasketController.php
+ Controller/Prod/BasketController.php
Basket has been deleted
Mandje werd verwijderd
- Controller/Prod/BasketController.php
+ Controller/Prod/BasketController.php
Basket has been unarchived
Mandje werd actief gezet
- Controller/Prod/BasketController.php
+ Controller/Prod/BasketController.php
Basket has been updated
Mandje werd geüpdate
- Controller/Prod/BasketController.php
+ Controller/Prod/BasketController.php
Basket updated
Mandje geüpdate
- Controller/Prod/BasketController.php
+ Controller/Prod/BasketController.php
Bitrate
@@ -1562,7 +1562,7 @@
Changes for rotation will be applied only on the sub-definitions of "image" type.
Changes for rotation will be applied only on the sub-definitions of "image" type.
- actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
Channels
@@ -1588,7 +1588,7 @@
Choisir
Kiezen
web/admin/subdefs.html.twig
- web/admin/subdefs.html.twig
+ web/admin/subdefs.html.twig
prod/Baskets/Reorder.html.twig
prod/Story/Reorder.html.twig
@@ -1616,7 +1616,8 @@
Clear
Wis
- admin/task-manager/log.html.twig
+ admin/task-manager/log_scheduler.html.twig
+ admin/task-manager/log_task.html.twig
Clear list
@@ -2272,7 +2273,7 @@
Disable document type sharing
Disable document type sharing
- actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
Disabled
@@ -2413,17 +2414,17 @@
ERREUR : La classe de subdef est necessaire et egal a "thumbnail","preview" ou "document"
ERREUR : La classe de subdef est necessaire et egal a "thumbnail","preview" ou "document"
- lib/classes/databox.php
+ lib/classes/databox.php
ERREUR : Les name de subdef sont uniques par groupe de subdefs et necessaire
FOUT : De namen van de thumbnail moeten uniek zijn per group van thumbnails
- lib/classes/databox.php
+ lib/classes/databox.php
ERREUR : TOUTES LES BALISES subdefgroup necessitent un attribut name
FOUT : ALLE TAGS subdefgroup vereisen een atribuut naam
- lib/classes/databox.php
+ lib/classes/databox.php
Edit
@@ -2498,7 +2499,7 @@
Email
Email
web/admin/dashboard.html.twig
- admin/publications/fiche.html.twig
+ admin/publications/fiche.html.twig
Email '%email%' for login '%login%' already exists in database
@@ -2633,7 +2634,7 @@
Enable document type sharing
Enable document type sharing
- actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
Enable maintenance message broadcast
@@ -2834,7 +2835,7 @@
Etendue de la publication
Omvang van de publicatie
- admin/publications/fiche.html.twig
+ admin/publications/fiche.html.twig
admin/publications/list.html.twig
@@ -3314,7 +3315,7 @@
Id
Id
- admin/publications/fiche.html.twig
+ admin/publications/fiche.html.twig
If request is bigger, then mail is still available
@@ -3401,12 +3402,12 @@
Indexation task
Indexation task
- TaskManager/Job/IndexerJob.php
+ TaskManager/Job/IndexerJob.php
Indexing Batch (collections/databox)
Indexing Batch (collections/databox)
- TaskManager/Job/IndexerJob.php
+ TaskManager/Job/IndexerJob.php
Informations
@@ -3472,7 +3473,7 @@
Invalid file type, only (%supported_file_types%) file formats are supported
Ongeldig bestandsformaat, enkel (%supported_file_types%) bestandsformaten worden ondersteund
admin/databox/databox.html.twig
- admin/statusbit/edit.html.twig
+ admin/statusbit/edit.html.twig
user/import/file.html.twig
@@ -3818,7 +3819,7 @@
Liste des personnes habilitees a publier sur ce fil
Lijst van personen die gekwalificeerd te publiceren op deze lijn
- admin/publications/fiche.html.twig
+ admin/publications/fiche.html.twig
Lists
@@ -4122,7 +4123,7 @@
Name or email
Naam of email
- admin/publications/fiche.html.twig
+ admin/publications/fiche.html.twig
Ne pas autoriser
@@ -4287,7 +4288,7 @@
Non-Restreinte (publique)
Niet beperkt (publiek)
- admin/publications/fiche.html.twig
+ admin/publications/fiche.html.twig
admin/publications/list.html.twig
@@ -4488,7 +4489,7 @@
Owner
Eigenaar
- admin/publications/fiche.html.twig
+ admin/publications/fiche.html.twig
Owner removed from list
@@ -4825,7 +4826,7 @@
Publique
Publiek
- admin/publications/fiche.html.twig
+ admin/publications/fiche.html.twig
admin/publications/list.html.twig
@@ -5149,12 +5150,12 @@
Record Not Found
Document niet gevonden
- Controller/Api/V1Controller.php
+ Controller/Api/V1Controller.php
Record removed from basket
Record uit het mandje verwijderd
- Controller/Prod/BasketController.php
+ Controller/Prod/BasketController.php
Record removed from story
@@ -5422,11 +5423,6 @@
Terug naar de bestellingen
prod/orders/order_item.html.twig
-
- Return
- Return
- admin/task-manager/log.html.twig
-
Review order on %website%
Review order on %website%
@@ -5744,7 +5740,7 @@
Short description
Korte beschrijving
- admin/publications/fiche.html.twig
+ admin/publications/fiche.html.twig
admin/publications/list.html.twig
@@ -5858,7 +5854,7 @@
Sous-titre
Onder titel
- admin/publications/fiche.html.twig
+ admin/publications/fiche.html.twig
admin/publications/list.html.twig
@@ -5963,7 +5959,7 @@
Story Not Found
Artikel niet gevonden
- Controller/Api/V1Controller.php
+ Controller/Api/V1Controller.php
Story created
@@ -6024,7 +6020,7 @@
Substitution is not possible for this kind of record
Vervanging is niet mogelijk voor dit soort van record
- actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
Success
@@ -6322,7 +6318,7 @@
The requested basket does not exist
Het gevraagde mandje bestaat niet
- Controller/Prod/BasketController.php
+ Controller/Prod/BasketController.php
The task status
@@ -6485,7 +6481,7 @@
Titre
Titel
- admin/publications/fiche.html.twig
+ admin/publications/fiche.html.twig
admin/publications/list.html.twig
admin/publications/list.html.twig
Bridge/Dailymotion/upload.html.twig
@@ -7380,7 +7376,7 @@
You are not the feed owner
U bent niet de feed eigenaar
- admin/publications/fiche.html.twig
+ admin/publications/fiche.html.twig
You are not the owner of this feed, you can not edit it
@@ -7443,7 +7439,7 @@
You do not have access to this basket
U hebt geen toegang tot dit mandje
- Controller/Prod/BasketController.php
+ Controller/Prod/BasketController.php
You do not have enough rights to access quarantine
@@ -7609,18 +7605,18 @@
Your install might need to build some sub-definitions
Uw installatie zou mogelijks enkele thumbnails moeten aanmaken
- lib/classes/appbox.php
+ lib/classes/appbox.php
Your install might need to re-read technical datas
Uw installatie moet de technische data herlezen
- lib/classes/appbox.php
+ lib/classes/appbox.php
Your install requires data migration, please execute the following command
Uw installatie heeft data migratie nodig, voer volgend commando uit
- lib/classes/appbox.php
- lib/classes/appbox.php
+ lib/classes/appbox.php
+ lib/classes/appbox.php
Your medias and their subdefinitions (previews, thumbnails..) will be stored in these directories.
@@ -7642,7 +7638,7 @@
action : ajouter au panier
aan mandje toevoegen
prod/preview/tools.html.twig
- prod/results/record.html.twig
+ prod/results/record.html.twig
action : bridge
@@ -7677,7 +7673,7 @@
web/lightbox/validate.html.twig
web/prod/index.html.twig
prod/preview/tools.html.twig
- prod/results/record.html.twig
+ prod/results/record.html.twig
web/prod/toolbar.html.twig
prod/WorkZone/Basket.html.twig
prod/WorkZone/Story.html.twig
@@ -7693,7 +7689,7 @@
action : print
Print
prod/preview/tools.html.twig
- prod/results/record.html.twig
+ prod/results/record.html.twig
web/prod/toolbar.html.twig
prod/WorkZone/Basket.html.twig
prod/WorkZone/Story.html.twig
@@ -8430,7 +8426,7 @@
admin::monitor: module admin
Beheerder
Controller/Admin/ConnectedUsersController.php
- lib/classes/phrasea.php
+ lib/classes/phrasea.php
web/admin/connected-users.html.twig
web/common/menubar.html.twig
@@ -8438,7 +8434,7 @@
admin::monitor: module client
Klant module
Controller/Admin/ConnectedUsersController.php
- lib/classes/phrasea.php
+ lib/classes/phrasea.php
classes/record/preview.php
web/admin/connected-users.html.twig
@@ -8446,7 +8442,7 @@
admin::monitor: module comparateur
Vergelijkings module
Controller/Admin/ConnectedUsersController.php
- lib/classes/phrasea.php
+ lib/classes/phrasea.php
web/admin/connected-users.html.twig
@@ -8458,7 +8454,7 @@
admin::monitor: module production
Productie
Controller/Admin/ConnectedUsersController.php
- lib/classes/phrasea.php
+ lib/classes/phrasea.php
classes/record/preview.php
web/admin/connected-users.html.twig
@@ -8466,7 +8462,7 @@
admin::monitor: module report
Rapport
Controller/Admin/ConnectedUsersController.php
- lib/classes/phrasea.php
+ lib/classes/phrasea.php
web/admin/connected-users.html.twig
web/common/menubar.html.twig
@@ -8474,7 +8470,7 @@
admin::monitor: module thesaurus
Thesaurus
Controller/Admin/ConnectedUsersController.php
- lib/classes/phrasea.php
+ lib/classes/phrasea.php
web/admin/connected-users.html.twig
web/common/menubar.html.twig
@@ -8482,7 +8478,7 @@
admin::monitor: module upload
Upload
Controller/Admin/ConnectedUsersController.php
- lib/classes/phrasea.php
+ lib/classes/phrasea.php
web/common/menubar.html.twig
@@ -8492,7 +8488,7 @@
Phrasea/Controller/LightboxController.php
Phrasea/Controller/LightboxController.php
Phrasea/Controller/LightboxController.php
- lib/classes/phrasea.php
+ lib/classes/phrasea.php
web/common/menubar.html.twig
@@ -8528,6 +8524,7 @@
admin::plugins: plugins
admin::plugins: plugins
+ admin/plugins/index.html.twig
web/admin/tree.html.twig
@@ -8673,7 +8670,7 @@
alert
alert
- actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
all caches services have been flushed
@@ -8684,7 +8681,7 @@
an error occured
een fout geeft zich voorgedaan
Controller/Prod/ToolsController.php
- actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
an error occured : %message%
@@ -8714,7 +8711,7 @@
audio
Audio
- Phrasea/Twig/PhraseanetExtension.php
+ Phrasea/Twig/PhraseanetExtension.php
task-manager/task-editor/subdefs.html.twig
@@ -8782,7 +8779,7 @@
web/account/reset-email.html.twig
admin/collection/create.html.twig
web/admin/index.html.twig
- admin/publications/fiche.html.twig
+ admin/publications/fiche.html.twig
web/common/dialog_export.html.twig
web/common/dialog_export.html.twig
web/common/dialog_export.html.twig
@@ -8974,7 +8971,7 @@
admin/collection/details.html.twig
admin/databox/details.html.twig
web/admin/editusers.html.twig
- admin/publications/fiche.html.twig
+ admin/publications/fiche.html.twig
admin/statusbit/edit.html.twig
user/import/file.html.twig
web/developers/application.html.twig
@@ -9023,7 +9020,7 @@
admin/collection/collection.html.twig
admin/collection/collection.html.twig
admin/collection/suggested_value.html.twig
- admin/publications/fiche.html.twig
+ admin/publications/fiche.html.twig
admin/publications/list.html.twig
web/admin/subdefs.html.twig
Bridge/Dailymotion/actioncontainers.html.twig
@@ -9074,14 +9071,14 @@
admin/collection/suggested_value.html.twig
web/admin/dashboard.html.twig
web/admin/editusers.html.twig
- admin/publications/fiche.html.twig
+ admin/publications/fiche.html.twig
admin/publications/list.html.twig
admin/search-engine/phrasea.html.twig
admin/search-engine/sphinx-search.html.twig
web/admin/setup.html.twig
admin/statusbit/edit.html.twig
web/admin/structure.html.twig
- web/admin/subdefs.html.twig
+ web/admin/subdefs.html.twig
user/import/view.html.twig
admin/user/registrations.html.twig
web/developers/application_form.html.twig
@@ -9144,9 +9141,9 @@
cancel
annuleren
actions/Tools/index.html.twig
- actions/Tools/index.html.twig
- actions/Tools/index.html.twig
- actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
cannes SAUF festival
@@ -9243,6 +9240,11 @@
alles uitschakelen
web/report/listColumn.html.twig
+
+ collection.label.unknown
+ collection.label.unknown
+ lib/classes/phrasea.php
+
commande::deadline
Deadline
@@ -9305,7 +9307,7 @@
document
document
- Phrasea/Twig/PhraseanetExtension.php
+ Phrasea/Twig/PhraseanetExtension.php
web/admin/subdefs.html.twig
web/admin/subdefs.html.twig
task-manager/task-editor/subdefs.html.twig
@@ -9477,7 +9479,7 @@
flash
flash
- Phrasea/Twig/PhraseanetExtension.php
+ Phrasea/Twig/PhraseanetExtension.php
task-manager/task-editor/subdefs.html.twig
@@ -9551,13 +9553,13 @@
image
Beeld
- Phrasea/Twig/PhraseanetExtension.php
+ Phrasea/Twig/PhraseanetExtension.php
task-manager/task-editor/subdefs.html.twig
image rotation
beeld rotatie
- actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
image tool
@@ -9756,7 +9758,7 @@
mettre a jour le nom original de fichier apres substitution
de naam van het bestand up to date zetten na vervanging
- actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
name
@@ -9772,14 +9774,14 @@
no
Nee
web/account/sessions.html.twig
- web/admin/subdefs.html.twig
+ web/admin/subdefs.html.twig
user/import/view.html.twig
web/common/technical_datas.html.twig
no image selected
geen beeld geselecteerd
- actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
non
@@ -10318,6 +10320,7 @@
previewLinkLabel
prod/Share/record.html.twig
prod/Share/record.html.twig
+ prod/Share/record.html.twig
print:: image de choix et description
@@ -10364,7 +10367,7 @@
processing
verwerken
- actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
prod::Les enregistrements ne provienent pas tous de la meme base et ne peuvent donc etre traites ensemble
@@ -10771,7 +10774,7 @@
reponses:: partager
delen
- prod/results/record.html.twig
+ prod/results/record.html.twig
reponses:: selectionner rien
@@ -10791,7 +10794,7 @@
reponses::document sans titre
Documenten zonder titel
- classes/record/adapter.php
+ classes/record/adapter.php
report :: aucun resultat trouve
@@ -11625,8 +11628,8 @@
reportage
Reportage
- Phrasea/Twig/PhraseanetExtension.php
- Phrasea/Twig/PhraseanetExtension.php
+ Phrasea/Twig/PhraseanetExtension.php
+ Phrasea/Twig/PhraseanetExtension.php
resultat numero %number%
@@ -11676,12 +11679,12 @@
rotation 90 degres anti-horaires
90 graden niet kloksgewijs draaien
- actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
rotation 90 degres horaire
90 graden kloksgewijs draaien
- actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
scheduled status
@@ -11792,18 +11795,17 @@
substitution HD
vervanging HD
- actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
substitution SD
vervanging SD
- actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
take a screenshot
Maak een schermafbeelding
actions/Tools/index.html.twig
- actions/Tools/index.html.twig
task::_common_:hotfolder
@@ -11818,12 +11820,12 @@
task::archive:Archivage
Archivering
- TaskManager/Job/ArchiveJob.php
+ TaskManager/Job/ArchiveJob.php
task::archive:Archiving files found into a 'hotfolder'
Archivering files gevonden in een 'hotfolder'
- TaskManager/Job/ArchiveJob.php
+ TaskManager/Job/ArchiveJob.php
task::archive:archivage sur base/collection/
@@ -12522,7 +12524,7 @@
thumbnail validation
thumbnail goedkeuring
- actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
to
@@ -12592,9 +12594,10 @@
validate
OK
actions/Tools/index.html.twig
- actions/Tools/index.html.twig
- actions/Tools/index.html.twig
- actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
+ actions/Tools/index.html.twig
validation:: NON
@@ -12659,7 +12662,7 @@
yes
Ja
web/account/sessions.html.twig
- web/admin/subdefs.html.twig
+ web/admin/subdefs.html.twig
web/common/technical_datas.html.twig
diff --git a/resources/locales/validators.de.xlf b/resources/locales/validators.de.xlf
index 3730f62134..c8d622b6ee 100644
--- a/resources/locales/validators.de.xlf
+++ b/resources/locales/validators.de.xlf
@@ -1,6 +1,6 @@
-
+
The source node in most cases contains the sample message as written by the developer. If it looks like a dot-delimitted string such as "form.label.firstname", then the developer has not provided a default message.
diff --git a/resources/locales/validators.en.xlf b/resources/locales/validators.en.xlf
index e4fcf336ce..c561b445ec 100644
--- a/resources/locales/validators.en.xlf
+++ b/resources/locales/validators.en.xlf
@@ -1,6 +1,6 @@
-
+
-
+
The source node in most cases contains the sample message as written by the developer. If it looks like a dot-delimitted string such as "form.label.firstname", then the developer has not provided a default message.
diff --git a/resources/locales/validators.fr.xlf b/resources/locales/validators.fr.xlf
index 6ae008a0b6..1322397415 100644
--- a/resources/locales/validators.fr.xlf
+++ b/resources/locales/validators.fr.xlf
@@ -1,6 +1,6 @@
-
+
The source node in most cases contains the sample message as written by the developer. If it looks like a dot-delimitted string such as "form.label.firstname", then the developer has not provided a default message.
diff --git a/resources/locales/validators.nl.xlf b/resources/locales/validators.nl.xlf
index cb0c63a263..048119eede 100644
--- a/resources/locales/validators.nl.xlf
+++ b/resources/locales/validators.nl.xlf
@@ -1,6 +1,6 @@
-
+
The source node in most cases contains the sample message as written by the developer. If it looks like a dot-delimitted string such as "form.label.firstname", then the developer has not provided a default message.
diff --git a/www/skins/admin/plugins-admin.png b/resources/www/admin/images/plugins-admin.png
similarity index 100%
rename from www/skins/admin/plugins-admin.png
rename to resources/www/admin/images/plugins-admin.png
diff --git a/www/skins/icons/plugins.png b/resources/www/common/images/icons/plugins.png
similarity index 100%
rename from www/skins/icons/plugins.png
rename to resources/www/common/images/icons/plugins.png
diff --git a/resources/www/common/js/jquery.tooltip.js b/resources/www/common/js/jquery.tooltip.js
index 6822568f64..91bc252ecf 100644
--- a/resources/www/common/js/jquery.tooltip.js
+++ b/resources/www/common/js/jquery.tooltip.js
@@ -348,7 +348,7 @@
//correction par ratio
- if (resizeImgTips && $('#' + settings($.tooltip.current).id + ' .imgTips')[0]) {
+ if (resizeImgTips && $imgTips.get(0)) {
if (ratioSurfaceH > ratioImage) {
horS = v.y * ratioImage * v.y;
@@ -416,13 +416,12 @@
top: top
});
-
//si ya une image on re-ajuste au ratio
- if (resizeImgTips && $('#' + settings($.tooltip.current).id + ' .imgTips')[0]) {
+ if (resizeImgTips && $imgTips.get(0)) {
if (width == 'auto')
- width = $('#' + settings($.tooltip.current).id).width();
+ width = $imgTips.get(0).width();
if (height == 'auto')
- height = $('#' + settings($.tooltip.current).id).height();
+ height = $imgTips.get(0).height();
if (ratio > 1) {
var nh = width / ratio;
if (nh > height) {
@@ -439,7 +438,19 @@
}
width = nw;
}
- } else {
+ }
+ else if (resizeVideoTips && $videoTips.get(0)) {
+ width = $videoTips.data('original-width');
+ height = $videoTips.data('original-height');
+ // limit video to maxWidth:
+ if( width > 720 ) {
+ var limitRatio = width/height;
+ width = 720;
+ height = width / limitRatio;
+ }
+ console.log('video first scaled to ', width, height)
+ }
+ else {
if (vertS < horS) {
height = 'auto';
}
@@ -469,7 +480,7 @@
width = imgWidth + 45;
height = imgHeight + 75;
-
+ console.log('video scaled to ', width, height)
$videoTips.css({
width: Math.round(imgWidth),
height: Math.round(imgHeight)
diff --git a/resources/www/lightbox/styles/jquery-ui/_shared.scss b/resources/www/lightbox/styles/jquery-ui/_shared.scss
new file mode 100644
index 0000000000..5fff3aef2b
--- /dev/null
+++ b/resources/www/lightbox/styles/jquery-ui/_shared.scss
@@ -0,0 +1,1006 @@
+
+/* Layout helpers
+----------------------------------*/
+.ui-helper-hidden {
+ display: none;
+}
+.ui-helper-hidden-accessible {
+ border: 0;
+ clip: rect(0 0 0 0);
+ height: 1px;
+ margin: -1px;
+ overflow: hidden;
+ padding: 0;
+ position: absolute;
+ width: 1px;
+}
+.ui-helper-reset {
+ margin: 0;
+ padding: 0;
+ border: 0;
+ outline: 0;
+ line-height: 1.3;
+ text-decoration: none;
+ font-size: 100%;
+ list-style: none;
+}
+.ui-helper-clearfix:before,
+.ui-helper-clearfix:after {
+ content: "";
+ display: table;
+ border-collapse: collapse;
+}
+.ui-helper-clearfix:after {
+ clear: both;
+}
+.ui-helper-clearfix {
+ min-height: 0; /* support: IE7 */
+}
+.ui-helper-zfix {
+ width: 100%;
+ height: 100%;
+ top: 0;
+ left: 0;
+ position: absolute;
+ opacity: 0;
+ filter:Alpha(Opacity=0);
+}
+
+.ui-front {
+ z-index: 100;
+}
+
+
+/* Interaction Cues
+----------------------------------*/
+.ui-state-disabled {
+ cursor: default !important;
+}
+
+
+/* Icons
+----------------------------------*/
+
+/* states and images */
+.ui-icon {
+ display: block;
+ text-indent: -99999px;
+ overflow: hidden;
+ background-repeat: no-repeat;
+}
+
+
+/* Misc visuals
+----------------------------------*/
+
+/* Overlays */
+.ui-widget-overlay {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+}
+.ui-resizable {
+ position: relative;
+}
+.ui-resizable-handle {
+ position: absolute;
+ font-size: 0.1px;
+ display: block;
+}
+.ui-resizable-disabled .ui-resizable-handle,
+.ui-resizable-autohide .ui-resizable-handle {
+ display: none;
+}
+.ui-resizable-n {
+ cursor: n-resize;
+ height: 7px;
+ width: 100%;
+ top: -5px;
+ left: 0;
+}
+.ui-resizable-s {
+ cursor: s-resize;
+ height: 7px;
+ width: 100%;
+ bottom: -5px;
+ left: 0;
+}
+.ui-resizable-e {
+ cursor: e-resize;
+ width: 7px;
+ right: -5px;
+ top: 0;
+ height: 100%;
+}
+.ui-resizable-w {
+ cursor: w-resize;
+ width: 7px;
+ left: -5px;
+ top: 0;
+ height: 100%;
+}
+.ui-resizable-se {
+ cursor: se-resize;
+ width: 12px;
+ height: 12px;
+ right: 1px;
+ bottom: 1px;
+}
+.ui-resizable-sw {
+ cursor: sw-resize;
+ width: 9px;
+ height: 9px;
+ left: -5px;
+ bottom: -5px;
+}
+.ui-resizable-nw {
+ cursor: nw-resize;
+ width: 9px;
+ height: 9px;
+ left: -5px;
+ top: -5px;
+}
+.ui-resizable-ne {
+ cursor: ne-resize;
+ width: 9px;
+ height: 9px;
+ right: -5px;
+ top: -5px;
+}
+.ui-selectable-helper {
+ position: absolute;
+ z-index: 100;
+ border: 1px dotted black;
+}
+.ui-accordion .ui-accordion-header {
+ display: block;
+ cursor: pointer;
+ position: relative;
+ margin-top: 2px;
+ padding: .5em .5em .5em .7em;
+ min-height: 0; /* support: IE7 */
+}
+.ui-accordion .ui-accordion-icons {
+ padding-left: 2.2em;
+}
+.ui-accordion .ui-accordion-noicons {
+ padding-left: .7em;
+}
+.ui-accordion .ui-accordion-icons .ui-accordion-icons {
+ padding-left: 2.2em;
+}
+.ui-accordion .ui-accordion-header .ui-accordion-header-icon {
+ position: absolute;
+ left: .5em;
+ top: 50%;
+ margin-top: -8px;
+}
+.ui-accordion .ui-accordion-content {
+ padding: 1em 2.2em;
+ border-top: 0;
+ overflow: auto;
+}
+.ui-autocomplete {
+ position: absolute;
+ top: 0;
+ left: 0;
+ cursor: default;
+}
+.ui-button {
+ display: inline-block;
+ position: relative;
+ padding: 0;
+ line-height: normal;
+ margin-right: .1em;
+ cursor: pointer;
+ vertical-align: middle;
+ text-align: center;
+ overflow: visible; /* removes extra width in IE */
+}
+.ui-button,
+.ui-button:link,
+.ui-button:visited,
+.ui-button:hover,
+.ui-button:active {
+ text-decoration: none;
+}
+/* to make room for the icon, a width needs to be set here */
+.ui-button-icon-only {
+ width: 2.2em;
+}
+/* button elements seem to need a little more width */
+button.ui-button-icon-only {
+ width: 2.4em;
+}
+.ui-button-icons-only {
+ width: 3.4em;
+}
+button.ui-button-icons-only {
+ width: 3.7em;
+}
+
+/* button text element */
+.ui-button .ui-button-text {
+ display: block;
+ line-height: normal;
+}
+.ui-button-text-only .ui-button-text {
+ padding: .4em 1em;
+}
+.ui-button-icon-only .ui-button-text,
+.ui-button-icons-only .ui-button-text {
+ padding: .4em;
+ text-indent: -9999999px;
+}
+.ui-button-text-icon-primary .ui-button-text,
+.ui-button-text-icons .ui-button-text {
+ padding: .4em 1em .4em 2.1em;
+}
+.ui-button-text-icon-secondary .ui-button-text,
+.ui-button-text-icons .ui-button-text {
+ padding: .4em 2.1em .4em 1em;
+}
+.ui-button-text-icons .ui-button-text {
+ padding-left: 2.1em;
+ padding-right: 2.1em;
+}
+/* no icon support for input elements, provide padding by default */
+input.ui-button {
+ padding: .4em 1em;
+}
+
+/* button icon element(s) */
+.ui-button-icon-only .ui-icon,
+.ui-button-text-icon-primary .ui-icon,
+.ui-button-text-icon-secondary .ui-icon,
+.ui-button-text-icons .ui-icon,
+.ui-button-icons-only .ui-icon {
+ position: absolute;
+ top: 50%;
+ margin-top: -8px;
+}
+.ui-button-icon-only .ui-icon {
+ left: 50%;
+ margin-left: -8px;
+}
+.ui-button-text-icon-primary .ui-button-icon-primary,
+.ui-button-text-icons .ui-button-icon-primary,
+.ui-button-icons-only .ui-button-icon-primary {
+ left: .5em;
+}
+.ui-button-text-icon-secondary .ui-button-icon-secondary,
+.ui-button-text-icons .ui-button-icon-secondary,
+.ui-button-icons-only .ui-button-icon-secondary {
+ right: .5em;
+}
+
+/* button sets */
+.ui-buttonset {
+ margin-right: 7px;
+}
+.ui-buttonset .ui-button {
+ margin-left: 0;
+ margin-right: -.3em;
+}
+
+/* workarounds */
+/* reset extra padding in Firefox, see h5bp.com/l */
+input.ui-button::-moz-focus-inner,
+button.ui-button::-moz-focus-inner {
+ border: 0;
+ padding: 0;
+}
+.ui-datepicker {
+ width: 17em;
+ padding: .2em .2em 0;
+ display: none;
+}
+.ui-datepicker .ui-datepicker-header {
+ position: relative;
+ padding: .2em 0;
+}
+.ui-datepicker .ui-datepicker-prev,
+.ui-datepicker .ui-datepicker-next {
+ position: absolute;
+ top: 2px;
+ width: 1.8em;
+ height: 1.8em;
+}
+.ui-datepicker .ui-datepicker-prev-hover,
+.ui-datepicker .ui-datepicker-next-hover {
+ top: 1px;
+}
+.ui-datepicker .ui-datepicker-prev {
+ left: 2px;
+}
+.ui-datepicker .ui-datepicker-next {
+ right: 2px;
+}
+.ui-datepicker .ui-datepicker-prev-hover {
+ left: 1px;
+}
+.ui-datepicker .ui-datepicker-next-hover {
+ right: 1px;
+}
+.ui-datepicker .ui-datepicker-prev span,
+.ui-datepicker .ui-datepicker-next span {
+ display: block;
+ position: absolute;
+ left: 50%;
+ margin-left: -8px;
+ top: 50%;
+ margin-top: -8px;
+}
+.ui-datepicker .ui-datepicker-title {
+ margin: 0 2.3em;
+ line-height: 1.8em;
+ text-align: center;
+}
+.ui-datepicker .ui-datepicker-title select {
+ //font-size: 1em;
+ margin: 1px 0;
+}
+.ui-datepicker select.ui-datepicker-month-year {
+ width: 100%;
+}
+.ui-datepicker select.ui-datepicker-month,
+.ui-datepicker select.ui-datepicker-year {
+ width: 49%;
+}
+.ui-datepicker table {
+ width: 100%;
+ //font-size: .9em;
+ border-collapse: collapse;
+ margin: 0 0 .4em;
+}
+.ui-datepicker th {
+ padding: .7em .3em;
+ text-align: center;
+ font-weight: bold;
+ border: 0;
+}
+.ui-datepicker td {
+ border: 0;
+ padding: 1px;
+}
+.ui-datepicker td span,
+.ui-datepicker td a {
+ display: block;
+ padding: .2em;
+ text-align: right;
+ text-decoration: none;
+}
+.ui-datepicker .ui-datepicker-buttonpane {
+ background-image: none;
+ margin: .7em 0 0 0;
+ padding: 0 .2em;
+ border-left: 0;
+ border-right: 0;
+ border-bottom: 0;
+}
+.ui-datepicker .ui-datepicker-buttonpane button {
+ float: right;
+ margin: .5em .2em .4em;
+ cursor: pointer;
+ padding: .2em .6em .3em .6em;
+ width: auto;
+ overflow: visible;
+}
+.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current {
+ float: left;
+}
+
+/* with multiple calendars */
+.ui-datepicker.ui-datepicker-multi {
+ width: auto;
+}
+.ui-datepicker-multi .ui-datepicker-group {
+ float: left;
+}
+.ui-datepicker-multi .ui-datepicker-group table {
+ width: 95%;
+ margin: 0 auto .4em;
+}
+.ui-datepicker-multi-2 .ui-datepicker-group {
+ width: 50%;
+}
+.ui-datepicker-multi-3 .ui-datepicker-group {
+ width: 33.3%;
+}
+.ui-datepicker-multi-4 .ui-datepicker-group {
+ width: 25%;
+}
+.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header,
+.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header {
+ border-left-width: 0;
+}
+.ui-datepicker-multi .ui-datepicker-buttonpane {
+ clear: left;
+}
+.ui-datepicker-row-break {
+ clear: both;
+ width: 100%;
+ font-size: 0;
+}
+
+/* RTL support */
+.ui-datepicker-rtl {
+ direction: rtl;
+}
+.ui-datepicker-rtl .ui-datepicker-prev {
+ right: 2px;
+ left: auto;
+}
+.ui-datepicker-rtl .ui-datepicker-next {
+ left: 2px;
+ right: auto;
+}
+.ui-datepicker-rtl .ui-datepicker-prev:hover {
+ right: 1px;
+ left: auto;
+}
+.ui-datepicker-rtl .ui-datepicker-next:hover {
+ left: 1px;
+ right: auto;
+}
+.ui-datepicker-rtl .ui-datepicker-buttonpane {
+ clear: right;
+}
+.ui-datepicker-rtl .ui-datepicker-buttonpane button {
+ float: left;
+}
+.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current,
+.ui-datepicker-rtl .ui-datepicker-group {
+ float: right;
+}
+.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header,
+.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header {
+ border-right-width: 0;
+ border-left-width: 1px;
+}
+.ui-dialog {
+ position: absolute;
+ top: 0;
+ left: 0;
+ padding: .2em;
+ outline: 0;
+}
+.ui-dialog .ui-dialog-titlebar {
+ padding: .4em 1em;
+ position: relative;
+}
+.ui-dialog .ui-dialog-title {
+ float: left;
+ margin: .1em 0;
+ white-space: nowrap;
+ width: 90%;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+.ui-dialog .ui-dialog-titlebar-close {
+ position: absolute;
+ right: .3em;
+ top: 50%;
+ width: 21px;
+ margin: -10px 0 0 0;
+ padding: 1px;
+ height: 20px;
+}
+.ui-dialog .ui-dialog-content {
+ position: relative;
+ border: 0;
+ padding: .5em 1em;
+ background: none;
+ overflow: auto;
+}
+.ui-dialog .ui-dialog-buttonpane {
+ text-align: left;
+ border-width: 1px 0 0 0;
+ background-image: none;
+ margin-top: .5em;
+ padding: .3em 1em .5em .4em;
+}
+.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset {
+ float: right;
+}
+.ui-dialog .ui-dialog-buttonpane button {
+ margin: .5em .4em .5em 0;
+ cursor: pointer;
+}
+.ui-dialog .ui-resizable-se {
+ width: 12px;
+ height: 12px;
+ right: -5px;
+ bottom: -5px;
+ background-position: 16px 16px;
+}
+.ui-draggable .ui-dialog-titlebar {
+ cursor: move;
+}
+.ui-menu {
+ list-style: none;
+ padding: 2px;
+ margin: 0;
+ display: block;
+ outline: none;
+}
+.ui-menu .ui-menu {
+ margin-top: -3px;
+ position: absolute;
+}
+.ui-menu .ui-menu-item {
+ margin: 0;
+ padding: 0;
+ width: 100%;
+ /* support: IE10, see #8844 */
+ list-style-image: url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7);
+}
+.ui-menu .ui-menu-divider {
+ margin: 5px -2px 5px -2px;
+ height: 0;
+ font-size: 0;
+ line-height: 0;
+ border-width: 1px 0 0 0;
+}
+.ui-menu .ui-menu-item a {
+ text-decoration: none;
+ display: block;
+ padding: 2px .4em;
+ line-height: 1.5;
+ min-height: 0; /* support: IE7 */
+ font-weight: normal;
+}
+.ui-menu .ui-menu-item a.ui-state-focus,
+.ui-menu .ui-menu-item a.ui-state-active {
+ font-weight: normal;
+ margin: -1px;
+}
+
+.ui-menu .ui-state-disabled {
+ font-weight: normal;
+ margin: .4em 0 .2em;
+ line-height: 1.5;
+}
+.ui-menu .ui-state-disabled a {
+ cursor: default;
+}
+
+/* icon support */
+.ui-menu-icons {
+ position: relative;
+}
+.ui-menu-icons .ui-menu-item a {
+ position: relative;
+ padding-left: 2em;
+}
+
+/* left-aligned */
+.ui-menu .ui-icon {
+ position: absolute;
+ top: .2em;
+ left: .2em;
+}
+
+/* right-aligned */
+.ui-menu .ui-menu-icon {
+ position: static;
+ float: right;
+}
+.ui-progressbar {
+ height: 2em;
+ text-align: left;
+ overflow: hidden;
+}
+.ui-progressbar .ui-progressbar-value {
+ margin: -1px;
+ height: 100%;
+}
+.ui-progressbar .ui-progressbar-overlay {
+ background: url('#{$imagesPath}animated-overlay.gif');
+ height: 100%;
+ filter: alpha(opacity=25);
+ opacity: 0.25;
+}
+.ui-progressbar-indeterminate .ui-progressbar-value {
+ background-image: none;
+}
+.ui-slider {
+ position: relative;
+ text-align: left;
+}
+.ui-slider .ui-slider-handle {
+ position: absolute;
+ z-index: 2;
+ width: 1.2em;
+ height: 1.2em;
+ cursor: default;
+}
+.ui-slider .ui-slider-range {
+ position: absolute;
+ z-index: 1;
+ font-size: .7em;
+ display: block;
+ border: 0;
+ background-position: 0 0;
+}
+
+/* For IE8 - See #6727 */
+.ui-slider.ui-state-disabled .ui-slider-handle,
+.ui-slider.ui-state-disabled .ui-slider-range {
+ filter: inherit;
+}
+
+.ui-slider-horizontal {
+ height: .8em;
+}
+.ui-slider-horizontal .ui-slider-handle {
+ top: -.3em;
+ margin-left: -.6em;
+}
+.ui-slider-horizontal .ui-slider-range {
+ top: 0;
+ height: 100%;
+}
+.ui-slider-horizontal .ui-slider-range-min {
+ left: 0;
+}
+.ui-slider-horizontal .ui-slider-range-max {
+ right: 0;
+}
+
+.ui-slider-vertical {
+ width: .8em;
+ height: 100px;
+}
+.ui-slider-vertical .ui-slider-handle {
+ left: -.3em;
+ margin-left: 0;
+ margin-bottom: -.6em;
+}
+.ui-slider-vertical .ui-slider-range {
+ left: 0;
+ width: 100%;
+}
+.ui-slider-vertical .ui-slider-range-min {
+ bottom: 0;
+}
+.ui-slider-vertical .ui-slider-range-max {
+ top: 0;
+}
+.ui-spinner {
+ position: relative;
+ display: inline-block;
+ overflow: hidden;
+ padding: 0;
+ vertical-align: middle;
+}
+.ui-spinner-input {
+ border: none;
+ background: none;
+ color: inherit;
+ padding: 0;
+ margin: .2em 0;
+ vertical-align: middle;
+ margin-left: .4em;
+ margin-right: 22px;
+}
+.ui-spinner-button {
+ width: 16px;
+ height: 50%;
+ font-size: .5em;
+ padding: 0;
+ margin: 0;
+ text-align: center;
+ position: absolute;
+ cursor: default;
+ display: block;
+ overflow: hidden;
+ right: 0;
+}
+/* more specificity required here to overide default borders */
+.ui-spinner a.ui-spinner-button {
+ border-top: none;
+ border-bottom: none;
+ border-right: none;
+}
+/* vertical centre icon */
+.ui-spinner .ui-icon {
+ position: absolute;
+ margin-top: -8px;
+ top: 50%;
+ left: 0;
+}
+.ui-spinner-up {
+ top: 0;
+}
+.ui-spinner-down {
+ bottom: 0;
+}
+
+/* TR overrides */
+.ui-spinner .ui-icon-triangle-1-s {
+ /* need to fix icons sprite */
+ background-position: -65px -16px;
+}
+.ui-tabs {
+ position: relative;/* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */
+ padding: .2em;
+}
+.ui-tabs .ui-tabs-nav {
+ margin: 0;
+ padding: .2em .2em 0;
+}
+.ui-tabs .ui-tabs-nav li {
+ list-style: none;
+ float: left;
+ position: relative;
+ top: 0;
+ margin: 1px .2em 0 0;
+ border-bottom-width: 0;
+ padding: 0;
+ white-space: nowrap;
+}
+.ui-tabs .ui-tabs-nav li a {
+ float: left;
+ padding: .5em 1em;
+ text-decoration: none;
+}
+.ui-tabs .ui-tabs-nav li.ui-tabs-active {
+ margin-bottom: -1px;
+ padding-bottom: 1px;
+}
+.ui-tabs .ui-tabs-nav li.ui-tabs-active a,
+.ui-tabs .ui-tabs-nav li.ui-state-disabled a,
+.ui-tabs .ui-tabs-nav li.ui-tabs-loading a {
+ cursor: text;
+}
+.ui-tabs .ui-tabs-nav li a, /* first selector in group seems obsolete, but required to overcome bug in Opera applying cursor: text overall if defined elsewhere... */
+.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-active a {
+ cursor: pointer;
+}
+.ui-tabs .ui-tabs-panel {
+ display: block;
+ border-width: 0;
+ padding: 1em 1.4em;
+ background: none;
+}
+.ui-tooltip {
+ padding: 8px;
+ position: absolute;
+ z-index: 9999;
+ max-width: 300px;
+ -webkit-box-shadow: 0 0 5px #aaa;
+ box-shadow: 0 0 5px #aaa;
+}
+body .ui-tooltip {
+ border-width: 2px;
+}
+
+/* Component containers
+----------------------------------*/
+/* let bootstrap handle font-size
+.ui-widget {
+ font-family: Verdana,Arial,sans-serif;
+ font-size: 1.1em;
+}
+.ui-widget .ui-widget {
+ font-size: 1em;
+}
+.ui-widget input,
+.ui-widget select,
+.ui-widget textarea,
+.ui-widget button {
+ font-family: Verdana,Arial,sans-serif;
+ font-size: 1em;
+}*/
+
+
+/* positioning */
+.ui-icon-blank { background-position: 16px 16px; }
+.ui-icon-carat-1-n { background-position: 0 0; }
+.ui-icon-carat-1-ne { background-position: -16px 0; }
+.ui-icon-carat-1-e { background-position: -32px 0; }
+.ui-icon-carat-1-se { background-position: -48px 0; }
+.ui-icon-carat-1-s { background-position: -64px 0; }
+.ui-icon-carat-1-sw { background-position: -80px 0; }
+.ui-icon-carat-1-w { background-position: -96px 0; }
+.ui-icon-carat-1-nw { background-position: -112px 0; }
+.ui-icon-carat-2-n-s { background-position: -128px 0; }
+.ui-icon-carat-2-e-w { background-position: -144px 0; }
+.ui-icon-triangle-1-n { background-position: 0 -16px; }
+.ui-icon-triangle-1-ne { background-position: -16px -16px; }
+.ui-icon-triangle-1-e { background-position: -32px -16px; }
+.ui-icon-triangle-1-se { background-position: -48px -16px; }
+.ui-icon-triangle-1-s { background-position: -64px -16px; }
+.ui-icon-triangle-1-sw { background-position: -80px -16px; }
+.ui-icon-triangle-1-w { background-position: -96px -16px; }
+.ui-icon-triangle-1-nw { background-position: -112px -16px; }
+.ui-icon-triangle-2-n-s { background-position: -128px -16px; }
+.ui-icon-triangle-2-e-w { background-position: -144px -16px; }
+.ui-icon-arrow-1-n { background-position: 0 -32px; }
+.ui-icon-arrow-1-ne { background-position: -16px -32px; }
+.ui-icon-arrow-1-e { background-position: -32px -32px; }
+.ui-icon-arrow-1-se { background-position: -48px -32px; }
+.ui-icon-arrow-1-s { background-position: -64px -32px; }
+.ui-icon-arrow-1-sw { background-position: -80px -32px; }
+.ui-icon-arrow-1-w { background-position: -96px -32px; }
+.ui-icon-arrow-1-nw { background-position: -112px -32px; }
+.ui-icon-arrow-2-n-s { background-position: -128px -32px; }
+.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; }
+.ui-icon-arrow-2-e-w { background-position: -160px -32px; }
+.ui-icon-arrow-2-se-nw { background-position: -176px -32px; }
+.ui-icon-arrowstop-1-n { background-position: -192px -32px; }
+.ui-icon-arrowstop-1-e { background-position: -208px -32px; }
+.ui-icon-arrowstop-1-s { background-position: -224px -32px; }
+.ui-icon-arrowstop-1-w { background-position: -240px -32px; }
+.ui-icon-arrowthick-1-n { background-position: 0 -48px; }
+.ui-icon-arrowthick-1-ne { background-position: -16px -48px; }
+.ui-icon-arrowthick-1-e { background-position: -32px -48px; }
+.ui-icon-arrowthick-1-se { background-position: -48px -48px; }
+.ui-icon-arrowthick-1-s { background-position: -64px -48px; }
+.ui-icon-arrowthick-1-sw { background-position: -80px -48px; }
+.ui-icon-arrowthick-1-w { background-position: -96px -48px; }
+.ui-icon-arrowthick-1-nw { background-position: -112px -48px; }
+.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; }
+.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; }
+.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; }
+.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; }
+.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; }
+.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; }
+.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; }
+.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; }
+.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; }
+.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; }
+.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; }
+.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; }
+.ui-icon-arrowreturn-1-w { background-position: -64px -64px; }
+.ui-icon-arrowreturn-1-n { background-position: -80px -64px; }
+.ui-icon-arrowreturn-1-e { background-position: -96px -64px; }
+.ui-icon-arrowreturn-1-s { background-position: -112px -64px; }
+.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; }
+.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; }
+.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; }
+.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; }
+.ui-icon-arrow-4 { background-position: 0 -80px; }
+.ui-icon-arrow-4-diag { background-position: -16px -80px; }
+.ui-icon-extlink { background-position: -32px -80px; }
+.ui-icon-newwin { background-position: -48px -80px; }
+.ui-icon-refresh { background-position: -64px -80px; }
+.ui-icon-shuffle { background-position: -80px -80px; }
+.ui-icon-transfer-e-w { background-position: -96px -80px; }
+.ui-icon-transferthick-e-w { background-position: -112px -80px; }
+.ui-icon-folder-collapsed { background-position: 0 -96px; }
+.ui-icon-folder-open { background-position: -16px -96px; }
+.ui-icon-document { background-position: -32px -96px; }
+.ui-icon-document-b { background-position: -48px -96px; }
+.ui-icon-note { background-position: -64px -96px; }
+.ui-icon-mail-closed { background-position: -80px -96px; }
+.ui-icon-mail-open { background-position: -96px -96px; }
+.ui-icon-suitcase { background-position: -112px -96px; }
+.ui-icon-comment { background-position: -128px -96px; }
+.ui-icon-person { background-position: -144px -96px; }
+.ui-icon-print { background-position: -160px -96px; }
+.ui-icon-trash { background-position: -176px -96px; }
+.ui-icon-locked { background-position: -192px -96px; }
+.ui-icon-unlocked { background-position: -208px -96px; }
+.ui-icon-bookmark { background-position: -224px -96px; }
+.ui-icon-tag { background-position: -240px -96px; }
+.ui-icon-home { background-position: 0 -112px; }
+.ui-icon-flag { background-position: -16px -112px; }
+.ui-icon-calendar { background-position: -32px -112px; }
+.ui-icon-cart { background-position: -48px -112px; }
+.ui-icon-pencil { background-position: -64px -112px; }
+.ui-icon-clock { background-position: -80px -112px; }
+.ui-icon-disk { background-position: -96px -112px; }
+.ui-icon-calculator { background-position: -112px -112px; }
+.ui-icon-zoomin { background-position: -128px -112px; }
+.ui-icon-zoomout { background-position: -144px -112px; }
+.ui-icon-search { background-position: -160px -112px; }
+.ui-icon-wrench { background-position: -176px -112px; }
+.ui-icon-gear { background-position: -192px -112px; }
+.ui-icon-heart { background-position: -208px -112px; }
+.ui-icon-star { background-position: -224px -112px; }
+.ui-icon-link { background-position: -240px -112px; }
+.ui-icon-cancel { background-position: 0 -128px; }
+.ui-icon-plus { background-position: -16px -128px; }
+.ui-icon-plusthick { background-position: -32px -128px; }
+.ui-icon-minus { background-position: -48px -128px; }
+.ui-icon-minusthick { background-position: -64px -128px; }
+.ui-icon-close { background-position: -80px -128px; }
+.ui-icon-closethick { background-position: -96px -128px; }
+.ui-icon-key { background-position: -112px -128px; }
+.ui-icon-lightbulb { background-position: -128px -128px; }
+.ui-icon-scissors { background-position: -144px -128px; }
+.ui-icon-clipboard { background-position: -160px -128px; }
+.ui-icon-copy { background-position: -176px -128px; }
+.ui-icon-contact { background-position: -192px -128px; }
+.ui-icon-image { background-position: -208px -128px; }
+.ui-icon-video { background-position: -224px -128px; }
+.ui-icon-script { background-position: -240px -128px; }
+.ui-icon-alert { background-position: 0 -144px; }
+.ui-icon-info { background-position: -16px -144px; }
+.ui-icon-notice { background-position: -32px -144px; }
+.ui-icon-help { background-position: -48px -144px; }
+.ui-icon-check { background-position: -64px -144px; }
+.ui-icon-bullet { background-position: -80px -144px; }
+.ui-icon-radio-on { background-position: -96px -144px; }
+.ui-icon-radio-off { background-position: -112px -144px; }
+.ui-icon-pin-w { background-position: -128px -144px; }
+.ui-icon-pin-s { background-position: -144px -144px; }
+.ui-icon-play { background-position: 0 -160px; }
+.ui-icon-pause { background-position: -16px -160px; }
+.ui-icon-seek-next { background-position: -32px -160px; }
+.ui-icon-seek-prev { background-position: -48px -160px; }
+.ui-icon-seek-end { background-position: -64px -160px; }
+.ui-icon-seek-start { background-position: -80px -160px; }
+/* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */
+.ui-icon-seek-first { background-position: -80px -160px; }
+.ui-icon-stop { background-position: -96px -160px; }
+.ui-icon-eject { background-position: -112px -160px; }
+.ui-icon-volume-off { background-position: -128px -160px; }
+.ui-icon-volume-on { background-position: -144px -160px; }
+.ui-icon-power { background-position: 0 -176px; }
+.ui-icon-signal-diag { background-position: -16px -176px; }
+.ui-icon-signal { background-position: -32px -176px; }
+.ui-icon-battery-0 { background-position: -48px -176px; }
+.ui-icon-battery-1 { background-position: -64px -176px; }
+.ui-icon-battery-2 { background-position: -80px -176px; }
+.ui-icon-battery-3 { background-position: -96px -176px; }
+.ui-icon-circle-plus { background-position: 0 -192px; }
+.ui-icon-circle-minus { background-position: -16px -192px; }
+.ui-icon-circle-close { background-position: -32px -192px; }
+.ui-icon-circle-triangle-e { background-position: -48px -192px; }
+.ui-icon-circle-triangle-s { background-position: -64px -192px; }
+.ui-icon-circle-triangle-w { background-position: -80px -192px; }
+.ui-icon-circle-triangle-n { background-position: -96px -192px; }
+.ui-icon-circle-arrow-e { background-position: -112px -192px; }
+.ui-icon-circle-arrow-s { background-position: -128px -192px; }
+.ui-icon-circle-arrow-w { background-position: -144px -192px; }
+.ui-icon-circle-arrow-n { background-position: -160px -192px; }
+.ui-icon-circle-zoomin { background-position: -176px -192px; }
+.ui-icon-circle-zoomout { background-position: -192px -192px; }
+.ui-icon-circle-check { background-position: -208px -192px; }
+.ui-icon-circlesmall-plus { background-position: 0 -208px; }
+.ui-icon-circlesmall-minus { background-position: -16px -208px; }
+.ui-icon-circlesmall-close { background-position: -32px -208px; }
+.ui-icon-squaresmall-plus { background-position: -48px -208px; }
+.ui-icon-squaresmall-minus { background-position: -64px -208px; }
+.ui-icon-squaresmall-close { background-position: -80px -208px; }
+.ui-icon-grip-dotted-vertical { background-position: 0 -224px; }
+.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; }
+.ui-icon-grip-solid-vertical { background-position: -32px -224px; }
+.ui-icon-grip-solid-horizontal { background-position: -48px -224px; }
+.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; }
+.ui-icon-grip-diagonal-se { background-position: -80px -224px; }
+
+
+/* Misc visuals
+----------------------------------*/
+
+/* Corner radius */
+.ui-corner-all,
+.ui-corner-top,
+.ui-corner-left,
+.ui-corner-tl {
+ border-top-left-radius: 2px;
+}
+.ui-corner-all,
+.ui-corner-top,
+.ui-corner-right,
+.ui-corner-tr {
+ border-top-right-radius: 2px;
+}
+.ui-corner-all,
+.ui-corner-bottom,
+.ui-corner-left,
+.ui-corner-bl {
+ border-bottom-left-radius: 2px;
+}
+.ui-corner-all,
+.ui-corner-bottom,
+.ui-corner-right,
+.ui-corner-br {
+ border-bottom-right-radius: 2px;
+}
diff --git a/resources/www/lightbox/styles/jquery-ui/_theme.scss b/resources/www/lightbox/styles/jquery-ui/_theme.scss
new file mode 100644
index 0000000000..46cc5ea127
--- /dev/null
+++ b/resources/www/lightbox/styles/jquery-ui/_theme.scss
@@ -0,0 +1,194 @@
+$imagesPath: '/assets/vendors/jquery-ui/images/dark-hive/';
+$uiTextContentColor: #ffffff !default;
+$uiTextTitleColor: #333333 !default;
+$uiLinkColor: #ffffff !default;
+$uiLinkFocusColor: #c77405 !default;
+$uiLinkActiveColor: #eb8f00 !default;
+$defaultBorderColor: #303030;
+$defaultBorderRadius: 2px;
+
+$darkBackgroundColor: #292929;
+$darkBorderColor: #303030;
+$darkTextColor: #A6A6A6;
+
+$mediumBackgroundColor: #3b3b3b;
+$mediumBackgroundHoverColor: #666666;
+$mediumBorderColor: #303030; //#404040; //
+$mediumBorderHighlightColor: #666666;
+$mediumTextColor: #a1a1a1;
+$mediumTextHoverColor: #EAEAEA;
+$mediumTextActiveColor: #EAEAEA;
+
+@import './shared';
+.ui-widget-content {
+ //border: 1px solid #555555;
+ background-color: #212121; // url('#{$imagesPath}ui-bg_loop_25_000000_21x21.png') 50% 50% repeat;
+ color: $uiTextContentColor;
+}
+.ui-widget-content a {
+ color: $uiLinkColor;
+}
+.ui-widget-header {
+ color: $uiLinkColor;
+ font-weight: bold;
+}
+.ui-widget-header a {
+ color: $uiLinkColor;
+}
+
+/* Interaction states
+----------------------------------*/
+.ui-state-default,
+.ui-widget-content .ui-state-default,
+.ui-widget-header .ui-state-default {
+ border: 1px solid $darkBackgroundColor;
+ background: $darkBackgroundColor;
+ font-weight: normal;
+ color: #eeeeee;
+}
+.ui-state-default a,
+.ui-state-default a:link,
+.ui-state-default a:visited {
+ color: #eeeeee;
+ text-decoration: none;
+}
+.ui-state-hover,
+.ui-widget-content .ui-state-hover,
+.ui-widget-header .ui-state-hover,
+.ui-state-focus,
+.ui-widget-content .ui-state-focus,
+.ui-widget-header .ui-state-focus {
+ border: 1px solid $mediumBackgroundColor;
+ background: $mediumBackgroundColor;
+ font-weight: normal;
+ color: #ffffff;
+}
+.ui-state-hover a,
+.ui-state-hover a:hover,
+.ui-state-hover a:link,
+.ui-state-hover a:visited {
+ color: #ffffff;
+ text-decoration: none;
+}
+.ui-state-active,
+.ui-widget-content .ui-state-active,
+.ui-widget-header .ui-state-active {
+ border: 1px solid $mediumBackgroundColor;
+ background: $mediumBackgroundColor;
+ font-weight: normal;
+ color: #ffffff;
+}
+.ui-state-active a,
+.ui-state-active a:link,
+.ui-state-active a:visited {
+ color: #ffffff;
+ text-decoration: none;
+}
+
+/* Interaction Cues
+----------------------------------*/
+.ui-state-highlight,
+.ui-widget-content .ui-state-highlight,
+.ui-widget-header .ui-state-highlight {
+ border: 1px solid #cccccc;
+ background: #eeeeee url('#{$imagesPath}ui-bg_highlight-soft_80_eeeeee_1x100.png') 50% top repeat-x;
+ color: #2e7db2;
+}
+.ui-state-highlight a,
+.ui-widget-content .ui-state-highlight a,
+.ui-widget-header .ui-state-highlight a {
+ color: #2e7db2;
+}
+.ui-state-error,
+.ui-widget-content .ui-state-error,
+.ui-widget-header .ui-state-error {
+ border: 1px solid #ffb73d;
+ background: #ffc73d url('#{$imagesPath}ui-bg_glass_40_ffc73d_1x400.png') 50% 50% repeat-x;
+ color: #111111;
+}
+.ui-state-error a,
+.ui-widget-content .ui-state-error a,
+.ui-widget-header .ui-state-error a {
+ color: #111111;
+}
+.ui-state-error-text,
+.ui-widget-content .ui-state-error-text,
+.ui-widget-header .ui-state-error-text {
+ color: #111111;
+}
+.ui-priority-primary,
+.ui-widget-content .ui-priority-primary,
+.ui-widget-header .ui-priority-primary {
+ font-weight: bold;
+}
+.ui-priority-secondary,
+.ui-widget-content .ui-priority-secondary,
+.ui-widget-header .ui-priority-secondary {
+ opacity: .7;
+ filter:Alpha(Opacity=70);
+ font-weight: normal;
+}
+.ui-state-disabled,
+.ui-widget-content .ui-state-disabled,
+.ui-widget-header .ui-state-disabled {
+ opacity: .35;
+ filter:Alpha(Opacity=35);
+ background-image: none;
+}
+.ui-state-disabled .ui-icon {
+ filter:Alpha(Opacity=35); /* For IE8 - See #6059 */
+}
+
+/* Icons
+----------------------------------*/
+
+/* states and images */
+.ui-icon {
+ width: 16px;
+ height: 16px;
+}
+.ui-icon,
+.ui-widget-content .ui-icon {
+ background-image: url('#{$imagesPath}ui-icons_cccccc_256x240.png');
+}
+.ui-widget-header .ui-icon {
+ background-image: url('#{$imagesPath}ui-icons_ffffff_256x240.png');
+}
+.ui-state-default .ui-icon {
+ background-image: url('#{$imagesPath}ui-icons_cccccc_256x240.png');
+}
+.ui-state-hover .ui-icon,
+.ui-state-focus .ui-icon {
+ background-image: url('#{$imagesPath}ui-icons_ffffff_256x240.png');
+}
+.ui-state-active .ui-icon {
+ background-image: url('#{$imagesPath}ui-icons_222222_256x240.png');
+}
+.ui-state-highlight .ui-icon {
+ background-image: url('#{$imagesPath}ui-icons_4b8e0b_256x240.png');
+}
+.ui-state-error .ui-icon,
+.ui-state-error-text .ui-icon {
+ background-image: url('#{$imagesPath}ui-icons_a83300_256x240.png');
+}
+
+/* Overlays */
+.ui-widget-overlay {
+ background: #5c5c5c url('#{$imagesPath}ui-bg_flat_50_5c5c5c_40x100.png') 50% 50% repeat-x;
+ opacity: .8;
+ filter: Alpha(Opacity=80);
+}
+.ui-widget-shadow {
+ margin: -7px 0 0 -7px;
+ padding: 7px;
+ background: #cccccc url('#{$imagesPath}ui-bg_flat_30_cccccc_40x100.png') 50% 50% repeat-x;
+ opacity: .6;
+ filter: Alpha(Opacity=60);
+ border-radius: 8px;
+}
+.ui-tabs {
+ .ui-tabs-panel {
+ background-color: $mediumBackgroundColor;
+ }
+}
+
diff --git a/resources/www/lightbox/styles/main.scss b/resources/www/lightbox/styles/main.scss
index cd308e35e2..c7e460aad1 100644
--- a/resources/www/lightbox/styles/main.scss
+++ b/resources/www/lightbox/styles/main.scss
@@ -1,6 +1,8 @@
@import '../../_shared/styles/variables';
$lightboxPath: '../images/';
+$imagesPath: '/assets/vendors/jquery-ui/images/dark-hive/';
@import '../../vendors/jquery-contextmenu/styles/jquery.contextmenu';
+@import 'jquery-ui/theme';
$mainMenuBackgroundColor: #c7c7c7; //BFBFBF;
$mainMenuBottomBorder: 1px solid #c7c7c7;
@@ -58,39 +60,41 @@ img {
font-weight: bold;
}
-.record_display_box table {
- vertical-align: middle;
- table-layout: fixed;
-}
-
.record_display_box {
width: 100%;
height: 100%;
display: block;
-}
-
-.record_display_box .record {
- position: relative;
- max-width: none;
- max-height: none;
-}
-
-.record_display_box .header .title {
- overflow: hidden;
- line-height: 20px;
- height: 20px;
-}
-
-.record_display_box .header {
- color: #BFBFBF;
- height: 30px;
- bottom: auto;
- overflow: hidden;
-}
-
-.record_display_box .lightbox_container {
- top: 30px;
- overflow: hidden;
+ table {
+ vertical-align: middle;
+ table-layout: fixed;
+ }
+ .title {
+ margin: 0 10px;
+ }
+ .record {
+ position: relative;
+ max-width: none;
+ max-height: none;
+ }
+ .header {
+ color: #BFBFBF;
+ height: 30px;
+ bottom: auto;
+ overflow: hidden;
+ .title {
+ overflow: hidden;
+ line-height: 20px;
+ height: 20px;
+ }
+ }
+ .lightbox_container {
+ top: 30px;
+ overflow: hidden;
+ }
+ .display_id {
+ top: 5px;
+ margin: 0 0 0 5px;
+ }
}
#sc_wrapper {
@@ -119,15 +123,35 @@ img {
border: 1px solid #212121;
text-align: left;
padding: 5px 8px;
-}
-
-.basket_element.selected {
- background-color: #212121;
-}
-
-.basket_element .display_id {
- top: 4px;
- left: 8px;
+ &.selected {
+ background-color: #212121;
+ }
+ .display_id {
+ top: 4px;
+ left: 8px;
+ }
+ .agreement {
+ position: absolute;
+ top: 4px;
+ right: 8px;
+ z-index: 99;
+ }
+ .image {
+ position: relative;
+ z-index: 90;
+ }
+ .previewTips {
+ background-image: url('#{$iconsPath}zoom.gif');
+ background-position: center center;
+ background-repeat: no-repeat;
+ cursor: help;
+ position: absolute;
+ bottom: 4px;
+ right: 8px;
+ height: 18px;
+ width: 18px;
+ z-index: 99;
+ }
}
#report .display_id {
@@ -152,13 +176,6 @@ img {
position: absolute;
}
-.basket_element .agreement {
- position: absolute;
- top: 4px;
- right: 8px;
- z-index: 99;
-}
-
.not_decided {
opacity: 0.30;
filter: alpha(opacity=30);
@@ -168,33 +185,6 @@ img {
display: none;
}
-.basket_element .image {
- position: relative;
- z-index: 90;
-}
-
-.basket_element .previewTips {
- background-image: url('#{$iconsPath}zoom.gif');
- background-position: center center;
- background-repeat: no-repeat;
- cursor: help;
- position: absolute;
- bottom: 4px;
- right: 8px;
- height: 18px;
- width: 18px;
- z-index: 99;
-}
-
-.record_display_box .title {
- margin: 0 10px;
-}
-
-.record_display_box .display_id {
- top: 5px;
- margin: 0 0 0 5px;
-}
-
#right_scroller {
width: 30px;
right: -10px;
@@ -235,17 +225,17 @@ img {
top: auto;
}
-#basket_infos table {
- width: 100%;
- margin: 5px 0;
-}
-
-#basket_infos table .title {
- width: 100%;
-}
-
-#basket_infos table .report_wrapper {
- text-align: right;
+#basket_infos {
+ table {
+ width: 100%;
+ margin: 5px 0;
+ .title {
+ width: 100%;
+ }
+ .report_wrapper {
+ text-align: right;
+ }
+ }
}
#right_column {
@@ -431,11 +421,10 @@ table th i {
.basket_wrapper {
margin: 10px;
-}
-
-.basket_wrapper table {
- margin: 10px;
- width: 580px;
+ table {
+ margin: 10px;
+ width: 580px;
+ }
}
.basket_description table {
@@ -456,10 +445,9 @@ table th i {
.thumb_wrapper {
text-align: center;
-}
-
-.thumb_wrapper .thumb {
- position: relative;
+ .thumb {
+ position: relative;
+ }
}
#sc_wrapper .thumb_wrapper {
@@ -467,50 +455,47 @@ table th i {
top: 28px;
}
-.record_display_box .notes_overlay {
- background-color: #BFBFBF;
- position: absolute;
- width: 100%;
- height: 100%;
- opacity: 0.70;
- filter: alpha(opacity=70);
- z-index: 100;
-}
-
-.record_display_box .notes_wrapper {
- position: absolute;
- top: -100%;
- height: 100%;
- width: 100%;
- left: 0px;
-}
-
-.record_display_box .note_wrapper {
- background-color: white;
- position: relative;
- max-width: 75%;
- margin: 20px 0;
- padding: 10px;
- color: #212121;
-}
-
-.record_display_box .note_wrapper.my_note {
- background-color: #C9FFF7;
-}
-
-.record_display_box .notes h2 {
- margin: 10px 0;
-}
-
-.record_display_box .notes {
- position: relative;
- margin: 0 auto;
- width: 80%;
- height: 90%;
- max-width: 450px;
- z-index: 110;
- overflow-y: auto;
- overflow-x: hidden;
+.record_display_box {
+ .notes_overlay {
+ background-color: #BFBFBF;
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ opacity: 0.70;
+ filter: alpha(opacity=70);
+ z-index: 100;
+ }
+ .notes_wrapper {
+ position: absolute;
+ top: -100%;
+ height: 100%;
+ width: 100%;
+ left: 0px;
+ }
+ .note_wrapper {
+ background-color: white;
+ position: relative;
+ max-width: 75%;
+ margin: 20px 0;
+ padding: 10px;
+ color: #212121;
+ }
+ .note_wrapper.my_note {
+ background-color: #C9FFF7;
+ }
+ .notes h2 {
+ margin: 10px 0;
+ }
+ .notes {
+ position: relative;
+ margin: 0 auto;
+ width: 80%;
+ height: 90%;
+ max-width: 450px;
+ z-index: 110;
+ overflow-y: auto;
+ overflow-x: hidden;
+ }
}
.ui-state-default.note_closer, .ui-state-default.note_saver {
@@ -577,15 +562,13 @@ hr {
padding: 0.15em;
}
-.ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default {
- color: #BFBFBF;
- background-image: none;
- border: none;
- background-color: transparent;
+.ui-dialog {
+ .ui-dialog-titlebar {
+ padding: 1em 1em;
+ }
+}
+.videoTips {
+ width: 100%;
+ height: 100%;
}
-.ui-widget-header {
- background-color: #1F1E1B;
- background-image: none;
- border: none;
-}
diff --git a/resources/www/prod/js/jquery.main-prod.js b/resources/www/prod/js/jquery.main-prod.js
index a32a0d33c3..31a458077d 100644
--- a/resources/www/prod/js/jquery.main-prod.js
+++ b/resources/www/prod/js/jquery.main-prod.js
@@ -566,7 +566,7 @@ function initAnswerForm() {
$('#answers').append('');
$('#tool_results').empty().append(datas.infos);
- $('#tool_navigate').empty().append(datas.navigation);
+ $('#tool_navigate').empty().append(datas.navigationTpl);
$.each(p4.Results.Selection.get(), function (i, el) {
$('#IMGT_' + el).addClass('selected');
@@ -575,6 +575,7 @@ function initAnswerForm() {
p4.tot = datas.total_answers;
p4.tot_options = datas.form;
p4.tot_query = datas.query;
+ p4.navigation = datas.navigation;
if (datas.next_page) {
$("#NEXT_PAGE, #answersNext").bind('click', function () {
diff --git a/resources/www/prod/js/jquery.p4.preview.js b/resources/www/prod/js/jquery.p4.preview.js
index badd5549a0..ba1b1909c0 100644
--- a/resources/www/prod/js/jquery.p4.preview.js
+++ b/resources/www/prod/js/jquery.p4.preview.js
@@ -27,6 +27,13 @@ function getNewVideoToken(lst, obj) {
});
}
+/**
+ *
+ * @param env
+ * @param pos - relative position in current page
+ * @param contId
+ * @param reload
+ */
function openPreview(env, pos, contId, reload) {
if (contId == undefined)
@@ -73,6 +80,12 @@ function openPreview(env, pos, contId, reload) {
var options_serial = p4.tot_options;
var query = p4.tot_query;
+ var navigation = p4.navigation;
+
+ // keep relative position for answer train:
+ var relativePos = pos;
+ // update real absolute position with pagination:
+ var absolutePos = parseInt(navigation.perPage,10) * (parseInt(navigation.page, 10) - 1) + parseInt(pos,10);
prevAjax = $.ajax({
type: "POST",
@@ -80,7 +93,7 @@ function openPreview(env, pos, contId, reload) {
dataType: 'json',
data: {
env: env,
- pos: pos,
+ pos: absolutePos,
cont: contId,
roll: roll,
options_serial: options_serial,
@@ -141,7 +154,7 @@ function openPreview(env, pos, contId, reload) {
p4.preview.current.width = parseInt($('#PREVIEWIMGCONT input[name=width]').val());
p4.preview.current.height = parseInt($('#PREVIEWIMGCONT input[name=height]').val());
p4.preview.current.tot = data.tot;
- p4.preview.current.pos = data.pos;
+ p4.preview.current.pos = relativePos;
if ($('#PREVIEWBOX img.record.zoomable').length > 0) {
$('#PREVIEWBOX img.record.zoomable').draggable();
@@ -162,9 +175,9 @@ function openPreview(env, pos, contId, reload) {
else {
if (!justOpen) {
$('#PREVIEWCURRENT li.selected').removeClass('selected');
- $('#PREVIEWCURRENTCONT li.current' + pos).addClass('selected');
+ $('#PREVIEWCURRENTCONT li.current' + absolutePos).addClass('selected');
}
- if (justOpen || ($('#PREVIEWCURRENTCONT li.current' + pos).length === 0) || ($('#PREVIEWCURRENTCONT li:last')[0] == $('#PREVIEWCURRENTCONT li.selected')[0]) || ($('#PREVIEWCURRENTCONT li:first')[0] == $('#PREVIEWCURRENTCONT li.selected')[0])) {
+ if (justOpen || ($('#PREVIEWCURRENTCONT li.current' + absolutePos).length === 0) || ($('#PREVIEWCURRENTCONT li:last')[0] == $('#PREVIEWCURRENTCONT li.selected')[0]) || ($('#PREVIEWCURRENTCONT li:first')[0] == $('#PREVIEWCURRENTCONT li.selected')[0])) {
getAnswerTrain(pos, data.tools, query, options_serial);
}
@@ -241,13 +254,18 @@ function zoomPreview(bool) {
}
function getAnswerTrain(pos, tools, query, options_serial) {
+ // keep relative position for answer train:
+ var relativePos = pos;
+ // update real absolute position with pagination:
+ var absolutePos = parseInt(p4.navigation.perPage,10) * (parseInt(p4.navigation.page, 10) - 1) + parseInt(pos,10);
+
$('#PREVIEWCURRENTCONT').fadeOut('fast');
$.ajax({
type: "POST",
url: "/prod/query/answer-train/",
dataType: 'json',
data: {
- pos: pos,
+ pos: absolutePos,
options_serial: options_serial,
query: query
},
@@ -334,7 +352,7 @@ function getNext() {
else {
if (p4.preview.mode == 'RESULT') {
posAsk = parseInt(p4.preview.current.pos) + 1;
- posAsk = (posAsk > parseInt(p4.tot) || isNaN(posAsk)) ? 0 : posAsk;
+ posAsk = (posAsk >= parseInt(p4.tot) || isNaN(posAsk)) ? 0 : posAsk;
openPreview('RESULT', posAsk, '', false);
}
else {
@@ -398,7 +416,11 @@ function setCurrent(current) {
$(el).removeClass('openPreview');
$(el).bind('click', function () {
viewCurrent($(this).parent());
- openPreview(jsopt[0], jsopt[1], jsopt[2],false);
+ // convert abssolute to relative position
+ var absolutePos = jsopt[1];
+ var relativePos = parseInt(absolutePos, 10) - parseInt(p4.navigation.perPage, 10) * (parseInt(p4.navigation.page, 10) - 1);
+ // keep relative position for answer train:
+ openPreview(jsopt[0], relativePos, jsopt[2],false);
});
});
}
diff --git a/resources/www/prod/skins/skin-shared.scss b/resources/www/prod/skins/skin-shared.scss
index 6360c94710..dd486bbe8d 100644
--- a/resources/www/prod/skins/skin-shared.scss
+++ b/resources/www/prod/skins/skin-shared.scss
@@ -518,6 +518,10 @@ h4 {
}
}
}
+.videoTips {
+ width: 100%;
+ height: 100%;
+}
@import 'ui-components/jquery-ui';
@import 'ui-components/context-menu';
diff --git a/resources/www/prod/skins/ui-components/_modal-edit.scss b/resources/www/prod/skins/ui-components/_modal-edit.scss
index 8670156d21..50bf2d4785 100644
--- a/resources/www/prod/skins/ui-components/_modal-edit.scss
+++ b/resources/www/prod/skins/ui-components/_modal-edit.scss
@@ -260,9 +260,12 @@
width: 100%;
display: none;
}
-
+.edit-zone-title {
+ height: 45px;
+ bottom: auto;
+}
#EDIT_EDIT {
- top: 35px;
+ top: 45px;
bottom: 60px;
overflow-x: hidden;
overflow-y: auto;
diff --git a/resources/www/prod/styles/main.scss b/resources/www/prod/styles/main.scss
index 666b3ffd16..74ad3a7830 100644
--- a/resources/www/prod/styles/main.scss
+++ b/resources/www/prod/styles/main.scss
@@ -23,13 +23,7 @@
overflow-y: auto;
}
-.caption-tooltip-container {
- max-width: 500px;
-
-}
.caption-tooltip-container .popover-inner .popover-content {
- max-width: 500px;
- max-height: 500px;
overflow: auto;
overflow-x: hidden;
}
diff --git a/templates-profiler/cache.html.twig b/templates-profiler/cache.html.twig
new file mode 100644
index 0000000000..434505015e
--- /dev/null
+++ b/templates-profiler/cache.html.twig
@@ -0,0 +1,192 @@
+{% extends '@WebProfiler/Profiler/layout.html.twig' %}
+
+{% block toolbar %}
+ {% set text %}
+
+ Cache hits
+ {{ collector.summary.hits }}
+
+
+ Cache misses
+ {{ collector.summary.misses }}
+
+ {% endset %}
+ {% set icon %}
+
+ {{ '%0.2f'|format(collector.summary.hitRatio * 100) }} % in {{ collector.totalTime }} ms
+ {% endset %}
+ {% include '@WebProfiler/Profiler/toolbar_item.html.twig' with { 'link': profiler_url } %}
+{% endblock %}
+
+{% block menu %}
+
+ Cache
+
+ {{ collector.summary.hits }} / {{ collector.summary.calls }}
+ {{ '%0.0f'|format(collector.totalTime) }} ms
+
+
+{% endblock %}
+
+{% block panel %}
+ Cache: {{ collector.summary.cacheType }}
+
+{% if collector.summary.namespace %}
+ Namespace: {{ collector.summary.namespace }}
+{% endif %}
+
+ Server statistics
+
+
+
+
+ | Server stat |
+ Initial value |
+ Final value |
+ Delta |
+
+
+
+ | Hits |
+ {{ collector.initialProfile.hits }} |
+ {{ collector.currentProfile.hits }} |
+ {{ collector.currentProfile.hits - collector.initialProfile.hits }} |
+
+
+ | Misses |
+ {{ collector.initialProfile.misses }} |
+ {{ collector.currentProfile.misses }} |
+ {{ collector.currentProfile.misses - collector.initialProfile.misses }} |
+
+
+ | Calls |
+ {% set initialCallCount = collector.initialProfile.misses + collector.initialProfile.hits %}
+ {% set finalCallCount = collector.currentProfile.misses + collector.currentProfile.hits %}
+ {{ initialCallCount }} |
+ {{ finalCallCount}} |
+ {{ finalCallCount - initialCallCount }} |
+
+
+ | Uptime |
+ {{ collector.initialProfile.uptime }} |
+ {{ collector.currentProfile.uptime }} |
+ {{ collector.summary.uptimeDelta }} |
+
+
+ | Memory usage |
+ {{ collector.initialProfile.memUsage }} |
+ {{ collector.currentProfile.memUsage }} |
+ {{ collector.summary.memUsageDelta }} |
+
+
+ | Memory available |
+ {{ collector.initialProfile.memAvailable }} |
+ {{ collector.currentProfile.memAvailable }} |
+ {{ collector.summary.memAvailableDelta }} |
+
+
+
+ HIT/MISS summary
+
+
+
+
+ | Call stat |
+ Value |
+
+
+
+
+ | Hits |
+ {{ collector.callSummary['hits'] }} |
+
+
+ | Misses |
+ {{ collector.callSummary['misses'] }} |
+
+
+ | Total |
+ {{ collector.callSummary['calls'] }} |
+
+
+
+
+ Calls by type summary
+
+
+
+
+ | Operation |
+ Count |
+
+
+
+ {% for callType, count in collector.callSummary['calls_by_type'] %}
+
+ | {{ callType }} |
+ {{ count }} |
+
+ {% endfor %}
+
+
+
+ Calls by key summary
+
+
+
+
+ | Key |
+ Hits |
+ Misses |
+ Reads |
+ Writes |
+ Total |
+
+
+
+ {% for callKey, stats in collector.callSummary['calls_by_key'] %}
+
+ | {{ callKey }} |
+ {{ stats['hits'] }} |
+ {{ stats['misses'] }} |
+ {{ stats['reads'] }} |
+ {{ stats['writes'] }} |
+ {{ stats['total'] }} |
+
+ {% endfor %}
+
+
+
+ Call list
+
+
+
+
+ | # |
+ Call type |
+ Key |
+ Hit / Miss |
+ Count |
+
+
+
+ {% set callCount = 1 %}
+ {% for call in collector.calls %}
+
+ | {{ callCount }} |
+
+ {% if call['type'] != 'fetch' %}
+ {{ call['type'] }}
+ {% else %}
+ {{ call['type'] }}
+ {% endif %}
+ |
+ {{ call['key'] }} |
+ {% if call['hit'] %}HIT{% else %}MISS{% endif %} |
+ {{ call['count'] }} |
+
+ {% set callCount = callCount + call['count'] %}
+ {% endfor %}
+
+
+{% endblock %}
diff --git a/templates/mobile/common/thumbnail.html.twig b/templates/mobile/common/thumbnail.html.twig
index bc71a69510..8a220c40e5 100644
--- a/templates/mobile/common/thumbnail.html.twig
+++ b/templates/mobile/common/thumbnail.html.twig
@@ -33,20 +33,20 @@
{% if record_type == 'VIDEO_MP4' or record_type == 'VIDEO_FLV' %}
{% elseif record_type == 'FLEXPAPER' %}
{% elseif record_type == 'AUDIO_MP3' %}
{% else %}
+ src="{{thumbnail.get_url()|trim}}" ondragstart="return false;">
{% endif %}
@@ -58,18 +58,18 @@
{% set record_type = thumbnail.get_type() %}
{% if record_type == 'VIDEO_MP4' or record_type == 'VIDEO_FLV' %}
{% elseif record_type == 'FLEXPAPER' %}
{% elseif record_type == 'AUDIO_MP3' %}
{% else %}
+ src="{{thumbnail.get_url()|trim}}" ondragstart="return false;" />
{% endif %}
{% endmacro %}
diff --git a/templates/web/admin/collection/suggested_value.html.twig b/templates/web/admin/collection/suggested_value.html.twig
index 509d4ca08f..a7ba88bbc5 100644
--- a/templates/web/admin/collection/suggested_value.html.twig
+++ b/templates/web/admin/collection/suggested_value.html.twig
@@ -582,9 +582,9 @@ function verifAndactiv()
o = $("#valajout");
if ( o.length > 0 ) {
if(o.val().length>0) {
- activer_bout('bout_add',false);
- } else {
activer_bout('bout_add',true);
+ } else {
+ activer_bout('bout_add',false);
}
}
}
diff --git a/templates/web/admin/dashboard.html.twig b/templates/web/admin/dashboard.html.twig
index c626b6a7fd..2242d18c17 100644
--- a/templates/web/admin/dashboard.html.twig
+++ b/templates/web/admin/dashboard.html.twig
@@ -106,11 +106,9 @@
{% endfor %}
-
-
-
-
+
+
@@ -128,7 +126,7 @@
diff --git a/templates/web/admin/plugins/index.html.twig b/templates/web/admin/plugins/index.html.twig
index cc6877f71a..c24865c5ff 100644
--- a/templates/web/admin/plugins/index.html.twig
+++ b/templates/web/admin/plugins/index.html.twig
@@ -3,14 +3,14 @@
{% block content %}
{% for pluginName in plugins.keys() %}
- {{ 'plugin.name'|trans([], plugins[pluginName].localeTextDomain) }}
+ {{ 'plugin_name'|trans([], plugins[pluginName].localeTextDomain) }}
{% endfor %}
diff --git a/templates/web/admin/plugins/show_fallback.html.twig b/templates/web/admin/plugins/show_fallback.html.twig
index cbdaa5668b..892579d26b 100644
--- a/templates/web/admin/plugins/show_fallback.html.twig
+++ b/templates/web/admin/plugins/show_fallback.html.twig
@@ -3,7 +3,7 @@
{% block content %}
diff --git a/templates/web/admin/publications/fiche.html.twig b/templates/web/admin/publications/fiche.html.twig
index 0d7f7ab51f..d53d912280 100644
--- a/templates/web/admin/publications/fiche.html.twig
+++ b/templates/web/admin/publications/fiche.html.twig
@@ -33,31 +33,36 @@
sequentialUploads: true,
add: function (e, data) {
$('#upload-error').empty();
- $.each(data.files, function (index, file) {
- var fileType = /^image\/(gif|jpeg|png)$/;
- if(typeof loadImage == 'function' && fileType.test(file.type)){
- if(file.size < 204800){ //200 ko
- var options = {
- maxWidth: 32,
- maxHeight: 32,
- minWidth: 32,
- minHeight: 32
- };
+ require([
+ "blueimp.loadimage"
+ ], function (loadImage) {
- data.oldImage = $("#img_before").get(0);
+ $.each(data.files, function (index, file) {
+ var fileType = /^image\/(gif|jpeg|png)$/;
+ if(typeof loadImage == 'function' && fileType.test(file.type)){
+ if(file.size < 204800){ //200 ko
+ var options = {
+ maxWidth: 32,
+ maxHeight: 32,
+ minWidth: 32,
+ minHeight: 32
+ };
- loadImage(file, function(img){
- $("#img_before").remove();
- $("#pub_icon .thumb_wrapper").append(img);
- $("#pub_icon .thumb_wrapper img").attr("img_before");
- return false;
- }, options);
+ data.oldImage = $("#img_before").get(0);
- data.submit();
- } else {
- $('#upload-error').empty().append(language.errorFileApiTooBig);
+ loadImage(file, function(img){
+ $("#img_before").remove();
+ $("#pub_icon .thumb_wrapper").append(img);
+ $("#pub_icon .thumb_wrapper img").attr("img_before");
+ return false;
+ }, options);
+
+ data.submit();
+ } else {
+ $('#upload-error').empty().append(language.errorFileApiTooBig);
+ }
}
- }
+ });
});
},
done: function (e, data) {
diff --git a/templates/web/admin/statusbit/edit.html.twig b/templates/web/admin/statusbit/edit.html.twig
index 7330477824..4001a0626e 100644
--- a/templates/web/admin/statusbit/edit.html.twig
+++ b/templates/web/admin/statusbit/edit.html.twig
@@ -166,6 +166,7 @@