mirror of
https://github.com/alchemy-fr/Phraseanet.git
synced 2025-10-15 14:03:27 +00:00
@@ -25,6 +25,10 @@
|
||||
{
|
||||
"type": "vcs",
|
||||
"url": "https://github.com/alchemy-fr/embed-bundle.git"
|
||||
},
|
||||
{
|
||||
"type": "git",
|
||||
"url": "https://github.com/bburnichon/fractal.git"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
@@ -38,7 +42,7 @@
|
||||
"alchemy/oauth2php": "1.0.0",
|
||||
"alchemy/phlickr": "0.2.9",
|
||||
"alchemy/phpexiftool": "^0.5.0",
|
||||
"alchemy/rest-bundle": "^0.0.4",
|
||||
"alchemy/rest-bundle": "^0.0.5",
|
||||
"alchemy/symfony-cors": "^0.1.0",
|
||||
"alchemy/task-manager": "2.0.x-dev@dev",
|
||||
"alchemy/zippy": "^0.3.0",
|
||||
@@ -69,6 +73,7 @@
|
||||
"justinrainbow/json-schema": "~1.3",
|
||||
"league/flysystem": "^1.0",
|
||||
"league/flysystem-aws-s3-v2": "^1.0",
|
||||
"league/fractal": "dev-bug/null-resource-serialization#891856f as 0.13.0",
|
||||
"media-alchemyst/media-alchemyst": "^0.5",
|
||||
"monolog/monolog": "~1.3",
|
||||
"mrclay/minify": "~2.1.6",
|
||||
|
99
composer.lock
generated
99
composer.lock
generated
@@ -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": "2f0d3c97831221555b3c1d1a725f897e",
|
||||
"content-hash": "898155bdbf7333a2634c4b04053469ea",
|
||||
"hash": "685ed8f8578c211ee14ebc00703ab281",
|
||||
"content-hash": "acd719252dc8e17e752f2e87f571a7b5",
|
||||
"packages": [
|
||||
{
|
||||
"name": "alchemy-fr/tcpdf-clone",
|
||||
@@ -15,12 +15,6 @@
|
||||
"url": "https://github.com/alchemy-fr/tcpdf-clone.git",
|
||||
"reference": "2ba0248a7187f1626df6c128750650416267f0e7"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/alchemy-fr/tcpdf-clone/zipball/2ba0248a7187f1626df6c128750650416267f0e7",
|
||||
"reference": "2ba0248a7187f1626df6c128750650416267f0e7",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.0"
|
||||
},
|
||||
@@ -67,10 +61,6 @@
|
||||
"qrcode",
|
||||
"tcpdf"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/alchemy-fr/tcpdf-clone/tree/6.0.039",
|
||||
"issues": "https://github.com/alchemy-fr/tcpdf-clone/issues"
|
||||
},
|
||||
"time": "2013-10-13 16:11:17"
|
||||
},
|
||||
{
|
||||
@@ -489,20 +479,21 @@
|
||||
},
|
||||
{
|
||||
"name": "alchemy/rest-bundle",
|
||||
"version": "0.0.4",
|
||||
"version": "0.0.5",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/alchemy-fr/rest-bundle.git",
|
||||
"reference": "9048a99dd328cd2d01efaad16e6af648d11ad2b4"
|
||||
"reference": "e795b3cd565086d575ee919d1b23279656c982ad"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/alchemy-fr/rest-bundle/zipball/9048a99dd328cd2d01efaad16e6af648d11ad2b4",
|
||||
"reference": "9048a99dd328cd2d01efaad16e6af648d11ad2b4",
|
||||
"url": "https://api.github.com/repos/alchemy-fr/rest-bundle/zipball/e795b3cd565086d575ee919d1b23279656c982ad",
|
||||
"reference": "e795b3cd565086d575ee919d1b23279656c982ad",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"league/fractal": "^0.12.0",
|
||||
"league/fractal": "^0.12.0|^0.13.0",
|
||||
"php": ">=5.4",
|
||||
"willdurand/negotiation": "~2.0@dev"
|
||||
},
|
||||
"require-dev": {
|
||||
@@ -536,7 +527,7 @@
|
||||
}
|
||||
],
|
||||
"description": "Simple REST utility bundle",
|
||||
"time": "2016-02-20 22:35:16"
|
||||
"time": "2016-05-16 09:37:34"
|
||||
},
|
||||
{
|
||||
"name": "alchemy/symfony-cors",
|
||||
@@ -546,12 +537,6 @@
|
||||
"url": "https://github.com/alchemy-fr/symfony-cors.git",
|
||||
"reference": "dbf7fcff1ce9fc1265db12955476ff169eab7375"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/alchemy-fr/symfony-cors/zipball/dbf7fcff1ce9fc1265db12955476ff169eab7375",
|
||||
"reference": "dbf7fcff1ce9fc1265db12955476ff169eab7375",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"symfony/http-kernel": "^2.3.0|^3.0.0"
|
||||
},
|
||||
@@ -572,7 +557,11 @@
|
||||
"Alchemy\\CorsBundle\\": "src/Bundle/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Alchemy\\Cors\\Tests\\": "tests/unit/Component/"
|
||||
}
|
||||
},
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
@@ -1801,12 +1790,12 @@
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/igorw/evenement.git",
|
||||
"reference": "fa966683e7df3e5dd5929d984a44abfbd6bafe8d"
|
||||
"reference": "v1.0.0"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/igorw/evenement/zipball/fa966683e7df3e5dd5929d984a44abfbd6bafe8d",
|
||||
"reference": "fa966683e7df3e5dd5929d984a44abfbd6bafe8d",
|
||||
"url": "https://api.github.com/repos/igorw/evenement/zipball/v1.0.0",
|
||||
"reference": "v1.0.0",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -1833,7 +1822,7 @@
|
||||
"keywords": [
|
||||
"event-dispatcher"
|
||||
],
|
||||
"time": "2012-05-30 15:01:08"
|
||||
"time": "2012-05-30 08:01:08"
|
||||
},
|
||||
{
|
||||
"name": "facebook/php-sdk",
|
||||
@@ -2822,12 +2811,12 @@
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/hoaproject/Stream.git",
|
||||
"reference": "3bc446bc00849bf51166adc415d77aa375d48d8c"
|
||||
"reference": "011ab91d942f1d7096deade4c8a10fe57d51c5b3"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/hoaproject/Stream/zipball/3bc446bc00849bf51166adc415d77aa375d48d8c",
|
||||
"reference": "3bc446bc00849bf51166adc415d77aa375d48d8c",
|
||||
"url": "https://api.github.com/repos/hoaproject/Stream/zipball/011ab91d942f1d7096deade4c8a10fe57d51c5b3",
|
||||
"reference": "011ab91d942f1d7096deade4c8a10fe57d51c5b3",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -2872,7 +2861,7 @@
|
||||
"stream",
|
||||
"wrapper"
|
||||
],
|
||||
"time": "2015-10-26 12:21:43"
|
||||
"time": "2015-10-22 06:30:43"
|
||||
},
|
||||
{
|
||||
"name": "hoa/ustring",
|
||||
@@ -3660,17 +3649,11 @@
|
||||
},
|
||||
{
|
||||
"name": "league/fractal",
|
||||
"version": "0.12.0",
|
||||
"version": "dev-bug/null-resource-serialization",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/thephpleague/fractal.git",
|
||||
"reference": "0a51dcb6398dc2377d086210d0624996a1df8322"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/thephpleague/fractal/zipball/0a51dcb6398dc2377d086210d0624996a1df8322",
|
||||
"reference": "0a51dcb6398dc2377d086210d0624996a1df8322",
|
||||
"shasum": ""
|
||||
"url": "https://github.com/bburnichon/fractal.git",
|
||||
"reference": "891856f"
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.4"
|
||||
@@ -3699,7 +3682,11 @@
|
||||
"League\\Fractal\\": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"League\\Fractal\\Test\\": "test"
|
||||
}
|
||||
},
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
@@ -3719,7 +3706,7 @@
|
||||
"league",
|
||||
"rest"
|
||||
],
|
||||
"time": "2015-03-19 15:16:43"
|
||||
"time": "2016-05-16 16:41:03"
|
||||
},
|
||||
{
|
||||
"name": "media-alchemyst/media-alchemyst",
|
||||
@@ -3903,7 +3890,7 @@
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Steve Clay",
|
||||
"name": "Stephen Clay",
|
||||
"email": "steve@mrclay.org",
|
||||
"homepage": "http://www.mrclay.org/",
|
||||
"role": "Developer"
|
||||
@@ -4089,21 +4076,21 @@
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/romainneutron/Imagine-Silex-Service-Provider.git",
|
||||
"reference": "a8a7862ae90419f2b23746cd8436c2310e4eb084"
|
||||
"reference": "0.1.2"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/romainneutron/Imagine-Silex-Service-Provider/zipball/a8a7862ae90419f2b23746cd8436c2310e4eb084",
|
||||
"reference": "a8a7862ae90419f2b23746cd8436c2310e4eb084",
|
||||
"url": "https://api.github.com/repos/romainneutron/Imagine-Silex-Service-Provider/zipball/0.1.2",
|
||||
"reference": "0.1.2",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"imagine/imagine": "*",
|
||||
"php": ">=5.3.3",
|
||||
"silex/silex": "~1.0"
|
||||
"silex/silex": ">=1.0,<2.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/browser-kit": "~2.0"
|
||||
"symfony/browser-kit": ">=2.0,<3.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
@@ -5576,7 +5563,7 @@
|
||||
},
|
||||
{
|
||||
"name": "Phraseanet Team",
|
||||
"email": "info@alchemy.fr",
|
||||
"email": "support@alchemy.fr",
|
||||
"homepage": "http://www.phraseanet.com/"
|
||||
}
|
||||
],
|
||||
@@ -7728,12 +7715,20 @@
|
||||
"time": "2015-06-21 13:59:46"
|
||||
}
|
||||
],
|
||||
"aliases": [],
|
||||
"aliases": [
|
||||
{
|
||||
"alias": "0.13.0",
|
||||
"alias_normalized": "0.13.0.0",
|
||||
"version": "dev-bug/null-resource-serialization",
|
||||
"package": "league/fractal"
|
||||
}
|
||||
],
|
||||
"minimum-stability": "stable",
|
||||
"stability-flags": {
|
||||
"alchemy/task-manager": 20,
|
||||
"imagine/imagine": 20,
|
||||
"jms/translation-bundle": 20,
|
||||
"league/fractal": 20,
|
||||
"neutron/process-manager": 20,
|
||||
"roave/security-advisories": 20,
|
||||
"willdurand/negotiation": 15
|
||||
|
@@ -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\Caption\CaptionServiceProvider;
|
||||
use Alchemy\Phrasea\Databox\Subdef\MediaSubdefServiceProvider;
|
||||
use Alchemy\Phrasea\Exception\InvalidArgumentException;
|
||||
use Alchemy\Phrasea\Filesystem\FilesystemServiceProvider;
|
||||
@@ -194,6 +195,7 @@ class Application extends SilexApplication
|
||||
$this->register(new ManipulatorServiceProvider());
|
||||
$this->register(new TechnicalDataServiceProvider());
|
||||
$this->register(new MediaSubdefServiceProvider());
|
||||
$this->register(new CaptionServiceProvider());
|
||||
$this->register(new InstallerServiceProvider());
|
||||
$this->register(new PhraseaVersionServiceProvider());
|
||||
|
||||
|
@@ -11,11 +11,16 @@
|
||||
namespace Alchemy\Phrasea\Controller\Api;
|
||||
|
||||
use Alchemy\Phrasea\Controller\Controller;
|
||||
use Alchemy\Phrasea\Fractal\ArraySerializer;
|
||||
use Alchemy\Phrasea\Model\Manipulator\UserManipulator;
|
||||
use Alchemy\Phrasea\Search\SearchResultView;
|
||||
use Alchemy\Phrasea\Search\V2SearchTransformer;
|
||||
use Alchemy\Phrasea\SearchEngine\SearchEngineInterface;
|
||||
use Alchemy\Phrasea\SearchEngine\SearchEngineLogger;
|
||||
use Alchemy\Phrasea\SearchEngine\SearchEngineOptions;
|
||||
use Alchemy\Phrasea\SearchEngine\SearchEngineResult;
|
||||
use League\Fractal\Manager;
|
||||
use League\Fractal\Resource\Item;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
@@ -28,25 +33,21 @@ class SearchController extends Controller
|
||||
*/
|
||||
public function searchAction(Request $request)
|
||||
{
|
||||
list($ret, $search_result) = $this->searchAndFormatEngineResult($request);
|
||||
$fractal = new Manager();
|
||||
$fractal->setSerializer(new ArraySerializer());
|
||||
$fractal->parseIncludes([]);
|
||||
|
||||
/** @var SearchEngineResult $search_result */
|
||||
$ret['search_type'] = $search_result->getOptions()->getSearchType();
|
||||
$ret['results'] = [];
|
||||
|
||||
foreach ($search_result->getResults() as $record) {
|
||||
$ret['results'][] = [
|
||||
'databox_id' => $record->getDataboxId(),
|
||||
'record_id' => $record->getRecordId(),
|
||||
'collection_id' => $record->getCollectionId(),
|
||||
'version' => $record->getUpdated()->getTimestamp(),
|
||||
];
|
||||
}
|
||||
$searchView = new SearchResultView($this->doSearch($request));
|
||||
$ret = $fractal->createData(new Item($searchView, new V2SearchTransformer()))->toArray();
|
||||
|
||||
return Result::create($request, $ret)->createResponse();
|
||||
}
|
||||
|
||||
private function searchAndFormatEngineResult(Request $request)
|
||||
/**
|
||||
* @param Request $request
|
||||
* @return SearchEngineResult
|
||||
*/
|
||||
private function doSearch(Request $request)
|
||||
{
|
||||
$options = SearchEngineOptions::fromRequest($this->app, $request);
|
||||
$options->setFirstResult($request->get('offset_start') ?: 0);
|
||||
@@ -55,9 +56,9 @@ class SearchController extends Controller
|
||||
$query = (string) $request->get('query');
|
||||
$this->getSearchEngine()->resetCache();
|
||||
|
||||
$search_result = $this->getSearchEngine()->query($query, $options);
|
||||
$result = $this->getSearchEngine()->query($query, $options);
|
||||
|
||||
$this->getUserManipulator()->logQuery($this->getAuthenticatedUser(), $search_result->getQuery());
|
||||
$this->getUserManipulator()->logQuery($this->getAuthenticatedUser(), $result->getQuery());
|
||||
|
||||
foreach ($options->getDataboxes() as $databox) {
|
||||
$colls = array_map(function (\collection $collection) {
|
||||
@@ -67,25 +68,12 @@ class SearchController extends Controller
|
||||
}));
|
||||
|
||||
$this->getSearchEngineLogger()
|
||||
->log($databox, $search_result->getQuery(), $search_result->getTotal(), $colls);
|
||||
->log($databox, $result->getQuery(), $result->getTotal(), $colls);
|
||||
}
|
||||
|
||||
$this->getSearchEngine()->clearCache();
|
||||
|
||||
$ret = [
|
||||
'offset_start' => $options->getFirstResult(),
|
||||
'per_page' => $options->getMaxResults(),
|
||||
'available_results' => $search_result->getAvailable(),
|
||||
'total_results' => $search_result->getTotal(),
|
||||
'error' => (string)$search_result->getError(),
|
||||
'warning' => (string)$search_result->getWarning(),
|
||||
'query_time' => $search_result->getDuration(),
|
||||
'search_indexes' => $search_result->getIndexes(),
|
||||
'facets' => $search_result->getFacets(),
|
||||
'results' => [],
|
||||
];
|
||||
|
||||
return [$ret, $search_result];
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -29,9 +29,15 @@ use Alchemy\Phrasea\Controller\Controller;
|
||||
use Alchemy\Phrasea\Core\Event\RecordEdit;
|
||||
use Alchemy\Phrasea\Core\PhraseaEvents;
|
||||
use Alchemy\Phrasea\Core\Version;
|
||||
use Alchemy\Phrasea\Databox\DataboxGroupable;
|
||||
use Alchemy\Phrasea\Feed\Aggregate;
|
||||
use Alchemy\Phrasea\Feed\FeedInterface;
|
||||
use Alchemy\Phrasea\Form\Login\PhraseaRenewPasswordForm;
|
||||
use Alchemy\Phrasea\Fractal\ArraySerializer;
|
||||
use Alchemy\Phrasea\Fractal\CallbackTransformer;
|
||||
use Alchemy\Phrasea\Fractal\IncludeResolver;
|
||||
use Alchemy\Phrasea\Fractal\SearchResultTransformerResolver;
|
||||
use Alchemy\Phrasea\Fractal\TraceableArraySerializer;
|
||||
use Alchemy\Phrasea\Model\Entities\ApiOauthToken;
|
||||
use Alchemy\Phrasea\Model\Entities\Basket;
|
||||
use Alchemy\Phrasea\Model\Entities\BasketElement;
|
||||
@@ -53,16 +59,32 @@ use Alchemy\Phrasea\Model\Repositories\FeedEntryRepository;
|
||||
use Alchemy\Phrasea\Model\Repositories\FeedRepository;
|
||||
use Alchemy\Phrasea\Model\Repositories\LazaretFileRepository;
|
||||
use Alchemy\Phrasea\Model\Repositories\TaskRepository;
|
||||
use Alchemy\Phrasea\Record\RecordCollection;
|
||||
use Alchemy\Phrasea\Record\RecordReferenceCollection;
|
||||
use Alchemy\Phrasea\Search\CaptionView;
|
||||
use Alchemy\Phrasea\Search\PermalinkTransformer;
|
||||
use Alchemy\Phrasea\Search\PermalinkView;
|
||||
use Alchemy\Phrasea\Search\RecordTransformer;
|
||||
use Alchemy\Phrasea\Search\RecordView;
|
||||
use Alchemy\Phrasea\Search\SearchResultView;
|
||||
use Alchemy\Phrasea\Search\StoryTransformer;
|
||||
use Alchemy\Phrasea\Search\StoryView;
|
||||
use Alchemy\Phrasea\Search\SubdefTransformer;
|
||||
use Alchemy\Phrasea\Search\SubdefView;
|
||||
use Alchemy\Phrasea\Search\TechnicalDataTransformer;
|
||||
use Alchemy\Phrasea\Search\TechnicalDataView;
|
||||
use Alchemy\Phrasea\Search\V1SearchCompositeResultTransformer;
|
||||
use Alchemy\Phrasea\Search\V1SearchRecordsResultTransformer;
|
||||
use Alchemy\Phrasea\Search\V1SearchResultTransformer;
|
||||
use Alchemy\Phrasea\SearchEngine\SearchEngineInterface;
|
||||
use Alchemy\Phrasea\SearchEngine\SearchEngineLogger;
|
||||
use Alchemy\Phrasea\SearchEngine\SearchEngineOptions;
|
||||
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 League\Fractal\Resource\Item;
|
||||
use Symfony\Component\Form\Form;
|
||||
use Symfony\Component\HttpFoundation\File\UploadedFile;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
@@ -295,7 +317,7 @@ class V1Controller extends Controller
|
||||
'active' => $conf->get(['main', 'bridge', 'dailymotion', 'enabled']),
|
||||
'clientId' => $conf->get(['main', 'bridge', 'dailymotion', 'client_id']),
|
||||
'clientSecret' => $conf->get(['main', 'bridge', 'dailymotion', 'client_secret']),
|
||||
]
|
||||
],
|
||||
],
|
||||
'navigator' => ['active' => $conf->get(['registry', 'api-clients', 'navigator-enabled']),],
|
||||
'office-plugin' => ['active' => $conf->get(['registry', 'api-clients', 'office-enabled']),],
|
||||
@@ -377,7 +399,7 @@ class V1Controller extends Controller
|
||||
'validationReminder' => $conf->get(['registry', 'actions', 'validation-reminder-days']),
|
||||
'expirationValue' => $conf->get(['registry', 'actions', 'validation-expiration-days']),
|
||||
],
|
||||
]
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
@@ -444,7 +466,7 @@ class V1Controller extends Controller
|
||||
public function getDataboxCollectionsAction(Request $request, $databox_id)
|
||||
{
|
||||
$ret = [
|
||||
"collections" => $this->listDataboxCollections($this->findDataboxById($databox_id))
|
||||
"collections" => $this->listDataboxCollections($this->findDataboxById($databox_id)),
|
||||
];
|
||||
|
||||
return Result::create($request, $ret)->createResponse();
|
||||
@@ -523,7 +545,7 @@ class V1Controller extends Controller
|
||||
$ret = [
|
||||
"document_metadatas" => $this->listDataboxMetadataFields(
|
||||
$this->findDataboxById($databox_id)->get_meta_structure()
|
||||
)
|
||||
),
|
||||
];
|
||||
|
||||
return Result::create($request, $ret)->createResponse();
|
||||
@@ -740,7 +762,7 @@ class V1Controller extends Controller
|
||||
'databox_id' => $base->get_sbas_id(),
|
||||
'base_id' => $base->get_base_id(),
|
||||
'collection_id' => $base->get_coll_id(),
|
||||
'rights' => $baseGrants
|
||||
'rights' => $baseGrants,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -788,7 +810,7 @@ class V1Controller extends Controller
|
||||
$service = $this->getAccountService();
|
||||
$command = new UpdatePasswordCommand();
|
||||
$form = $this->app->form(new PhraseaRenewPasswordForm(), $command, [
|
||||
'csrf_protection' => false
|
||||
'csrf_protection' => false,
|
||||
]);
|
||||
|
||||
$form->handleRequest($request);
|
||||
@@ -1035,24 +1057,48 @@ class V1Controller extends Controller
|
||||
*/
|
||||
public function searchAction(Request $request)
|
||||
{
|
||||
list($ret, $search_result) = $this->prepareSearchRequest($request);
|
||||
$subdefTransformer = new SubdefTransformer($this->app['acl'], $this->getAuthenticatedUser(), new PermalinkTransformer());
|
||||
$technicalDataTransformer = new TechnicalDataTransformer();
|
||||
$recordTransformer = new RecordTransformer($subdefTransformer, $technicalDataTransformer);
|
||||
$storyTransformer = new StoryTransformer($subdefTransformer, $recordTransformer);
|
||||
$compositeTransformer = new V1SearchCompositeResultTransformer($recordTransformer, $storyTransformer);
|
||||
$searchTransformer = new V1SearchResultTransformer($compositeTransformer);
|
||||
|
||||
$records = [];
|
||||
$stories = [];
|
||||
$transformerResolver = new SearchResultTransformerResolver([
|
||||
'' => $searchTransformer,
|
||||
'results' => $compositeTransformer,
|
||||
'results.stories' => $storyTransformer,
|
||||
'results.stories.thumbnail' => $subdefTransformer,
|
||||
'results.stories.metadatas' => new CallbackTransformer(),
|
||||
'results.stories.records' => $recordTransformer,
|
||||
'results.stories.records.thumbnail' => $subdefTransformer,
|
||||
'results.stories.records.technical_informations' => $technicalDataTransformer,
|
||||
'results.stories.records.subdefs' => $subdefTransformer,
|
||||
'results.stories.records.metadata' => new CallbackTransformer(),
|
||||
'results.stories.records.status' => new CallbackTransformer(),
|
||||
'results.stories.records.caption' => new CallbackTransformer(),
|
||||
'results.records' => $recordTransformer,
|
||||
'results.records.thumbnail' => $subdefTransformer,
|
||||
'results.records.technical_informations' => $technicalDataTransformer,
|
||||
'results.records.subdefs' => $subdefTransformer,
|
||||
'results.records.metadata' => new CallbackTransformer(),
|
||||
'results.records.status' => new CallbackTransformer(),
|
||||
'results.records.caption' => new CallbackTransformer(),
|
||||
]);
|
||||
$includeResolver = new IncludeResolver($transformerResolver);
|
||||
|
||||
/** @var SearchEngineResult $search_result */
|
||||
foreach ($search_result->getResults() as $record) {
|
||||
if ($record->isStory()) {
|
||||
$stories[] = $record;
|
||||
} else {
|
||||
$records[] = $record;
|
||||
}
|
||||
}
|
||||
$fractal = new \League\Fractal\Manager();
|
||||
$fractal->setSerializer(new TraceableArraySerializer($this->app['dispatcher']));
|
||||
$fractal->parseIncludes($this->resolveSearchIncludes($request));
|
||||
|
||||
$ret['results'] = [
|
||||
'records' => $this->listRecords($request, $records),
|
||||
'stories' => $this->listStories($request, $stories),
|
||||
];
|
||||
$result = $this->doSearch($request);
|
||||
$searchView = $this->buildSearchView(
|
||||
$result,
|
||||
$includeResolver->resolve($fractal),
|
||||
$this->resolveSubdefUrlTTL($request)
|
||||
);
|
||||
|
||||
$ret = $fractal->createData(new Item($searchView, $searchTransformer))->toArray();
|
||||
|
||||
return Result::create($request, $ret)->createResponse();
|
||||
}
|
||||
@@ -1068,33 +1114,330 @@ class V1Controller extends Controller
|
||||
*/
|
||||
public function searchRecordsAction(Request $request)
|
||||
{
|
||||
list($ret, $search_result) = $this->prepareSearchRequest($request);
|
||||
$subdefTransformer = new SubdefTransformer($this->app['acl'], $this->getAuthenticatedUser(), new PermalinkTransformer());
|
||||
$technicalDataTransformer = new TechnicalDataTransformer();
|
||||
$recordTransformer = new RecordTransformer($subdefTransformer, $technicalDataTransformer);
|
||||
$searchTransformer = new V1SearchRecordsResultTransformer($recordTransformer);
|
||||
|
||||
/** @var SearchEngineResult $search_result */
|
||||
foreach ($search_result->getResults() as $es_record) {
|
||||
try {
|
||||
$record = new \record_adapter($this->app, $es_record->getDataboxId(), $es_record->getRecordId());
|
||||
} catch (\Exception $e) {
|
||||
continue;
|
||||
}
|
||||
$transformerResolver = new SearchResultTransformerResolver([
|
||||
'' => $searchTransformer,
|
||||
'results' => $recordTransformer,
|
||||
'results.thumbnail' => $subdefTransformer,
|
||||
'results.technical_informations' => $technicalDataTransformer,
|
||||
'results.subdefs' => $subdefTransformer,
|
||||
'results.metadata' => new CallbackTransformer(),
|
||||
'results.status' => new CallbackTransformer(),
|
||||
'results.caption' => new CallbackTransformer(),
|
||||
]);
|
||||
$includeResolver = new IncludeResolver($transformerResolver);
|
||||
|
||||
$ret['results'][] = $this->listRecord($request, $record);
|
||||
}
|
||||
$fractal = new \League\Fractal\Manager();
|
||||
$fractal->setSerializer(new ArraySerializer());
|
||||
$fractal->parseIncludes($this->resolveSearchRecordsIncludes($request));
|
||||
|
||||
$searchView = $this->buildSearchRecordsView(
|
||||
$this->doSearch($request),
|
||||
$includeResolver->resolve($fractal),
|
||||
$this->resolveSubdefUrlTTL($request)
|
||||
);
|
||||
|
||||
$ret = $fractal->createData(new Item($searchView, $searchTransformer))->toArray();
|
||||
|
||||
return Result::create($request, $ret)->createResponse();
|
||||
}
|
||||
|
||||
private function prepareSearchRequest(Request $request)
|
||||
/**
|
||||
* @param SearchEngineResult $result
|
||||
* @param string[] $includes
|
||||
* @param int $urlTTL
|
||||
* @return SearchResultView
|
||||
*/
|
||||
private function buildSearchView(SearchEngineResult $result, array $includes, $urlTTL)
|
||||
{
|
||||
$references = new RecordReferenceCollection($result->getResults());
|
||||
|
||||
$records = new RecordCollection();
|
||||
$stories = new RecordCollection();
|
||||
|
||||
foreach ($references->toRecords($this->getApplicationBox()) as $record) {
|
||||
if ($record->isStory()) {
|
||||
$stories[$record->getId()] = $record;
|
||||
} else {
|
||||
$records[$record->getId()] = $record;
|
||||
}
|
||||
}
|
||||
|
||||
$resultView = new SearchResultView($result);
|
||||
|
||||
if ($stories->count() > 0) {
|
||||
$user = $this->getAuthenticatedUser();
|
||||
$children = [];
|
||||
|
||||
foreach ($stories->getDataboxIds() as $databoxId) {
|
||||
$storyIds = $stories->getDataboxRecordIds($databoxId);
|
||||
|
||||
$selections = $this->findDataboxById($databoxId)
|
||||
->getRecordRepository()
|
||||
->findChildren($storyIds, $user);
|
||||
$children[$databoxId] = array_combine($storyIds, $selections);
|
||||
}
|
||||
|
||||
/** @var StoryView[] $storyViews */
|
||||
$storyViews = [];
|
||||
/** @var RecordView[] $childrenViews */
|
||||
$childrenViews = [];
|
||||
|
||||
foreach ($stories as $index => $story) {
|
||||
$storyView = new StoryView($story);
|
||||
|
||||
$selection = $children[$story->getDataboxId()][$story->getRecordId()];
|
||||
|
||||
$childrenView = $this->buildRecordViews($selection);
|
||||
|
||||
foreach ($childrenView as $view) {
|
||||
$childrenViews[spl_object_hash($view)] = $view;
|
||||
}
|
||||
|
||||
$storyView->setChildren($childrenView);
|
||||
|
||||
$storyViews[$index] = $storyView;
|
||||
}
|
||||
|
||||
if (in_array('results.stories.thumbnail', $includes, true)) {
|
||||
$subdefViews = $this->buildSubdefsViews($stories, ['thumbnail'], $urlTTL);
|
||||
|
||||
foreach ($storyViews as $index => $storyView) {
|
||||
$storyView->setSubdefs($subdefViews[$index]);
|
||||
}
|
||||
}
|
||||
|
||||
if (in_array('results.stories.metadatas', $includes, true)) {
|
||||
$captions = $this->app['service.caption']->findByReferenceCollection($stories);
|
||||
$canSeeBusiness = $this->retrieveSeeBusinessPerDatabox($stories);
|
||||
|
||||
$this->buildCaptionViews($storyViews, $captions, $canSeeBusiness);
|
||||
}
|
||||
|
||||
$allChildren = new RecordCollection();
|
||||
foreach ($childrenViews as $index => $childrenView) {
|
||||
$allChildren[$index] = $childrenView->getRecord();
|
||||
}
|
||||
|
||||
$names = in_array('results.stories.records.subdefs', $includes, true) ? null : ['thumbnail'];
|
||||
$subdefViews = $this->buildSubdefsViews($allChildren, $names, $urlTTL);
|
||||
$technicalDatasets = $this->app['service.technical_data']->fetchRecordsTechnicalData($allChildren);
|
||||
|
||||
foreach ($childrenViews as $index => $recordView) {
|
||||
$recordView->setSubdefs($subdefViews[$index]);
|
||||
$recordView->setTechnicalDataView(new TechnicalDataView($technicalDatasets[$index]));
|
||||
}
|
||||
|
||||
if (array_intersect($includes, ['results.stories.records.metadata', 'results.stories.records.caption'])) {
|
||||
$captions = $this->app['service.caption']->findByReferenceCollection($allChildren);
|
||||
$canSeeBusiness = $this->retrieveSeeBusinessPerDatabox($allChildren);
|
||||
|
||||
$this->buildCaptionViews($childrenViews, $captions, $canSeeBusiness);
|
||||
}
|
||||
|
||||
$resultView->setStories($storyViews);
|
||||
}
|
||||
|
||||
if ($records->count() > 0) {
|
||||
$names = in_array('results.records.subdefs', $includes, true) ? null : ['thumbnail'];
|
||||
$recordViews = $this->buildRecordViews($records);
|
||||
$subdefViews = $this->buildSubdefsViews($records, $names, $urlTTL);
|
||||
|
||||
$technicalDatasets = $this->app['service.technical_data']->fetchRecordsTechnicalData($records);
|
||||
|
||||
foreach ($recordViews as $index => $recordView) {
|
||||
$recordView->setSubdefs($subdefViews[$index]);
|
||||
$recordView->setTechnicalDataView(new TechnicalDataView($technicalDatasets[$index]));
|
||||
}
|
||||
|
||||
if (array_intersect($includes, ['results.records.metadata', 'results.records.caption'])) {
|
||||
$captions = $this->app['service.caption']->findByReferenceCollection($records);
|
||||
$canSeeBusiness = $this->retrieveSeeBusinessPerDatabox($records);
|
||||
|
||||
$this->buildCaptionViews($recordViews, $captions, $canSeeBusiness);
|
||||
}
|
||||
|
||||
$resultView->setRecords($recordViews);
|
||||
}
|
||||
|
||||
return $resultView;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param SearchEngineResult $result
|
||||
* @param string[] $includes
|
||||
* @param int $urlTTL
|
||||
* @return SearchResultView
|
||||
*/
|
||||
private function buildSearchRecordsView(SearchEngineResult $result, array $includes, $urlTTL)
|
||||
{
|
||||
$references = new RecordReferenceCollection($result->getResults());
|
||||
$references = new RecordCollection($references->toRecords($this->getApplicationBox()));
|
||||
|
||||
$names = in_array('results.subdefs', $includes, true) ? null : ['thumbnail'];
|
||||
|
||||
$recordViews = $this->buildRecordViews($references);
|
||||
$subdefViews = $this->buildSubdefsViews($references, $names, $urlTTL);
|
||||
$technicalDatasets = $this->app['service.technical_data']->fetchRecordsTechnicalData($references);
|
||||
|
||||
foreach ($recordViews as $index => $recordView) {
|
||||
$recordView->setSubdefs($subdefViews[$index]);
|
||||
$recordView->setTechnicalDataView(new TechnicalDataView($technicalDatasets[$index]));
|
||||
}
|
||||
|
||||
if (array_intersect($includes, ['results.metadata', 'results.caption'])) {
|
||||
$captions = $this->app['service.caption']->findByReferenceCollection($references);
|
||||
$canSeeBusiness = $this->retrieveSeeBusinessPerDatabox($references);
|
||||
|
||||
$this->buildCaptionViews($recordViews, $captions, $canSeeBusiness);
|
||||
}
|
||||
|
||||
$resultView = new SearchResultView($result);
|
||||
$resultView->setRecords($recordViews);
|
||||
|
||||
return $resultView;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param RecordReferenceInterface[]|RecordReferenceCollection|DataboxGroupable $references
|
||||
* @param array|null $names
|
||||
* @param int $urlTTL
|
||||
* @return SubdefView[][]
|
||||
*/
|
||||
private function buildSubdefsViews($references, array $names = null, $urlTTL)
|
||||
{
|
||||
$subdefGroups = $this->app['service.media_subdef']
|
||||
->findSubdefsByRecordReferenceFromCollection($references, $names);
|
||||
|
||||
$fakeSubdefs = [];
|
||||
|
||||
foreach ($subdefGroups as $index => $subdefGroup) {
|
||||
if (!isset($subdefGroup['thumbnail'])) {
|
||||
$fakeSubdef = new \media_subdef($this->app, $references[$index], 'thumbnail', true, []);
|
||||
$fakeSubdefs[spl_object_hash($fakeSubdef)] = $fakeSubdef;
|
||||
|
||||
$subdefGroups[$index]['thumbnail'] = $fakeSubdef;
|
||||
}
|
||||
}
|
||||
|
||||
$allSubdefs = $this->mergeGroupsIntoOneList($subdefGroups);
|
||||
$allPermalinks = \media_Permalink_Adapter::getMany(
|
||||
$this->app,
|
||||
array_filter($allSubdefs, function (\media_subdef $subdef) use ($fakeSubdefs) {
|
||||
return !isset($fakeSubdefs[spl_object_hash($subdef)]);
|
||||
})
|
||||
);
|
||||
$urls = $this->app['media_accessor.subdef_url_generator']
|
||||
->generateMany($this->getAuthenticatedUser(), $allSubdefs, $urlTTL);
|
||||
|
||||
$subdefViews = [];
|
||||
|
||||
/** @var \media_subdef $subdef */
|
||||
foreach ($allSubdefs as $index => $subdef) {
|
||||
$subdefView = new SubdefView($subdef);
|
||||
|
||||
if (isset($allPermalinks[$index])) {
|
||||
$subdefView->setPermalinkView(new PermalinkView($allPermalinks[$index]));
|
||||
}
|
||||
|
||||
$subdefView->setUrl($urls[$index]);
|
||||
$subdefView->setUrlTTL($urlTTL);
|
||||
|
||||
$subdefViews[spl_object_hash($subdef)] = $subdefView;
|
||||
}
|
||||
|
||||
$reorderedGroups = [];
|
||||
|
||||
/** @var \media_subdef[] $subdefGroup */
|
||||
foreach ($subdefGroups as $index => $subdefGroup) {
|
||||
$reordered = [];
|
||||
|
||||
foreach ($subdefGroup as $subdef) {
|
||||
$reordered[] = $subdefViews[spl_object_hash($subdef)];
|
||||
}
|
||||
|
||||
$reorderedGroups[$index] = $reordered;
|
||||
}
|
||||
|
||||
return $reorderedGroups;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns requested includes
|
||||
*
|
||||
* @param Request $request
|
||||
* @return string[]
|
||||
*/
|
||||
private function resolveSearchIncludes(Request $request)
|
||||
{
|
||||
if ($request->attributes->get('_extended', false)) {
|
||||
return [
|
||||
'results.stories.records.subdefs',
|
||||
'results.stories.records.metadata',
|
||||
'results.stories.records.caption',
|
||||
'results.stories.records.status',
|
||||
'results.records.subdefs',
|
||||
'results.records.metadata',
|
||||
'results.records.caption',
|
||||
'results.records.status',
|
||||
];
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns requested includes
|
||||
*
|
||||
* @param Request $request
|
||||
* @return string[]
|
||||
*/
|
||||
private function resolveSearchRecordsIncludes(Request $request)
|
||||
{
|
||||
if ($request->attributes->get('_extended', false)) {
|
||||
return [
|
||||
'results.subdefs',
|
||||
'results.metadata',
|
||||
'results.caption',
|
||||
'results.status',
|
||||
];
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @return int
|
||||
*/
|
||||
private function resolveSubdefUrlTTL(Request $request)
|
||||
{
|
||||
$urlTTL = $request->query->get('subdef_url_ttl');
|
||||
|
||||
if (null !== $urlTTL) {
|
||||
return (int)$urlTTL;
|
||||
}
|
||||
|
||||
return $this->getConf()->get(['registry', 'general', 'default-subdef-url-ttl']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @return SearchEngineResult
|
||||
*/
|
||||
private function doSearch(Request $request)
|
||||
{
|
||||
$options = SearchEngineOptions::fromRequest($this->app, $request);
|
||||
$options->setFirstResult((int)($request->get('offset_start') ?: 0));
|
||||
$options->setMaxResults((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, $options);
|
||||
$search_result = $this->getSearchEngine()->query((string)$request->get('query'), $options);
|
||||
|
||||
$this->getUserManipulator()->logQuery($this->getAuthenticatedUser(), $search_result->getQuery());
|
||||
|
||||
@@ -1111,25 +1454,7 @@ class V1Controller extends Controller
|
||||
|
||||
$this->getSearchEngine()->clearCache();
|
||||
|
||||
$ret = [
|
||||
'offset_start' => $options->getFirstResult(),
|
||||
'per_page' => $options->getMaxResults(),
|
||||
'available_results' => $search_result->getAvailable(),
|
||||
'total_results' => $search_result->getTotal(),
|
||||
'error' => (string)$search_result->getError(),
|
||||
'warning' => (string)$search_result->getWarning(),
|
||||
'query_time' => $search_result->getDuration(),
|
||||
'search_indexes' => $search_result->getIndexes(),
|
||||
'suggestions' => array_map(
|
||||
function (SearchEngineSuggestion $suggestion) {
|
||||
return $suggestion->toArray();
|
||||
}, $search_result->getSuggestions()->toArray()),
|
||||
'facets' => $search_result->getFacets(),
|
||||
'results' => [],
|
||||
'query' => $search_result->getQuery(),
|
||||
];
|
||||
|
||||
return [$ret, $search_result];
|
||||
return $search_result;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1137,7 +1462,7 @@ class V1Controller extends Controller
|
||||
* @param RecordReferenceInterface[]|RecordReferenceCollection $records
|
||||
* @return array
|
||||
*/
|
||||
public function listRecords(Request $request, $records)
|
||||
private function listRecords(Request $request, $records)
|
||||
{
|
||||
if (!$records instanceof RecordReferenceCollection) {
|
||||
$records = new RecordReferenceCollection($records);
|
||||
@@ -1163,7 +1488,7 @@ class V1Controller extends Controller
|
||||
* @param \record_adapter $record
|
||||
* @return array
|
||||
*/
|
||||
public function listRecord(Request $request, \record_adapter $record)
|
||||
private function listRecord(Request $request, \record_adapter $record)
|
||||
{
|
||||
$technicalInformation = [];
|
||||
foreach ($record->get_technical_infos()->getValues() as $name => $value) {
|
||||
@@ -1199,27 +1524,6 @@ 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
|
||||
*
|
||||
@@ -1228,7 +1532,7 @@ class V1Controller extends Controller
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function listStory(Request $request, \record_adapter $story)
|
||||
private function listStory(Request $request, \record_adapter $story)
|
||||
{
|
||||
if (!$story->isStory()) {
|
||||
return Result::createError($request, 404, 'Story not found')->createResponse();
|
||||
@@ -1398,7 +1702,7 @@ class V1Controller extends Controller
|
||||
foreach ($record->getStatusStructure() as $bit => $status) {
|
||||
$ret[] = [
|
||||
'bit' => $bit,
|
||||
'state' => \databox_status::bitIsSet($record->getStatusBitField(), $bit)
|
||||
'state' => \databox_status::bitIsSet($record->getStatusBitField(), $bit),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -1686,7 +1990,7 @@ class V1Controller extends Controller
|
||||
{
|
||||
$ret = [
|
||||
"basket" => $this->listBasket($basket),
|
||||
"basket_elements" => $this->listBasketContent($request, $basket)
|
||||
"basket_elements" => $this->listBasketContent($request, $basket),
|
||||
];
|
||||
|
||||
return Result::create($request, $ret)->createResponse();
|
||||
@@ -2089,7 +2393,7 @@ class V1Controller extends Controller
|
||||
$metadatas[] = array(
|
||||
'meta_struct_id' => $field->get_id(),
|
||||
'meta_id' => null,
|
||||
'value' => $data->{'title'}
|
||||
'value' => $data->{'title'},
|
||||
);
|
||||
$thumbtitle_set = true;
|
||||
}
|
||||
@@ -2110,7 +2414,7 @@ class V1Controller extends Controller
|
||||
$metadatas[] = array(
|
||||
'meta_struct_id' => $field->get_id(),
|
||||
'meta_id' => null,
|
||||
'value' => $value
|
||||
'value' => $value,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -2254,7 +2558,7 @@ class V1Controller extends Controller
|
||||
{
|
||||
$ret = [
|
||||
"user" => $this->listUser($this->getAuthenticatedUser()),
|
||||
"collections" => $this->listUserCollections($this->getAuthenticatedUser())
|
||||
"collections" => $this->listUserCollections($this->getAuthenticatedUser()),
|
||||
];
|
||||
|
||||
if (defined('API_SKIP_USER_REGISTRATIONS') && ! constant('API_SKIP_USER_REGISTRATIONS')) {
|
||||
@@ -2317,7 +2621,7 @@ class V1Controller extends Controller
|
||||
$command = new UpdatePasswordCommand();
|
||||
/** @var Form $form */
|
||||
$form = $this->app->form(new PhraseaRenewPasswordForm(), $command, [
|
||||
'csrf_protection' => false
|
||||
'csrf_protection' => false,
|
||||
]);
|
||||
|
||||
$form->submit($data);
|
||||
@@ -2356,7 +2660,7 @@ class V1Controller extends Controller
|
||||
|
||||
return Result::create($request, [
|
||||
'user' => $user,
|
||||
'token' => $token
|
||||
'token' => $token,
|
||||
])->createResponse();
|
||||
}
|
||||
|
||||
@@ -2614,4 +2918,86 @@ class V1Controller extends Controller
|
||||
|
||||
return $caption;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $groups
|
||||
* @return array|mixed
|
||||
*/
|
||||
private function mergeGroupsIntoOneList(array $groups)
|
||||
{
|
||||
// Strips keys from the internal array
|
||||
array_walk($groups, function (array &$group) {
|
||||
$group = array_values($group);
|
||||
});
|
||||
|
||||
if ($groups) {
|
||||
return call_user_func_array('array_merge', $groups);
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param RecordCollection|\record_adapter[] $references
|
||||
* @return RecordView[]
|
||||
*/
|
||||
private function buildRecordViews($references)
|
||||
{
|
||||
if (!$references instanceof RecordCollection) {
|
||||
$references = new RecordCollection($references);
|
||||
}
|
||||
|
||||
$recordViews = [];
|
||||
|
||||
foreach ($references as $index => $record) {
|
||||
$recordViews[$index] = new RecordView($record);
|
||||
}
|
||||
|
||||
return $recordViews;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param RecordReferenceInterface[]|DataboxGroupable $references
|
||||
* @return array<int, bool>
|
||||
*/
|
||||
private function retrieveSeeBusinessPerDatabox($references)
|
||||
{
|
||||
if (!$references instanceof DataboxGroupable) {
|
||||
$references = new RecordReferenceCollection($references);
|
||||
}
|
||||
|
||||
$acl = $this->getAclForUser();
|
||||
|
||||
$canSeeBusiness = [];
|
||||
|
||||
foreach ($references->getDataboxIds() as $databoxId) {
|
||||
$canSeeBusiness[$databoxId] = $acl->can_see_business_fields($this->findDataboxById($databoxId));
|
||||
}
|
||||
|
||||
$rights = [];
|
||||
|
||||
foreach ($references as $index => $reference) {
|
||||
$rights[$index] = $canSeeBusiness[$reference->getDataboxId()];
|
||||
}
|
||||
|
||||
return $rights;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param RecordView[] $recordViews
|
||||
* @param \caption_record[] $captions
|
||||
* @param bool[] $canSeeBusiness
|
||||
*/
|
||||
private function buildCaptionViews($recordViews, $captions, $canSeeBusiness)
|
||||
{
|
||||
foreach ($recordViews as $index => $recordView) {
|
||||
$caption = $captions[$index];
|
||||
|
||||
$captionView = new CaptionView($caption);
|
||||
|
||||
$captionView->setFields($caption->get_fields(null, isset($canSeeBusiness[$index]) && (bool)$canSeeBusiness[$index]));
|
||||
|
||||
$recordView->setCaption($captionView);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,116 @@
|
||||
<?php
|
||||
/*
|
||||
* This file is part of Phraseanet
|
||||
*
|
||||
* (c) 2005-2016 Alchemy
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Alchemy\Phrasea\Databox\Caption;
|
||||
|
||||
use Alchemy\Phrasea\Cache\MultiAdapter;
|
||||
use Doctrine\Common\Cache\Cache;
|
||||
use Doctrine\Common\Cache\MultiGetCache;
|
||||
use Doctrine\Common\Cache\MultiPutCache;
|
||||
|
||||
class CachedCaptionDataRepository implements CaptionDataRepository
|
||||
{
|
||||
/**
|
||||
* @var CaptionDataRepository
|
||||
*/
|
||||
private $decorated;
|
||||
/**
|
||||
* @var Cache|MultiGetCache|MultiPutCache
|
||||
*/
|
||||
private $cache;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $baseKey;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $lifeTime = 0;
|
||||
|
||||
/**
|
||||
* CachedCaptionDataRepository constructor.
|
||||
* @param CaptionDataRepository $decorated
|
||||
* @param Cache $cache
|
||||
* @param string $baseKey
|
||||
*/
|
||||
public function __construct(CaptionDataRepository $decorated, Cache $cache, $baseKey)
|
||||
{
|
||||
$this->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 array $recordIds
|
||||
* @return \array[]
|
||||
*/
|
||||
public function findByRecordIds(array $recordIds)
|
||||
{
|
||||
$keys = $this->computeKeys($recordIds);
|
||||
|
||||
$data = $this->cache->fetchMultiple($keys);
|
||||
|
||||
if (count($data) === count($keys)) {
|
||||
return array_combine($recordIds, $data);
|
||||
}
|
||||
|
||||
$data = $this->decorated->findByRecordIds($recordIds);
|
||||
|
||||
$this->cache->saveMultiple(array_combine($keys, $data));
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $recordId
|
||||
* @return void
|
||||
*/
|
||||
public function invalidate($recordId)
|
||||
{
|
||||
$this->cache->delete($this->computeKey($recordId));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int[] $recordIds
|
||||
* @return string[]
|
||||
*/
|
||||
private function computeKeys(array $recordIds)
|
||||
{
|
||||
return array_map([$this, 'computeKey'], array_unique($recordIds));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $recordId
|
||||
* @return string
|
||||
*/
|
||||
private function computeKey($recordId)
|
||||
{
|
||||
return sprintf('%scaption[%d]', $this->baseKey, $recordId);
|
||||
}
|
||||
}
|
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
/*
|
||||
* This file is part of Phraseanet
|
||||
*
|
||||
* (c) 2005-2016 Alchemy
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Alchemy\Phrasea\Databox\Caption;
|
||||
|
||||
use Alchemy\Phrasea\Core\Event\Record\MetadataChangedEvent;
|
||||
use Alchemy\Phrasea\Core\Event\Record\RecordEvents;
|
||||
use Alchemy\Phrasea\Databox\DataboxBoundRepositoryProvider;
|
||||
use Assert\Assertion;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
|
||||
class CaptionCacheInvalider implements EventSubscriberInterface
|
||||
{
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return [
|
||||
RecordEvents::METADATA_CHANGED => 'onMetadataChange',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @var callable
|
||||
*/
|
||||
private $locator;
|
||||
|
||||
/**
|
||||
* @param callable $locator CachedCaptionDataRepository provider
|
||||
*/
|
||||
public function __construct(callable $locator)
|
||||
{
|
||||
$this->locator = $locator;
|
||||
}
|
||||
|
||||
public function onMetadataChange(MetadataChangedEvent $event)
|
||||
{
|
||||
$record = $event->getRecord();
|
||||
|
||||
$repository = $this->getCaptionRepository($record->getDataboxId());
|
||||
$repository->invalidate($record->getRecordId());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $databoxId
|
||||
* @return CachedCaptionDataRepository
|
||||
*/
|
||||
private function getCaptionRepository($databoxId)
|
||||
{
|
||||
$locator = $this->locator;
|
||||
|
||||
/** @var DataboxBoundRepositoryProvider $repositoryProvider */
|
||||
$repositoryProvider = $locator();
|
||||
|
||||
Assertion::isInstanceOf($repositoryProvider, DataboxBoundRepositoryProvider::class);
|
||||
|
||||
$repository = $repositoryProvider->getRepositoryForDatabox($databoxId);
|
||||
|
||||
Assertion::isInstanceOf($repository, CachedCaptionDataRepository::class);
|
||||
|
||||
return $repository;
|
||||
}
|
||||
}
|
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
/*
|
||||
* This file is part of Phraseanet
|
||||
*
|
||||
* (c) 2005-2016 Alchemy
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Alchemy\Phrasea\Databox\Caption;
|
||||
|
||||
interface CaptionDataRepository
|
||||
{
|
||||
/**
|
||||
* @param int[] $recordIds
|
||||
* @return array[]
|
||||
*/
|
||||
public function findByRecordIds(array $recordIds);
|
||||
}
|
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
/*
|
||||
* This file is part of Phraseanet
|
||||
*
|
||||
* (c) 2005-2016 Alchemy
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Alchemy\Phrasea\Databox\Caption;
|
||||
|
||||
use Alchemy\Phrasea\Databox\DataboxBoundRepositoryFactory;
|
||||
use Alchemy\Phrasea\Databox\DataboxConnectionProvider;
|
||||
use Doctrine\Common\Cache\Cache;
|
||||
|
||||
class CaptionDataRepositoryFactory implements DataboxBoundRepositoryFactory
|
||||
{
|
||||
/**
|
||||
* @var DataboxConnectionProvider
|
||||
*/
|
||||
private $connectionProvider;
|
||||
|
||||
/**
|
||||
* @var Cache
|
||||
*/
|
||||
private $cache;
|
||||
|
||||
public function __construct(DataboxConnectionProvider $connectionProvider, Cache $cache)
|
||||
{
|
||||
$this->connectionProvider = $connectionProvider;
|
||||
$this->cache = $cache;
|
||||
}
|
||||
|
||||
public function createRepositoryFor($databoxId)
|
||||
{
|
||||
return new CachedCaptionDataRepository(
|
||||
new DbalCaptionDataRepository($this->connectionProvider->getConnection($databoxId)),
|
||||
$this->cache,
|
||||
sprintf('databox[%d]:', $databoxId)
|
||||
);
|
||||
}
|
||||
}
|
70
lib/Alchemy/Phrasea/Databox/Caption/CaptionRepository.php
Normal file
70
lib/Alchemy/Phrasea/Databox/Caption/CaptionRepository.php
Normal file
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
/*
|
||||
* This file is part of Phraseanet
|
||||
*
|
||||
* (c) 2005-2016 Alchemy
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Alchemy\Phrasea\Databox\Caption;
|
||||
|
||||
class CaptionRepository
|
||||
{
|
||||
/**
|
||||
* @var \caption_record[]
|
||||
*/
|
||||
private $idMap = [];
|
||||
|
||||
/**
|
||||
* @var CaptionDataRepository
|
||||
*/
|
||||
private $dataRepository;
|
||||
|
||||
/**
|
||||
* @var callable
|
||||
*/
|
||||
private $captionFactory;
|
||||
|
||||
public function __construct(CaptionDataRepository $dataRepository, callable $captionFactory)
|
||||
{
|
||||
$this->dataRepository = $dataRepository;
|
||||
$this->captionFactory = $captionFactory;
|
||||
}
|
||||
|
||||
public function findByRecordIds(array $recordIds)
|
||||
{
|
||||
$this->fetchMissing($recordIds);
|
||||
|
||||
$instances = [];
|
||||
|
||||
foreach ($recordIds as $index => $recordId) {
|
||||
$instances[$index] = $this->idMap[$recordId];
|
||||
}
|
||||
|
||||
return $instances;
|
||||
}
|
||||
|
||||
public function clear()
|
||||
{
|
||||
$this->idMap = [];
|
||||
}
|
||||
|
||||
private function fetchMissing(array $recordIds)
|
||||
{
|
||||
$missing = array_diff($recordIds, array_keys($this->idMap));
|
||||
|
||||
if (!$missing) {
|
||||
return;
|
||||
}
|
||||
|
||||
$data = $this->dataRepository->findByRecordIds($missing);
|
||||
|
||||
$factory = $this->captionFactory;
|
||||
|
||||
foreach ($data as $recordId => $item) {
|
||||
$this->idMap[(int)$recordId] = $factory($recordId, $item);
|
||||
}
|
||||
}
|
||||
}
|
96
lib/Alchemy/Phrasea/Databox/Caption/CaptionService.php
Normal file
96
lib/Alchemy/Phrasea/Databox/Caption/CaptionService.php
Normal file
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
/*
|
||||
* This file is part of Phraseanet
|
||||
*
|
||||
* (c) 2005-2016 Alchemy
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Alchemy\Phrasea\Databox\Caption;
|
||||
|
||||
use Alchemy\Phrasea\Databox\DataboxBoundRepositoryProvider;
|
||||
use Alchemy\Phrasea\Model\RecordReferenceInterface;
|
||||
use Alchemy\Phrasea\Record\RecordReferenceCollection;
|
||||
|
||||
class CaptionService
|
||||
{
|
||||
/**
|
||||
* @var DataboxBoundRepositoryProvider
|
||||
*/
|
||||
private $repositoryProvider;
|
||||
|
||||
public function __construct(DataboxBoundRepositoryProvider $repositoryProvider)
|
||||
{
|
||||
$this->repositoryProvider = $repositoryProvider;
|
||||
}
|
||||
|
||||
public function findByReferenceCollection($references)
|
||||
{
|
||||
$references = $this->normalizeReferenceCollection($references);
|
||||
|
||||
$groups = [];
|
||||
|
||||
foreach ($references->getDataboxIds() as $databoxId) {
|
||||
$recordIds = $references->getDataboxRecordIds($databoxId);
|
||||
|
||||
$groups[$databoxId] = $this->getRepositoryForDatabox($databoxId)->findByRecordIds($recordIds);
|
||||
}
|
||||
|
||||
return $this->reorderInstances($references, $groups);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param RecordReferenceInterface[]|RecordReferenceCollection $references
|
||||
* @return RecordReferenceCollection
|
||||
*/
|
||||
private function normalizeReferenceCollection($references)
|
||||
{
|
||||
if ($references instanceof RecordReferenceCollection) {
|
||||
return $references;
|
||||
}
|
||||
|
||||
return new RecordReferenceCollection($references);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $databoxId
|
||||
* @return CaptionRepository
|
||||
*/
|
||||
private function getRepositoryForDatabox($databoxId)
|
||||
{
|
||||
return $this->repositoryProvider->getRepositoryForDatabox($databoxId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param RecordReferenceCollection $references
|
||||
* @param \caption_record[][] $groups
|
||||
* @return \caption_record[]
|
||||
*/
|
||||
private function reorderInstances(RecordReferenceCollection $references, array $groups)
|
||||
{
|
||||
$captions = [];
|
||||
|
||||
foreach ($groups as $databoxId => $group) {
|
||||
$captions[$databoxId] = array_reduce($group, function (array &$carry, \caption_record $caption) {
|
||||
$carry[$caption->getRecordReference()->getRecordId()] = $caption;
|
||||
|
||||
return $carry;
|
||||
}, []);
|
||||
}
|
||||
|
||||
$instances = [];
|
||||
|
||||
foreach ($references as $index => $reference) {
|
||||
$databoxId = $reference->getDataboxId();
|
||||
$recordId = $reference->getRecordId();
|
||||
|
||||
if (isset($captions[$databoxId][$recordId])) {
|
||||
$instances[$index] = $captions[$databoxId][$recordId];
|
||||
}
|
||||
}
|
||||
|
||||
return $instances;
|
||||
}
|
||||
}
|
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
/*
|
||||
* This file is part of Phraseanet
|
||||
*
|
||||
* (c) 2005-2016 Alchemy
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Alchemy\Phrasea\Databox\Caption;
|
||||
|
||||
use Alchemy\Phrasea\Controller\LazyLocator;
|
||||
use Alchemy\Phrasea\Databox\ClosureDataboxBoundRepositoryFactory;
|
||||
use Alchemy\Phrasea\Databox\DataboxBoundRepositoryProvider;
|
||||
use Alchemy\Phrasea\Databox\DataboxConnectionProvider;
|
||||
use Alchemy\Phrasea\Record\RecordReference;
|
||||
use Silex\Application;
|
||||
use Silex\ServiceProviderInterface;
|
||||
|
||||
class CaptionServiceProvider implements ServiceProviderInterface
|
||||
{
|
||||
public function register(Application $app)
|
||||
{
|
||||
$app['provider.factory.caption'] = $app->protect(function ($databoxId) use ($app) {
|
||||
return function ($recordId, array $data) use ($app, $databoxId) {
|
||||
$recordReference = RecordReference::createFromDataboxIdAndRecordId($databoxId, $recordId);
|
||||
|
||||
return new \caption_record($app, $recordReference, $data);
|
||||
};
|
||||
});
|
||||
|
||||
$app['provider.data_repo.caption'] = $app->share(function (Application $app) {
|
||||
return new DataboxBoundRepositoryProvider(new CaptionDataRepositoryFactory(
|
||||
new DataboxConnectionProvider($app['phraseanet.appbox']),
|
||||
$app['cache']
|
||||
));
|
||||
});
|
||||
|
||||
$app['provider.repo.caption'] = $app->share(function (Application $app) {
|
||||
return new DataboxBoundRepositoryProvider(
|
||||
new ClosureDataboxBoundRepositoryFactory(function ($databoxId) use ($app) {
|
||||
/** @var CaptionDataRepository $dataRepository */
|
||||
$dataRepository = $app['provider.data_repo.caption']->getRepositoryForDatabox($databoxId);
|
||||
$captionFactoryProvider = $app['provider.factory.caption'];
|
||||
|
||||
return new CaptionRepository(
|
||||
$dataRepository,
|
||||
$captionFactoryProvider($databoxId)
|
||||
);
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
$app['service.caption'] = $app->share(function (Application $app) {
|
||||
return new CaptionService($app['provider.repo.caption']);
|
||||
});
|
||||
}
|
||||
|
||||
public function boot(Application $app)
|
||||
{
|
||||
$app['dispatcher']->addSubscriber(new CaptionCacheInvalider(new LazyLocator($app, 'provider.data_repo.caption')));
|
||||
}
|
||||
}
|
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
/*
|
||||
* This file is part of Phraseanet
|
||||
*
|
||||
* (c) 2005-2016 Alchemy
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Alchemy\Phrasea\Databox\Caption;
|
||||
|
||||
use Doctrine\DBAL\Connection;
|
||||
|
||||
class DbalCaptionDataRepository implements CaptionDataRepository
|
||||
{
|
||||
/**
|
||||
* @var Connection
|
||||
*/
|
||||
private $connection;
|
||||
|
||||
public function __construct(Connection $connection)
|
||||
{
|
||||
$this->connection = $connection;
|
||||
}
|
||||
|
||||
public function findByRecordIds(array $recordIds)
|
||||
{
|
||||
if (!$recordIds) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$sql = <<<'SQL'
|
||||
SELECT m.record_id, 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 IN (:recordIds)
|
||||
ORDER BY m.record_id ASC, s.sorter ASC
|
||||
SQL;
|
||||
|
||||
$data = $this->connection->fetchAll(
|
||||
$sql, ['recordIds' => $recordIds], ['recordIds' => Connection::PARAM_INT_ARRAY]
|
||||
);
|
||||
|
||||
return $this->mapByRecordId($data, $recordIds);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
* @param int[] $recordIds
|
||||
* @return array[]
|
||||
*/
|
||||
private function mapByRecordId(array $data, array $recordIds)
|
||||
{
|
||||
$groups = array_fill_keys($recordIds, []);
|
||||
|
||||
foreach ($data as $item) {
|
||||
$recordId = $item['record_id'];
|
||||
|
||||
$groups[$recordId][] = $item;
|
||||
}
|
||||
|
||||
return $groups;
|
||||
}
|
||||
}
|
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
/*
|
||||
* This file is part of Phraseanet
|
||||
*
|
||||
* (c) 2005-2016 Alchemy
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Alchemy\Phrasea\Databox;
|
||||
|
||||
class ClosureDataboxBoundRepositoryFactory implements DataboxBoundRepositoryFactory
|
||||
{
|
||||
/**
|
||||
* @var callable
|
||||
*/
|
||||
private $factory;
|
||||
|
||||
public function __construct(callable $factory)
|
||||
{
|
||||
$this->factory = $factory;
|
||||
}
|
||||
|
||||
public function createRepositoryFor($databoxId)
|
||||
{
|
||||
$factory = $this->factory;
|
||||
|
||||
return $factory($databoxId);
|
||||
}
|
||||
}
|
41
lib/Alchemy/Phrasea/Databox/DataboxGroupable.php
Normal file
41
lib/Alchemy/Phrasea/Databox/DataboxGroupable.php
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
/*
|
||||
* This file is part of Phraseanet
|
||||
*
|
||||
* (c) 2005-2016 Alchemy
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Alchemy\Phrasea\Databox;
|
||||
|
||||
interface DataboxGroupable
|
||||
{
|
||||
/**
|
||||
* Group instance by Databox Id
|
||||
*
|
||||
* @return array<int,array>
|
||||
*/
|
||||
public function groupByDatabox();
|
||||
|
||||
/**
|
||||
* Returns databoxes ids
|
||||
*
|
||||
* @return int[]
|
||||
*/
|
||||
public function getDataboxIds();
|
||||
|
||||
/**
|
||||
* @param int $databoxId
|
||||
* @return array
|
||||
*/
|
||||
public function getDataboxGroup($databoxId);
|
||||
|
||||
/**
|
||||
* Reorder groups if needed
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function reorderGroups();
|
||||
}
|
@@ -151,11 +151,12 @@ class CachedMediaSubdefDataRepository implements MediaSubdefDataRepository
|
||||
*/
|
||||
private function generateCacheKeys(array $recordIds, array $names)
|
||||
{
|
||||
$names = array_unique($names);
|
||||
$namesCount = count($names);
|
||||
|
||||
$keys = array_map(function ($recordId) use ($namesCount, $names) {
|
||||
return array_map([$this, 'getCacheKey'], array_fill(0, $namesCount, $recordId), $names);
|
||||
}, $recordIds);
|
||||
}, array_unique($recordIds));
|
||||
|
||||
return $keys ? call_user_func_array('array_merge', $keys) : [];
|
||||
}
|
||||
|
@@ -30,33 +30,55 @@ class MediaSubdefService
|
||||
* Returns all available subdefs grouped by each record reference and by its name
|
||||
*
|
||||
* @param RecordReferenceInterface[]|RecordReferenceCollection $records
|
||||
* @param null|array $names
|
||||
* @return \media_subdef[][]
|
||||
*/
|
||||
public function findSubdefsByRecordReferenceFromCollection($records)
|
||||
public function findSubdefsByRecordReferenceFromCollection($records, array $names = null)
|
||||
{
|
||||
$subdefs = $this->reduceRecordReferenceCollection(
|
||||
$records,
|
||||
function (array &$carry, array $subdefs, array $indexes) {
|
||||
function (array &$carry, array $subdefs, array $references) {
|
||||
$subdefsByRecordId = [];
|
||||
|
||||
/** @var \media_subdef $subdef */
|
||||
foreach ($subdefs as $subdef) {
|
||||
$index = $indexes[$subdef->get_record_id()];
|
||||
$recordId = $subdef->get_record_id();
|
||||
|
||||
$carry[$index][$subdef->get_name()] = $subdef;
|
||||
if (!isset($subdefsByRecordId[$recordId])) {
|
||||
$subdefsByRecordId[$recordId] = [];
|
||||
}
|
||||
|
||||
$subdefsByRecordId[$recordId][$subdef->get_name()] = $subdef;
|
||||
}
|
||||
|
||||
/** @var RecordReferenceInterface $reference */
|
||||
foreach ($references as $index => $reference) {
|
||||
if (isset($subdefsByRecordId[$reference->getRecordId()])) {
|
||||
$carry[$index] = $subdefsByRecordId[$reference->getRecordId()];
|
||||
};
|
||||
}
|
||||
|
||||
return $carry;
|
||||
},
|
||||
array_fill_keys(array_keys(iterator_to_array($records)), [])
|
||||
array_fill_keys(array_keys($records instanceof \Traversable ? iterator_to_array($records) : $records), []),
|
||||
$names
|
||||
);
|
||||
|
||||
ksort($subdefs);
|
||||
$reordered = [];
|
||||
|
||||
return $subdefs;
|
||||
foreach ($records as $index => $record) {
|
||||
$reordered[$index] = $subdefs[$index];
|
||||
}
|
||||
|
||||
return $reordered;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param RecordReferenceInterface[]|RecordReferenceCollection $records
|
||||
* @param null|string[] $names
|
||||
* @return \media_subdef[]
|
||||
*/
|
||||
public function findSubdefsFromRecordReferenceCollection($records)
|
||||
public function findSubdefsFromRecordReferenceCollection($records, array $names = null)
|
||||
{
|
||||
$groups = $this->reduceRecordReferenceCollection(
|
||||
$records,
|
||||
@@ -65,7 +87,8 @@ class MediaSubdefService
|
||||
|
||||
return $carry;
|
||||
},
|
||||
[]
|
||||
[],
|
||||
$names
|
||||
);
|
||||
|
||||
if ($groups) {
|
||||
@@ -79,18 +102,22 @@ class MediaSubdefService
|
||||
* @param RecordReferenceInterface[]|RecordReferenceCollection $records
|
||||
* @param callable $process
|
||||
* @param mixed $initialValue
|
||||
* @param null|string[] $names
|
||||
* @return mixed
|
||||
*/
|
||||
private function reduceRecordReferenceCollection($records, callable $process, $initialValue)
|
||||
private function reduceRecordReferenceCollection($records, callable $process, $initialValue, array $names = null)
|
||||
{
|
||||
$records = $this->normalizeRecordCollection($records);
|
||||
|
||||
$carry = $initialValue;
|
||||
|
||||
foreach ($records->groupPerDataboxId() as $databoxId => $indexes) {
|
||||
$subdefs = $this->getRepositoryForDatabox($databoxId)->findByRecordIdsAndNames(array_keys($indexes));
|
||||
foreach ($records->getDataboxIds() as $databoxId) {
|
||||
$recordIds = $records->getDataboxRecordIds($databoxId);
|
||||
|
||||
$carry = $process($carry, $subdefs, $indexes, $databoxId);
|
||||
$subdefs = $this->getRepositoryForDatabox($databoxId)
|
||||
->findByRecordIdsAndNames($recordIds, $names);
|
||||
|
||||
$carry = $process($carry, $subdefs, $records->getDataboxGroup($databoxId), $databoxId);
|
||||
}
|
||||
|
||||
return $carry;
|
||||
|
54
lib/Alchemy/Phrasea/Fractal/ArraySerializer.php
Normal file
54
lib/Alchemy/Phrasea/Fractal/ArraySerializer.php
Normal file
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
/*
|
||||
* This file is part of Phraseanet
|
||||
*
|
||||
* (c) 2005-2016 Alchemy
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Alchemy\Phrasea\Fractal;
|
||||
|
||||
use League\Fractal\Pagination\CursorInterface;
|
||||
use League\Fractal\Pagination\PaginatorInterface;
|
||||
use League\Fractal\Resource\ResourceInterface;
|
||||
use League\Fractal\Serializer\SerializerAbstract;
|
||||
|
||||
class ArraySerializer extends SerializerAbstract
|
||||
{
|
||||
public function collection($resourceKey, array $data)
|
||||
{
|
||||
return array_values(array_filter($data));
|
||||
}
|
||||
|
||||
public function item($resourceKey, array $data)
|
||||
{
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function null($resourceKey)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function includedData(ResourceInterface $resource, array $data)
|
||||
{
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function meta(array $meta)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function paginator(PaginatorInterface $paginator)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function cursor(CursorInterface $cursor)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
37
lib/Alchemy/Phrasea/Fractal/CallbackTransformer.php
Normal file
37
lib/Alchemy/Phrasea/Fractal/CallbackTransformer.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
/*
|
||||
* This file is part of Phraseanet
|
||||
*
|
||||
* (c) 2005-2016 Alchemy
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Alchemy\Phrasea\Fractal;
|
||||
|
||||
use League\Fractal\TransformerAbstract;
|
||||
|
||||
class CallbackTransformer extends TransformerAbstract
|
||||
{
|
||||
/**
|
||||
* @var callable
|
||||
*/
|
||||
private $callback;
|
||||
|
||||
public function __construct(callable $callback = null)
|
||||
{
|
||||
if (null === $callback) {
|
||||
$callback = function () {
|
||||
return [];
|
||||
};
|
||||
}
|
||||
|
||||
$this->callback = $callback;
|
||||
}
|
||||
|
||||
public function transform()
|
||||
{
|
||||
return call_user_func_array($this->callback, func_get_args());
|
||||
}
|
||||
}
|
53
lib/Alchemy/Phrasea/Fractal/GetSerializationEvent.php
Normal file
53
lib/Alchemy/Phrasea/Fractal/GetSerializationEvent.php
Normal file
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
/*
|
||||
* This file is part of Phraseanet
|
||||
*
|
||||
* (c) 2005-2016 Alchemy
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Alchemy\Phrasea\Fractal;
|
||||
|
||||
use Symfony\Component\EventDispatcher\Event;
|
||||
|
||||
class GetSerializationEvent extends Event
|
||||
{
|
||||
private $resourceKey;
|
||||
private $data;
|
||||
private $serialization;
|
||||
|
||||
public function __construct($resourceKey, $data)
|
||||
{
|
||||
$this->resourceKey = $resourceKey;
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getResourceKey()
|
||||
{
|
||||
return $this->resourceKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getData()
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
public function setSerialization($serialization)
|
||||
{
|
||||
$this->serialization = $serialization;
|
||||
$this->stopPropagation();
|
||||
}
|
||||
|
||||
public function getSerialization()
|
||||
{
|
||||
return $this->serialization;
|
||||
}
|
||||
}
|
77
lib/Alchemy/Phrasea/Fractal/IncludeResolver.php
Normal file
77
lib/Alchemy/Phrasea/Fractal/IncludeResolver.php
Normal file
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
/*
|
||||
* This file is part of Phraseanet
|
||||
*
|
||||
* (c) 2005-2016 Alchemy
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Alchemy\Phrasea\Fractal;
|
||||
|
||||
use League\Fractal\Manager;
|
||||
|
||||
class IncludeResolver
|
||||
{
|
||||
/**
|
||||
* @var TransformerResolver
|
||||
*/
|
||||
private $transformerResolver;
|
||||
|
||||
public function __construct(TransformerResolver $transformerResolver)
|
||||
{
|
||||
$this->transformerResolver = $transformerResolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Manager $manager
|
||||
* @return array
|
||||
*/
|
||||
public function resolve(Manager $manager)
|
||||
{
|
||||
$scope = new ResourceTransformerAccessibleScope($manager, $this->createNullResource());
|
||||
$scopes = [];
|
||||
|
||||
$this->appendScopeIdentifiers($scopes, $scope);
|
||||
|
||||
return array_values(array_filter($scopes));
|
||||
}
|
||||
|
||||
private function appendScopeIdentifiers(array &$scopes, ResourceTransformerAccessibleScope $scope)
|
||||
{
|
||||
foreach ($this->figureOutWhichIncludes($scope) as $include) {
|
||||
$scopeIdentifier = $scope->getIdentifier($include);
|
||||
|
||||
$scopes[] = $scopeIdentifier;
|
||||
|
||||
$childScope = $scope->createChildScope($include, $this->createNullResource($scopeIdentifier));
|
||||
|
||||
$this->appendScopeIdentifiers($scopes, $childScope);
|
||||
}
|
||||
}
|
||||
|
||||
private function figureOutWhichIncludes(ResourceTransformerAccessibleScope $scope)
|
||||
{
|
||||
$transformer = $scope->getResourceTransformer();
|
||||
|
||||
$includes = $transformer->getDefaultIncludes();
|
||||
|
||||
foreach ($transformer->getAvailableIncludes() as $include) {
|
||||
if ($scope->isRequested($include)) {
|
||||
$includes[] = $include;
|
||||
}
|
||||
}
|
||||
|
||||
return array_values(array_filter(array_unique($includes)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $scopeIdentifier
|
||||
* @return NullResource
|
||||
*/
|
||||
private function createNullResource($scopeIdentifier = '')
|
||||
{
|
||||
return new NullResource($this->transformerResolver->resolve($scopeIdentifier));
|
||||
}
|
||||
}
|
26
lib/Alchemy/Phrasea/Fractal/NullResource.php
Normal file
26
lib/Alchemy/Phrasea/Fractal/NullResource.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
/*
|
||||
* This file is part of Phraseanet
|
||||
*
|
||||
* (c) 2005-2016 Alchemy
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Alchemy\Phrasea\Fractal;
|
||||
|
||||
use League\Fractal\Resource\ResourceAbstract;
|
||||
use League\Fractal\TransformerAbstract;
|
||||
|
||||
class NullResource extends ResourceAbstract
|
||||
{
|
||||
/**
|
||||
* @param callable|TransformerAbstract $transformer
|
||||
* @param null|string $resourceKey
|
||||
*/
|
||||
public function __construct($transformer, $resourceKey = null)
|
||||
{
|
||||
parent::__construct(null, $transformer, $resourceKey);
|
||||
}
|
||||
}
|
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
/*
|
||||
* This file is part of Phraseanet
|
||||
*
|
||||
* (c) 2005-2016 Alchemy
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Alchemy\Phrasea\Fractal;
|
||||
|
||||
use League\Fractal\Resource\ResourceInterface;
|
||||
use League\Fractal\Scope;
|
||||
use League\Fractal\TransformerAbstract;
|
||||
|
||||
class ResourceTransformerAccessibleScope extends Scope
|
||||
{
|
||||
/**
|
||||
* @return TransformerAbstract
|
||||
*/
|
||||
public function getResourceTransformer()
|
||||
{
|
||||
$transformer = $this->resource->getTransformer();
|
||||
|
||||
if ($transformer instanceof TransformerAbstract) {
|
||||
return $transformer;
|
||||
}
|
||||
|
||||
return new CallbackTransformer($transformer);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $scopeIdentifier
|
||||
* @param ResourceInterface $resource
|
||||
* @return ResourceTransformerAccessibleScope
|
||||
*/
|
||||
public function createChildScope($scopeIdentifier, ResourceInterface $resource)
|
||||
{
|
||||
$child = new self($this->manager, $resource, $scopeIdentifier);
|
||||
|
||||
$scopeArray = $this->getParentScopes();
|
||||
$scopeArray[] = $this->getScopeIdentifier();
|
||||
|
||||
$child->setParentScopes($scopeArray);
|
||||
|
||||
return $child;
|
||||
}
|
||||
}
|
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
/*
|
||||
* This file is part of Phraseanet
|
||||
*
|
||||
* (c) 2005-2016 Alchemy
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Alchemy\Phrasea\Fractal;
|
||||
|
||||
use League\Fractal\TransformerAbstract;
|
||||
|
||||
class SearchResultTransformerResolver implements TransformerResolver
|
||||
{
|
||||
/**
|
||||
* @var \ArrayAccess|\League\Fractal\TransformerAbstract[]
|
||||
*/
|
||||
private $transformers;
|
||||
|
||||
/**
|
||||
* @param TransformerAbstract[]|\ArrayAccess $transformers
|
||||
*/
|
||||
public function __construct($transformers)
|
||||
{
|
||||
$this->transformers = $transformers;
|
||||
}
|
||||
|
||||
public function resolve($scopeIdentifier)
|
||||
{
|
||||
if (!isset($this->transformers[$scopeIdentifier])) {
|
||||
throw new \RuntimeException(sprintf('Unknown scope identifier: %s', $scopeIdentifier));
|
||||
}
|
||||
|
||||
return $this->transformers[$scopeIdentifier];
|
||||
}
|
||||
}
|
63
lib/Alchemy/Phrasea/Fractal/TraceableArraySerializer.php
Normal file
63
lib/Alchemy/Phrasea/Fractal/TraceableArraySerializer.php
Normal file
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
/*
|
||||
* This file is part of Phraseanet
|
||||
*
|
||||
* (c) 2005-2016 Alchemy
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Alchemy\Phrasea\Fractal;
|
||||
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
|
||||
class TraceableArraySerializer extends ArraySerializer
|
||||
{
|
||||
/**
|
||||
* @var EventDispatcherInterface
|
||||
*/
|
||||
private $dispatcher;
|
||||
|
||||
public function __construct(EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$this->dispatcher = $dispatcher;
|
||||
}
|
||||
|
||||
public function collection($resourceKey, array $data)
|
||||
{
|
||||
/** @var GetSerializationEvent $event */
|
||||
$event = $this->dispatcher->dispatch('fractal.serializer.collection', new GetSerializationEvent($resourceKey, $data));
|
||||
|
||||
$serialization = parent::collection($resourceKey, $data);
|
||||
|
||||
$event->setSerialization($serialization);
|
||||
|
||||
return $serialization;
|
||||
}
|
||||
|
||||
public function item($resourceKey, array $data)
|
||||
{
|
||||
/** @var GetSerializationEvent $event */
|
||||
$event = $this->dispatcher->dispatch('fractal.serializer.item', new GetSerializationEvent($resourceKey, $data));
|
||||
|
||||
$serialization = parent::item($resourceKey, $data);
|
||||
|
||||
$event->setSerialization($serialization);
|
||||
|
||||
return $serialization;
|
||||
}
|
||||
|
||||
public function null($resourceKey)
|
||||
{
|
||||
/** @var GetSerializationEvent $event */
|
||||
$event = $this->dispatcher->dispatch('fractal.serializer.null', new GetSerializationEvent($resourceKey, null));
|
||||
|
||||
$serialization = parent::null($resourceKey);
|
||||
|
||||
$event->setSerialization($serialization);
|
||||
|
||||
return $serialization;
|
||||
}
|
||||
|
||||
}
|
22
lib/Alchemy/Phrasea/Fractal/TransformerResolver.php
Normal file
22
lib/Alchemy/Phrasea/Fractal/TransformerResolver.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
/*
|
||||
* This file is part of Phraseanet
|
||||
*
|
||||
* (c) 2005-2016 Alchemy
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Alchemy\Phrasea\Fractal;
|
||||
|
||||
use League\Fractal\TransformerAbstract;
|
||||
|
||||
interface TransformerResolver
|
||||
{
|
||||
/**
|
||||
* @param string $scopeIdentifier full scope name, empty string for root
|
||||
* @return TransformerAbstract
|
||||
*/
|
||||
public function resolve($scopeIdentifier);
|
||||
}
|
@@ -10,6 +10,8 @@
|
||||
|
||||
namespace Alchemy\Phrasea\Media;
|
||||
|
||||
use Alchemy\Phrasea\Databox\DataboxGroupable;
|
||||
use Alchemy\Phrasea\Record\PerDataboxRecordId;
|
||||
use Alchemy\Phrasea\Record\RecordReference;
|
||||
use Alchemy\Phrasea\Record\RecordReferenceCollection;
|
||||
|
||||
@@ -26,27 +28,40 @@ class TechnicalDataService
|
||||
}
|
||||
|
||||
/**
|
||||
* @param RecordReference[] $references
|
||||
* @param DataboxGroupable|PerDataboxRecordId|RecordReference[] $references
|
||||
* @return RecordTechnicalDataSet[]
|
||||
*/
|
||||
public function fetchRecordsTechnicalData($references)
|
||||
{
|
||||
if (!$references instanceof RecordReferenceCollection) {
|
||||
if (!($references instanceof DataboxGroupable && $references instanceof PerDataboxRecordId)) {
|
||||
$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()];
|
||||
foreach ($references->getDataboxIds() as $databoxId) {
|
||||
$recordIds = $references->getDataboxRecordIds($databoxId);
|
||||
|
||||
$sets[$index] = $set;
|
||||
$setPerRecordId = [];
|
||||
|
||||
foreach ($this->provider->getRepositoryFor($databoxId)->findByRecordIds($recordIds) as $set) {
|
||||
$setPerRecordId[$set->getRecordId()] = $set;
|
||||
}
|
||||
|
||||
$sets[$databoxId] = $setPerRecordId;
|
||||
}
|
||||
|
||||
ksort($sets);
|
||||
$reorder = [];
|
||||
|
||||
return $sets;
|
||||
foreach ($references as $index => $reference) {
|
||||
$databoxId = $reference->getDataboxId();
|
||||
$recordId = $reference->getRecordId();
|
||||
|
||||
$reorder[$index] = isset($sets[$databoxId][$recordId])
|
||||
? $sets[$databoxId][$recordId]
|
||||
: new RecordTechnicalDataSet($recordId);
|
||||
}
|
||||
|
||||
return $reorder;
|
||||
}
|
||||
}
|
||||
|
@@ -187,20 +187,23 @@ class BaseOrderController extends Controller
|
||||
return;
|
||||
}
|
||||
|
||||
$references = new RecordReferenceCollection();
|
||||
$basketReferences = new RecordReferenceCollection();
|
||||
|
||||
$basket->getElements()->forAll(function (BasketElement $element) use ($references) {
|
||||
$references->addRecordReference($element->getSbasId(), $element->getRecordId());
|
||||
$basket->getElements()->forAll(function (BasketElement $element) use ($basketReferences) {
|
||||
$basketReferences->addRecordReference($element->getSbasId(), $element->getRecordId());
|
||||
});
|
||||
|
||||
$toAddReferences = new RecordReferenceCollection();
|
||||
|
||||
foreach ($elements as $element) {
|
||||
$references->addRecordReference($element->getSbasId(), $element->getRecordId());
|
||||
$toAddReferences->addRecordReference($element->getSbasId(), $element->getRecordId());
|
||||
}
|
||||
|
||||
$groups = $references->groupPerDataboxId();
|
||||
foreach ($toAddReferences->getDataboxIds() as $databoxId) {
|
||||
$toAddRecordIds = $toAddReferences->getDataboxRecordIds($databoxId);
|
||||
$basketRecordIds = $basketReferences->getDataboxRecordIds($databoxId);
|
||||
|
||||
foreach ($basket->getElements() as $element) {
|
||||
if (isset($groups[$element->getSbasId()][$element->getRecordId()])) {
|
||||
if (array_intersect($toAddRecordIds, $basketRecordIds)) {
|
||||
throw new ConflictHttpException('Some records have already been handled');
|
||||
}
|
||||
}
|
||||
|
20
lib/Alchemy/Phrasea/Record/PerDataboxRecordId.php
Normal file
20
lib/Alchemy/Phrasea/Record/PerDataboxRecordId.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
/*
|
||||
* This file is part of Phraseanet
|
||||
*
|
||||
* (c) 2005-2016 Alchemy
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Alchemy\Phrasea\Record;
|
||||
|
||||
interface PerDataboxRecordId
|
||||
{
|
||||
/**
|
||||
* @param int $databoxId
|
||||
* @return int[]
|
||||
*/
|
||||
public function getDataboxRecordIds($databoxId);
|
||||
}
|
218
lib/Alchemy/Phrasea/Record/RecordCollection.php
Normal file
218
lib/Alchemy/Phrasea/Record/RecordCollection.php
Normal file
@@ -0,0 +1,218 @@
|
||||
<?php
|
||||
/*
|
||||
* This file is part of Phraseanet
|
||||
*
|
||||
* (c) 2005-2016 Alchemy
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Alchemy\Phrasea\Record;
|
||||
|
||||
use Alchemy\Phrasea\Databox\DataboxGroupable;
|
||||
use Assert\Assertion;
|
||||
|
||||
class RecordCollection implements \IteratorAggregate, \ArrayAccess, \Countable, DataboxGroupable, PerDataboxRecordId
|
||||
{
|
||||
/**
|
||||
* @var \record_adapter[]
|
||||
*/
|
||||
private $records = [];
|
||||
|
||||
/**
|
||||
* @var array<int, int|string>
|
||||
*/
|
||||
private $groups = [];
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $reorderNeeded = false;
|
||||
|
||||
public function __construct($records = [])
|
||||
{
|
||||
Assertion::allIsInstanceOf($records, \record_adapter::class);
|
||||
|
||||
foreach ($records as $index => $record) {
|
||||
$this->add($record, $index);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \record_adapter $record
|
||||
* @param null|int|string $index
|
||||
* @return void
|
||||
*/
|
||||
public function add(\record_adapter $record, $index = null)
|
||||
{
|
||||
if (null === $index) {
|
||||
$this->addWithUnknownIndex($record);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (isset($this->records[$index])){
|
||||
unset($this->groups[$this->records[$index]->getDataboxId()][$index]);
|
||||
$this->reorderNeeded = true;
|
||||
}
|
||||
|
||||
$this->records[$index] = $record;
|
||||
|
||||
$this->addIndexToGroups($record, $index);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \ArrayIterator
|
||||
*/
|
||||
public function getIterator()
|
||||
{
|
||||
return new \ArrayIterator($this->records);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int|string $offset
|
||||
* @return bool
|
||||
*/
|
||||
public function offsetExists($offset)
|
||||
{
|
||||
return isset($this->records[$offset]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int|string $offset
|
||||
* @return \record_adapter
|
||||
*/
|
||||
public function offsetGet($offset)
|
||||
{
|
||||
return $this->records[$offset];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int|string $offset
|
||||
* @param \record_adapter $value
|
||||
*/
|
||||
public function offsetSet($offset, $value)
|
||||
{
|
||||
Assertion::isInstanceOf($value, \record_adapter::class);
|
||||
|
||||
$this->add($value, $offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int|string $offset
|
||||
* @return void
|
||||
*/
|
||||
public function offsetUnset($offset)
|
||||
{
|
||||
if (isset($this->records[$offset])) {
|
||||
unset($this->groups[$this->records[$offset]->getDataboxId()][$offset]);
|
||||
}
|
||||
|
||||
unset($this->records[$offset]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function count()
|
||||
{
|
||||
return count($this->records);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns records groups by databoxId (possibly not in order)
|
||||
*
|
||||
* @return \record_adapter[][]
|
||||
*/
|
||||
public function groupByDatabox()
|
||||
{
|
||||
return $this->groups;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int[]
|
||||
*/
|
||||
public function getDataboxIds()
|
||||
{
|
||||
return array_keys($this->groups);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $databoxId
|
||||
* @return \record_adapter[]
|
||||
*/
|
||||
public function getDataboxGroup($databoxId)
|
||||
{
|
||||
return isset($this->groups[$databoxId]) ? $this->groups[$databoxId] : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function reorderGroups()
|
||||
{
|
||||
if (!$this->reorderNeeded) {
|
||||
return;
|
||||
}
|
||||
|
||||
$groups = [];
|
||||
|
||||
foreach ($this->records as $index => $record) {
|
||||
$databoxId = $record->getDataboxId();
|
||||
|
||||
if (!isset($groups[$databoxId])) {
|
||||
$groups[$databoxId] = [];
|
||||
}
|
||||
|
||||
$groups[$databoxId][$index] = $record;
|
||||
}
|
||||
|
||||
$this->groups = $groups;
|
||||
$this->reorderNeeded = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $databoxId
|
||||
* @return int[]
|
||||
*/
|
||||
public function getDataboxRecordIds($databoxId)
|
||||
{
|
||||
$recordIds = [];
|
||||
|
||||
foreach ($this->getDataboxGroup($databoxId) as $record) {
|
||||
$recordIds[$record->getRecordId()] = true;
|
||||
}
|
||||
|
||||
return array_keys($recordIds);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \record_adapter $record
|
||||
* @return void
|
||||
*/
|
||||
private function addWithUnknownIndex(\record_adapter $record)
|
||||
{
|
||||
$this->records[] = $record;
|
||||
|
||||
end($this->records);
|
||||
|
||||
$this->addIndexToGroups($record, key($this->records));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \record_adapter $record
|
||||
* @param int|string $index
|
||||
* @return void
|
||||
*/
|
||||
private function addIndexToGroups(\record_adapter $record, $index)
|
||||
{
|
||||
$databoxId = $record->getDataboxId();
|
||||
|
||||
if (!isset($this->groups[$databoxId])) {
|
||||
$this->groups[$databoxId] = [];
|
||||
}
|
||||
|
||||
$this->groups[$databoxId][$index] = $record;
|
||||
}
|
||||
}
|
@@ -10,10 +10,11 @@
|
||||
|
||||
namespace Alchemy\Phrasea\Record;
|
||||
|
||||
use Alchemy\Phrasea\Databox\DataboxGroupable;
|
||||
use Alchemy\Phrasea\Model\RecordReferenceInterface;
|
||||
use Assert\Assertion;
|
||||
|
||||
class RecordReferenceCollection implements \IteratorAggregate, \ArrayAccess
|
||||
class RecordReferenceCollection implements \IteratorAggregate, \ArrayAccess, \Countable, DataboxGroupable, PerDataboxRecordId
|
||||
{
|
||||
/**
|
||||
* @param array<int|string,array> $records
|
||||
@@ -126,34 +127,16 @@ class RecordReferenceCollection implements \IteratorAggregate, \ArrayAccess
|
||||
return new \ArrayIterator($this->references);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int,array<int,int>>
|
||||
*/
|
||||
public function groupPerDataboxId()
|
||||
{
|
||||
if (null === $this->groups) {
|
||||
$this->groups = [];
|
||||
|
||||
foreach ($this->references as $index => $reference) {
|
||||
$databoxId = $reference->getDataboxId();
|
||||
|
||||
if (!isset($this->groups[$databoxId])) {
|
||||
$this->groups[$databoxId] = [];
|
||||
}
|
||||
|
||||
$this->groups[$databoxId][$reference->getRecordId()] = $index;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->groups;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getDataboxIds()
|
||||
{
|
||||
return array_keys($this->groupPerDataboxId());
|
||||
if (null === $this->groups) {
|
||||
$this->reorderGroups();
|
||||
}
|
||||
|
||||
return array_keys($this->groups);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -162,43 +145,43 @@ class RecordReferenceCollection implements \IteratorAggregate, \ArrayAccess
|
||||
*/
|
||||
public function toRecords(\appbox $appbox)
|
||||
{
|
||||
$groups = $this->groupPerDataboxId();
|
||||
$databoxIds = $this->getDataboxIds();
|
||||
$records = array_fill_keys($databoxIds, []);
|
||||
|
||||
$records = [];
|
||||
|
||||
foreach ($groups as $databoxId => $recordIds) {
|
||||
foreach ($databoxIds as $databoxId) {
|
||||
$databox = $appbox->get_databox($databoxId);
|
||||
$recordIds = $this->getDataboxRecordIds($databoxId);
|
||||
|
||||
foreach ($databox->getRecordRepository()->findByRecordIds(array_keys($recordIds)) as $record) {
|
||||
$records[$recordIds[$record->getRecordId()]] = $record;
|
||||
foreach ($databox->getRecordRepository()->findByRecordIds($recordIds) as $record) {
|
||||
$records[$record->getDataboxId()][$record->getRecordId()] = $record;
|
||||
}
|
||||
}
|
||||
|
||||
$indexes = array_flip(array_keys($this->references));
|
||||
$sorted = [];
|
||||
|
||||
uksort($records, function ($keyA, $keyB) use ($indexes) {
|
||||
$indexA = $indexes[$keyA];
|
||||
$indexB = $indexes[$keyB];
|
||||
foreach ($this->references as $index => $reference) {
|
||||
$databoxId = $reference->getDataboxId();
|
||||
$recordId = $reference->getRecordId();
|
||||
|
||||
if ($indexA < $indexB) {
|
||||
return -1;
|
||||
} elseif ($indexA > $indexB) {
|
||||
return 1;
|
||||
if (isset($records[$databoxId][$recordId])) {
|
||||
$sorted[$index] = $records[$databoxId][$recordId];
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
});
|
||||
|
||||
return $records;
|
||||
return $sorted;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int|string $offset
|
||||
* @return bool
|
||||
*/
|
||||
public function offsetExists($offset)
|
||||
{
|
||||
return isset($this->references[$offset]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $offset
|
||||
* @param int|string $offset
|
||||
* @return RecordReferenceInterface
|
||||
*/
|
||||
public function offsetGet($offset)
|
||||
@@ -206,6 +189,10 @@ class RecordReferenceCollection implements \IteratorAggregate, \ArrayAccess
|
||||
return $this->references[$offset];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int|string $offset
|
||||
* @param RecordReferenceInterface $value
|
||||
*/
|
||||
public function offsetSet($offset, $value)
|
||||
{
|
||||
Assertion::isInstanceOf($value, RecordReferenceInterface::class);
|
||||
@@ -213,9 +200,74 @@ class RecordReferenceCollection implements \IteratorAggregate, \ArrayAccess
|
||||
$this->add($value, $offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int|string $offset
|
||||
* @return void
|
||||
*/
|
||||
public function offsetUnset($offset)
|
||||
{
|
||||
unset($this->references[$offset]);
|
||||
$this->groups = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return RecordReferenceInterface[][]
|
||||
*/
|
||||
public function groupByDatabox()
|
||||
{
|
||||
if (null === $this->groups) {
|
||||
$this->reorderGroups();
|
||||
}
|
||||
|
||||
return $this->groups;
|
||||
}
|
||||
|
||||
public function reorderGroups()
|
||||
{
|
||||
if (null !== $this->groups) {
|
||||
return;
|
||||
}
|
||||
|
||||
$groups = [];
|
||||
|
||||
foreach ($this->references as $index => $reference) {
|
||||
if (!isset($groups[$reference->getDataboxId()])) {
|
||||
$groups[$reference->getDataboxId()] = [];
|
||||
}
|
||||
|
||||
$groups[$reference->getDataboxId()][$index] = $reference;
|
||||
}
|
||||
|
||||
$this->groups = $groups;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $databoxId
|
||||
* @return RecordReferenceInterface[]
|
||||
*/
|
||||
public function getDataboxGroup($databoxId)
|
||||
{
|
||||
// avoid call to reorderGroups when not needed
|
||||
if (null === $this->groups) {
|
||||
$this->reorderGroups();
|
||||
}
|
||||
|
||||
return isset($this->groups[$databoxId]) ? $this->groups[$databoxId] : [];
|
||||
}
|
||||
|
||||
public function getDataboxRecordIds($databoxId)
|
||||
{
|
||||
$indexes = [];
|
||||
|
||||
foreach ($this->getDataboxGroup($databoxId) as $index => $references) {
|
||||
$indexes[$references->getRecordId()] = $index;
|
||||
}
|
||||
|
||||
return array_flip($indexes);
|
||||
}
|
||||
|
||||
public function count()
|
||||
{
|
||||
return count($this->references);
|
||||
}
|
||||
}
|
||||
|
35
lib/Alchemy/Phrasea/Search/CaptionAware.php
Normal file
35
lib/Alchemy/Phrasea/Search/CaptionAware.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
/*
|
||||
* This file is part of Phraseanet
|
||||
*
|
||||
* (c) 2005-2016 Alchemy
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Alchemy\Phrasea\Search;
|
||||
|
||||
trait CaptionAware
|
||||
{
|
||||
/**
|
||||
* @var CaptionView
|
||||
*/
|
||||
private $caption;
|
||||
|
||||
/**
|
||||
* @param CaptionView $caption
|
||||
*/
|
||||
public function setCaption(CaptionView $caption)
|
||||
{
|
||||
$this->caption = $caption;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return CaptionView
|
||||
*/
|
||||
public function getCaption()
|
||||
{
|
||||
return $this->caption;
|
||||
}
|
||||
}
|
61
lib/Alchemy/Phrasea/Search/CaptionView.php
Normal file
61
lib/Alchemy/Phrasea/Search/CaptionView.php
Normal file
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
/*
|
||||
* This file is part of Phraseanet
|
||||
*
|
||||
* (c) 2005-2016 Alchemy
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Alchemy\Phrasea\Search;
|
||||
|
||||
use Assert\Assertion;
|
||||
|
||||
class CaptionView
|
||||
{
|
||||
/**
|
||||
* @var \caption_record
|
||||
*/
|
||||
private $caption;
|
||||
|
||||
/**
|
||||
* @var \caption_field[]
|
||||
*/
|
||||
private $fields = [];
|
||||
|
||||
public function __construct(\caption_record $caption)
|
||||
{
|
||||
$this->caption = $caption;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \caption_record
|
||||
*/
|
||||
public function getCaption()
|
||||
{
|
||||
return $this->caption;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \caption_field[] $fields
|
||||
*/
|
||||
public function setFields($fields)
|
||||
{
|
||||
Assertion::allIsInstanceOf($fields, \caption_field::class);
|
||||
|
||||
$this->fields = [];
|
||||
|
||||
foreach ($fields as $field) {
|
||||
$this->fields[$field->get_name()] = $field;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \caption_field[]
|
||||
*/
|
||||
public function getFields()
|
||||
{
|
||||
return $this->fields;
|
||||
}
|
||||
}
|
36
lib/Alchemy/Phrasea/Search/PermalinkTransformer.php
Normal file
36
lib/Alchemy/Phrasea/Search/PermalinkTransformer.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
/*
|
||||
* This file is part of Phraseanet
|
||||
*
|
||||
* (c) 2005-2016 Alchemy
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Alchemy\Phrasea\Search;
|
||||
|
||||
use League\Fractal\TransformerAbstract;
|
||||
|
||||
class PermalinkTransformer extends TransformerAbstract
|
||||
{
|
||||
public function transform(PermalinkView $permalinkView)
|
||||
{
|
||||
$permalink = $permalinkView->getPermalink();
|
||||
|
||||
$downloadUrl = $permalink->get_url();
|
||||
$downloadUrl->getQuery()->set('download', '1');
|
||||
|
||||
return [
|
||||
'created_on' => $permalink->get_created_on()->format(DATE_ATOM),
|
||||
'id' => $permalink->get_id(),
|
||||
'is_activated' => $permalink->get_is_activated(),
|
||||
/** @Ignore */
|
||||
'label' => $permalink->get_label(),
|
||||
'updated_on' => $permalink->get_last_modified()->format(DATE_ATOM),
|
||||
'page_url' => $permalink->get_page(),
|
||||
'download_url' => (string)$downloadUrl,
|
||||
'url' => (string)$permalink->get_url(),
|
||||
];
|
||||
}
|
||||
}
|
32
lib/Alchemy/Phrasea/Search/PermalinkView.php
Normal file
32
lib/Alchemy/Phrasea/Search/PermalinkView.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
/*
|
||||
* This file is part of Phraseanet
|
||||
*
|
||||
* (c) 2005-2016 Alchemy
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Alchemy\Phrasea\Search;
|
||||
|
||||
class PermalinkView
|
||||
{
|
||||
/**
|
||||
* @var \media_Permalink_Adapter
|
||||
*/
|
||||
private $permalink;
|
||||
|
||||
public function __construct(\media_Permalink_Adapter $permalink)
|
||||
{
|
||||
$this->permalink = $permalink;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \media_Permalink_Adapter
|
||||
*/
|
||||
public function getPermalink()
|
||||
{
|
||||
return $this->permalink;
|
||||
}
|
||||
}
|
138
lib/Alchemy/Phrasea/Search/RecordTransformer.php
Normal file
138
lib/Alchemy/Phrasea/Search/RecordTransformer.php
Normal file
@@ -0,0 +1,138 @@
|
||||
<?php
|
||||
/*
|
||||
* This file is part of Phraseanet
|
||||
*
|
||||
* (c) 2005-2016 Alchemy
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Alchemy\Phrasea\Search;
|
||||
|
||||
use League\Fractal\TransformerAbstract;
|
||||
|
||||
class RecordTransformer extends TransformerAbstract
|
||||
{
|
||||
protected $availableIncludes = ['thumbnail', 'technical_informations', 'subdefs', 'metadata', 'status', 'caption'];
|
||||
protected $defaultIncludes = ['thumbnail', 'technical_informations'];
|
||||
|
||||
/**
|
||||
* @var SubdefTransformer
|
||||
*/
|
||||
private $subdefTransformer;
|
||||
|
||||
/**
|
||||
* @var TechnicalDataTransformer
|
||||
*/
|
||||
private $technicalDataTransformer;
|
||||
|
||||
public function __construct(SubdefTransformer $subdefTransformer, TechnicalDataTransformer $technicalDataTransformer)
|
||||
{
|
||||
$this->subdefTransformer = $subdefTransformer;
|
||||
$this->technicalDataTransformer = $technicalDataTransformer;
|
||||
}
|
||||
|
||||
public function transform(RecordView $recordView)
|
||||
{
|
||||
$record = $recordView->getRecord();
|
||||
|
||||
return [
|
||||
'databox_id' => $record->getDataboxId(),
|
||||
'record_id' => $record->getRecordId(),
|
||||
'mime_type' => $record->getMimeType(),
|
||||
'title' => $record->get_title(),
|
||||
'original_name' => $record->get_original_name(),
|
||||
'updated_on' => $record->getUpdated()->format(DATE_ATOM),
|
||||
'created_on' => $record->getCreated()->format(DATE_ATOM),
|
||||
'collection_id' => $record->getCollectionId(),
|
||||
'base_id' => $record->getBaseId(),
|
||||
'sha256' => $record->getSha256(),
|
||||
'phrasea_type' => $record->getType(),
|
||||
'uuid' => $record->getUuid(),
|
||||
];
|
||||
}
|
||||
|
||||
public function includeThumbnail(RecordView $recordView)
|
||||
{
|
||||
return $this->item($recordView->getSubdef('thumbnail'), $this->subdefTransformer);
|
||||
}
|
||||
|
||||
public function includeTechnicalInformations(RecordView $recordView)
|
||||
{
|
||||
return $this->collection($recordView->getTechnicalDataView()->getDataSet(), $this->technicalDataTransformer);
|
||||
}
|
||||
|
||||
public function includeSubdefs(RecordView $recordView)
|
||||
{
|
||||
return $this->collection($recordView->getSubdefs(), $this->subdefTransformer);
|
||||
}
|
||||
|
||||
public function includeMetadata(RecordView $recordView)
|
||||
{
|
||||
$fieldData = [];
|
||||
$values = [];
|
||||
|
||||
foreach ($recordView->getCaption()->getFields() as $field) {
|
||||
$databox_field = $field->get_databox_field();
|
||||
|
||||
$fieldData[$field->get_meta_struct_id()] = [
|
||||
'meta_structure_id' => $field->get_meta_struct_id(),
|
||||
'name' => $field->get_name(),
|
||||
'labels' => [
|
||||
'fr' => $databox_field->get_label('fr'),
|
||||
'en' => $databox_field->get_label('en'),
|
||||
'de' => $databox_field->get_label('de'),
|
||||
'nl' => $databox_field->get_label('nl'),
|
||||
],
|
||||
];
|
||||
|
||||
$values[] = $field->get_values();
|
||||
}
|
||||
|
||||
if ($values) {
|
||||
$values = call_user_func_array('array_merge', $values);
|
||||
}
|
||||
|
||||
return $this->collection($values, function (\caption_Field_Value $value) use ($fieldData) {
|
||||
$data = $fieldData[$value->getDatabox_field()->get_id()];
|
||||
|
||||
$data['meta_id'] = $value->getId();
|
||||
$data['value'] = $value->getValue();
|
||||
|
||||
return $data;
|
||||
});
|
||||
}
|
||||
|
||||
public function includeStatus(RecordView $recordView)
|
||||
{
|
||||
$data = [];
|
||||
|
||||
$bitMask = $recordView->getRecord()->getStatusBitField();
|
||||
|
||||
foreach ($recordView->getRecord()->getDatabox()->getStatusStructure() as $bit => $status) {
|
||||
$data[] = [
|
||||
'bit' => $bit,
|
||||
'mask' => $bitMask,
|
||||
];
|
||||
}
|
||||
|
||||
return $this->collection($data, function (array $bitData) {
|
||||
return [
|
||||
'bit' => $bitData['bit'],
|
||||
'state' => \databox_status::bitIsSet($bitData['mask'], $bitData['bit']),
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
public function includeCaption(RecordView $recordView)
|
||||
{
|
||||
return $this->collection($recordView->getCaption()->getFields(), function (\caption_field $field) {
|
||||
return [
|
||||
'meta_structure_id' => $field->get_meta_struct_id(),
|
||||
'name' => $field->get_name(),
|
||||
'value' => $field->get_serialized_values(';'),
|
||||
];
|
||||
});
|
||||
}
|
||||
}
|
53
lib/Alchemy/Phrasea/Search/RecordView.php
Normal file
53
lib/Alchemy/Phrasea/Search/RecordView.php
Normal file
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
/*
|
||||
* This file is part of Phraseanet
|
||||
*
|
||||
* (c) 2005-2016 Alchemy
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Alchemy\Phrasea\Search;
|
||||
|
||||
class RecordView
|
||||
{
|
||||
use SubdefsAware;
|
||||
use CaptionAware;
|
||||
|
||||
/**
|
||||
* @var \record_adapter
|
||||
*/
|
||||
private $record;
|
||||
|
||||
/**
|
||||
* @var TechnicalDataView
|
||||
*/
|
||||
private $technicalDataView;
|
||||
|
||||
public function __construct(\record_adapter $record)
|
||||
{
|
||||
$this->record = $record;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \record_adapter
|
||||
*/
|
||||
public function getRecord()
|
||||
{
|
||||
return $this->record;
|
||||
}
|
||||
|
||||
public function setTechnicalDataView(TechnicalDataView $technicalDataView)
|
||||
{
|
||||
$this->technicalDataView = $technicalDataView;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return TechnicalDataView
|
||||
*/
|
||||
public function getTechnicalDataView()
|
||||
{
|
||||
return $this->technicalDataView;
|
||||
}
|
||||
}
|
83
lib/Alchemy/Phrasea/Search/SearchResultView.php
Normal file
83
lib/Alchemy/Phrasea/Search/SearchResultView.php
Normal file
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
/*
|
||||
* This file is part of Phraseanet
|
||||
*
|
||||
* (c) 2005-2016 Alchemy
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Alchemy\Phrasea\Search;
|
||||
|
||||
use Alchemy\Phrasea\SearchEngine\SearchEngineResult;
|
||||
use Assert\Assertion;
|
||||
|
||||
class SearchResultView
|
||||
{
|
||||
/**
|
||||
* @var SearchEngineResult
|
||||
*/
|
||||
private $result;
|
||||
|
||||
/**
|
||||
* @var StoryView[]
|
||||
*/
|
||||
private $stories = [];
|
||||
|
||||
/**
|
||||
* @var RecordView[]
|
||||
*/
|
||||
private $records = [];
|
||||
|
||||
public function __construct(SearchEngineResult $result)
|
||||
{
|
||||
$this->result = $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return SearchEngineResult
|
||||
*/
|
||||
public function getResult()
|
||||
{
|
||||
return $this->result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param StoryView[] $stories
|
||||
* @return void
|
||||
*/
|
||||
public function setStories($stories)
|
||||
{
|
||||
Assertion::allIsInstanceOf($stories, StoryView::class);
|
||||
|
||||
$this->stories = $stories instanceof \Traversable ? iterator_to_array($stories, false) : array_values($stories);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return StoryView[]
|
||||
*/
|
||||
public function getStories()
|
||||
{
|
||||
return $this->stories;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param RecordView[] $records
|
||||
* @return void
|
||||
*/
|
||||
public function setRecords($records)
|
||||
{
|
||||
Assertion::allIsInstanceOf($records, RecordView::class);
|
||||
|
||||
$this->records = $records instanceof \Traversable ? iterator_to_array($records, false) : array_values($records);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return RecordView[]
|
||||
*/
|
||||
public function getRecords()
|
||||
{
|
||||
return $this->records;
|
||||
}
|
||||
}
|
109
lib/Alchemy/Phrasea/Search/StoryTransformer.php
Normal file
109
lib/Alchemy/Phrasea/Search/StoryTransformer.php
Normal file
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
/*
|
||||
* This file is part of Phraseanet
|
||||
*
|
||||
* (c) 2005-2016 Alchemy
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Alchemy\Phrasea\Search;
|
||||
|
||||
use Alchemy\Phrasea\Utilities\NullableDateTime;
|
||||
use League\Fractal\TransformerAbstract;
|
||||
|
||||
class StoryTransformer extends TransformerAbstract
|
||||
{
|
||||
protected $availableIncludes = ['thumbnail', 'metadatas', 'records'];
|
||||
protected $defaultIncludes = ['thumbnail', 'metadatas', 'records'];
|
||||
|
||||
/**
|
||||
* @var SubdefTransformer
|
||||
*/
|
||||
private $subdefTransformer;
|
||||
|
||||
/**
|
||||
* @var RecordTransformer
|
||||
*/
|
||||
private $recordTransformer;
|
||||
|
||||
/**
|
||||
* @param SubdefTransformer $subdefTransformer
|
||||
* @param RecordTransformer $recordTransformer
|
||||
*/
|
||||
public function __construct(SubdefTransformer $subdefTransformer, RecordTransformer $recordTransformer)
|
||||
{
|
||||
$this->subdefTransformer = $subdefTransformer;
|
||||
$this->recordTransformer = $recordTransformer;
|
||||
}
|
||||
|
||||
public function transform(StoryView $storyView)
|
||||
{
|
||||
$story = $storyView->getStory();
|
||||
|
||||
return [
|
||||
'@entity@' => 'http://api.phraseanet.com/api/objects/story',
|
||||
'databox_id' => $story->getDataboxId(),
|
||||
'story_id' => $story->getRecordId(),
|
||||
'updated_on' => NullableDateTime::format($story->getUpdated()),
|
||||
'created_on' => NullableDateTime::format($story->getUpdated()),
|
||||
'collection_id' => $story->getCollectionId(),
|
||||
'base_id' => $story->getBaseId(),
|
||||
'uuid' => $story->getUuid(),
|
||||
];
|
||||
}
|
||||
|
||||
public function includeThumbnail(StoryView $storyView)
|
||||
{
|
||||
return $this->item($storyView->getSubdef('thumbnail'), $this->subdefTransformer);
|
||||
}
|
||||
|
||||
public function includeMetadatas(StoryView $storyView)
|
||||
{
|
||||
return $this->item($storyView->getCaption(), $this->getCaptionTransformer());
|
||||
}
|
||||
|
||||
public function includeRecords(StoryView $storyView)
|
||||
{
|
||||
return $this->collection($storyView->getChildren(), $this->recordTransformer);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Closure
|
||||
*/
|
||||
private function getCaptionTransformer()
|
||||
{
|
||||
/**
|
||||
* @param \caption_field[] $fields
|
||||
* @param string $dcField
|
||||
* @return string|null
|
||||
*/
|
||||
$format = function ($fields, $dcField) {
|
||||
return isset($fields[$dcField]) ? $fields[$dcField]->get_serialized_values() : null;
|
||||
};
|
||||
|
||||
return function (CaptionView $captionView) use ($format) {
|
||||
$caption = $captionView->getCaption()->getDCFields();
|
||||
|
||||
return [
|
||||
'@entity@' => 'http://api.phraseanet.com/api/objects/story-metadata-bag',
|
||||
'dc:contributor' => $format($caption, \databox_Field_DCESAbstract::Contributor),
|
||||
'dc:coverage' => $format($caption, \databox_Field_DCESAbstract::Coverage),
|
||||
'dc:creator' => $format($caption, \databox_Field_DCESAbstract::Creator),
|
||||
'dc:date' => $format($caption, \databox_Field_DCESAbstract::Date),
|
||||
'dc:description' => $format($caption, \databox_Field_DCESAbstract::Description),
|
||||
'dc:format' => $format($caption, \databox_Field_DCESAbstract::Format),
|
||||
'dc:identifier' => $format($caption, \databox_Field_DCESAbstract::Identifier),
|
||||
'dc:language' => $format($caption, \databox_Field_DCESAbstract::Language),
|
||||
'dc:publisher' => $format($caption, \databox_Field_DCESAbstract::Publisher),
|
||||
'dc:relation' => $format($caption, \databox_Field_DCESAbstract::Relation),
|
||||
'dc:rights' => $format($caption, \databox_Field_DCESAbstract::Rights),
|
||||
'dc:source' => $format($caption, \databox_Field_DCESAbstract::Source),
|
||||
'dc:subject' => $format($caption, \databox_Field_DCESAbstract::Subject),
|
||||
'dc:title' => $format($caption, \databox_Field_DCESAbstract::Title),
|
||||
'dc:type' => $format($caption, \databox_Field_DCESAbstract::Type),
|
||||
];
|
||||
};
|
||||
}
|
||||
}
|
63
lib/Alchemy/Phrasea/Search/StoryView.php
Normal file
63
lib/Alchemy/Phrasea/Search/StoryView.php
Normal file
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
/*
|
||||
* This file is part of Phraseanet
|
||||
*
|
||||
* (c) 2005-2016 Alchemy
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Alchemy\Phrasea\Search;
|
||||
|
||||
use Assert\Assertion;
|
||||
|
||||
class StoryView
|
||||
{
|
||||
use SubdefsAware;
|
||||
use CaptionAware;
|
||||
|
||||
/**
|
||||
* @var \record_adapter
|
||||
*/
|
||||
private $story;
|
||||
/**
|
||||
* @var RecordView[]
|
||||
*/
|
||||
private $children = [];
|
||||
|
||||
/**
|
||||
* @param \record_adapter $story
|
||||
*/
|
||||
public function __construct(\record_adapter $story)
|
||||
{
|
||||
|
||||
$this->story = $story;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \record_adapter
|
||||
*/
|
||||
public function getStory()
|
||||
{
|
||||
return $this->story;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param RecordView[] $children
|
||||
*/
|
||||
public function setChildren($children)
|
||||
{
|
||||
Assertion::allIsInstanceOf($children, RecordView::class);
|
||||
|
||||
$this->children = $children instanceof \Traversable ? iterator_to_array($children, false) : array_values($children);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return RecordView[]
|
||||
*/
|
||||
public function getChildren()
|
||||
{
|
||||
return $this->children;
|
||||
}
|
||||
}
|
82
lib/Alchemy/Phrasea/Search/SubdefTransformer.php
Normal file
82
lib/Alchemy/Phrasea/Search/SubdefTransformer.php
Normal file
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
/*
|
||||
* This file is part of Phraseanet
|
||||
*
|
||||
* (c) 2005-2016 Alchemy
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Alchemy\Phrasea\Search;
|
||||
|
||||
use Alchemy\Phrasea\Authentication\ACLProvider;
|
||||
use Alchemy\Phrasea\Model\Entities\User;
|
||||
use League\Fractal\TransformerAbstract;
|
||||
|
||||
class SubdefTransformer extends TransformerAbstract
|
||||
{
|
||||
/**
|
||||
* @var ACLProvider
|
||||
*/
|
||||
private $aclProvider;
|
||||
|
||||
/**
|
||||
* @var User
|
||||
*/
|
||||
private $user;
|
||||
|
||||
/**
|
||||
* @var PermalinkTransformer
|
||||
*/
|
||||
private $permalinkTransformer;
|
||||
|
||||
public function __construct(ACLProvider $aclProvider, User $user, PermalinkTransformer $permalinkTransformer)
|
||||
{
|
||||
$this->aclProvider = $aclProvider;
|
||||
$this->user = $user;
|
||||
$this->permalinkTransformer = $permalinkTransformer;
|
||||
}
|
||||
|
||||
public function transform(SubdefView $subdefView)
|
||||
{
|
||||
$media = $subdefView->getSubdef();
|
||||
|
||||
if (!$media->is_physically_present()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$acl = $this->aclProvider->get($this->user);
|
||||
$record = $media->get_record();
|
||||
|
||||
if ($media->get_name() !== 'document' && false === $acl->has_access_to_subdef($record, $media->get_name())) {
|
||||
return null;
|
||||
}
|
||||
if ($media->get_name() === 'document'
|
||||
&& !$acl->has_right_on_base($record->getBaseId(), 'candwnldhd')
|
||||
&& !$acl->has_hd_grant($record)
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$permalink = $subdefView->getPermalinkView()
|
||||
? $this->permalinkTransformer->transform($subdefView->getPermalinkView())
|
||||
: null;
|
||||
|
||||
return [
|
||||
'name' => $media->get_name(),
|
||||
'permalink' => $permalink,
|
||||
'height' => $media->get_height(),
|
||||
'width' => $media->get_width(),
|
||||
'filesize' => $media->get_size(),
|
||||
'devices' => $media->getDevices(),
|
||||
'player_type' => $media->get_type(),
|
||||
'mime_type' => $media->get_mime(),
|
||||
'substituted' => $media->is_substituted(),
|
||||
'created_on' => $media->get_creation_date()->format(DATE_ATOM),
|
||||
'updated_on' => $media->get_modification_date()->format(DATE_ATOM),
|
||||
'url' => $subdefView->getUrl(),
|
||||
'url_ttl' => $subdefView->getUrlTTL(),
|
||||
];
|
||||
}
|
||||
}
|
99
lib/Alchemy/Phrasea/Search/SubdefView.php
Normal file
99
lib/Alchemy/Phrasea/Search/SubdefView.php
Normal file
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
/*
|
||||
* This file is part of Phraseanet
|
||||
*
|
||||
* (c) 2005-2016 Alchemy
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Alchemy\Phrasea\Search;
|
||||
|
||||
use Assert\Assertion;
|
||||
|
||||
class SubdefView
|
||||
{
|
||||
/**
|
||||
* @var \media_subdef
|
||||
*/
|
||||
private $subdef;
|
||||
|
||||
/**
|
||||
* @var PermalinkView
|
||||
*/
|
||||
private $permalinkView;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $url;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $urlTTL;
|
||||
|
||||
public function __construct(\media_subdef $subdef)
|
||||
{
|
||||
$this->subdef = $subdef;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \media_subdef
|
||||
*/
|
||||
public function getSubdef()
|
||||
{
|
||||
return $this->subdef;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param PermalinkView $permalinkView
|
||||
*/
|
||||
public function setPermalinkView($permalinkView)
|
||||
{
|
||||
$this->permalinkView = $permalinkView;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return PermalinkView
|
||||
*/
|
||||
public function getPermalinkView()
|
||||
{
|
||||
return $this->permalinkView;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $url
|
||||
*/
|
||||
public function setUrl($url)
|
||||
{
|
||||
$this->url = (string)$url;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getUrl()
|
||||
{
|
||||
return $this->url;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param null|int $urlTTL
|
||||
*/
|
||||
public function setUrlTTL($urlTTL)
|
||||
{
|
||||
Assertion::nullOrIntegerish($urlTTL);
|
||||
|
||||
$this->urlTTL = null === $urlTTL ? null : (int)$urlTTL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return null|int
|
||||
*/
|
||||
public function getUrlTTL()
|
||||
{
|
||||
return $this->urlTTL;
|
||||
}
|
||||
}
|
56
lib/Alchemy/Phrasea/Search/SubdefsAware.php
Normal file
56
lib/Alchemy/Phrasea/Search/SubdefsAware.php
Normal file
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
/*
|
||||
* This file is part of Phraseanet
|
||||
*
|
||||
* (c) 2005-2016 Alchemy
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Alchemy\Phrasea\Search;
|
||||
|
||||
use Assert\Assertion;
|
||||
|
||||
trait SubdefsAware
|
||||
{
|
||||
/**
|
||||
* @var SubdefView[]
|
||||
*/
|
||||
private $subdefs = [];
|
||||
|
||||
/**
|
||||
* @param SubdefView[] $subdefs
|
||||
*/
|
||||
public function setSubdefs($subdefs)
|
||||
{
|
||||
Assertion::allIsInstanceOf($subdefs, SubdefView::class);
|
||||
|
||||
$this->subdefs = [];
|
||||
|
||||
foreach ($subdefs as $subdef) {
|
||||
$this->subdefs[$subdef->getSubdef()->get_name()] = $subdef;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @return SubdefView
|
||||
*/
|
||||
public function getSubdef($name)
|
||||
{
|
||||
if (isset($this->subdefs[$name])) {
|
||||
return $this->subdefs[$name];
|
||||
}
|
||||
|
||||
throw new \OutOfBoundsException(sprintf('There are no subdef named "%s"', $name));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return SubdefView
|
||||
*/
|
||||
public function getSubdefs()
|
||||
{
|
||||
return $this->subdefs;
|
||||
}
|
||||
}
|
25
lib/Alchemy/Phrasea/Search/TechnicalDataTransformer.php
Normal file
25
lib/Alchemy/Phrasea/Search/TechnicalDataTransformer.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
/*
|
||||
* This file is part of Phraseanet
|
||||
*
|
||||
* (c) 2005-2016 Alchemy
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Alchemy\Phrasea\Search;
|
||||
|
||||
use Alchemy\Phrasea\Media\TechnicalData;
|
||||
use League\Fractal\TransformerAbstract;
|
||||
|
||||
class TechnicalDataTransformer extends TransformerAbstract
|
||||
{
|
||||
public function transform(TechnicalData $technicalData)
|
||||
{
|
||||
return [
|
||||
'name' => $technicalData->getName(),
|
||||
'value' => $technicalData->getValue(),
|
||||
];
|
||||
}
|
||||
}
|
34
lib/Alchemy/Phrasea/Search/TechnicalDataView.php
Normal file
34
lib/Alchemy/Phrasea/Search/TechnicalDataView.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
/*
|
||||
* This file is part of Phraseanet
|
||||
*
|
||||
* (c) 2005-2016 Alchemy
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Alchemy\Phrasea\Search;
|
||||
|
||||
use Alchemy\Phrasea\Media\TechnicalDataSet;
|
||||
|
||||
class TechnicalDataView
|
||||
{
|
||||
/**
|
||||
* @var TechnicalDataSet
|
||||
*/
|
||||
private $dataSet;
|
||||
|
||||
public function __construct(TechnicalDataSet $dataSet)
|
||||
{
|
||||
$this->dataSet = $dataSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return TechnicalDataSet
|
||||
*/
|
||||
public function getDataSet()
|
||||
{
|
||||
return $this->dataSet;
|
||||
}
|
||||
}
|
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
/*
|
||||
* This file is part of Phraseanet
|
||||
*
|
||||
* (c) 2005-2016 Alchemy
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Alchemy\Phrasea\Search;
|
||||
|
||||
use League\Fractal\TransformerAbstract;
|
||||
|
||||
class V1SearchCompositeResultTransformer extends TransformerAbstract
|
||||
{
|
||||
protected $availableIncludes = ['stories', 'records'];
|
||||
protected $defaultIncludes = ['stories', 'records'];
|
||||
|
||||
/**
|
||||
* @var RecordTransformer
|
||||
*/
|
||||
private $recordTransformer;
|
||||
/**
|
||||
* @var StoryTransformer
|
||||
*/
|
||||
private $storyTransformer;
|
||||
|
||||
public function __construct(RecordTransformer $recordTransformer, StoryTransformer $storyTransformer)
|
||||
{
|
||||
$this->recordTransformer = $recordTransformer;
|
||||
$this->storyTransformer = $storyTransformer;
|
||||
}
|
||||
|
||||
public function transform()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function includeRecords(SearchResultView $resultView)
|
||||
{
|
||||
return $this->collection($resultView->getRecords(), $this->recordTransformer);
|
||||
}
|
||||
|
||||
public function includeStories(SearchResultView $resultView)
|
||||
{
|
||||
return $this->collection($resultView->getStories(), $this->storyTransformer);
|
||||
}
|
||||
}
|
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
/*
|
||||
* This file is part of Phraseanet
|
||||
*
|
||||
* (c) 2005-2016 Alchemy
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Alchemy\Phrasea\Search;
|
||||
|
||||
class V1SearchRecordsResultTransformer extends V1SearchTransformer
|
||||
{
|
||||
/**
|
||||
* @var RecordTransformer
|
||||
*/
|
||||
private $recordTransformer;
|
||||
|
||||
public function __construct(RecordTransformer $recordTransformer)
|
||||
{
|
||||
$this->recordTransformer = $recordTransformer;
|
||||
}
|
||||
|
||||
public function includeResults(SearchResultView $resultView)
|
||||
{
|
||||
return $this->collection($resultView->getRecords(), $this->recordTransformer);
|
||||
}
|
||||
}
|
31
lib/Alchemy/Phrasea/Search/V1SearchResultTransformer.php
Normal file
31
lib/Alchemy/Phrasea/Search/V1SearchResultTransformer.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
/*
|
||||
* This file is part of Phraseanet
|
||||
*
|
||||
* (c) 2005-2016 Alchemy
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Alchemy\Phrasea\Search;
|
||||
|
||||
use League\Fractal\TransformerAbstract;
|
||||
|
||||
class V1SearchResultTransformer extends V1SearchTransformer
|
||||
{
|
||||
/**
|
||||
* @var TransformerAbstract
|
||||
*/
|
||||
private $transformer;
|
||||
|
||||
public function __construct(TransformerAbstract $transformer)
|
||||
{
|
||||
$this->transformer = $transformer;
|
||||
}
|
||||
|
||||
public function includeResults(SearchResultView $resultView)
|
||||
{
|
||||
return $this->item($resultView, $this->transformer);
|
||||
}
|
||||
}
|
44
lib/Alchemy/Phrasea/Search/V1SearchTransformer.php
Normal file
44
lib/Alchemy/Phrasea/Search/V1SearchTransformer.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
/*
|
||||
* This file is part of Phraseanet
|
||||
*
|
||||
* (c) 2005-2016 Alchemy
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Alchemy\Phrasea\Search;
|
||||
|
||||
use Alchemy\Phrasea\SearchEngine\SearchEngineSuggestion;
|
||||
use League\Fractal\TransformerAbstract;
|
||||
|
||||
abstract class V1SearchTransformer extends TransformerAbstract
|
||||
{
|
||||
protected $availableIncludes = ['results'];
|
||||
protected $defaultIncludes = ['results'];
|
||||
|
||||
public function transform(SearchResultView $resultView)
|
||||
{
|
||||
$result = $resultView->getResult();
|
||||
|
||||
return [
|
||||
'offset_start' => $result->getOptions()->getFirstResult(),
|
||||
'per_page' => $result->getOptions()->getMaxResults(),
|
||||
'available_results' => $result->getAvailable(),
|
||||
'total_results' => $result->getTotal(),
|
||||
'error' => (string)$result->getError(),
|
||||
'warning' => (string)$result->getWarning(),
|
||||
'query_time' => $result->getDuration(),
|
||||
'search_indexes' => $result->getIndexes(),
|
||||
'suggestions' => array_map(
|
||||
function (SearchEngineSuggestion $suggestion) {
|
||||
return $suggestion->toArray();
|
||||
}, $result->getSuggestions()->toArray()),
|
||||
'facets' => $result->getFacets(),
|
||||
'query' => $result->getQuery(),
|
||||
];
|
||||
}
|
||||
|
||||
abstract public function includeResults(SearchResultView $resultView);
|
||||
}
|
48
lib/Alchemy/Phrasea/Search/V2SearchTransformer.php
Normal file
48
lib/Alchemy/Phrasea/Search/V2SearchTransformer.php
Normal file
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
/*
|
||||
* This file is part of Phraseanet
|
||||
*
|
||||
* (c) 2005-2016 Alchemy
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Alchemy\Phrasea\Search;
|
||||
|
||||
use Alchemy\Phrasea\Model\RecordInterface;
|
||||
use League\Fractal\TransformerAbstract;
|
||||
|
||||
class V2SearchTransformer extends TransformerAbstract
|
||||
{
|
||||
protected $availableIncludes = ['results'];
|
||||
protected $defaultIncludes = ['results'];
|
||||
|
||||
public function transform(SearchResultView $searchView)
|
||||
{
|
||||
return [
|
||||
'offset_start' => $searchView->getResult()->getOptions()->getFirstResult(),
|
||||
'per_page' => $searchView->getResult()->getOptions()->getMaxResults(),
|
||||
'available_results' => $searchView->getResult()->getAvailable(),
|
||||
'total_results' => $searchView->getResult()->getTotal(),
|
||||
'error' => (string)$searchView->getResult()->getError(),
|
||||
'warning' => (string)$searchView->getResult()->getWarning(),
|
||||
'query_time' => $searchView->getResult()->getDuration(),
|
||||
'search_indexes' => $searchView->getResult()->getIndexes(),
|
||||
'facets' => $searchView->getResult()->getFacets(),
|
||||
'search_type' => $searchView->getResult()->getOptions()->getSearchType(),
|
||||
];
|
||||
}
|
||||
|
||||
public function includeResults(SearchResultView $searchView)
|
||||
{
|
||||
return $this->collection($searchView->getResult()->getResults(), function (RecordInterface $record) {
|
||||
return [
|
||||
'databox_id' => $record->getDataboxId(),
|
||||
'record_id' => $record->getRecordId(),
|
||||
'collection_id' => $record->getCollectionId(),
|
||||
'version' => $record->getUpdated()->getTimestamp(),
|
||||
];
|
||||
});
|
||||
}
|
||||
}
|
@@ -9,6 +9,7 @@
|
||||
*/
|
||||
|
||||
use Alchemy\Phrasea\Application;
|
||||
use Alchemy\Phrasea\Databox\Caption\CachedCaptionDataRepository;
|
||||
use Alchemy\Phrasea\Model\RecordReferenceInterface;
|
||||
use Alchemy\Phrasea\Model\Serializer\CaptionSerializer;
|
||||
|
||||
@@ -29,10 +30,16 @@ class caption_record implements cache_cacheableInterface
|
||||
*/
|
||||
protected $app;
|
||||
|
||||
public function __construct(Application $app, RecordReferenceInterface $record)
|
||||
/**
|
||||
* @param Application $app
|
||||
* @param RecordReferenceInterface $record
|
||||
* @param array[]|null $fieldsData
|
||||
*/
|
||||
public function __construct(Application $app, RecordReferenceInterface $record, array $fieldsData = null)
|
||||
{
|
||||
$this->app = $app;
|
||||
$this->record = $record;
|
||||
$this->fields = null === $fieldsData ? null : $this->mapFieldsFromData($fieldsData);
|
||||
}
|
||||
|
||||
public function toArray($includeBusinessFields)
|
||||
@@ -61,81 +68,9 @@ class caption_record implements cache_cacheableInterface
|
||||
return $this->fields;
|
||||
}
|
||||
|
||||
$databox = $this->getDatabox();
|
||||
$data = $this->getDataRepository()->findByRecordIds([$this->getRecordReference()->getRecordId()]);
|
||||
|
||||
try {
|
||||
$fields = $this->get_data_from_cache();
|
||||
} catch (\Exception $e) {
|
||||
$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 = $databox->get_meta_structure();
|
||||
$record = $databox->get_record($this->record->getRecordId());
|
||||
|
||||
// first group values by field
|
||||
$caption_fields = [];
|
||||
foreach ($fields as $row) {
|
||||
$structure_id = $row['structure_id'];
|
||||
if(!array_key_exists($structure_id, $caption_fields)) {
|
||||
$caption_fields[$structure_id] = [
|
||||
'db_field' => $databox_descriptionStructure->get_element($structure_id),
|
||||
'values' => []
|
||||
];
|
||||
}
|
||||
|
||||
if (count($caption_fields[$structure_id]['values']) > 0 && !$caption_fields[$structure_id]['db_field']->is_multi()) {
|
||||
// Inconsistent, should not happen
|
||||
continue;
|
||||
}
|
||||
|
||||
// build an EMPTY caption_Field_Value
|
||||
$cfv = new caption_Field_Value(
|
||||
$this->app,
|
||||
$caption_fields[$structure_id]['db_field'],
|
||||
$record,
|
||||
$row['meta_id'],
|
||||
caption_Field_Value::DONT_RETRIEVE_VALUES // ask caption_Field_Value "no n+1 sql"
|
||||
);
|
||||
|
||||
// inject the value we already know
|
||||
$cfv->injectValues($row['value'], $row['VocabularyType'], $row['VocabularyId']);
|
||||
|
||||
// add the value to the field
|
||||
$caption_fields[$structure_id]['values'][] = $cfv;
|
||||
}
|
||||
|
||||
// now build a "caption_field" with already known "caption_Field_Value"s
|
||||
foreach($caption_fields as $structure_id => $caption_field) {
|
||||
|
||||
// build an EMPTY caption_field
|
||||
$cf = new caption_field(
|
||||
$this->app,
|
||||
$caption_field['db_field'],
|
||||
$record,
|
||||
caption_field::DONT_RETRIEVE_VALUES // ask caption_field "no n+1 sql"
|
||||
);
|
||||
|
||||
// inject the value we already know
|
||||
$cf->injectValues($caption_field['values']);
|
||||
|
||||
// add the field to the fields
|
||||
$rec_fields[$structure_id] = $cf;
|
||||
}
|
||||
}
|
||||
$this->fields = $rec_fields;
|
||||
$this->fields = $this->mapFieldsFromData(array_shift($data));
|
||||
|
||||
return $this->fields;
|
||||
}
|
||||
@@ -305,6 +240,8 @@ SQL;
|
||||
{
|
||||
$this->fields = null;
|
||||
|
||||
$this->getDataRepository()->invalidate($this->getRecordReference()->getRecordId());
|
||||
|
||||
return $this->getDatabox()->delete_data_from_cache($this->get_cache_key($option));
|
||||
}
|
||||
|
||||
@@ -315,4 +252,82 @@ SQL;
|
||||
{
|
||||
return $this->app->findDataboxById($this->record->getDataboxId());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
* @return caption_field[]
|
||||
*/
|
||||
protected function mapFieldsFromData($data)
|
||||
{
|
||||
if (!$data) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$rec_fields = array();
|
||||
|
||||
$databox = $this->getDatabox();
|
||||
$databox_descriptionStructure = $databox->get_meta_structure();
|
||||
$record = $databox->get_record($this->record->getRecordId());
|
||||
|
||||
// first group values by field
|
||||
$caption_fields = [];
|
||||
foreach ($data as $row) {
|
||||
$structure_id = $row['structure_id'];
|
||||
if (!array_key_exists($structure_id, $caption_fields)) {
|
||||
$caption_fields[$structure_id] = [
|
||||
'db_field' => $databox_descriptionStructure->get_element($structure_id),
|
||||
'values' => []
|
||||
];
|
||||
}
|
||||
|
||||
if (count($caption_fields[$structure_id]['values']) > 0 && !$caption_fields[$structure_id]['db_field']->is_multi()) {
|
||||
// Inconsistent, should not happen
|
||||
continue;
|
||||
}
|
||||
|
||||
// build an EMPTY caption_Field_Value
|
||||
$cfv = new caption_Field_Value(
|
||||
$this->app,
|
||||
$caption_fields[$structure_id]['db_field'],
|
||||
$record,
|
||||
$row['meta_id'],
|
||||
caption_Field_Value::DONT_RETRIEVE_VALUES // ask caption_Field_Value "no n+1 sql"
|
||||
);
|
||||
|
||||
// inject the value we already know
|
||||
$cfv->injectValues($row['value'], $row['VocabularyType'], $row['VocabularyId']);
|
||||
|
||||
// add the value to the field
|
||||
$caption_fields[$structure_id]['values'][] = $cfv;
|
||||
}
|
||||
|
||||
// now build a "caption_field" with already known "caption_Field_Value"s
|
||||
foreach ($caption_fields as $structure_id => $caption_field) {
|
||||
|
||||
// build an EMPTY caption_field
|
||||
$cf = new caption_field(
|
||||
$this->app,
|
||||
$caption_field['db_field'],
|
||||
$record,
|
||||
caption_field::DONT_RETRIEVE_VALUES // ask caption_field "no n+1 sql"
|
||||
);
|
||||
|
||||
// inject the value we already know
|
||||
$cf->injectValues($caption_field['values']);
|
||||
|
||||
// add the field to the fields
|
||||
$rec_fields[$structure_id] = $cf;
|
||||
}
|
||||
|
||||
return $rec_fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return CachedCaptionDataRepository
|
||||
*/
|
||||
private function getDataRepository()
|
||||
{
|
||||
return $this->app['provider.data_repo.caption']
|
||||
->getRepositoryForDatabox($this->getRecordReference()->getDataboxId());
|
||||
}
|
||||
}
|
||||
|
@@ -800,25 +800,29 @@ class ApiJsonTest extends ApiTestCase
|
||||
|
||||
public function testSearchRoute()
|
||||
{
|
||||
self::$DI['app']['manipulator.user'] = $this->getMockBuilder('Alchemy\Phrasea\Model\Manipulator\UserManipulator')
|
||||
$app = $this->getApplication();
|
||||
|
||||
$app['manipulator.user'] = $this->getMockBuilder('Alchemy\Phrasea\Model\Manipulator\UserManipulator')
|
||||
->setConstructorArgs([
|
||||
self::$DI['app']['model.user-manager'],
|
||||
self::$DI['app']['auth.password-encoder'],
|
||||
self::$DI['app']['geonames.connector'],
|
||||
self::$DI['app']['repo.users'],
|
||||
self::$DI['app']['random.low'],
|
||||
self::$DI['app']['dispatcher'],
|
||||
$app['model.user-manager'],
|
||||
$app['auth.password-encoder'],
|
||||
$app['geonames.connector'],
|
||||
$app['repo.users'],
|
||||
$app['random.low'],
|
||||
$app['dispatcher'],
|
||||
])
|
||||
->setMethods(['logQuery'])
|
||||
->getMock();
|
||||
|
||||
self::$DI['app']['manipulator.user']->expects($this->once())->method('logQuery');
|
||||
$app['manipulator.user']->expects($this->once())->method('logQuery');
|
||||
|
||||
$this->setToken($this->userAccessToken);
|
||||
self::$DI['client']->request('POST', '/api/v1/search/', $this->getParameters(), [], ['HTTP_Accept' => $this->getAcceptMimeType()]);
|
||||
$content = $this->unserialize(self::$DI['client']->getResponse()->getContent());
|
||||
$response = $this->request('POST', '/api/v1/search/', $this->getParameters(), [
|
||||
'HTTP_Accept' => $this->getAcceptMimeType(),
|
||||
]);
|
||||
$content = $this->unserialize($response->getContent());
|
||||
|
||||
$this->evaluateResponse200(self::$DI['client']->getResponse());
|
||||
$this->evaluateResponse200($response);
|
||||
$this->evaluateMeta200($content);
|
||||
|
||||
$response = $content['response'];
|
||||
@@ -837,17 +841,15 @@ class ApiJsonTest extends ApiTestCase
|
||||
|
||||
self::$DI['record_story_1'];
|
||||
|
||||
$client = $this->getClient();
|
||||
$client->request(
|
||||
$response = $this->request(
|
||||
'POST',
|
||||
'/api/v1/search/',
|
||||
$this->getParameters(['search_type' => SearchEngineOptions::RECORD_GROUPING]),
|
||||
[],
|
||||
['HTTP_Accept' => $this->getAcceptMimeType()]
|
||||
);
|
||||
$content = $this->unserialize($client->getResponse()->getContent());
|
||||
$content = $this->unserialize($response->getContent());
|
||||
|
||||
$this->evaluateResponse200($client->getResponse());
|
||||
$this->evaluateResponse200($response);
|
||||
$this->evaluateMeta200($content);
|
||||
|
||||
$response = $content['response'];
|
||||
@@ -873,10 +875,10 @@ class ApiJsonTest extends ApiTestCase
|
||||
public function testRecordsSearchRoute()
|
||||
{
|
||||
$this->setToken($this->userAccessToken);
|
||||
self::$DI['client']->request('POST', '/api/v1/records/search/', $this->getParameters(), [], ['HTTP_Accept' => $this->getAcceptMimeType()]);
|
||||
$content = $this->unserialize(self::$DI['client']->getResponse()->getContent());
|
||||
$response = $this->request('POST', '/api/v1/records/search/', $this->getParameters(), ['HTTP_Accept' => $this->getAcceptMimeType()]);
|
||||
$content = $this->unserialize($response->getContent());
|
||||
|
||||
$this->evaluateResponse200(self::$DI['client']->getResponse());
|
||||
$this->evaluateResponse200($response);
|
||||
$this->evaluateMeta200($content);
|
||||
|
||||
$response = $content['response'];
|
||||
|
@@ -17,7 +17,7 @@ class caption_recordTest extends \PhraseanetTestCase
|
||||
public function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
$this->object = new caption_record(self::$DI['app'], self::$DI['record_1'], self::$DI['record_1']->get_databox());
|
||||
$this->object = new caption_record(self::$DI['app'], self::$DI['record_1']);
|
||||
}
|
||||
|
||||
/**
|
||||
|
Reference in New Issue
Block a user