diff --git a/lib/Alchemy/Phrasea/Border/Checker/AbstractChecker.php b/lib/Alchemy/Phrasea/Border/Checker/AbstractChecker.php index 8b4de0fb58..de6f4c25d6 100644 --- a/lib/Alchemy/Phrasea/Border/Checker/AbstractChecker.php +++ b/lib/Alchemy/Phrasea/Border/Checker/AbstractChecker.php @@ -34,6 +34,11 @@ abstract class AbstractChecker implements CheckerInterface */ protected $collections = []; + /** + * @var \collection[] + */ + protected $compareIgnoreCollections = []; + public function __construct(Application $app) { $this->app = $app; @@ -44,7 +49,7 @@ abstract class AbstractChecker implements CheckerInterface * Warning, you can not restrict on both databoxes and collections * * @param \databox[] $databoxes A databox or an array of databoxes - * @return bool + * @return \databox[] * * @throws \LogicException If already restricted to collections * @throws \InvalidArgumentException In case invalid databoxes are provided @@ -72,7 +77,7 @@ abstract class AbstractChecker implements CheckerInterface * Warning, you can not restrict on both databoxes and collections * * @param \collection[] $collections - * @return bool + * @return \collection[] * * @throws \LogicException If already restricted to databoxes * @throws \InvalidArgumentException In case invalid collections are provided @@ -95,6 +100,11 @@ abstract class AbstractChecker implements CheckerInterface return $this->collections; } + public function setCompareIgnoreCollections($collections) + { + $this->compareIgnoreCollections = $collections; + } + /** * Returns true if the checker should be executed against the current file * diff --git a/lib/Alchemy/Phrasea/Border/Checker/Filename.php b/lib/Alchemy/Phrasea/Border/Checker/Filename.php index ec4f0b0be4..ea1c25a7a6 100644 --- a/lib/Alchemy/Phrasea/Border/Checker/Filename.php +++ b/lib/Alchemy/Phrasea/Border/Checker/Filename.php @@ -45,8 +45,18 @@ class Filename extends AbstractChecker */ public function check(EntityManager $em, File $file) { - $boolean = empty(\record_adapter::get_records_by_originalname( - $file->getCollection()->get_databox(), $file->getOriginalName(), $this->sensitive, 0, 1 + $excludedCollIds = []; + if (!empty($this->compareIgnoreCollections)) { + foreach ($this->compareIgnoreCollections as $collection) { + // use only collection in the same databox and retrieve the coll_id + if ($collection->get_sbas_id() === $file->getCollection()->get_sbas_id()) { + $excludedCollIds[] = $collection->get_coll_id(); + } + } + } + + $boolean = empty(\record_adapter::getRecordsByOriginalnameWithExcludedCollIds( + $file->getCollection()->get_databox(), $file->getOriginalName(), $this->sensitive, 0, 1, $excludedCollIds )); return new Response($boolean, $this); diff --git a/lib/Alchemy/Phrasea/Border/Checker/Sha256.php b/lib/Alchemy/Phrasea/Border/Checker/Sha256.php index d17b28e5b9..5116e35dfd 100644 --- a/lib/Alchemy/Phrasea/Border/Checker/Sha256.php +++ b/lib/Alchemy/Phrasea/Border/Checker/Sha256.php @@ -34,7 +34,17 @@ class Sha256 extends AbstractChecker */ public function check(EntityManager $em, File $file) { - $boolean = empty($file->getCollection()->get_databox()->getRecordRepository()->findBySha256($file->getSha256())); + $excludedCollIds = []; + if (!empty($this->compareIgnoreCollections)) { + foreach ($this->compareIgnoreCollections as $collection) { + // use only collection in the same databox and retrieve the coll_id + if ($collection->get_sbas_id() === $file->getCollection()->get_sbas_id()) { + $excludedCollIds[] = $collection->get_coll_id(); + } + } + } + + $boolean = empty($file->getCollection()->get_databox()->getRecordRepository()->findBySha256WithExcludedCollIds($file->getSha256(), $excludedCollIds)); return new Response($boolean, $this); } diff --git a/lib/Alchemy/Phrasea/Border/Checker/UUID.php b/lib/Alchemy/Phrasea/Border/Checker/UUID.php index c3857fbe76..cdacf973a5 100644 --- a/lib/Alchemy/Phrasea/Border/Checker/UUID.php +++ b/lib/Alchemy/Phrasea/Border/Checker/UUID.php @@ -33,7 +33,17 @@ class UUID extends AbstractChecker */ public function check(EntityManager $em, File $file) { - $boolean = empty($file->getCollection()->get_databox()->getRecordRepository()->findByUuid($file->getUUID())); + $excludedCollIds = []; + if (!empty($this->compareIgnoreCollections)) { + foreach ($this->compareIgnoreCollections as $collection) { + // use only collection in the same databox and retrieve the coll_id + if ($collection->get_sbas_id() === $file->getCollection()->get_sbas_id()) { + $excludedCollIds[] = $collection->get_coll_id(); + } + } + } + + $boolean = empty($file->getCollection()->get_databox()->getRecordRepository()->findByUuidWithExcludedCollIds($file->getUUID(), $excludedCollIds)); return new Response($boolean, $this); } diff --git a/lib/Alchemy/Phrasea/Core/Provider/BorderManagerServiceProvider.php b/lib/Alchemy/Phrasea/Core/Provider/BorderManagerServiceProvider.php index 36f0ed0e7e..d7a0f636f9 100644 --- a/lib/Alchemy/Phrasea/Core/Provider/BorderManagerServiceProvider.php +++ b/lib/Alchemy/Phrasea/Core/Provider/BorderManagerServiceProvider.php @@ -78,6 +78,20 @@ class BorderManagerServiceProvider implements ServiceProviderInterface $checkerObj->restrictToCollections($collections); } + + if (isset($checker['compare-ignore-collections'])) { + $collections = []; + foreach ($checker['compare-ignore-collections'] as $base_id) { + try { + $collections[] = \collection::getByBaseId($app, $base_id); + } catch (\Exception $e) { + throw new \InvalidArgumentException('Invalid collection option'); + } + } + + $checkerObj->setCompareIgnoreCollections($collections); + } + $registeredCheckers[] = $checkerObj; } catch (\InvalidArgumentException $e) { $app['monolog']->error( diff --git a/lib/Alchemy/Phrasea/Databox/Record/LegacyRecordRepository.php b/lib/Alchemy/Phrasea/Databox/Record/LegacyRecordRepository.php index fe196201a8..30feada346 100644 --- a/lib/Alchemy/Phrasea/Databox/Record/LegacyRecordRepository.php +++ b/lib/Alchemy/Phrasea/Databox/Record/LegacyRecordRepository.php @@ -82,6 +82,34 @@ class LegacyRecordRepository implements RecordRepository return $this->mapRecordsFromResultSet($result); } + public function findBySha256WithExcludedCollIds($sha256, $excludedCollIds = []) + { + static $sql; + + if (!$sql) { + $qb = $this->createSelectBuilder() + ->where('sha256 = :sha256'); + + if (!empty($excludedCollIds)) { + $qb->andWhere($qb->expr()->notIn('coll_id', ':coll_id')); + } + + $sql = $qb->getSQL(); + } + + $result = $this->databox->get_connection()->fetchAll($sql, + [ + 'sha256' => $sha256, + 'coll_id' => $excludedCollIds + ], + [ + ':coll_id' => Connection::PARAM_INT_ARRAY + ] + ); + + return $this->mapRecordsFromResultSet($result); + } + /** * @param string $uuid * @return \record_adapter[] @@ -99,6 +127,40 @@ class LegacyRecordRepository implements RecordRepository return $this->mapRecordsFromResultSet($result); } + /** + * @param string $uuid + * @param array $excludedCollIds + * @return \record_adapter[] + */ + public function findByUuidWithExcludedCollIds($uuid, $excludedCollIds = []) + { + static $sql; + + if (!$sql) { + $qb = $this->createSelectBuilder() + ->where('uuid = :uuid') + ; + + if (!empty($excludedCollIds)) { + $qb->andWhere($qb->expr()->notIn('coll_id', ':coll_id')); + } + + $sql = $qb->getSQL(); + } + + $result = $this->databox->get_connection()->fetchAll($sql, + [ + 'uuid' => $uuid, + 'coll_id' => $excludedCollIds + ], + [ + ':coll_id' => Connection::PARAM_INT_ARRAY + ] + ); + + return $this->mapRecordsFromResultSet($result); + } + public function findByRecordIds(array $recordIds) { static $sql; diff --git a/lib/Alchemy/Phrasea/Databox/Record/RecordRepository.php b/lib/Alchemy/Phrasea/Databox/Record/RecordRepository.php index 870339a238..bab31055da 100644 --- a/lib/Alchemy/Phrasea/Databox/Record/RecordRepository.php +++ b/lib/Alchemy/Phrasea/Databox/Record/RecordRepository.php @@ -26,12 +26,26 @@ interface RecordRepository */ public function findBySha256($sha256); + /** + * @param string $sha256 + * @param array $excludedCollIds + * @return \record_adapter[] + */ + public function findBySha256WithExcludedCollIds($sha256, $excludedCollIds = []); + /** * @param string $uuid * @return \record_adapter[] */ public function findByUuid($uuid); + /** + * @param string $uuid + * @param array $excludedCollIds + * @return \record_adapter[] + */ + public function findByUuidWithExcludedCollIds($uuid, $excludedCollIds = []); + /** * @param array $recordIds * @return \record_adapter[] diff --git a/lib/classes/record/adapter.php b/lib/classes/record/adapter.php index 103a2b4cb0..8e181c5d2e 100644 --- a/lib/classes/record/adapter.php +++ b/lib/classes/record/adapter.php @@ -1673,6 +1673,43 @@ class record_adapter implements RecordInterface, cache_cacheableInterface return $records; } + public static function getRecordsByOriginalnameWithExcludedCollIds(databox $databox, $original_name, $caseSensitive = false, $offset_start = 0, $how_many = 10, $excludedCollIds = []) + { + $offset_start = max(0, (int)$offset_start); + $how_many = max(1, (int)$how_many); + $collate = $caseSensitive ? 'utf8_bin' : 'utf8_unicode_ci'; + + $qb = $databox->get_connection()->createQueryBuilder() + ->select('record_id') + ->from('record') + ->where('originalname = :original_name COLLATE :collate') + ; + + $params = ['original_name' => $original_name, 'collate' => $collate]; + $types = []; + + if (!empty($excludedCollIds)) { + $qb->andWhere($qb->expr()->notIn('coll_id', ':coll_id')); + + $params['coll_id'] = $excludedCollIds; + $types[':coll_id'] = Connection::PARAM_INT_ARRAY; + } + + $sql = $qb->setFirstResult($offset_start) + ->setMaxResults($how_many) + ->getSQL() + ; + + $rs = $databox->get_connection()->fetchAll($sql, $params, $types); + + $records = []; + foreach ($rs as $row) { + $records[] = $databox->get_record($row['record_id']); + } + + return $records; + } + /** * @return set_selection|record_adapter[] * @throws Exception