diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000000..a37aa2475b --- /dev/null +++ b/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,16 @@ +## Changelog +### Changed + - Breaking change + +### Fixes + - PHRAS-XXX: Short description of bug and fix + - Short description of bug and fix without issue/ticket + +### Adds + - PHRAS-XXX: Short feature description + - Short feature description without issue/ticket + +### Removes + - PHRAS-XXX: Short feature removal description + - Short feature removal description without issue/ticket + diff --git a/composer.json b/composer.json index efc06b5678..5b8441ae34 100644 --- a/composer.json +++ b/composer.json @@ -47,6 +47,7 @@ "dailymotion/sdk": "~1.5", "data-uri/data-uri": "~0.1.0", "dflydev/doctrine-orm-service-provider": "~1.0", + "doctrine/cache": "^1.6", "doctrine/dbal": "^2.4.0", "doctrine/migrations": "^1.0.0", "doctrine/orm": "^2.4.0", @@ -61,7 +62,7 @@ "hoa/dispatcher": "~0.0", "hoa/router": "~2.0", "igorw/get-in": "~1.0", - "imagine/imagine": "dev-alchemy-0.6.2 as 0.6.2", + "imagine/imagine": "0.6.x-dev", "ircmaxell/random-lib": "~1.0", "jms/serializer": "~0.10", "jms/translation-bundle": "dev-rebase-2015-10-20", @@ -88,7 +89,7 @@ "simple-bus/jms-serializer-bridge": "^1.0", "simple-bus/message-bus": "^2.1", "simple-bus/serialization": "^2.0", - "sorien/silex-dbal-profiler": "~1.0.0", + "sorien/silex-dbal-profiler": "^1.1", "sorien/silex-pimple-dumper": "^1.0", "swiftmailer/swiftmailer": "~5.3.0", "symfony/symfony": "~2.7.10|~2.8.3", diff --git a/composer.lock b/composer.lock index 26aafbbcb3..6f103ba897 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,8 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "baf4148980adfb15bed6b72c438da7eb", - "content-hash": "5d8ffafd15285984d97c82a1e13a697f", + "hash": "8db2333764b17b4b04f474a6b9fc2c49", + "content-hash": "2961cef34c7a94f9afb11b859781cb65", "packages": [ { "name": "alchemy-fr/tcpdf-clone", @@ -1137,33 +1137,33 @@ }, { "name": "doctrine/cache", - "version": "v1.5.1", + "version": "v1.6.0", "source": { "type": "git", "url": "https://github.com/doctrine/cache.git", - "reference": "2b9cec5a5e722010cbebc91713d4c11eaa064d5e" + "reference": "f8af318d14bdb0eff0336795b428b547bd39ccb6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/cache/zipball/2b9cec5a5e722010cbebc91713d4c11eaa064d5e", - "reference": "2b9cec5a5e722010cbebc91713d4c11eaa064d5e", + "url": "https://api.github.com/repos/doctrine/cache/zipball/f8af318d14bdb0eff0336795b428b547bd39ccb6", + "reference": "f8af318d14bdb0eff0336795b428b547bd39ccb6", "shasum": "" }, "require": { - "php": ">=5.3.2" + "php": "~5.5|~7.0" }, "conflict": { "doctrine/common": ">2.2,<2.4" }, "require-dev": { - "phpunit/phpunit": ">=3.7", + "phpunit/phpunit": "~4.8|~5.0", "predis/predis": "~1.0", "satooshi/php-coveralls": "~0.6" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.5.x-dev" + "dev-master": "1.6.x-dev" } }, "autoload": { @@ -1203,7 +1203,7 @@ "cache", "caching" ], - "time": "2015-11-02 18:35:48" + "time": "2015-12-31 16:37:02" }, { "name": "doctrine/collections", @@ -2930,7 +2930,7 @@ }, { "name": "imagine/imagine", - "version": "dev-alchemy-0.6.2", + "version": "0.6.x-dev", "source": { "type": "git", "url": "https://github.com/alchemy-fr/Imagine.git", @@ -4744,7 +4744,7 @@ }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/dcdeb1d923b981a55f0a4ce6c2ceac14cb8476f8", + "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/438196c8f66647073bcaf99908b543bda7ffd6d4", "reference": "c18abc6df90257ddbbe2654a98cc5cc7be804411", "shasum": "" }, @@ -5221,22 +5221,28 @@ }, { "name": "sorien/silex-dbal-profiler", - "version": "1.0.0", + "version": "1.1.2", "source": { "type": "git", "url": "https://github.com/Sorien/silex-dbal-profiler.git", - "reference": "8cbbef7a8fae53381d9005163070485471089908" + "reference": "4d3f642144e96685270f7ffb1648a081d0c5eb7e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Sorien/silex-dbal-profiler/zipball/8cbbef7a8fae53381d9005163070485471089908", - "reference": "8cbbef7a8fae53381d9005163070485471089908", + "url": "https://api.github.com/repos/Sorien/silex-dbal-profiler/zipball/4d3f642144e96685270f7ffb1648a081d0c5eb7e", + "reference": "4d3f642144e96685270f7ffb1648a081d0c5eb7e", "shasum": "" }, - "require-dev": { - "silex/silex": "~1.0" + "require": { + "silex/silex": "~1.0", + "silex/web-profiler": "~1.0" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + }, "autoload": { "psr-0": { "Sorien": "src" @@ -5259,7 +5265,7 @@ "profiler", "silex" ], - "time": "2014-02-26 10:01:20" + "time": "2015-03-12 09:26:42" }, { "name": "sorien/silex-pimple-dumper", @@ -7389,14 +7395,7 @@ "time": "2015-06-21 13:59:46" } ], - "aliases": [ - { - "alias": "0.6.2", - "alias_normalized": "0.6.2.0", - "version": "dev-alchemy-0.6.2", - "package": "imagine/imagine" - } - ], + "aliases": [], "minimum-stability": "stable", "stability-flags": { "alchemy/task-manager": 20, diff --git a/lib/Alchemy/Phrasea/Application.php b/lib/Alchemy/Phrasea/Application.php index a4fc9e03df..2214926e63 100644 --- a/lib/Alchemy/Phrasea/Application.php +++ b/lib/Alchemy/Phrasea/Application.php @@ -73,6 +73,7 @@ use Alchemy\Phrasea\Core\Provider\UnicodeServiceProvider; use Alchemy\Phrasea\Core\Provider\WebhookServiceProvider; use Alchemy\Phrasea\Core\Provider\ZippyServiceProvider; use Alchemy\Phrasea\Core\Provider\WebProfilerServiceProvider as PhraseaWebProfilerServiceProvider; +use Alchemy\Phrasea\Databox\Subdef\MediaSubdefServiceProvider; use Alchemy\Phrasea\Exception\InvalidArgumentException; use Alchemy\Phrasea\Filesystem\FilesystemServiceProvider; use Alchemy\Phrasea\Filesystem\ApplicationPathServiceGenerator; @@ -80,6 +81,7 @@ use Alchemy\Phrasea\Form\Extension\HelpTypeExtension; use Alchemy\Phrasea\Media\DatafilesResolver; use Alchemy\Phrasea\Media\MediaAccessorResolver; use Alchemy\Phrasea\Media\PermalinkMediaResolver; +use Alchemy\Phrasea\Media\TechnicalDataServiceProvider; use Alchemy\Phrasea\Model\Entities\User; use Doctrine\DBAL\Event\ConnectionEventArgs; use MediaVorus\Media\MediaInterface; @@ -190,6 +192,8 @@ class Application extends SilexApplication $this->register(new NotificationDelivererServiceProvider()); $this->register(new RepositoriesServiceProvider()); $this->register(new ManipulatorServiceProvider()); + $this->register(new TechnicalDataServiceProvider()); + $this->register(new MediaSubdefServiceProvider()); $this->register(new InstallerServiceProvider()); $this->register(new PhraseaVersionServiceProvider()); diff --git a/lib/Alchemy/Phrasea/Border/Attribute/Story.php b/lib/Alchemy/Phrasea/Border/Attribute/Story.php index b694839e41..6b8e7eabcf 100644 --- a/lib/Alchemy/Phrasea/Border/Attribute/Story.php +++ b/lib/Alchemy/Phrasea/Border/Attribute/Story.php @@ -69,7 +69,7 @@ class Story implements AttributeInterface */ public function asString() { - return $this->story->get_serialize_key(); + return $this->story->getId(); } /** diff --git a/lib/Alchemy/Phrasea/Border/Manager.php b/lib/Alchemy/Phrasea/Border/Manager.php index 295d46db87..d8abc9ac99 100644 --- a/lib/Alchemy/Phrasea/Border/Manager.php +++ b/lib/Alchemy/Phrasea/Border/Manager.php @@ -278,7 +278,7 @@ class Manager $file->addAttribute( new MetadataAttr( new Metadata( - new TfRecordid(), new MonoValue($element->get_record_id()) + new TfRecordid(), new MonoValue($element->getRecordId()) ) ) ); @@ -314,7 +314,7 @@ class Manager break; case AttributeInterface::NAME_STATUS: /** @var StatusAttr $attribute */ - $element->set_binary_status(decbin(bindec($element->get_status()) | bindec($attribute->getValue()))); + $element->setStatus(decbin(bindec($element->getStatus()) | bindec($attribute->getValue()))); break; case AttributeInterface::NAME_STORY: diff --git a/lib/Alchemy/Phrasea/Cache/MultiAdapter.php b/lib/Alchemy/Phrasea/Cache/MultiAdapter.php new file mode 100644 index 0000000000..d9133275a3 --- /dev/null +++ b/lib/Alchemy/Phrasea/Cache/MultiAdapter.php @@ -0,0 +1,88 @@ +cache = $cache; + } + + public function fetch($id) + { + return $this->cache->fetch($id); + } + + public function contains($id) + { + return $this->cache->contains($id); + } + + public function save($id, $data, $lifeTime = 0) + { + return $this->cache->save($id, $data, $lifeTime); + } + + public function delete($id) + { + return $this->cache->delete($id); + } + + public function getStats() + { + return $this->cache->getStats(); + } + + public function fetchMultiple(array $keys) + { + if ($this->cache instanceof MultiGetCache) { + return $this->cache->fetchMultiple($keys); + } + + // Pass data by reference to avoid copies of whole array on key add. + $data = array_reduce($keys, function (array &$data, $key) { + $value = $this->fetch($key); + + if (false !== $value || true === $this->contains($key)) { + $data[$key] = $value; + } + + return $data; + }, []); + + return $data; + } + + public function saveMultiple(array $keysAndValues, $lifetime = 0) + { + if ($this->cache instanceof MultiPutCache) { + return $this->cache->saveMultiple($keysAndValues, $lifetime); + } + + foreach ($keysAndValues as $key => $value) { + if (!$this->cache->save($key, $value, $lifetime)) { + return false; + } + } + + return true; + } +} diff --git a/lib/Alchemy/Phrasea/Collection/CollectionHelper.php b/lib/Alchemy/Phrasea/Collection/CollectionHelper.php index a56df2e356..4725938d7f 100644 --- a/lib/Alchemy/Phrasea/Collection/CollectionHelper.php +++ b/lib/Alchemy/Phrasea/Collection/CollectionHelper.php @@ -12,12 +12,8 @@ namespace Alchemy\Phrasea\Collection; use Assert\Assertion; -class CollectionHelper +final class CollectionHelper { - private function __construct() - { - } - /** * @param \collection[] $collections * @return \collection[] @@ -31,7 +27,11 @@ class CollectionHelper } usort($collections, function (\collection $left, \collection $right) { - return ($left->get_ord() < $right->get_ord()) ? -1 : (($left->get_ord() < $right->get_ord()) ? 1 : 0); + if ($left->get_ord() === $right->get_ord()) { + return 0; + } + + return $left->get_ord() < $right->get_ord() ? -1 : 1; }); return $collections; diff --git a/lib/Alchemy/Phrasea/Command/BuildSubdefs.php b/lib/Alchemy/Phrasea/Command/BuildSubdefs.php index 55446b7de0..b927744c2c 100644 --- a/lib/Alchemy/Phrasea/Command/BuildSubdefs.php +++ b/lib/Alchemy/Phrasea/Command/BuildSubdefs.php @@ -179,13 +179,14 @@ class BuildSubdefs extends Command if($this->databox !== null) { - foreach ($this->databox->get_subdef_structure() as $sgname=>$sg) { - if (empty($types) || in_array($sgname, $types)) { - $this->subdefsNameByType[$sgname] = []; + /** @var SubdefGroup $sg */ + foreach ($this->databox->get_subdef_structure() as $sg) { + if (empty($types) || in_array($sg->getName(), $types)) { + $this->subdefsNameByType[$sg->getName()] = []; /** @var databox_subdef $sd */ foreach ($sg as $sd) { if (empty($names) || in_array($sd->get_name(), $names)) { - $this->subdefsNameByType[$sgname][] = $sd->get_name(); + $this->subdefsNameByType[$sg->getName()][] = $sd->get_name(); } } } diff --git a/lib/Alchemy/Phrasea/Command/RecordAdd.php b/lib/Alchemy/Phrasea/Command/RecordAdd.php index e40514ac4c..47192053af 100644 --- a/lib/Alchemy/Phrasea/Command/RecordAdd.php +++ b/lib/Alchemy/Phrasea/Command/RecordAdd.php @@ -61,7 +61,7 @@ class RecordAdd extends Command if (!$input->getOption('yes')) { do { $continue = strtolower($dialog->ask($output, sprintf("Will add record %s (%s) on collection %s\nContinue ? (y/N)", $file, $media->getType(), $collection->get_label($this->container['locale'])), 'N')); - } while ( ! in_array($continue, ['y', 'n'])); + } while (!in_array($continue, ['y', 'n'])); if (strtolower($continue) !== 'y') { $output->writeln('Aborted !'); @@ -112,7 +112,10 @@ class RecordAdd extends Command if ($elementCreated instanceof \record_adapter) { $output->writeln( sprintf( - "Record id %d on collection `%s` (databox `%s`) has been created", $elementCreated->get_record_id(), $elementCreated->get_collection()->get_label($this->container['locale']), $elementCreated->get_databox()->get_label($this->container['locale']) + "Record id %d on collection `%s` (databox `%s`) has been created", + $elementCreated->getRecordId(), + $elementCreated->getCollection()->get_label($this->container['locale']), + $elementCreated->getDatabox()->get_label($this->container['locale']) ) ); } elseif ($elementCreated instanceof LazaretFile) { diff --git a/lib/Alchemy/Phrasea/Command/Upgrade/Step35.php b/lib/Alchemy/Phrasea/Command/Upgrade/Step35.php index 911585335f..c9246cf319 100644 --- a/lib/Alchemy/Phrasea/Command/Upgrade/Step35.php +++ b/lib/Alchemy/Phrasea/Command/Upgrade/Step35.php @@ -80,13 +80,13 @@ class Step35 implements DatasUpgraderInterface try { $this->updateMetadatas($record, $row['xml']); } catch (\Exception $e) { - $this->app['monolog']->addError(sprintf("Error while upgrading metadatas for record %d on databox %d : %s", $record->get_record_id(), $record->get_sbas_id(), $e->getMessage())); + $this->app['monolog']->addError(sprintf("Error while upgrading metadatas for record %d on databox %d : %s", $record->getRecordId(), $record->getDataboxId(), $e->getMessage())); } try { - $record->set_binary_status($row['status']); + $record->setStatus($row['status']); } catch (\Exception $e) { - $this->app['monolog']->addError(sprintf("Error while upgrading status for record %d on databox %d : %s", $record->get_record_id(), $record->get_sbas_id(), $e->getMessage())); + $this->app['monolog']->addError(sprintf("Error while upgrading status for record %d on databox %d : %s", $record->getRecordId(), $record->getDataboxId(), $e->getMessage())); } unset($record); } @@ -131,7 +131,7 @@ class Step35 implements DatasUpgraderInterface */ protected function updateMetadatas(\record_adapter $record, $xml) { - $metas = $record->get_databox()->get_meta_structure(); + $metas = $record->getDatabox()->get_meta_structure(); $datas = $metadatas = []; diff --git a/lib/Alchemy/Phrasea/Controller/AbstractDelivery.php b/lib/Alchemy/Phrasea/Controller/AbstractDelivery.php index e550c21912..edce950bd1 100644 --- a/lib/Alchemy/Phrasea/Controller/AbstractDelivery.php +++ b/lib/Alchemy/Phrasea/Controller/AbstractDelivery.php @@ -62,7 +62,7 @@ abstract class AbstractDelivery private function logView(\record_adapter $record, Request $request) { try { - $logger = $this->getDataboxLogger($record->get_databox()); + $logger = $this->getDataboxLogger($record->getDatabox()); $log_id = $logger->get_id(); $record->log_view( $log_id, diff --git a/lib/Alchemy/Phrasea/Controller/Admin/FieldsController.php b/lib/Alchemy/Phrasea/Controller/Admin/FieldsController.php index 12e7703fbb..c13efa100d 100644 --- a/lib/Alchemy/Phrasea/Controller/Admin/FieldsController.php +++ b/lib/Alchemy/Phrasea/Controller/Admin/FieldsController.php @@ -12,8 +12,8 @@ namespace Alchemy\Phrasea\Controller\Admin; use Alchemy\Phrasea\Controller\Controller; use Alchemy\Phrasea\Metadata\TagProvider; -use Alchemy\Phrasea\Vocabulary\Controller as VocabularyController; use Alchemy\Phrasea\Vocabulary\ControlProvider\ControlProviderInterface; +use Assert\Assertion; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; @@ -96,24 +96,57 @@ class FieldsController extends Controller public function listVocabularies() { - $vocabularies = VocabularyController::getAvailable($this->app); + return $this->app->json(array_map( + [$this, 'getVocabularyAsArray'], + $this->fetchVocabularies() + )); + } - return $this->app->json(array_map(function (ControlProviderInterface $vocabulary) { - return [ - 'type' => $vocabulary->getType(), - 'name' => $vocabulary->getName(), - ]; - }, $vocabularies)); + /** + * @return ControlProviderInterface[] + */ + private function fetchVocabularies() + { + $vocabularies = $this->getVocabularies(); + + $instances = array_map( + function ($type) use ($vocabularies) { + return $vocabularies[$type]; + }, + $vocabularies->keys() + ); + + Assertion::allIsInstanceOf($instances, ControlProviderInterface::class); + + return $instances; + } + + /** + * @param string $type + * @return ControlProviderInterface + */ + private function fetchVocabulary($type) + { + $vocabularies = $this->getVocabularies(); + + $vocabulary = $vocabularies[strtolower($type)]; + + Assertion::isInstanceOf($vocabulary, ControlProviderInterface::class); + + return $vocabulary; + } + + private function getVocabularyAsArray(ControlProviderInterface $vocabulary) + { + return [ + 'type' => $vocabulary->getType(), + 'name' => $vocabulary->getName(), + ]; } public function getVocabulary($type) { - $vocabulary = VocabularyController::get($this->app, $type); - - return $this->app->json([ - 'type' => $vocabulary->getType(), - 'name' => $vocabulary->getName(), - ]); + return $this->app->json($this->getVocabularyAsArray($this->fetchVocabulary($type))); } public function searchTag(Request $request) @@ -293,11 +326,11 @@ class FieldsController extends Controller } try { - $vocabulary = VocabularyController::get($this->app, $data['vocabulary-type']); + $vocabulary = $this->fetchVocabulary($data['vocabulary-type']); $field->setVocabularyControl($vocabulary); $field->setVocabularyRestricted($data['vocabulary-restricted']); } catch (\InvalidArgumentException $e) { - + // Invalid vocabulary requested } if ('' !== $dcesElement = (string) $data['dces-element']) { @@ -347,4 +380,12 @@ class FieldsController extends Controller return $data; } + + /** + * @return ControlProviderInterface[]|\Pimple + */ + private function getVocabularies() + { + return $this->app['vocabularies']; + } } diff --git a/lib/Alchemy/Phrasea/Controller/Admin/SearchEngineController.php b/lib/Alchemy/Phrasea/Controller/Admin/SearchEngineController.php index 2c0a3be48e..30b4b84782 100644 --- a/lib/Alchemy/Phrasea/Controller/Admin/SearchEngineController.php +++ b/lib/Alchemy/Phrasea/Controller/Admin/SearchEngineController.php @@ -38,9 +38,28 @@ class SearchEngineController extends Controller return $this->render('admin/search-engine/elastic-search.html.twig', [ 'form' => $form->createView(), + 'indexer' => $this->app['elasticsearch.indexer'] ]); } + public function dropIndexAction(Request $request) + { + $indexer = $this->app['elasticsearch.indexer']; + if ($indexer->indexExists()) { + $indexer->deleteIndex(); + } + return $this->app->redirectPath('admin_searchengine_form'); + } + + public function createIndexAction(Request $request) + { + $indexer = $this->app['elasticsearch.indexer']; + if (!$indexer->indexExists()) { + $indexer->createIndex(); + } + return $this->app->redirectPath('admin_searchengine_form'); + } + /** * @return ElasticsearchOptions */ diff --git a/lib/Alchemy/Phrasea/Controller/Api/SearchController.php b/lib/Alchemy/Phrasea/Controller/Api/SearchController.php index 9a8ca841eb..e1113b3919 100644 --- a/lib/Alchemy/Phrasea/Controller/Api/SearchController.php +++ b/lib/Alchemy/Phrasea/Controller/Api/SearchController.php @@ -1,5 +1,5 @@ app, $request); - - $offsetStart = (int) ($request->get('offset_start') ?: 0); - $perPage = (int) $request->get('per_page') ?: 10; + $options->setFirstResult($request->get('offset_start') ?: 0); + $options->setMaxResults($request->get('per_page') ?: 10); $query = (string) $request->get('query'); $this->getSearchEngine()->resetCache(); - $search_result = $this->getSearchEngine()->query($query, $offsetStart, $perPage, $options); + $search_result = $this->getSearchEngine()->query($query, $options); $this->getUserManipulator()->logQuery($this->getAuthenticatedUser(), $search_result->getQuery()); @@ -74,8 +73,8 @@ class SearchController extends Controller $this->getSearchEngine()->clearCache(); $ret = [ - 'offset_start' => $offsetStart, - 'per_page' => $perPage, + 'offset_start' => $options->getFirstResult(), + 'per_page' => $options->getMaxResults(), 'available_results' => $search_result->getAvailable(), 'total_results' => $search_result->getTotal(), 'error' => (string)$search_result->getError(), diff --git a/lib/Alchemy/Phrasea/Controller/Api/V1Controller.php b/lib/Alchemy/Phrasea/Controller/Api/V1Controller.php index b98ee254d2..add7eccc8c 100644 --- a/lib/Alchemy/Phrasea/Controller/Api/V1Controller.php +++ b/lib/Alchemy/Phrasea/Controller/Api/V1Controller.php @@ -47,7 +47,7 @@ use Alchemy\Phrasea\Model\Entities\ValidationData; use Alchemy\Phrasea\Model\Entities\ValidationParticipant; use Alchemy\Phrasea\Model\Manipulator\TaskManipulator; use Alchemy\Phrasea\Model\Manipulator\UserManipulator; -use Alchemy\Phrasea\Model\Provider\SecretProvider; +use Alchemy\Phrasea\Model\RecordReferenceInterface; use Alchemy\Phrasea\Model\Repositories\BasketRepository; use Alchemy\Phrasea\Model\Repositories\FeedEntryRepository; use Alchemy\Phrasea\Model\Repositories\FeedRepository; @@ -61,8 +61,8 @@ use Alchemy\Phrasea\SearchEngine\SearchEngineResult; use Alchemy\Phrasea\SearchEngine\SearchEngineSuggestion; use Alchemy\Phrasea\Status\StatusStructure; use Alchemy\Phrasea\TaskManager\LiveInformation; +use Alchemy\Phrasea\Utilities\NullableDateTime; use Doctrine\ORM\EntityManager; -use Firebase\JWT\JWT; use Symfony\Component\Form\Form; use Symfony\Component\HttpFoundation\File\UploadedFile; use Symfony\Component\HttpFoundation\Request; @@ -139,10 +139,10 @@ class V1Controller extends Controller 'pid' => $data['process-id'], 'jobId' => $task->getJobId(), 'period' => $task->getPeriod(), - 'last_exec_time' => $task->getLastExecution() ? $task->getLastExecution()->format(DATE_ATOM) : null, - 'last_execution' => $task->getLastExecution() ? $task->getLastExecution()->format(DATE_ATOM) : null, - 'updated' => $task->getUpdated() ? $task->getUpdated()->format(DATE_ATOM) : null, - 'created' => $task->getCreated() ? $task->getCreated()->format(DATE_ATOM) : null, + 'last_exec_time' => NullableDateTime::format($task->getLastExecution()), + 'last_execution' => NullableDateTime::format($task->getLastExecution()), + 'updated' => NullableDateTime::format($task->getUpdated()), + 'created' => NullableDateTime::format($task->getCreated()), 'auto_start' => $task->getStatus() === Task::STATUS_STARTED, 'crashed' => $task->getCrashed(), ]; @@ -710,9 +710,9 @@ class V1Controller extends Controller 'position' => $user->getActivity() ?: null, 'company' => $user->getCompany() ?: null, 'geoname_id' => $user->getGeonameId() ?: null, - 'last_connection' => $user->getLastConnection() ? $user->getLastConnection()->format(DATE_ATOM) : null, - 'created_on' => $user->getCreated() ? $user->getCreated()->format(DATE_ATOM) : null, - 'updated_on' => $user->getUpdated() ? $user->getUpdated()->format(DATE_ATOM) : null, + 'last_connection' => NullableDateTime::format($user->getLastConnection()), + 'created_on' => NullableDateTime::format($user->getCreated()), + 'updated_on' => NullableDateTime::format($user->getUpdated()), 'locale' => $user->getLocale() ?: null, ]; } @@ -1003,37 +1003,11 @@ class V1Controller extends Controller 'substituted' => $media->is_substituted(), 'created_on' => $media->get_creation_date()->format(DATE_ATOM), 'updated_on' => $media->get_modification_date()->format(DATE_ATOM), - 'url' => $this->generateSubDefinitionUrl($issuer, $media, $urlTTL), + 'url' => $this->app['media_accessor.subdef_url_generator']->generate($issuer, $media, $urlTTL), 'url_ttl' => $urlTTL, ]; } - /** - * @param User $issuer - * @param \media_subdef $subdef - * @param int $url_ttl - * @return string - */ - private function generateSubDefinitionUrl(User $issuer, \media_subdef $subdef, $url_ttl) - { - $payload = [ - 'iat' => time(), - 'iss' => $issuer->getId(), - 'sdef' => [$subdef->get_sbas_id(), $subdef->get_record_id(), $subdef->get_name()], - ]; - if ($url_ttl >= 0) { - $payload['exp'] = $payload['iat'] + $url_ttl; - } - - /** @var SecretProvider $provider */ - $provider = $this->app['provider.secrets']; - $secret = $provider->getSecretForUser($issuer); - - return $this->app->url('media_accessor', [ - 'token' => JWT::encode($payload, $secret->getToken(), 'HS256', $secret->getId()), - ]); - } - private function listPermalink(\media_Permalink_Adapter $permalink) { $downloadUrl = $permalink->get_url(); @@ -1063,19 +1037,23 @@ class V1Controller extends Controller { list($ret, $search_result) = $this->prepareSearchRequest($request); - $ret['results'] = ['records' => [], 'stories' => []]; + $records = []; + $stories = []; /** @var SearchEngineResult $search_result */ - $references = new RecordReferenceCollection($search_result->getResults()); - - foreach ($references->toRecords($this->getApplicationBox()) as $record) { + foreach ($search_result->getResults() as $record) { if ($record->isStory()) { - $ret['results']['stories'][] = $this->listStory($request, $record); + $stories[] = $record; } else { - $ret['results']['records'][] = $this->listRecord($request, $record); + $records[] = $record; } } + $ret['results'] = [ + 'records' => $this->listRecords($request, $records), + 'stories' => $this->listStories($request, $stories), + ]; + return Result::create($request, $ret)->createResponse(); } @@ -1110,13 +1088,13 @@ class V1Controller extends Controller { $options = SearchEngineOptions::fromRequest($this->app, $request); - $offsetStart = (int) ($request->get('offset_start') ?: 0); - $perPage = (int) $request->get('per_page') ?: 10; + $options->setFirstResult((int) ($request->get('offset_start') ?: 0)); + $options->setMaxResults((int) $request->get('per_page') ?: 10); $query = (string) $request->get('query'); $this->getSearchEngine()->resetCache(); - $search_result = $this->getSearchEngine()->query($query, $offsetStart, $perPage, $options); + $search_result = $this->getSearchEngine()->query($query, $options); $this->getUserManipulator()->logQuery($this->getAuthenticatedUser(), $search_result->getQuery()); @@ -1134,8 +1112,8 @@ class V1Controller extends Controller $this->getSearchEngine()->clearCache(); $ret = [ - 'offset_start' => $offsetStart, - 'per_page' => $perPage, + 'offset_start' => $options->getFirstResult(), + 'per_page' => $options->getMaxResults(), 'available_results' => $search_result->getAvailable(), 'total_results' => $search_result->getTotal(), 'error' => (string)$search_result->getError(), @@ -1154,6 +1132,30 @@ class V1Controller extends Controller return [$ret, $search_result]; } + /** + * @param Request $request + * @param RecordReferenceInterface[]|RecordReferenceCollection $records + * @return array + */ + public function listRecords(Request $request, $records) + { + if (!$records instanceof RecordReferenceCollection) { + $records = new RecordReferenceCollection($records); + } + + $technicalData = $this->app['service.technical_data']->fetchRecordsTechnicalData($records); + + $data = []; + + foreach ($records->toRecords($this->getApplicationBox()) as $index => $record) { + $record->setTechnicalDataSet($technicalData[$index]); + + $data[$index] = $this->listRecord($request, $record); + } + + return $data; + } + /** * Retrieve detailed information about one record * @@ -1197,6 +1199,27 @@ class V1Controller extends Controller return $data; } + /** + * @param Request $request + * @param RecordReferenceInterface[]|RecordReferenceCollection $stories + * @return array + * @throws \Exception + */ + public function listStories(Request $request, $stories) + { + if (!$stories instanceof RecordReferenceCollection) { + $stories = new RecordReferenceCollection($stories); + } + + $data = []; + + foreach ($stories->toRecords($this->getApplicationBox()) as $story) { + $data[] = $this->listStory($request, $story); + } + + return $data; + } + /** * Retrieve detailed information about one story * @@ -1211,10 +1234,6 @@ class V1Controller extends Controller return Result::createError($request, 404, 'Story not found')->createResponse(); } - $records = array_map(function (\record_adapter $record) use ($request) { - return $this->listRecord($request, $record); - }, array_values($story->get_children()->get_elements())); - $caption = $story->get_caption(); $format = function (\caption_record $caption, $dcField) { @@ -1256,7 +1275,7 @@ class V1Controller extends Controller 'dc:title' => $format($caption, \databox_Field_DCESAbstract::Title), 'dc:type' => $format($caption, \databox_Field_DCESAbstract::Type), ], - 'records' => $records, + 'records' => $this->listRecords($request, $story->getChildren()->get_elements()), ]; } @@ -1448,11 +1467,7 @@ class V1Controller extends Controller ]; }, iterator_to_array($basket->getValidation()->getParticipants())); - $expires_on_atom = $basket->getValidation()->getExpires(); - - if ($expires_on_atom instanceof \DateTime) { - $expires_on_atom = $expires_on_atom->format(DATE_ATOM); - } + $expires_on_atom = NullableDateTime::format($basket->getValidation()->getExpires()); $ret = array_merge([ 'validation_users' => $users, @@ -1530,7 +1545,7 @@ class V1Controller extends Controller $status = $request->get('status'); - $datas = strrev($record->get_status()); + $datas = strrev($record->getStatus()); if (!is_array($status)) { return $this->getBadRequestAction($request); @@ -1549,7 +1564,7 @@ class V1Controller extends Controller $datas = substr($datas, 0, ($n)) . $value . substr($datas, ($n + 2)); } - $record->set_binary_status(strrev($datas)); + $record->setStatus(strrev($datas)); // @todo Move event dispatch inside record_adapter class (keeps things encapsulated) $this->dispatch(PhraseaEvents::RECORD_EDIT, new RecordEdit($record)); @@ -2170,7 +2185,7 @@ class V1Controller extends Controller $story->removeChild($record); } - return $record->get_serialize_key(); + return $record->getId(); } public function addRecordsToStoryAction(Request $request, $databox_id, $story_id) @@ -2232,7 +2247,7 @@ class V1Controller extends Controller $this->dispatch(PhraseaEvents::RECORD_EDIT, new RecordEdit($story)); - return $record->get_serialize_key(); + return $record->getId(); } public function getCurrentUserAction(Request $request) diff --git a/lib/Alchemy/Phrasea/Controller/DatafileController.php b/lib/Alchemy/Phrasea/Controller/DatafileController.php index e76e6952cb..c6449111ae 100644 --- a/lib/Alchemy/Phrasea/Controller/DatafileController.php +++ b/lib/Alchemy/Phrasea/Controller/DatafileController.php @@ -68,7 +68,7 @@ class DatafileController extends AbstractDelivery $stamp = false; $watermark = !$this->acl->get($this->authentication->getUser()) - ->has_right_on_base($record->get_base_id(), 'nowatermark'); + ->has_right_on_base($record->getBaseId(), 'nowatermark'); if ($watermark && !$all_access) { $subdef_class = null; diff --git a/lib/Alchemy/Phrasea/Controller/PermalinkController.php b/lib/Alchemy/Phrasea/Controller/PermalinkController.php index 04aed5ec2c..6850439985 100644 --- a/lib/Alchemy/Phrasea/Controller/PermalinkController.php +++ b/lib/Alchemy/Phrasea/Controller/PermalinkController.php @@ -122,7 +122,7 @@ class PermalinkController extends AbstractDelivery $watermark = $stamp = false; if ($this->authentication->isAuthenticated()) { - $watermark = !$this->acl->get($this->authentication->getUser())->has_right_on_base($record->get_base_id(), 'nowatermark'); + $watermark = !$this->acl->get($this->authentication->getUser())->has_right_on_base($record->getBaseId(), 'nowatermark'); if ($watermark) { /** @var BasketElementRepository $repository */ @@ -138,7 +138,7 @@ class PermalinkController extends AbstractDelivery return $this->deliverContentWithCaptionLink($request, $record, $subdef, $watermark, $stamp, $token); } - $collection = \collection::getByBaseId($this->app, $record->get_base_id()); + $collection = \collection::getByBaseId($this->app, $record->getBaseId()); switch ($collection->get_pub_wm()) { default: case 'none': @@ -169,8 +169,8 @@ class PermalinkController extends AbstractDelivery $response = $this->deliverContent($request, $record, $subdef, $watermark, $stamp); $response->headers->set('Link', $this->app->url("permalinks_caption", [ - 'sbas_id' => $record->get_sbas_id(), - 'record_id' => $record->get_record_id(), + 'sbas_id' => $record->getDataboxId(), + 'record_id' => $record->getRecordId(), 'token' => $token, ])); diff --git a/lib/Alchemy/Phrasea/Controller/Prod/EditController.php b/lib/Alchemy/Phrasea/Controller/Prod/EditController.php index d935211685..d33284d5d2 100644 --- a/lib/Alchemy/Phrasea/Controller/Prod/EditController.php +++ b/lib/Alchemy/Phrasea/Controller/Prod/EditController.php @@ -20,7 +20,7 @@ use Alchemy\Phrasea\Model\Entities\Preset; use Alchemy\Phrasea\Model\Entities\User; use Alchemy\Phrasea\Model\Manipulator\PresetManipulator; use Alchemy\Phrasea\Model\Repositories\PresetRepository; -use Alchemy\Phrasea\Vocabulary\Controller as VocabularyController; +use Alchemy\Phrasea\Vocabulary\ControlProvider\ControlProviderInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; @@ -146,17 +146,17 @@ class EditController extends Controller foreach ($records as $record) { $indice = $record->getNumber(); $elements[$indice] = [ - 'bid' => $record->get_base_id(), - 'rid' => $record->get_record_id(), + 'bid' => $record->getBaseId(), + 'rid' => $record->getRecordId(), 'sselcont_id' => null, '_selected' => false, 'fields' => $databox_fields, ]; $elements[$indice]['statbits'] = []; - if ($this->getAclForUser()->has_right_on_base($record->get_base_id(), 'chgstatus')) { + if ($this->getAclForUser()->has_right_on_base($record->getBaseId(), 'chgstatus')) { foreach ($status as $n => $s) { - $tmp_val = substr(strrev($record->get_status()), $n, 1); + $tmp_val = substr(strrev($record->getStatus()), $n, 1); $elements[$indice]['statbits'][$n]['value'] = ($tmp_val == '1') ? '1' : '0'; $elements[$indice]['statbits'][$n]['dirty'] = false; } @@ -209,7 +209,7 @@ class EditController extends Controller ['record' => $record] ); - $elements[$indice]['type'] = $record->get_type(); + $elements[$indice]['type'] = $record->getType(); } } @@ -231,7 +231,7 @@ class EditController extends Controller } public function searchVocabularyAction(Request $request, $vocabulary) { - $datas = ['success' => false, 'message' => '', 'results' => []]; + $data = ['success' => false, 'message' => '', 'results' => []]; $sbas_id = (int) $request->query->get('sbas_id'); @@ -240,33 +240,33 @@ class EditController extends Controller throw new \Exception('Invalid sbas_id'); } - $VC = VocabularyController::get($this->app, $vocabulary); + /** @var ControlProviderInterface $vocabularyProvider */ + $vocabularyProvider = $this->app['vocabularies'][strtolower($vocabulary)]; $databox = $this->findDataboxById($sbas_id); } catch (\Exception $e) { - $datas['message'] = $this->app->trans('Vocabulary not found'); + $data['message'] = $this->app->trans('Vocabulary not found'); - return $this->app->json($datas); + return $this->app->json($data); } $query = $request->query->get('query'); - $results = $VC->find($query, $this->getAuthenticatedUser(), $databox); + $results = $vocabularyProvider->find($query, $this->getAuthenticatedUser(), $databox); $list = []; - foreach ($results as $Term) { - /* @var \Alchemy\Phrasea\Vocabulary\Term $Term */ + foreach ($results as $term) { $list[] = [ - 'id' => $Term->getId(), - 'context' => $Term->getContext(), - 'value' => $Term->getValue(), + 'id' => $term->getId(), + 'context' => $term->getContext(), + 'value' => $term->getValue(), ]; } - $datas['success'] = true; - $datas['results'] = $list; + $data['success'] = true; + $data['results'] = $list; - return $this->app->json($datas); + return $this->app->json($data); } public function applyAction(Request $request) { @@ -287,7 +287,7 @@ class EditController extends Controller try { $reg_record = $records->singleStory(); - $newsubdef_reg = new \record_adapter($this->app, $reg_record->get_sbas_id(), $request->request->get('newrepresent')); + $newsubdef_reg = new \record_adapter($this->app, $reg_record->getDataboxId(), $request->request->get('newrepresent')); foreach ($newsubdef_reg->get_subdefs() as $name => $value) { if (!in_array($name, ['thumbnail', 'preview'])) { @@ -300,7 +300,7 @@ class EditController extends Controller $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( + $this->getDataboxLogger($reg_record->getDatabox())->log( $reg_record, \Session_Logger::EVENT_SUBSTITUTE, $name == 'document' ? 'HD' : $name, @@ -325,7 +325,7 @@ class EditController extends Controller continue; } - $key = $record->get_serialize_key(); + $key = $record->getId(); if (!array_key_exists($key, $elements)) { continue; @@ -345,7 +345,7 @@ class EditController extends Controller $this->getDispatcher()->dispatch(PhraseaEvents::RECORD_EDIT, new RecordEdit($record)); } - $newstat = $record->get_status(); + $newstat = $record->getStatus(); $statbits = ltrim($statbits, 'x'); if (!in_array($statbits, ['', 'null'])) { $mask_and = ltrim(str_replace(['x', '0', '1', 'z'], ['1', 'z', '0', '1'], $statbits), '0'); @@ -359,13 +359,13 @@ class EditController extends Controller $newstat = \databox_status::operation_or($newstat, $mask_or); } - $record->set_binary_status($newstat); + $record->setStatus($newstat); } $record ->write_metas() - ->get_collection() - ->reset_stamp($record->get_record_id()); + ->getCollection() + ->reset_stamp($record->getRecordId()); if ($statbits != '') { $this->getDataboxLogger($databox) diff --git a/lib/Alchemy/Phrasea/Controller/Prod/FeedController.php b/lib/Alchemy/Phrasea/Controller/Prod/FeedController.php index 084c317aee..6c51b0fe5c 100644 --- a/lib/Alchemy/Phrasea/Controller/Prod/FeedController.php +++ b/lib/Alchemy/Phrasea/Controller/Prod/FeedController.php @@ -81,8 +81,8 @@ class FeedController extends Controller foreach ($publishing as $record) { $item = new FeedItem(); $item->setEntry($entry) - ->setRecordId($record->get_record_id()) - ->setSbasId($record->get_sbas_id()); + ->setRecordId($record->getRecordId()) + ->setSbasId($record->getDataboxId()); $entry->addItem($item); $manager->persist($item); } diff --git a/lib/Alchemy/Phrasea/Controller/Prod/LazaretController.php b/lib/Alchemy/Phrasea/Controller/Prod/LazaretController.php index c146c428b8..465687a060 100644 --- a/lib/Alchemy/Phrasea/Controller/Prod/LazaretController.php +++ b/lib/Alchemy/Phrasea/Controller/Prod/LazaretController.php @@ -206,7 +206,7 @@ class LazaretController extends Controller //Check if the chosen record is eligible to the substitution foreach ($lazaretFile->getRecordsToSubstitute($this->app) as $record) { - if ($record->get_record_id() !== (int) $recordId) { + if ($record->getRecordId() !== (int) $recordId) { continue; } @@ -230,7 +230,7 @@ class LazaretController extends Controller $record = $lazaretFile->getCollection($this->app)->get_databox()->get_record($recordId); $this->getSubDefinitionSubstituer() ->substitute($record, 'document', $media); - $this->getDataboxLogger($record->get_databox())->log( + $this->getDataboxLogger($record->getDatabox())->log( $record, \Session_Logger::EVENT_SUBSTITUTE, 'HD', diff --git a/lib/Alchemy/Phrasea/Controller/Prod/MoveCollectionController.php b/lib/Alchemy/Phrasea/Controller/Prod/MoveCollectionController.php index 74c8378e84..14833a2edb 100644 --- a/lib/Alchemy/Phrasea/Controller/Prod/MoveCollectionController.php +++ b/lib/Alchemy/Phrasea/Controller/Prod/MoveCollectionController.php @@ -70,8 +70,8 @@ class MoveCollectionController extends Controller if ($request->request->get("chg_coll_son") == "1") { /** @var \record_adapter $child */ - foreach ($record->get_children() as $child) { - if ($this->getAclForUser()->has_right_on_base($child->get_base_id(), 'candeleterecord')) { + foreach ($record->getChildren() as $child) { + if ($this->getAclForUser()->has_right_on_base($child->getBaseId(), 'candeleterecord')) { $child->move_to_collection($collection, $this->getApplicationBox()); } } diff --git a/lib/Alchemy/Phrasea/Controller/Prod/PrinterController.php b/lib/Alchemy/Phrasea/Controller/Prod/PrinterController.php index 968e6b23b9..d2bc03b190 100644 --- a/lib/Alchemy/Phrasea/Controller/Prod/PrinterController.php +++ b/lib/Alchemy/Phrasea/Controller/Prod/PrinterController.php @@ -34,7 +34,7 @@ class PrinterController extends Controller $layout = $request->request->get('lay'); foreach ($printer->get_elements() as $record) { - $this->getDataboxLogger($record->get_databox())->log($record, \Session_Logger::EVENT_PRINT, $layout, ''); + $this->getDataboxLogger($record->getDatabox())->log($record, \Session_Logger::EVENT_PRINT, $layout, ''); } $PDF = new PDFExport($this->app, $printer->get_elements(), $layout); diff --git a/lib/Alchemy/Phrasea/Controller/Prod/PropertyController.php b/lib/Alchemy/Phrasea/Controller/Prod/PropertyController.php index f368b24046..317aeb7cb7 100644 --- a/lib/Alchemy/Phrasea/Controller/Prod/PropertyController.php +++ b/lib/Alchemy/Phrasea/Controller/Prod/PropertyController.php @@ -87,17 +87,17 @@ class PropertyController extends Controller foreach ($records as $record) { //perform logic - $sbasId = $record->get_databox()->get_sbas_id(); + $sbasId = $record->getDataboxId(); if (!isset($recordsType[$sbasId])) { $recordsType[$sbasId] = []; } - if (!isset($recordsType[$sbasId][$record->get_type()])) { - $recordsType[$sbasId][$record->get_type()] = []; + if (!isset($recordsType[$sbasId][$record->getType()])) { + $recordsType[$sbasId][$record->getType()] = []; } - $recordsType[$sbasId][$record->get_type()][] = $record; + $recordsType[$sbasId][$record->getType()][] = $record; } return new Response($this->render('prod/actions/Property/type.html.twig', [ @@ -120,18 +120,18 @@ class PropertyController extends Controller $postStatus = (array) $request->request->get('status'); foreach ($records as $record) { - $sbasId = $record->get_databox()->get_sbas_id(); + $sbasId = $record->getDataboxId(); //update record if (null !== $updatedStatus = $this->updateRecordStatus($record, $postStatus)) { - $updated[$record->get_serialize_key()] = $updatedStatus; + $updated[$record->getId()] = $updatedStatus; } //update children if current record is a story if (isset($applyStatusToChildren[$sbasId]) && $record->isStory()) { - foreach ($record->get_children() as $child) { + foreach ($record->getChildren() as $child) { if (null !== $updatedStatus = $this->updateRecordStatus($child, $postStatus)) { - $updated[$record->get_serialize_key()] = $updatedStatus; + $updated[$record->getId()] = $updatedStatus; } } } @@ -156,17 +156,17 @@ class PropertyController extends Controller foreach ($records as $record) { try { - $recordType = !empty($forceType) ? $forceType : (isset($typeLst[$record->get_serialize_key()]) ? $typeLst[$record->get_serialize_key()] : null); - $mimeType = isset($mimeLst[$record->get_serialize_key()]) ? $mimeLst[$record->get_serialize_key()] : null; + $recordType = !empty($forceType) ? $forceType : (isset($typeLst[$record->getId()]) ? $typeLst[$record->getId()] : null); + $mimeType = isset($mimeLst[$record->getId()]) ? $mimeLst[$record->getId()] : null; if ($recordType) { - $record->set_type($recordType); - $updated[$record->get_serialize_key()]['record_type'] = $recordType; + $record->setType($recordType); + $updated[$record->getId()]['record_type'] = $recordType; } if ($mimeType) { - $record->set_mime($mimeType); - $updated[$record->get_serialize_key()]['mime_type'] = $mimeType; + $record->setMimeType($mimeType); + $updated[$record->getId()]['mime_type'] = $mimeType; } } catch (\Exception $e) { @@ -185,18 +185,18 @@ class PropertyController extends Controller */ private function updateRecordStatus(\record_adapter $record, array $postStatus) { - $sbasId = $record->get_databox()->get_sbas_id(); + $sbasId = $record->getDataboxId(); if (isset($postStatus[$sbasId]) && is_array($postStatus[$sbasId])) { $postStatus = $postStatus[$sbasId]; - $currentStatus = strrev($record->get_status()); + $currentStatus = strrev($record->getStatus()); $newStatus = ''; foreach (range(0, 31) as $i) { $newStatus .= isset($postStatus[$i]) ? ($postStatus[$i] ? '1' : '0') : $currentStatus[$i]; } - $record->set_binary_status(strrev($newStatus)); + $record->setStatus(strrev($newStatus)); return [ 'current_status' => $currentStatus, diff --git a/lib/Alchemy/Phrasea/Controller/Prod/PushController.php b/lib/Alchemy/Phrasea/Controller/Prod/PushController.php index e666350fa8..90abd15573 100644 --- a/lib/Alchemy/Phrasea/Controller/Prod/PushController.php +++ b/lib/Alchemy/Phrasea/Controller/Prod/PushController.php @@ -124,7 +124,7 @@ class PushController extends Controller ); } - $this->getDataboxLogger($element->get_databox())->log( + $this->getDataboxLogger($element->getDatabox())->log( $element, \Session_Logger::EVENT_VALIDATE, $user_receiver->getId(), @@ -322,7 +322,7 @@ class PushController extends Controller $manager->merge($basketElement); $manager->persist($validationData); - $this->getDataboxLogger($basketElement->getRecord($this->app)->get_databox())->log( + $this->getDataboxLogger($basketElement->getRecord($this->app)->getDatabox())->log( $basketElement->getRecord($this->app), \Session_Logger::EVENT_PUSH, $participantUser->getId(), diff --git a/lib/Alchemy/Phrasea/Controller/Prod/QueryController.php b/lib/Alchemy/Phrasea/Controller/Prod/QueryController.php index 839fa0f2dc..154ca2389c 100644 --- a/lib/Alchemy/Phrasea/Controller/Prod/QueryController.php +++ b/lib/Alchemy/Phrasea/Controller/Prod/QueryController.php @@ -43,8 +43,6 @@ class QueryController extends Controller $options = SearchEngineOptions::fromRequest($this->app, $request); - $form = $options->serialize(); - $perPage = (int) $this->getSettings()->getUserSetting($this->getAuthenticatedUser(), 'images_per_page'); $page = (int) $request->request->get('pag'); @@ -56,13 +54,15 @@ class QueryController extends Controller $page = 1; } + $options->setFirstResult(($page - 1) * $perPage); + $options->setMaxResults($perPage); + $user = $this->getAuthenticatedUser(); $userManipulator = $this->getUserManipulator(); $userManipulator->logQuery($user, $query); try { - /** @var SearchEngineResult $result */ - $result = $engine->query($query, (($page - 1) * $perPage), $perPage, $options); + $result = $engine->query($query, $options); if ($this->getSettings()->getUserSetting($user, 'start_page') === 'LAST_QUERY') { $userManipulator->setUserSetting($user, 'start_page_query', $query); @@ -216,7 +216,7 @@ class QueryController extends Controller $json['total_answers'] = (int) $result->getAvailable(); $json['next_page'] = ($page < $npages && $result->getAvailable() > 0) ? ($page + 1) : false; $json['prev_page'] = ($page > 1 && $result->getAvailable() > 0) ? ($page - 1) : false; - $json['form'] = $form; + $json['form'] = $options->serialize(); } catch(\Exception $e) { // we'd like a message from the parser so get all the exceptions messages diff --git a/lib/Alchemy/Phrasea/Controller/Prod/RecordController.php b/lib/Alchemy/Phrasea/Controller/Prod/RecordController.php index cc8f86c7e9..d378691b84 100644 --- a/lib/Alchemy/Phrasea/Controller/Prod/RecordController.php +++ b/lib/Alchemy/Phrasea/Controller/Prod/RecordController.php @@ -106,8 +106,8 @@ class RecordController extends Controller ]), "pos" => $record->getNumber(), "title" => str_replace(array('[[em]]', '[[/em]]'), array('', ''), $record->get_title($query, $searchEngine)), - "collection_name" => $record->get_collection()->get_name(), - "collection_logo" => $record->get_collection()->getLogo($record->get_base_id(), $this->app), + "collection_name" => $record->getCollection()->get_name(), + "collection_logo" => $record->getCollection()->getLogo($record->getBaseId(), $this->app), ]); } @@ -119,7 +119,8 @@ class RecordController extends Controller */ public function doDeleteRecords(Request $request) { - $records = RecordsRequest::fromRequest($this->app, $request, !!$request->request->get('del_children'), [ + $flatten = (bool)($request->request->get('del_children')) ? RecordsRequest::FLATTEN_YES_PRESERVE_STORIES : RecordsRequest::FLATTEN_NO; + $records = RecordsRequest::fromRequest($this->app, $request, $flatten, [ 'candeleterecord' ]); @@ -135,7 +136,7 @@ class RecordController extends Controller foreach ($basketElements as $element) { $manager->remove($element); - $deleted[] = $element->getRecord($this->app)->get_serialize_key(); + $deleted[] = $element->getRecord($this->app)->getId(); } $attachedStories = $StoryWZRepository->findByRecord($this->app, $record); @@ -144,7 +145,7 @@ class RecordController extends Controller $manager->remove($attachedStory); } - $deleted[] = $record->get_serialize_key(); + $deleted[] = $record->getId(); $record->delete(); } catch (\Exception $e) { @@ -186,7 +187,7 @@ class RecordController extends Controller $renewed = []; foreach ($records as $record) { - $renewed[$record->get_serialize_key()] = (string) $record->get_preview()->renew_url(); + $renewed[$record->getId()] = (string) $record->get_preview()->renew_url(); }; return $this->app->json($renewed); diff --git a/lib/Alchemy/Phrasea/Controller/Prod/StoryController.php b/lib/Alchemy/Phrasea/Controller/Prod/StoryController.php index e8efc59e47..39ffb95bf9 100644 --- a/lib/Alchemy/Phrasea/Controller/Prod/StoryController.php +++ b/lib/Alchemy/Phrasea/Controller/Prod/StoryController.php @@ -84,8 +84,8 @@ class StoryController extends Controller 'message' => $this->app->trans('Story created'), 'WorkZone' => $storyWZ->getId(), 'story' => [ - 'sbas_id' => $story->get_sbas_id(), - 'record_id' => $story->get_record_id(), + 'sbas_id' => $story->getDataboxId(), + 'record_id' => $story->getRecordId(), ], ]; @@ -109,7 +109,7 @@ class StoryController extends Controller { $Story = new \record_adapter($this->app, $sbas_id, $record_id); - if (!$this->getAclForUser()->has_right_on_base($Story->get_base_id(), 'canmodifrecord')) { + if (!$this->getAclForUser()->has_right_on_base($Story->getBaseId(), 'canmodifrecord')) { throw new AccessDeniedHttpException('You can not add document to this Story'); } @@ -145,7 +145,7 @@ class StoryController extends Controller $story = new \record_adapter($this->app, $sbas_id, $record_id); $record = new \record_adapter($this->app, $child_sbas_id, $child_record_id); - if (!$this->getAclForUser()->has_right_on_base($story->get_base_id(), 'canmodifrecord')) { + if (!$this->getAclForUser()->has_right_on_base($story->getBaseId(), 'canmodifrecord')) { throw new AccessDeniedHttpException('You can not add document to this Story'); } @@ -188,17 +188,17 @@ class StoryController extends Controller throw new \Exception('This is not a story'); } - if (!$this->getAclForUser()->has_right_on_base($story->get_base_id(), 'canmodifrecord')) { + if (!$this->getAclForUser()->has_right_on_base($story->getBaseId(), 'canmodifrecord')) { throw new ControllerException($this->app->trans('You can not edit this story')); } $sql = 'UPDATE regroup SET ord = :ord WHERE rid_parent = :parent_id AND rid_child = :children_id'; - $stmt = $story->get_databox()->get_connection()->prepare($sql); + $stmt = $story->getDatabox()->get_connection()->prepare($sql); foreach ($request->request->get('element') as $record_id => $ord) { $params = [ ':ord' => $ord, - ':parent_id' => $story->get_record_id(), + ':parent_id' => $story->getRecordId(), ':children_id' => $record_id ]; $stmt->execute($params); diff --git a/lib/Alchemy/Phrasea/Controller/Prod/ToolsController.php b/lib/Alchemy/Phrasea/Controller/Prod/ToolsController.php index 4579c8a676..da497eb9ec 100644 --- a/lib/Alchemy/Phrasea/Controller/Prod/ToolsController.php +++ b/lib/Alchemy/Phrasea/Controller/Prod/ToolsController.php @@ -68,7 +68,7 @@ class ToolsController extends Controller continue; } $label = $this->app->trans('prod::tools: document'); - } elseif (isset($databoxSubdefs[$subdefName])) { + } elseif ($databoxSubdefs->hasSubdef($subdefName)) { if (!$acl->has_access_to_subdef($record, $subdefName)) { continue; } @@ -202,7 +202,7 @@ class ToolsController extends Controller $record->insertTechnicalDatas($this->getMediaVorus()); $this->getMetadataSetter()->replaceMetadata($this->getMetadataReader() ->read($media), $record); - $this->getDataboxLogger($record->get_databox()) + $this->getDataboxLogger($record->getDatabox()) ->log($record, \Session_Logger::EVENT_SUBSTITUTE, 'HD', '' ); if ((int) $request->request->get('ccfilename') === 1) { @@ -259,7 +259,7 @@ class ToolsController extends Controller $media = $this->app->getMediaFromUri($tempoFile); $this->getSubDefinitionSubstituer()->substitute($record, 'thumbnail', $media); - $this->getDataboxLogger($record->get_databox()) + $this->getDataboxLogger($record->getDatabox()) ->log($record, \Session_Logger::EVENT_SUBSTITUTE, 'thumbnail', ''); unlink($tempoFile); @@ -415,7 +415,7 @@ class ToolsController extends Controller { $dataUri = Parser::parse($subDefDataUri); - $name = sprintf('extractor_thumb_%s', $record->get_serialize_key()); + $name = sprintf('extractor_thumb_%s', $record->getId()); $fileName = sprintf('%s/%s.png', sys_get_temp_dir(), $name); file_put_contents($fileName, $dataUri->getData()); @@ -423,7 +423,7 @@ class ToolsController extends Controller $media = $this->app->getMediaFromUri($fileName); $this->getSubDefinitionSubstituer()->substitute($record, $subDefName, $media); - $this->getDataboxLogger($record->get_databox()) + $this->getDataboxLogger($record->getDatabox()) ->log($record, \Session_Logger::EVENT_SUBSTITUTE, $subDefName, ''); unset($media); diff --git a/lib/Alchemy/Phrasea/Controller/Prod/UploadController.php b/lib/Alchemy/Phrasea/Controller/Prod/UploadController.php index 1c04bfd251..d800aacdb9 100644 --- a/lib/Alchemy/Phrasea/Controller/Prod/UploadController.php +++ b/lib/Alchemy/Phrasea/Controller/Prod/UploadController.php @@ -178,7 +178,7 @@ class UploadController extends Controller } if ($elementCreated instanceof \record_adapter) { - $id = $elementCreated->get_serialize_key(); + $id = $elementCreated->getId(); $element = 'record'; $message = $this->app->trans('The record was successfully created'); @@ -194,7 +194,7 @@ class UploadController extends Controller $media = $this->app->getMediaFromUri($fileName); $this->getSubDefinitionSubstituer()->substitute($elementCreated, 'thumbnail', $media); - $this->getDataboxLogger($elementCreated->get_databox()) + $this->getDataboxLogger($elementCreated->getDatabox()) ->log($elementCreated, \Session_Logger::EVENT_SUBSTITUTE, 'thumbnail', ''); unset($media); diff --git a/lib/Alchemy/Phrasea/Controller/Prod/WorkzoneController.php b/lib/Alchemy/Phrasea/Controller/Prod/WorkzoneController.php index 696c47b3d1..98b87bd732 100644 --- a/lib/Alchemy/Phrasea/Controller/Prod/WorkzoneController.php +++ b/lib/Alchemy/Phrasea/Controller/Prod/WorkzoneController.php @@ -100,7 +100,7 @@ class WorkzoneController extends Controller throw new \Exception('You can only attach stories'); } - if (!$acl->has_access_to_base($story->get_base_id())) { + if (!$acl->has_access_to_base($story->getBaseId())) { throw new AccessDeniedHttpException('You do not have access to this Story'); } diff --git a/lib/Alchemy/Phrasea/Controller/RecordsRequest.php b/lib/Alchemy/Phrasea/Controller/RecordsRequest.php index 1e255c41b2..7768a30b15 100644 --- a/lib/Alchemy/Phrasea/Controller/RecordsRequest.php +++ b/lib/Alchemy/Phrasea/Controller/RecordsRequest.php @@ -14,6 +14,7 @@ namespace Alchemy\Phrasea\Controller; use Alchemy\Phrasea\Model\Entities\Basket; use Doctrine\Common\Collections\ArrayCollection; use Alchemy\Phrasea\Application; +use record_adapter; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; @@ -46,13 +47,14 @@ class RecordsRequest extends ArrayCollection if (self::FLATTEN_NO !== $flatten) { $to_remove = []; + /** @var record_adapter $record */ foreach ($this as $key => $record) { if ($record->isStory()) { if (self::FLATTEN_YES === $flatten) { $to_remove[] = $key; } - foreach ($record->get_children() as $child) { - $this->set($child->get_serialize_key(), $child); + foreach ($record->getChildren() as $child) { + $this->set($child->getId(), $child); } } } @@ -180,7 +182,7 @@ class RecordsRequest extends ArrayCollection public function serializedList() { if ($this->isSingleStory()) { - return $this->singleStory()->get_serialize_key(); + return $this->singleStory()->getId(); } $basrec = []; @@ -211,7 +213,7 @@ class RecordsRequest extends ArrayCollection $app['acl.basket']->hasAccess($basket, $app->getAuthenticatedUser()); foreach ($basket->getElements() as $basket_element) { - $received[$basket_element->getRecord($app)->get_serialize_key()] = $basket_element->getRecord($app); + $received[$basket_element->getRecord($app)->getId()] = $basket_element->getRecord($app); } } elseif ($request->get('story')) { $repository = $app['repo.story-wz']; @@ -230,7 +232,7 @@ class RecordsRequest extends ArrayCollection } try { $record = new \record_adapter($app, (int) $basrec[0], (int) $basrec[1]); - $received[$record->get_serialize_key()] = $record; + $received[$record->getId()] = $record; unset($record); } catch (NotFoundHttpException $e) { continue; diff --git a/lib/Alchemy/Phrasea/Controller/Report/InformationController.php b/lib/Alchemy/Phrasea/Controller/Report/InformationController.php index 4b9bf720f1..6868292743 100644 --- a/lib/Alchemy/Phrasea/Controller/Report/InformationController.php +++ b/lib/Alchemy/Phrasea/Controller/Report/InformationController.php @@ -325,8 +325,8 @@ class InformationController extends Controller /** @var \record_adapter $record */ $reportArray = $what->buildTabUserWhat( - $record->get_base_id(), - $record->get_record_id(), + $record->getBaseId(), + $record->getRecordId(), $config ); @@ -393,7 +393,7 @@ class InformationController extends Controller } } - $filter->addfilter('record_id', '=', $record->get_record_id()); + $filter->addfilter('record_id', '=', $record->getRecordId()); $download->setFilter($filter->getTabFilter()); $download->setOrder('ddate', 'DESC'); diff --git a/lib/Alchemy/Phrasea/ControllerProvider/Admin/Fields.php b/lib/Alchemy/Phrasea/ControllerProvider/Admin/Fields.php index 155fbe132c..b6f111a37a 100644 --- a/lib/Alchemy/Phrasea/ControllerProvider/Admin/Fields.php +++ b/lib/Alchemy/Phrasea/ControllerProvider/Admin/Fields.php @@ -14,6 +14,7 @@ namespace Alchemy\Phrasea\ControllerProvider\Admin; use Alchemy\Phrasea\Application as PhraseaApplication; use Alchemy\Phrasea\Controller\Admin\FieldsController; use Alchemy\Phrasea\ControllerProvider\ControllerProviderTrait; +use Alchemy\Phrasea\Vocabulary\ControlProvider\UserProvider; use Silex\Application; use Silex\ControllerProviderInterface; use Silex\ServiceProviderInterface; @@ -24,6 +25,15 @@ class Fields implements ControllerProviderInterface, ServiceProviderInterface public function register(Application $app) { + $app['vocabularies'] = $app->share(function (PhraseaApplication $app) { + $vocabularies = new \Pimple(); + + $vocabularies['user'] = $vocabularies->share(function () use ($app) { + return new UserProvider($app); + }); + + return $vocabularies; + }); $app['controller.admin.fields'] = $app->share(function (PhraseaApplication $app) { return new FieldsController($app); }); diff --git a/lib/Alchemy/Phrasea/ControllerProvider/Admin/SearchEngine.php b/lib/Alchemy/Phrasea/ControllerProvider/Admin/SearchEngine.php index b2da35b10b..139e133d09 100644 --- a/lib/Alchemy/Phrasea/ControllerProvider/Admin/SearchEngine.php +++ b/lib/Alchemy/Phrasea/ControllerProvider/Admin/SearchEngine.php @@ -37,9 +37,15 @@ class SearchEngine implements ControllerProviderInterface, ServiceProviderInterf /** @var ControllerCollection $controllers */ $controllers = $app['controllers_factory']; + $controllers->post('/drop_index', 'controller.admin.search-engine:dropIndexAction') + ->bind("admin_searchengine_drop_index"); + + $controllers->post('/create_index', 'controller.admin.search-engine:createIndexAction') + ->bind("admin_searchengine_create_index"); + $controllers->match('/', 'controller.admin.search-engine:formConfigurationPanelAction') - ->method('GET|POST') - ->bind('admin_searchengine_form'); + ->method('GET|POST') + ->bind('admin_searchengine_form'); return $controllers; } diff --git a/lib/Alchemy/Phrasea/ControllerProvider/Api/V2.php b/lib/Alchemy/Phrasea/ControllerProvider/Api/V2.php index f878b46b17..f5ff897179 100644 --- a/lib/Alchemy/Phrasea/ControllerProvider/Api/V2.php +++ b/lib/Alchemy/Phrasea/ControllerProvider/Api/V2.php @@ -13,8 +13,10 @@ use Alchemy\Phrasea\Application as PhraseaApplication; use Alchemy\Phrasea\Controller\Api\BasketController; use Alchemy\Phrasea\Controller\Api\LazaretController; use Alchemy\Phrasea\Controller\Api\SearchController; +use Alchemy\Phrasea\Controller\LazyLocator; use Alchemy\Phrasea\ControllerProvider\ControllerProviderTrait; use Alchemy\Phrasea\Core\Event\Listener\OAuthListener; +use Alchemy\Phrasea\Order\Controller\ApiOrderController; use Silex\Application; use Silex\Controller; use Silex\ControllerProviderInterface; @@ -48,6 +50,15 @@ class V2 implements ControllerProviderInterface, ServiceProviderInterface return new SearchController($app); } ); + + $app['controller.api.v2.orders'] = $app->share( + function (PhraseaApplication $app) { + return (new ApiOrderController($app)) + ->setDispatcher($app['dispatcher']) + ->setEntityManagerLocator(new LazyLocator($app, 'orm.em')) + ->setJsonBodyHelper($app['json.body_helper']); + } + ); } public function boot(Application $app) @@ -89,6 +100,22 @@ class V2 implements ControllerProviderInterface, ServiceProviderInterface ->bind('api_v2_quarantine_item_add'); $this->addQuarantineMiddleware($controller); + $controllers->post('/orders/', 'controller.api.v2.orders:createAction') + ->bind('api_v2_orders_create'); + $controllers->get('/orders/', 'controller.api.v2.orders:indexAction') + ->bind('api_v2_orders_index'); + $controllers->get('/orders/{orderId}', 'controller.api.v2.orders:showAction') + ->assert('orderId', '\d+') + ->bind('api_v2_orders_show'); + + $controllers->post('/orders/{orderId}/accept', 'controller.api.v2.orders:acceptElementsAction') + ->assert('orderId', '\d+') + ->bind('api_v2_orders_accept'); + + $controllers->post('/orders/{orderId}/deny', 'controller.api.v2.orders:denyElementsAction') + ->assert('orderId', '\d+') + ->bind('api_v2_orders_deny'); + return $controllers; } diff --git a/lib/Alchemy/Phrasea/ControllerProvider/MediaAccessor.php b/lib/Alchemy/Phrasea/ControllerProvider/MediaAccessor.php index a9987bf7e5..44b7fe658f 100644 --- a/lib/Alchemy/Phrasea/ControllerProvider/MediaAccessor.php +++ b/lib/Alchemy/Phrasea/ControllerProvider/MediaAccessor.php @@ -10,6 +10,7 @@ namespace Alchemy\Phrasea\ControllerProvider; use Alchemy\Phrasea\Controller\MediaAccessorController; +use Alchemy\Phrasea\Media\MediaSubDefinitionUrlGenerator; use Alchemy\Phrasea\Model\Entities\Secret; use Alchemy\Phrasea\Model\Provider\DefaultSecretProvider; use Doctrine\ORM\EntityManagerInterface; @@ -33,6 +34,13 @@ class MediaAccessor implements ServiceProviderInterface, ControllerProviderInter return new DefaultSecretProvider($app['repo.secrets'], $app['random.medium']); }); + $app['media_accessor.subdef_url_generator'] = $app->share(function (Application $app) { + $defaultTTL = (int)$app['conf']->get(['registry', 'general', 'default-subdef-url-ttl'], 0); + + return new MediaSubDefinitionUrlGenerator($app['url_generator'], $app['provider.secrets'], $defaultTTL); + }); + + $app['controller.media_accessor'] = $app->share(function (Application $app) { return (new MediaAccessorController($app)) ->setAllowedAlgorithms(['HS256']) diff --git a/lib/Alchemy/Phrasea/ControllerProvider/Permalink.php b/lib/Alchemy/Phrasea/ControllerProvider/Permalink.php index 619c7526c9..b6339e786d 100644 --- a/lib/Alchemy/Phrasea/ControllerProvider/Permalink.php +++ b/lib/Alchemy/Phrasea/ControllerProvider/Permalink.php @@ -14,6 +14,7 @@ namespace Alchemy\Phrasea\ControllerProvider; use Alchemy\Phrasea\Application as PhraseaApplication; use Alchemy\Phrasea\Controller\LazyLocator; use Alchemy\Phrasea\Controller\PermalinkController; +use Alchemy\Phrasea\Core\Event\Listener\OAuthListener; use Silex\Application; use Silex\ControllerCollection; use Silex\ControllerProviderInterface; @@ -69,6 +70,7 @@ class Permalink implements ControllerProviderInterface, ServiceProviderInterface ->bind('permalinks_permaview_old'); $controllers->get('/v1/{sbas_id}/{record_id}/{subdef}/{label}', 'controller.permalink:deliverPermalink') + ->before(new OAuthListener(['exit_not_present' => false])) ->bind('permalinks_permalink'); $controllers->match('/v1/{sbas_id}/{record_id}/{subdef}/{label}', 'controller.permalink:getOptionsResponse') diff --git a/lib/Alchemy/Phrasea/Core/Configuration/Configuration.php b/lib/Alchemy/Phrasea/Core/Configuration/Configuration.php index d62fa8d52d..6457dc2187 100644 --- a/lib/Alchemy/Phrasea/Core/Configuration/Configuration.php +++ b/lib/Alchemy/Phrasea/Core/Configuration/Configuration.php @@ -212,6 +212,9 @@ class Configuration implements ConfigurationInterface private function writeCacheConfig($content) { $this->dumpFile($this->compiled, $content, 0600); + if(function_exists("opcache_invalidate")) { + opcache_invalidate($this->compiled); + } } private function isConfigFresh() diff --git a/lib/Alchemy/Phrasea/Core/Connection/ConnectionPoolManager.php b/lib/Alchemy/Phrasea/Core/Connection/ConnectionPoolManager.php index 79cfa88a64..4953f85f3d 100644 --- a/lib/Alchemy/Phrasea/Core/Connection/ConnectionPoolManager.php +++ b/lib/Alchemy/Phrasea/Core/Connection/ConnectionPoolManager.php @@ -1,9 +1,8 @@ closeAll(); - $this->connections = []; } public function closeAll() { - foreach ($this->connections as $key => $conn) { + foreach ($this->connections as $conn) { $conn->close(); } } @@ -88,4 +84,9 @@ class ConnectionPoolManager return $this->connections[$key] = DriverManager::getConnection($params); } + + public function all() + { + return $this->connections; + } } diff --git a/lib/Alchemy/Phrasea/Core/Event/Subscriber/BridgeSubscriber.php b/lib/Alchemy/Phrasea/Core/Event/Subscriber/BridgeSubscriber.php index 1e39bf68d7..0de92461df 100644 --- a/lib/Alchemy/Phrasea/Core/Event/Subscriber/BridgeSubscriber.php +++ b/lib/Alchemy/Phrasea/Core/Event/Subscriber/BridgeSubscriber.php @@ -26,8 +26,8 @@ class BridgeSubscriber extends AbstractNotificationSubscriber 'usr_id' => $user->getId(), 'reason' => $event->getReason(), 'account_id' => $account->get_id(), - 'sbas_id' => $event->getElement()->get_record()->get_sbas_id(), - 'record_id' => $event->getElement()->get_record()->get_record_id(), + 'sbas_id' => $event->getElement()->get_record()->getDataboxId(), + 'record_id' => $event->getElement()->get_record()->getRecordId(), ]; $datas = json_encode($params); diff --git a/lib/Alchemy/Phrasea/Core/Event/Subscriber/DoctrineQueriesLoggerSubscriber.php b/lib/Alchemy/Phrasea/Core/Event/Subscriber/DoctrineQueriesLoggerSubscriber.php deleted file mode 100644 index eadbaf0bf3..0000000000 --- a/lib/Alchemy/Phrasea/Core/Event/Subscriber/DoctrineQueriesLoggerSubscriber.php +++ /dev/null @@ -1,53 +0,0 @@ -app = $app; - } - - public static function getSubscribedEvents() - { - return [ - KernelEvents::RESPONSE => [ - ['logQueries', -255], - ], - ]; - } - - public function logQueries(GetResponseEvent $event) - { - if (Application::ENV_DEV !== $this->app->getEnvironment()) { - return; - } - - foreach ($this->app['orm.query.logger']->queries as $query ) { - $this->app['orm.sql-logger']->debug($query['sql'], array( - 'params' => $query['params'], - 'types' => $query['types'], - 'time' => $query['executionMS'] - )); - } - - } -} diff --git a/lib/Alchemy/Phrasea/Core/MetaProvider/DatabaseMetaProvider.php b/lib/Alchemy/Phrasea/Core/MetaProvider/DatabaseMetaProvider.php index 84ecea5311..8ce614c119 100644 --- a/lib/Alchemy/Phrasea/Core/MetaProvider/DatabaseMetaProvider.php +++ b/lib/Alchemy/Phrasea/Core/MetaProvider/DatabaseMetaProvider.php @@ -1,4 +1,12 @@ share($app->extend('dbs.config', function ($configs, $app) { - if (! $app->isDebug()) { + if (!isset($app['dbal.config.register.loggers'])) { return $configs; } - foreach($configs->keys() as $service) { - $app['dbal.config.register.loggers']($configs[$service]); + $loggerRegisterCallable = $app['dbal.config.register.loggers']; + + foreach ($configs->keys() as $service) { + $loggerRegisterCallable($configs[$service], $service); } return $configs; @@ -56,7 +67,7 @@ class DatabaseMetaProvider implements ServiceProviderInterface { // 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) { + $app['orm.cache.configurer'] = $app->protect(function ($name, Configuration $config, $options) use ($app) { /** @var Manager $service */ $service = $app['phraseanet.cache-service']; @@ -74,7 +85,7 @@ class DatabaseMetaProvider implements ServiceProviderInterface ); }); - $app['orm.proxies_dir'] = $app['root.path'].'/resources/proxies'; + $app['orm.proxies_dir'] = $app['root.path'] . '/resources/proxies'; $app['orm.auto_generate_proxies'] = $app['debug']; $app['orm.proxies_namespace'] = 'Alchemy\Phrasea\Model\Proxies'; diff --git a/lib/Alchemy/Phrasea/Core/Provider/ORMServiceProvider.php b/lib/Alchemy/Phrasea/Core/Provider/ORMServiceProvider.php index 91e4544cbd..77a2a7a9f0 100644 --- a/lib/Alchemy/Phrasea/Core/Provider/ORMServiceProvider.php +++ b/lib/Alchemy/Phrasea/Core/Provider/ORMServiceProvider.php @@ -22,7 +22,6 @@ use Alchemy\Phrasea\Model\NativeQueryProvider; use Doctrine\Common\Annotations\AnnotationReader; use Doctrine\Common\Annotations\CachedReader; use Doctrine\Common\Cache\ArrayCache; -use Doctrine\DBAL\Logging\DebugStack; use Doctrine\DBAL\Types\Type; use Doctrine\ORM\Configuration as ORMConfig; use Doctrine\ORM\EntityManager; @@ -37,10 +36,11 @@ class ORMServiceProvider implements ServiceProviderInterface public function register(Application $app) { // Provides DSN string using database information - $app['db.dsn'] = $app->protect(function(array $params) use ($app) { + $app['db.dsn'] = $app->protect(function (array $params) use ($app) { $params = $app['db.info']($params); - switch ($params['driver']) { + switch ($params['driver']) + { case 'pdo_mysql': return sprintf('%s://%s:%s@%s:%s/%s', $params['driver'], @@ -61,22 +61,22 @@ class ORMServiceProvider implements ServiceProviderInterface }); // Hash a DSN string - $app['hash.dsn'] = $app->protect(function($dsn) { + $app['hash.dsn'] = $app->protect(function ($dsn) { return md5($dsn); }); // Return database test configuration - $app['db.test.info'] = $app->share(function() use ($app) { + $app['db.test.info'] = $app->share(function () use ($app) { return $app['conf']->get(['main', 'database-test'], array()); }); // Return application box database configuration - $app['db.appbox.info'] = $app->share(function() use ($app) { + $app['db.appbox.info'] = $app->share(function () use ($app) { return $app['conf']->get(['main', 'database'], array()); }); // Return database fixture configuration - $app['db.fixture.info'] = $app->share(function() use ($app) { + $app['db.fixture.info'] = $app->share(function () use ($app) { return [ 'driver' => 'pdo_sqlite', 'path' => sprintf('%s/%s', $app['tmp.path'], 'db-ref.sqlite'), @@ -85,7 +85,7 @@ class ORMServiceProvider implements ServiceProviderInterface }); // Return databox database configuration - $app['db.databox.info'] = $app->share(function() use ($app) { + $app['db.databox.info'] = $app->share(function () use ($app) { if (false === $app['phraseanet.configuration']->isSetup()) { return array(); } @@ -101,46 +101,46 @@ class ORMServiceProvider implements ServiceProviderInterface }); // Return unique key for fixture database - $app['db.fixture.hash.key'] = $app->share(function() use ($app) { + $app['db.fixture.hash.key'] = $app->share(function () use ($app) { $info = $app['db.fixture.info']; return $app['hash.dsn']($app['db.dsn']($info)); }); // Return unique key for test database - $app['db.test.hash.key'] = $app->share(function() use ($app) { + $app['db.test.hash.key'] = $app->share(function () use ($app) { $info = $app['db.test.info']; return $app['hash.dsn']($app['db.dsn']($info)); }); // Return unique for appbox database - $app['db.appbox.hash.key'] = $app->share(function() use ($app) { + $app['db.appbox.hash.key'] = $app->share(function () use ($app) { $info = $app['db.appbox.info']; return $app['hash.dsn']($app['db.dsn']($info)); }); // Return configuration option for test database in DoctrineServiceProvider - $app['db.test.options'] = $app->share(function() use ($app) { + $app['db.test.options'] = $app->share(function () use ($app) { return array($app['db.test.hash.key'] => $app['db.test.info']); }); // Return configuration option for test database in DoctrineServiceProvider - $app['db.fixture.options'] = $app->share(function() use ($app) { + $app['db.fixture.options'] = $app->share(function () use ($app) { return array($app['db.fixture.hash.key'] => $app['db.fixture.info']); }); // Return configuration option for appbox database in DoctrineServiceProvider - $app['db.appbox.options'] = $app->share(function() use ($app) { + $app['db.appbox.options'] = $app->share(function () use ($app) { return array($app['db.appbox.hash.key'] => $app['db.appbox.info']); }); // Return configuration option for databox databases in DoctrineServiceProvider - $app['dbs.databox.options'] = $app->share(function() use ($app) { + $app['dbs.databox.options'] = $app->share(function () use ($app) { $options = array(); - foreach($app['db.databox.info'] as $info) { + foreach ($app['db.databox.info'] as $info) { $info = $app['db.info']($info); $key = $app['hash.dsn']($app['db.dsn']($info)); @@ -153,7 +153,7 @@ class ORMServiceProvider implements ServiceProviderInterface // Return DoctrineServiceProvider database options, it merges all previous // set database configuration - $app['dbs.options'] = $app->share(function() use ($app) { + $app['dbs.options'] = $app->share(function () use ($app) { if (false === $app['phraseanet.configuration']->isSetup()) { return []; } @@ -167,7 +167,7 @@ class ORMServiceProvider implements ServiceProviderInterface }); // Return DoctrineORMServiceProvider information for a database from its parameters - $app['orm.em.options.from_info'] = $app->protect(function(array $info) use ($app) { + $app['orm.em.options.from_info'] = $app->protect(function (array $info) use ($app) { $info = $app['db.info']($info); $key = $app['hash.dsn']($app['db.dsn']($info)); @@ -176,7 +176,7 @@ class ORMServiceProvider implements ServiceProviderInterface }); //Return DoctrineServiceProvider information for a database from its parameters - $app['db.options.from_info'] = $app->protect(function(array $info) use ($app) { + $app['db.options.from_info'] = $app->protect(function (array $info) use ($app) { $info = $app['db.info']($info); $key = $app['hash.dsn']($app['db.dsn']($info)); @@ -188,7 +188,7 @@ class ORMServiceProvider implements ServiceProviderInterface * Add orm on the fly, used only when a new databox is mounted. * This allow to use new EM instance right after the database is mounted. */ - $app['orm.add'] = $app->protect(function($info) use ($app) { + $app['orm.add'] = $app->protect(function ($info) use ($app) { $info = $app['db.info']($info); $key = $app['hash.dsn']($app['db.dsn']($info)); @@ -200,7 +200,7 @@ class ORMServiceProvider implements ServiceProviderInterface $app['dbs.config'][$key] = new Configuration(); $app['dbs'][$key] = $app['dbs']->share(function () use ($app, $info, $key) { - return DriverManager::getConnection($info,$app['dbs.config'][$key] ,$app['dbs.event_manager'][$key]); + return DriverManager::getConnection($info, $app['dbs.config'][$key], $app['dbs.event_manager'][$key]); }); $options = $app['orm.options']($key); @@ -230,7 +230,7 @@ class ORMServiceProvider implements ServiceProviderInterface return new \SplObjectStorage(); }); - $app['dbal.evm.register.listeners'] = $app->protect(function(EventManager $evm) use ($app) { + $app['dbal.evm.register.listeners'] = $app->protect(function (EventManager $evm) use ($app) { $evm->addEventSubscriber(new TimestampableListener()); /** @var \SplObjectStorage $listeners */ $listeners = $app['dbal.evm.listeners']; @@ -239,23 +239,17 @@ class ORMServiceProvider implements ServiceProviderInterface } }); - $app['dbal.config.register.loggers'] = $app->protect(function(Configuration $config) use ($app) { - if ($app->getEnvironment() === PhraseaApplication::ENV_DEV) { - $config->setSQLLogger($app['orm.query.logger']); - } - }); - - $app['orm.annotation.register'] = $app->protect(function($key) use($app) { + $app['orm.annotation.register'] = $app->protect(function ($key) use($app) { $driver = new AnnotationDriver($app['orm.annotation.reader'], array( - $app['root.path'].'/vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Entity/MappedSuperclass', - $app['root.path'].'/vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Entity/MappedSuperclass', - $app['root.path'].'/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Entity/MappedSuperclass', + $app['root.path'] . '/vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Entity/MappedSuperclass', + $app['root.path'] . '/vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Entity/MappedSuperclass', + $app['root.path'] . '/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Entity/MappedSuperclass', )); $app['orm.add_mapping_driver']($driver, 'Gedmo', $key); }); - $app['dbal.type.register'] = $app->protect(function(Connection $connection, $types) { + $app['dbal.type.register'] = $app->protect(function (Connection $connection, $types) { $platform = $connection->getDatabasePlatform(); foreach (array_keys((array) $types) as $type) { @@ -263,7 +257,7 @@ class ORMServiceProvider implements ServiceProviderInterface } }); - $app['orm.config.new'] = $app->protect(function($key, $options) use($app) { + $app['orm.config.new'] = $app->protect(function ($key, $options) use($app) { $config = new ORMConfig(); $app['orm.cache.configurer']($key, $config, $options); @@ -287,7 +281,7 @@ class ORMServiceProvider implements ServiceProviderInterface $chain = $app['orm.mapping_driver_chain.locator']($key); - foreach ((array)$options['mappings'] as $entity) { + foreach ((array) $options['mappings'] as $entity) { if (!is_array($entity)) { throw new \InvalidArgumentException( "The 'orm.em.options' option 'mappings' should be an array of arrays." @@ -302,7 +296,8 @@ class ORMServiceProvider implements ServiceProviderInterface $config->addEntityNamespace($entity['alias'], $entity['namespace']); } - switch ($entity['type']) { + switch ($entity['type']) + { case 'annotation': $useSimpleAnnotationReader = isset($entity['use_simple_annotation_reader']) @@ -349,7 +344,7 @@ class ORMServiceProvider implements ServiceProviderInterface return $config; }); - $app['orm.ems.options'] = $app->share(function() use ($app) { + $app['orm.ems.options'] = $app->share(function () use ($app) { if (false === $app['phraseanet.configuration']->isSetup()) { return []; } @@ -366,7 +361,7 @@ class ORMServiceProvider implements ServiceProviderInterface /** * Check database connection information */ - $app['db.info'] = $app->protect(function(array $info){ + $app['db.info'] = $app->protect(function (array $info) { if (!isset($info['driver'])) { $info['driver'] = 'pdo_mysql'; } @@ -375,7 +370,8 @@ class ORMServiceProvider implements ServiceProviderInterface $info['charset'] = 'utf8'; } - switch ($info['driver']) { + switch ($info['driver']) + { case 'pdo_mysql': foreach (array('user', 'password', 'host', 'dbname', 'port') as $param) { if (!array_key_exists($param, $info)) { @@ -396,8 +392,8 @@ class ORMServiceProvider implements ServiceProviderInterface /** * Return configuration option for appbox database in DoctrineORMServiceProvider */ - $app['orm.em.appbox.options'] = $app->share(function() use ($app) { - $key = $app['db.appbox.hash.key']; + $app['orm.em.appbox.options'] = $app->share(function () use ($app) { + $key = $app['db.appbox.hash.key']; return array($key => $app['orm.options']($key)); }); @@ -405,8 +401,8 @@ class ORMServiceProvider implements ServiceProviderInterface /** * Return configuration option for fixture database in DoctrineORMServiceProvider */ - $app['orm.em.fixture.options'] = $app->share(function() use ($app) { - $key = $app['db.fixture.hash.key']; + $app['orm.em.fixture.options'] = $app->share(function () use ($app) { + $key = $app['db.fixture.hash.key']; return array($key => $app['orm.options']($key)); }); @@ -414,8 +410,8 @@ class ORMServiceProvider implements ServiceProviderInterface /** * Return configuration option for test database in DoctrineORMServiceProvider */ - $app['orm.em.test.options'] = $app->share(function() use ($app) { - $key = $app['db.test.hash.key']; + $app['orm.em.test.options'] = $app->share(function () use ($app) { + $key = $app['db.test.hash.key']; return array($key => $app['orm.options']($key)); }); @@ -423,7 +419,7 @@ class ORMServiceProvider implements ServiceProviderInterface /** * Return configuration option for databox databases in DoctrineORMServiceProvider */ - $app['orm.ems.databox.options'] = $app->share(function() use ($app) { + $app['orm.ems.databox.options'] = $app->share(function () use ($app) { $options = array(); foreach ($app['db.databox.info'] as $base) { @@ -444,13 +440,13 @@ class ORMServiceProvider implements ServiceProviderInterface "alias" => "Phraseanet", "use_simple_annotation_reader" => false, "namespace" => 'Alchemy\Phrasea\Model\Entities', - "path" => $app['root.path'].'/lib/Alchemy/Phrasea/Model/Entities', + "path" => $app['root.path'] . '/lib/Alchemy/Phrasea/Model/Entities', ) ); }); // Return orm configuration for a connection given its unique id - $app['orm.options'] = $app->protect(function($connection) use ($app) { + $app['orm.options'] = $app->protect(function ($connection) use ($app) { return array( "connection" => $connection, "mappings" => $app['orm.options.mappings'], @@ -469,7 +465,7 @@ class ORMServiceProvider implements ServiceProviderInterface * Path to doctrine log file */ $app['orm.monolog.handler.file'] = $app->share(function (Application $app) { - return $app['log.path'].'/doctrine.log'; + return $app['log.path'] . '/doctrine.log'; }); /** @@ -480,7 +476,7 @@ class ORMServiceProvider implements ServiceProviderInterface /** * Monolog handler for doctrine */ - $app['orm.monolog.handler'] = $app->share(function(Application $app) { + $app['orm.monolog.handler'] = $app->share(function (Application $app) { return new RotatingFileHandler($app['orm.monolog.handler.file'], $app['orm.monolog.handler.file.max-files']); }); @@ -495,13 +491,6 @@ class ORMServiceProvider implements ServiceProviderInterface return $logger; }); - /** - * Doctrine query logger - */ - $app['orm.query.logger'] = $app->share(function () { - return new DebugStack(); - }); - /** * Return cache driver */ @@ -548,7 +537,7 @@ class ORMServiceProvider implements ServiceProviderInterface return $manager->get($info); }); - $app['connection.pool.manager'] = $app->share(function() { + $app['connection.pool.manager'] = $app->share(function () { return new ConnectionPoolManager(); }); @@ -563,7 +552,7 @@ class ORMServiceProvider implements ServiceProviderInterface /** * Return an instance of annotation cache reader */ - $app['orm.annotation.reader'] = $app->share(function() use ($app) { + $app['orm.annotation.reader'] = $app->share(function () use ($app) { $cache = new ArrayCache(); if ($app->getEnvironment() !== PhraseaApplication::ENV_DEV) { $cache = $app['phraseanet.cache-service']->factory( diff --git a/lib/Alchemy/Phrasea/Core/Provider/RepositoriesServiceProvider.php b/lib/Alchemy/Phrasea/Core/Provider/RepositoriesServiceProvider.php index 61d2fbbd83..be6bae51d3 100644 --- a/lib/Alchemy/Phrasea/Core/Provider/RepositoriesServiceProvider.php +++ b/lib/Alchemy/Phrasea/Core/Provider/RepositoriesServiceProvider.php @@ -1,5 +1,5 @@ share(function (Application $app) { - return new CaptionSerializer(); + return new CaptionSerializer(new LazyLocator($app, 'service.technical_data')); }); - $app['serializer.es-record'] = $app->share(function (Application $app) { + $app['serializer.es-record'] = $app->share(function () { return new ESRecordSerializer(); }); } 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 index d6fd0d25f8..633dc4cc37 100644 --- a/lib/Alchemy/Phrasea/Core/Provider/WebProfilerServiceProvider.php +++ b/lib/Alchemy/Phrasea/Core/Provider/WebProfilerServiceProvider.php @@ -2,11 +2,13 @@ namespace Alchemy\Phrasea\Core\Provider; +use Alchemy\Phrasea\Controller\LazyLocator; use Alchemy\Phrasea\Core\Event\Subscriber\CacheStatisticsSubscriber; use Alchemy\Phrasea\Core\Profiler\CacheDataCollector; use Alchemy\Phrasea\Core\Profiler\TraceableCache; +use Alchemy\Phrasea\Utilities\LazyArrayAccess; use Doctrine\Common\Cache\Cache; -use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Configuration; use Doctrine\DBAL\Logging\DebugStack; use Doctrine\DBAL\Logging\LoggerChain; use Silex\Application; @@ -47,19 +49,33 @@ class WebProfilerServiceProvider implements ServiceProviderInterface return $templates; })); - $app['data_collectors'] = $app->share($app->extend('data_collectors', function ($collectors) use ($app) { - $collectors['ajax'] = function () { + $app['data_collectors'] = $app->share($app->extend('data_collectors', function (array $collectors, $app) { + $collectors['ajax'] = $app->share(function () { return new AjaxDataCollector(); - }; + }); return $collectors; })); } + $app['dbal.config.register.loggers'] = $app->protect(function (Configuration $config, $name) use ($app) { + $debugLogger = new DebugStack(); + + $loggerChain = new LoggerChain(); + $loggerChain->addLogger($debugLogger); + $loggerChain->addLogger($app['data_collectors.doctrine.logger']); + + $app['data_collectors.doctrine']->addLogger($name, $debugLogger); + + $config->setSQLLogger($loggerChain); + }); + + $app['data_collectors.doctrine.logger'] = $app->share(function ($app) { + return new DbalLogger($app['logger'], $app['stopwatch']); + }); + $app['data_collectors.doctrine'] = $app->share(function ($app) { - /** @var Connection $db */ - $db = $app['db']; - return new DoctrineDataCollector($db); + return new DoctrineDataCollector(new LazyArrayAccess(new LazyLocator($app, 'dbs'))); }); $app['cache'] = $app->share($app->extend('cache', function (Cache $cache, $app) { @@ -75,24 +91,9 @@ class WebProfilerServiceProvider implements ServiceProviderInterface return new CacheStatisticsSubscriber($app['cache']); }); - $app['data_collectors'] = $app->share($app->extend('data_collectors', function ($collectors) use ($app) { + $app['data_collectors'] = $app->share($app->extend('data_collectors', function (array $collectors, $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; + return $app['data_collectors.doctrine']; }); $collectors['cache'] = $app->share(function ($app) { diff --git a/lib/Alchemy/Phrasea/Databox/DataboxBoundRepositoryFactory.php b/lib/Alchemy/Phrasea/Databox/DataboxBoundRepositoryFactory.php new file mode 100644 index 0000000000..74662001f8 --- /dev/null +++ b/lib/Alchemy/Phrasea/Databox/DataboxBoundRepositoryFactory.php @@ -0,0 +1,22 @@ +factory = $factory; + } + + /** + * @param int $databoxId + * @return object + */ + public function getRepositoryForDatabox($databoxId) + { + if (!isset($this->repositories[$databoxId])) { + $this->repositories[$databoxId] = $this->factory->createRepositoryFor($databoxId); + } + + return $this->repositories[$databoxId]; + } +} diff --git a/lib/Alchemy/Phrasea/Databox/DataboxConnectionProvider.php b/lib/Alchemy/Phrasea/Databox/DataboxConnectionProvider.php index a1601dd00d..f1e8c160e7 100644 --- a/lib/Alchemy/Phrasea/Databox/DataboxConnectionProvider.php +++ b/lib/Alchemy/Phrasea/Databox/DataboxConnectionProvider.php @@ -1,10 +1,20 @@ quoteIdentifier('type'), 'originalname AS originalName', 'sha256', - 'mime' + 'mime', + 'LPAD(BIN(status), 32, \'0\') as status' ) ->from('record', 'r'); } diff --git a/lib/Alchemy/Phrasea/Databox/Subdef/CachedMediaSubdefDataRepository.php b/lib/Alchemy/Phrasea/Databox/Subdef/CachedMediaSubdefDataRepository.php new file mode 100644 index 0000000000..712575c628 --- /dev/null +++ b/lib/Alchemy/Phrasea/Databox/Subdef/CachedMediaSubdefDataRepository.php @@ -0,0 +1,244 @@ +decorated = $decorated; + $this->cache = $cache instanceof MultiGetCache && $cache instanceof MultiPutCache + ? $cache + : new MultiAdapter($cache); + $this->baseKey = $baseKey; + } + + /** + * @return int + */ + public function getLifeTime() + { + return $this->lifeTime; + } + + /** + * @param int $lifeTime + */ + public function setLifeTime($lifeTime) + { + $this->lifeTime = (int)$lifeTime; + } + + /** + * @param int[] $recordIds + * @param string[]|null $names + * @return array + */ + public function findByRecordIdsAndNames(array $recordIds, array $names = null) + { + $keys = $this->computeKeys($recordIds, $names); + + if ($keys) { + $data = $this->cache->fetchMultiple($keys); + + if (count($keys) === count($data)) { + return $this->filterNonNull($data); + } + } + + return $this->fetchAndSave($recordIds, $names, $keys); + } + + /** + * @param array $data + * @return array + */ + private function filterNonNull(array $data) + { + return array_values(array_filter($data, function ($value) { + return null !== $value; + })); + } + + public function delete(array $subdefIds) + { + $deleted = $this->decorated->delete($subdefIds); + + $keys = array_map([$this, 'dataToKey'], $subdefIds); + + $this->cache->saveMultiple(array_fill_keys($keys, null), $this->lifeTime); + + return $deleted; + } + + public function save(array $data) + { + $this->decorated->save($data); + + // all saved keys are now stalled. decorated repository could modify values on store (update time for example) + $recordIds = []; + + foreach ($data as $item) { + $recordIds[] = $item['record_id']; + } + + $keys = array_merge(array_map([$this, 'dataToKey'], $data), $this->generateAllCacheKeys($recordIds)); + + array_walk($keys, [$this->cache, 'delete']); + } + + /** + * @param array $data + * @return string + */ + private function dataToKey(array $data) + { + return $this->getCacheKey($data['record_id'], $data['name']); + } + + /** + * @param int $recordId + * @param string|null $name + * @return string + */ + private function getCacheKey($recordId, $name = null) + { + return $this->baseKey . 'media_subdef' . json_encode([(int)$recordId, $name]); + } + + /** + * @param int[] $recordIds + * @param string[] $names + * @return string[] + */ + private function generateCacheKeys(array $recordIds, array $names) + { + $namesCount = count($names); + + $keys = array_map(function ($recordId) use ($namesCount, $names) { + return array_map([$this, 'getCacheKey'], array_fill(0, $namesCount, $recordId), $names); + }, $recordIds); + + return $keys ? call_user_func_array('array_merge', $keys) : []; + } + + /** + * @param int[] $recordIds + * @return string[] + */ + private function generateAllCacheKeys(array $recordIds) + { + $recordIds = array_unique($recordIds); + + return array_map([$this, 'getCacheKey'], $recordIds, array_fill(0, count($recordIds), null)); + } + + /** + * @param int[] $recordIds + * @param string[]|null $names + * @param string[] $keys Known keys supposed to be fetched + * @return array + */ + private function fetchAndSave(array $recordIds, array $names = null, array $keys = []) + { + $retrieved = $this->decorated->findByRecordIdsAndNames($recordIds, $names); + + $data = $this->normalizeRetrievedData($retrieved, $keys); + + $toCache = null === $names ? $this->appendCacheExtraData($data, $retrieved, $recordIds) : $data; + + $this->cache->saveMultiple($toCache, $this->lifeTime); + + return $this->filterNonNull($data); + } + + /** + * @param int[] $recordIds + * @param string[]|null $names + * @return string[] + */ + private function computeKeys(array $recordIds, array $names = null) + { + if (!$recordIds) { + return []; + } elseif (null !== $names) { + return $this->generateCacheKeys($recordIds, $names); + } + + $keys = $this->generateAllCacheKeys($recordIds); + $data = $this->cache->fetchMultiple($keys); + + return count($keys) === count($data) ? call_user_func_array('array_merge', $data) : []; + } + + /** + * @param array $retrieved + * @param array $keys + * @return array + */ + private function normalizeRetrievedData(array $retrieved, array $keys) + { + $data = array_fill_keys($keys, null); + + foreach ($retrieved as $item) { + $data[$this->dataToKey($item)] = $item; + } + + return $data; + } + + /** + * @param array $data + * @param array $retrieved + * @param array $recordIds + * @return array + */ + private function appendCacheExtraData(array $data, array $retrieved, array $recordIds) + { + $extra = array_fill_keys($this->generateAllCacheKeys($recordIds), []); + + foreach ($retrieved as $item) { + $extra[$this->getCacheKey($item['record_id'])][] = $this->getCacheKey($item['record_id'], $item['name']); + } + + return array_merge($data, $extra); + } +} diff --git a/lib/Alchemy/Phrasea/Databox/Subdef/DbalMediaSubdefDataRepository.php b/lib/Alchemy/Phrasea/Databox/Subdef/DbalMediaSubdefDataRepository.php new file mode 100644 index 0000000000..baf8f9c519 --- /dev/null +++ b/lib/Alchemy/Phrasea/Databox/Subdef/DbalMediaSubdefDataRepository.php @@ -0,0 +1,280 @@ +connection = $connection; + } + + /** + * @param int[] $recordIds + * @param string[]|null $names + * @return array[] + */ + public function findByRecordIdsAndNames(array $recordIds, array $names = null) + { + if (!$recordIds || (null !== $names && !$names)) { + return []; + } + + $sql = $this->getSelectSQL() . ' WHERE record_id IN (:recordIds)'; + $params = ['recordIds' => $recordIds]; + $types = ['recordIds' => Connection::PARAM_INT_ARRAY]; + + if ($names) { + $sql .= ' AND name IN (:names)'; + $params['names'] = $names; + $types['names'] = Connection::PARAM_STR_ARRAY; + } + + return array_map([$this, 'sqlToPhp'], $this->connection->fetchAll($sql, $params, $types)); + } + + /** + * @param array[] $subdefIds + * @return int The number of affected rows + * @throws \Exception + */ + public function delete(array $subdefIds) + { + if (!$subdefIds) { + return 0; + } + + $statement = $this->connection->prepare('DELETE FROM subdef WHERE record_id = :record_id AND name = :name'); + + $this->connection->beginTransaction(); + + try { + $deleted = array_reduce($subdefIds, function ($carry, $data) use ($statement) { + $carry += $statement->execute([ + 'record_id' => $data['record_id'], + 'name' => $data['name'], + ]); + + return $carry; + }, 0); + + $this->connection->commit(); + } catch (\Exception $exception) { + $this->connection->rollBack(); + + throw $exception; + } + + return $deleted; + } + + /** + * @param array $data + * @throws \Exception + */ + public function save(array $data) + { + $this->connection->transactional(function () use ($data) { + $partitions = $this->partitionInsertAndUpdate($data); + + $updateNeeded = $this->createMissing($partitions['insert']); + + $this->updatePresent($partitions['update'] + $updateNeeded); + }); + } + + /** + * @param array $data + * @return array + */ + private function partitionInsertAndUpdate(array $data) + { + $partitions = [ + 'insert' => [], + 'update' => [], + ]; + + foreach ($data as $index => $item) { + $partitions[isset($item['subdef_id']) ? 'update' : 'insert'][$index] = $item; + } + + return $partitions; + } + + /** + * @param array $toInsert + * @return array + * @throws DBALException + */ + private function createMissing(array $toInsert) + { + if (!$toInsert) { + return []; + } + + $statement = $this->connection->prepare($this->getInsertSql()); + + $updateNeeded = []; + + foreach ($toInsert as $index => $data) { + try { + $statement->execute($this->phpToSql($data)); + } catch (DriverException $exception) { + $updateNeeded[$index] = $data; + } + } + + return $updateNeeded; + } + + /** + * @param array $toUpdate + * @throws DBALException + */ + private function updatePresent(array $toUpdate) + { + if (!$toUpdate) { + return; + } + + $statement = $this->connection->prepare($this->getUpdateSql()); + + foreach ($toUpdate as $data) { + $statement->execute($this->phpToSql($data)); + } + } + + private function getSelectSQL() + { + return <<<'SQL' +SELECT subdef_id, record_id, name, path, file, width, height, mime, size, substit, etag, created_on, updated_on +FROM subdef +SQL; + } + + /** + * @param array $data + * @return array + */ + private function phpToSql(array $data) + { + return [ + 'record_id' => $data['record_id'], + 'name' => $data['name'], + 'path' => $data['path'], + 'file' => $data['file'], + 'width' => $data['width'], + 'height' => $data['height'], + 'mime' => $data['mime'], + 'size' => $data['size'], + 'substit' => $data['is_substituted'], + 'etag' => $data['etag'], + ]; + } + + private function sqlToPhp(array $data) + { + return [ + 'subdef_id' => (int)$data['subdef_id'], + 'record_id' => (int)$data['record_id'], + 'name' => $data['name'], + 'path' => $data['path'], + 'file' => $data['file'], + 'width' => (int)$data['width'], + 'height' => (int)$data['height'], + 'mime' => $data['mime'], + 'size' => (int)$data['size'], + 'is_substituted' => (bool)$data['substit'], + 'etag' => $data['etag'], + 'created_on' => $data['created_on'], + 'updated_on' => $data['updated_on'], + 'physically_present' => true, + ]; + } + + /** + * @return string + */ + private function getInsertSql() + { + static $sql; + + if (null !== $sql) { + return $sql; + } + + $values = [ + 'record_id' => ':record_id', + 'name' => ':name', + 'path' => ':path', + 'file' => ':file', + 'width' => ':width', + 'height' => ':height', + 'mime' => ':mime', + 'size' => ':size', + 'substit' => ':substit', + 'etag' => ':etag', + 'created_on' => 'NOW()', + 'updated_on' => 'NOW()', + 'dispatched' => '1', + ]; + + $sql = sprintf( + 'INSERT INTO subdef (%s) VALUES (%s)', + implode(', ', array_keys($values)), + implode(', ', array_values($values)) + ); + + return $sql; + } + + /** + * @return string + */ + private function getUpdateSql() + { + static $sql; + + if (null !== $sql) { + return $sql; + } + + $values = [ + 'path = :path', + 'file = :file', + 'width = :width', + 'height = :height', + 'mime = :mime', + 'size = :size', + 'substit = :substit', + 'etag = :etag', + 'updated_on = NOW()', + ]; + + $where = [ + 'record_id = :record_id', + 'name = :name', + ]; + + $sql = sprintf('UPDATE subdef SET %s WHERE %s', implode(', ', $values), implode(' AND ', $where)); + + return $sql; + } +} diff --git a/lib/Alchemy/Phrasea/Databox/Subdef/MediaSubdefDataRepository.php b/lib/Alchemy/Phrasea/Databox/Subdef/MediaSubdefDataRepository.php new file mode 100644 index 0000000000..c97f4dc6fc --- /dev/null +++ b/lib/Alchemy/Phrasea/Databox/Subdef/MediaSubdefDataRepository.php @@ -0,0 +1,33 @@ +loadFromArray($data); + }, $instance, \media_subdef::class); + + $closure($data); + } + + /** + * @param \media_subdef $instance + * @return array + * @throws \Assert\AssertionFailedException + */ + public function extract($instance) + { + Assertion::isInstanceOf($instance, \media_subdef::class); + + $closure = \Closure::bind(function () { + return $this->toArray(); + }, $instance, \media_subdef::class); + + return $closure(); + } +} diff --git a/lib/Alchemy/Phrasea/Databox/Subdef/MediaSubdefRepository.php b/lib/Alchemy/Phrasea/Databox/Subdef/MediaSubdefRepository.php new file mode 100644 index 0000000000..aba28b6fd1 --- /dev/null +++ b/lib/Alchemy/Phrasea/Databox/Subdef/MediaSubdefRepository.php @@ -0,0 +1,138 @@ +repository = $repository; + $this->subdefFactory = $subdefFactory; + $this->hydrator = $hydrator ?: new MediaSubdefHydrator(); + } + + /** + * @param int $recordId + * @param string $name + * @return \media_subdef|null + */ + public function findOneByRecordIdAndName($recordId, $name) + { + $subdefs = $this->repository->findByRecordIdsAndNames([$recordId], [$name]); + + if (!$subdefs) { + return null; + } + + $instances = $this->hydrateAll($subdefs); + + return reset($instances); + } + + /** + * @param int[] $recordIds + * @param string[] $names + * @return \media_subdef[] + */ + public function findByRecordIdsAndNames(array $recordIds, array $names = null) + { + if (!$recordIds) { + return []; + } + + $data = $this->repository->findByRecordIdsAndNames($recordIds, $names); + + return $this->hydrateAll($data); + } + + /** + * @param \media_subdef|\media_subdef[] $subdefs + */ + public function save($subdefs) + { + if (!is_array($subdefs) || !$subdefs instanceof \Traversable) { + $subdefs = [$subdefs]; + } elseif ($subdefs instanceof \Traversable) { + $subdefs = iterator_to_array($subdefs); + } + Assertion::allIsInstanceOf($subdefs, \media_subdef::class); + + $data = array_map([$this->hydrator, 'extract'], $subdefs); + + $this->repository->save($data); + } + + public function clear() + { + $this->idMap = []; + } + + /** + * @param string $index + * @param array $data + * @return \media_subdef + */ + private function hydrate($index, array $data) + { + if (isset($this->idMap[$index])) { + $this->hydrator->hydrate($this->idMap[$index], $data); + + return $this->idMap[$index]; + } + + $factory = $this->subdefFactory; + + $instance = $factory($data); + Assertion::isInstanceOf($instance, \media_subdef::class); + + $this->idMap[$index] = $instance; + + return $instance; + } + + /** + * @param array $data + * @return \media_subdef[] + */ + private function hydrateAll(array $data) + { + $instances = []; + + foreach ($data as $item) { + $instances[] = $this->hydrate(json_encode([$item['record_id'], $item['name']]), $item); + } + + return $instances; + } +} diff --git a/lib/Alchemy/Phrasea/Databox/Subdef/MediaSubdefRepositoryFactory.php b/lib/Alchemy/Phrasea/Databox/Subdef/MediaSubdefRepositoryFactory.php new file mode 100644 index 0000000000..e2d1d14265 --- /dev/null +++ b/lib/Alchemy/Phrasea/Databox/Subdef/MediaSubdefRepositoryFactory.php @@ -0,0 +1,60 @@ +connectionProvider = $connectionProvider; + $this->cache = $cache; + $this->mediaSubdefFactoryProvider = $mediaSubdefFactoryProvider; + } + + public function createRepositoryFor($databoxId) + { + $connection = $this->connectionProvider->getConnection($databoxId); + + $dbalRepository = new DbalMediaSubdefDataRepository($connection); + $dataRepository = new CachedMediaSubdefDataRepository($dbalRepository, $this->cache, sprintf('databox%d:', $databoxId)); + + $provider = $this->mediaSubdefFactoryProvider; + $factory = $provider($databoxId); + + if (!is_callable($factory)) { + throw new \UnexpectedValueException(sprintf( + 'Media subdef factory is expected to be callable, got %s', + is_object($factory) ? get_class($factory) : gettype($factory) + )); + } + + return new MediaSubdefRepository($dataRepository, $factory); + } +} diff --git a/lib/Alchemy/Phrasea/Databox/Subdef/MediaSubdefService.php b/lib/Alchemy/Phrasea/Databox/Subdef/MediaSubdefService.php new file mode 100644 index 0000000000..60a3c9d9d8 --- /dev/null +++ b/lib/Alchemy/Phrasea/Databox/Subdef/MediaSubdefService.php @@ -0,0 +1,120 @@ +repositoryProvider = $repositoryProvider; + } + + /** + * Returns all available subdefs grouped by each record reference and by its name + * + * @param RecordReferenceInterface[]|RecordReferenceCollection $records + * @return \media_subdef[][] + */ + public function findSubdefsByRecordReferenceFromCollection($records) + { + $subdefs = $this->reduceRecordReferenceCollection( + $records, + function (array &$carry, array $subdefs, array $indexes) { + /** @var \media_subdef $subdef */ + foreach ($subdefs as $subdef) { + $index = $indexes[$subdef->get_record_id()]; + + $carry[$index][$subdef->get_name()] = $subdef; + } + }, + array_fill_keys(array_keys(iterator_to_array($records)), []) + ); + + ksort($subdefs); + + return $subdefs; + } + + /** + * @param RecordReferenceInterface[]|RecordReferenceCollection $records + * @return \media_subdef[] + */ + public function findSubdefsFromRecordReferenceCollection($records) + { + $groups = $this->reduceRecordReferenceCollection( + $records, + function (array &$carry, array $subdefs) { + $carry[] = $subdefs; + + return $carry; + }, + [] + ); + + if ($groups) { + return call_user_func_array('array_merge', $groups); + } + + return []; + } + + /** + * @param RecordReferenceInterface[]|RecordReferenceCollection $records + * @param callable $process + * @param mixed $initialValue + * @return mixed + */ + private function reduceRecordReferenceCollection($records, callable $process, $initialValue) + { + $records = $this->normalizeRecordCollection($records); + + $carry = $initialValue; + + foreach ($records->groupPerDataboxId() as $databoxId => $indexes) { + $subdefs = $this->getRepositoryForDatabox($databoxId)->findByRecordIdsAndNames(array_keys($indexes)); + + $carry = $process($carry, $subdefs, $indexes, $databoxId); + } + + return $carry; + } + + /** + * @param int $databoxId + * @return MediaSubdefRepository + */ + private function getRepositoryForDatabox($databoxId) + { + return $this->repositoryProvider->getRepositoryForDatabox($databoxId); + } + + /** + * @param RecordReferenceInterface[]|RecordReferenceCollection $records + * @return RecordReferenceCollection + */ + private function normalizeRecordCollection($records) + { + if ($records instanceof RecordReferenceCollection) { + return $records; + } + + return new RecordReferenceCollection($records); + } +} diff --git a/lib/Alchemy/Phrasea/Databox/Subdef/MediaSubdefServiceProvider.php b/lib/Alchemy/Phrasea/Databox/Subdef/MediaSubdefServiceProvider.php new file mode 100644 index 0000000000..a9ae274166 --- /dev/null +++ b/lib/Alchemy/Phrasea/Databox/Subdef/MediaSubdefServiceProvider.php @@ -0,0 +1,49 @@ +protect(function ($databoxId) use ($app) { + return function (array $data) use ($app, $databoxId) { + $recordReference = RecordReference::createFromDataboxIdAndRecordId($databoxId, $data['record_id']); + + return new \media_subdef($app, $recordReference, $data['name'], false, $data); + }; + }); + + $app['provider.repo.media_subdef'] = $app->share(function (Application $app) { + $connectionProvider = new DataboxConnectionProvider($app['phraseanet.appbox']); + $factoryProvider = $app['provider.factory.media_subdef']; + + $repositoryFactory = new MediaSubdefRepositoryFactory($connectionProvider, $app['cache'], $factoryProvider); + + return new DataboxBoundRepositoryProvider($repositoryFactory); + }); + + $app['service.media_subdef'] = $app->share(function (Application $app) { + return new MediaSubdefService($app['provider.repo.media_subdef']); + }); + } + + public function boot(Application $app) + { + // no-op + } +} diff --git a/lib/Alchemy/Phrasea/Feed/Formatter/AtomFormatter.php b/lib/Alchemy/Phrasea/Feed/Formatter/AtomFormatter.php index be7533249d..5371f97a41 100644 --- a/lib/Alchemy/Phrasea/Feed/Formatter/AtomFormatter.php +++ b/lib/Alchemy/Phrasea/Feed/Formatter/AtomFormatter.php @@ -17,6 +17,7 @@ use Alchemy\Phrasea\Feed\FeedInterface; use Alchemy\Phrasea\Feed\Link\FeedLink; use Alchemy\Phrasea\Feed\Link\LinkGeneratorCollection; use Alchemy\Phrasea\Model\Entities\User; +use Alchemy\Phrasea\Utilities\NullableDateTime; use Symfony\Component\HttpFoundation\Response; class AtomFormatter extends FeedFormatterAbstract implements FeedFormatterInterface @@ -48,8 +49,6 @@ class AtomFormatter extends FeedFormatterAbstract implements FeedFormatterInterf */ public function format(FeedInterface $feed, $page, User $user = null, $generator = 'Phraseanet', Application $app = null) { - $updated_on = $feed->getUpdatedOn(); - $document = new \DOMDocument('1.0', 'UTF-8'); $document->formatOutput = true; $document->standalone = true; @@ -59,8 +58,7 @@ class AtomFormatter extends FeedFormatterAbstract implements FeedFormatterInterf $root->setAttribute('xmlns:media', 'http://search.yahoo.com/mrss/'); $this->addTag($document, $root, 'title', $feed->getTitle()); - if ($updated_on instanceof \DateTime) { - $updated_on = $updated_on->format(DATE_ATOM); + if (null !== $updated_on = NullableDateTime::format($feed->getUpdatedOn())) { $this->addTag($document, $root, 'updated', $updated_on); } diff --git a/lib/Alchemy/Phrasea/Feed/Formatter/CoolirisFormatter.php b/lib/Alchemy/Phrasea/Feed/Formatter/CoolirisFormatter.php index 1b00e8a779..a7f81addf9 100644 --- a/lib/Alchemy/Phrasea/Feed/Formatter/CoolirisFormatter.php +++ b/lib/Alchemy/Phrasea/Feed/Formatter/CoolirisFormatter.php @@ -19,6 +19,7 @@ use Alchemy\Phrasea\Model\Entities\FeedEntry; use Alchemy\Phrasea\Model\Entities\FeedItem; use Alchemy\Phrasea\Feed\Link\LinkGeneratorCollection; use Alchemy\Phrasea\Model\Entities\User; +use Alchemy\Phrasea\Utilities\NullableDateTime; use DateTime; use Symfony\Component\HttpFoundation\Response; @@ -52,8 +53,6 @@ class CoolirisFormatter extends FeedFormatterAbstract implements FeedFormatterIn */ public function format(FeedInterface $feed, $page, User $user = null, $generator = 'Phraseanet', Application $app = null) { - $updated_on = $feed->getUpdatedOn(); - $doc = new \DOMDocument('1.0', 'UTF-8'); $doc->formatOutput = true; $doc->standalone = true; @@ -89,8 +88,7 @@ class CoolirisFormatter extends FeedFormatterAbstract implements FeedFormatterIn $this->addTag($doc, $channel, 'managingEditor', $this->managingEditor); if (isset($this->webMaster)) $this->addTag($doc, $channel, 'webMaster', $this->webMaster); - if ($updated_on instanceof DateTime) { - $updated_on = $updated_on->format(DATE_RFC2822); + if (null !== $updated_on = NullableDateTime::format($feed->getUpdatedOn(), DATE_RFC2822)) { $this->addTag($doc, $channel, 'pubDate', $updated_on); } if (isset($this->lastBuildDate) && $this->lastBuildDate instanceof DateTime) { @@ -191,7 +189,7 @@ class CoolirisFormatter extends FeedFormatterAbstract implements FeedFormatterIn $thumbnail_sd = $content->getRecord($app)->get_thumbnail(); $thumbnail_permalink = $thumbnail_sd->get_permalink(); - $medium = strtolower($content->getRecord($app)->get_type()); + $medium = strtolower($content->getRecord($app)->getType()); if ( ! in_array($medium, ['image', 'audio', 'video'])) { return $this; diff --git a/lib/Alchemy/Phrasea/Feed/Formatter/FeedFormatterAbstract.php b/lib/Alchemy/Phrasea/Feed/Formatter/FeedFormatterAbstract.php index 948dd3021d..4024a9157f 100644 --- a/lib/Alchemy/Phrasea/Feed/Formatter/FeedFormatterAbstract.php +++ b/lib/Alchemy/Phrasea/Feed/Formatter/FeedFormatterAbstract.php @@ -52,7 +52,7 @@ abstract class FeedFormatterAbstract $thumbnail_sd = $content->getRecord($app)->get_thumbnail(); $thumbnail_permalink = $thumbnail_sd->get_permalink(); - $medium = strtolower($content->getRecord($app)->get_type()); + $medium = strtolower($content->getRecord($app)->getType()); if ( ! in_array($medium, ['image', 'audio', 'video'])) { return $this; diff --git a/lib/Alchemy/Phrasea/Feed/Formatter/RssFormatter.php b/lib/Alchemy/Phrasea/Feed/Formatter/RssFormatter.php index ec892d2fc3..9e5a923470 100644 --- a/lib/Alchemy/Phrasea/Feed/Formatter/RssFormatter.php +++ b/lib/Alchemy/Phrasea/Feed/Formatter/RssFormatter.php @@ -17,6 +17,7 @@ use Alchemy\Phrasea\Feed\Link\FeedLink; use Alchemy\Phrasea\Feed\Link\LinkGeneratorCollection; use Alchemy\Phrasea\Feed\RSS\FeedRSSImage; use Alchemy\Phrasea\Model\Entities\User; +use Alchemy\Phrasea\Utilities\NullableDateTime; use Symfony\Component\HttpFoundation\Response; use Alchemy\Phrasea\Model\Entities\FeedEntry; use Alchemy\Phrasea\Feed\Link\FeedLinkGenerator; @@ -51,8 +52,6 @@ class RssFormatter extends FeedFormatterAbstract implements FeedFormatterInterfa */ public function format(FeedInterface $feed, $page, User $user = null, $generator = 'Phraseanet', Application $app = null) { - $updated_on = $feed->getUpdatedOn(); - $next = $prev = null; if ($feed->hasPage($page + 1, self::PAGE_SIZE)) { @@ -104,11 +103,10 @@ class RssFormatter extends FeedFormatterAbstract implements FeedFormatterInterfa $this->addTag($doc, $channel, 'managingEditor', $this->managingEditor); if (isset($this->webMaster)) $this->addTag($doc, $channel, 'webMaster', $this->webMaster); - if ($updated_on instanceof \DateTime) { - $updated_on = $updated_on->format(DATE_RFC2822); + if (null !== $updated_on = NullableDateTime::format($feed->getUpdatedOn(), DATE_RFC2822)) { $this->addTag($doc, $channel, 'pubDate', $updated_on); } - if (isset($this->lastBuildDate) && $this->lastBuildDate instanceof DateTime) { + if (isset($this->lastBuildDate) && $this->lastBuildDate instanceof \DateTime) { $last_build = $this->lastBuildDate->format(DATE_RFC2822); $this->addTag($doc, $channel, 'lastBuildDate', $last_build); } diff --git a/lib/Alchemy/Phrasea/Helper/Record/Helper.php b/lib/Alchemy/Phrasea/Helper/Record/Helper.php index 5e04bab6d1..853e5d0f3b 100644 --- a/lib/Alchemy/Phrasea/Helper/Record/Helper.php +++ b/lib/Alchemy/Phrasea/Helper/Record/Helper.php @@ -284,7 +284,7 @@ class Helper extends \Alchemy\Phrasea\Helper\Helper public function get_serialize_list() { if ($this->is_single_grouping()) { - return $this->get_grouping_head()->get_serialize_key(); + return $this->get_grouping_head()->getId(); } else { return $this->selection->serialize_list(); } diff --git a/lib/Alchemy/Phrasea/Hydration/Hydrator.php b/lib/Alchemy/Phrasea/Hydration/Hydrator.php new file mode 100644 index 0000000000..ac248f7513 --- /dev/null +++ b/lib/Alchemy/Phrasea/Hydration/Hydrator.php @@ -0,0 +1,31 @@ +hydrator = $hydrator; + $this->prototype = $prototype; + } + + /** + * @param string|int $index + * @param array $data + * @return object + */ + public function hydrate($index, array $data) + { + if (!isset($this->entities[$index])) { + $this->entities[$index] = clone $this->prototype; + } + + $instance = $this->entities[$index]; + + $this->hydrator->hydrate($instance, $data); + + return $instance; + } + + /** + * @param array[] $data + * @return object[] + */ + public function hydrateAll(array $data) + { + Assertion::allIsArray($data); + + $instances = []; + + foreach ($data as $index => $item) { + $instances[$index] = $this->hydrate($index, $item); + } + + return $instances; + } + + public function clear() + { + $this->entities = []; + } + + public function getIterator() + { + return new \ArrayIterator($this->entities); + } + + public function offsetExists($offset) + { + return isset($this->entities[$offset]); + } + + public function offsetGet($offset) + { + return $this->entities[$offset]; + } + + public function offsetSet($offset, $value) + { + Assertion::notNull($offset); + Assertion::isArray($value); + + $this->hydrate($offset, $value); + } + + public function offsetUnset($offset) + { + unset($this->entities[$offset]); + } + + public function count() + { + return count($this->entities); + } +} diff --git a/lib/Alchemy/Phrasea/Hydration/ReflectionHydrator.php b/lib/Alchemy/Phrasea/Hydration/ReflectionHydrator.php new file mode 100644 index 0000000000..5a635e8eb6 --- /dev/null +++ b/lib/Alchemy/Phrasea/Hydration/ReflectionHydrator.php @@ -0,0 +1,130 @@ +className = $className; + $this->properties = $properties; + } + + /** + * @param array $data + * @param object $instance + * @throws \Assert\AssertionFailedException + */ + public function hydrate($instance, array $data) + { + Assertion::isInstanceOf($instance, $this->className); + + foreach ($data as $key => $value) { + $this->getReflectionProperty($key)->setValue($instance, $value); + } + } + + /** + * @param object $instance + * @return array + * @throws \Assert\AssertionFailedException + */ + public function extract($instance) + { + Assertion::isInstanceOf($instance, $this->className); + + $data = []; + + foreach ($this->getReflectionProperties() as $name => $property) { + $data[$name] = $property->getValue($instance); + } + + return $data; + } + + /** + * @return \ReflectionClass + */ + private function getReflectionClass() + { + if (null === $this->reflectionClass) { + $this->reflectionClass = new \ReflectionClass($this->className); + } + + return $this->reflectionClass; + } + + /** + * @param string $name + * @return \ReflectionProperty + * @throws \RuntimeException + */ + private function getReflectionProperty($name) + { + $this->loadReflectionProperties(); + + return $this->reflectionProperties[$name]; + } + + /** + * @return \ReflectionProperty[] + */ + private function getReflectionProperties() + { + $this->loadReflectionProperties(); + + return $this->reflectionProperties; + } + + private function loadReflectionProperties() + { + if (null !== $this->reflectionProperties) { + return; + } + + $class = $this->getReflectionClass(); + $properties = []; + + foreach ($this->properties as $name) { + $property = $class->getProperty($name); + $property->setAccessible(true); + + $properties[$name] = $property; + } + + $this->reflectionProperties = $properties; + } +} diff --git a/lib/Alchemy/Phrasea/Media/ArrayTechnicalDataSet.php b/lib/Alchemy/Phrasea/Media/ArrayTechnicalDataSet.php index 4809823fe8..e4ab83baf1 100644 --- a/lib/Alchemy/Phrasea/Media/ArrayTechnicalDataSet.php +++ b/lib/Alchemy/Phrasea/Media/ArrayTechnicalDataSet.php @@ -11,22 +11,20 @@ namespace Alchemy\Phrasea\Media; use Assert\Assertion; -final class ArrayTechnicalDataSet implements \IteratorAggregate, TechnicalDataSet +class ArrayTechnicalDataSet implements \IteratorAggregate, TechnicalDataSet { /** @var TechnicalData[] */ - private $data; + private $data = []; /** * @param TechnicalData[] $data */ public function __construct($data = []) { - Assertion::allIsInstanceOf($data, TechnicalData::class); - - $this->data = []; + Assertion::isTraversable($data); foreach ($data as $technicalData) { - $this->data[$technicalData->getName()] = $technicalData; + $this[] = $technicalData; } } @@ -41,7 +39,7 @@ final class ArrayTechnicalDataSet implements \IteratorAggregate, TechnicalDataSe $offset = $offset->getName(); } - return isset($this->data[$offset]) || array_key_exists($offset, $this->data); + return isset($this->data[$offset]); } public function offsetGet($offset) @@ -50,7 +48,7 @@ final class ArrayTechnicalDataSet implements \IteratorAggregate, TechnicalDataSe } /** - * @param string $offset + * @param null|string $offset * @param TechnicalData $value */ public function offsetSet($offset, $value) @@ -58,6 +56,7 @@ final class ArrayTechnicalDataSet implements \IteratorAggregate, TechnicalDataSe Assertion::isInstanceOf($value, TechnicalData::class); $name = $value->getName(); + if (null !== $offset) { Assertion::eq($name, $offset); } @@ -82,6 +81,7 @@ final class ArrayTechnicalDataSet implements \IteratorAggregate, TechnicalDataSe public function getValues() { $values = []; + foreach ($this->data as $key => $value) { $values[$key] = $value->getValue(); } diff --git a/lib/Alchemy/Phrasea/Media/Factory/DbalRepositoryFactory.php b/lib/Alchemy/Phrasea/Media/Factory/DbalRepositoryFactory.php new file mode 100644 index 0000000000..5ec88a6433 --- /dev/null +++ b/lib/Alchemy/Phrasea/Media/Factory/DbalRepositoryFactory.php @@ -0,0 +1,36 @@ +connectionProvider = $connectionProvider; + } + + public function createRepositoryForDatabox($databoxId) + { + return new DbalRecordTechnicalDataSetRepository( + $this->connectionProvider->getConnection($databoxId), + new TechnicalDataFactory() + ); + } +} diff --git a/lib/Alchemy/Phrasea/Media/Factory/TechnicalDataFactory.php b/lib/Alchemy/Phrasea/Media/Factory/TechnicalDataFactory.php new file mode 100644 index 0000000000..c03f80e911 --- /dev/null +++ b/lib/Alchemy/Phrasea/Media/Factory/TechnicalDataFactory.php @@ -0,0 +1,35 @@ +urlGenerator = $urlGenerator; + $this->secretProvider = $secretProvider; + $this->defaultTTL = (int)$defaultTTL; + } + + /** + * @return int + */ + public function getDefaultTTL() + { + return $this->defaultTTL; + } + + /** + * @param User $issuer + * @param \media_subdef $subdef + * @param int $url_ttl + * @return string + */ + public function generate(User $issuer, \media_subdef $subdef, $url_ttl = null) + { + $url_ttl = $url_ttl ?: $this->defaultTTL; + + $payload = [ + 'iat' => time(), + 'iss' => $issuer->getId(), + 'sdef' => [$subdef->get_sbas_id(), $subdef->get_record_id(), $subdef->get_name()], + ]; + + if ($url_ttl > 0) { + $payload['exp'] = $payload['iat'] + $url_ttl; + } + + $secret = $this->secretProvider->getSecretForUser($issuer); + + return $this->urlGenerator->generate('media_accessor', [ + 'token' => JWT::encode($payload, $secret->getToken(), 'HS256', $secret->getId()), + ], UrlGeneratorInterface::ABSOLUTE_URL); + } + + /** + * @param User $issuer + * @param \media_subdef[] $subdefs + * @param int $url_ttl + * @return string[] + */ + public function generateMany(User $issuer, $subdefs, $url_ttl = null) + { + Assertion::allIsInstanceOf($subdefs, \media_subdef::class); + $url_ttl = $url_ttl ?: $this->defaultTTL; + + $payloads = []; + + $payload = [ + 'iat' => time(), + 'iss' => $issuer->getId(), + 'sdef' => null, + ]; + + if ($url_ttl > 0) { + $payload['exp'] = $payload['iat'] + $url_ttl; + } + + foreach ($subdefs as $index => $subdef) { + $payload['sdef'] = [$subdef->get_sbas_id(), $subdef->get_record_id(), $subdef->get_name()]; + + $payloads[$index] = $payload; + } + + $secret = $this->secretProvider->getSecretForUser($issuer); + + return array_map(function ($payload) use ($secret) { + return $this->urlGenerator->generate('media_accessor', [ + 'token' => JWT::encode($payload, $secret->getToken(), 'HS256', $secret->getId()), + ], UrlGeneratorInterface::ABSOLUTE_URL); + }, $payloads); + } +} diff --git a/lib/Alchemy/Phrasea/Media/RecordTechnicalDataSet.php b/lib/Alchemy/Phrasea/Media/RecordTechnicalDataSet.php new file mode 100644 index 0000000000..df7caa70c6 --- /dev/null +++ b/lib/Alchemy/Phrasea/Media/RecordTechnicalDataSet.php @@ -0,0 +1,37 @@ +recordId = (int)$recordId; + parent::__construct($technicalData); + } + + /** + * @return int + */ + public function getRecordId() + { + return $this->recordId; + } +} diff --git a/lib/Alchemy/Phrasea/Media/RecordTechnicalDataSetRepository.php b/lib/Alchemy/Phrasea/Media/RecordTechnicalDataSetRepository.php new file mode 100644 index 0000000000..46d388f29f --- /dev/null +++ b/lib/Alchemy/Phrasea/Media/RecordTechnicalDataSetRepository.php @@ -0,0 +1,20 @@ +factory = $factory; + } + + /** + * @param int $databoxId + * @return RecordTechnicalDataSetRepository + */ + public function getRepositoryFor($databoxId) + { + if (!isset($this->repositories[$databoxId])) { + $this->repositories[$databoxId] = $this->factory->createRepositoryForDatabox($databoxId); + } + + return $this->repositories[$databoxId]; + } +} diff --git a/lib/Alchemy/Phrasea/Media/Repository/DbalRecordTechnicalDataSetRepository.php b/lib/Alchemy/Phrasea/Media/Repository/DbalRecordTechnicalDataSetRepository.php new file mode 100644 index 0000000000..5c1e035f11 --- /dev/null +++ b/lib/Alchemy/Phrasea/Media/Repository/DbalRecordTechnicalDataSetRepository.php @@ -0,0 +1,75 @@ +connection = $connection; + $this->dataFactory = $dataFactory; + } + + /** + * @param int[] $recordIds + * @return RecordTechnicalDataSet[] + */ + public function findByRecordIds(array $recordIds) + { + if (empty($recordIds)) { + return []; + } + + $data = $this->connection->fetchAll( + 'SELECT record_id, name, value FROM technical_datas WHERE record_id IN (:recordIds)', + ['recordIds' => $recordIds], + ['recordIds' => Connection::PARAM_INT_ARRAY] + ); + + return $this->mapSetsFromDatabaseResult($recordIds, $data); + } + + /** + * @param array $recordIds + * @param array $data + * @return RecordTechnicalDataSet[] + */ + private function mapSetsFromDatabaseResult(array $recordIds, array $data) + { + $groups = []; + + foreach ($recordIds as $recordId) { + $groups[$recordId] = new RecordTechnicalDataSet($recordId); + } + + foreach ($data as $item) { + $group =& $groups[$item['record_id']]; + $group[] = $this->dataFactory->createFromNameAndValue($item['name'], $item['value']); + } + + return array_values($groups); + } +} diff --git a/lib/Alchemy/Phrasea/Media/TechnicalDataService.php b/lib/Alchemy/Phrasea/Media/TechnicalDataService.php new file mode 100644 index 0000000000..da1449cfc2 --- /dev/null +++ b/lib/Alchemy/Phrasea/Media/TechnicalDataService.php @@ -0,0 +1,52 @@ +provider = $provider; + } + + /** + * @param RecordReference[] $references + * @return RecordTechnicalDataSet[] + */ + public function fetchRecordsTechnicalData($references) + { + if (!$references instanceof RecordReferenceCollection) { + $references = new RecordReferenceCollection($references); + } + + $sets = []; + + foreach ($references->groupPerDataboxId() as $databoxId => $indexes) { + foreach ($this->provider->getRepositoryFor($databoxId)->findByRecordIds(array_keys($indexes)) as $set) { + $index = $indexes[$set->getRecordId()]; + + $sets[$index] = $set; + } + } + + ksort($sets); + + return $sets; + } +} diff --git a/lib/Alchemy/Phrasea/Media/TechnicalDataServiceProvider.php b/lib/Alchemy/Phrasea/Media/TechnicalDataServiceProvider.php new file mode 100644 index 0000000000..d0f19e3159 --- /dev/null +++ b/lib/Alchemy/Phrasea/Media/TechnicalDataServiceProvider.php @@ -0,0 +1,34 @@ +share(function (Application $app) { + $connectionProvider = new DataboxConnectionProvider($app['phraseanet.appbox']); + $repositoryFactory = new DbalRepositoryFactory($connectionProvider); + + return new TechnicalDataService(new RecordTechnicalDataSetRepositoryProvider($repositoryFactory)); + }); + } + + public function boot(Application $app) + { + // no-op + } +} diff --git a/lib/Alchemy/Phrasea/Model/Entities/Basket.php b/lib/Alchemy/Phrasea/Model/Entities/Basket.php index ae7e087a27..c25b074634 100644 --- a/lib/Alchemy/Phrasea/Model/Entities/Basket.php +++ b/lib/Alchemy/Phrasea/Model/Entities/Basket.php @@ -460,8 +460,8 @@ class Basket foreach ($this->getElements() as $basket_element) { $bask_record = $basket_element->getRecord($app); - if ($bask_record->get_record_id() == $record->get_record_id() - && $bask_record->get_sbas_id() == $record->get_sbas_id()) { + if ($bask_record->getRecordId() == $record->getRecordId() + && $bask_record->getDataboxId() == $record->getDataboxId()) { return true; } } diff --git a/lib/Alchemy/Phrasea/Model/Entities/BasketElement.php b/lib/Alchemy/Phrasea/Model/Entities/BasketElement.php index 45c5598696..80af7802fc 100644 --- a/lib/Alchemy/Phrasea/Model/Entities/BasketElement.php +++ b/lib/Alchemy/Phrasea/Model/Entities/BasketElement.php @@ -138,8 +138,8 @@ class BasketElement public function setRecord(\record_adapter $record) { - $this->setRecordId($record->get_record_id()); - $this->setSbasId($record->get_sbas_id()); + $this->setRecordId($record->getRecordId()); + $this->setSbasId($record->getDataboxId()); } /** diff --git a/lib/Alchemy/Phrasea/Model/Entities/FeedEntry.php b/lib/Alchemy/Phrasea/Model/Entities/FeedEntry.php index b8fe006db2..bbd33b2be5 100644 --- a/lib/Alchemy/Phrasea/Model/Entities/FeedEntry.php +++ b/lib/Alchemy/Phrasea/Model/Entities/FeedEntry.php @@ -259,7 +259,7 @@ class FeedEntry /** * Get items * - * @return \Doctrine\Common\Collections\Collection + * @return FeedItem[]|\Doctrine\Common\Collections\Collection */ public function getItems() { diff --git a/lib/Alchemy/Phrasea/Model/Entities/OrderElement.php b/lib/Alchemy/Phrasea/Model/Entities/OrderElement.php index d232795067..010049ab98 100644 --- a/lib/Alchemy/Phrasea/Model/Entities/OrderElement.php +++ b/lib/Alchemy/Phrasea/Model/Entities/OrderElement.php @@ -80,7 +80,7 @@ class OrderElement } /** - * @return mixed + * @return User|null */ public function getOrderMaster() { diff --git a/lib/Alchemy/Phrasea/Model/Entities/StoryWZ.php b/lib/Alchemy/Phrasea/Model/Entities/StoryWZ.php index c7fdd93a3a..8761a77865 100644 --- a/lib/Alchemy/Phrasea/Model/Entities/StoryWZ.php +++ b/lib/Alchemy/Phrasea/Model/Entities/StoryWZ.php @@ -116,8 +116,8 @@ class StoryWZ public function setRecord(\record_adapter $record) { - $this->setRecordId($record->get_record_id()); - $this->setSbasId($record->get_sbas_id()); + $this->setRecordId($record->getRecordId()); + $this->setSbasId($record->getDataboxId()); } /** * @param User $user diff --git a/lib/Alchemy/Phrasea/Model/Manipulator/LazaretManipulator.php b/lib/Alchemy/Phrasea/Model/Manipulator/LazaretManipulator.php index 6980775102..7febfbc983 100644 --- a/lib/Alchemy/Phrasea/Model/Manipulator/LazaretManipulator.php +++ b/lib/Alchemy/Phrasea/Model/Manipulator/LazaretManipulator.php @@ -8,6 +8,7 @@ * file that was distributed with this source code. */ namespace Alchemy\Phrasea\Model\Manipulator; + use Alchemy\Phrasea\Application; use Alchemy\Phrasea\Border; use Alchemy\Phrasea\Border\Attribute\AttributeInterface; @@ -17,6 +18,8 @@ use Doctrine\ORM\EntityRepository; use PHPExiftool\Driver\Metadata\Metadata; use Symfony\Component\Filesystem\Exception\IOException; use Symfony\Component\Filesystem\Filesystem; + + class LazaretManipulator { /** @var Application */ @@ -31,6 +34,7 @@ class LazaretManipulator * @var EntityManager */ private $entityManager; + public function __construct(Application $app, EntityRepository $repository, Filesystem $fileSystem, EntityManager $entityManager) { $this->app = $app; @@ -38,23 +42,29 @@ class LazaretManipulator $this->fileSystem = $fileSystem; $this->entityManager = $entityManager; } + public function deny($lazaret_id) { $ret = ['success' => false, 'message' => '', 'result' => []]; + /** @var LazaretFile $lazaretFile */ $lazaretFile = $this->repository->find($lazaret_id); if (null === $lazaretFile) { $ret['message'] = $this->app->trans('File is not present in quarantine anymore, please refresh'); + return $ret; } + try { $this->denyLazaretFile($lazaretFile); $ret['success'] = true; } catch (\Exception $e) { // No-op } + return $ret; } + /** * Empty lazaret * @@ -74,14 +84,17 @@ class LazaretManipulator 'max' => '', ) ); + if( $maxTodo <= 0) { $maxTodo = -1; // all } $ret['result']['max'] = $maxTodo; + $ret['result']['tobedone'] = (int) $this->repository->createQueryBuilder('id') ->select('COUNT(id)') ->getQuery() ->getSingleScalarResult(); + if($maxTodo == -1) { // all $lazaretFiles = $this->repository->findAll(); @@ -89,7 +102,9 @@ class LazaretManipulator // limit maxTodo $lazaretFiles = $this->repository->findBy(array(), null, $maxTodo); } + $this->entityManager->beginTransaction(); + try { foreach ($lazaretFiles as $lazaretFile) { $this->denyLazaretFile($lazaretFile); @@ -102,20 +117,28 @@ class LazaretManipulator $ret['message'] = $this->app->trans('An error occured'); } $ret['result']['todo'] = $ret['result']['tobedone'] - $ret['result']['done']; + return $ret; } + + public function add($file_id, $keepAttributes=true, Array $attributesToKeep=[]) { $ret = ['success' => false, 'message' => '', 'result' => []]; + /* @var LazaretFile $lazaretFile */ $lazaretFile = $this->repository->find($file_id); + if (null === $lazaretFile) { $ret['message'] = $this->app->trans('File is not present in quarantine anymore, please refresh'); + return $ret; } + $path = $this->app['tmp.lazaret.path']; $lazaretFileName = $path .'/'.$lazaretFile->getFilename(); $lazaretThumbFileName = $path .'/'.$lazaretFile->getThumbFilename(); + try { $borderFile = Border\File::buildFromPathfile( $lazaretFileName, @@ -123,12 +146,14 @@ class LazaretManipulator $this->app, $lazaretFile->getOriginalName() ); + //Post record creation /** @var \record_adapter $record */ $record = null; $callBack = function ($element) use (&$record) { $record = $element; }; + //Force creation record $this->getBorderManager()->process( $lazaretFile->getSession(), @@ -136,10 +161,13 @@ class LazaretManipulator $callBack, Border\Manager::FORCE_RECORD ); + if ($keepAttributes) { //add attribute + $metaFields = new Border\MetaFieldsBag(); $metadataBag = new Border\MetadataBag(); + foreach ($lazaretFile->getAttributes() as $attr) { //Check which ones to keep if (!!count($attributesToKeep)) { @@ -147,11 +175,13 @@ class LazaretManipulator continue; } } + try { $attribute = Border\Attribute\Factory::getFileAttribute($this->app, $attr->getName(), $attr->getValue()); } catch (\InvalidArgumentException $e) { continue; } + switch ($attribute->getName()) { case AttributeInterface::NAME_METADATA: /** @var Metadata $value */ @@ -164,7 +194,7 @@ class LazaretManipulator $value->appendChild($record); break; case AttributeInterface::NAME_STATUS: - $record->set_binary_status($attribute->getValue()); + $record->setStatus($attribute->getValue()); break; case AttributeInterface::NAME_METAFIELD: /** @var Border\Attribute\MetaField $attribute */ @@ -172,25 +202,32 @@ class LazaretManipulator break; } } - $data = $metadataBag->toMetadataArray($record->get_databox()->get_meta_structure()); + + $data = $metadataBag->toMetadataArray($record->getDatabox()->get_meta_structure()); $record->set_metadatas($data); - $fields = $metaFields->toMetadataArray($record->get_databox()->get_meta_structure()); + + $fields = $metaFields->toMetadataArray($record->getDatabox()->get_meta_structure()); $record->set_metadatas($fields); } + //Delete lazaret file $this->entityManager->remove($lazaretFile); $this->entityManager->flush(); + $ret['success'] = true; } catch (\Exception $e) { $ret['message'] = $this->app->trans('An error occured'); } + try { $this->fileSystem->remove([$lazaretFileName, $lazaretThumbFileName]); } catch (IOException $e) { // no-op } + return $ret; } + /** * @return Border\Manager */ @@ -198,19 +235,24 @@ class LazaretManipulator { return $this->app['border-manager']; } + protected function denyLazaretFile(LazaretFile $lazaretFile) { $path = $this->app['tmp.lazaret.path']; $lazaretFileName = $path .'/'.$lazaretFile->getFilename(); $lazaretThumbFileName = $path .'/'.$lazaretFile->getThumbFilename(); + $this->entityManager->remove($lazaretFile); $this->entityManager->flush(); + try { $this->fileSystem->remove([$lazaretFileName, $lazaretThumbFileName]); } catch (IOException $e) { // no-op } + return $this; } -} + +} diff --git a/lib/Alchemy/Phrasea/Model/Repositories/BasketRepository.php b/lib/Alchemy/Phrasea/Model/Repositories/BasketRepository.php index 7f02a63ba2..6304f3eaa6 100644 --- a/lib/Alchemy/Phrasea/Model/Repositories/BasketRepository.php +++ b/lib/Alchemy/Phrasea/Model/Repositories/BasketRepository.php @@ -196,7 +196,7 @@ class BasketRepository extends EntityRepository AND b.user = :usr_id'; $params = [ - 'record_id' => $record->get_record_id(), + 'record_id' => $record->getRecordId(), 'usr_id' => $user->getId() ]; diff --git a/lib/Alchemy/Phrasea/Model/Repositories/StoryWZRepository.php b/lib/Alchemy/Phrasea/Model/Repositories/StoryWZRepository.php index 9b6f3f1605..d973d9ccdf 100644 --- a/lib/Alchemy/Phrasea/Model/Repositories/StoryWZRepository.php +++ b/lib/Alchemy/Phrasea/Model/Repositories/StoryWZRepository.php @@ -99,8 +99,8 @@ class StoryWZRepository extends EntityRepository { $story = $this->findOneBy([ 'user' => $user->getId(), - 'sbas_id' => $Story->get_sbas_id(), - 'record_id' => $Story->get_record_id(), + 'sbas_id' => $Story->getDataboxId(), + 'record_id' => $Story->getRecordId(), ]); if ($story) { @@ -129,8 +129,8 @@ class StoryWZRepository extends EntityRepository $query = $this->_em->createQuery($dql); $query->setParameters([ - 'sbas_id' => $Story->get_sbas_id(), - 'record_id' => $Story->get_record_id(), + 'sbas_id' => $Story->getDataboxId(), + 'record_id' => $Story->getRecordId(), ]); /** @var StoryWZ[] $stories */ diff --git a/lib/Alchemy/Phrasea/Model/Serializer/CaptionSerializer.php b/lib/Alchemy/Phrasea/Model/Serializer/CaptionSerializer.php index 10af2018cf..3c0618e949 100644 --- a/lib/Alchemy/Phrasea/Model/Serializer/CaptionSerializer.php +++ b/lib/Alchemy/Phrasea/Model/Serializer/CaptionSerializer.php @@ -1,9 +1,8 @@ technicalDataService = $technicalDataService; + } + + /** + * @param \caption_record $caption + * @param string $format + * @param bool $includeBusinessFields + * @return string + * @throws \Exception + */ public function serialize(\caption_record $caption, $format, $includeBusinessFields = false) { switch ($format) { case self::SERIALIZE_XML: - return $this->serializeXML($caption, (Boolean) $includeBusinessFields); - break; + return $this->serializeXML($caption, (bool) $includeBusinessFields); case self::SERIALIZE_YAML: - return $this->serializeYAML($caption, (Boolean) $includeBusinessFields); - break; + return $this->serializeYAML($caption, (bool) $includeBusinessFields); case self::SERIALIZE_JSON: - return $this->serializeJSON($caption, (Boolean) $includeBusinessFields); - break; + return $this->serializeJSON($caption, (bool) $includeBusinessFields); default: throw new \Exception(sprintf('Unknown format %s', $format)); - break; } } @@ -47,6 +72,11 @@ class CaptionSerializer extends AbstractSerializer return \p4string::jsonencode($this->toArray($caption, $includeBusinessFields)); } + /** + * @param \caption_record $caption + * @param bool $includeBusinessFields + * @return array + */ public function toArray(\caption_record $caption, $includeBusinessFields) { $buffer = []; @@ -77,7 +107,7 @@ class CaptionSerializer extends AbstractSerializer $dom_doc->standalone = true; $record = $dom_doc->createElement('record'); - $record->setAttribute('record_id', $caption->get_record()->get_record_id()); + $record->setAttribute('record_id', $caption->getRecordReference()->getRecordId()); $dom_doc->appendChild($record); $description = $dom_doc->createElement('description'); $record->appendChild($description); @@ -96,7 +126,8 @@ class CaptionSerializer extends AbstractSerializer $doc = $dom_doc->createElement('doc'); - $tc_datas = $caption->get_record()->get_technical_infos()->getValues(); + $technicalData = $this->getTechnicalDataService()->fetchRecordsTechnicalData([$caption->getRecordReference()]); + $tc_datas = $technicalData[0]->getValues(); foreach ($tc_datas as $key => $data) { $doc->setAttribute($key, $data); @@ -106,4 +137,27 @@ class CaptionSerializer extends AbstractSerializer return $dom_doc->saveXML(); } + + /** + * @return TechnicalDataService + * @throws \UnexpectedValueException + */ + private function getTechnicalDataService() + { + if (!$this->technicalDataService instanceof TechnicalDataService) { + $instance = call_user_func($this->technicalDataService); + + if (!$instance instanceof TechnicalDataService) { + throw new \UnexpectedValueException(sprintf( + 'Expected a %s instance, got %s.', + TechnicalDataService::class, + is_object($instance) ? get_class($instance) : gettype($instance) + )); + } + + $this->technicalDataService = $instance; + } + + return $this->technicalDataService; + } } diff --git a/lib/Alchemy/Phrasea/Model/Serializer/ESRecordSerializer.php b/lib/Alchemy/Phrasea/Model/Serializer/ESRecordSerializer.php index e3a991c436..8c63c947dd 100644 --- a/lib/Alchemy/Phrasea/Model/Serializer/ESRecordSerializer.php +++ b/lib/Alchemy/Phrasea/Model/Serializer/ESRecordSerializer.php @@ -50,7 +50,7 @@ class ESRecordSerializer extends AbstractSerializer } $i = 0; - foreach (preg_split('//', strrev($record->get_status()), -1, PREG_SPLIT_NO_EMPTY) as $val) { + foreach (preg_split('//', strrev($record->getStatus()), -1, PREG_SPLIT_NO_EMPTY) as $val) { $status['status-'.$i] = (int) $val; $i++; } diff --git a/lib/Alchemy/Phrasea/Order/Controller/ApiOrderController.php b/lib/Alchemy/Phrasea/Order/Controller/ApiOrderController.php new file mode 100644 index 0000000000..2b0e05a618 --- /dev/null +++ b/lib/Alchemy/Phrasea/Order/Controller/ApiOrderController.php @@ -0,0 +1,260 @@ +decodeJsonBody($request, 'orders.json#/definitions/order_request'); + + $availableRecords = $this->toRequestedRecords($data->data->records); + $records = $this->filterOrderableRecords($availableRecords); + + $recordRequest = new RecordsRequest($records, new ArrayCollection($availableRecords), null, RecordsRequest::FLATTEN_YES); + + $filler = new OrderFiller($this->app['repo.collection-references'], $this->app['orm.em']); + + $filler->assertAllRecordsHaveOrderMaster($recordRequest); + + $order = new Order(); + $order->setUser($this->getAuthenticatedUser()); + $order->setDeadline(new \DateTime($data->data->deadline, new \DateTimeZone('UTC'))); + $order->setOrderUsage($data->data->usage); + + $filler->fillOrder($order, $recordRequest); + + $this->dispatch(PhraseaEvents::ORDER_CREATE, new OrderEvent($order)); + + $resource = new Item($order, $this->getOrderTransformer()); + + return $this->returnResourceResponse($request, ['elements'], $resource); + } + + public function indexAction(Request $request) + { + $page = max((int) $request->get('page', '1'), 1); + $perPage = min(max((int)$request->get('per_page', '10'), 1), 100); + $fractal = $this->buildFractalManager($request->get('includes', [])); + + $routeGenerator = function ($page) use ($perPage) { + return $this->app->path('api_v2_orders_index', [ + 'page' => $page, + 'per_page' => $perPage, + ]); + }; + + $builder = $this->app['repo.orders']->createQueryBuilder('o'); + $builder + ->where($builder->expr()->eq('o.user', $this->getAuthenticatedUser()->getId())) + ; + + if (in_array('elements', $fractal->getRequestedIncludes(), false)) { + $builder + ->addSelect('e') + ->leftJoin('o.elements', 'e') + ; + } + + $collection = $this->getViewBuilder()->buildViews( + $builder->getQuery()->getResult(), + $fractal->getRequestedIncludes() + ); + + $resource = new Collection($collection, $this->getOrderTransformer()); + + $pager = new Pagerfanta(new DoctrineORMAdapter($builder, false)); + $pager->setCurrentPage($page); + $pager->setMaxPerPage($perPage); + + $resource->setPaginator(new PagerfantaPaginatorAdapter($pager, $routeGenerator)); + + return $this->returnResourceResponse($request, $fractal, $resource); + } + + /** + * @param Request $request + * @param int $orderId + * @return Response + */ + public function showAction(Request $request, $orderId) + { + $order = $this->findOr404($orderId); + + $fractal = $this->buildFractalManager($request->get('includes', [])); + + if ($order->getUser()->getId() !== $this->getAuthenticatedUser()->getId()) { + throw new AccessDeniedHttpException(sprintf('Cannot access order "%d"', $order->getId())); + } + + $model = $this->getViewBuilder()->buildView($order, $fractal->getRequestedIncludes()); + $resource = new Item($model, $this->getOrderTransformer()); + + return $this->returnResourceResponse($request, $fractal, $resource); + } + + public function acceptElementsAction(Request $request, $orderId) + { + $elementIds = $this->fetchElementIdsFromRequest($request); + + $elements = $this->doAcceptElements($orderId, $elementIds, $this->getAuthenticatedUser()); + + $resource = new Collection($elements, function (BasketElement $element) { + return [ + 'id' => $element->getId(), + 'created' => $element->getCreated(), + 'databox_id' => $element->getSbasId(), + 'record_id' => $element->getRecordId(), + 'index' => $element->getOrd(), + ]; + }); + + return $this->returnResourceResponse($request, [], $resource); + } + + public function denyElementsAction(Request $request, $orderId) + { + $elementIds = $this->fetchElementIdsFromRequest($request); + + $this->doDenyElements($orderId, $elementIds, $this->getAuthenticatedUser()); + + return Result::create($request, [])->createResponse(); + } + + /** + * @param array $records + * @return \record_adapter[] + */ + private function toRequestedRecords(array $records) + { + $requestedRecords = []; + + foreach ($records as $item) { + $requestedRecords[] = [ + 'databox_id' => $item->databox_id, + 'record_id' => $item->record_id, + ]; + } + + return RecordReferenceCollection::fromArrayOfArray($requestedRecords)->toRecords($this->getApplicationBox()); + } + + /** + * @param \record_adapter[] $records + * @return \record_adapter[] + */ + private function filterOrderableRecords(array $records) + { + $acl = $this->getAclForUser(); + + $filtered = []; + + foreach ($records as $index => $record) { + if ($acl->has_right_on_base($record->getBaseId(), 'cancmd')) { + $filtered[$index] = $record; + } + } + + return $filtered; + } + + /** + * @return OrderTransformer + */ + private function getOrderTransformer() + { + return new OrderTransformer(new OrderElementTransformer($this->app['media_accessor.subdef_url_generator'])); + } + + /** + * @param string|array $includes + * @return Manager + */ + private function buildFractalManager($includes) + { + $fractal = new Manager(); + + $fractal->parseIncludes($includes ?: []); + + return $fractal; + } + + /** + * @param Request $request + * @param string|array|Manager $includes + * @param ResourceInterface $resource + * @return Response + */ + private function returnResourceResponse(Request $request, $includes, ResourceInterface $resource) + { + $fractal = $includes instanceof Manager ? $includes : $this->buildFractalManager($includes); + + return Result::create($request, $fractal->createData($resource)->toArray())->createResponse(); + } + + /** + * @param Request $request + * @return array + */ + private function fetchElementIdsFromRequest(Request $request) + { + $data = $this->decodeJsonBody($request, 'orders.json#/definitions/order_element_collection'); + + $elementIds = []; + + foreach ($data as $elementId) { + $elementIds[] = $elementId->id; + } + + return $elementIds; + } + + private function getViewBuilder() + { + return new OrderViewBuilder( + $this->app, + $this->getApplicationBox(), + $this->app['service.media_subdef'] + ); + } +} diff --git a/lib/Alchemy/Phrasea/Order/Controller/BaseOrderController.php b/lib/Alchemy/Phrasea/Order/Controller/BaseOrderController.php index eaee31a899..14f10cb5da 100644 --- a/lib/Alchemy/Phrasea/Order/Controller/BaseOrderController.php +++ b/lib/Alchemy/Phrasea/Order/Controller/BaseOrderController.php @@ -189,10 +189,12 @@ class BaseOrderController extends Controller $references = new RecordReferenceCollection(); - foreach ($elements as $element) { - $reference = RecordReference::createFromDataboxIdAndRecordId($element->getSbasId(), $element->getRecordId()); + $basket->getElements()->forAll(function (BasketElement $element) use ($references) { + $references->addRecordReference($element->getSbasId(), $element->getRecordId()); + }); - $references->addRecordReference($reference); + foreach ($elements as $element) { + $references->addRecordReference($element->getSbasId(), $element->getRecordId()); } $groups = $references->groupPerDataboxId(); diff --git a/lib/Alchemy/Phrasea/Order/OrderElementTransformer.php b/lib/Alchemy/Phrasea/Order/OrderElementTransformer.php new file mode 100644 index 0000000000..2e961dcf93 --- /dev/null +++ b/lib/Alchemy/Phrasea/Order/OrderElementTransformer.php @@ -0,0 +1,108 @@ +urlGenerator = $urlGenerator; + } + + public function transform(OrderElementView $model) + { + $element = $model->getElement(); + $record = $model->getRecordReference(); + + $data = [ + 'id' => $element->getId(), + 'record' => [ + 'databox_id' => $record->getDataboxId(), + 'record_id' => $record->getRecordId(), + ], + ]; + + $data['status'] = 'pending'; + + if (null !== $element->getOrderMaster()) { + $data['validator_id'] = $element->getOrderMaster()->getId(); + $data['status'] = $element->getDeny() ? 'rejected' : 'accepted'; + } + + return $data; + } + + public function includeResourceLinks(OrderElementView $model, ParamBag $params = null) + { + $parameterArray = $this->extractParamBagValues($params); + $usedParams = array_keys(array_filter($parameterArray)); + + if (array_diff($usedParams, $this->validParams)) { + throw new \RuntimeException(sprintf( + 'Invalid param(s): "%s". Valid param(s): "%s"', + implode(', ', $usedParams), + implode(', ', $this->validParams) + )); + } + + list ($ttl) = $parameterArray['ttl']; + + if (null === $ttl) { + $ttl = $this->urlGenerator->getDefaultTTL(); + } + + $subdefs = $model->getOrderableMediaSubdefs(); + $urls = $this->urlGenerator->generateMany($model->getAuthenticatedUser(), $subdefs, $ttl); + + $data = array_map(null, $subdefs, $urls); + + return $this->collection($data, function (array $data) use ($ttl) { + /** @var \media_subdef $subdef */ + list($subdef, $url) = $data; + + return [ + 'name' => $subdef->get_name(), + 'url' => $url, + 'url_ttl' => $ttl, + ]; + }); + } + + /** + * @param ParamBag|null $params + * @return array + */ + private function extractParamBagValues(ParamBag $params = null) + { + $array = array_fill_keys($this->validParams, null); + + if ($params) { + array_walk($array, function (&$value, $key) use ($params) { + $value = $params[$key]; + }); + } + + return $array; + } +} diff --git a/lib/Alchemy/Phrasea/Order/OrderElementView.php b/lib/Alchemy/Phrasea/Order/OrderElementView.php new file mode 100644 index 0000000000..a4d2c31135 --- /dev/null +++ b/lib/Alchemy/Phrasea/Order/OrderElementView.php @@ -0,0 +1,95 @@ +element = $element; + $this->record = $record; + $this->user = $user; + } + + /** + * @return OrderElement + */ + public function getElement() + { + return $this->element; + } + + /** + * @return RecordReferenceInterface + */ + public function getRecordReference() + { + return $this->record; + } + + /** + * @return User + */ + public function getAuthenticatedUser() + { + return $this->user; + } + + /** + * @param \media_subdef[] $subdefs + */ + public function setOrderableMediaSubdefs($subdefs) + { + Assertion::allIsInstanceOf($subdefs, \media_subdef::class); + + $this->subdefs = $subdefs instanceof \Traversable ? iterator_to_array($subdefs) : $subdefs; + } + + /** + * @return \media_subdef[] + */ + public function getOrderableMediaSubdefs() + { + return $this->subdefs; + } +} diff --git a/lib/Alchemy/Phrasea/Order/OrderTransformer.php b/lib/Alchemy/Phrasea/Order/OrderTransformer.php new file mode 100644 index 0000000000..9838e9dafa --- /dev/null +++ b/lib/Alchemy/Phrasea/Order/OrderTransformer.php @@ -0,0 +1,56 @@ +elementTransformer = $elementTransformer; + } + + public function transform(OrderView $view) + { + $order = $view->getOrder(); + + $data = [ + 'id' => (int)$order->getId(), + 'owner_id' => (int)$order->getUser()->getId(), + 'created' => $order->getCreatedOn()->format(DATE_ATOM), + 'usage' => $order->getOrderUsage(), + 'status' => 0 === $order->getTodo() ? 'finished' : 'pending' + ]; + + if ($order->getDeadline()) { + $data['deadline'] = $order->getDeadline()->format(DATE_ATOM); + } + + return $data; + } + + public function includeElements(OrderView $order) + { + $elements = $order->getElements(); + + return $this->collection($elements, $this->elementTransformer); + } +} diff --git a/lib/Alchemy/Phrasea/Order/OrderValidator.php b/lib/Alchemy/Phrasea/Order/OrderValidator.php index d0e919040f..55339c2697 100644 --- a/lib/Alchemy/Phrasea/Order/OrderValidator.php +++ b/lib/Alchemy/Phrasea/Order/OrderValidator.php @@ -137,7 +137,7 @@ class OrderValidator throw new \RuntimeException('At least one collection was not found.'); } - $references->addRecordReference(RecordReference::createFromDataboxIdAndRecordId( + $references->add(RecordReference::createFromDataboxIdAndRecordId( $databoxIdMap[$orderElement->getBaseId()], $orderElement->getRecordId() )); diff --git a/lib/Alchemy/Phrasea/Order/OrderView.php b/lib/Alchemy/Phrasea/Order/OrderView.php new file mode 100644 index 0000000000..cd43659ba1 --- /dev/null +++ b/lib/Alchemy/Phrasea/Order/OrderView.php @@ -0,0 +1,61 @@ +order = $order; + } + + /** + * @param OrderElementView[] $viewElements + */ + public function setViewElements($viewElements) + { + Assertion::allIsInstanceOf($viewElements, OrderElementView::class); + + $this->viewElements = $viewElements instanceof \Traversable ? iterator_to_array($viewElements) : $viewElements; + } + + /** + * @return Order + */ + public function getOrder() + { + return $this->order; + } + + /** + * @return OrderElementView[] + */ + public function getElements() + { + return $this->viewElements; + } +} diff --git a/lib/Alchemy/Phrasea/Order/OrderViewBuilder.php b/lib/Alchemy/Phrasea/Order/OrderViewBuilder.php new file mode 100644 index 0000000000..bd31b48dbf --- /dev/null +++ b/lib/Alchemy/Phrasea/Order/OrderViewBuilder.php @@ -0,0 +1,221 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Alchemy\Phrasea\Order; + +use Alchemy\Phrasea\Application; +use Alchemy\Phrasea\Databox\Subdef\MediaSubdefService; +use Alchemy\Phrasea\Model\Entities\Order; +use Alchemy\Phrasea\Model\Entities\OrderElement; +use Alchemy\Phrasea\Model\RecordReferenceInterface; +use Alchemy\Phrasea\Record\RecordReference; +use Alchemy\Phrasea\Record\RecordReferenceCollection; +use Assert\Assertion; + +class OrderViewBuilder +{ + + /** + * @var Application + */ + private $application; + + /** + * @var \appbox + */ + private $applicationBox; + + /** + * @var MediaSubdefService + */ + private $mediaSubdefService; + + /** + * @param Application $application + * @param \appbox $appbox + * @param MediaSubdefService $subdefService + */ + public function __construct(Application $application, \appbox $appbox, MediaSubdefService $subdefService) + { + $this->application = $application; + $this->applicationBox = $appbox; + $this->mediaSubdefService = $subdefService; + } + + public function buildView(Order $order, array $includes) + { + $view = new OrderView($order); + + $this->fillViews([$view], $includes); + + return $view; + } + + /** + * @param Order[] $orders + * @param string[] $includes + * @return OrderView[] + */ + public function buildViews(array $orders, array $includes) + { + Assertion::allIsInstanceOf($orders, Order::class); + + $views = array_map(function (Order $order) { + return new OrderView($order); + }, $orders); + + $this->fillViews($views, $includes); + + return $views; + } + + /** + * @param OrderView[] $views + * @param array $includes + * @return void + */ + private function fillViews(array $views, array $includes) + { + if (!in_array('elements', $includes, true)) { + return; + } + + $elements = $this->gatherElements($views); + + $allElements = $elements ? call_user_func_array('array_merge', $elements) : []; + $allElements = array_combine( + array_map(function (OrderElement $element) { + return $element->getId(); + }, $allElements), + $allElements + ); + + if (!$allElements) { + return; + } + + $collectionToDataboxMap = $this->mapBaseIdToDataboxId($allElements); + + $records = RecordReferenceCollection::fromListExtractor( + $allElements, + function (OrderElement $element) use ($collectionToDataboxMap) { + return isset($collectionToDataboxMap[$element->getBaseId()]) + ? [$collectionToDataboxMap[$element->getBaseId()], $element->getRecordId()] + : null; + }, + function (array $data) { + list ($databoxId, $recordId) = $data; + + return RecordReference::createFromDataboxIdAndRecordId($databoxId, $recordId); + } + ); + + $this->createOrderElementViews($views, $elements, $records); + + if (!in_array('elements.resource_links', $includes, true)) { + return; + } + + // Load all records + $records->toRecords($this->applicationBox); + + // Load all subdefs + $subdefs = $this->mediaSubdefService->findSubdefsFromRecordReferenceCollection($records); + \media_Permalink_Adapter::getMany($this->application, $subdefs); + + $orderableSubdefs = []; + + foreach ($subdefs as $subdef) { + $databoxId = $subdef->get_sbas_id(); + $recordId = $subdef->get_record_id(); + + if (!isset($orderableSubdefs[$databoxId][$recordId])) { + $orderableSubdefs[$databoxId][$recordId] = []; + } + + $orderableSubdefs[$databoxId][$recordId][] = $subdef; + } + + foreach ($views as $model) { + foreach ($model->getElements() as $element) { + $databoxId = $collectionToDataboxMap[$element->getElement()->getBaseId()]; + $recordId = $element->getElement()->getRecordId(); + + if (isset($orderableSubdefs[$databoxId][$recordId])) { + $element->setOrderableMediaSubdefs($orderableSubdefs[$databoxId][$recordId]); + } + } + } + } + + + /** + * @param OrderView[] $orderViews + * @return OrderElement[][] + */ + private function gatherElements(array $orderViews) + { + Assertion::allIsInstanceOf($orderViews, OrderView::class); + + $elements = []; + + foreach ($orderViews as $index => $orderView) { + $elements[$index] = $orderView->getOrder()->getElements()->toArray(); + } + + return $elements; + } + + /** + * @param OrderElement[] $elements + * @return array + */ + private function mapBaseIdToDataboxId(array $elements) + { + $baseIds = array_keys(array_reduce($elements, function (array &$baseIds, OrderElement $element) { + $baseIds[$element->getBaseId()] = true; + + return $baseIds; + }, [])); + + $collectionToDataboxMap = []; + + foreach ($this->application['repo.collection-references']->findMany($baseIds) as $collectionReference) { + $collectionToDataboxMap[$collectionReference->getBaseId()] = $collectionReference->getDataboxId(); + } + + return $collectionToDataboxMap; + } + + /** + * @param OrderView[] $orderViews + * @param OrderElement[][] $elements + * @param RecordReferenceInterface[]|RecordReferenceCollection $records + * @return void + */ + private function createOrderElementViews(array $orderViews, $elements, $records) + { + $user = $this->application->getAuthenticatedUser(); + + foreach ($orderViews as $index => $model) { + $models = []; + + /** @var OrderElement $element */ + foreach ($elements[$index] as $elementIndex => $element) { + if (isset($records[$element->getId()])) { + $models[$elementIndex] = new OrderElementView($element, $records[$element->getId()], $user); + } + } + + $model->setViewElements($models); + } + } +} diff --git a/lib/Alchemy/Phrasea/Out/Module/PDF.php b/lib/Alchemy/Phrasea/Out/Module/PDF.php index 8fe925d8b3..bdbd809655 100644 --- a/lib/Alchemy/Phrasea/Out/Module/PDF.php +++ b/lib/Alchemy/Phrasea/Out/Module/PDF.php @@ -166,7 +166,7 @@ class PDF $fimg = $subdef->getRealPath(); - if (!$this->app->getAclForUser($this->app->getAuthenticatedUser())->has_right_on_base($rec->get_base_id(), "nowatermark") + if (!$this->app->getAclForUser($this->app->getAuthenticatedUser())->has_right_on_base($rec->getBaseId(), "nowatermark") && $subdef->get_type() == \media_subdef::TYPE_IMAGE) { $fimg = \recordutils_image::watermark($this->app, $subdef); } @@ -258,7 +258,7 @@ class PDF $y = $this->pdf->GetY(); - $t = \phrasea::bas_labels($rec->get_base_id(), $this->app); + $t = \phrasea::bas_labels($rec->getBaseId(), $this->app); $this->pdf->SetFont(PhraseaPDF::FONT, '', 10); $this->pdf->SetFillColor(220, 220, 220); $this->pdf->SetLeftMargin($lmargin); @@ -339,10 +339,10 @@ class PDF $RIGHT_TEXT = ""; $RIGHT_IMG = NULL; - $LEFT__IMG = $this->app['root.path'] . "/config/minilogos/logopdf_" . $rec->get_sbas_id() . ".jpg"; + $LEFT__IMG = $this->app['root.path'] . "/config/minilogos/logopdf_" . $rec->getDataboxId() . ".jpg"; if (!is_file($LEFT__IMG)) { - $databox = $rec->get_databox(); + $databox = $rec->getDatabox(); $str = $databox->get_sxml_structure(); $vn = (string) ($str->pdfPrintLogo); if (($vn * 1) == 1) { @@ -350,7 +350,7 @@ class PDF } } - $collection = \collection::getByBaseId($this->app, $rec->get_base_id()); + $collection = \collection::getByBaseId($this->app, $rec->getBaseId()); $vn = ""; if (false !== $str = simplexml_load_string($collection->get_prefs())) { @@ -358,9 +358,9 @@ class PDF } if ($vn == "" || $vn == "1") { - $RIGHT_TEXT = \phrasea::bas_labels($rec->get_base_id(), $this->app); + $RIGHT_TEXT = \phrasea::bas_labels($rec->getBaseId(), $this->app); } elseif ($vn == "2") { - $RIGHT_IMG = $this->app['root.path'] . "/config/minilogos/" . $rec->get_base_id(); + $RIGHT_IMG = $this->app['root.path'] . "/config/minilogos/" . $rec->getBaseId(); } $xtmp = $this->pdf->GetX(); @@ -438,7 +438,7 @@ class PDF $f = $subdef->getRealPath(); - if (!$this->app->getAclForUser($this->app->getAuthenticatedUser())->has_right_on_base($rec->get_base_id(), "nowatermark") + if (!$this->app->getAclForUser($this->app->getAuthenticatedUser())->has_right_on_base($rec->getBaseId(), "nowatermark") && $subdef->get_type() == \media_subdef::TYPE_IMAGE) $f = \recordutils_image::watermark($this->app, $subdef); diff --git a/lib/Alchemy/Phrasea/Record/RecordReferenceCollection.php b/lib/Alchemy/Phrasea/Record/RecordReferenceCollection.php index 802d4a9e67..97bb80ceef 100644 --- a/lib/Alchemy/Phrasea/Record/RecordReferenceCollection.php +++ b/lib/Alchemy/Phrasea/Record/RecordReferenceCollection.php @@ -1,5 +1,5 @@ $records @@ -28,7 +28,7 @@ class RecordReferenceCollection implements \IteratorAggregate foreach ($records as $index => $record) { if (isset($record['id'])) { $references[$index] = RecordReference::createFromRecordReference($record['id']); - } elseif (isset($record['databox_id']) && isset($record['record_id'])) { + } elseif (isset($record['databox_id'], $record['record_id'])) { $references[$index] = RecordReference::createFromDataboxIdAndRecordId($record['databox_id'], $record['record_id']); } } @@ -36,6 +36,43 @@ class RecordReferenceCollection implements \IteratorAggregate return new self($references); } + /** + * Append all RecordReferences extracted via call to extractor on each element + * + * @param array|\Traversable $list List of elements to process + * @param callable $extractor Extracts data from each element or return null if unavailable + * @param callable $creator Creates Reference from extracted data. no-op when null + * @return RecordReferenceCollection + */ + public static function fromListExtractor($list, callable $extractor, callable $creator = null) + { + Assertion::isTraversable($list); + + $references = []; + + if (null === $creator) { + $creator = function ($data) { + return $data; + }; + } + + foreach ($list as $index => $item) { + $data = $extractor($item); + + if (null === $data) { + continue; + } + + $reference = $creator($data); + + if ($reference instanceof RecordReferenceInterface) { + $references[$index] = $reference; + } + } + + return new self($references); + } + /** * @var RecordReferenceInterface[] */ @@ -53,13 +90,35 @@ class RecordReferenceCollection implements \IteratorAggregate { Assertion::allIsInstanceOf($references, RecordReferenceInterface::class); - $this->references = $references instanceof \Traversable ? iterator_to_array($references) : $references; + $this->references = $references instanceof \Traversable ? iterator_to_array($references, true) : $references; } - public function addRecordReference(RecordReferenceInterface $reference) + /** + * @param RecordReferenceInterface $reference + * @param null|string|int $index + */ + public function add(RecordReferenceInterface $reference, $index = null) { - $this->references[] = $reference; $this->groups = null; + + if (null === $index) { + $this->references[] = $reference; + + return; + } + + $this->references[$index] = $reference; + } + + /** + * @param int $databoxId + * @param int $recordId + * @param null|string|int $index + * @return void + */ + public function addRecordReference($databoxId, $recordId, $index = null) + { + $this->add(RecordReference::createFromDataboxIdAndRecordId($databoxId, $recordId), $index); } public function getIterator() @@ -115,8 +174,48 @@ class RecordReferenceCollection implements \IteratorAggregate } } - ksort($records); + $indexes = array_flip(array_keys($this->references)); - return array_values($records); + uksort($records, function ($keyA, $keyB) use ($indexes) { + $indexA = $indexes[$keyA]; + $indexB = $indexes[$keyB]; + + if ($indexA < $indexB) { + return -1; + } elseif ($indexA > $indexB) { + return 1; + } + + return 0; + }); + + return $records; + } + + public function offsetExists($offset) + { + return isset($this->references[$offset]); + } + + /** + * @param mixed $offset + * @return RecordReferenceInterface + */ + public function offsetGet($offset) + { + return $this->references[$offset]; + } + + public function offsetSet($offset, $value) + { + Assertion::isInstanceOf($value, RecordReferenceInterface::class); + + $this->add($value, $offset); + } + + public function offsetUnset($offset) + { + unset($this->references[$offset]); + $this->groups = null; } } diff --git a/lib/Alchemy/Phrasea/SearchEngine/Elastic/ElasticSearchEngine.php b/lib/Alchemy/Phrasea/SearchEngine/Elastic/ElasticSearchEngine.php index 4807f28b81..073325d510 100644 --- a/lib/Alchemy/Phrasea/SearchEngine/Elastic/ElasticSearchEngine.php +++ b/lib/Alchemy/Phrasea/SearchEngine/Elastic/ElasticSearchEngine.php @@ -268,7 +268,7 @@ class ElasticSearchEngine implements SearchEngineInterface /** * {@inheritdoc} */ - public function query($string, $offset, $perPage, SearchEngineOptions $options = null) + public function query($string, SearchEngineOptions $options = null) { $options = $options ?: new SearchEngineOptions(); $context = $this->context_factory->createContext($options); @@ -282,13 +282,15 @@ class ElasticSearchEngine implements SearchEngineInterface // ask ES to return field _version (incremental version number of document) $params['body']['version'] = true; - $params['body']['from'] = $offset; - $params['body']['size'] = $perPage; + $params['body']['from'] = $options->getFirstResult(); + $params['body']['size'] = $options->getMaxResults(); if($this->options->getHighlight()) { $params['body']['highlight'] = $this->buildHighlightRules($context); } - if ($aggs = $this->getAggregationQueryParams($options)) { + $aggs = $this->getAggregationQueryParams($options); + + if ($aggs) { $params['body']['aggs'] = $aggs; } @@ -314,7 +316,7 @@ class ElasticSearchEngine implements SearchEngineInterface $results, // ArrayCollection of results json_encode($query), $res['took'], // duration - $offset, // offset start + $options->getFirstResult(), $res['hits']['total'], // available $res['hits']['total'], // total null, // error @@ -369,16 +371,6 @@ class ElasticSearchEngine implements SearchEngineInterface throw new RuntimeException('Elasticsearch engine currently does not support auto-complete.'); } - /** - * {@inheritdoc} - */ - public function excerpt($query, $fields, \record_adapter $record, SearchEngineOptions $options = null) - { - //@todo implements - - return array(); - } - /** * {@inheritdoc} */ diff --git a/lib/Alchemy/Phrasea/SearchEngine/Elastic/ElasticsearchSettingsFormType.php b/lib/Alchemy/Phrasea/SearchEngine/Elastic/ElasticsearchSettingsFormType.php index 2bd893309e..7874269643 100644 --- a/lib/Alchemy/Phrasea/SearchEngine/Elastic/ElasticsearchSettingsFormType.php +++ b/lib/Alchemy/Phrasea/SearchEngine/Elastic/ElasticsearchSettingsFormType.php @@ -30,6 +30,14 @@ class ElasticsearchSettingsFormType extends AbstractType 'label' => 'ElasticSearch index name', 'constraints' => new NotBlank(), ]) + ->add('esSettingsDropIndexButton', 'button', [ + 'label' => "Drop index", + 'attr' => ['data-id' => "esSettingsDropIndexButton"] + ]) + ->add('esSettingsCreateIndexButton', 'button', [ + 'label' => "Create index", + 'attr' => ['data-id' => "esSettingsCreateIndexButton"] + ]) ->add('shards', 'integer', [ 'label' => 'Number of shards', 'constraints' => new Range(['min' => 1]), diff --git a/lib/Alchemy/Phrasea/SearchEngine/Elastic/Mapping.php b/lib/Alchemy/Phrasea/SearchEngine/Elastic/Mapping.php index 2f44fcc7bd..aad818cc95 100644 --- a/lib/Alchemy/Phrasea/SearchEngine/Elastic/Mapping.php +++ b/lib/Alchemy/Phrasea/SearchEngine/Elastic/Mapping.php @@ -36,6 +36,7 @@ class Mapping const TYPE_LONG = 'long'; const TYPE_SHORT = 'short'; const TYPE_BYTE = 'byte'; + const TYPE_IP = 'ip'; // Compound types const TYPE_OBJECT = 'object'; @@ -49,6 +50,7 @@ class Mapping self::TYPE_LONG, self::TYPE_SHORT, self::TYPE_BYTE, + self::TYPE_IP, ); public function add($name, $type) diff --git a/lib/Alchemy/Phrasea/SearchEngine/SearchEngineInterface.php b/lib/Alchemy/Phrasea/SearchEngine/SearchEngineInterface.php index 4a388ba2ae..e0f993d70b 100644 --- a/lib/Alchemy/Phrasea/SearchEngine/SearchEngineInterface.php +++ b/lib/Alchemy/Phrasea/SearchEngine/SearchEngineInterface.php @@ -76,7 +76,7 @@ interface SearchEngineInterface /** * - * @return an array of self::GEM_TYPE_* indexed types + * @return array an array of self::GEM_TYPE_* indexed types */ public function getAvailableTypes(); @@ -162,15 +162,12 @@ interface SearchEngineInterface public function updateFeedEntry(FeedEntry $entry); /** - * * @param string $query - * @param integer $offset - * @param integer $perPage * @param SearchEngineOptions $options * * @return SearchEngineResult */ - public function query($query, $offset, $perPage, SearchEngineOptions $options = null); + public function query($query, SearchEngineOptions $options = null); /** * Return an array of suggestions corresponding to the last word of the @@ -182,17 +179,6 @@ interface SearchEngineInterface */ public function autocomplete($query, SearchEngineOptions $options); - /** - * Highlight the fields of a record - * - * @param type $query - * @param type $fields - * @param \record_adapter $record - * - * @return array The array of highlighted fields - */ - public function excerpt($query, $fields, \record_adapter $record, SearchEngineOptions $options = null); - /** * Reset the cache of the SE (if applicable) * diff --git a/lib/Alchemy/Phrasea/SearchEngine/SearchEngineOptions.php b/lib/Alchemy/Phrasea/SearchEngine/SearchEngineOptions.php index 0b1dad1798..ac682970c8 100644 --- a/lib/Alchemy/Phrasea/SearchEngine/SearchEngineOptions.php +++ b/lib/Alchemy/Phrasea/SearchEngine/SearchEngineOptions.php @@ -14,6 +14,8 @@ namespace Alchemy\Phrasea\SearchEngine; use Alchemy\Phrasea\Application; use Alchemy\Phrasea\Authentication\ACLProvider; use Alchemy\Phrasea\Authentication\Authenticator; +use Alchemy\Phrasea\Collection\Reference\CollectionReferenceCollection; +use Assert\Assertion; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; @@ -47,9 +49,80 @@ class SearchEngineOptions 'stemming', 'sort_by', 'sort_ord', - 'business_fields' + 'business_fields', + 'max_results', + 'first_result', ]; + /** + * @param Application $app + * @return callable[] + */ + private static function getHydrateMethods(Application $app) + { + $fieldNormalizer = function ($value) use ($app) { + return array_map(function ($serialized) use ($app) { + $data = explode('_', $serialized, 2); + + return $app->findDataboxById($data[0])->get_meta_structure()->get_element($data[1]); + }, $value); + }; + + $collectionNormalizer = function ($value) use ($app) { + $references = new CollectionReferenceCollection($app['repo.collection-references']->findMany($value)); + + $collections = []; + + foreach ($references->groupByDataboxIdAndCollectionId() as $databoxId => $indexes) { + $repository = $app['repo.collections-registry']->getRepositoryByDatabox($databoxId); + + foreach ($indexes as $collectionId => $index) { + $collections[] = $repository->find($collectionId); + } + } + + return $collections; + }; + + $optionSetter = function ($setter) { + return function ($value, SearchEngineOptions $options) use ($setter) { + $options->{$setter}($value); + }; + }; + + return [ + 'record_type' => $optionSetter('setRecordType'), + 'search_type' => $optionSetter('setSearchType'), + 'status' => $optionSetter('setStatus'), + 'date_min' => function ($value, SearchEngineOptions $options) { + $options->setMinDate($value ? \DateTime::createFromFormat(DATE_ATOM, $value) : null); + }, + 'date_max' => function ($value, SearchEngineOptions $options) { + $options->setMaxDate($value ? \DateTime::createFromFormat(DATE_ATOM, $value) : null); + }, + 'i18n' => function ($value, SearchEngineOptions $options) { + if ($value) { + $options->setLocale($value); + } + }, + 'stemming' => $optionSetter('setStemming'), + 'date_fields' => function ($value, SearchEngineOptions $options) use ($fieldNormalizer) { + $options->setDateFields($fieldNormalizer($value)); + }, + 'fields' => function ($value, SearchEngineOptions $options) use ($fieldNormalizer) { + $options->setFields($fieldNormalizer($value)); + }, + 'collections' => function ($value, SearchEngineOptions $options) use ($collectionNormalizer) { + $options->onCollections($collectionNormalizer($value)); + }, + 'business_fields' => function ($value, SearchEngineOptions $options) use ($collectionNormalizer) { + $options->allowBusinessFieldsOn($collectionNormalizer($value)); + }, + 'first_result' => $optionSetter('setFirstResult'), + 'max_results' => $optionSetter('setMaxResults'), + ]; + } + /** @var string */ protected $record_type; @@ -77,6 +150,16 @@ class SearchEngineOptions protected $sort_ord = self::SORT_MODE_DESC; protected $business_fields = []; + /** + * @var int + */ + private $max_results = 10; + + /** + * @var int + */ + private $first_result = 0; + /** * Defines locale code to use for query * @@ -452,86 +535,30 @@ class SearchEngineOptions $options = new static(); $options->disallowBusinessFields(); - $sort_by = $sort_ord = null; + $methods = self::getHydrateMethods($app); + + $sort_by = null; + $methods['sort_by'] = function ($value) use (&$sort_by) { + $sort_by = $value; + }; + + $sort_ord = null; + $methods['sort_ord'] = function ($value) use (&$sort_ord) { + $sort_ord = $value; + }; foreach ($serialized as $key => $value) { - - switch (true) { - case is_null($value): - $value = null; - break; - case in_array($key, ['date_min', 'date_max']): - $value = \DateTime::createFromFormat(DATE_ATOM, $value); - break; - case $value instanceof \stdClass: - $tmpvalue = (array) $value; - $value = []; - - foreach ($tmpvalue as $k => $data) { - $k = ctype_digit($k) ? (int) $k : $k; - $value[$k] = $data; - } - break; - case in_array($key, ['date_fields', 'fields']): - $value = array_map(function ($serialized) use ($app) { - $data = explode('_', $serialized); - - return $app->findDataboxById($data[0])->get_meta_structure()->get_element($data[1]); - }, $value); - break; - case in_array($key, ['collections', 'business_fields']): - $value = array_map(function ($base_id) use ($app) { - return \collection::getByBaseId($app, $base_id); - }, $value); - break; + if (!isset($methods[$key])) { + throw new \RuntimeException(sprintf('Unable to handle key `%s`', $key)); } - switch ($key) { - case 'record_type': - $options->setRecordType($value); - break; - case 'search_type': - $options->setSearchType($value); - break; - case 'status': - $options->setStatus($value); - break; - case 'date_min': - $options->setMinDate($value); - break; - case 'date_max': - $options->setMaxDate($value); - break; - case 'i18n': - if ($value) { - $options->setLocale($value); - } - break; - case 'stemming': - $options->setStemming($value); - break; - case 'sort_by': - $sort_by = $value; - break; - case 'sort_ord': - $sort_ord = $value; - break; - case 'date_fields': - $options->setDateFields($value); - break; - case 'fields': - $options->setFields($value); - break; - case 'collections': - $options->onCollections($value); - break; - case 'business_fields': - $options->allowBusinessFieldsOn($value); - break; - default: - throw new \RuntimeException(sprintf('Unable to handle key `%s`', $key)); - break; + if ($value instanceof \stdClass) { + $value = (array)$value; } + + $callable = $methods[$key]; + + $callable($value, $options); } if ($sort_by) { @@ -682,4 +709,35 @@ class SearchEngineOptions return $options; } + + public function setMaxResults($max_results) + { + Assertion::greaterOrEqualThan($max_results, 0); + + $this->max_results = (int)$max_results; + } + + public function getMaxResults() + { + return $this->max_results; + } + + /** + * @param int $first_result + * @return void + */ + public function setFirstResult($first_result) + { + Assertion::greaterOrEqualThan($first_result, 0); + + $this->first_result = (int)$first_result; + } + + /** + * @return int + */ + public function getFirstResult() + { + return $this->first_result; + } } diff --git a/lib/Alchemy/Phrasea/TaskManager/Job/ArchiveJob.php b/lib/Alchemy/Phrasea/TaskManager/Job/ArchiveJob.php index 3f7965ad21..210d00e85e 100644 --- a/lib/Alchemy/Phrasea/TaskManager/Job/ArchiveJob.php +++ b/lib/Alchemy/Phrasea/TaskManager/Job/ArchiveJob.php @@ -872,7 +872,7 @@ class ArchiveJob extends AbstractJob $story = $this->createStory($app, $collection, $path . '/' . $representationFileName, $path . '/' . $captionFileName, $stat0, $stat1); } - $rid = $story->get_record_id(); + $rid = $story->getRecordId(); $this->log('debug', sprintf('story %s created', $rid)); @@ -1011,7 +1011,7 @@ class ArchiveJob extends AbstractJob $story->set_metadatas($metaFields->toMetadataArray($metadatasStructure), true); } - $story->set_binary_status(\databox_status::operation_or($stat0, $stat1)); + $story->setStatus(\databox_status::operation_or($stat0, $stat1)); $story->rebuild_subdefs(); unset($media); diff --git a/lib/Alchemy/Phrasea/TaskManager/Job/FtpJob.php b/lib/Alchemy/Phrasea/TaskManager/Job/FtpJob.php index a5eabfc08f..332e501a86 100644 --- a/lib/Alchemy/Phrasea/TaskManager/Job/FtpJob.php +++ b/lib/Alchemy/Phrasea/TaskManager/Job/FtpJob.php @@ -412,7 +412,7 @@ class FtpJob extends AbstractJob private function logexport(Application $app, \record_adapter $record, $obj, $ftpLog) { foreach ($obj as $oneObj) { - $app['phraseanet.logger']($record->get_databox()) + $app['phraseanet.logger']($record->getDatabox()) ->log($record, \Session_Logger::EVENT_EXPORTFTP, $ftpLog, ''); } diff --git a/lib/Alchemy/Phrasea/TaskManager/Job/RecordMoverJob.php b/lib/Alchemy/Phrasea/TaskManager/Job/RecordMoverJob.php index 8ac3f3064a..9e2878cdd0 100644 --- a/lib/Alchemy/Phrasea/TaskManager/Job/RecordMoverJob.php +++ b/lib/Alchemy/Phrasea/TaskManager/Job/RecordMoverJob.php @@ -91,14 +91,14 @@ class RecordMoverJob extends AbstractJob // change sb ? if (array_key_exists('sb', $row)) { - $status = str_split($rec->get_status()); + $status = str_split($rec->getStatus()); foreach (str_split(strrev($row['sb'])) as $bit => $val) { if ($val == '0' || $val == '1') { $status[31 - $bit] = $val; } } $status = implode('', $status); - $rec->set_binary_status($status); + $rec->setStatus($status); if ($logsql) { $this->log('debug', sprintf("on sbas %s set rid %s status to %s \n", $row['sbas_id'], $row['record_id'], $status)); } @@ -108,16 +108,16 @@ class RecordMoverJob extends AbstractJob case 'DELETE': if ($row['deletechildren'] && $rec->isStory()) { /** @var record_adapter $child */ - foreach ($rec->get_children() as $child) { + foreach ($rec->getChildren() as $child) { $child->delete(); if ($logsql) { - $this->log('debug', sprintf("on sbas %s delete (grp child) rid %s \n", $row['sbas_id'], $child->get_record_id())); + $this->log('debug', sprintf("on sbas %s delete (grp child) rid %s \n", $row['sbas_id'], $child->getRecordId())); } } } $rec->delete(); if ($logsql) { - $this->log('debug', sprintf("on sbas %s delete rid %s \n", $row['sbas_id'], $rec->get_record_id())); + $this->log('debug', sprintf("on sbas %s delete rid %s \n", $row['sbas_id'], $rec->getRecordId())); } break; } diff --git a/lib/Alchemy/Phrasea/Utilities/LazyArrayAccess.php b/lib/Alchemy/Phrasea/Utilities/LazyArrayAccess.php new file mode 100644 index 0000000000..47418a7ce4 --- /dev/null +++ b/lib/Alchemy/Phrasea/Utilities/LazyArrayAccess.php @@ -0,0 +1,54 @@ +locator = $locator; + } + + public function offsetExists($offset) + { + return $this->fetchArrayAccessible()->offsetExists($offset); + } + + public function offsetGet($offset) + { + return $this->fetchArrayAccessible()->offsetGet($offset); + } + + public function offsetSet($offset, $value) + { + $this->fetchArrayAccessible()->offsetSet($offset, $value); + } + + public function offsetUnset($offset) + { + $this->fetchArrayAccessible()->offsetUnset($offset); + } + + /** + * @return \ArrayAccess + */ + private function fetchArrayAccessible() + { + $locator = $this->locator; + + return $locator(); + } +} diff --git a/lib/Alchemy/Phrasea/Utilities/NullableDateTime.php b/lib/Alchemy/Phrasea/Utilities/NullableDateTime.php new file mode 100644 index 0000000000..6f47da81c7 --- /dev/null +++ b/lib/Alchemy/Phrasea/Utilities/NullableDateTime.php @@ -0,0 +1,19 @@ +format($format) : $default; + } +} diff --git a/lib/Alchemy/Phrasea/Vocabulary/ControlProvider/ControlProviderInterface.php b/lib/Alchemy/Phrasea/Vocabulary/ControlProvider/ControlProviderInterface.php index bbe9153ab2..557f3d2600 100644 --- a/lib/Alchemy/Phrasea/Vocabulary/ControlProvider/ControlProviderInterface.php +++ b/lib/Alchemy/Phrasea/Vocabulary/ControlProvider/ControlProviderInterface.php @@ -1,5 +1,4 @@ app['phraseanet.user-query']; @@ -64,35 +60,28 @@ class UserProvider implements ControlProviderInterface ->limit(0, 50) ->execute()->get_results(); - $results = new ArrayCollection(); - - foreach ($users as $user) { - $results->add( - new Term($user->getDisplayName(), '', $this, $user->getId()) - ); - } - - return $results; + return array_map(function (User $user) { + return new Term($user->getDisplayName(), '', $this, $user->getId()); + }, $users->toArray()); } /** - * * @param mixed $id - * @return boolean + * @return bool */ public function validate($id) { - return (Boolean) $this->app['repo.users']->find($id); + return (bool)$this->fetchUser($id); } /** - * - * @param mixed $id + * @param mixed $id * @return string + * @throws \Exception */ public function getValue($id) { - $user = $this->app['repo.users']->find($id); + $user = $this->fetchUser($id); if (null === $user) { throw new \Exception('User unknown'); @@ -102,11 +91,19 @@ class UserProvider implements ControlProviderInterface } /** - * * @param mixed $id * @return string */ public function getResource($id) + { + return $this->fetchUser($id); + } + + /** + * @param $id + * @return null|User + */ + private function fetchUser($id) { return $this->app['repo.users']->find($id); } diff --git a/lib/Alchemy/Phrasea/Vocabulary/Controller.php b/lib/Alchemy/Phrasea/Vocabulary/Controller.php deleted file mode 100644 index 83ae884ed4..0000000000 --- a/lib/Alchemy/Phrasea/Vocabulary/Controller.php +++ /dev/null @@ -1,51 +0,0 @@ -value = $value; $this->context = $context; $this->type = $type; $this->id = $id; - - return $this; } /** @@ -84,7 +74,6 @@ class Term } /** - * * @return ControlProviderInterface */ public function getType() @@ -93,7 +82,6 @@ class Term } /** - * * @return mixed */ public function getId() diff --git a/lib/classes/ACL.php b/lib/classes/ACL.php index 649a781528..f5f62f4cb5 100644 --- a/lib/classes/ACL.php +++ b/lib/classes/ACL.php @@ -26,6 +26,7 @@ use Alchemy\Phrasea\Core\Event\Acl\SysadminChangedEvent; use Alchemy\Phrasea\Model\Entities\User; use Alchemy\Phrasea\Model\RecordInterface; use Alchemy\Phrasea\Model\RecordReferenceInterface; +use Alchemy\Phrasea\Utilities\NullableDateTime; use Doctrine\DBAL\DBALException; @@ -1706,10 +1707,10 @@ class ACL implements cache_cacheableInterface } $params = [ - ':usr_id' => $this->user->getId() - , ':base_id' => $base_id - , 'limited_from' => ($limit_from ? $limit_from->format(DATE_ISO8601) : null) - , 'limited_to' => ($limit_to ? $limit_to->format(DATE_ISO8601) : null) + ':usr_id' => $this->user->getId(), + ':base_id' => $base_id, + 'limited_from' => NullableDateTime::format($limit_from, DATE_ISO8601), + 'limited_to' => NullableDateTime::format($limit_to, DATE_ISO8601), ]; $stmt = $this->app->getApplicationBox()->get_connection()->prepare($sql); diff --git a/lib/classes/Bridge/Api/Dailymotion.php b/lib/classes/Bridge/Api/Dailymotion.php index 7edc4ebeab..aefa57cd42 100644 --- a/lib/classes/Bridge/Api/Dailymotion.php +++ b/lib/classes/Bridge/Api/Dailymotion.php @@ -466,7 +466,7 @@ class Bridge_Api_Dailymotion extends Bridge_Api_Abstract implements Bridge_Api_I public function acceptable_records() { return function (record_adapter $record) { - return $record->get_type() === 'video'; + return $record->getType() === 'video'; }; } @@ -568,7 +568,7 @@ class Bridge_Api_Dailymotion extends Bridge_Api_Abstract implements Bridge_Api_I */ public function upload(record_adapter $record, array $options = []) { - switch ($record->get_type()) { + switch ($record->getType()) { case self::ELEMENT_TYPE_VIDEO : $url_file = $this->_api->sendFile($record->get_hd_file()->getRealPath(), $this->oauth_token); $options = array_merge(['url' => $url_file], $options); @@ -793,7 +793,7 @@ class Bridge_Api_Dailymotion extends Bridge_Api_Abstract implements Bridge_Api_I { $errors = $this->check_record_constraints($record); $check = function ($field) use (&$errors, $datas, $record) { - $key = $record->get_serialize_key(); + $key = $record->getId(); $required = ! ! $field["required"]; $name = $field["name"]; $length = (int) $field["length"]; @@ -850,7 +850,7 @@ class Bridge_Api_Dailymotion extends Bridge_Api_Abstract implements Bridge_Api_I */ public function get_upload_datas(Request $request, record_adapter $record) { - $key = $record->get_serialize_key(); + $key = $record->getId(); $datas = [ 'title' => $request->get('title_' . $key), 'description' => $request->get('description_' . $key), diff --git a/lib/classes/Bridge/Api/Flickr.php b/lib/classes/Bridge/Api/Flickr.php index d89b7391e8..7d51eba91d 100644 --- a/lib/classes/Bridge/Api/Flickr.php +++ b/lib/classes/Bridge/Api/Flickr.php @@ -504,7 +504,7 @@ class Bridge_Api_Flickr extends Bridge_Api_Abstract implements Bridge_Api_Interf $privacy = $this->get_default_privacy(); $uploader->setPerms($privacy['public'], $privacy['friends'], $privacy['family']); - $type = $record->get_type() == 'image' ? self::ELEMENT_TYPE_PHOTO : $record->get_type(); + $type = $record->getType() == 'image' ? self::ELEMENT_TYPE_PHOTO : $record->getType(); switch ($type) { case self::ELEMENT_TYPE_PHOTO : return $uploader->upload($record->get_hd_file()->getRealPath(), $options['title'], $options['description'], $options['tags'], true); @@ -522,7 +522,7 @@ class Bridge_Api_Flickr extends Bridge_Api_Abstract implements Bridge_Api_Interf public function acceptable_records() { return function (record_adapter $record) { - return in_array($record->get_type(), ['image']); + return in_array($record->getType(), ['image']); }; } @@ -683,7 +683,7 @@ class Bridge_Api_Flickr extends Bridge_Api_Abstract implements Bridge_Api_Interf { $errors = $this->check_record_constraints($record); $check = function ($field) use (&$errors, $datas, $record) { - $key = $record->get_serialize_key(); + $key = $record->getId(); $name = $field['name']; $length = (int) $field['length']; $required = ! ! $field['required']; @@ -751,7 +751,7 @@ class Bridge_Api_Flickr extends Bridge_Api_Abstract implements Bridge_Api_Interf */ public function get_upload_datas(Request $request, record_adapter $record) { - $key = $record->get_serialize_key(); + $key = $record->getId(); $datas = [ 'title' => $request->get('title_' . $key), 'description' => $request->get('description_' . $key), diff --git a/lib/classes/Bridge/Api/Youtube.php b/lib/classes/Bridge/Api/Youtube.php index 98779a273b..75e99a4113 100644 --- a/lib/classes/Bridge/Api/Youtube.php +++ b/lib/classes/Bridge/Api/Youtube.php @@ -418,7 +418,7 @@ class Bridge_Api_Youtube extends Bridge_Api_Abstract implements Bridge_Api_Inter public function acceptable_records() { return function (record_adapter $record) { - return $record->get_type() === 'video'; + return $record->getType() === 'video'; }; } @@ -646,7 +646,7 @@ class Bridge_Api_Youtube extends Bridge_Api_Abstract implements Bridge_Api_Inter */ public function upload(record_adapter $record, array $options = []) { - switch ($record->get_type()) { + switch ($record->getType()) { case 'video': $video_entry = new Zend_Gdata_YouTube_VideoEntry(); @@ -902,7 +902,7 @@ class Bridge_Api_Youtube extends Bridge_Api_Abstract implements Bridge_Api_Inter $errors = $this->check_record_constraints($record); $check = function ($field) use (&$errors, $datas, $record) { - $key = $record->get_serialize_key(); + $key = $record->getId(); $name = $field['name']; $length = (int) $field['length']; $required = ! ! $field['required']; @@ -981,7 +981,7 @@ class Bridge_Api_Youtube extends Bridge_Api_Abstract implements Bridge_Api_Inter */ public function get_upload_datas(Request $request, record_adapter $record) { - $key = $record->get_serialize_key(); + $key = $record->getId(); $datas = [ 'title' => $request->get('title_' . $key), 'description' => $request->get('description_' . $key), @@ -1012,7 +1012,7 @@ class Bridge_Api_Youtube extends Bridge_Api_Abstract implements Bridge_Api_Inter private function check_record_constraints(record_adapter $record) { $errors = []; - $key = $record->get_serialize_key(); + $key = $record->getId(); //Record must rely on real file if ( ! $record->get_hd_file() instanceof SplFileInfo) { $errors["file_size_" . $key] = $this->translator->trans("Le record n'a pas de fichier physique"); diff --git a/lib/classes/Bridge/Element.php b/lib/classes/Bridge/Element.php index 84f0184446..5a81808e55 100644 --- a/lib/classes/Bridge/Element.php +++ b/lib/classes/Bridge/Element.php @@ -10,6 +10,7 @@ */ use Alchemy\Phrasea\Application; +use Alchemy\Phrasea\Utilities\NullableDateTime; class Bridge_Element { @@ -383,9 +384,9 @@ class Bridge_Element SET uploaded_on = :uploaded_on, updated_on = :update WHERE id = :id'; $params = [ - ':uploaded_on' => $this->uploaded_on ? $this->uploaded_on->format(DATE_ISO8601) : null - , ':id' => $this->id - , ':update' => $this->updated_on->format(DATE_ISO8601) + ':uploaded_on' => NullableDateTime::format($this->uploaded_on, DATE_ISO8601), + ':id' => $this->id, + ':update' => $this->updated_on->format(DATE_ISO8601), ]; $stmt = $this->app->getApplicationBox()->get_connection()->prepare($sql); @@ -448,13 +449,13 @@ class Bridge_Element ,:datas , :status, NOW(), NOW())'; $params = [ - ':account_id' => $account->get_id() - , ':sbas_id' => $record->get_sbas_id() - , ':record_id' => $record->get_record_id() - , ':status' => $status - , ':title' => $title - , ':type' => $type - , ':datas' => serialize($datas) + ':account_id' => $account->get_id(), + ':sbas_id' => $record->getDataboxId(), + ':record_id' => $record->getRecordId(), + ':status' => $status, + ':title' => $title, + ':type' => $type, + ':datas' => serialize($datas), ]; $stmt = $app->getApplicationBox()->get_connection()->prepare($sql); diff --git a/lib/classes/Session/Logger.php b/lib/classes/Session/Logger.php index 02f838492a..9ab980b192 100644 --- a/lib/classes/Session/Logger.php +++ b/lib/classes/Session/Logger.php @@ -73,11 +73,11 @@ class Session_Logger $stmt = $this->databox->get_connection()->prepare($sql); $params = [ - ':log_id' => $this->get_id() - , ':record_id' => $record->get_record_id() - , ':action' => $action - , ':final' => $final - , ':comm' => $comment + ':log_id' => $this->get_id(), + ':record_id' => $record->getRecordId(), + ':action' => $action, + ':final' => $final, + ':comm' => $comment, ]; $stmt->execute($params); diff --git a/lib/classes/cache/databox.php b/lib/classes/cache/databox.php index 229eb7676c..4e9ed4dd82 100644 --- a/lib/classes/cache/databox.php +++ b/lib/classes/cache/databox.php @@ -75,8 +75,6 @@ class cache_databox $databox->delete_data_from_cache($key); $key = 'record_' . $sbas_id . '_' . $row['value'] . '_' . \record_adapter::CACHE_SHA256; $databox->delete_data_from_cache($key); - $key = 'record_' . $sbas_id . '_' . $row['value'] . '_' . \record_adapter::CACHE_STATUS; - $databox->delete_data_from_cache($key); $key = 'record_' . $sbas_id . '_' . $row['value'] . '_' . \record_adapter::CACHE_TECHNICAL_DATA; $databox->delete_data_from_cache($key); diff --git a/lib/classes/caption/Field/ThesaurusValue.php b/lib/classes/caption/Field/ThesaurusValue.php deleted file mode 100644 index 6fbdfb05ee..0000000000 --- a/lib/classes/caption/Field/ThesaurusValue.php +++ /dev/null @@ -1,47 +0,0 @@ -value = $value; - $this->field = $field; - $this->query = $query; - } - - public function getValue() - { - return $this->value; - } - - public function getField() - { - return $this->field; - } - - public function getQuery() - { - return $this->query; - } - - public function __toString() - { - return $this->value; - } -} diff --git a/lib/classes/caption/Field/Value.php b/lib/classes/caption/Field/Value.php index d216de6f78..b51ef904bf 100644 --- a/lib/classes/caption/Field/Value.php +++ b/lib/classes/caption/Field/Value.php @@ -11,30 +11,46 @@ use Alchemy\Phrasea\Application; use Alchemy\Phrasea\Utilities\StringHelper; -use Alchemy\Phrasea\Vocabulary; +use Alchemy\Phrasea\Vocabulary\ControlProvider\ControlProviderInterface; class caption_Field_Value implements cache_cacheableInterface { const RETRIEVE_VALUES = true; const DONT_RETRIEVE_VALUES = false; - /** @var int */ + /** + * @var int + */ protected $id; - /** @var string */ + /** + * @var string + */ protected $value; - /** @var \Alchemy\Phrasea\Vocabulary\ControlProvider\ControlProviderInterface */ - protected $VocabularyType; + /** + * @var ControlProviderInterface|null + */ + protected $vocabularyType; - /** @var int */ - protected $VocabularyId; + /** + * @var mixed + */ + protected $vocabularyId; - /** @var databox_field */ + /** + * @var databox_field + */ protected $databox_field; - /** @var record_adapter */ + /** + * @var record_adapter + */ protected $record; + + /** + * @var Application + */ protected $app; /** @@ -45,21 +61,20 @@ class caption_Field_Value implements cache_cacheableInterface */ protected $qjs; - /* + /** * Tells whether the value is matched against a thesaurus value. + * @var bool */ protected $isThesaurusValue; protected static $localCache = []; /** - * - * @param Application $app - * @param databox_field $databox_field - * @param record_adapter $record - * @param mixed $id - * @param bool $retrieveValues - * @return \caption_Field_Value + * @param Application $app + * @param databox_field $databox_field + * @param record_adapter $record + * @param mixed $id + * @param bool $retrieveValues */ public function __construct(Application $app, databox_field $databox_field, record_adapter $record, $id, $retrieveValues = self::RETRIEVE_VALUES) { @@ -68,107 +83,94 @@ class caption_Field_Value implements cache_cacheableInterface $this->record = $record; $this->app = $app; - if($retrieveValues == self::RETRIEVE_VALUES) { + if ($retrieveValues === self::RETRIEVE_VALUES) { $this->retrieveValues(); } } + /** + * @return string + */ public function getQjs() { return $this->qjs; } - public function injectValues($value, $VocabularyType, $VocabularyId) + /** + * @param string $value + * @param string $vocabularyType + * @param string $vocabularyId + */ + 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) { + $this->fetchVocabulary($vocabularyType, $vocabularyId); - } - - if ($this->VocabularyType) { - /** - * Vocabulary Control has been deactivated - */ - if ( ! $this->databox_field->getVocabularyControl()) { + if ($this->vocabularyType) { + if (!$this->databox_field->getVocabularyControl()) { + // Vocabulary Control has been deactivated $this->removeVocabulary(); - } - /** - * Vocabulary Control has changed - */ - elseif ($this->databox_field->getVocabularyControl()->getType() !== $this->VocabularyType->getType()) { + } elseif ($this->databox_field->getVocabularyControl()->getType() !== $this->vocabularyType->getType()) { + // Vocabulary Control has changed $this->removeVocabulary(); - } - /** - * Current Id is not available anymore - */ - elseif ( ! $this->VocabularyType->validate($this->VocabularyId)) { + } elseif (!$this->vocabularyType->validate($this->vocabularyId)) { + // Current Id is not available anymore $this->removeVocabulary(); - } - /** - * String equivalence has changed - */ - elseif ($this->VocabularyType->getValue($this->VocabularyId) !== $this->value) { - $this->set_value($this->VocabularyType->getValue($this->VocabularyId)); + } elseif ($this->vocabularyType->getValue($this->vocabularyId) !== $this->value) { + // String equivalence has changed + $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 { - $datas = $this->get_data_from_cache(); - - $this->value = $datas['value']; - $this->VocabularyType = $datas['vocabularyType'] ? Vocabulary\Controller::get($this->app, $datas['vocabularyType']) : null; - $this->VocabularyId = $datas['vocabularyId']; - - return $this; + $data = $this->get_data_from_cache(); + $cacheRefreshNeeded = false; } catch (\Exception $e) { + $data = $this->databox_field->get_databox()->get_connection() + ->fetchAssoc( + 'SELECT value, VocabularyType as vocabularyType, VocabularyId as vocabularyId FROM metadatas WHERE id = :id', + [':id' => $this->id] + ); + if (!is_array($data)) { + $data = [ + 'value' => null, + 'vocabularyId' => null, + 'vocabularyType' => null, + ]; + } + + $cacheRefreshNeeded = true; } - $connbas = $this->databox_field->get_databox()->get_connection(); + $this->injectValues($data['value'], $data['vocabularyType'], $data['vocabularyId']); - $sql = 'SELECT record_id, value, VocabularyType, VocabularyId FROM metadatas WHERE id = :id'; - - $stmt = $connbas->prepare($sql); - $stmt->execute([':id' => $this->id]); - $row = $stmt->fetch(PDO::FETCH_ASSOC); - $stmt->closeCursor(); - - if($row) { - $this->injectValues($row['value'], $row['VocabularyType'], $row['VocabularyId']); - } - else { - $this->injectValues(null, null, null); + if ($cacheRefreshNeeded) { + $this->set_data_to_cache([ + 'value' => $this->value, + 'vocabularyId' => $this->vocabularyId, + 'vocabularyType' => $this->vocabularyType ? $this->vocabularyType->getType() : null, + ]); } return $this; } /** - * @return Vocabulary\ControlProvider\ControlProviderInterface + * @return ControlProviderInterface|null */ public function getVocabularyType() { - return $this->VocabularyType; + return $this->vocabularyType; } public function getVocabularyId() { - return $this->VocabularyId; + return $this->vocabularyId; } public function getId() @@ -183,7 +185,7 @@ class caption_Field_Value implements cache_cacheableInterface public function getResource() { - return $this->VocabularyType ? $this->VocabularyType->getResource($this->VocabularyId) : null; + return $this->vocabularyType ? $this->vocabularyType->getResource($this->vocabularyId) : null; } public function getDatabox_field() @@ -198,12 +200,7 @@ class caption_Field_Value implements cache_cacheableInterface public function delete() { - $connbas = $this->databox_field->get_connection(); - - $sql = 'DELETE FROM metadatas WHERE id = :id'; - $stmt = $connbas->prepare($sql); - $stmt->execute([':id' => $this->id]); - $stmt->closeCursor(); + $this->getConnection()->delete('metadatas', ['id' => $this->id]); $this->delete_data_from_cache(); $this->databox_field->delete_data_from_cache(); @@ -213,144 +210,129 @@ class caption_Field_Value implements cache_cacheableInterface return $this; } + /** + * @return $this + * @throws \Doctrine\DBAL\DBALException + */ public function removeVocabulary() { - $connbas = $this->databox_field->get_connection(); + $this->getConnection()->executeUpdate( + 'UPDATE metadatas SET VocabularyType = NULL, VocabularyId = NULL WHERE id = :meta_id', + ['meta_id' => $this->getId()] + ); - $params = [ - ':VocabType' => null - , ':VocabularyId' => null - , ':meta_id' => $this->getId() - ]; - - $sql_up = 'UPDATE metadatas' - . ' SET VocabularyType = :VocabType, VocabularyId = :VocabularyId' - . ' WHERE id = :meta_id'; - $stmt_up = $connbas->prepare($sql_up); - $stmt_up->execute($params); - $stmt_up->closeCursor(); - - $this->VocabularyId = $this->VocabularyType = null; + $this->vocabularyId = null; + $this->vocabularyType = null; $this->delete_data_from_cache(); return $this; } - public function setVocab(Vocabulary\ControlProvider\ControlProviderInterface $vocabulary, $vocab_id) + /** + * @param ControlProviderInterface $vocabulary + * @param mixed $vocab_id + * @return $this + * @throws \Doctrine\DBAL\DBALException + */ + public function setVocab(ControlProviderInterface $vocabulary, $vocab_id) { - $connbas = $this->databox_field->get_connection(); - - $params = [ - ':VocabType' => $vocabulary->getType() - , ':VocabularyId' => $vocab_id - , ':meta_id' => $this->getId() - ]; - - $sql_up = 'UPDATE metadatas' - . ' SET VocabularyType = :VocabType, VocabularyId = :VocabularyId' - . ' WHERE id = :meta_id'; - $stmt_up = $connbas->prepare($sql_up); - $stmt_up->execute($params); - $stmt_up->closeCursor(); + $this->getConnection()->executeUpdate( + 'UPDATE metadatas SET VocabularyType = :VocabType, VocabularyId = :VocabularyId WHERE id = :meta_id', + [ + 'VocabType' => $vocabulary->getType(), + 'VocabularyId' => $vocab_id, + 'meta_id' => $this->getId(), + ] + ); $this->set_value($vocabulary->getValue($vocab_id)); return $this; } + /** + * @param ControlProviderInterface|null $vocabulary + * @param mixed|null $vocabularyId + * @return $this + * @throws \Doctrine\DBAL\DBALException + */ + public function changeVocabulary(ControlProviderInterface $vocabulary = null, $vocabularyId = null) + { + if (isset($vocabulary, $vocabularyId)) { + return $this->setVocab($vocabulary, $vocabularyId); + } + + return $this->removeVocabulary(); + } + + /** + * @param string $value + * @return $this + * @throws \Doctrine\DBAL\DBALException + */ public function set_value($value) { $this->value = $value; - $connbas = $this->databox_field->get_connection(); - - $params = [ - ':meta_id' => $this->id - , ':value' => $value - ]; - - $sql_up = 'UPDATE metadatas SET value = :value WHERE id = :meta_id'; - $stmt_up = $connbas->prepare($sql_up); - $stmt_up->execute($params); - $stmt_up->closeCursor(); + $this->getConnection()->executeUpdate( + 'UPDATE metadatas SET value = :value WHERE id = :meta_id', + [ + 'meta_id' => $this->id, + 'value' => $value, + ] + ); $this->delete_data_from_cache(); - $this->update_cache_value($value); - return $this; } - /** - * - * @param array $value - * @return caption_field - */ - public function update_cache_value($value) + public static function create(Application $app, databox_field $databox_field, \record_adapter $record, $value, ControlProviderInterface $vocabulary = null, $vocabularyId = null) { - $this->record->get_caption()->delete_data_from_cache(); + $connection = $databox_field->get_connection(); - return $this; - } - - public static function create(Application $app, databox_field $databox_field, \record_adapter $record, $value, Vocabulary\ControlProvider\ControlProviderInterface $vocabulary = null, $vocabularyId = null) - { - $connbas = $databox_field->get_connection(); - - /** - * Check consistency - */ - if ( ! $databox_field->is_multi()) { + // Check consistency + if (!$databox_field->is_multi()) { try { $field = $record->get_caption()->get_field($databox_field->get_name()); $values = $field->get_values(); - $caption_field_value = array_pop($values); - /* @var $value \caption_Field_Value */ + } catch (Exception $exception) { + // Field was not found, so no values found either + $values = []; + } + if (!empty($values)) { + /** @var caption_Field_Value $caption_field_value */ + $caption_field_value = reset($values); $caption_field_value->set_value($value); - - if (! $vocabulary || ! $vocabularyId) { - $caption_field_value->removeVocabulary(); - } else { - $caption_field_value->setVocab($vocabulary, $vocabularyId); - } + $caption_field_value->changeVocabulary($vocabulary, $vocabularyId); return $caption_field_value; - } catch (\Exception $e) { - } } - $sql_ins = 'INSERT INTO metadatas (id, record_id, meta_struct_id, value, VocabularyType, VocabularyId)' - . ' VALUES (null, :record_id, :field, :value, :VocabType, :VocabId)'; - - $params = [ - ':record_id' => $record->get_record_id(), - ':field' => $databox_field->get_id(), - ':value' => $value, - ':VocabType' => $vocabulary ? $vocabulary->getType() : null, - ':VocabId' => $vocabulary ? $vocabularyId : null, + $data = [ + 'record_id' => $record->getRecordId(), + 'meta_struct_id' => $databox_field->get_id(), + 'value' => $value, + 'VocabularyType' => $vocabulary ? $vocabulary->getType() : null, + 'VocabularyId' => $vocabulary ? $vocabularyId : null, ]; - $stmt_ins = $connbas->prepare($sql_ins); - $stmt_ins->execute($params); + $connection->insert('metadatas', $data); - $stmt_ins->closeCursor(); - $meta_id = $connbas->lastInsertId(); + $meta_id = $connection->lastInsertId(); - $caption_field_value = new self($app, $databox_field, $record, $meta_id); - $caption_field_value->update_cache_value($value); + $caption_field_value = new self($app, $databox_field, $record, $meta_id, self::DONT_RETRIEVE_VALUES); + $caption_field_value->injectValues($data['value'], $data['VocabularyType'], $data['VocabularyId']); - $record->get_caption()->delete_data_from_cache(); $databox_field->delete_data_from_cache(); - $caption_field_value->delete_data_from_cache(); return $caption_field_value; } /** - * * @return string */ public function highlight_thesaurus() @@ -361,7 +343,7 @@ class caption_Field_Value implements cache_cacheableInterface $tbranch = $this->databox_field->get_tbranch(); - if (! $tbranch || ! $XPATH_thesaurus) { + if (!$tbranch || !$XPATH_thesaurus) { return $value; } @@ -390,8 +372,9 @@ class caption_Field_Value implements cache_cacheableInterface $note = 0; $note += ($node->getAttribute("lng") == $this->app['locale']) ? 4 : 0; $note += ($node->getAttribute("w") == $term_noacc) ? 2 : 0; - if($context_noacc != "") + if ($context_noacc != "") { $note += ($node->getAttribute("k") == $context_noacc) ? 1 : 0; + } if ($note > $bestnote) { $bestnode = $node; } @@ -401,41 +384,39 @@ class caption_Field_Value implements cache_cacheableInterface list($term, $context) = $this->splitTermAndContext(str_replace(["[[em]]", "[[/em]]"], ["", ""], $value)); // a value has been found in thesaurus, update value & set the query to bounce to the value $this->value = $bestnode->getAttribute('v'); - $this->qjs = $term . ($context ? '['.$context.']' : ''); + $this->qjs = $term . ($context ? '[' . $context . ']' : ''); $this->isThesaurusValue = true; } else { $this->isThesaurusValue = false; } - return $this; + return $this->value; } /** - * @return boolean + * @return bool */ public function isThesaurusValue() { if (null === $this->isThesaurusValue) { - $this->highlight_thesaurus(); + throw new LogicException('Value was not checked against thesaurus yet. Call hightlight_thesaurus() first'); } return $this->isThesaurusValue; } /** - * * @param string $word - * @return array + * @return string[] */ protected function splitTermAndContext($word) { $term = trim($word); - $context = ""; - if (($po = strpos($term, "(")) !== false) { - if (($pc = strpos($term, ")", $po)) !== false) { - $context = trim(substr($term, $po + 1, $pc - $po - 1)); - $term = trim(substr($term, 0, $po)); - } + $context = ''; + + if (($po = strpos($term, '(')) !== false && ($pc = strpos($term, ')', $po)) !== false) { + $context = trim(substr($term, $po + 1, $pc - $po - 1)); + $term = trim(substr($term, 0, $po)); } return [$term, $context]; @@ -488,14 +469,9 @@ class caption_Field_Value implements cache_cacheableInterface */ public function delete_data_from_cache($option = null) { - $this->value = $this->VocabularyId = $this->VocabularyType = null; + $this->value = $this->vocabularyId = $this->vocabularyType = null; $this->record->delete_data_from_cache(record_adapter::CACHE_TITLE); - - try { - $this->record->get_caption()->get_field($this->databox_field->get_name())->delete_data_from_cache(); - } catch (\Exception $e) { - - } + $this->record->get_caption()->delete_data_from_cache(); unset(self::$localCache[$this->get_cache_key($option)]); } @@ -509,4 +485,26 @@ class caption_Field_Value implements cache_cacheableInterface { self::$localCache = []; } + + /** + * @param string $vocabularyType + * @param mixed $vocabularyId + */ + private function fetchVocabulary($vocabularyType, $vocabularyId) + { + try { + $this->vocabularyType = $vocabularyType ? $this->app['vocabularies'][strtolower($vocabularyType)] : null; + $this->vocabularyId = $vocabularyId; + } catch (\InvalidArgumentException $e) { + // Invalid or unknown Vocabulary + } + } + + /** + * @return \Doctrine\DBAL\Connection + */ + private function getConnection() + { + return $this->databox_field->get_connection(); + } } diff --git a/lib/classes/caption/field.php b/lib/classes/caption/field.php index ecd4ac9bf2..37b2b8ab5a 100644 --- a/lib/classes/caption/field.php +++ b/lib/classes/caption/field.php @@ -40,8 +40,6 @@ class caption_field implements cache_cacheableInterface * @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, $retrieveValues = self::RETRIEVE_VALUES) { @@ -62,8 +60,6 @@ class caption_field implements cache_cacheableInterface } } } - - return $this; } /** @@ -147,27 +143,27 @@ class caption_field implements cache_cacheableInterface } /** - * @param array $values - * @param string $separator + * @param caption_Field_Value[] $values + * @param string $separator + * @param bool $highlight * @return string */ - protected static function serialize_value(Array $values, $separator, $highlight = false) + protected function serialize_value(array $values, $separator, $highlight = false) { - if (strlen($separator) > 1) + if (strlen($separator) > 1) { $separator = $separator[0]; + } - if (trim($separator) === '') + if (trim($separator) === '') { $separator = ' '; - else + } else { $separator = ' ' . $separator . ' '; + } $array_values = []; foreach ($values as $value) { - if ($highlight) - $array_values[] = $value->highlight_thesaurus(); - else - $array_values[] = $value->getValue(); + $array_values[] = $highlight ? $value->highlight_thesaurus() : $value->getValue(); } return implode($separator, $array_values); @@ -191,10 +187,10 @@ class caption_field implements cache_cacheableInterface } /** - * @param String $custom_separator - * @param Boolean $highlightTheso + * @param string|bool $custom_separator + * @param bool $highlight * - * @return mixed + * @return string */ public function get_serialized_values($custom_separator = false, $highlight = false) { @@ -205,12 +201,12 @@ class caption_field implements cache_cacheableInterface if ($this->is_multi()) { $separator = $custom_separator !== false ? $custom_separator : $this->databox_field->get_separator(); - return self::serialize_value($this->values, $separator, $highlight); + return $this->serialize_value($this->values, $separator, $highlight); } + /** @var caption_Field_Value $value */ $value = current($this->values); - /* @var $value Caption_Field_Value */ if ($highlight) { return $value->highlight_thesaurus(); } @@ -258,7 +254,6 @@ class caption_field implements cache_cacheableInterface */ public static function get_multi_values($serialized_value, $separator) { - $values = []; if (strlen($separator) == 1) { $values = explode($separator, $serialized_value); } else { @@ -360,8 +355,7 @@ class caption_field implements cache_cacheableInterface $caption_field->delete(); $record->set_metadatas([]); - unset($caption_field); - unset($record); + unset($caption_field, $record); } catch (\Exception $e) { } @@ -381,7 +375,7 @@ class caption_field implements cache_cacheableInterface */ public function get_cache_key($option = null) { - return 'caption_field_' . $this->databox_field->get_id() . '_' . $this->record->get_serialize_key() . ($option ? '_' . $option : ''); + return 'caption_field_' . $this->databox_field->get_id() . '_' . $this->record->getId() . ($option ? '_' . $option : ''); } /** @@ -389,6 +383,7 @@ class caption_field implements cache_cacheableInterface * * @param string $option * @return mixed + * @throws Exception */ public function get_data_from_cache($option = null) { diff --git a/lib/classes/caption/interface.php b/lib/classes/caption/interface.php deleted file mode 100644 index 477b24e6ba..0000000000 --- a/lib/classes/caption/interface.php +++ /dev/null @@ -1,22 +0,0 @@ -app = $app; - $this->sbas_id = $record->getDataboxId(); $this->record = $record; - $this->databox = $databox; } public function toArray($includeBusinessFields) @@ -57,7 +43,10 @@ class caption_record implements caption_interface, cache_cacheableInterface return $serializer->toArray($this, $includeBusinessFields); } - public function get_record() + /** + * @return RecordReferenceInterface + */ + public function getRecordReference() { return $this->record; } @@ -72,26 +61,29 @@ class caption_record implements caption_interface, cache_cacheableInterface return $this->fields; } + $databox = $this->getDatabox(); + try { $fields = $this->get_data_from_cache(); } catch (\Exception $e) { - $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->getRecordId()]); - $fields = $stmt->fetchAll(PDO::FETCH_ASSOC); - $stmt->closeCursor(); - if ($fields) { - $this->set_data_to_cache($fields); - } + $sql = <<<'SQL' +SELECT m.id AS meta_id, s.id AS structure_id, value, VocabularyType, VocabularyId +FROM metadatas m INNER JOIN metadatas_structure s ON s.id = m.meta_struct_id +WHERE m.record_id = :record_id +ORDER BY s.sorter ASC +SQL; + $fields = $databox->get_connection() + ->executeQuery($sql, [':record_id' => $this->record->getRecordId()]) + ->fetchAll(PDO::FETCH_ASSOC); + + $this->set_data_to_cache($fields); } $rec_fields = array(); if ($fields) { - $databox_descriptionStructure = $this->databox->get_meta_structure(); + $databox_descriptionStructure = $databox->get_meta_structure(); + $record = $databox->get_record($this->record->getRecordId()); // first group values by field $caption_fields = []; @@ -113,7 +105,7 @@ class caption_record implements caption_interface, cache_cacheableInterface $cfv = new caption_Field_Value( $this->app, $caption_fields[$structure_id]['db_field'], - $this->record, + $record, $row['meta_id'], caption_Field_Value::DONT_RETRIEVE_VALUES // ask caption_Field_Value "no n+1 sql" ); @@ -132,7 +124,7 @@ class caption_record implements caption_interface, cache_cacheableInterface $cf = new caption_field( $this->app, $caption_field['db_field'], - $this->record, + $record, caption_field::DONT_RETRIEVE_VALUES // ask caption_field "no n+1 sql" ); @@ -159,7 +151,7 @@ class caption_record implements caption_interface, cache_cacheableInterface $fields = []; foreach ($this->retrieve_fields() as $meta_struct_id => $field) { - if ($grep_fields && ! in_array($field->get_name(), $grep_fields)) { + if ($grep_fields && ! in_array($field->get_name(), $grep_fields, true)) { continue; } @@ -201,15 +193,29 @@ class caption_record implements caption_interface, cache_cacheableInterface } /** - * + * @return caption_field[] + */ + public function getDCFields() + { + $databoxDcesFieldIds = array_map(function (databox_field $databox_field) { + return $databox_field->get_id(); + }, $this->getDatabox()->get_meta_structure()->getDcesFields()); + + return array_intersect_key( + $this->retrieve_fields(), + array_fill_keys($databoxDcesFieldIds, null) + ); + } + + /** * @param string $label - * @return caption_field + * @return caption_field|null */ public function get_dc_field($label) { $fields = $this->retrieve_fields(); - if (null !== $field = $this->databox->get_meta_structure()->get_dces_field($label)) { + if (null !== $field = $this->getDatabox()->get_meta_structure()->get_dces_field($label)) { if (isset($fields[$field->get_id()])) { return $fields[$field->get_id()]; } @@ -219,15 +225,12 @@ class caption_record implements caption_interface, cache_cacheableInterface } /** - * - * @param string $highlight - * @param array $grep_fields - * @param SearchEngineInterface $searchEngine - * @param Boolean $includeBusiness - * + * @param string $highlight + * @param array $grep_fields + * @param bool $includeBusiness * @return array */ - public function get_highlight_fields($highlight = '', Array $grep_fields = null, SearchEngineInterface $searchEngine = null, $includeBusiness = false, SearchEngineOptions $options = null) + public function get_highlight_fields($highlight = '', array $grep_fields = null, $includeBusiness = false) { $fields = []; @@ -236,9 +239,14 @@ class caption_record implements caption_interface, cache_cacheableInterface foreach ($field->get_values() as $metaId => $v) { $values[$metaId] = [ 'value' => $v->getValue(), - 'from_thesaurus' => $highlight ? $v->isThesaurusValue() : false, - 'qjs' => $v->getQjs(), + 'from_thesaurus' => false, + 'qjs' => null, ]; + if ($highlight) { + $v->highlight_thesaurus(); + $values[$metaId]['from_thesaurus'] = $v->isThesaurusValue(); + $values[$metaId]['qjs'] = $v->getQjs(); + } } $fields[$field->get_name()] = [ 'values' => $values, @@ -249,24 +257,6 @@ class caption_record implements caption_interface, cache_cacheableInterface ]; } - if ($searchEngine instanceof SearchEngineInterface) { - $ret = $searchEngine->excerpt($highlight, $fields, $this->record, $options); - - // sets highlighted value from search engine, highlighted values will now - // be surrounded by [[em]][[/em]] tags - if ($ret) { - foreach ($fields as $key => $value) { - if (!isset($ret[$key])) { - continue; - } - - foreach ($ret[$key] as $metaId => $newValue) { - $fields[$key]['values'][$metaId]['value'] = $newValue; - } - } - } - } - return $fields; } @@ -278,7 +268,7 @@ class caption_record implements caption_interface, cache_cacheableInterface */ public function get_cache_key($option = null) { - return 'caption_' . $this->record->get_serialize_key() . ($option ? '_' . $option : ''); + return 'caption_' . $this->record->getId() . ($option ? '_' . $option : ''); } /** @@ -289,7 +279,7 @@ class caption_record implements caption_interface, cache_cacheableInterface */ public function get_data_from_cache($option = null) { - return $this->record->getDatabox()->get_data_from_cache($this->get_cache_key($option)); + return $this->getDatabox()->get_data_from_cache($this->get_cache_key($option)); } /** @@ -302,7 +292,7 @@ class caption_record implements caption_interface, cache_cacheableInterface */ public function set_data_to_cache($value, $option = null, $duration = 0) { - return $this->record->getDatabox()->set_data_to_cache($value, $this->get_cache_key($option), $duration); + return $this->getDatabox()->set_data_to_cache($value, $this->get_cache_key($option), $duration); } /** @@ -315,6 +305,14 @@ class caption_record implements caption_interface, cache_cacheableInterface { $this->fields = null; - return $this->record->getDatabox()->delete_data_from_cache($this->get_cache_key($option)); + return $this->getDatabox()->delete_data_from_cache($this->get_cache_key($option)); + } + + /** + * @return databox + */ + private function getDatabox() + { + return $this->app->findDataboxById($this->record->getDataboxId()); } } diff --git a/lib/classes/databox/descriptionStructure.php b/lib/classes/databox/descriptionStructure.php index 24c5c4ff74..d772d0e514 100644 --- a/lib/classes/databox/descriptionStructure.php +++ b/lib/classes/databox/descriptionStructure.php @@ -1,5 +1,4 @@ |null */ - protected $cache_name_id = []; + protected $cache_name_id; /** * @param databox_field[] $fields @@ -51,6 +52,10 @@ class databox_descriptionStructure implements IteratorAggregate, Countable { $this->elements[$field->get_id()] = $field; + if (null !== $this->cache_name_id) { + $this->cache_name_id[$field->get_name()] = $field->get_id(); + } + return $this; } @@ -60,8 +65,9 @@ class databox_descriptionStructure implements IteratorAggregate, Countable */ public function remove_element(databox_field $field) { - if (isset($this->elements[$field->get_id()])) - unset($this->elements[$field->get_id()]); + if (isset($this->elements[$field->get_id()])) { + unset($this->elements[$field->get_id()], $this->cache_name_id[$field->get_name()]); + } return $this; } @@ -75,14 +81,14 @@ class databox_descriptionStructure implements IteratorAggregate, Countable } /** - * - * @param int $id + * @param int $id * @return databox_field */ public function get_element($id) { - if ( ! isset($this->elements[$id])) + if (!isset($this->elements[$id])) { throw new Exception_Databox_FieldNotFound (); + } return $this->elements[$id]; } @@ -93,23 +99,35 @@ class databox_descriptionStructure implements IteratorAggregate, Countable */ public function get_element_by_name($name) { - $name = databox_field::generateName($name); + if (null === $this->cache_name_id) { + $this->cache_name_id = []; - if (isset($this->cache_name_id[$name])) { - return $this->elements[$this->cache_name_id[$name]]; - } - - foreach ($this->elements as $id => $meta) { - if ($meta->get_name() === $name) { - $this->cache_name_id[$name] = $id; - - return $meta; + foreach ($this->elements as $id => $meta) { + $this->cache_name_id[$meta->get_name()] = $id; } } - return null; + $name = databox_field::generateName($name); + + return isset($this->cache_name_id[$name]) + ? $this->elements[$this->cache_name_id[$name]] + : null; } + /** + * @return databox_field[] + */ + public function getDcesFields() + { + return array_filter($this->elements, function (databox_field $field) { + return null !== $field->get_dces_element(); + }); + } + + /** + * @param string $label + * @return databox_field|null + */ public function get_dces_field($label) { foreach ($this->elements as $field) { @@ -125,13 +143,16 @@ class databox_descriptionStructure implements IteratorAggregate, Countable /** * @param string $id - * @return boolean + * @return bool */ public function isset_element($id) { return isset($this->elements[$id]); } + /** + * @return array + */ public function toArray() { return array_map(function (databox_field $element) { diff --git a/lib/classes/databox/field.php b/lib/classes/databox/field.php index 7fb8299d37..4e4eb70970 100644 --- a/lib/classes/databox/field.php +++ b/lib/classes/databox/field.php @@ -1,5 +1,4 @@ 'databox_Field_DCES_Type', ]; + /** + * @var databox_Field_DCESAbstract|null + */ protected $dces_element; + + /** + * @var ControlProviderInterface|null + */ protected $Vocabulary; + + /** + * @var string|null + */ protected $VocabularyType; + + /** + * @var bool + */ protected $VocabularyRestriction = false; protected $on_error = false; protected $original_src; @@ -504,7 +517,6 @@ class databox_field implements cache_cacheableInterface } /** - * * @return databox_Field_DCESAbstract */ public function get_dces_element() @@ -514,29 +526,25 @@ class databox_field implements cache_cacheableInterface public function set_dces_element(databox_Field_DCESAbstract $DCES_element = null) { - $connbas = $this->get_connection(); - + $connection = $this->get_connection(); if (null !== $DCES_element) { - $sql = 'UPDATE metadatas_structure - SET dces_element = null WHERE dces_element = :dces_element'; - - $stmt = $connbas->prepare($sql); - $stmt->execute([ - ':dces_element' => $DCES_element->get_label() - ]); - $stmt->closeCursor(); + $connection->executeUpdate( + 'UPDATE metadatas_structure SET dces_element = null WHERE dces_element = :dces_element', + [ + 'dces_element' => $DCES_element->get_label(), + ] + ); } - $sql = 'UPDATE metadatas_structure - SET dces_element = :dces_element WHERE id = :id'; + $connection->executeUpdate( + 'UPDATE metadatas_structure SET dces_element = :dces_element WHERE id = :id', + [ + 'dces_element' => $DCES_element ? $DCES_element->get_label() : null, + 'id' => $this->id, + ] + ); - $stmt = $connbas->prepare($sql); - $stmt->execute([ - ':dces_element' => $DCES_element ? $DCES_element->get_label() : null - , ':id' => $this->id - ]); - $stmt->closeCursor(); $this->dces_element = $DCES_element; $this->delete_data_from_cache(); @@ -934,12 +942,12 @@ class databox_field implements cache_cacheableInterface } /** - * * @return array */ public function __sleep() { $vars = []; + foreach ($this as $key => $value) { if (in_array($key, ['databox', 'app', 'Vocabulary'])) continue; @@ -997,10 +1005,14 @@ class databox_field implements cache_cacheableInterface private function loadVocabulary() { - try { - $this->Vocabulary = Vocabulary\Controller::get($this->app, $this->VocabularyType); - } catch (\InvalidArgumentException $e) { + if ($this->VocabularyType === '') { + return; + } + try { + $this->Vocabulary = $this->app['vocabularies'][$this->VocabularyType]; + } catch (\InvalidArgumentException $e) { + // Could not find Vocabulary } } diff --git a/lib/classes/media/Permalink/Adapter.php b/lib/classes/media/Permalink/Adapter.php index d7f0382c8b..38c662b3f2 100644 --- a/lib/classes/media/Permalink/Adapter.php +++ b/lib/classes/media/Permalink/Adapter.php @@ -12,6 +12,9 @@ use Alchemy\Phrasea\Application; use Alchemy\Phrasea\Exception\RuntimeException; use Alchemy\Phrasea\Model\Manipulator\TokenManipulator; +use Alchemy\Phrasea\Utilities\NullableDateTime; +use Assert\Assertion; +use Doctrine\DBAL\Connection; use Doctrine\DBAL\DBALException; use Guzzle\Http\Url; @@ -37,17 +40,22 @@ class media_Permalink_Adapter implements cache_cacheableInterface protected $app; /** - * @param Application $app - * @param databox $databox - * @param media_subdef $media_subdef + * @param Application $app + * @param databox $databox + * @param media_subdef $media_subdef + * @param array $data */ - public function __construct(Application $app, databox $databox, media_subdef $media_subdef) + public function __construct(Application $app, databox $databox, media_subdef $media_subdef, array $data = null) { $this->app = $app; $this->databox = $databox; $this->media_subdef = $media_subdef; - $this->load(); + if (null === $data) { + $this->load(); + } else { + $this->loadFromData($data); + } } /** @@ -106,12 +114,12 @@ class media_Permalink_Adapter implements cache_cacheableInterface $label = $this->get_label() . '.' . pathinfo($this->media_subdef->get_file(), PATHINFO_EXTENSION); return Url::factory($this->app->url('permalinks_permalink', [ - 'sbas_id' => $this->media_subdef->get_sbas_id(), + 'sbas_id' => $this->media_subdef->get_sbas_id(), 'record_id' => $this->media_subdef->get_record_id(), - 'subdef' => $this->media_subdef->get_name(), + 'subdef' => $this->media_subdef->get_name(), /** @Ignore */ - 'label' => $label, - 'token' => $this->get_token(), + 'label' => $label, + 'token' => $this->get_token(), ])); } @@ -121,10 +129,10 @@ class media_Permalink_Adapter implements cache_cacheableInterface public function get_page() { return $this->app->url('permalinks_permaview', [ - 'sbas_id' => $this->media_subdef->get_sbas_id(), + 'sbas_id' => $this->media_subdef->get_sbas_id(), 'record_id' => $this->media_subdef->get_record_id(), - 'subdef' => $this->media_subdef->get_name(), - 'token' => $this->get_token(), + 'subdef' => $this->media_subdef->get_name(), + 'token' => $this->get_token(), ]); } @@ -139,7 +147,7 @@ class media_Permalink_Adapter implements cache_cacheableInterface $sql = 'UPDATE permalinks SET token = :token, last_modified = NOW() WHERE id = :id'; $stmt = $this->databox->get_connection()->prepare($sql); - $stmt->execute([':token' => $this->token, ':id' => $this->get_id()]); + $stmt->execute([':token' => $this->token, ':id' => $this->get_id()]); $stmt->closeCursor(); $this->delete_data_from_cache(); @@ -148,24 +156,17 @@ class media_Permalink_Adapter implements cache_cacheableInterface } /** - * @param string $is_activated + * @param bool $is_activated * @return $this */ public function set_is_activated($is_activated) { - $this->is_activated = ! ! $is_activated; + $this->is_activated = (bool)$is_activated; - $sql = 'UPDATE permalinks SET activated = :activated, last_modified = NOW() - WHERE id = :id'; - $stmt = $this->databox->get_connection()->prepare($sql); - - $params = [ - ':activated' => $this->is_activated, - ':id' => $this->get_id() - ]; - - $stmt->execute($params); - $stmt->closeCursor(); + $this->databox->get_connection()->executeUpdate( + 'UPDATE permalinks SET activated = :activated, last_modified = NOW() WHERE id = :id', + ['activated' => $this->is_activated, 'id' => $this->get_id()] + ); $this->delete_data_from_cache(); @@ -179,81 +180,79 @@ class media_Permalink_Adapter implements cache_cacheableInterface public function set_label($label) { $label = trim($label) ? trim($label) : 'untitled'; - while (strpos($label, ' ') !== false) + + while (strpos($label, ' ') !== false) { $label = str_replace(' ', ' ', $label); + } $this->label = $this->app['unicode']->remove_nonazAZ09( str_replace(' ', '-', $label) ); - $sql = 'UPDATE permalinks SET label = :label, last_modified = NOW() - WHERE id = :id'; - $stmt = $this->databox->get_connection()->prepare($sql); - $stmt->execute([':label' => $this->label, ':id' => $this->get_id()]); - $stmt->closeCursor(); + $this->databox->get_connection()->executeUpdate( + 'UPDATE permalinks SET label = :label, last_modified = NOW() WHERE id = :id', + ['label' => $this->label, 'id' => $this->get_id()] + ); $this->delete_data_from_cache(); return $this; } - /** - * @return $this - */ protected function load() { try { - $datas = $this->get_data_from_cache(); - $this->id = $datas['id']; - $this->token = $datas['token']; - $this->is_activated = $datas['is_activated']; - $this->created_on = $datas['created_on']; - $this->last_modified = $datas['last_modified']; - $this->label = $datas['label']; - - return $this; + $data = $this->get_data_from_cache(); } catch (\Exception $e) { - + $data = false; } - $sql = ' - SELECT p.id, p.token, p.activated, p.created_on, p.last_modified, p.label - FROM permalinks p - WHERE p.subdef_id = :subdef_id'; - $stmt = $this->databox->get_connection()->prepare($sql); - $stmt->execute([':subdef_id' => $this->media_subdef->get_subdef_id()]); - $row = $stmt->fetch(PDO::FETCH_ASSOC); - $stmt->closeCursor(); + if (is_array($data)) { + $this->loadFromData($data); - if (!$row) { - throw new Exception_Media_SubdefNotFound (); + return; } - $this->id = (int) $row['id']; - $this->token = $row['token']; - $this->is_activated = ! ! $row['activated']; - $this->created_on = new DateTime($row['created_on']); - $this->last_modified = new DateTime($row['last_modified']); - $this->label = $row['label']; + $data = $this->databox->get_connection()->fetchAssoc( + self::getSelectSql(), + [':subdef_id' => $this->media_subdef->get_subdef_id()] + ); + + if (!$data) { + throw new Exception_Media_SubdefNotFound(); + } + + $this->loadFromData($data); + + $this->set_data_to_cache($this->toArray()); + } + + private function loadFromData(array $data) + { + $this->id = (int)$data['id']; + $this->token = $data['token']; + $this->is_activated = (bool)$data['is_activated']; + $this->created_on = new DateTime($data['created_on']); + $this->last_modified = new DateTime($data['last_modified']); + $this->label = $data['label']; + } + + private function toArray() + { + return [ + 'id' => $this->id, + 'token' => $this->token, + 'is_activated' => $this->is_activated, + 'created_on' => NullableDateTime::format($this->created_on), + 'last_modified' => NullableDateTime::format($this->last_modified), + 'label' => $this->label, - $datas = [ - 'id' => $this->id, - 'token' => $this->token, - 'is_activated' => $this->is_activated, - 'created_on' => $this->created_on, - 'last_modified' => $this->last_modified, - /** @Ignore */ - 'label' => $this->label, ]; - - $this->set_data_to_cache($datas); - - return $this; } /** - * @param Application $app - * @param databox $databox + * @param Application $app + * @param databox $databox * @param media_subdef $media_subdef * @return $this */ @@ -262,57 +261,220 @@ class media_Permalink_Adapter implements cache_cacheableInterface try { return new self($app, $databox, $media_subdef); } catch (\Exception $e) { - + // Could not load, try to create } return self::create($app, $databox, $media_subdef); } /** - * @param Application $app - * @param databox $databox + * @param Application $app + * @param media_subdef[] $subdefs + * @return media_Permalink_Adapter[] + */ + public static function getMany(Application $app, $subdefs) + { + Assertion::allIsInstanceOf($subdefs, media_subdef::class); + + $permalinks = []; + $subdefPerDatabox = []; + + foreach ($subdefs as $index => $subdef) { + if (!isset($subdefPerDatabox[$subdef->get_sbas_id()])) { + $subdefPerDatabox[$subdef->get_sbas_id()] = []; + } + $subdefPerDatabox[$subdef->get_sbas_id()][$index] = $subdef; + + $permalinks[$index] = null; + } + + foreach ($subdefPerDatabox as $databoxId => $media_subdefs) { + $databox = $app->findDataboxById($databoxId); + + $subdefIds = array_map(function (media_subdef $media_subdef) { + return $media_subdef->get_subdef_id(); + }, $media_subdefs); + + $data = self::fetchData($databox, $subdefIds); + + $missing = array_diff_key($media_subdefs, $data); + + if ($missing) { + self::createMany($app, $databox, $missing); + $data = array_replace($data, self::fetchData($databox, array_diff_key($subdefIds, $data))); + } + + foreach ($media_subdefs as $index => $subdef) { + if (!isset($data[$index])) { + throw new \RuntimeException('Could not fetch some data. Should never happen'); + } + + $permalinks[$index] = new self($app, $databox, $subdef, $data[$index]); + } + } + + return $permalinks; + } + + /** + * Returns present data in storage with same indexes but different order + * + * @param databox $databox + * @param int[] $subdefIds + * @return array + */ + private static function fetchData(databox $databox, array $subdefIds) + { + $found = []; + $missing = []; + + foreach ($subdefIds as $index => $subdefId) { + try { + $data = self::doGetDataFromCache($databox, $subdefId); + } catch (Exception $exception) { + $data = false; + } + + if (is_array($data)) { + $found[$index] = $data; + + continue; + } + + $missing[$index] = $subdefId; + } + + if (!$missing) { + return $found; + } + + $dbalData = $databox->get_connection()->fetchAll( + self::getSelectSql(), + ['subdef_id' => array_values($missing)], + ['subdef_id' => Connection::PARAM_INT_ARRAY] + ); + + foreach ($dbalData as $item) { + $itemSubdefId = $item['subdef_id']; + + $databox->set_data_to_cache($item, self::generateCacheKey($itemSubdefId)); + + $foundIndexes = array_keys(array_intersect($missing, [$itemSubdefId])); + + foreach ($foundIndexes as $foundIndex) { + $found[$foundIndex] = $item; + unset($missing[$foundIndex]); + } + } + + return $found; + } + + /** + * @param Application $app + * @param databox $databox + * @param media_subdef[] $subdefs + * @throws DBALException + * @throws \InvalidArgumentException + */ + public static function createMany(Application $app, databox $databox, $subdefs) + { + $databoxId = $databox->get_sbas_id(); + $recordIds = []; + + foreach ($subdefs as $media_subdef) { + if ($media_subdef->get_sbas_id() !== $databoxId) { + throw new InvalidArgumentException(sprintf( + 'All subdefs should be from databox %d, got %d', + $databoxId, + $media_subdef->get_sbas_id() + )); + } + + $recordIds[] = $media_subdef->get_record_id(); + } + + $databoxRecords = $databox->getRecordRepository()->findByRecordIds($recordIds); + + /** @var record_adapter[] $records */ + $records = array_combine( + array_map(function (record_adapter $record) { + return $record->getRecordId(); + }, $databoxRecords), + $databoxRecords + ); + + if (count(array_unique($recordIds)) !== count($records)) { + throw new \RuntimeException('Some records are missing'); + } + + $generator = $app['random.medium']; + + $data = []; + + foreach ($subdefs as $media_subdef) { + $data[] = [ + 'subdef_id' => $media_subdef->get_subdef_id(), + 'token' => $generator->generateString(64, TokenManipulator::LETTERS_AND_NUMBERS), + 'label' => $records[$media_subdef->get_record_id()]->get_title(false, null, true), + ]; + } + + try { + $databox->get_connection()->transactional(function (Connection $connection) use ($data) { + $sql = <<<'SQL' +INSERT INTO permalinks (subdef_id, token, activated, created_on, last_modified, label) +VALUES (:subdef_id, :token, 1, NOW(), NOW(), :label) +SQL; + + $statement = $connection->prepare($sql); + + foreach ($data as $params) { + $statement->execute($params); + } + }); + } catch (Exception $e) { + throw new RuntimeException('Permalink already exists', $e->getCode(), $e); + } + } + + /** + * @param Application $app + * @param databox $databox * @param media_subdef $media_subdef * @return $this */ public static function create(Application $app, databox $databox, media_subdef $media_subdef) { - $sql = 'INSERT INTO permalinks - (id, subdef_id, token, activated, created_on, last_modified, label) - VALUES (null, :subdef_id, :token, :activated, NOW(), NOW(), "")'; + self::createMany($app, $databox, [$media_subdef]); - $params = [ - ':subdef_id' => $media_subdef->get_subdef_id() - , ':token' => $app['random.medium']->generateString(64, TokenManipulator::LETTERS_AND_NUMBERS) - , ':activated' => '1' - ]; + return self::getPermalink($app, $databox, $media_subdef); + } - $error = null; - $stmt = $databox->get_connection()->prepare($sql); - try { - $stmt->execute($params); - } catch (DBALException $e) { - $error = $e; - } - $stmt->closeCursor(); - - if ($error) { - throw new RuntimeException('Permalink already exists', $e->getCode(), $e); - } - - $permalink = self::getPermalink($app, $databox, $media_subdef); - $permalink->set_label(strip_tags($media_subdef->get_record()->get_title(false, null, true))); - - return $permalink; + private static function generateCacheKey($id, $option = null) + { + return 'permalink_' . $id . ($option ? '_' . $option : ''); } public function get_cache_key($option = null) { - return 'permalink_' . $this->media_subdef->get_subdef_id() . ($option ? '_' . $option : ''); + return self::generateCacheKey($this->media_subdef->get_subdef_id(), $option); + } + + /** + * @param databox $databox + * @param int $subdefId + * @param null $option + * @return string + */ + private static function doGetDataFromCache(databox $databox, $subdefId, $option = null) + { + return $databox->get_data_from_cache(self::generateCacheKey($subdefId, $option)); } public function get_data_from_cache($option = null) { - return $this->databox->get_data_from_cache($this->get_cache_key($option)); + return self::doGetDataFromCache($this->databox, $this->media_subdef->get_subdef_id(), $option); } public function set_data_to_cache($value, $option = null, $duration = 0) @@ -324,4 +486,16 @@ class media_Permalink_Adapter implements cache_cacheableInterface { $this->databox->delete_data_from_cache($this->get_cache_key($option)); } + + /** + * @return string + */ + protected static function getSelectSql() + { + return <<<'SQL' +SELECT p.id, p.subdef_id, p.token, p.activated AS is_activated, p.created_on, p.last_modified, p.label +FROM permalinks p +WHERE p.subdef_id IN (:subdef_id) +SQL; + } } diff --git a/lib/classes/media/subdef.php b/lib/classes/media/subdef.php index bf8711d1e4..7150a3c989 100644 --- a/lib/classes/media/subdef.php +++ b/lib/classes/media/subdef.php @@ -10,7 +10,11 @@ */ use Alchemy\Phrasea\Application; +use Alchemy\Phrasea\Databox\Subdef\MediaSubdefRepository; use Alchemy\Phrasea\Http\StaticFile\Symlink\SymLinker; +use Alchemy\Phrasea\Model\RecordReferenceInterface; +use Alchemy\Phrasea\Utilities\NullableDateTime; +use Assert\Assertion; use Guzzle\Http\Url; use MediaAlchemyst\Alchemyst; use MediaVorus\Media\MediaInterface; @@ -18,6 +22,16 @@ use MediaVorus\MediaVorus; class media_subdef extends media_abstract implements cache_cacheableInterface { + /** + * @param Application $app + * @param int $databoxId + * @return MediaSubdefRepository + */ + private static function getMediaSubdefRepository(Application $app, $databoxId) + { + return $app['provider.repo.media_subdef']->getRepositoryForDatabox($databoxId); + } + /** @var Application */ protected $app; @@ -103,114 +117,149 @@ class media_subdef extends media_abstract implements cache_cacheableInterface const TC_DATA_LIGHTVALUE = 'LightValue'; /** - * @param Application $app - * @param record_adapter $record - * @param string $name - * @param bool $substitute + * @param Application $app + * @param RecordReferenceInterface $record + * @param string $name + * @param bool $substitute + * @param array|null $data */ - public function __construct(Application $app, record_adapter $record, $name, $substitute = false) + public function __construct(Application $app, RecordReferenceInterface $record, $name, $substitute = false, array $data = null) { $this->app = $app; $this->name = $name; - $this->record = $record; - $this->load($substitute); + $this->record = $record instanceof record_adapter + ? $record + : $app->findDataboxById($record->getDataboxId())->get_record($record->getRecordId()); - $this->generate_url(); + if (null !== $data) { + $this->loadFromArray($data); + } else { + $this->load($substitute); + } - parent::__construct($this->width, $this->height, $this->url); + parent::__construct($this->width, $this->height, $this->generateUrl()); } /** * @param bool $substitute - * @return $this + * @return void */ protected function load($substitute) { try { - $datas = $this->get_data_from_cache(); - if (!is_array($datas)) { - throw new \Exception('Could not retrieve data'); - } - $this->mime = $datas['mime']; - $this->width = $datas['width']; - $this->height = $datas['height']; - $this->size = $datas['size']; - $this->etag = $datas['etag']; - $this->path = $datas['path']; - $this->url = $datas['url']; - $this->file = $datas['file']; - $this->is_physically_present = $datas['physically_present']; - $this->is_substituted = $datas['is_substituted']; - $this->subdef_id = $datas['subdef_id']; - $this->modification_date = $datas['modification_date']; - $this->creation_date = $datas['creation_date']; - - return $this; - } catch (\Exception $e) { - + $data = $this->get_data_from_cache(); + } catch (Exception $e) { + $data = false; } - $connbas = $this->record->getDatabox()->get_connection(); + if (is_array($data)) { + $this->loadFromArray($data); - $sql = "SELECT subdef_id, name, file, width, height, mime," - . " path, size, substit, created_on, updated_on, etag" - . " FROM subdef" - . " WHERE name = :name AND record_id = :record_id"; + return; + } - $params = [ - ':record_id' => $this->record->getRecordId(), - ':name' => $this->name - ]; + $sql = <<<'SQL' +SELECT subdef_id, name, file, width, height, mime, path, size, substit, created_on, updated_on, etag +FROM subdef +WHERE name = :name AND record_id = :record_id +SQL; - $stmt = $connbas->prepare($sql); - $stmt->execute($params); - $row = $stmt->fetch(PDO::FETCH_ASSOC); - $stmt->closeCursor(); + $row = $this->getDataboxConnection()->fetchAssoc($sql, [ + 'record_id' => $this->record->getRecordId(), + 'name' => $this->name + ]); if ($row) { - $this->width = (int) $row['width']; - $this->size = (int) $row['size']; - $this->height = (int) $row['height']; - $this->mime = $row['mime']; - $this->file = $row['file']; - $this->path = p4string::addEndSlash($row['path']); - $this->is_physically_present = file_exists($this->getRealPath()); - $this->etag = $row['etag']; - $this->is_substituted = ! ! $row['substit']; - $this->subdef_id = (int) $row['subdef_id']; - - if ($row['updated_on']) - $this->modification_date = new DateTime($row['updated_on']); - if ($row['created_on']) - $this->creation_date = new DateTime($row['created_on']); - + $this->loadFromArray([ + 'width' => (int)$row['width'], + 'size' => (int)$row['size'], + 'height' => (int)$row['height'], + 'mime' => $row['mime'], + 'file' => $row['file'], + 'path' => p4string::addEndSlash($row['path']), + 'physically_present' => true, + 'is_substituted' => (bool)$row['substit'], + 'subdef_id' => (int)$row['subdef_id'], + 'updated_on' => $row['updated_on'], + 'created_on' => $row['created_on'], + 'etag' => $row['etag'] ?: null, + ]); } elseif ($substitute === false) { throw new Exception_Media_SubdefNotFound($this->name . ' not found'); } - if (! $row) { - $this->find_substitute_file(); + if (!$row) { + $this->markPhysicallyUnavailable(); } - $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, + $this->set_data_to_cache($this->toArray()); + } + + private function loadFromArray(array $data) + { + if (!$data) { + $data = [ + 'mime' => 'unknown', + 'width' => 0, + 'height' => 0, + 'size' => 0, + 'path' => '', + 'file' => '', + 'physically_present' => false, + 'is_substituted' => false, + 'subdef_id' => null, + 'updated_on' => null, + 'created_on' => null, + 'url' => null, + ]; + } + + $normalizer = function ($field, callable $then, callable $else = null) use ($data) { + if (isset($data[$field]) || array_key_exists($field, $data)) { + return $then($data[$field]); + } + + return $else ? $else() : null; + }; + + $this->mime = $data['mime']; + $this->width = (int)$data['width']; + $this->height = (int)$data['height']; + $this->size = (int)$data['size']; + $this->etag = $normalizer('etag', 'strval'); + $this->path = p4string::addEndSlash($data['path']); + $this->file = $data['file']; + $this->is_physically_present = (bool)$data['physically_present']; + $this->is_substituted = (bool)$data['is_substituted']; + $this->subdef_id = $normalizer('subdef_id', 'intval'); + $this->modification_date = $normalizer('updated_on', 'date_create'); + $this->creation_date = $normalizer('created_on', 'date_create'); + $this->url = $normalizer('url', [Url::class, 'factory'], [$this, 'generateUrl']); + + if (!$this->isStillAccessible()) { + $this->markPhysicallyUnavailable(); + } + } + + private function toArray() + { + return [ + 'record_id' => $this->get_record_id(), + 'name' => $this->get_name(), + 'width' => $this->width, + 'size' => $this->size, + 'height' => $this->height, + 'mime' => $this->mime, + 'file' => $this->file, + 'path' => $this->path, '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, + 'is_substituted' => $this->is_substituted, + 'subdef_id' => $this->subdef_id, + 'updated_on' => NullableDateTime::format($this->modification_date), + 'created_on' => NullableDateTime::format($this->creation_date), + 'etag' => $this->etag, + 'url' => (string)$this->url, ]; - - $this->set_data_to_cache($datas); - - return $this; } /** @@ -225,11 +274,13 @@ class media_subdef extends media_abstract implements cache_cacheableInterface $this->delete_data_from_cache(); - if ($this->get_permalink() instanceof media_Permalink_Adapter) { - $this->get_permalink()->delete_data_from_cache(); + $permalink = $this->get_permalink(); + + if ($permalink instanceof media_Permalink_Adapter) { + $permalink->delete_data_from_cache(); } - $this->find_substitute_file(); + $this->markPhysicallyUnavailable(); } return $this; @@ -245,57 +296,55 @@ class media_subdef extends media_abstract implements cache_cacheableInterface $subdef_id = $this->subdef_id; $this->remove_file(); - $connbas = $this->record->getDatabox()->get_connection(); + $connection = $this->getDataboxConnection(); - $sql = "DELETE FROM subdef WHERE subdef_id = :subdef_id"; - $stmt = $connbas->prepare($sql); - $stmt->execute(['subdef_id'=>$subdef_id]); - $stmt->closeCursor(); + $connection->executeUpdate( + 'DELETE FROM subdef WHERE subdef_id = :subdef_id', + ['subdef_id' => $subdef_id] + ); - $sql = "DELETE FROM permalinks WHERE subdef_id = :subdef_id"; - $stmt = $connbas->prepare($sql); - $stmt->execute(['subdef_id'=>$subdef_id]); - $stmt->closeCursor(); + $connection->executeUpdate( + 'DELETE FROM permalinks WHERE subdef_id = :subdef_id', + ['subdef_id'=>$subdef_id] + ); $this->delete_data_from_cache(); $this->record->delete_data_from_cache(record_adapter::CACHE_SUBDEFS); } - /** - * Find a substitution file for a subdef - * - * @return \media_subdef - */ - protected function find_substitute_file() + private function getSubstituteFilename() { if ($this->record->isStory()) { - $this->mime = 'image/png'; - $this->width = 256; - $this->height = 256; - $this->path = $this->app['root.path'] . '/www/assets/common/images/icons/substitution/'; - $this->file = 'regroup_thumb.png'; - $this->url = Url::factory('/assets/common/images/icons/substitution/regroup_thumb.png'); - } else { - $mime = $this->record->getMimeType(); - $mime = trim($mime) != '' ? str_replace('/', '_', $mime) : 'application_octet-stream'; - - $this->mime = 'image/png'; - $this->width = 256; - $this->height = 256; - $this->path = $this->app['root.path'] . '/www/assets/common/images/icons/substitution/'; - $this->file = str_replace('+', '%20', $mime) . '.png'; - $this->url = Url::factory('/assets/common/images/icons/substitution/' . $this->file); + return 'regroup_thumb.png'; } + $mime = $this->record->getMimeType(); + $mime = trim($mime) != '' ? str_replace('/', '_', $mime) : 'application_octet-stream'; + + return str_replace('+', '%20', $mime) . '.png'; + } + + /** + * Find a substitution file for a subdef + * @return void + */ + protected function markPhysicallyUnavailable() + { $this->is_physically_present = false; - if ( ! file_exists($this->path . $this->file)) { + $this->mime = 'image/png'; + $this->width = 256; + $this->height = 256; + $this->file = $this->getSubstituteFilename(); + + $this->path = $this->app['root.path'] . '/www/assets/common/images/icons/substitution/'; + $this->url = Url::factory('/assets/common/images/icons/substitution/' . $this->file); + + if (!file_exists($this->getRealPath())) { $this->path = $this->app['root.path'] . '/www/assets/common/images/icons/'; $this->file = 'substitution.png'; $this->url = Url::factory('/assets/common/images/icons/' . $this->file); } - - return $this; } /** @@ -319,8 +368,9 @@ class media_subdef extends media_abstract implements cache_cacheableInterface */ public function get_permalink() { - if ( ! $this->permalink && $this->is_physically_present()) + if (null === $this->permalink && $this->is_physically_present()) { $this->permalink = media_Permalink_Adapter::getPermalink($this->app, $this->record->getDatabox(), $this); + } return $this->permalink; } @@ -337,37 +387,44 @@ class media_subdef extends media_abstract implements cache_cacheableInterface { if (!$this->etag && $this->is_physically_present()) { $file = new SplFileInfo($this->getRealPath()); + if ($file->isFile()) { - $this->setEtag(md5($file->getMTime())); + $this->setEtag(md5($file->getRealPath() . $file->getMTime())); } } return $this->etag; } + /** + * @param string|null $etag + */ public function setEtag($etag) { $this->etag = $etag; - $sql = "UPDATE subdef SET etag = :etag WHERE subdef_id = :subdef_id"; - $stmt = $this->record->getDatabox()->get_connection()->prepare($sql); - $stmt->execute([':subdef_id' => $this->subdef_id, ':etag' => $etag]); - $stmt->closeCursor(); + $this->getDataboxConnection()->executeUpdate( + 'UPDATE subdef SET etag = :etag WHERE subdef_id = :subdef_id', + [':subdef_id' => $this->subdef_id, ':etag' => $etag] + ); return $this; } + /** + * @param boolean $substit + */ public function set_substituted($substit) { $this->is_substituted = !!$substit; - $sql = "UPDATE subdef SET substit = :substit, updated_on=NOW() WHERE subdef_id = :subdef_id"; - $stmt = $this->record->getDatabox()->get_connection()->prepare($sql); - $stmt->execute(array( - ':subdef_id' => $this->subdef_id, - ':substit' => $this->is_substituted - )); - $stmt->closeCursor(); + $this->getDataboxConnection()->executeUpdate( + 'UPDATE subdef SET substit = :substit, updated_on=NOW() WHERE subdef_id = :subdef_id', + [ + ':subdef_id' => $this->subdef_id, + ':substit' => $this->is_substituted + ] + ); $this->delete_data_from_cache(); @@ -387,31 +444,22 @@ class media_subdef extends media_abstract implements cache_cacheableInterface */ public function get_type() { - switch ($this->mime) { - case 'video/mp4': - $type = self::TYPE_VIDEO_MP4; - break; - case 'video/x-flv': - $type = self::TYPE_VIDEO_FLV; - break; - case 'application/x-shockwave-flash': - $type = self::TYPE_FLEXPAPER; - break; - case 'audio/mpeg': - case 'audio/mp3': - $type = self::TYPE_AUDIO_MP3; - break; - case 'image/jpeg': - case 'image/png': - case 'image/gif': - $type = self::TYPE_IMAGE; - break; - default: - $type = self::TYPE_NO_PLAYER; - break; + static $types = [ + 'application/x-shockwave-flash' => self::TYPE_FLEXPAPER, + 'audio/mp3' => self::TYPE_AUDIO_MP3, + 'audio/mpeg' => self::TYPE_AUDIO_MP3, + 'image/gif' => self::TYPE_IMAGE, + 'image/jpeg' => self::TYPE_IMAGE, + 'image/png' => self::TYPE_IMAGE, + 'video/mp4' => self::TYPE_VIDEO_MP4, + 'video/x-flv' => self::TYPE_VIDEO_FLV, + ]; + + if (isset($types[$this->mime])) { + return $types[$this->mime]; } - return $type; + return self::TYPE_NO_PLAYER; } /** @@ -496,11 +544,11 @@ class media_subdef extends media_abstract implements cache_cacheableInterface } /** - * @return string + * @return Url */ public function renew_url() { - $this->generate_url(); + $this->url = $this->generateUrl(); return $this->get_url(); } @@ -520,7 +568,7 @@ class media_subdef extends media_abstract implements cache_cacheableInterface public function getDevices() { - if ($this->get_name() == 'document') { + if ($this->get_name() === 'document') { return [\databox_subdef::DEVICE_ALL]; } @@ -544,7 +592,7 @@ class media_subdef extends media_abstract implements cache_cacheableInterface */ public function rotate($angle, Alchemyst $alchemyst, MediaVorus $mediavorus) { - if ( ! $this->is_physically_present()) { + if (!$this->is_physically_present()) { throw new \Alchemy\Phrasea\Exception\RuntimeException('You can not rotate a substitution'); } @@ -559,27 +607,26 @@ class media_subdef extends media_abstract implements cache_cacheableInterface $media = $mediavorus->guess($this->getRealPath()); - $sql = "UPDATE subdef SET height = :height , width = :width, updated_on = NOW()" - . " WHERE record_id = :record_id AND name = :name"; + $sql = <<<'SQL' +UPDATE subdef SET height = :height , width = :width, updated_on = NOW() +WHERE record_id = :record_id AND name = :name +SQL; - $params = [ - ':width' => $media->getWidth(), - ':height' => $media->getHeight(), - ':record_id' => $this->get_record_id(), - ':name' => $this->get_name(), - ]; - - $stmt = $this->record->getDatabox()->get_connection()->prepare($sql); - $stmt->execute($params); - $stmt->closeCursor(); + $this->getDataboxConnection()->executeUpdate( + $sql, + [ + ':width' => $media->getWidth(), + ':height' => $media->getHeight(), + ':record_id' => $this->get_record_id(), + ':name' => $this->get_name(), + ] + ); $this->width = $media->getWidth(); $this->height = $media->getHeight(); $this->delete_data_from_cache(); - unset($media); - return $this; } @@ -591,7 +638,7 @@ class media_subdef extends media_abstract implements cache_cacheableInterface */ public function readTechnicalDatas(MediaVorus $mediavorus) { - if ( ! $this->is_physically_present()) { + if (!$this->is_physically_present()) { return []; } @@ -643,50 +690,47 @@ class media_subdef extends media_abstract implements cache_cacheableInterface return $datas; } - public static function create(Application $app, \record_adapter $record, $name, MediaInterface $media) + public static function create(Application $app, RecordReferenceInterface $record, $name, MediaInterface $media) { - $databox = $record->getDatabox(); - $connbas = $databox->get_connection(); - $path = $media->getFile()->getPath(); $newname = $media->getFile()->getFilename(); $params = [ - ':record_id' => $record->getRecordId(), - ':name' => $name, - ':path' => $path, - ':file' => $newname, - ':width' => 0, - ':height' => 0, - ':mime' => $media->getFile()->getMimeType(), - ':size' => $media->getFile()->getSize(), - ':dispatched' => 1, + 'record_id' => $record->getRecordId(), + 'name' => $name, + 'path' => $path, + 'file' => $newname, + 'width' => 0, + 'height' => 0, + 'mime' => $media->getFile()->getMimeType(), + 'size' => $media->getFile()->getSize(), + 'physically_present' => true, + 'is_substituted' => false, ]; if (method_exists($media, 'getWidth') && null !== $media->getWidth()) { - $params[':width'] = $media->getWidth(); + $params['width'] = $media->getWidth(); } if (method_exists($media, 'getHeight') && null !== $media->getHeight()) { - $params[':height'] = $media->getHeight(); + $params['height'] = $media->getHeight(); } - $sql = "INSERT INTO subdef" - . " (record_id, name, path, file, width, height, mime, size, dispatched, created_on, updated_on)" - . " VALUES (:record_id, :name, :path, :file, :width, :height, :mime, :size, :dispatched, NOW(), NOW())" - . " ON DUPLICATE KEY UPDATE" - . " path = VALUES(path), file = VALUES(file)," - . " width = VALUES(width) , height = VALUES(height), mime = VALUES(mime)," - . " size = VALUES(size), dispatched = VALUES(dispatched), updated_on = NOW()"; + $factoryProvider = $app['provider.factory.media_subdef']; + $factory = $factoryProvider($record->getDataboxId()); - $stmt = $connbas->prepare($sql); - $stmt->execute($params); - $stmt->closeCursor(); + $subdef = $factory($params); + Assertion::isInstanceOf($subdef, \media_subdef::class); - $subdef = new self($app, $record, $name); - $subdef->delete_data_from_cache(); + $repository = self::getMediaSubdefRepository($app, $record->getDataboxId()); + $repository->save($subdef); - if ($subdef->get_permalink() instanceof media_Permalink_Adapter) { - $subdef->get_permalink()->delete_data_from_cache(); + // Refresh from Database. + $subdef = $repository->findOneByRecordIdAndName($record->getRecordId(), $name); + + $permalink = $subdef->get_permalink(); + + if ($permalink instanceof media_Permalink_Adapter) { + $permalink->delete_data_from_cache(); } if ($name === 'thumbnail') { @@ -695,48 +739,44 @@ class media_subdef extends media_abstract implements cache_cacheableInterface $symlinker->symlink($subdef->getRealPath()); } - unset($media); - return $subdef; } - protected function generate_url() + /** + * @return Url + */ + protected function generateUrl() { if (!$this->is_physically_present()) { - return; + $this->markPhysicallyUnavailable(); + + return $this->url; } - // serve thumbnails using static file service - if ($this->get_name() === 'thumbnail') { - if (null !== $url = $this->app['phraseanet.static-file']->getUrl($this->getRealPath())) { - $url->getQuery()->offsetSet('etag', $this->getEtag()); - $this->url = $url; + $generators = [ + [$this, 'tryGetThumbnailUrl'], + [$this, 'tryGetVideoUrl'], + ]; - return; - } - } - - if ($this->app['phraseanet.h264-factory']->isH264Enabled() && in_array($this->mime, ['video/mp4'])) { - if (null !== $url = $this->app['phraseanet.h264']->getUrl($this->getRealPath())) { - $this->url = $url; + foreach ($generators as $generator) { + $url = $generator(); - return; + if ($url instanceof Url) { + return $url; } } - $this->url = Url::factory($this->app->path('datafile', [ + return Url::factory($this->app->path('datafile', [ 'sbas_id' => $this->record->getDataboxId(), 'record_id' => $this->record->getRecordId(), 'subdef' => $this->get_name(), 'etag' => $this->getEtag(), ])); - - return; } public function get_cache_key($option = null) { - return 'subdef_' . $this->get_record()->get_serialize_key() + return 'subdef_' . $this->get_record()->getId() . '_' . $this->name . ($option ? '_' . $option : ''); } @@ -785,4 +825,52 @@ class media_subdef extends media_abstract implements cache_cacheableInterface { return $this->path . 'stamp_' . $this->file; } + + /** + * @return \Doctrine\DBAL\Connection + */ + private function getDataboxConnection() + { + return $this->record->getDatabox()->get_connection(); + } + + /** + * @return bool + */ + private function isStillAccessible() + { + return $this->is_physically_present && file_exists($this->getRealPath()); + } + + /** + * @return Url|null + */ + protected function tryGetThumbnailUrl() + { + if ('thumbnail' !== $this->get_name()) { + return null; + } + + $url = $this->app['phraseanet.static-file']->getUrl($this->getRealPath()); + + if (null === $url) { + return null; + } + + $url->getQuery()->offsetSet('etag', $this->getEtag()); + + return $url; + } + + /** + * @return Url|null + */ + protected function tryGetVideoUrl() + { + if ($this->mime !== 'video/mp4' || !$this->app['phraseanet.h264-factory']->isH264Enabled()) { + return null; + } + + return $this->app['phraseanet.h264']->getUrl($this->getRealPath()); + } } diff --git a/lib/classes/module/console/systemExport.php b/lib/classes/module/console/systemExport.php index 272124fb75..f6763d52b5 100644 --- a/lib/classes/module/console/systemExport.php +++ b/lib/classes/module/console/systemExport.php @@ -252,9 +252,9 @@ class module_console_systemExport extends Command $dest_file = new \SplFileInfo($outfile); touch( - $dest_file->getPathname() - , $record->get_creation_date()->format('U') - , $record->get_modification_date()->format('U') + $dest_file->getPathname(), + $record->getCreated()->format('U'), + $record->getUpdated()->format('U') ); switch (strtolower($caption)) { diff --git a/lib/classes/module/report/nav.php b/lib/classes/module/report/nav.php index 25e42f3eed..09ad769ff9 100644 --- a/lib/classes/module/report/nav.php +++ b/lib/classes/module/report/nav.php @@ -516,7 +516,7 @@ class module_report_nav extends module_report 'photo' => "" - , 'record_id' => $record->get_record_id() + , 'record_id' => $record->getRecordId() , 'date' => $this->app['date-formatter']->getPrettyString($document->get_creation_date()) , 'type' => $document->get_mime() , 'titre' => $record->get_title() diff --git a/lib/classes/patch/320alpha4b.php b/lib/classes/patch/320alpha4b.php index a0e649bb87..fa1bd4184f 100644 --- a/lib/classes/patch/320alpha4b.php +++ b/lib/classes/patch/320alpha4b.php @@ -133,8 +133,8 @@ class patch_320alpha4b extends patchAbstract $item = new FeedItem(); $item->setEntry($entry); $entry->addItem($item); - $item->setRecordId($record->get_record_id()); - $item->setSbasId($record->get_sbas_id()); + $item->setRecordId($record->getRecordId()); + $item->setSbasId($record->getDataboxId()); $app['orm.em']->persist($item); } catch (NotFoundHttpException $e) { diff --git a/lib/classes/record/adapter.php b/lib/classes/record/adapter.php index a827125487..b765eee819 100644 --- a/lib/classes/record/adapter.php +++ b/lib/classes/record/adapter.php @@ -1,5 +1,5 @@ app = $app; $this->reference = RecordReference::createFromDataboxIdAndRecordId($sbas_id, $record_id); - $this->number = (int) $number; + $this->number = (int)$number; if ($load) { $this->load(); @@ -141,6 +140,7 @@ class record_adapter implements RecordInterface, cache_cacheableInterface $this->created = $record->getCreated(); $this->base_id = $record->getBaseId(); $this->collection_id = $record->getCollectionId(); + $this->status = $record->getStatus(); } /** @@ -201,7 +201,7 @@ class record_adapter implements RecordInterface, cache_cacheableInterface */ public function setNumber($number) { - $this->number = (int) $number; + $this->number = (int)$number; return $this; } @@ -370,8 +370,19 @@ class record_adapter implements RecordInterface, cache_cacheableInterface * Return record collection * * @return \collection + * @deprecated use {@link self::getCollection} instead. */ public function get_collection() + { + return $this->getCollection(); + } + + /** + * Return collection to which the record belongs to. + * + * @return \collection + */ + public function getCollection() { return \collection::getByCollectionId($this->app, $this->getDatabox(), $this->collection_id); } @@ -460,7 +471,7 @@ class record_adapter implements RecordInterface, cache_cacheableInterface */ public function move_to_collection(collection $collection, appbox $appbox) { - if ($this->get_collection()->get_base_id() === $collection->get_base_id()) { + if ($this->getCollection()->get_base_id() === $collection->get_base_id()) { return $this; } @@ -517,54 +528,9 @@ class record_adapter implements RecordInterface, cache_cacheableInterface return $this->sha256; } - /** - * @return string - */ - public function get_status() - { - if (!$this->status) { - $this->status = $this->retrieve_status(); - } - - return $this->status; - } - - /** - * @return string - * @throws Exception - * @throws \Doctrine\DBAL\DBALException - */ - protected function retrieve_status() - { - try { - $data = $this->get_data_from_cache(self::CACHE_STATUS); - } catch (Exception $e) { - $data = false; - } - - if (false !== $data) { - return $data; - } - - $status = $this->getDataboxConnection()->fetchColumn( - 'SELECT BIN(status) as status FROM record WHERE record_id = :record_id', - [':record_id' => $this->getRecordId()] - ); - - if (false === $status) { - throw new Exception('status not found'); - } - - $status = str_pad($status, 32, '0', STR_PAD_LEFT); - - $this->set_data_to_cache($status, self::CACHE_STATUS); - - return $status; - } - public function has_subdef($name) { - return in_array($name, $this->get_available_subdefs()); + return in_array($name, $this->get_available_subdefs(), false); } /** @@ -579,17 +545,13 @@ class record_adapter implements RecordInterface, cache_cacheableInterface return $this->subdefs[$name]; } - if (!in_array($name, $this->get_available_subdefs())) { + if (!in_array($name, $this->get_available_subdefs(), false)) { throw new Exception_Media_SubdefNotFound(sprintf("subdef `%s` not found", $name)); } - if (!$this->subdefs) { - $this->subdefs = []; - } + $subdefs = $this->getMediaSubdefRepository()->findOneByRecordIdAndName($this->getRecordId(), $name); - $substitute = ($name !== 'document'); - - return $this->subdefs[$name] = new media_subdef($this->app, $this, $name, $substitute); + return $subdefs ?: new media_subdef($this->app, $this, $name, ($name !== 'document')); } /** @@ -607,15 +569,15 @@ class record_adapter implements RecordInterface, cache_cacheableInterface if (isset($availableSubdefs['document'])) { - $mime_ok = !$mimes || in_array($availableSubdefs['document']->get_mime(), (array) $mimes); - $devices_ok = !$devices || array_intersect($availableSubdefs['document']->getDevices(), (array) $devices); + $mime_ok = !$mimes || in_array($availableSubdefs['document']->get_mime(), (array)$mimes, false); + $devices_ok = !$devices || array_intersect($availableSubdefs['document']->getDevices(), (array)$devices); if ($mime_ok && $devices_ok) { $subdefs['document'] = $availableSubdefs['document']; } } - $searchDevices = array_merge((array) $devices, (array) databox_subdef::DEVICE_ALL); + $searchDevices = array_merge((array)$devices, (array)databox_subdef::DEVICE_ALL); $type = $this->isStory() ? 'image' : $this->getType(); @@ -641,7 +603,7 @@ class record_adapter implements RecordInterface, cache_cacheableInterface continue; } - if ($mimes && !in_array($subdef->get_mime(), (array) $mimes)) { + if ($mimes && !in_array($subdef->get_mime(), (array)$mimes)) { continue; } @@ -656,13 +618,20 @@ class record_adapter implements RecordInterface, cache_cacheableInterface */ public function get_subdefs() { - if (!$this->subdefs) { - $this->subdefs = []; + if (null !== $this->subdefs) { + return $this->subdefs; } - $subdefs = $this->get_available_subdefs(); - foreach ($subdefs as $name) { - $this->get_subdef($name); + $this->subdefs = []; + + foreach ($this->getMediaSubdefRepository()->findByRecordIdsAndNames([$this->getRecordId()]) as $subdef) { + $this->subdefs[$subdef->get_name()] = $subdef; + } + + foreach (['preview', 'thumbnail'] as $name) { + if (!isset($this->subdefs[$name])) { + $this->subdefs[$name] = new media_subdef($this->app, $this, $name, true, []); + } } return $this->subdefs; @@ -673,6 +642,10 @@ class record_adapter implements RecordInterface, cache_cacheableInterface */ protected function get_available_subdefs() { + if (null !== $this->subdefs) { + return array_keys($this->subdefs); + } + try { $data = $this->get_data_from_cache(self::CACHE_SUBDEFS); } catch (\Exception $e) { @@ -683,18 +656,8 @@ class record_adapter implements RecordInterface, cache_cacheableInterface return $data; } - $rs = $this->getDataboxConnection()->fetchAll( - 'SELECT name FROM subdef WHERE record_id = :record_id', - ['record_id' => $this->getRecordId()] - ); + $subdefs = array_keys($this->get_subdefs()); - $subdefs = ['preview', 'thumbnail']; - - foreach ($rs as $row) { - $subdefs[] = $row['name']; - } - - $subdefs = array_unique($subdefs); $this->set_data_to_cache($subdefs, self::CACHE_SUBDEFS); return $subdefs; @@ -716,13 +679,10 @@ class record_adapter implements RecordInterface, cache_cacheableInterface */ public function get_technical_infos($data = '') { - if (!$this->technical_data && !$this->mapTechnicalDataFromCache()) { - $this->technical_data = []; - $rs = $this->fetchTechnicalDataFromDb(); + if (null === $this->technical_data) { + $sets = $this->app['service.technical_data']->fetchRecordsTechnicalData([$this]); - $this->mapTechnicalDataFromDb($rs); - - $this->set_data_to_cache($this->technical_data, self::CACHE_TECHNICAL_DATA); + $this->setTechnicalDataSet(reset($sets)); } if ($data) { @@ -736,12 +696,21 @@ class record_adapter implements RecordInterface, cache_cacheableInterface return $this->technical_data; } + /** + * @param TechnicalDataSet $dataSet + * @internal + */ + public function setTechnicalDataSet(TechnicalDataSet $dataSet) + { + $this->technical_data = $dataSet; + } + /** * @return caption_record */ public function get_caption() { - return new caption_record($this->app, $this, $this->getDatabox()); + return new caption_record($this->app, $this); } public function getCaption(array $fields = null) @@ -758,7 +727,7 @@ class record_adapter implements RecordInterface, cache_cacheableInterface $collection = []; foreach ($fields as $field) { - $values = array_map(function(caption_Field_Value $fieldValue) { + $values = array_map(function (caption_Field_Value $fieldValue) { return $fieldValue->getValue(); }, $field->get_values()); @@ -857,7 +826,7 @@ class record_adapter implements RecordInterface, cache_cacheableInterface } if (count($fields_to_retrieve) > 0) { - $retrieved_fields = $this->get_caption()->get_highlight_fields($highlight, $fields_to_retrieve, $searchEngine); + $retrieved_fields = $this->get_caption()->get_highlight_fields($highlight, $fields_to_retrieve); $titles = []; foreach ($retrieved_fields as $value) { foreach ($value['values'] as $v) { @@ -904,6 +873,7 @@ class record_adapter implements RecordInterface, cache_cacheableInterface /** * @return string + * @deprecated use {@link self::getId} instead. */ public function get_serialize_key() { @@ -1066,16 +1036,16 @@ class record_adapter implements RecordInterface, cache_cacheableInterface } $record = $this; - $wanted_subdefs = array_map(function(databox_subdef $subDef) { + $wanted_subdefs = array_map(function (databox_subdef $subDef) { return $subDef->get_name(); - }, array_filter($subDefDefinitions, function(databox_subdef $subDef) use ($record) { + }, array_filter(iterator_to_array($subDefDefinitions), function (databox_subdef $subDef) use ($record) { return !$record->has_subdef($subDef->get_name()); })); - $missing_subdefs = array_map(function(media_subdef $subDef) { + $missing_subdefs = array_map(function (media_subdef $subDef) { return $subDef->get_name(); - }, array_filter($this->get_subdefs(), function(media_subdef $subdef) { + }, array_filter($this->get_subdefs(), function (media_subdef $subdef) { return !$subdef->is_physically_present(); })); @@ -1096,26 +1066,6 @@ class record_adapter implements RecordInterface, cache_cacheableInterface return $this; } - /** - * @param string $status - * @return record_adapter - */ - public function set_binary_status($status) - { - $connection = $this->getDataboxConnection(); - - $connection->executeUpdate( - 'UPDATE record SET moddate = NOW(), status = :status WHERE record_id= :record_id', - ['status' => bindec($status), 'record_id' => $this->getRecordId()] - ); - - $this->delete_data_from_cache(self::CACHE_STATUS); - - $this->dispatch(RecordEvents::STATUS_CHANGED, new StatusChangedEvent($this)); - - return $this; - } - private function dispatch($eventName, RecordEvent $event) { $this->app['dispatcher']->dispatch($eventName, $event); @@ -1255,12 +1205,12 @@ class record_adapter implements RecordInterface, cache_cacheableInterface } $connection = $this->getDataboxConnection(); - $sql = 'DELETE FROM technical_datas WHERE record_id = :record_id'; - $stmt = $connection->prepare($sql); - $stmt->execute([':record_id' => $this->getRecordId()]); - $stmt->closeCursor(); + $connection->executeUpdate('DELETE FROM technical_datas WHERE record_id = :record_id', [ + ':record_id' => $this->getRecordId(), + ]); $sqlValues = []; + foreach ($document->readTechnicalDatas($mediavorus) as $name => $value) { if (is_null($value)) { continue; @@ -1271,21 +1221,15 @@ class record_adapter implements RecordInterface, cache_cacheableInterface $value = 0; } } - $sqlValues[] = join(',', array( - 'null', - $connection->quote($this->getRecordId()), - $connection->quote($name), - $connection->quote($value), - )); + $sqlValues[] = [$this->getRecordId(), $name, $value]; } - $sql = "INSERT INTO technical_datas (id, record_id, name, value)" - . " VALUES (" . join('),(', $sqlValues) . ")"; - ; - $stmt = $connection->prepare($sql); - $stmt->execute(); - - $stmt->closeCursor(); + if ($sqlValues) { + $connection->transactional(function (Connection $connection) use ($sqlValues) { + $statement = $connection->prepare('INSERT INTO technical_datas (record_id, name, value) VALUES (?, ?, ?)'); + array_walk($sqlValues, [$statement, 'execute']); + }); + } $this->delete_data_from_cache(self::CACHE_TECHNICAL_DATA); @@ -1357,8 +1301,9 @@ class record_adapter implements RecordInterface, cache_cacheableInterface $ftodel = []; foreach ($this->get_subdefs() as $subdef) { - if (!$subdef->is_physically_present()) + if (!$subdef->is_physically_present()) { continue; + } if ($subdef->get_name() === 'thumbnail') { $this->app['phraseanet.thumb-symlinker']->unlink($subdef->getRealPath()); @@ -1366,11 +1311,13 @@ class record_adapter implements RecordInterface, cache_cacheableInterface $ftodel[] = $subdef->getRealPath(); $watermark = $subdef->getWatermarkRealPath(); - if (file_exists($watermark)) + if (file_exists($watermark)) { $ftodel[] = $watermark; + } $stamp = $subdef->getStampRealPath(); - if (file_exists($stamp)) + if (file_exists($stamp)) { $ftodel[] = $stamp; + } } $origcoll = $this->collection_id; @@ -1500,16 +1447,13 @@ class record_adapter implements RecordInterface, cache_cacheableInterface public function delete_data_from_cache($option = null) { - switch ($option) { - case self::CACHE_STATUS: - $this->status = null; - break; + switch ($option) + { case self::CACHE_SUBDEFS: $this->subdefs = null; break; - default: - break; } + $databox = $this->getDatabox(); $databox->delete_data_from_cache($this->get_cache_key($option)); @@ -1540,7 +1484,7 @@ class record_adapter implements RecordInterface, cache_cacheableInterface /** * @todo de meme avec stories - * @return Array + * @return \Alchemy\Phrasea\Model\Entities\Basket[] */ public function get_container_baskets(EntityManager $em, User $user) { @@ -1560,8 +1504,8 @@ class record_adapter implements RecordInterface, cache_cacheableInterface */ public static function get_records_by_originalname(databox $databox, $original_name, $caseSensitive = false, $offset_start = 0, $how_many = 10) { - $offset_start = (int) ($offset_start < 0 ? 0 : $offset_start); - $how_many = (int) (($how_many > 20 || $how_many < 1) ? 10 : $how_many); + $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', @@ -1694,7 +1638,7 @@ class record_adapter implements RecordInterface, cache_cacheableInterface public function hasChild(\record_adapter $record) { - return $this->get_children()->offsetExists($record->get_serialize_key()); + return $this->getChildren()->offsetExists($record->getId()); } public function appendChild(\record_adapter $record) @@ -1786,17 +1730,36 @@ class record_adapter implements RecordInterface, cache_cacheableInterface return $this->reference->getId(); } + /** + * @param string $status + * @return void + */ public function setStatus($status) { - $this->set_binary_status($status); + $this->getDataboxConnection()->executeUpdate( + 'UPDATE record SET moddate = NOW(), status = :status WHERE record_id=:record_id', + ['status' => bindec($status), 'record_id' => $this->getRecordId()] + ); - $this->delete_data_from_cache(self::CACHE_STATUS); + $this->status = str_pad($status, 32, '0', STR_PAD_LEFT); + // modification date is now unknown, delete from cache to reload on another record + $this->delete_data_from_cache(); + + $this->dispatch(RecordEvents::STATUS_CHANGED, new StatusChangedEvent($this)); + } + + /** + * @return string + */ + public function getStatus() + { + return $this->status; } /** {@inheritdoc} */ public function getStatusBitField() { - return bindec($this->get_status()); + return bindec($this->getStatus()); } /** {@inheritdoc} */ @@ -1823,6 +1786,7 @@ class record_adapter implements RecordInterface, cache_cacheableInterface 'created' => $this->created->format(DATE_ISO8601), 'base_id' => $this->base_id, 'collection_id' => $this->collection_id, + 'status' => $this->status, ]; $this->set_data_to_cache($data); @@ -1848,64 +1812,13 @@ class record_adapter implements RecordInterface, cache_cacheableInterface $this->original_name = $row['originalName']; $this->sha256 = $row['sha256']; $this->mime = $row['mime']; - } - - /** - * @return bool - */ - private function mapTechnicalDataFromCache() - { - try { - $technical_data = $this->get_data_from_cache(self::CACHE_TECHNICAL_DATA); - } catch (Exception $e) { - $technical_data = false; - } - - if (false === $technical_data) { - return false; - } - - $this->technical_data = $technical_data; - - return true; - } - - /** - * @return false|array - */ - private function fetchTechnicalDataFromDb() - { - $sql = 'SELECT name, value FROM technical_datas WHERE record_id = :record_id'; - - return $this->getDataboxConnection() - ->fetchAll($sql, ['record_id' => $this->getRecordId()]); - } - - /** - * @param array $rows - */ - private function mapTechnicalDataFromDb(array $rows) - { - $this->technical_data = new ArrayTechnicalDataSet(); - - foreach ($rows as $row) { - switch (true) { - case ctype_digit($row['value']): - $this->technical_data[] = new IntegerTechnicalData($row['name'], $row['value']); - break; - case preg_match('/[0-9]?\.[0-9]+/', $row['value']): - $this->technical_data[] = new FloatTechnicalData($row['name'], $row['value']); - break; - default: - $this->technical_data[] = new StringTechnicalData($row['name'], $row['value']); - } - } + $this->status = str_pad($row['status'], 32, '0', STR_PAD_LEFT); } /** * @return Connection */ - private function getDataboxConnection() + protected function getDataboxConnection() { if (null === $this->connection) { $this->connection = $this->getDatabox()->get_connection(); @@ -1913,4 +1826,12 @@ class record_adapter implements RecordInterface, cache_cacheableInterface return $this->connection; } + + /** + * @return MediaSubdefRepository + */ + private function getMediaSubdefRepository() + { + return $this->app['provider.repo.media_subdef']->getRepositoryForDatabox($this->getDataboxId()); + } } diff --git a/lib/classes/record/exportElement.php b/lib/classes/record/exportElement.php index fe225f145d..737ad5a1ad 100644 --- a/lib/classes/record/exportElement.php +++ b/lib/classes/record/exportElement.php @@ -83,14 +83,13 @@ class record_exportElement extends record_adapter $sd = $this->get_subdefs(); - $sbas_id = phrasea::sbasFromBas($this->app, $this->get_base_id()); - - $subdefgroups = $this->app->findDataboxById($sbas_id)->get_subdef_structure(); + $sbas_id = phrasea::sbasFromBas($this->app, $this->getBaseId()); + /** @var databox_subdef[] $subdefs */ $subdefs = []; - foreach ($subdefgroups as $subdef_type => $subdefs_obj) { - if ($subdef_type == $this->get_type()) { + foreach ($this->app->findDataboxById($sbas_id)->get_subdef_structure() as $subdef_type => $subdefs_obj) { + if ($subdef_type == $this->getType()) { $subdefs = $subdefs_obj; break; } @@ -102,10 +101,10 @@ class record_exportElement extends record_adapter 'thumbnail' => true ]; - if ($this->app->getAclForUser($this->app->getAuthenticatedUser())->has_right_on_base($this->get_base_id(), 'candwnldhd')) { + if ($this->app->getAclForUser($this->app->getAuthenticatedUser())->has_right_on_base($this->getBaseId(), 'candwnldhd')) { $go_dl['document'] = true; } - if ($this->app->getAclForUser($this->app->getAuthenticatedUser())->has_right_on_base($this->get_base_id(), 'candwnldpreview')) { + if ($this->app->getAclForUser($this->app->getAuthenticatedUser())->has_right_on_base($this->getBaseId(), 'candwnldpreview')) { $go_dl['preview'] = true; } if ($this->app->getAclForUser($this->app->getAuthenticatedUser())->has_hd_grant($this)) { @@ -118,18 +117,18 @@ class record_exportElement extends record_adapter $query = $this->app['phraseanet.user-query']; - $masters = $query->on_base_ids([$this->get_base_id()]) + $masters = $query->on_base_ids([$this->getBaseId()]) ->who_have_right(['order_master']) ->execute()->get_results(); - $go_cmd = (count($masters) > 0 && $this->app->getAclForUser($this->app->getAuthenticatedUser())->has_right_on_base($this->get_base_id(), 'cancmd')); + $go_cmd = (count($masters) > 0 && $this->app->getAclForUser($this->app->getAuthenticatedUser())->has_right_on_base($this->getBaseId(), 'cancmd')); $orderable['document'] = false; $downloadable['document'] = false; 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())) { + if ($this->app->getAclForUser($this->app->getAuthenticatedUser())->is_restricted_download($this->getBaseId())) { $this->remain_hd --; if ($this->remain_hd >= 0) { $localizedLabel = $this->app->trans('document original'); @@ -183,7 +182,7 @@ class record_exportElement extends record_adapter if (isset($sd[$name]) && $sd[$name]->is_physically_present()) { if ($class == 'document') { - if ($this->app->getAclForUser($this->app->getAuthenticatedUser())->is_restricted_download($this->get_base_id())) { + if ($this->app->getAclForUser($this->app->getAuthenticatedUser())->is_restricted_download($this->getBaseId())) { $this->remain_hd --; if ($this->remain_hd >= 0) $downloadable[$name] = [ diff --git a/lib/classes/record/preview.php b/lib/classes/record/preview.php index 805b9ba120..4c84db903e 100644 --- a/lib/classes/record/preview.php +++ b/lib/classes/record/preview.php @@ -10,8 +10,7 @@ */ use Alchemy\Phrasea\Application; -use Alchemy\Phrasea\Model\Entities\Basket; -use Alchemy\Phrasea\Model\Entities\BasketElement; +use Alchemy\Phrasea\Model\Entities\FeedEntry; use Alchemy\Phrasea\SearchEngine\SearchEngineInterface; use Alchemy\Phrasea\SearchEngine\SearchEngineOptions; use Guzzle\Http\Url; @@ -19,69 +18,59 @@ use Guzzle\Http\Url; class record_preview extends record_adapter { /** - * * @var string */ protected $env; /** - * * @var int */ protected $total; /** - * * @var string */ protected $name; /** - * * @var mixed content */ protected $container; /** - * * @var mixed content */ protected $train; /** - * * @var string */ protected $title; /** - * - * @var Array + * @var array */ protected $short_history; /** - * - * @var media + * @var media_adapter */ protected $view_popularity; /** - * - * @var media + * @var media_adapter */ protected $refferer_popularity; /** - * - * @var media + * @var media_adapter */ protected $download_popularity; protected $original_item; protected $pos; - protected$search_engine; + protected $searchEngine; protected $query; protected $options; @@ -100,8 +89,13 @@ class record_preview extends record_adapter if (null === $search_engine) { throw new \LogicException('Search Engine should be provided'); } + if (!$options) { + $options = new SearchEngineOptions(); + } + $options->setFirstResult($pos); + $options->setMaxResults(1); - $results = $search_engine->query($query, (int) ($pos), 1, $options); + $results = $search_engine->query($query, $options); if ($results->getResults()->isEmpty()) { throw new Exception('Record introuvable'); @@ -124,11 +118,11 @@ class record_preview extends record_adapter if ($pos == 0) { $number = 0; } else { - $children = $this->container->get_children(); + $children = $this->container->getChildren(); foreach ($children as $child) { - $sbas_id = $child->get_sbas_id(); + $sbas_id = $child->getDataboxId(); $this->original_item = $child; - $record_id = $child->get_record_id(); + $record_id = $child->getRecordId(); if ($child->getNumber() == $pos) break; } @@ -141,34 +135,25 @@ class record_preview extends record_adapter $Basket = $app['converter.basket']->convert($contId); $app['acl.basket']->hasAccess($Basket, $app->getAuthenticatedUser()); - /* @var $Basket Basket */ $this->container = $Basket; $this->total = $Basket->getElements()->count(); $i = 0; $first = true; foreach ($Basket->getElements() as $element) { - /* @var $element BasketElement */ $i ++; - if ($first) { + if ($element->getOrd() == $pos || $first) { $this->original_item = $element; - $sbas_id = $element->getRecord($this->app)->get_sbas_id(); - $record_id = $element->getRecord($this->app)->get_record_id(); - $this->name = $Basket->getName(); - $number = $element->getOrd(); - } - $first = false; - - if ($element->getOrd() == $pos) { - $this->original_item = $element; - $sbas_id = $element->getRecord($this->app)->get_sbas_id(); - $record_id = $element->getRecord($this->app)->get_record_id(); + $sbas_id = $element->getSbasId(); + $record_id = $element->getRecordId(); $this->name = $Basket->getName(); $number = $element->getOrd(); + $first = false; } } break; case "FEED": + /** @var FeedEntry $entry */ $entry = $app['repo.feed-entries']->find($contId); $this->container = $entry; @@ -178,25 +163,24 @@ class record_preview extends record_adapter foreach ($entry->getItems() as $element) { $i ++; - if ($first) { - $sbas_id = $element->getRecord($this->app)->get_sbas_id(); - $record_id = $element->getRecord($this->app)->get_record_id(); - $this->name = $entry->getTitle(); - $this->original_item = $element; - $number = $element->getOrd(); - } - $first = false; - - if ($element->getOrd() == $pos) { - $sbas_id = $element->getRecord($this->app)->get_sbas_id(); - $record_id = $element->getRecord($this->app)->get_record_id(); + if ($element->getOrd() == $pos || $first) { + $sbas_id = $element->getSbasId(); + $record_id = $element->getRecordId(); $this->name = $entry->getTitle(); $this->original_item = $element; $number = $element->getOrd(); + $first = false; } } break; + default: + throw new InvalidArgumentException(sprintf('Expects env argument to one of (RESULT, REG, BASK, FEED) got %s', $env)); } + + if (!(isset($sbas_id) && isset($record_id))) { + throw new Exception('No record could be found'); + } + parent::__construct($app, $sbas_id, $record_id, $number); } @@ -208,9 +192,11 @@ class record_preview extends record_adapter switch ($this->env) { case 'RESULT': - $perPage = 56; - $index = ($this->pos - 3) < 0 ? 0 : ($this->pos - 3); - $results = $this->searchEngine->query($this->query, $index, $perPage, $this->options); + $options = $this->options ?: new SearchEngineOptions(); + $options->setFirstResult(($this->pos - 3) < 0 ? 0 : ($this->pos - 3)); + $options->setMaxResults(56); + + $results = $this->searchEngine->query($this->query, $options); $this->train = $results->getResults()->toArray(); break; @@ -226,22 +212,23 @@ class record_preview extends record_adapter } /** - * - * @return boolean + * @return bool */ public function is_from_result() { return $this->env == 'RESULT'; } + /** + * @return bool + */ public function is_from_feed() { return $this->env == 'FEED'; } /** - * - * @return boolean + * @return bool */ public function is_from_basket() { @@ -249,8 +236,7 @@ class record_preview extends record_adapter } /** - * - * @return boolean + * @return bool */ public function is_from_reg() { @@ -272,7 +258,7 @@ class record_preview extends record_adapter return $this->title; } - $this->title = collection::getLogo($this->get_base_id(), $this->app) . ' '; + $this->title = collection::getLogo($this->getBaseId(), $this->app) . ' '; switch ($this->env) { @@ -303,7 +289,6 @@ class record_preview extends record_adapter } /** - * * @return mixed content */ public function get_container() @@ -312,8 +297,7 @@ class record_preview extends record_adapter } /** - * - * @return Array + * @return array */ public function get_short_history() { @@ -323,16 +307,13 @@ class record_preview extends record_adapter $tab = []; - $report = $this->app->getAclForUser($this->app->getAuthenticatedUser())->has_right_on_base($this->get_base_id(), 'canreport'); - - $databox = $this->app->findDataboxById($this->get_sbas_id()); - $connsbas = $databox->get_connection(); + $report = $this->app->getAclForUser($this->app->getAuthenticatedUser())->has_right_on_base($this->getBaseId(), 'canreport'); $sql = 'SELECT d . * , l.user, l.usrid as usr_id, l.site FROM log_docs d, log l WHERE d.log_id = l.id AND d.record_id = :record_id '; - $params = [':record_id' => $this->get_record_id()]; + $params = [':record_id' => $this->getRecordId()]; if (! $report) { $sql .= ' AND ((l.usrid = :usr_id AND l.site= :site) OR action="add")'; @@ -342,12 +323,7 @@ class record_preview extends record_adapter $sql .= 'ORDER BY d.date, usrid DESC'; - $stmt = $connsbas->prepare($sql); - $stmt->execute($params); - $rs = $stmt->fetchAll(PDO::FETCH_ASSOC); - $stmt->closeCursor(); - - foreach ($rs as $row) { + foreach ($this->getDataboxConnection()->executeQuery($sql, $params)->fetchAll(PDO::FETCH_ASSOC) as $row) { $hour = $this->app['date-formatter']->getPrettyString(new DateTime($row['date'])); if ( ! isset($tab[$hour])) @@ -374,7 +350,7 @@ class record_preview extends record_adapter if ( ! in_array($row['final'], $tab[$hour][$site][$action][$row['usr_id']]['final'])) { if ($action == 'collection') { - $tab[$hour][$site][$action][$row['usr_id']]['final'][] = phrasea::baseFromColl($this->get_sbas_id(), $row['final'], $this->app); + $tab[$hour][$site][$action][$row['usr_id']]['final'][] = phrasea::baseFromColl($this->getDataboxId(), $row['final'], $this->app); } else { $tab[$hour][$site][$action][$row['usr_id']]['final'][] = $row['final']; } @@ -390,8 +366,7 @@ class record_preview extends record_adapter } /** - * - * @return media_image + * @return media_adapter */ public function get_view_popularity() { @@ -400,7 +375,7 @@ class record_preview extends record_adapter } $report = $this->app->getAclForUser($this->app->getAuthenticatedUser())->has_right_on_base( - $this->get_base_id(), 'canreport'); + $this->getBaseId(), 'canreport'); if ( ! $report && ! $this->app['conf']->get(['registry', 'webservices', 'google-charts-enabled'])) { $this->view_popularity = false; @@ -429,19 +404,14 @@ class record_preview extends record_adapter AND site_id = :site GROUP BY datee ORDER BY datee ASC'; - $databox = $this->app->findDataboxById($this->get_sbas_id()); - $connsbas = $databox->get_connection(); - $stmt = $connsbas->prepare($sql); - $stmt->execute( - [ - ':record_id' => $this->get_record_id(), - ':site' => $this->app['conf']->get(['main', 'key']) - ] - ); - $rs = $stmt->fetchAll(PDO::FETCH_ASSOC); - $stmt->closeCursor(); + $result = $this->getDataboxConnection() + ->executeQuery($sql, [ + ':record_id' => $this->getRecordId(), + ':site' => $this->app['conf']->get(['main', 'key']) + ]) + ->fetchAll(PDO::FETCH_ASSOC); - foreach ($rs as $row) { + foreach ($result as $row) { if (isset($views[$row['datee']])) { $views[$row['datee']] = (int) $row['views']; $top = max((int) $row['views'], $top); @@ -480,8 +450,7 @@ class record_preview extends record_adapter } /** - * - * @return media + * @return media_adapter */ public function get_refferer_popularity() { @@ -490,7 +459,7 @@ class record_preview extends record_adapter } $report = $this->app->getAclForUser($this->app->getAuthenticatedUser())->has_right_on_base( - $this->get_base_id(), 'canreport'); + $this->getBaseId(), 'canreport'); if ( ! $report && ! $this->app['conf']->get(['registry', 'webservices', 'google-charts-enabled'])) { $this->refferer_popularity = false; @@ -498,23 +467,19 @@ class record_preview extends record_adapter return $this->refferer_popularity; } - $databox = $this->app->findDataboxById($this->get_sbas_id()); - $connsbas = $databox->get_connection(); - $sql = 'SELECT count( id ) AS views, referrer FROM `log_view` WHERE record_id = :record_id AND date > ( NOW( ) - INTERVAL 1 MONTH ) GROUP BY referrer ORDER BY referrer ASC'; - $stmt = $connsbas->prepare($sql); - $stmt->execute([':record_id' => $this->get_record_id()]); - $rs = $stmt->fetchAll(PDO::FETCH_ASSOC); - $stmt->closeCursor(); + $result = $this->getDataboxConnection() + ->executeQuery($sql, [':record_id' => $this->getRecordId()]) + ->fetchAll(PDO::FETCH_ASSOC); $referrers = []; - foreach ($rs as $row) { + foreach ($result as $row) { if ($row['referrer'] == 'NO REFERRER') $row['referrer'] = $this->app->trans('report::acces direct'); if ($row['referrer'] == $this->app['conf']->get('servername') . 'prod/') @@ -553,8 +518,7 @@ class record_preview extends record_adapter } /** - * - * @return media + * @return media_adapter */ public function get_download_popularity() { @@ -562,7 +526,7 @@ class record_preview extends record_adapter return $this->download_popularity; } - $report = $this->app->getAclForUser($this->app->getAuthenticatedUser())->has_right_on_base($this->get_base_id(), 'canreport'); + $report = $this->app->getAclForUser($this->app->getAuthenticatedUser())->has_right_on_base($this->getBaseId(), 'canreport'); $ret = false; if ( ! $report && ! $this->app['conf']->get(['registry', 'webservices', 'google-charts-enabled'])) { @@ -592,21 +556,16 @@ class record_preview extends record_adapter AND site= :site GROUP BY datee ORDER BY datee ASC'; - $databox = $this->app->findDataboxById($this->get_sbas_id()); - $connsbas = $databox->get_connection(); - $stmt = $connsbas->prepare($sql); - $stmt->execute( - [ - ':record_id' => $this->get_record_id(), + $result = $this->getDataboxConnection() + ->executeQuery($sql, [ + ':record_id' => $this->getRecordId(), ':site' => $this->app['conf']->get(['main', 'key']) - ] - ); - $rs = $stmt->fetchAll(PDO::FETCH_ASSOC); - $stmt->closeCursor(); + ]) + ->fetchAll(PDO::FETCH_ASSOC); $top = 10; - foreach ($rs as $row) { + foreach ($result as $row) { if (isset($dwnls[$row['datee']])) { $dwnls[$row['datee']] = (int) $row['dwnl']; $top = max(((int) $row['dwnl'] + 10), $top); diff --git a/lib/classes/recordutils/image.php b/lib/classes/recordutils/image.php index 1c0fa411e9..a7fee96aeb 100644 --- a/lib/classes/recordutils/image.php +++ b/lib/classes/recordutils/image.php @@ -49,7 +49,7 @@ class recordutils_image } }; - $base_id = $subdef->get_record()->get_base_id(); + $base_id = $subdef->get_record()->getBaseId(); if ($subdef->get_type() !== \media_subdef::TYPE_IMAGE) { return $subdef->getRealPath(); @@ -71,7 +71,7 @@ class recordutils_image $domprefs = new DOMDocument(); - if (false === $domprefs->loadXML($subdef->get_record()->get_collection()->get_prefs())) { + if (false === $domprefs->loadXML($subdef->get_record()->getCollection()->get_prefs())) { return $subdef->getRealPath(); } @@ -172,7 +172,7 @@ class recordutils_image @unlink($pathOut); // no cache possible when date changes break; case 'RECORD_ID': - $varval = $subdef->get_record()->get_record_id(); + $varval = $subdef->get_record()->getRecordId(); break; } $n->parentNode->replaceChild($domprefs->createTextNode($varval), $n); @@ -381,7 +381,7 @@ class recordutils_image $palette = new RGB(); } - $base_id = $subdef->get_record()->get_base_id(); + $base_id = $subdef->get_record()->getBaseId(); if ($subdef->get_name() !== 'preview') { return $subdef->getRealPath(); @@ -429,7 +429,7 @@ class recordutils_image $in_image->paste($wm_image, new Point(($in_w - $wm_size->getWidth()) >> 1, ($in_h - $wm_size->getHeight()) >> 1))->save($pathOut); } else { - $collname = $subdef->get_record()->get_collection()->get_name(); + $collname = $subdef->get_record()->getCollection()->get_name(); $draw = $in_image->draw(); $black = $palette->color("000000"); $white = $palette->color("FFFFFF"); diff --git a/lib/classes/set/abstract.php b/lib/classes/set/abstract.php index d724199676..499332df36 100644 --- a/lib/classes/set/abstract.php +++ b/lib/classes/set/abstract.php @@ -121,7 +121,7 @@ abstract class set_abstract implements IteratorAggregate */ public function add_element(\record_adapter $record) { - $this->elements[$record->get_serialize_key()] = $record; + $this->elements[$record->getId()] = $record; return $this; } @@ -132,7 +132,7 @@ abstract class set_abstract implements IteratorAggregate */ public function remove_element(\record_adapter $record) { - $key = $record->get_serialize_key(); + $key = $record->getId(); if (isset($this->elements[$key])) unset($this->elements[$key]); @@ -146,7 +146,7 @@ abstract class set_abstract implements IteratorAggregate { $basrec = []; foreach ($this->elements as $record) { - $basrec[] = $record->get_serialize_key(); + $basrec[] = $record->getId(); } return implode(';', $basrec); diff --git a/lib/classes/unicode.php b/lib/classes/unicode.php index 4372f89fca..cb360240df 100644 --- a/lib/classes/unicode.php +++ b/lib/classes/unicode.php @@ -1541,17 +1541,7 @@ class unicode ] ]; - protected $endCharacters_utf8 = "\t\r\n !\"#\$%&'()+,-./:;<=>@[\]^_`{|}~£§¨°"; - - public function get_indexer_bad_chars() - { - return $this->endCharacters_utf8; - } - - public function has_indexer_bad_char($string) - { - return mb_strpos($this->endCharacters_utf8, $string); - } + protected $endCharacters_utf8 = "\t\r\n !\"#\$%&'()+,-./:;<=>@[\\]^_`{|}~£§¨°"; /** * Converts a string diff --git a/lib/conf.d/json_schema/orders.json b/lib/conf.d/json_schema/orders.json new file mode 100644 index 0000000000..9e10d09909 --- /dev/null +++ b/lib/conf.d/json_schema/orders.json @@ -0,0 +1,119 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "definitions": { + "order_request": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "usage": { + "type": "string" + }, + "deadline": { + "type": "string", + "format": "date-time" + }, + "records": { + "type": "array", + "items": { + "$ref": "records.json#/definitions/record" + } + } + }, + "required": [ + "usage", + "records" + ] + } + }, + "required": ["data"] + }, + "order": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "owner_id": { + "type": "integer" + }, + "created": { + "type": "string", + "format": "date-time" + }, + "usage": { + "type": "string" + }, + "deadline": { + "type": "string", + "format": "date-time" + }, + "elements": { + "type": "array", + "items": { + "$ref": "records.json#/definitions/record" + } + }, + "basket_id": { + "$ref": "records.json#/definitions/record" + } + }, + "required": [ + "id", + "owner_id", + "created", + "usage", + "elements" + ] + }, + "order_element": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "record_id": { + "$ref": "records.json#/definitions/record" + }, + "order_master_id": { + "type": "integer" + }, + "status": { + "enum": ["accepted", "rejected"] + } + }, + "required": [ + "id", + "record_id" + ] + }, + "order_element_id": { + "type": "object", + "properties": { + "id": { + "type": "integer" + } + }, + "required": [ + "id" + ] + }, + "order_element_collection": { + "type": "array", + "items": { + "$ref": "#/definitions/order_element_id" + } + } + }, + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/order" + } + } + }, + "required": ["data"] +} diff --git a/templates/web/admin/search-engine/elastic-search.html.twig b/templates/web/admin/search-engine/elastic-search.html.twig index 3f347614d4..d4fd99361f 100644 --- a/templates/web/admin/search-engine/elastic-search.html.twig +++ b/templates/web/admin/search-engine/elastic-search.html.twig @@ -1,3 +1,22 @@

{{ 'ElasticSearch configuration' | trans }}

{{ form(form) }} + +
+
+
+
+ +
Really drop index ?
+ +{% block javascript %} + + +{% endblock %} diff --git a/templates/web/admin/search-engine/phrasea.html.twig b/templates/web/admin/search-engine/phrasea.html.twig deleted file mode 100644 index 88871479d2..0000000000 --- a/templates/web/admin/search-engine/phrasea.html.twig +++ /dev/null @@ -1,21 +0,0 @@ - - -
-
- {{ 'Stemming' | trans }} - {{ 'Enable stemming' | trans }}  -
-
- {{ 'Default sort' | trans }} - -
-
- -
-
diff --git a/templates/web/admin/search-engine/sphinx-search.html.twig b/templates/web/admin/search-engine/sphinx-search.html.twig deleted file mode 100644 index 5ae9b85257..0000000000 --- a/templates/web/admin/search-engine/sphinx-search.html.twig +++ /dev/null @@ -1,26 +0,0 @@ -

{{ 'SphinxSearch search-engine configuration' | trans }}

- -
-
{{ 'Sphinx Search connection configuration' | trans }}
- -
{{ 'Sphinx Search server' | trans }}
- - -
{{ 'Sphinx Search RealTime server' | trans }}
- - - -
{{ 'Charset to use for indexation' | trans }}
- {% for charset, charsetObject in charsets %} - {{ charsetObject.get_name() }} - {% endfor %} -

-
{{ 'Date fields available for search' | trans }}
- {% for field in date_fields %} - {{ field }} - {% endfor %} - - -
- - \ No newline at end of file diff --git a/templates/web/common/macros.html.twig b/templates/web/common/macros.html.twig index cb5824f8f3..b3388b8e6c 100644 --- a/templates/web/common/macros.html.twig +++ b/templates/web/common/macros.html.twig @@ -103,7 +103,7 @@ {% endmacro %} {% macro format_caption(record, highlight, search_engine, include_business, bounceable, technical_data) %} - {% for field in record.get_caption().get_highlight_fields(highlight, null, search_engine, include_business) %} + {% for field in record.get_caption().get_highlight_fields(highlight, null, include_business) %} {% set extra_classes = ['pair'] %} {% if loop.index is odd %} {% set extra_classes = ['impair'] %} diff --git a/tests/Alchemy/Tests/Phrasea/Application/OverviewTest.php b/tests/Alchemy/Tests/Phrasea/Application/OverviewTest.php index cbae2afcff..5e2d3ca11a 100644 --- a/tests/Alchemy/Tests/Phrasea/Application/OverviewTest.php +++ b/tests/Alchemy/Tests/Phrasea/Application/OverviewTest.php @@ -34,21 +34,23 @@ class OverviewTest extends \PhraseanetAuthenticatedWebTestCase ->method('get') ->will($this->returnValue($acl)); - self::$DI['app']['acl'] = $aclProvider; + $app = $this->getApplication(); + $app['acl'] = $aclProvider; - $path = self::$DI['app']['url_generator']->generate('datafile', [ - 'sbas_id' => self::$DI['record_1']->get_sbas_id(), - 'record_id' => self::$DI['record_1']->get_record_id(), + $record1 = $this->getRecord1(); + + $path = $app['url_generator']->generate('datafile', [ + 'sbas_id' => $record1->getDataboxId(), + 'record_id' => $record1->getRecordId(), 'subdef' => $subdef, ]); - self::$DI['client']->request('GET', $path); - $response = self::$DI['client']->getResponse(); + $response = $this->request('GET', $path); $this->assertEquals(200, $response->getStatusCode()); $this->assertEquals('inline', explode(';', $response->headers->get('content-disposition'))[0]); - $this->assertEquals(self::$DI['record_1']->get_preview()->get_mime(), $response->headers->get('content-type')); - $this->assertEquals(self::$DI['record_1']->get_preview()->get_size(), $response->headers->get('content-length')); + $this->assertEquals($record1->get_preview()->get_mime(), $response->headers->get('content-type')); + $this->assertEquals($record1->get_preview()->get_size(), $response->headers->get('content-length')); } public function testDatafilesNonExistentSubdef() @@ -255,8 +257,8 @@ class OverviewTest extends \PhraseanetAuthenticatedWebTestCase self::$DI['app']['subdef.substituer']->substitute($story, $name, $media); $path = self::$DI['app']['url_generator']->generate('datafile', [ - 'sbas_id' => $story->get_sbas_id(), - 'record_id' => $story->get_record_id(), + 'sbas_id' => $story->getDataboxId(), + 'record_id' => $story->getRecordId(), 'subdef' => $name, ]); @@ -334,13 +336,13 @@ class OverviewTest extends \PhraseanetAuthenticatedWebTestCase // Ensure permalink is created \media_Permalink_Adapter::getPermalink( self::$DI['app'], - $record->get_databox(), + $record->getDatabox(), $record->get_subdef('preview') ); $path = self::$DI['app']['url_generator']->generate('permalinks_permaview', [ - 'sbas_id' => $record->get_sbas_id(), - 'record_id' => $record->get_record_id(), + 'sbas_id' => $record->getDataboxId(), + 'record_id' => $record->getRecordId(), 'subdef' => 'preview', ]); diff --git a/tests/Alchemy/Tests/Phrasea/Authentication/Provider/LinkedinTest.php b/tests/Alchemy/Tests/Phrasea/Authentication/Provider/LinkedinTest.php index 3cdf7b9417..920e80ffbf 100644 --- a/tests/Alchemy/Tests/Phrasea/Authentication/Provider/LinkedinTest.php +++ b/tests/Alchemy/Tests/Phrasea/Authentication/Provider/LinkedinTest.php @@ -16,12 +16,24 @@ class LinkedinTest extends ProviderTestCase { $state = md5(mt_rand()); + // test cases + $data = []; + + $data[] = [$this->getProvider(), $this->getRequestMock()]; + + // Second test $request = $this->getRequestMock(); $this->addQueryParameter($request, ['state' => $state]); - $provider1 = $this->getProvider(); - $provider1->setGuzzleClient($this->getGuzzleMock(401)); - $provider1->getSession()->set('linkedin.provider.state', $state); + $provider = $this->getProvider(); + $provider->setGuzzleClient($this->getGuzzleMock(401)); + $provider->getSession()->set('linkedin.provider.state', $state); + + $data[] = [$provider, $request]; + + // Third test + $request = $this->getRequestMock(); + $this->addQueryParameter($request, ['state' => $state]); $mock = $this->getMock('Guzzle\Http\ClientInterface'); @@ -67,15 +79,13 @@ class LinkedinTest extends ProviderTestCase ->method('post') ->will($this->returnValue($requestPost)); - $provider2 = $this->getProvider(); - $provider2->setGuzzleClient($mock); - $provider2->getSession()->set('linkedin.provider.state', $state); + $provider = $this->getProvider(); + $provider->setGuzzleClient($mock); + $provider->getSession()->set('linkedin.provider.state', $state); - return [ - [$this->getProvider(), $this->getRequestMock()], - [$provider1, $request], - [$provider2, $request], - ]; + $data[] = [$provider, $request]; + + return $data; } public function getProviderForLogout() diff --git a/tests/Alchemy/Tests/Phrasea/Border/ManagerTest.php b/tests/Alchemy/Tests/Phrasea/Border/ManagerTest.php index e893004740..5dedd2bc7a 100644 --- a/tests/Alchemy/Tests/Phrasea/Border/ManagerTest.php +++ b/tests/Alchemy/Tests/Phrasea/Border/ManagerTest.php @@ -13,6 +13,7 @@ use Alchemy\Phrasea\Border\Attribute\MetaField; use Alchemy\Phrasea\Border\Attribute\Metadata; use Alchemy\Phrasea\Border\Attribute\Status; use Alchemy\Phrasea\Border\Attribute\Story; +use Alchemy\Phrasea\Model\Entities\LazaretFile; /** * @group functional @@ -144,13 +145,15 @@ class ManagerTest extends \PhraseanetAuthenticatedWebTestCase $records = []; $postProcessRecord = function ($record) use (&$records) { - $records[] = $record; - }; + $records[] = $record; + }; - $file = File::buildFromPathfile(self::$file1, self::$DI['collection'], self::$DI['app']); + $app = $this->getApplication(); + $collection = $this->getCollection(); + $file = File::buildFromPathfile(self::$file1, $collection, $app); $first = $odd = false; $tofetch = []; - foreach (self::$DI['collection']->get_databox()->get_meta_structure() as $databox_field) { + foreach ($collection->get_databox()->get_meta_structure() as $databox_field) { if ($databox_field->is_readonly()) { continue; } @@ -196,7 +199,7 @@ class ManagerTest extends \PhraseanetAuthenticatedWebTestCase $odd = !$odd; } - $story = \record_adapter::createStory(self::$DI['app'], self::$DI['collection']); + $story = \record_adapter::createStory($app, $collection); $file->addAttribute(new Story($story)); $status = ''; @@ -208,17 +211,19 @@ class ManagerTest extends \PhraseanetAuthenticatedWebTestCase } } - $file->addAttribute(new Status(self::$DI['app'], strrev($status))); + $file->addAttribute(new Status($app, strrev($status))); - self::$DI['app']['phraseanet.SE'] = $this->createSearchEngineMock(); + $app['phraseanet.SE'] = $this->createSearchEngineMock(); $this->assertEquals(Manager::RECORD_CREATED, $this->object->process($this->session, $file, $postProcessRecord, Manager::FORCE_RECORD)); + /** @var \record_adapter $record */ $record = current($records); + $this->assertInstanceOf(\record_adapter::class, $record); $found = false; foreach ($record->get_grouping_parents()->get_elements() as $parent_story) { - if ($parent_story->get_serialize_key() === $story->get_serialize_key()) { + if ($parent_story->getId() === $story->getId()) { $found = true; } } @@ -227,18 +232,19 @@ class ManagerTest extends \PhraseanetAuthenticatedWebTestCase $this->fail('Unable to find story in parents'); } - $status = strrev($record->get_status()); + $status = strrev($record->getStatus()); $this->assertEquals(32, strlen($status)); $this->assertEquals('1', substr($status, 4, 1)); $this->assertEquals('1', substr($status, 8, 1)); foreach ($tofetch as $name => $values) { - $found = []; + foreach ($record->get_caption()->get_field($name)->get_values() as $value) { $found[] = $value->getValue(); } + $this->assertEquals($values, $found); } @@ -261,10 +267,12 @@ class ManagerTest extends \PhraseanetAuthenticatedWebTestCase $lazaret = $element; }; - $file = File::buildFromPathfile(self::$file1, self::$DI['collection'], self::$DI['app']); + $app = $this->getApplication(); + $collection = $this->getCollection(); + $file = File::buildFromPathfile(self::$file1, $collection, $app); $odd = false; $tofetchMeta = $tofetchField = []; - foreach (self::$DI['collection']->get_databox()->get_meta_structure() as $databox_field) { + foreach ($collection->get_databox()->get_meta_structure() as $databox_field) { if ($databox_field->is_readonly()) { continue; } @@ -300,14 +308,16 @@ class ManagerTest extends \PhraseanetAuthenticatedWebTestCase $odd = !$odd; } - $file->addAttribute(new Story(self::$DI['record_story_1'])); + $story = $this->getRecordStory1(); + $file->addAttribute(new Story($story)); $status = '1'; + foreach (range(1, 31) as $i) { $status .= '0'; } - $file->addAttribute(new Status(self::$DI['app'], $status)); + $file->addAttribute(new Status($app, $status)); $this->assertEquals(Manager::LAZARET_CREATED, $this->object->process($this->session, $file, $postProcessRecord, Manager::FORCE_LAZARET)); @@ -315,12 +325,13 @@ class ManagerTest extends \PhraseanetAuthenticatedWebTestCase $foundMeta = $foundField = []; - /* @var $lazaret \Alchemy\Phrasea\Model\Entities\LazaretFile */ + /** @var LazaretFile $lazaret */ foreach ($lazaret->getAttributes() as $attr) { - $attribute = Factory::getFileAttribute(self::$DI['app'], $attr->getName(), $attr->getValue()); + $attribute = Factory::getFileAttribute($app, $attr->getName(), $attr->getValue()); if ($attribute->getName() == AttributeInterface::NAME_STORY) { - if ($attribute->getValue()->get_serialize_key() == self::$DI['record_story_1']->get_serialize_key()) { + /** @var Story $attribute */ + if ($attribute->getValue()->getId() == $story->getId()) { $story_found = true; } } elseif ($attribute->getName() == AttributeInterface::NAME_METADATA) { diff --git a/tests/Alchemy/Tests/Phrasea/Controller/Admin/FieldsTest.php b/tests/Alchemy/Tests/Phrasea/Controller/Admin/FieldsTest.php index 78b4cbcf39..8e10b1842c 100644 --- a/tests/Alchemy/Tests/Phrasea/Controller/Admin/FieldsTest.php +++ b/tests/Alchemy/Tests/Phrasea/Controller/Admin/FieldsTest.php @@ -3,8 +3,6 @@ namespace Alchemy\Tests\Phrasea\Controller\Admin; use PHPExiftool\Driver\Tag\IPTC\ObjectName; -use Alchemy\Phrasea\Vocabulary\Controller as VocabularyController; -use Symfony\Component\HttpKernel\Client; /** * @group functional @@ -16,18 +14,17 @@ class FieldsTest extends \PhraseanetAuthenticatedWebTestCase { public function testRoot() { - $databoxes = self::$DI['app']->getDataboxes(); + $databoxes = $this->getApplication()->getDataboxes(); $databox = array_shift($databoxes); - self::$DI['client']->request("GET", "/admin/fields/" . $databox->get_sbas_id()); + $response = $this->request("GET", "/admin/fields/" . $databox->get_sbas_id()); - $this->assertTrue(self::$DI['client']->getResponse()->isOk()); + $this->assertTrue($response->isOk()); } public function testLanguage() { - self::$DI['client']->request("GET", "/admin/fields/language.json"); - $response = self::$DI['client']->getResponse(); + $response = $this->request("GET", "/admin/fields/language.json"); $this->assertTrue($response->isOk()); $this->assertEquals("application/json", $response->headers->get("content-type")); @@ -37,9 +34,7 @@ class FieldsTest extends \PhraseanetAuthenticatedWebTestCase { $tag = new ObjectName(); - self::$DI['client']->request("GET", "/admin/fields/tags/".$tag->getTagname()); - - $response = self::$DI['client']->getResponse(); + $response = $this->request("GET", "/admin/fields/tags/".$tag->getTagname()); $this->assertEquals("application/json", $response->headers->get("content-type")); $data = json_decode($response->getContent(), true); @@ -52,13 +47,11 @@ class FieldsTest extends \PhraseanetAuthenticatedWebTestCase public function testListDcFields() { - self::$DI['client']->request("GET", "/admin/fields/dc-fields"); + $response = $this->request("GET", "/admin/fields/dc-fields"); - $response = self::$DI['client']->getResponse()->getContent(); + $this->assertEquals("application/json", $response->headers->get("content-type")); - $this->assertEquals("application/json", self::$DI['client']->getResponse()->headers->get("content-type")); - - $data = json_decode($response, true); + $data = json_decode($response->getContent(), true); $this->assertInternalType('array', $data); foreach ($data as $dc) { @@ -72,49 +65,40 @@ class FieldsTest extends \PhraseanetAuthenticatedWebTestCase public function testListVocabularies() { - self::$DI['client']->request("GET", "/admin/fields/vocabularies"); + $response = $this->request("GET", "/admin/fields/vocabularies"); - $response = self::$DI['client']->getResponse()->getContent(); - $this->assertEquals("application/json", self::$DI['client']->getResponse()->headers->get("content-type")); + $this->assertEquals("application/json", $response->headers->get("content-type")); - $data = json_decode($response, true); + $data = json_decode($response->getContent(), true); $this->assertInternalType('array', $data); foreach ($data as $vocabulary) { $this->assertArrayHasKey('type', $vocabulary); $this->assertArrayHasKey('name', $vocabulary); - - $voc = VocabularyController::get(self::$DI['app'], $vocabulary['type']); - $this->assertInstanceOf('Alchemy\Phrasea\Vocabulary\ControlProvider\ControlProviderInterface', $voc); } } public function testGetVocabulary() { - self::$DI['client']->request("GET", "/admin/fields/vocabularies/user"); + $response = $this->request("GET", "/admin/fields/vocabularies/user"); - $response = self::$DI['client']->getResponse()->getContent(); - $this->assertEquals("application/json", self::$DI['client']->getResponse()->headers->get("content-type")); + $this->assertEquals("application/json", $response->headers->get("content-type")); - $data = json_decode($response, true); + $data = json_decode($response->getContent(), true); $this->assertArrayHasKey('type', $data); $this->assertEquals('User', $data['type']); $this->assertArrayHasKey('name', $data); - - $voc = VocabularyController::get(self::$DI['app'], $data['type']); - $this->assertInstanceOf('Alchemy\Phrasea\Vocabulary\ControlProvider\UserProvider', $voc); } public function testSearchTag() { - self::$DI['client']->request("GET", "/admin/fields/tags/search?term=xmp-exif"); + $response = $this->request("GET", "/admin/fields/tags/search?term=xmp-exif"); - $response = self::$DI['client']->getResponse()->getContent(); - $this->assertEquals("application/json", self::$DI['client']->getResponse()->headers->get("content-type")); + $this->assertEquals("application/json", $response->headers->get("content-type")); - $data = json_decode($response, true); + $data = json_decode($response->getContent(), true); $this->assertGreaterThan(90, count($data)); @@ -128,7 +112,7 @@ class FieldsTest extends \PhraseanetAuthenticatedWebTestCase public function testUpdateFields() { - $databoxes = self::$DI['app']->getDataboxes(); + $databoxes = $this->getApplication()->getDataboxes(); $databox = array_shift($databoxes); $fieldObjects = []; // create two fields @@ -170,7 +154,7 @@ class FieldsTest extends \PhraseanetAuthenticatedWebTestCase ]]; foreach ($fields as $fieldData) { - $field = \databox_field::create(self::$DI['app'], $databox, $fieldData['name'], $fieldData['multi']); + $field = \databox_field::create($this->getApplication(), $databox, $fieldData['name'], $fieldData['multi']); $field ->set_thumbtitle($fieldData['thumbtitle']) ->set_tag(\databox_field::loadClassFromTagName($fieldData['tag'])) @@ -197,13 +181,11 @@ class FieldsTest extends \PhraseanetAuthenticatedWebTestCase $body[count($body) - 1]['readonly'] = true; $body[count($body) - 1]['required'] = false; - self::$DI['client']->request("PUT", sprintf("/admin/fields/%d/fields", $databox->get_sbas_id()), [], [], [], json_encode($body)); + $response = $this->request("PUT", sprintf("/admin/fields/%d/fields", $databox->get_sbas_id()), [], [], json_encode($body)); - $response = self::$DI['client']->getResponse()->getContent(); + $this->assertEquals("application/json", $response->headers->get("content-type")); - $this->assertEquals("application/json", self::$DI['client']->getResponse()->headers->get("content-type")); - - $data = json_decode($response, true); + $data = json_decode($response->getContent(), true); $this->assertTrue(is_array($data)); @@ -248,14 +230,11 @@ class FieldsTest extends \PhraseanetAuthenticatedWebTestCase 'vocabulary-restricted' => true, ]); - /** @var Client $client */ - $client = self::$DI['client']; - $client->request("POST", sprintf("/admin/fields/%d/fields", $databox->get_sbas_id()), [], [], [], $body); + $response = $this->request("POST", sprintf("/admin/fields/%d/fields", $databox->get_sbas_id()), [], [], $body); - $response = $client->getResponse()->getContent(); - $this->assertEquals("application/json", $client->getResponse()->headers->get("content-type")); + $this->assertEquals("application/json", $response->headers->get("content-type")); - $data = json_decode($response, true); + $data = json_decode($response->getContent(), true); $this->assertTrue(is_array($data)); @@ -271,15 +250,14 @@ class FieldsTest extends \PhraseanetAuthenticatedWebTestCase public function testListField() { - $databoxes = self::$DI['app']->getDataboxes(); + $databoxes = $this->getApplication()->getDataboxes(); $databox = array_shift($databoxes); - self::$DI['client']->request("GET", sprintf("/admin/fields/%d/fields", $databox->get_sbas_id())); + $response = $this->request("GET", sprintf("/admin/fields/%d/fields", $databox->get_sbas_id())); - $response = self::$DI['client']->getResponse()->getContent(); - $this->assertEquals("application/json", self::$DI['client']->getResponse()->headers->get("content-type")); + $this->assertEquals("application/json", $response->headers->get("content-type")); - $data = json_decode($response, true); + $data = json_decode($response->getContent(), true); $this->assertInternalType('array', $data); @@ -297,13 +275,11 @@ class FieldsTest extends \PhraseanetAuthenticatedWebTestCase $data = $field->toArray(); - $client = $this->getClient(); - $client->request("GET", sprintf("/admin/fields/%d/fields/%d", $databox->get_sbas_id(), $field->get_id())); + $response = $this->request("GET", sprintf("/admin/fields/%d/fields/%d", $databox->get_sbas_id(), $field->get_id())); - $response = $client->getResponse()->getContent(); - $this->assertEquals("application/json", $client->getResponse()->headers->get("content-type")); + $this->assertEquals("application/json", $response->headers->get("content-type")); - $this->assertEquals($data, json_decode($response, true)); + $this->assertEquals($data, json_decode($response->getContent(), true)); $field->delete(); } @@ -320,11 +296,9 @@ class FieldsTest extends \PhraseanetAuthenticatedWebTestCase $data['business'] = true; $data['vocabulary-type'] = 'User'; - $client = $this->getClient(); - $client->request("PUT", sprintf("/admin/fields/%d/fields/%d", $databox->get_sbas_id(), $field->get_id()), [], [], [], json_encode($data)); + $response = $this->request("PUT", sprintf("/admin/fields/%d/fields/%d", $databox->get_sbas_id(), $field->get_id()), [], [], json_encode($data)); - $response = $client->getResponse()->getContent(); - $this->assertEquals($data, json_decode($response, true)); + $this->assertEquals($data, json_decode($response->getContent(), true)); $field->delete(); } @@ -342,13 +316,10 @@ class FieldsTest extends \PhraseanetAuthenticatedWebTestCase $data['business'] = true; $data['vocabulary-type'] = 'User'; - /** @var Client $client */ - $client = self::$DI['client']; - $client->request("DELETE", sprintf("/admin/fields/%d/fields/%d", $databox->get_sbas_id(), $field->get_id()), [], [], [], json_encode($data)); + $response = $this->request("DELETE", sprintf("/admin/fields/%d/fields/%d", $databox->get_sbas_id(), $field->get_id()), [], [], json_encode($data)); - $response = $client->getResponse()->getContent(); - $this->assertEquals('', $response); - $this->assertEquals(204, $client->getResponse()->getStatusCode()); + $this->assertEquals('', $response->getContent()); + $this->assertEquals(204, $response->getStatusCode()); try { $databox->get_meta_structure()->get_element($fieldId); diff --git a/tests/Alchemy/Tests/Phrasea/Controller/Api/ApiJsonTest.php b/tests/Alchemy/Tests/Phrasea/Controller/Api/ApiJsonTest.php index f3ca34b2e2..6945e4c151 100644 --- a/tests/Alchemy/Tests/Phrasea/Controller/Api/ApiJsonTest.php +++ b/tests/Alchemy/Tests/Phrasea/Controller/Api/ApiJsonTest.php @@ -60,8 +60,8 @@ class ApiJsonTest extends ApiTestCase $record = \record_adapter::createFromFile($file, $app); $story['story_records'] = array(array( - 'databox_id' => $record->get_sbas_id(), - 'record_id' => $record->get_record_id() + 'databox_id' => $record->getDataboxId(), + 'record_id' => $record->getRecordId() )); $client = $this->getClient(); @@ -96,7 +96,7 @@ class ApiJsonTest extends ApiTestCase $this->setToken($this->userAccessToken); $story = \record_adapter::createStory(self::$DI['app'], self::$DI['collection']); - $route = sprintf('/api/v1/stories/%s/%s/addrecords', $story->get_sbas_id(), $story->get_record_id()); + $route = sprintf('/api/v1/stories/%s/%s/addrecords', $story->getDataboxId(), $story->getRecordId()); $file = new File( self::$DI['app'], @@ -106,8 +106,8 @@ class ApiJsonTest extends ApiTestCase $record = \record_adapter::createFromFile($file, self::$DI['app']); $records = array( - 'databox_id' => $record->get_sbas_id(), - 'record_id' => $record->get_record_id() + 'databox_id' => $record->getDataboxId(), + 'record_id' => $record->getRecordId() ); self::$DI['client']->request( @@ -549,7 +549,7 @@ class ApiJsonTest extends ApiTestCase $record_1 = $this->getRecord1(); $client = $this->getClient(); - $route = '/api/v1/records/' . $record_1->get_sbas_id() . '/' . $record_1->get_record_id() . '/'; + $route = '/api/v1/records/' . $record_1->getDataboxId() . '/' . $record_1->getRecordId() . '/'; $this->evaluateMethodNotAllowedRoute($route, ['POST', 'PUT', 'DELETE']); $client->request('GET', $route, $this->getParameters(), [], ['HTTP_Accept' => $this->getAcceptMimeType()]); $content = $this->unserialize($client->getResponse()->getContent()); @@ -903,11 +903,14 @@ class ApiJsonTest extends ApiTestCase ->method('getSuggestions') ->will($this->returnValue(new ArrayCollection())); - self::$DI['app']['phraseanet.SE'] = $this->getMock('Alchemy\Phrasea\SearchEngine\SearchEngineInterface'); + $app = $this->getApplication(); + $mock = $this->getMock('Alchemy\Phrasea\SearchEngine\SearchEngineInterface'); + $app['phraseanet.SE'] = $mock; - self::$DI['app']['phraseanet.SE']->expects($this->once()) + $mock + ->expects($this->once()) ->method('query') - ->with('koala', 0, 10) + ->withAnyParameters() ->will($this->returnValue( $this->getMockBuilder('Alchemy\Phrasea\SearchEngine\SearchEngineResult') ->disableOriginalConstructor() @@ -1012,7 +1015,7 @@ class ApiJsonTest extends ApiTestCase /** @var \record_adapter $record_1 */ $record_1 = self::$DI['record_1']; - $route = '/api/v1/records/' . $record_1->get_sbas_id() . '/' . $record_1->get_record_id() . '/embed/'; + $route = '/api/v1/records/' . $record_1->getDataboxId() . '/' . $record_1->getRecordId() . '/embed/'; $this->evaluateMethodNotAllowedRoute($route, ['POST', 'PUT', 'DELETE']); /** @var Client $client */ @@ -1099,7 +1102,7 @@ class ApiJsonTest extends ApiTestCase /** @var \record_adapter $story */ $story = self::$DI['record_story_1']; - $route = '/api/v1/stories/' . $story->get_sbas_id() . '/' . $story->get_record_id() . '/embed/'; + $route = '/api/v1/stories/' . $story->getDataboxId() . '/' . $story->getRecordId() . '/embed/'; $this->evaluateMethodNotAllowedRoute($route, ['POST', 'PUT', 'DELETE']); /** @var Client $client */ @@ -1234,13 +1237,15 @@ class ApiJsonTest extends ApiTestCase public function testRecordsSetStatus() { - self::$DI['app']['phraseanet.SE'] = $this->createSearchEngineMock(); + $app = $this->getApplication(); + $app['phraseanet.SE'] = $this->createSearchEngineMock(); $this->setToken($this->userAccessToken); - $route = '/api/v1/records/' . self::$DI['record_1']->get_sbas_id() . '/' . self::$DI['record_1']->get_record_id() . '/setstatus/'; + $record1 = $this->getRecord1(); + $route = '/api/v1/records/' . $record1->getDataboxId() . '/' . $record1->getRecordId() . '/setstatus/'; - $record_status = strrev(self::$DI['record_1']->get_status()); - $statusStructure = self::$DI['record_1']->getStatusStructure(); + $record_status = strrev($record1->getStatus()); + $statusStructure = $record1->getStatusStructure(); $tochange = []; foreach ($statusStructure as $n => $datas) { @@ -1248,20 +1253,18 @@ class ApiJsonTest extends ApiTestCase } $this->evaluateMethodNotAllowedRoute($route, ['GET', 'PUT', 'DELETE']); - self::$DI['client']->request('POST', $route, $this->getParameters(['status' => $tochange]), [], ['HTTP_Accept' => $this->getAcceptMimeType()]); - $content = $this->unserialize(self::$DI['client']->getResponse()->getContent()); + $response = $this->request('POST', $route, $this->getParameters(['status' => $tochange]), ['HTTP_Accept' => $this->getAcceptMimeType()]); + $content = $this->unserialize($response->getContent()); - /** - * Get fresh record_1 - */ - $testRecord = new \record_adapter(self::$DI['app'], self::$DI['record_1']->get_sbas_id(), self::$DI['record_1']->get_record_id()); + // Get fresh record_1 + $testRecord = new \record_adapter($app, $record1->getDataboxId(), $record1->getRecordId()); - $this->evaluateResponse200(self::$DI['client']->getResponse()); + $this->evaluateResponse200($response); $this->evaluateMeta200($content); $this->evaluateRecordsStatusResponse($testRecord, $content); - $record_status = strrev($testRecord->get_status()); + $record_status = strrev($testRecord->getStatus()); foreach ($statusStructure as $n => $datas) { $this->assertEquals(substr($record_status, ($n), 1), $tochange[$n]); } @@ -1270,25 +1273,23 @@ class ApiJsonTest extends ApiTestCase $tochange[$n] = $value == '0' ? '1' : '0'; } - self::$DI['client']->request('POST', $route, $this->getParameters(['status' => $tochange]), [], ['HTTP_Accept' => $this->getAcceptMimeType()]); - $content = $this->unserialize(self::$DI['client']->getResponse()->getContent()); + $response = $this->request('POST', $route, $this->getParameters(['status' => $tochange]), ['HTTP_Accept' => $this->getAcceptMimeType()]); + $content = $this->unserialize($response->getContent()); - /** - * Get fresh record_1 - */ - $testRecord = new \record_adapter(self::$DI['app'], $testRecord->get_sbas_id(), $testRecord->get_record_id()); + // Get fresh record_1 + $testRecord = new \record_adapter($app, $testRecord->getDataboxId(), $testRecord->getRecordId()); - $this->evaluateResponse200(self::$DI['client']->getResponse()); + $this->evaluateResponse200($response); $this->evaluateMeta200($content); $this->evaluateRecordsStatusResponse($testRecord, $content); - $record_status = strrev($testRecord->get_status()); + $record_status = strrev($testRecord->getStatus()); foreach ($statusStructure as $n => $datas) { $this->assertEquals(substr($record_status, ($n), 1), $tochange[$n]); } - self::$DI['record_1']->set_binary_status(str_repeat('0', 32)); + $record1->setStatus(str_repeat('0', 32)); } public function testMoveRecordToCollection() @@ -1299,11 +1300,11 @@ class ApiJsonTest extends ApiTestCase $this->setToken($this->userAccessToken); - $route = '/api/v1/records/' . $record->get_sbas_id() . '/' . $record->get_record_id() . '/setcollection/'; + $route = '/api/v1/records/' . $record->getDataboxId() . '/' . $record->getRecordId() . '/setcollection/'; $base_id = false; - foreach ($record->get_databox()->get_collections() as $collection) { - if ($collection->get_base_id() != $record->get_base_id()) { + foreach ($record->getDatabox()->get_collections() as $collection) { + if ($collection->get_base_id() != $record->getBaseId()) { $base_id = $collection->get_base_id(); break; } diff --git a/tests/Alchemy/Tests/Phrasea/Controller/Api/ApiTestCase.php b/tests/Alchemy/Tests/Phrasea/Controller/Api/ApiTestCase.php index ea90eefb66..054f779076 100644 --- a/tests/Alchemy/Tests/Phrasea/Controller/Api/ApiTestCase.php +++ b/tests/Alchemy/Tests/Phrasea/Controller/Api/ApiTestCase.php @@ -694,9 +694,9 @@ abstract class ApiTestCase extends \PhraseanetWebTestCase protected function evaluateRecordsStatusResponse(\record_adapter $record, $content) { - $statusStructure = $record->get_databox()->getStatusStructure(); + $statusStructure = $record->getDatabox()->getStatusStructure(); - $r_status = strrev($record->get_status()); + $r_status = strrev($record->getStatus()); $this->assertArrayHasKey('status', $content['response']); $this->assertEquals(count((array) $content['response']['status']), count($statusStructure->toArray())); foreach ($content['response']['status'] as $status) { @@ -714,7 +714,7 @@ abstract class ApiTestCase extends \PhraseanetWebTestCase protected function injectMetadatas(\record_adapter $record) { - foreach ($record->get_databox()->get_meta_structure()->get_elements() as $field) { + foreach ($record->getDatabox()->get_meta_structure()->get_elements() as $field) { try { $values = $record->get_caption()->get_field($field->get_name())->get_values(); $value = array_pop($values); diff --git a/tests/Alchemy/Tests/Phrasea/Controller/Prod/DownloadTest.php b/tests/Alchemy/Tests/Phrasea/Controller/Prod/DownloadTest.php index 670e0d9a3d..a3909c95e9 100644 --- a/tests/Alchemy/Tests/Phrasea/Controller/Prod/DownloadTest.php +++ b/tests/Alchemy/Tests/Phrasea/Controller/Prod/DownloadTest.php @@ -57,7 +57,7 @@ class DownloadTest extends \PhraseanetAuthenticatedWebTestCase //has_right_on_base return true $stubbedACL->expects($this->any()) - ->method('has_right_on_bas') + ->method('has_right_on_base') ->will($this->returnValue(false)); //has_access_to_subdef return true diff --git a/tests/Alchemy/Tests/Phrasea/Controller/Prod/ExportTest.php b/tests/Alchemy/Tests/Phrasea/Controller/Prod/ExportTest.php index b7a42e6448..da278639c2 100644 --- a/tests/Alchemy/Tests/Phrasea/Controller/Prod/ExportTest.php +++ b/tests/Alchemy/Tests/Phrasea/Controller/Prod/ExportTest.php @@ -142,7 +142,7 @@ class ExportTest extends \PhraseanetAuthenticatedWebTestCase //inserted rows from this function are deleted in tearDownAfterClass $this->getClient()->request('POST', '/prod/export/ftp/', [ - 'lst' => $this->getRecord1()->get_serialize_key(), + 'lst' => $this->getRecord1()->getId(), 'user_dest' => $user->getId(), 'address' => 'local.phrasea.test', 'login' => $user->getEmail(), @@ -168,7 +168,7 @@ class ExportTest extends \PhraseanetAuthenticatedWebTestCase $this->mockNotificationDeliverer('Alchemy\Phrasea\Notification\Mail\MailRecordsExport'); $this->getClient()->request('POST', '/prod/export/mail/', [ - 'lst' => $this->getRecord1()->get_serialize_key(), + 'lst' => $this->getRecord1()->getId(), 'destmail' => 'user@example.com', 'obj' => ['preview'], ]); diff --git a/tests/Alchemy/Tests/Phrasea/Controller/Prod/LazaretTest.php b/tests/Alchemy/Tests/Phrasea/Controller/Prod/LazaretTest.php index 38d0d6f05f..82c916e197 100644 --- a/tests/Alchemy/Tests/Phrasea/Controller/Prod/LazaretTest.php +++ b/tests/Alchemy/Tests/Phrasea/Controller/Prod/LazaretTest.php @@ -142,7 +142,7 @@ class LazaretTest extends \PhraseanetAuthenticatedWebTestCase //Provide some valid test values $lazaretAttribute->expects($this->exactly(4)) ->method('getValue') - ->will($this->onConsecutiveCalls('metadataValue', $story->get_serialize_key(), '00001111', 'metafieldValue')); + ->will($this->onConsecutiveCalls('metadataValue', $story->getId(), '00001111', 'metafieldValue')); //Add the 5 attribute $lazaretFile->addAttribute($lazaretAttribute); diff --git a/tests/Alchemy/Tests/Phrasea/Controller/Prod/OrderTest.php b/tests/Alchemy/Tests/Phrasea/Controller/Prod/OrderTest.php index 19c49119c3..558b599b88 100644 --- a/tests/Alchemy/Tests/Phrasea/Controller/Prod/OrderTest.php +++ b/tests/Alchemy/Tests/Phrasea/Controller/Prod/OrderTest.php @@ -29,7 +29,7 @@ class OrderTest extends \PhraseanetAuthenticatedWebTestCase }); $client = $this->getClient(); $client->request('POST', '/prod/order/', [ - 'lst' => $this->getRecord1()->get_serialize_key(), + 'lst' => $this->getRecord1()->getId(), 'deadline' => '+10 minutes' ]); @@ -54,7 +54,7 @@ class OrderTest extends \PhraseanetAuthenticatedWebTestCase }); $response = $this->XMLHTTPRequest('POST', '/prod/order/', [ - 'lst' => $this->getRecord1()->get_serialize_key(), + 'lst' => $this->getRecord1()->getId(), 'deadline' => '+10 minutes' ]); @@ -71,7 +71,7 @@ class OrderTest extends \PhraseanetAuthenticatedWebTestCase public function testDisplayOrders() { $this->XMLHTTPRequest('POST', '/prod/order/', [ - 'lst' => $this->getRecord1()->get_serialize_key(), + 'lst' => $this->getRecord1()->getId(), 'deadline' => '+10 minutes' ]); $response = $this->request('GET', '/prod/order/', [ diff --git a/tests/Alchemy/Tests/Phrasea/Controller/Prod/PropertyTest.php b/tests/Alchemy/Tests/Phrasea/Controller/Prod/PropertyTest.php index c0d2088b82..2389da527e 100644 --- a/tests/Alchemy/Tests/Phrasea/Controller/Prod/PropertyTest.php +++ b/tests/Alchemy/Tests/Phrasea/Controller/Prod/PropertyTest.php @@ -102,12 +102,12 @@ class PropertyTest extends \PhraseanetAuthenticatedWebTestCase self::$DI['app']['acl'] = $aclProvider; self::$DI['client']->request('POST', '/prod/records/property/status/', [ - 'apply_to_children' => [$story->get_sbas_id() => true], + 'apply_to_children' => [$story->getDataboxId() => true], 'status' => [ - $record->get_sbas_id() => [6 => true, 8 => true, 11 => true] + $record->getDataboxId() => [6 => true, 8 => true, 11 => true] ], 'lst' => implode(';', [ - $record->get_serialize_key(),$story->get_serialize_key() + $record->getId(),$story->getId() ]) ]); $response = self::$DI['client']->getResponse(); @@ -116,11 +116,11 @@ class PropertyTest extends \PhraseanetAuthenticatedWebTestCase $this->assertTrue($datas['success']); $this->assertArrayHasKey('updated', $datas); - $record = new \record_adapter(self::$DI['app'], $record->get_sbas_id(), $record->get_record_id()); - $story = new \record_adapter(self::$DI['app'], $story->get_sbas_id(), $story->get_record_id()); + $record = new \record_adapter(self::$DI['app'], $record->getDataboxId(), $record->getRecordId()); + $story = new \record_adapter(self::$DI['app'], $story->getDataboxId(), $story->getRecordId()); - $recordStatus = strrev($record->get_status()); - $storyStatus = strrev($story->get_status()); + $recordStatus = strrev($record->getStatus()); + $storyStatus = strrev($story->getStatus()); $this->assertEquals(1, substr($recordStatus, 6, 1)); $this->assertEquals(1, substr($recordStatus, 8, 1)); @@ -130,8 +130,8 @@ class PropertyTest extends \PhraseanetAuthenticatedWebTestCase $this->assertEquals(1, substr($storyStatus, 8, 1)); $this->assertEquals(1, substr($storyStatus, 11, 1)); - foreach ($story->get_children() as $child) { - $childStatus = strrev($child->get_status()); + foreach ($story->getChildren() as $child) { + $childStatus = strrev($child->getStatus()); $this->assertEquals(1, substr($childStatus, 6, 1)); $this->assertEquals(1, substr($childStatus, 8, 1)); $this->assertEquals(1, substr($childStatus, 11, 1)); @@ -155,11 +155,11 @@ class PropertyTest extends \PhraseanetAuthenticatedWebTestCase self::$DI['client']->request('POST', '/prod/records/property/type/', [ 'lst' => implode(';', [ - $record->get_serialize_key(), $record2->get_serialize_key() + $record->getId(), $record2->getId() ]), 'types' => [ - $record->get_serialize_key() => 'document', - $record2->get_serialize_key() => 'flash', + $record->getId() => 'document', + $record2->getId() => 'flash', ] ]); $response = self::$DI['client']->getResponse(); @@ -168,11 +168,11 @@ class PropertyTest extends \PhraseanetAuthenticatedWebTestCase $this->assertTrue($datas['success']); $this->assertArrayHasKey('updated', $datas); - $record = new \record_adapter(self::$DI['app'], $record->get_sbas_id(), $record->get_record_id()); - $record2 = new \record_adapter(self::$DI['app'], $record2->get_sbas_id(), $record2->get_record_id()); + $record = new \record_adapter(self::$DI['app'], $record->getDataboxId(), $record->getRecordId()); + $record2 = new \record_adapter(self::$DI['app'], $record2->getDataboxId(), $record2->getRecordId()); - $this->assertEquals('document', $record->get_type()); - $this->assertEquals('flash', $record2->get_type()); + $this->assertEquals('document', $record->getType()); + $this->assertEquals('flash', $record2->getType()); $record->delete(); $record2->delete(); diff --git a/tests/Alchemy/Tests/Phrasea/Controller/Prod/QueryTest.php b/tests/Alchemy/Tests/Phrasea/Controller/Prod/QueryTest.php index a89a2a2877..3cc103a18b 100644 --- a/tests/Alchemy/Tests/Phrasea/Controller/Prod/QueryTest.php +++ b/tests/Alchemy/Tests/Phrasea/Controller/Prod/QueryTest.php @@ -13,10 +13,6 @@ use Prophecy\Argument; */ class QueryTest extends \PhraseanetAuthenticatedWebTestCase { - - /** - * @covers Alchemy\Phrasea\Controller\Prod\Query::query - */ public function testQuery() { $route = '/prod/query/'; @@ -46,9 +42,6 @@ class QueryTest extends \PhraseanetAuthenticatedWebTestCase $this->assertInternalType('array', $data); } - /** - * @covers Alchemy\Phrasea\Controller\Prod\Query::queryAnswerTrain - */ public function testQueryAnswerTrain() { $app = $this->mockElasticsearchResult(self::$DI['record_2']); @@ -58,13 +51,11 @@ class QueryTest extends \PhraseanetAuthenticatedWebTestCase $options->onCollections($app->getAclForUser($app->getAuthenticatedUser())->get_granted_base()); $serializedOptions = $options->serialize(); - $client = $this->getClient(); - $client->request('POST', '/prod/query/answer-train/', [ + $response = $this->request('POST', '/prod/query/answer-train/', [ 'options_serial' => $serializedOptions, 'pos' => 0, 'query' => '' ]); - $response = $client->getResponse(); $this->assertTrue($response->isOk()); $datas = (array) json_decode($response->getContent()); $this->assertArrayHasKey('current', $datas); diff --git a/tests/Alchemy/Tests/Phrasea/Controller/Prod/RecordsTest.php b/tests/Alchemy/Tests/Phrasea/Controller/Prod/RecordsTest.php index e8bfa58d8f..2a94180c1f 100644 --- a/tests/Alchemy/Tests/Phrasea/Controller/Prod/RecordsTest.php +++ b/tests/Alchemy/Tests/Phrasea/Controller/Prod/RecordsTest.php @@ -18,9 +18,6 @@ class RecordsTest extends \PhraseanetAuthenticatedWebTestCase { protected $client; - /** - * @covers Alchemy\Phrasea\Controller\Prod\Records::whatCanIDelete - */ public function testWhatCanIDelete() { self::$DI['client']->request('POST', '/prod/records/delete/what/', ['lst' => self::$DI['record_1']->get_serialize_key()]); @@ -29,40 +26,31 @@ class RecordsTest extends \PhraseanetAuthenticatedWebTestCase unset($response); } - /** - * @covers Alchemy\Phrasea\Controller\Prod\Records::doDeleteRecords - */ public function testDoDeleteRecords() { $file = new File(self::$DI['app'], self::$DI['app']['mediavorus']->guess(__DIR__ . '/../../../../../files/cestlafete.jpg'), self::$DI['collection']); $record = \record_adapter::createFromFile($file, self::$DI['app']); - $response = $this->XMLHTTPRequest('POST', '/prod/records/delete/', ['lst' => $record->get_serialize_key()]); + $response = $this->XMLHTTPRequest('POST', '/prod/records/delete/', ['lst' => $record->getId()]); $datas = (array) json_decode($response->getContent()); - $this->assertContains($record->get_serialize_key(), $datas); + $this->assertContains($record->getId(), $datas); try { - new \record_adapter(self::$DI['app'], $record->get_sbas_id(), $record->get_record_id()); + new \record_adapter(self::$DI['app'], $record->getDataboxId(), $record->getRecordId()); $this->fail('Record not deleted'); } catch (\Exception $e) { } } - /** - * @covers Alchemy\Phrasea\Controller\Prod\Records::renewUrl - */ public function testRenewUrl() { $file = new File(self::$DI['app'], self::$DI['app']['mediavorus']->guess(__DIR__ . '/../../../../../files/cestlafete.jpg'), self::$DI['collection']); $record = \record_adapter::createFromFile($file, self::$DI['app']); - $response = $this->XMLHTTPRequest('POST', '/prod/records/renew-url/', ['lst' => $record->get_serialize_key()]); + $response = $this->XMLHTTPRequest('POST', '/prod/records/renew-url/', ['lst' => $record->getId()]); $datas = (array) json_decode($response->getContent()); $this->assertTrue(count($datas) > 0); $record->delete(); } - /** - * @covers Alchemy\Phrasea\Controller\Prod\Records::getRecord - */ public function testGetRecordDetailNotAjax() { self::$DI['client']->request('POST', '/prod/records/'); @@ -108,9 +96,6 @@ class RecordsTest extends \PhraseanetAuthenticatedWebTestCase $this->assertArrayHasKey('title', $data); } - /** - * @covers Alchemy\Phrasea\Controller\Prod\Records::getRecord - */ public function testGetRecordDetailResult() { $app = $this->mockElasticsearchResult(self::$DI['record_1']); @@ -141,9 +126,6 @@ class RecordsTest extends \PhraseanetAuthenticatedWebTestCase $this->assertArrayHasKey('title', $data); } - /** - * @covers Alchemy\Phrasea\Controller\Prod\Records::getRecord - */ public function testGetRecordDetailREG() { $this->authenticate(self::$DI['app']); @@ -168,9 +150,6 @@ class RecordsTest extends \PhraseanetAuthenticatedWebTestCase $this->assertObjectHasAttribute('title', $data); } - /** - * @covers Alchemy\Phrasea\Controller\Prod\Records::getRecord - */ public function testGetRecordDetailBasket() { $this->authenticate(self::$DI['app']); @@ -198,9 +177,6 @@ class RecordsTest extends \PhraseanetAuthenticatedWebTestCase unset($response, $data); } - /** - * @covers Alchemy\Phrasea\Controller\Prod\Records::getRecord - */ public function testGetRecordDetailFeed() { $this->authenticate(self::$DI['app']); diff --git a/tests/Alchemy/Tests/Phrasea/Controller/Prod/RootTest.php b/tests/Alchemy/Tests/Phrasea/Controller/Prod/RootTest.php index 95c1d4a675..2083ed043c 100644 --- a/tests/Alchemy/Tests/Phrasea/Controller/Prod/RootTest.php +++ b/tests/Alchemy/Tests/Phrasea/Controller/Prod/RootTest.php @@ -16,9 +16,8 @@ class RootTest extends \PhraseanetAuthenticatedWebTestCase public function testRouteSlash() { self::$DI['app']['phraseanet.SE'] = $this->createSearchEngineMock(); - self::$DI['client']->request('GET', '/prod/'); + $response = $this->request('GET', '/prod/'); - $response = self::$DI['client']->getResponse(); $this->assertTrue($response->isOk()); $this->assertEquals('UTF-8', $response->getCharset()); } diff --git a/tests/Alchemy/Tests/Phrasea/Controller/Prod/StoryTest.php b/tests/Alchemy/Tests/Phrasea/Controller/Prod/StoryTest.php index 182cffc9a5..f6a4a1a92d 100644 --- a/tests/Alchemy/Tests/Phrasea/Controller/Prod/StoryTest.php +++ b/tests/Alchemy/Tests/Phrasea/Controller/Prod/StoryTest.php @@ -100,7 +100,7 @@ class StoryTest extends \PhraseanetAuthenticatedWebTestCase { $story = \record_adapter::createStory(self::$DI['app'], self::$DI['collection']); - $route = sprintf("/prod/story/%s/%s/addElements/", $story->get_sbas_id(), $story->get_record_id()); + $route = sprintf("/prod/story/%s/%s/addElements/", $story->getDataboxId(), $story->getRecordId()); $records = [ self::$DI['record_1']->get_serialize_key(), @@ -115,7 +115,7 @@ class StoryTest extends \PhraseanetAuthenticatedWebTestCase $this->assertEquals(302, $response->getStatusCode()); - $this->assertEquals(2, $story->get_children()->get_count()); + $this->assertEquals(2, $story->getChildren()->get_count()); $story->delete(); } @@ -123,7 +123,7 @@ class StoryTest extends \PhraseanetAuthenticatedWebTestCase { $story = \record_adapter::createStory(self::$DI['app'], self::$DI['collection']); - $route = sprintf("/prod/story/%s/%s/addElements/", $story->get_sbas_id(), $story->get_record_id()); + $route = sprintf("/prod/story/%s/%s/addElements/", $story->getDataboxId(), $story->getRecordId()); $records = [ self::$DI['record_1']->get_serialize_key(), @@ -140,7 +140,7 @@ class StoryTest extends \PhraseanetAuthenticatedWebTestCase $this->assertEquals(200, $response->getStatusCode()); - $this->assertEquals(2, $story->get_children()->get_count()); + $this->assertEquals(2, $story->getChildren()->get_count()); $story->delete(); } @@ -157,16 +157,16 @@ class StoryTest extends \PhraseanetAuthenticatedWebTestCase $story->appendChild($record); } - $totalRecords = $story->get_children()->get_count(); + $totalRecords = $story->getChildren()->get_count(); $n = 0; foreach ($records as $record) { /* @var $record \record_adapter */ $route = sprintf( - "/prod/story/%s/%s/delete/%s/%s/" - , $story->get_sbas_id() - , $story->get_record_id() - , $record->get_sbas_id() - , $record->get_record_id() + "/prod/story/%s/%s/delete/%s/%s/", + $story->getDataboxId(), + $story->getRecordId(), + $record->getDataboxId(), + $record->getRecordId() ); if (($n % 2) === 0) { @@ -188,7 +188,7 @@ class StoryTest extends \PhraseanetAuthenticatedWebTestCase $this->assertTrue($data['success']); } $n ++; - $this->assertEquals($totalRecords - $n, $story->get_children()->get_count()); + $this->assertEquals($totalRecords - $n, $story->getChildren()->get_count()); } $story->delete(); } diff --git a/tests/Alchemy/Tests/Phrasea/Controller/Prod/UploadTest.php b/tests/Alchemy/Tests/Phrasea/Controller/Prod/UploadTest.php index 97bc731f56..65832cf4dc 100644 --- a/tests/Alchemy/Tests/Phrasea/Controller/Prod/UploadTest.php +++ b/tests/Alchemy/Tests/Phrasea/Controller/Prod/UploadTest.php @@ -377,7 +377,7 @@ class UploadTest extends \PhraseanetAuthenticatedWebTestCase $id = explode('_', $datas['id']); $record = new \record_adapter(self::$DI['app'], $id[0], $id[1]); $this->assertFalse($record->isStory()); - $this->assertEquals(1, substr(strrev($record->get_status()), 4, 1)); + $this->assertEquals(1, substr(strrev($record->getStatus()), 4, 1)); $this->assertEquals([], $datas['reasons']); } diff --git a/tests/Alchemy/Tests/Phrasea/Controller/Report/InformationsTest.php b/tests/Alchemy/Tests/Phrasea/Controller/Report/InformationsTest.php index 73bc5a7701..4f6456d31e 100644 --- a/tests/Alchemy/Tests/Phrasea/Controller/Report/InformationsTest.php +++ b/tests/Alchemy/Tests/Phrasea/Controller/Report/InformationsTest.php @@ -269,8 +269,8 @@ class InformationsTest extends \PhraseanetAuthenticatedWebTestCase 'dmax' => $this->dmax->format('Y-m-d H:i:s'), 'sbasid' => $this->getCollection()->get_sbas_id(), 'collection' => $this->getCollection()->get_coll_id(), - 'sbasid' => $this->getRecord1()->get_sbas_id(), - 'rid' => $this->getRecord1()->get_record_id(), + 'sbasid' => $this->getRecord1()->getDataboxId(), + 'rid' => $this->getRecord1()->getRecordId(), ]); $response = $this->client->getResponse(); @@ -285,8 +285,8 @@ class InformationsTest extends \PhraseanetAuthenticatedWebTestCase 'dmax' => $this->dmax->format('Y-m-d H:i:s'), 'sbasid' => $this->getCollection()->get_sbas_id(), 'collection' => $this->getCollection()->get_coll_id(), - 'sbasid' => $this->getRecord1()->get_sbas_id(), - 'rid' => $this->getRecord1()->get_record_id(), + 'sbasid' => $this->getRecord1()->getDataboxId(), + 'rid' => $this->getRecord1()->getRecordId(), 'from' => 'TOOL' ]); @@ -302,8 +302,8 @@ class InformationsTest extends \PhraseanetAuthenticatedWebTestCase 'dmax' => $this->dmax->format('Y-m-d H:i:s'), 'sbasid' => $this->getCollection()->get_sbas_id(), 'collection' => $this->getCollection()->get_coll_id(), - 'sbasid' => $this->getRecord1()->get_sbas_id(), - 'rid' => $this->getRecord1()->get_record_id(), + 'sbasid' => $this->getRecord1()->getDataboxId(), + 'rid' => $this->getRecord1()->getRecordId(), 'from' => 'DASH' ]); @@ -319,8 +319,8 @@ class InformationsTest extends \PhraseanetAuthenticatedWebTestCase 'dmax' => $this->dmax->format('Y-m-d H:i:s'), 'sbasid' => $this->getCollection()->get_sbas_id(), 'collection' => $this->getCollection()->get_coll_id(), - 'sbasid' => $this->getRecord1()->get_sbas_id(), - 'rid' => $this->getRecord1()->get_record_id(), + 'sbasid' => $this->getRecord1()->getDataboxId(), + 'rid' => $this->getRecord1()->getRecordId(), 'user' => $this->getUser()->getId() ]); diff --git a/tests/Alchemy/Tests/Phrasea/Controller/Root/RSSFeedTest.php b/tests/Alchemy/Tests/Phrasea/Controller/Root/RSSFeedTest.php index c1d78612c2..9ae0c68a2a 100644 --- a/tests/Alchemy/Tests/Phrasea/Controller/Root/RSSFeedTest.php +++ b/tests/Alchemy/Tests/Phrasea/Controller/Root/RSSFeedTest.php @@ -388,7 +388,7 @@ class RSSFeedTest extends \PhraseanetWebTestCase $this->assertEquals($ressource->get_mime(), $value); break; case "medium": - $this->assertEquals(strtolower($record->get_type()), $value); + $this->assertEquals(strtolower($record->getType()), $value); break; case "isDefault": !$is_thumbnail ? $this->assertEquals("true", $value) : $this->assertEquals("false", $value); diff --git a/tests/Alchemy/Tests/Phrasea/Core/Configuration/Fixtures/configuration-setup.yml b/tests/Alchemy/Tests/Phrasea/Core/Configuration/Fixtures/configuration-setup.yml index 371b7b1d02..3a312ed3bb 100644 --- a/tests/Alchemy/Tests/Phrasea/Core/Configuration/Fixtures/configuration-setup.yml +++ b/tests/Alchemy/Tests/Phrasea/Core/Configuration/Fixtures/configuration-setup.yml @@ -196,3 +196,18 @@ crossdomain: - domain: '*.cooliris.com' secure: 'false' +embed_bundle: + video: + player: videojs + autoplay: false + coverSubdef: thumbnail + available-speeds: + - 1 + - 1.5 + - 3 + audio: + player: videojs + autoplay: false + document: + player: flexpaper + enable-pdfjs: true diff --git a/tests/Alchemy/Tests/Phrasea/Core/Configuration/Fixtures/configuration.yml b/tests/Alchemy/Tests/Phrasea/Core/Configuration/Fixtures/configuration.yml index 371b7b1d02..3a312ed3bb 100644 --- a/tests/Alchemy/Tests/Phrasea/Core/Configuration/Fixtures/configuration.yml +++ b/tests/Alchemy/Tests/Phrasea/Core/Configuration/Fixtures/configuration.yml @@ -196,3 +196,18 @@ crossdomain: - domain: '*.cooliris.com' secure: 'false' +embed_bundle: + video: + player: videojs + autoplay: false + coverSubdef: thumbnail + available-speeds: + - 1 + - 1.5 + - 3 + audio: + player: videojs + autoplay: false + document: + player: flexpaper + enable-pdfjs: true diff --git a/tests/Alchemy/Tests/Phrasea/Databox/DataboxBoundRepositoryProviderTest.php b/tests/Alchemy/Tests/Phrasea/Databox/DataboxBoundRepositoryProviderTest.php new file mode 100644 index 0000000000..99859f2f08 --- /dev/null +++ b/tests/Alchemy/Tests/Phrasea/Databox/DataboxBoundRepositoryProviderTest.php @@ -0,0 +1,57 @@ +prophesize(DataboxBoundRepositoryFactory::class); + + $factory + ->createRepositoryFor(Argument::type('integer')) + ->will(function ($args) { + return (object)['databoxId' => $args[0]]; + }); + + $this->sut = new DataboxBoundRepositoryProvider($factory->reveal()); + } + + public function testItCreatesRepositoriesIfUnknown() + { + $repository = $this->sut->getRepositoryForDatabox(42); + + $this->assertNotNull($repository, 'Failed to create a repository'); + $this->assertSame($repository, $this->sut->getRepositoryForDatabox(42)); + } + + public function testItShouldNotCreateTwoRepositoriesPerDatabox() + { + $repository1 = $this->sut->getRepositoryForDatabox(1); + $repository2 = $this->sut->getRepositoryForDatabox(2); + + $this->assertNotNull($repository1, 'Failed to create first repository'); + $this->assertNotNull($repository2, 'Failed to create second repository'); + $this->assertNotSame($repository1, $repository2, 'Different Databoxes should have different repositories'); + + $this->assertSame($repository2, $this->sut->getRepositoryForDatabox(2), 'Second Repository should be returned'); + $this->assertSame($repository1, $this->sut->getRepositoryForDatabox(1), 'First Repository should be returned'); + } +} diff --git a/tests/Alchemy/Tests/Phrasea/Hydration/IdentityMapTest.php b/tests/Alchemy/Tests/Phrasea/Hydration/IdentityMapTest.php new file mode 100644 index 0000000000..ceeb43eea4 --- /dev/null +++ b/tests/Alchemy/Tests/Phrasea/Hydration/IdentityMapTest.php @@ -0,0 +1,114 @@ +prophesize(Hydrator::class); + + $hydrator->extract(Argument::type(\stdClass::class)) + ->will(function ($args) { + return (array)$args[0]; + }); + + $hydrator->hydrate(Argument::type(\stdClass::class), Argument::type('array')) + ->will(function ($args) { + + foreach ($args[1] as $property => $value) { + $args[0]->{$property} = $value; + } + }); + + $this->sut = new IdentityMap($hydrator->reveal(), (object)['foo' => null, 'bar' => null]); + } + + public function testItShouldBeArrayAccessibleTraversableAndCountable() + { + $this->assertInstanceOf(\Traversable::class, $this->sut); + $this->assertInstanceOf(\ArrayAccess::class, $this->sut); + $this->assertInstanceOf(\Countable::class, $this->sut); + } + + public function testItShouldHydrateAnInstanceWhenNotYetInMap() + { + $expected = (object)['foo' => 'foo', 'bar' => 'bar']; + + $instance = $this->sut->hydrate(42, ['foo' => 'foo', 'bar' => 'bar']); + + $this->assertEquals($expected, $instance, 'Invalid instance generated'); + + $this->assertSame($instance, $this->sut[42], 'Accessing by offset should succeed'); + } + + public function testItShouldReHydrateAnInstance() + { + $instance = $this->sut->hydrate(42, ['foo' => 'Foo', 'bar' => 'Bar']); + + $this->assertAttributeSame('Foo', 'foo', $instance); + $this->assertAttributeSame('Bar', 'bar', $instance); + + $instance2 = $this->sut->hydrate(42, ['foo' => 'new foo value', 'bar' => null]); + + $this->assertAttributeSame('new foo value', 'foo', $instance); + $this->assertAttributeSame(null, 'bar', $instance); + + $this->assertSame($instance, $instance2, 'Same instance was not rehydrated'); + + $this->assertCount(1, $this->sut); + } + + public function testItsOffsetCanBeUnset() + { + $this->sut[42] = ['foo' => 'Foo', 'bar' => 'Bar']; + + $this->assertTrue(isset($this->sut[42]), 'Offset should exists'); + + unset($this->sut[42]); + + $this->assertFalse(isset($this->sut[42]), 'Offset should not exists after unset'); + } + + public function testItHydratesAllEntities() + { + $data = [ + ['foo' => 'Foo1', 'bar' => 'Bar1'], + ['foo' => 'Foo2', 'bar' => 'Bar2'], + ]; + + $this->sut->hydrateAll($data); + + $entities = []; + + foreach ($this->sut as $key => $value) { + $entities[$key] = $value; + } + + foreach ($data as $key => $value) { + $this->assertArrayHasKey($key, $entities, 'An entity is missing'); + $this->assertEquals((object)$value, $entities[$key], 'Unexpected entity value'); + } + + $this->assertCount(2, $this->sut); + $this->sut->clear(); + $this->assertCount(0, $this->sut, 'Map was not cleared'); + } +} diff --git a/tests/Alchemy/Tests/Phrasea/Hydration/ReflectionHydratorTest.php b/tests/Alchemy/Tests/Phrasea/Hydration/ReflectionHydratorTest.php new file mode 100644 index 0000000000..a956414f94 --- /dev/null +++ b/tests/Alchemy/Tests/Phrasea/Hydration/ReflectionHydratorTest.php @@ -0,0 +1,73 @@ +sut = new ReflectionHydrator(ToHydrate::class, ['foo', 'bar']); + } + + public function testItThrowsExceptionOnUnknownProperty() + { + $this->setExpectedException(\ReflectionException::class); + + $sut = new ReflectionHydrator(ToHydrate::class, ['baz']); + $sut->extract(new ToHydrate()); + } + + public function testItShouldImplementHydrator() + { + $this->assertInstanceOf(Hydrator::class, $this->sut); + } + + public function testItShouldProperlyHydrateInstance() + { + $stub = new ToHydrate(); + + $this->sut->hydrate($stub, ['foo' => 'foo modified', 'bar' => 'bar changed']); + + $this->assertEquals('foo modified', $stub->getFoo(), 'Property foo was not hydrated'); + $this->assertEquals('bar changed', $stub->getBar(), 'Property bar was not hydrated'); + } + + public function testItShouldProperlyExtractData() + { + $data = $this->sut->extract(new ToHydrate()); + + $this->assertSame(['foo' => 'foo', 'bar' => 'bar'], $data, 'Improper extraction of properties'); + } +} + +class ToHydrate +{ + private $foo = 'foo'; + private $bar = 'bar'; + + public function getFoo() + { + return $this->foo; + } + + public function getBar() + { + return $this->bar; + } +} diff --git a/tests/Alchemy/Tests/Phrasea/SearchEngine/ElasticSearchEngineTest.php b/tests/Alchemy/Tests/Phrasea/SearchEngine/ElasticSearchEngineTest.php deleted file mode 100644 index 1848ef38ef..0000000000 --- a/tests/Alchemy/Tests/Phrasea/SearchEngine/ElasticSearchEngineTest.php +++ /dev/null @@ -1,54 +0,0 @@ -markTestSkipped(); - if (false === @file_get_contents('http://localhost:9200')) { - $this->markTestSkipped('Unable to connect to elasticsearch.'); - } - - parent::setUp(); - - /** @var Indexer $indexer */ - $indexer = self::$DI['app']['elasticsearch.indexer']; - - // Re-index everything - ob_start(); - $indexer->deleteIndex(); - $indexer->createIndex(); - $indexer->populateIndex(); - ob_end_clean(); - } - - public function initialize() - { - // Change the index name - self::$DI['app']['conf']->set(['main', 'search-engine', 'options', 'index'], 'test'); - - self::$searchEngine = $es = new ElasticSearchEngine( - self::$DI['app'], - self::$DI['app']['elasticsearch.client'], - self::$DI['app']['elasticsearch.options']['index'] - ); - - self::$searchEngineClass = 'Alchemy\Phrasea\SearchEngine\Elastic\ElasticSearchEngine'; - } - - public function testAutocomplete() - { - $this->markTestSkipped("Not implemented yet."); - } - - protected function updateIndex(array $stemms = []) - { - $client = self::$searchEngine->getClient(); - $client->indices()->refresh(); - } -} diff --git a/tests/Alchemy/Tests/Phrasea/SearchEngine/SearchEngineAbstractTest.php b/tests/Alchemy/Tests/Phrasea/SearchEngine/SearchEngineAbstractTest.php deleted file mode 100644 index e5f24b7aa2..0000000000 --- a/tests/Alchemy/Tests/Phrasea/SearchEngine/SearchEngineAbstractTest.php +++ /dev/null @@ -1,773 +0,0 @@ -get_databox()->get_meta_structure()->get_elements() as $field) { - if (!$field->isBusiness()) { - continue; - } - $found = true; - } - - if (!$found) { - $field = \databox_field::create(self::$DI['app'], self::$DI['record_2']->get_databox(), 'testBusiness' . mt_rand(), false); - $field->set_business(true); - $field->save(); - } - - foreach (self::$DI['app']->getDataboxes() as $databox) { - break; - } - } - - $this->initialize(); - - if (!self::$searchEngine instanceof SearchEngineInterface) { - $this->markTestSkipped('Unable to initialize search Engine'); - } - - $options = new SearchEngineOptions(); - $options->onCollections($databox->get_collections()); - - $this->options = $options; - } - - public static function tearDownAfterClass() - { - self::$searchEngine = self::$searchEngineClass = self::$initialized = null; - parent::tearDownAfterClass(); - } - - /** - * @return SearchEngineOptions - */ - private function getOptions() - { - return $this->options; - } - - public function testQueryRecordId() - { - $record = self::$DI['record_2']; - $query_string = 'recordid=' . $record->get_record_id(); - - self::$searchEngine->addRecord($record); - $this->updateIndex(); - - self::$searchEngine->resetCache(); - $results = self::$searchEngine->query($query_string, 0, 1, $this->options); - $this->assertEquals(1, $results->getTotal()); - - $result = $results->getResults()->first(); - - $this->assertEquals($record->get_record_id(), $result->get_record_id()); - $this->assertEquals($record->get_sbas_id(), $result->get_sbas_id()); - } - - public function testQueryStoryId() - { - $record = self::$DI['record_2']; - $query_string = 'storyid=' . $record->get_record_id(); - - self::$searchEngine->addRecord($record); - $this->updateIndex(); - - self::$searchEngine->resetCache(); - $results = self::$searchEngine->query($query_string, 0, 1, $this->options); - $this->assertEquals(1, $results->getTotal()); - - $result = $results->getResults()->first(); - - $this->assertEquals($record->get_record_id(), $result->get_record_id()); - $this->assertEquals($record->get_sbas_id(), $result->get_sbas_id()); - } - - public function testQueryByDateMin() - { - $record = self::$DI['record_2']; - $query_string = 'boomboklot' . $record->get_record_id() . 'dateMin'; - - $this->editRecord($query_string, $record); - - $date_field = $this->editDateRecord('2012-12-21 12:12:00', $record); - - if (!$date_field) { - $this->markTestSkipped('unable to add a date to record'); - } - - self::$searchEngine->addRecord($record); - $this->updateIndex(); - - $options = $this->getOptions(); - $options->setDateFields([$date_field]); - $options->setMinDate(\DateTime::createFromFormat('Y-m-d H:i:s', '2012-12-23 01:01:00')); - - self::$searchEngine->resetCache(); - $results = self::$searchEngine->query($query_string, 0, 1, $options); - $this->assertEquals(0, $results->getTotal()); - - $options->setMinDate(\DateTime::createFromFormat('Y-m-d H:i:s', '2012-12-20 01:01:00')); - - self::$searchEngine->resetCache(); - $results = self::$searchEngine->query($query_string, 0, 1, $options); - $this->assertEquals(1, $results->getTotal()); - } - - private function editDateRecord($date, \record_adapter $record) - { - $date_field = null; - - foreach ($record->get_databox()->get_meta_structure() as $databox_field) { - if ($databox_field->get_type() != \databox_field::TYPE_DATE) { - continue; - } - - $date_field = $databox_field; - - break; - } - - if ($date_field) { - - $toupdate = []; - - try { - $values = $record->get_caption()->get_field($databox_field->get_name())->get_values(); - $value = array_pop($values); - $meta_id = $value->getId(); - } catch (\Exception $e) { - $meta_id = null; - } - - $toupdate[$databox_field->get_id()] = [ - 'meta_id' => $meta_id - , 'meta_struct_id' => $databox_field->get_id() - , 'value' => $date - ]; - - $record->set_metadatas($toupdate); - } - - return $date_field; - } - - public function testQueryByDateMax() - { - $record = self::$DI['record_2']; - $query_string = 'boomboklot' . $record->get_record_id() . 'dateMax'; - - $this->editRecord($query_string, $record); - - $date_field = $this->editDateRecord('2012-12-21 12:12:00', $record); - - if (!$date_field) { - $this->markTestSkipped('unable to add a date to record'); - } - - self::$searchEngine->addRecord($record); - $this->updateIndex(); - - $options = $this->getOptions(); - $options->setDateFields([$date_field]); - $options->setMaxDate(\DateTime::createFromFormat('Y-m-d H:i:s', '2012-12-20 01:01:00')); - - self::$searchEngine->resetCache(); - $results = self::$searchEngine->query($query_string, 0, 1, $options); - $this->assertEquals(0, $results->getTotal()); - - $options->setMaxDate(\DateTime::createFromFormat('Y-m-d H:i:s', '2012-12-23 01:01:00')); - - self::$searchEngine->resetCache(); - $results = self::$searchEngine->query($query_string, 0, 1, $options); - $this->assertEquals(1, $results->getTotal()); - } - - public function testQueryByDateRange() - { - $record = self::$DI['record_2']; - $query_string = 'boomboklot' . $record->get_record_id() . 'dateRange'; - - $this->editRecord($query_string, $record); - - $date_field = $this->editDateRecord('2012-12-21 12:12:00', $record); - - if (!$date_field) { - $this->markTestSkipped('unable to add a date to record'); - } - - self::$searchEngine->addRecord($record); - $this->updateIndex(); - - $options = $this->getOptions(); - $options->setDateFields([$date_field]); - $options->setMinDate(\DateTime::createFromFormat('Y-m-d H:i:s', '2012-12-18 01:01:00')); - $options->setMaxDate(\DateTime::createFromFormat('Y-m-d H:i:s', '2012-12-20 01:01:00')); - - self::$searchEngine->resetCache(); - $results = self::$searchEngine->query($query_string, 0, 1, $options); - $this->assertEquals(0, $results->getTotal()); - - $options->setMaxDate(\DateTime::createFromFormat('Y-m-d H:i:s', '2012-12-22 01:01:00')); - - self::$searchEngine->resetCache(); - $results = self::$searchEngine->query($query_string, 0, 1, $options); - $this->assertEquals(1, $results->getTotal()); - } - - protected function editRecord($string2add, \record_adapter &$record, $indexable = true, $business = false) - { - $toupdate = []; - $field = null; - - foreach ($record->get_databox()->get_meta_structure()->get_elements() as $field) { - - if ($indexable !== $field->is_indexable() || $field->isBusiness() !== $business) { - continue; - } - - try { - $values = $record->get_caption()->get_field($field->get_name())->get_values(); - $value = array_pop($values); - $meta_id = $value->getId(); - } catch (\Exception $e) { - $meta_id = null; - } - - $toupdate[$field->get_id()] = [ - 'meta_id' => $meta_id - , 'meta_struct_id' => $field->get_id() - , 'value' => $string2add - ]; - break; - } - - $record->set_metadatas($toupdate); - - return $field; - } - - public function testRecordNotIndexed() - { - $record = self::$DI['record_2']; - $query_string = 'boomboklot' . $record->get_record_id() . 'defaultNotIndexed'; - - self::$searchEngine->resetCache(); - $results = self::$searchEngine->query($query_string, 0, 1, $this->options); - $this->assertEquals(0, $results->getTotal()); - - $this->editRecord($query_string, $record); - - self::$searchEngine->resetCache(); - $this->updateIndex(); - $results = self::$searchEngine->query($query_string, 0, 1, $this->options); - $this->assertEquals(1, $results->getTotal()); - } - - public function testAddRecord() - { - $record = self::$DI['record_2']; - $query_string = 'boomboklot' . $record->get_record_id() . 'defaultAdd'; - - $this->editRecord($query_string, $record); - - self::$searchEngine->addRecord($record); - $this->updateIndex(); - - self::$searchEngine->resetCache(); - $results = self::$searchEngine->query($query_string, 0, 1, $this->options); - $this->assertEquals(1, $results->getTotal()); - } - - public function testUpdateRecord() - { - $record = self::$DI['record_2']; - - self::$searchEngine->addRecord($record); - $this->updateIndex(); - - $query_string = 'boomboklot' . $record->get_record_id() . 'updateRecord'; - - $this->editRecord($query_string, $record); - - self::$searchEngine->updateRecord($record); - $this->updateIndex(); - - self::$searchEngine->resetCache(); - $results = self::$searchEngine->query($query_string, 0, 1, $this->options); - $this->assertEquals(1, $results->getTotal()); - } - - protected function getDefaultOptions() - { - $appbox = self::$DI['app']['phraseanet.appbox']; - foreach ($appbox->get_databoxes() as $databox) { - break; - } - $options = new SearchEngineOptions(); - $options->onCollections($databox->get_collections()); - - return $options; - } - - /** - * @dataProvider provideStemmData - */ - public function testUpdateRecordWithStemm($language, $word, $stemm) - { - if (!self::$searchEngine->hasStemming()) { - $this->markTestSkipped(sprintf( - '%s does not support stemm, passing stemmatization for language %s', - get_class(self::$searchEngine), - $language - )); - } - - $options = $this->getDefaultOptions(); - $options->setStemming(true); - $options->setLocale($language); - - $record = self::$DI['record_2']; - $index_string = sprintf( - 'boomboklot%dstemmed%s %s', - $record->get_record_id(), - $language, - $word - ); - $query_string = sprintf( - 'boomboklot%dstemmed%s %s', - $record->get_record_id(), - $language, - $stemm - ); - - $this->editRecord($index_string, $record); - - self::$searchEngine->addRecord($record); - $this->updateIndex([$language]); - - self::$searchEngine->resetCache(); - $results = self::$searchEngine->query($query_string, 0, 1, $options); - $this->assertEquals(1, $results->getTotal()); - } - - public function provideStemmData() - { - $stemms = []; - - $examples = [ - 'fr' => ['word' => 'chevaux', 'stemm' => 'cheval'], - 'en' => ['word' => 'consistency', 'stemm' => 'consistent'], - 'de' => ['word' => 'aufeinanderfolgender', 'stemm' => 'aufeinanderfolg'], - 'nl' => ['word' => 'lichamelijk', 'stemm' => 'licham'], - ]; - - foreach (Application::getAvailableLanguages() as $languageCode => $name) { - $data = explode('_', $languageCode); - $code = $data[0]; - - if (!isset($examples[$code])) { - $this->fail(sprintf('Missing stemm examples for language %s', $code)); - } - - $stemms[] = [ - $code, - $examples[$code]['word'], - $examples[$code]['stemm'], - ]; - } - - return $stemms; - } - - public function testUpdateQueryOnField() - { - $options = $this->getDefaultOptions(); - $record = self::$DI['record_2']; - - $query_string = 'boomboklot' . $record->get_record_id() . 'onfield'; - - $field = $this->editRecord($query_string, $record); - $options->setFields([$field]); - - self::$searchEngine->addRecord($record); - $this->updateIndex(); - - self::$searchEngine->resetCache(); - $results = self::$searchEngine->query($query_string, 0, 1, $options); - $this->assertEquals(1, $results->getTotal()); - } - - public function testBusinessFieldAvailable() - { - $options = $this->getDefaultOptions(); - $record = self::$DI['record_2']; - - $query_string = 'boomboklot' . $record->get_record_id() . 'businessAvailable'; - - $this->editRecord($query_string, $record, true, true); - $options->allowBusinessFieldsOn([$record->get_collection()]); - - self::$searchEngine->addRecord($record); - $this->updateIndex(); - - self::$searchEngine->resetCache(); - $results = self::$searchEngine->query($query_string, 0, 1, $options); - $this->assertEquals(1, $results->getTotal()); - } - - public function testBusinessFieldNotAvailable() - { - $record = self::$DI['record_2']; - $query_string = 'boomboklot' . $record->get_record_id() . 'businessNotAvailable'; - - $this->editRecord($query_string, $record, true, true); - - self::$searchEngine->addRecord($record); - $this->updateIndex(); - - self::$searchEngine->resetCache(); - $results = self::$searchEngine->query($query_string, 0, 1, $this->options); - $this->assertEquals(0, $results->getTotal()); - } - - public function testUpdateQueryOnEmptyField() - { - $options = $this->getDefaultOptions(); - - $record = self::$DI['record_2']; - $query_string = 'boomboklot' . $record->get_record_id() . 'anotherfield'; - - $selectedField = $this->editRecord($query_string, $record); - - foreach ($record->get_databox()->get_meta_structure()->get_elements() as $field) { - if ($selectedField->get_id() != $field->get_id()) { - $options->setFields([$field]); - - break; - } - } - - self::$searchEngine->addRecord($record); - $this->updateIndex(); - - self::$searchEngine->resetCache(); - $results = self::$searchEngine->query($query_string, 0, 1, $options); - $this->assertEquals(0, $results->getTotal()); - } - - public function testUpdateNonIndexableRecord() - { - $record = self::$DI['record_2']; - $query_string = 'boomboklot_no_index_' . $record->get_record_id() . '_'; - - $field = $this->editRecord($query_string, $record, false); - if (!$field) { - $this->markTestSkipped('No non-indexable field found'); - } - - self::$searchEngine->addRecord($record); - $this->updateIndex(); - - self::$searchEngine->resetCache(); - $results = self::$searchEngine->query($query_string, 0, 1, $this->options); - $this->assertEquals(0, $results->getTotal()); - } - - public function testDeleteRecord() - { - $record = self::$DI['record_2']; - $query_string = 'boomboklot' . $record->get_record_id() . 'deleteRecord'; - - $field = $this->editRecord($query_string, $record); - - self::$searchEngine->addRecord($record); - $this->updateIndex(); - self::$searchEngine->removeRecord($record); - $this->updateIndex(); - - self::$searchEngine->resetCache(); - $results = self::$searchEngine->query($query_string, 0, 1, $this->options); - $this->assertEquals(0, $results->getTotal()); - - $options = $this->getDefaultOptions(); - $options->setFields([$field]); - - self::$searchEngine->resetCache(); - $results = self::$searchEngine->query($query_string, 0, 1, $options); - $this->assertEquals(0, $results->getTotal()); - } - - /** - * @dataProvider provideStemmData - */ - public function testDeleteRecordWithinStemmContext($language, $word, $stemm) - { - $record = self::$DI['record_2']; - $index_string = 'boomboklot' . $record->get_record_id() . 'deleteRecordInStemmContext '.$word; - $query_string = 'boomboklot' . $record->get_record_id() . 'deleteRecordInStemmContext '.$stemm; - - $options = $this->getDefaultOptions(); - $options->setStemming(true); - $options->setLocale($language); - - $field = $this->editRecord($index_string, $record); - - self::$searchEngine->addRecord($record); - $this->updateIndex(); - self::$searchEngine->removeRecord($record); - $this->updateIndex(); - - self::$searchEngine->resetCache(); - $results = self::$searchEngine->query($query_string, 0, 1, $options); - $this->assertEquals(0, $results->getTotal()); - - $options->setFields([$field]); - - self::$searchEngine->resetCache(); - $results = self::$searchEngine->query($query_string, 0, 1, $options); - $this->assertEquals(0, $results->getTotal()); - } - - public function testAvailableTypes() - { - $this->assertTrue(is_array(self::$searchEngine->getAvailableTypes())); - foreach (self::$searchEngine->getAvailableTypes() as $type) { - $this->assertTrue(in_array($type, [SearchEngineInterface::GEM_TYPE_ENTRY, SearchEngineInterface::GEM_TYPE_RECORD, SearchEngineInterface::GEM_TYPE_STORY])); - } - } - - public function testStatus() - { - foreach (self::$searchEngine->getStatus() as $StatusKeyValue) { - $this->assertTrue(is_array($StatusKeyValue)); - $this->assertTrue(is_scalar($StatusKeyValue[0])); - $this->assertTrue(is_scalar($StatusKeyValue[1])); - } - } - - public function testAddStory() - { - $story = self::$DI['record_story_1']; - $query_string = 'story' . $story->get_record_id() . 'addStory'; - - $options = $this->getDefaultOptions(); - $options->setSearchType(SearchEngineOptions::RECORD_GROUPING); - - $this->editRecord($query_string, $story); - - self::$searchEngine->addStory($story); - $this->updateIndex(); - - self::$searchEngine->resetCache(); - $results = self::$searchEngine->query($query_string, 0, 1, $options); - $this->assertEquals(1, $results->getTotal()); - } - - public function testUpdateStory() - { - $story = self::$DI['record_story_1']; - - $options = $this->getDefaultOptions(); - $options->setSearchType(SearchEngineOptions::RECORD_GROUPING); - - self::$searchEngine->addStory($story); - $this->updateIndex(); - - $query_string = 'story' . $story->get_record_id() . 'updateStory'; - $this->editRecord($query_string, $story); - - self::$searchEngine->updateStory($story); - $this->updateIndex(); - - self::$searchEngine->resetCache(); - $results = self::$searchEngine->query($query_string, 0, 1, $options); - $this->assertEquals(1, $results->getTotal()); - } - - public function testStatusQueryOnOverOff() - { - $options = $this->getDefaultOptions(); - $record = self::$DI['record_2']; - $record->set_binary_status('00000'); - - $query_string = 'boomboklot' . $record->get_record_id() . 'statusQueryOff'; - $this->editRecord($query_string, $record); - - self::$searchEngine->addRecord($record); - $this->updateIndex(); - - $options->setStatus([4 => ['on' => [$record->get_databox()->get_sbas_id()]]]); - - self::$searchEngine->resetCache(); - $results = self::$searchEngine->query($query_string, 0, 1, $options); - $this->assertEquals(0, $results->getTotal()); - } - - public function testStatusQueryOnOverOn() - { - $options = $this->getDefaultOptions(); - - $record = self::$DI['record_2']; - $record->set_binary_status('10000'); - - $options->setStatus([4 => ['on' => [$record->get_databox()->get_sbas_id()]]]); - - $query_string = 'boomboklot' . $record->get_record_id() . 'statusQueryOnOverOn'; - $this->editRecord($query_string, $record); - - self::$searchEngine->addRecord($record); - $this->updateIndex(); - - self::$searchEngine->resetCache(); - $results = self::$searchEngine->query($query_string, 0, 1, $options); - $this->assertEquals(1, $results->getTotal()); - } - - public function testStatusQueryOffOverOn() - { - $options = $this->getDefaultOptions(); - - $record = self::$DI['record_2']; - $record->set_binary_status('10000'); - - $options->setStatus([4 => ['off' => [$record->get_databox()->get_sbas_id()]]]); - - $query_string = 'boomboklot' . $record->get_record_id() . 'statusQueryOff'; - $this->editRecord($query_string, $record); - - self::$searchEngine->addRecord($record); - $this->updateIndex(); - - self::$searchEngine->resetCache(); - $results = self::$searchEngine->query($query_string, 0, 1, $options); - $this->assertEquals(0, $results->getTotal()); - } - - public function testStatusQueryOffOverOff() - { - $options = $this->getDefaultOptions(); - - $record = self::$DI['record_2']; - $record->set_binary_status('00000'); - - $options->setStatus([4 => ['off' => [$record->get_databox()->get_sbas_id()]]]); - - $query_string = 'boomboklot' . $record->get_record_id() . 'statusQueryOff'; - $this->editRecord($query_string, $record); - - self::$searchEngine->addRecord($record); - $this->updateIndex(); - - self::$searchEngine->resetCache(); - $results = self::$searchEngine->query($query_string, 0, 1, $options); - $this->assertEquals(1, $results->getTotal()); - } - - public function testStatusQueryUpdate() - { - $options = $this->getDefaultOptions(); - $record = self::$DI['record_2']; - $record->set_binary_status('00000'); - - $query_string = 'boomboklot' . $record->get_record_id() . 'statusQueryUpdate'; - $this->editRecord($query_string, $record); - - self::$searchEngine->addRecord($record); - $this->updateIndex(); - - $options->setStatus([4 => ['on' => [$record->get_databox()->get_sbas_id()]]]); - - self::$searchEngine->resetCache(); - $results = self::$searchEngine->query($query_string, 0, 1, $options); - $this->assertEquals(0, $results->getTotal()); - - $record->set_binary_status('10000'); - - self::$searchEngine->updateRecord($record); - $this->updateIndex(); - - self::$searchEngine->resetCache(); - $results = self::$searchEngine->query($query_string, 0, 1, $this->options); - $this->assertEquals(1, $results->getTotal()); - } - - public function testExcerptFromSimpleQuery() - { - $record = self::$DI['record_2']; - $query_string = 'boomboklot' . $record->get_record_id() . 'excerptSimpleQuery'; - - $this->editRecord($query_string, $record); - - self::$searchEngine->addRecord($record); - $this->updateIndex(); - - self::$searchEngine->resetCache(); - $results = self::$searchEngine->query($query_string, 0, 1, $this->options); - $fields = []; - $foundRecord = $results->getResults()->first(); - - $this->assertInstanceOf('\record_adapter', $foundRecord); - - foreach ($foundRecord->get_caption()->get_fields() as $field) { - foreach ($field->get_values() as $metaId => $v) { - $values[$metaId] = [ - 'value' => $v->getValue(), - 'from_thesaurus' => false, - 'qjs' => null, - ]; - } - - $fields[$field->get_name()] = [ - 'values' => $values, - 'separator' => ';', - ]; - } - - $found = false; - $highlightedValues = self::$searchEngine->excerpt($query_string, $fields, $foundRecord); - foreach ($highlightedValues as $fieldValues) { - foreach ($fieldValues as $metaId => $field) { - if (strpos($field, '[[em]]') !== false && strpos($field, '[[/em]]') !== false) { - $found = true; - break 2; - } - } - } - - if (!$found && count($highlightedValues) > 0) { - $this->fail('Unable to build the excerpt'); - } - } - - public function testCreateSubscriber() - { - $classname = self::$searchEngineClass; - //$this->assertInstanceOf('Symfony\Component\EventDispatcher\EventSubscriberInterface', $classname::createSubscriber(self::$DI['app'])); - } - - abstract public function initialize(); - - abstract public function testAutocomplete(); - - abstract protected function updateIndex(array $stemms = []); -} diff --git a/tests/Alchemy/Tests/Phrasea/SearchEngine/SearchEngineOptionsTest.php b/tests/Alchemy/Tests/Phrasea/SearchEngine/SearchEngineOptionsTest.php index 3276d1ed99..ef9e66cf31 100644 --- a/tests/Alchemy/Tests/Phrasea/SearchEngine/SearchEngineOptionsTest.php +++ b/tests/Alchemy/Tests/Phrasea/SearchEngine/SearchEngineOptionsTest.php @@ -38,6 +38,8 @@ class SearchEngineOptionsTest extends \PhraseanetTestCase $options->setMinDate(\DateTime::createFromFormat(DATE_ATOM, $min_date->format(DATE_ATOM))); $options->setMaxDate(\DateTime::createFromFormat(DATE_ATOM, $max_date->format(DATE_ATOM))); + $options->setFirstResult(3); + $options->setMaxResults(42); $serialized = $options->serialize(); diff --git a/tests/Alchemy/Tests/Phrasea/Vocabulary/ControlProvider/UserProviderTest.php b/tests/Alchemy/Tests/Phrasea/Vocabulary/ControlProvider/UserProviderTest.php index 6dd28f28c8..0940636d44 100644 --- a/tests/Alchemy/Tests/Phrasea/Vocabulary/ControlProvider/UserProviderTest.php +++ b/tests/Alchemy/Tests/Phrasea/Vocabulary/ControlProvider/UserProviderTest.php @@ -3,7 +3,6 @@ namespace Alchemy\Tests\Phrasea\Vocabulary\ControlProvider; use Alchemy\Phrasea\Vocabulary\ControlProvider\UserProvider; -use Doctrine\ORM\EntityManager; /** * @group functional @@ -63,22 +62,22 @@ class UserProviderTest extends \PhraseanetTestCase $results = $this->object->find('BABE', $user, self::$DI['collection']->get_databox()); - $this->assertInstanceOf('\\Doctrine\\Common\\Collections\\ArrayCollection', $results); + $this->assertInternalType('array', $results); $results = $this->object->find($user->getEmail(), $user, self::$DI['collection']->get_databox()); - $this->assertInstanceOf('\\Doctrine\\Common\\Collections\\ArrayCollection', $results); - $this->assertTrue($results->count() > 0); + $this->assertInternalType('array', $results); + $this->assertGreaterThan(0, count($results), 'There should be more users matching'); $results = $this->object->find($user->getFirstName(), $user, self::$DI['collection']->get_databox()); - $this->assertInstanceOf('\\Doctrine\\Common\\Collections\\ArrayCollection', $results); - $this->assertTrue($results->count() > 0); + $this->assertInternalType('array', $results); + $this->assertGreaterThan(0, count($results), 'There should be more users matching'); $results = $this->object->find($user->getLastName(), $user, self::$DI['collection']->get_databox()); - $this->assertInstanceOf('\\Doctrine\\Common\\Collections\\ArrayCollection', $results); - $this->assertTrue($results->count() > 0); + $this->assertInternalType('array', $results); + $this->assertGreaterThan(0, count($results), 'There should be more users matching'); self::$DI['app']['manipulator.user']->delete($user); } diff --git a/tests/Alchemy/Tests/Phrasea/Vocabulary/ControllerTest.php b/tests/Alchemy/Tests/Phrasea/Vocabulary/ControllerTest.php deleted file mode 100644 index 83c0bc16c0..0000000000 --- a/tests/Alchemy/Tests/Phrasea/Vocabulary/ControllerTest.php +++ /dev/null @@ -1,38 +0,0 @@ -assertInstanceOf('\\Alchemy\\Phrasea\\Vocabulary\\ControlProvider\\UserProvider', $provider); - - try { - $provider = Controller::get(self::$DI['app'], 'Zebulon'); - $this->fail('Should raise an exception'); - } catch (\Exception $e) { - - } - } - - public function testGetAvailable() - { - $available = Controller::getAvailable(self::$DI['app']); - - $this->assertTrue(is_array($available)); - - foreach ($available as $controller) { - $this->assertInstanceOf('\\Alchemy\\Phrasea\\Vocabulary\\ControlProvider\\ControlProviderInterface', $controller); - } - } -} diff --git a/tests/classes/ACLTest.php b/tests/classes/ACLTest.php index e259569347..11ee12c1eb 100644 --- a/tests/classes/ACLTest.php +++ b/tests/classes/ACLTest.php @@ -29,22 +29,30 @@ class ACLTest extends \PhraseanetTestCase $this->assertTrue($this->object->has_status_access_to_record(self::$DI['record_1'])); } - public function testHasAccesToRecordStatus() + public function testHasAccessToRecordStatus() { - self::$DI['record_1']->set_binary_status(str_repeat('0', 32)); - $this->object->set_masks_on_base(self::$DI['record_1']->get_base_id(), '10000', '10000', '0', '0'); - self::$DI['record_1']->set_binary_status('10000'); - $this->assertFalse($this->object->has_status_access_to_record(self::$DI['record_1'])); - self::$DI['record_1']->set_binary_status('00000'); - $this->assertTrue($this->object->has_status_access_to_record(self::$DI['record_1'])); - $this->object->set_masks_on_base(self::$DI['record_1']->get_base_id(), '10000', '10000', '10000', '10000'); - $this->assertFalse($this->object->has_status_access_to_record(self::$DI['record_1'])); - self::$DI['record_1']->set_binary_status('10000'); - $this->assertTrue($this->object->has_status_access_to_record(self::$DI['record_1'])); - $this->object->set_masks_on_base(self::$DI['record_1']->get_base_id(), '0', '0', '0', '0'); - $this->assertTrue($this->object->has_status_access_to_record(self::$DI['record_1'])); - self::$DI['record_1']->set_binary_status(str_repeat('0', 32)); - $this->assertTrue($this->object->has_status_access_to_record(self::$DI['record_1'])); + $record1 = $this->getRecord1(); + + $record1->setStatus(str_repeat('0', 32)); + $this->object->set_masks_on_base($record1->getBaseId(), '10000', '10000', '0', '0'); + + $record1->setStatus('10000'); + $this->assertFalse($this->object->has_status_access_to_record($record1)); + + $record1->setStatus('00000'); + $this->assertTrue($this->object->has_status_access_to_record($record1)); + + $this->object->set_masks_on_base($record1->getBaseId(), '10000', '10000', '10000', '10000'); + $this->assertFalse($this->object->has_status_access_to_record($record1)); + + $record1->setStatus('10000'); + $this->assertTrue($this->object->has_status_access_to_record($record1)); + + $this->object->set_masks_on_base($record1->getBaseId(), '0', '0', '0', '0'); + $this->assertTrue($this->object->has_status_access_to_record($record1)); + + $record1->setStatus(str_repeat('0', 32)); + $this->assertTrue($this->object->has_status_access_to_record($record1)); } public function testHasAccesToRecordFailsOnBase() diff --git a/tests/classes/Bridge/ElementTest.php b/tests/classes/Bridge/ElementTest.php index 84644a8143..bb8fef2032 100644 --- a/tests/classes/Bridge/ElementTest.php +++ b/tests/classes/Bridge/ElementTest.php @@ -70,8 +70,8 @@ class Bridge_ElementTest extends \PhraseanetTestCase public function testGet_record() { $this->assertInstanceOf('record_adapter', $this->object->get_record()); - $this->assertEquals(self::$DI['record_1']->get_sbas_id(), $this->object->get_record()->get_sbas_id()); - $this->assertEquals(self::$DI['record_1']->get_record_id(), $this->object->get_record()->get_record_id()); + $this->assertEquals(self::$DI['record_1']->get_sbas_id(), $this->object->get_record()->getDataboxId()); + $this->assertEquals(self::$DI['record_1']->get_record_id(), $this->object->get_record()->getRecordId()); } public function testGet_dist_id() diff --git a/tests/classes/PhraseanetAuthenticatedWebTestCase.php b/tests/classes/PhraseanetAuthenticatedWebTestCase.php index ed839881ba..e5d002f164 100644 --- a/tests/classes/PhraseanetAuthenticatedWebTestCase.php +++ b/tests/classes/PhraseanetAuthenticatedWebTestCase.php @@ -66,7 +66,7 @@ abstract class PhraseanetAuthenticatedWebTestCase extends \PhraseanetAuthenticat 'is_admin' => $returnBool, 'give_access_to_sbas' => $returnSelf, 'update_rights_to_sbas' => $returnSelf, - 'update_rights_to_bas' => $returnSelf, + 'update_rights_to_base' => $returnSelf, 'has_right_on_base' => $returnBool, 'has_right_on_sbas' => $returnBool, 'has_access_to_sbas' => $returnBool, @@ -202,8 +202,8 @@ abstract class PhraseanetAuthenticatedWebTestCase extends \PhraseanetAuthenticat $app = $this->getApplication(); $elasticsearchRecord = new ElasticsearchRecord(); - $elasticsearchRecord->setDataboxId($record->get_sbas_id()); - $elasticsearchRecord->setRecordId($record->get_record_id()); + $elasticsearchRecord->setDataboxId($record->getDataboxId()); + $elasticsearchRecord->setRecordId($record->getRecordId()); $result = new SearchEngineResult( new SearchEngineOptions(), @@ -221,10 +221,8 @@ abstract class PhraseanetAuthenticatedWebTestCase extends \PhraseanetAuthenticat ); $searchEngine = $this->prophesize(SearchEngineInterface::class); - $searchEngine->query('', 0, Argument::any(), Argument::any()) + $searchEngine->query('', Argument::any()) ->willReturn($result); - $searchEngine->excerpt(Argument::any(), Argument::any(), Argument::any(), Argument::any()) - ->willReturn([]); $app['search_engine'] = $searchEngine->reveal(); return $app; diff --git a/tests/classes/PhraseanetTestCase.php b/tests/classes/PhraseanetTestCase.php index 32440a1549..8ae9183b8e 100644 --- a/tests/classes/PhraseanetTestCase.php +++ b/tests/classes/PhraseanetTestCase.php @@ -234,7 +234,7 @@ abstract class PhraseanetTestCase extends WebTestCase $file = new File(self::$DI['app'], self::$DI['app']['mediavorus']->guess(__DIR__ . '/../files/cestlafete.jpg'), self::$DI['collection_no_access']); $record = record_adapter::createFromFile($file, self::$DI['app']); self::$DI['app']['subdef.generator']->generateSubdefs($record); - self::$fixtureIds['records'][$id] = $record->get_record_id(); + self::$fixtureIds['records'][$id] = $record->getRecordId(); return self::$fixtureIds['records'][$id]; }); @@ -250,7 +250,7 @@ abstract class PhraseanetTestCase extends WebTestCase $file = new File(self::$DI['app'], self::$DI['app']['mediavorus']->guess(__DIR__ . '/../files/cestlafete.jpg'), self::$DI['collection_no_access_by_status']); $record = record_adapter::createFromFile($file, self::$DI['app']); self::$DI['app']['subdef.generator']->generateSubdefs($record); - self::$fixtureIds['records'][$id] = $record->get_record_id(); + self::$fixtureIds['records'][$id] = $record->getRecordId(); return self::$fixtureIds['records'][$id]; }); @@ -790,9 +790,6 @@ abstract class PhraseanetTestCase extends WebTestCase protected function createSearchEngineMock() { $mock = $this->getMock(SearchEngineInterface::class); - $mock->expects($this->any()) - ->method('createSubscriber') - ->will($this->returnValue($this->getMock('Symfony\Component\EventDispatcher\EventSubscriberInterface'))); $mock->expects($this->any()) ->method('getStatus') ->will($this->returnValue([])); diff --git a/tests/classes/media/subdefTest.php b/tests/classes/media/subdefTest.php index c489f103a8..f097da6a7b 100644 --- a/tests/classes/media/subdefTest.php +++ b/tests/classes/media/subdefTest.php @@ -28,13 +28,14 @@ class media_subdefTest extends \PhraseanetTestCase parent::setUp(); if (null === self::$recordonbleu) { - $file = new File(self::$DI['app'], self::$DI['app']['mediavorus']->guess(__DIR__ . "/../../files/iphone_pic.jpg"), self::$DI['collection']); + $app = $this->getApplication(); + $file = new File($app, $app['mediavorus']->guess(__DIR__ . "/../../files/iphone_pic.jpg"), self::$DI['collection']); - self::$recordonbleu = record_adapter::createFromFile($file, self::$DI['app']); - self::$DI['app']['subdef.generator']->generateSubdefs(self::$recordonbleu); + self::$recordonbleu = record_adapter::createFromFile($file, $app); + $app['subdef.generator']->generateSubdefs(self::$recordonbleu); foreach (self::$recordonbleu->get_subdefs() as $subdef) { - if ($subdef->get_name() == 'document') { + if (!in_array($subdef->get_name(), ['thumbnail', 'preview'], true)) { continue; } @@ -80,10 +81,10 @@ class media_subdefTest extends \PhraseanetTestCase */ public function testGet_record() { - $this->assertEquals(self::$recordonbleu->get_record_id(), self::$objectNotPresent->get_record()->get_record_id()); - $this->assertEquals(self::$recordonbleu->get_record_id(), self::$objectPresent->get_record()->get_record_id()); - $this->assertEquals(self::$recordonbleu->get_sbas_id(), self::$objectNotPresent->get_record()->get_sbas_id()); - $this->assertEquals(self::$recordonbleu->get_sbas_id(), self::$objectPresent->get_record()->get_sbas_id()); + $this->assertEquals(self::$recordonbleu->getRecordId(), self::$objectNotPresent->get_record()->getRecordId()); + $this->assertEquals(self::$recordonbleu->getRecordId(), self::$objectPresent->get_record()->getRecordId()); + $this->assertEquals(self::$recordonbleu->getDataboxId(), self::$objectNotPresent->get_record()->getDataboxId()); + $this->assertEquals(self::$recordonbleu->getDataboxId(), self::$objectPresent->get_record()->getDataboxId()); } /** @@ -111,8 +112,8 @@ class media_subdefTest extends \PhraseanetTestCase */ public function testGet_record_id() { - $this->assertEquals(self::$recordonbleu->get_record_id(), self::$objectNotPresent->get_record()->get_record_id()); - $this->assertEquals(self::$recordonbleu->get_record_id(), self::$objectPresent->get_record()->get_record_id()); + $this->assertEquals(self::$recordonbleu->getRecordId(), self::$objectNotPresent->get_record()->getRecordId()); + $this->assertEquals(self::$recordonbleu->getRecordId(), self::$objectPresent->get_record()->getRecordId()); } /** @@ -140,8 +141,8 @@ class media_subdefTest extends \PhraseanetTestCase */ public function testGet_sbas_id() { - $this->assertEquals(self::$recordonbleu->get_sbas_id(), self::$objectNotPresent->get_record()->get_sbas_id()); - $this->assertEquals(self::$recordonbleu->get_sbas_id(), self::$objectPresent->get_record()->get_sbas_id()); + $this->assertEquals(self::$recordonbleu->getDataboxId(), self::$objectNotPresent->get_record()->getDataboxId()); + $this->assertEquals(self::$recordonbleu->getDataboxId(), self::$objectPresent->get_record()->getDataboxId()); } /** diff --git a/tests/classes/record/adapterTest.php b/tests/classes/record/adapterTest.php index 9728cb7da0..c0eafba115 100644 --- a/tests/classes/record/adapterTest.php +++ b/tests/classes/record/adapterTest.php @@ -21,7 +21,7 @@ class record_adapterTest extends \PhraseanetAuthenticatedTestCase // Reset thumbtitle in order to have consistent tests (testGet_title) if (!self::$thumbtitled) { - foreach ($this->getRecord1()->get_databox()->get_meta_structure() as $databox_field) { + foreach ($this->getRecord1()->getDatabox()->get_meta_structure() as $databox_field) { $databox_field->set_thumbtitle(false)->save(); } self::$thumbtitled = true; @@ -86,7 +86,7 @@ class record_adapterTest extends \PhraseanetAuthenticatedTestCase $this->getClient()->request( 'POST', $app['url_generator']->generate('prod_order_new'), [ - 'lst' => $this->getRecord1()->get_serialize_key(), + 'lst' => $this->getRecord1()->getId(), 'deadline' => '+10 minutes' ]); @@ -137,16 +137,16 @@ class record_adapterTest extends \PhraseanetAuthenticatedTestCase { $record_1 = $this->getRecord1(); try { - $record_1->set_type('jambon'); + $record_1->setType('jambon'); $this->fail(); } catch (Exception $e) { } - $old_type = $record_1->get_type(); - $record_1->set_type('video'); - $this->assertEquals('video', $record_1->get_type()); - $record_1->set_type($old_type); - $this->assertEquals($old_type, $record_1->get_type()); + $old_type = $record_1->getType(); + $record_1->setType('video'); + $this->assertEquals('video', $record_1->getType()); + $record_1->setType($old_type); + $this->assertEquals($old_type, $record_1->getType()); } public function testIs_grouping() @@ -158,17 +158,17 @@ class record_adapterTest extends \PhraseanetAuthenticatedTestCase public function testGet_base_id() { $record_1 = $this->getRecord1(); - $this->assertTrue(is_int($record_1->get_base_id())); - $this->assertEquals($this->getCollection()->get_base_id(), $record_1->get_base_id()); + $this->assertTrue(is_int($record_1->getBaseId())); + $this->assertEquals($this->getCollection()->get_base_id(), $record_1->getBaseId()); $record_story_1 = $this->getRecordStory1(); - $this->assertTrue(is_int($record_story_1->get_base_id())); - $this->assertEquals($this->getCollection()->get_base_id(), $record_story_1->get_base_id()); + $this->assertTrue(is_int($record_story_1->getBaseId())); + $this->assertEquals($this->getCollection()->get_base_id(), $record_story_1->getBaseId()); } public function testGet_record_id() { - $this->assertTrue(is_int($this->getRecord1()->get_record_id())); - $this->assertTrue(is_int($this->getRecordStory1()->get_record_id())); + $this->assertTrue(is_int($this->getRecord1()->getRecordId())); + $this->assertTrue(is_int($this->getRecordStory1()->getRecordId())); } public function testGet_thumbnail() @@ -187,7 +187,7 @@ class record_adapterTest extends \PhraseanetAuthenticatedTestCase public function testGet_type() { - $this->assertTrue(in_array($this->getRecord1()->get_type(), ['video', 'audio', 'image', 'document', 'flash', 'unknown'])); + $this->assertTrue(in_array($this->getRecord1()->getType(), ['video', 'audio', 'image', 'document', 'flash', 'unknown'])); } public function testGet_formatted_duration() @@ -208,31 +208,31 @@ class record_adapterTest extends \PhraseanetAuthenticatedTestCase public function testGet_sha256() { $record_1 = $this->getRecord1(); - $this->assertNotNull($record_1->get_sha256()); - $this->assertRegExp('/[a-zA-Z0-9]{32}/', $record_1->get_sha256()); - $this->assertNull($this->getRecordStory1()->get_sha256()); + $this->assertNotNull($record_1->getSha256()); + $this->assertRegExp('/[a-zA-Z0-9]{32}/', $record_1->getSha256()); + $this->assertNull($this->getRecordStory1()->getSha256()); } public function testGet_mime() { - $this->assertRegExp('/image\/\w+/', $this->getRecord1()->get_mime()); + $this->assertRegExp('/image\/\w+/', $this->getRecord1()->getMimeType()); } public function testSetMimeType() { $record_1 = $this->getRecord1(); - $oldMime = $record_1->get_mime(); - $record_1->set_mime('foo/bar'); - $this->assertEquals('foo/bar', $record_1->get_mime()); + $oldMime = $record_1->getMimeType(); + $record_1->setMimeType('foo/bar'); + $this->assertEquals('foo/bar', $record_1->getMimeType()); - $record_1->set_mime($oldMime); - $this->assertEquals($oldMime, $record_1->get_mime()); + $record_1->setMimeType($oldMime); + $this->assertEquals($oldMime, $record_1->getMimeType()); } public function testGet_status() { - $this->assertRegExp('/[01]{32}/', $this->getRecord1()->get_status()); + $this->assertRegExp('/[01]{32}/', $this->getRecord1()->getStatus()); } public function testGet_subdef() @@ -307,12 +307,12 @@ class record_adapterTest extends \PhraseanetAuthenticatedTestCase public function testGet_serialize_key() { $record_1 = $this->getRecord1(); - $this->assertTrue($record_1->get_serialize_key() == $record_1->get_sbas_id() . '_' . $record_1->get_record_id()); + $this->assertTrue($record_1->getId() == $record_1->getDataboxId() . '_' . $record_1->getRecordId()); } public function testGet_sbas_id() { - $this->assertTrue(is_int($this->getRecord1()->get_sbas_id())); + $this->assertTrue(is_int($this->getRecord1()->getDataboxId())); } public function testSet_metadatas() @@ -431,15 +431,15 @@ class record_adapterTest extends \PhraseanetAuthenticatedTestCase FROM record WHERE jeton & ' . JETON_MAKE_SUBDEF . ' > 0 AND record_id = :record_id'; - $stmt = $record_1->get_databox()->get_connection()->prepare($sql); + $stmt = $record_1->getDatabox()->get_connection()->prepare($sql); - $stmt->execute([':record_id' => $record_1->get_record_id()]); + $stmt->execute([':record_id' => $record_1->getRecordId()]); $row = $stmt->fetch(PDO::FETCH_ASSOC); $stmt->closeCursor(); if ( ! $row) $this->fail(); - if ($row['record_id'] != $record_1->get_record_id()) + if ($row['record_id'] != $record_1->getRecordId()) $this->fail(); } @@ -450,15 +450,15 @@ class record_adapterTest extends \PhraseanetAuthenticatedTestCase $sql = 'SELECT record_id, coll_id, jeton FROM record WHERE (jeton & ' . JETON_WRITE_META . ' > 0) AND record_id = :record_id'; - $stmt = $record_1->get_databox()->get_connection()->prepare($sql); + $stmt = $record_1->getDatabox()->get_connection()->prepare($sql); - $stmt->execute([':record_id' => $record_1->get_record_id()]); + $stmt->execute([':record_id' => $record_1->getRecordId()]); $row = $stmt->fetch(PDO::FETCH_ASSOC); $stmt->closeCursor(); if ( ! $row) $this->fail(); - if ($row['record_id'] != $record_1->get_record_id()) + if ($row['record_id'] != $record_1->getRecordId()) $this->fail(); } @@ -474,34 +474,34 @@ class record_adapterTest extends \PhraseanetAuthenticatedTestCase } $record_1 = $this->getRecord1(); - $record_1->set_binary_status($status); + $record_1->setStatus($status); - $this->assertEquals($status, $record_1->get_status()); + $this->assertEquals($status, $record_1->getStatus()); } public function testGet_record_by_sha() { $record_1 = $this->getRecord1(); - $tmp_records = record_adapter::get_record_by_sha($record_1->getDatabox(), $record_1->get_sha256()); + $tmp_records = record_adapter::get_record_by_sha($record_1->getDatabox(), $record_1->getSha256()); $this->assertTrue(is_array($tmp_records)); foreach ($tmp_records as $tmp_record) { $this->assertInstanceOf('record_adapter', $tmp_record); - $this->assertEquals($record_1->get_sha256(), $tmp_record->get_sha256()); + $this->assertEquals($record_1->getSha256(), $tmp_record->getSha256()); } $tmp_records = record_adapter::get_record_by_sha( $record_1->getDatabox(), - $record_1->get_sha256(), - $record_1->get_record_id() + $record_1->getSha256(), + $record_1->getRecordId() ); $this->assertTrue(is_array($tmp_records)); $this->assertTrue(count($tmp_records) === 1); foreach ($tmp_records as $tmp_record) { $this->assertInstanceOf('record_adapter', $tmp_record); - $this->assertEquals($record_1->get_sha256(), $tmp_record->get_sha256()); - $this->assertEquals($record_1->get_record_id(), $tmp_record->get_record_id()); + $this->assertEquals($record_1->getSha256(), $tmp_record->getSha256()); + $this->assertEquals($record_1->getRecordId(), $tmp_record->getRecordId()); } } @@ -518,8 +518,8 @@ class record_adapterTest extends \PhraseanetAuthenticatedTestCase $found = $sselcont_id = false; $record_1 = $this->getRecord1(); - $sbas_id = $record_1->get_sbas_id(); - $record_id = $record_1->get_record_id(); + $sbas_id = $record_1->getDataboxId(); + $record_id = $record_1->getRecordId(); foreach ($record_1->get_container_baskets($app['orm.em'], self::$DI['user']) as $c_basket) { if ($c_basket->getId() == $basket->getId()) { @@ -538,8 +538,8 @@ class record_adapterTest extends \PhraseanetAuthenticatedTestCase public function testSetStatus() { $record_1 = $this->getRecord1(); - $record = new \record_adapter($this->getApplication(), $record_1->get_sbas_id(), $record_1->get_record_id()); - $record->set_binary_status('1001001001010101'); - $this->assertSame('00000000000000001001001001010101', $record->get_status()); + $record = new \record_adapter($this->getApplication(), $record_1->getDataboxId(), $record_1->getRecordId()); + $record->setStatus('1001001001010101'); + $this->assertSame('00000000000000001001001001010101', $record->getStatus()); } } diff --git a/tests/classes/recordutils/imageTest.php b/tests/classes/recordutils/imageTest.php index 2ab653e927..e27503f3a0 100644 --- a/tests/classes/recordutils/imageTest.php +++ b/tests/classes/recordutils/imageTest.php @@ -17,7 +17,7 @@ class recordutils_imageTest extends \PhraseanetTestCase $app->getApplicationBox()->write_collection_pic( $app['media-alchemyst'], $app['filesystem'], - $record_1->get_collection(), + $record_1->getCollection(), null, \collection::PIC_WM ); @@ -36,7 +36,7 @@ class recordutils_imageTest extends \PhraseanetTestCase $app->getApplicationBox()->write_collection_pic( $app['media-alchemyst'], $app['filesystem'], - $record_1->get_collection(), + $record_1->getCollection(), new SymfoFile(__DIR__ . '/../../files/logocoll.gif'), \collection::PIC_WM ); @@ -82,13 +82,13 @@ class recordutils_imageTest extends \PhraseanetTestCase { /** @var record_adapter $record_1 */ $record_1 = self::$DI['record_1']; - $this->addStampConf($record_1->get_collection()); + $this->addStampConf($record_1->getCollection()); $app = $this->getApplication(); $app->getApplicationBox()->write_collection_pic( $app['media-alchemyst'], $app['filesystem'], - $record_1->get_collection(), + $record_1->getCollection(), null, \collection::PIC_STAMP ); @@ -103,13 +103,13 @@ class recordutils_imageTest extends \PhraseanetTestCase { /** @var record_adapter $record_1 */ $record_1 = self::$DI['record_1']; - $this->addStampConf($record_1->get_collection()); + $this->addStampConf($record_1->getCollection()); $app = $this->getApplication(); $app->getApplicationBox()->write_collection_pic( $app['media-alchemyst'], $app['filesystem'], - $record_1->get_collection(), + $record_1->getCollection(), new SymfoFile(__DIR__ . '/../../files/logocoll.gif'), \collection::PIC_STAMP ); diff --git a/www/scripts/apps/admin/main/views/rightPanel.js b/www/scripts/apps/admin/main/views/rightPanel.js index 89b15fc828..84f70b95d0 100644 --- a/www/scripts/apps/admin/main/views/rightPanel.js +++ b/www/scripts/apps/admin/main/views/rightPanel.js @@ -73,11 +73,11 @@ define([ event.preventDefault(); var $this = this; var link = $(event.currentTarget); - var url = link.attr('action') || 'GET'; + var url = link.attr('action'); if(url) { $.ajax({ - type: link.attr('method'), + type: link.attr('method') || 'GET', url: url, data: link.serializeArray(), success: function (data) { diff --git a/www/scripts/apps/admin/search-engine/views/es_config.js b/www/scripts/apps/admin/search-engine/views/es_config.js new file mode 100644 index 0000000000..23ba10f4ee --- /dev/null +++ b/www/scripts/apps/admin/search-engine/views/es_config.js @@ -0,0 +1,39 @@ +function searchEngineConfigurationFormInit(indexExists) { + $("#dropIndexConfirmDialog").dialog({ + autoOpen: false, + modal: true, + title: "Drop index", + buttons: [ + { + text: "Ok", + click: function () { + $("#ElasticSearchDropIndexForm").submit(); + $("#dropIndexConfirmDialog").dialog("close"); + } + }, + { + text: "Cancel", + click: function () { + $("#dropIndexConfirmDialog").dialog("close"); + } + } + ] + }); + + if(indexExists) { + $("BUTTON[data-id=esSettingsCreateIndexButton]").hide(); + $("BUTTON[data-id=esSettingsDropIndexButton]").show().bind("click", function (event) { + event.preventDefault(); + $("#dropIndexConfirmDialog").dialog("open"); + return false; + }); + } + else { + $("BUTTON[data-id=esSettingsDropIndexButton]").hide(); + $("BUTTON[data-id=esSettingsCreateIndexButton]").show().bind("click", function (event) { + event.preventDefault(); + $("#ElasticSearchCreateIndexForm").submit(); + return false; + }); + } +}