This is a combination of 33 commits.

- Squashed Pull request #1730
- Squashed Pull request #1741
- Squashed Pull request #1742
- Squash merge branch 4.0
- Squashed Pull request #1744
- Squashed Pull request #1746
- Squashed merge branch 4.0
- Squashed merge branch 4.0
- Squashed merge branch 4.0
- Squashed merge branch 4.0
- Squashed Pull request #1758
- Avoid using imagine/imagine alias as it is causing install issues
- Squashed merge branch 4.0
- Squashed Pull request #1763
- Squashed merge branch 4.0
- Squash of 6 commits
- Squashed merge branch 4.0
- This is a combination of 2 commits.
- Squashed Pull request #1775
- Squashed Pull request #1777
- Squashed Pull request #1779
- Squashed Pull request #1780
- Squashed Pull request #1782
- Adds a Pull request template
- Squased Pull request #1783
- Squash Pull request #1786
- Squashed Pull request #1796
- Squashed merge branch 4.0
- Squash Pull request #1791
- Squashed merge branch 4.0
- Squashed Pull request #1808
- Squashed Pull request #1811
- Squashed Pull request #1809
This commit is contained in:
Benoît Burnichon
2016-04-19 19:21:04 +02:00
parent 01b06c5144
commit 1e18b3e69f
179 changed files with 5652 additions and 3030 deletions

16
PULL_REQUEST_TEMPLATE.md Normal file
View File

@@ -0,0 +1,16 @@
## Changelog
### Changed
- Breaking change
### Fixes
- PHRAS-XXX: Short description of bug and fix
- Short description of bug and fix without issue/ticket
### Adds
- PHRAS-XXX: Short feature description
- Short feature description without issue/ticket
### Removes
- PHRAS-XXX: Short feature removal description
- Short feature removal description without issue/ticket

View File

@@ -47,6 +47,7 @@
"dailymotion/sdk": "~1.5", "dailymotion/sdk": "~1.5",
"data-uri/data-uri": "~0.1.0", "data-uri/data-uri": "~0.1.0",
"dflydev/doctrine-orm-service-provider": "~1.0", "dflydev/doctrine-orm-service-provider": "~1.0",
"doctrine/cache": "^1.6",
"doctrine/dbal": "^2.4.0", "doctrine/dbal": "^2.4.0",
"doctrine/migrations": "^1.0.0", "doctrine/migrations": "^1.0.0",
"doctrine/orm": "^2.4.0", "doctrine/orm": "^2.4.0",
@@ -61,7 +62,7 @@
"hoa/dispatcher": "~0.0", "hoa/dispatcher": "~0.0",
"hoa/router": "~2.0", "hoa/router": "~2.0",
"igorw/get-in": "~1.0", "igorw/get-in": "~1.0",
"imagine/imagine": "dev-alchemy-0.6.2 as 0.6.2", "imagine/imagine": "0.6.x-dev",
"ircmaxell/random-lib": "~1.0", "ircmaxell/random-lib": "~1.0",
"jms/serializer": "~0.10", "jms/serializer": "~0.10",
"jms/translation-bundle": "dev-rebase-2015-10-20", "jms/translation-bundle": "dev-rebase-2015-10-20",
@@ -88,7 +89,7 @@
"simple-bus/jms-serializer-bridge": "^1.0", "simple-bus/jms-serializer-bridge": "^1.0",
"simple-bus/message-bus": "^2.1", "simple-bus/message-bus": "^2.1",
"simple-bus/serialization": "^2.0", "simple-bus/serialization": "^2.0",
"sorien/silex-dbal-profiler": "~1.0.0", "sorien/silex-dbal-profiler": "^1.1",
"sorien/silex-pimple-dumper": "^1.0", "sorien/silex-pimple-dumper": "^1.0",
"swiftmailer/swiftmailer": "~5.3.0", "swiftmailer/swiftmailer": "~5.3.0",
"symfony/symfony": "~2.7.10|~2.8.3", "symfony/symfony": "~2.7.10|~2.8.3",

53
composer.lock generated
View File

@@ -4,8 +4,8 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"hash": "baf4148980adfb15bed6b72c438da7eb", "hash": "8db2333764b17b4b04f474a6b9fc2c49",
"content-hash": "5d8ffafd15285984d97c82a1e13a697f", "content-hash": "2961cef34c7a94f9afb11b859781cb65",
"packages": [ "packages": [
{ {
"name": "alchemy-fr/tcpdf-clone", "name": "alchemy-fr/tcpdf-clone",
@@ -1137,33 +1137,33 @@
}, },
{ {
"name": "doctrine/cache", "name": "doctrine/cache",
"version": "v1.5.1", "version": "v1.6.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/doctrine/cache.git", "url": "https://github.com/doctrine/cache.git",
"reference": "2b9cec5a5e722010cbebc91713d4c11eaa064d5e" "reference": "f8af318d14bdb0eff0336795b428b547bd39ccb6"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/doctrine/cache/zipball/2b9cec5a5e722010cbebc91713d4c11eaa064d5e", "url": "https://api.github.com/repos/doctrine/cache/zipball/f8af318d14bdb0eff0336795b428b547bd39ccb6",
"reference": "2b9cec5a5e722010cbebc91713d4c11eaa064d5e", "reference": "f8af318d14bdb0eff0336795b428b547bd39ccb6",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": ">=5.3.2" "php": "~5.5|~7.0"
}, },
"conflict": { "conflict": {
"doctrine/common": ">2.2,<2.4" "doctrine/common": ">2.2,<2.4"
}, },
"require-dev": { "require-dev": {
"phpunit/phpunit": ">=3.7", "phpunit/phpunit": "~4.8|~5.0",
"predis/predis": "~1.0", "predis/predis": "~1.0",
"satooshi/php-coveralls": "~0.6" "satooshi/php-coveralls": "~0.6"
}, },
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-master": "1.5.x-dev" "dev-master": "1.6.x-dev"
} }
}, },
"autoload": { "autoload": {
@@ -1203,7 +1203,7 @@
"cache", "cache",
"caching" "caching"
], ],
"time": "2015-11-02 18:35:48" "time": "2015-12-31 16:37:02"
}, },
{ {
"name": "doctrine/collections", "name": "doctrine/collections",
@@ -2930,7 +2930,7 @@
}, },
{ {
"name": "imagine/imagine", "name": "imagine/imagine",
"version": "dev-alchemy-0.6.2", "version": "0.6.x-dev",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/alchemy-fr/Imagine.git", "url": "https://github.com/alchemy-fr/Imagine.git",
@@ -4744,7 +4744,7 @@
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/dcdeb1d923b981a55f0a4ce6c2ceac14cb8476f8", "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/438196c8f66647073bcaf99908b543bda7ffd6d4",
"reference": "c18abc6df90257ddbbe2654a98cc5cc7be804411", "reference": "c18abc6df90257ddbbe2654a98cc5cc7be804411",
"shasum": "" "shasum": ""
}, },
@@ -5221,22 +5221,28 @@
}, },
{ {
"name": "sorien/silex-dbal-profiler", "name": "sorien/silex-dbal-profiler",
"version": "1.0.0", "version": "1.1.2",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/Sorien/silex-dbal-profiler.git", "url": "https://github.com/Sorien/silex-dbal-profiler.git",
"reference": "8cbbef7a8fae53381d9005163070485471089908" "reference": "4d3f642144e96685270f7ffb1648a081d0c5eb7e"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/Sorien/silex-dbal-profiler/zipball/8cbbef7a8fae53381d9005163070485471089908", "url": "https://api.github.com/repos/Sorien/silex-dbal-profiler/zipball/4d3f642144e96685270f7ffb1648a081d0c5eb7e",
"reference": "8cbbef7a8fae53381d9005163070485471089908", "reference": "4d3f642144e96685270f7ffb1648a081d0c5eb7e",
"shasum": "" "shasum": ""
}, },
"require-dev": { "require": {
"silex/silex": "~1.0" "silex/silex": "~1.0",
"silex/web-profiler": "~1.0"
}, },
"type": "library", "type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.1.x-dev"
}
},
"autoload": { "autoload": {
"psr-0": { "psr-0": {
"Sorien": "src" "Sorien": "src"
@@ -5259,7 +5265,7 @@
"profiler", "profiler",
"silex" "silex"
], ],
"time": "2014-02-26 10:01:20" "time": "2015-03-12 09:26:42"
}, },
{ {
"name": "sorien/silex-pimple-dumper", "name": "sorien/silex-pimple-dumper",
@@ -7389,14 +7395,7 @@
"time": "2015-06-21 13:59:46" "time": "2015-06-21 13:59:46"
} }
], ],
"aliases": [ "aliases": [],
{
"alias": "0.6.2",
"alias_normalized": "0.6.2.0",
"version": "dev-alchemy-0.6.2",
"package": "imagine/imagine"
}
],
"minimum-stability": "stable", "minimum-stability": "stable",
"stability-flags": { "stability-flags": {
"alchemy/task-manager": 20, "alchemy/task-manager": 20,

View File

@@ -73,6 +73,7 @@ use Alchemy\Phrasea\Core\Provider\UnicodeServiceProvider;
use Alchemy\Phrasea\Core\Provider\WebhookServiceProvider; use Alchemy\Phrasea\Core\Provider\WebhookServiceProvider;
use Alchemy\Phrasea\Core\Provider\ZippyServiceProvider; use Alchemy\Phrasea\Core\Provider\ZippyServiceProvider;
use Alchemy\Phrasea\Core\Provider\WebProfilerServiceProvider as PhraseaWebProfilerServiceProvider; use Alchemy\Phrasea\Core\Provider\WebProfilerServiceProvider as PhraseaWebProfilerServiceProvider;
use Alchemy\Phrasea\Databox\Subdef\MediaSubdefServiceProvider;
use Alchemy\Phrasea\Exception\InvalidArgumentException; use Alchemy\Phrasea\Exception\InvalidArgumentException;
use Alchemy\Phrasea\Filesystem\FilesystemServiceProvider; use Alchemy\Phrasea\Filesystem\FilesystemServiceProvider;
use Alchemy\Phrasea\Filesystem\ApplicationPathServiceGenerator; use Alchemy\Phrasea\Filesystem\ApplicationPathServiceGenerator;
@@ -80,6 +81,7 @@ use Alchemy\Phrasea\Form\Extension\HelpTypeExtension;
use Alchemy\Phrasea\Media\DatafilesResolver; use Alchemy\Phrasea\Media\DatafilesResolver;
use Alchemy\Phrasea\Media\MediaAccessorResolver; use Alchemy\Phrasea\Media\MediaAccessorResolver;
use Alchemy\Phrasea\Media\PermalinkMediaResolver; use Alchemy\Phrasea\Media\PermalinkMediaResolver;
use Alchemy\Phrasea\Media\TechnicalDataServiceProvider;
use Alchemy\Phrasea\Model\Entities\User; use Alchemy\Phrasea\Model\Entities\User;
use Doctrine\DBAL\Event\ConnectionEventArgs; use Doctrine\DBAL\Event\ConnectionEventArgs;
use MediaVorus\Media\MediaInterface; use MediaVorus\Media\MediaInterface;
@@ -190,6 +192,8 @@ class Application extends SilexApplication
$this->register(new NotificationDelivererServiceProvider()); $this->register(new NotificationDelivererServiceProvider());
$this->register(new RepositoriesServiceProvider()); $this->register(new RepositoriesServiceProvider());
$this->register(new ManipulatorServiceProvider()); $this->register(new ManipulatorServiceProvider());
$this->register(new TechnicalDataServiceProvider());
$this->register(new MediaSubdefServiceProvider());
$this->register(new InstallerServiceProvider()); $this->register(new InstallerServiceProvider());
$this->register(new PhraseaVersionServiceProvider()); $this->register(new PhraseaVersionServiceProvider());

View File

@@ -69,7 +69,7 @@ class Story implements AttributeInterface
*/ */
public function asString() public function asString()
{ {
return $this->story->get_serialize_key(); return $this->story->getId();
} }
/** /**

View File

@@ -278,7 +278,7 @@ class Manager
$file->addAttribute( $file->addAttribute(
new MetadataAttr( new MetadataAttr(
new Metadata( new Metadata(
new TfRecordid(), new MonoValue($element->get_record_id()) new TfRecordid(), new MonoValue($element->getRecordId())
) )
) )
); );
@@ -314,7 +314,7 @@ class Manager
break; break;
case AttributeInterface::NAME_STATUS: case AttributeInterface::NAME_STATUS:
/** @var StatusAttr $attribute */ /** @var StatusAttr $attribute */
$element->set_binary_status(decbin(bindec($element->get_status()) | bindec($attribute->getValue()))); $element->setStatus(decbin(bindec($element->getStatus()) | bindec($attribute->getValue())));
break; break;
case AttributeInterface::NAME_STORY: case AttributeInterface::NAME_STORY:

View File

@@ -0,0 +1,88 @@
<?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\Cache;
use Doctrine\Common\Cache\Cache as DoctrineCache;
use Doctrine\Common\Cache\MultiGetCache;
use Doctrine\Common\Cache\MultiPutCache;
class MultiAdapter implements DoctrineCache, MultiGetCache, MultiPutCache
{
/**
* @var DoctrineCache
*/
private $cache;
public function __construct(DoctrineCache $cache)
{
$this->cache = $cache;
}
public function fetch($id)
{
return $this->cache->fetch($id);
}
public function contains($id)
{
return $this->cache->contains($id);
}
public function save($id, $data, $lifeTime = 0)
{
return $this->cache->save($id, $data, $lifeTime);
}
public function delete($id)
{
return $this->cache->delete($id);
}
public function getStats()
{
return $this->cache->getStats();
}
public function fetchMultiple(array $keys)
{
if ($this->cache instanceof MultiGetCache) {
return $this->cache->fetchMultiple($keys);
}
// Pass data by reference to avoid copies of whole array on key add.
$data = array_reduce($keys, function (array &$data, $key) {
$value = $this->fetch($key);
if (false !== $value || true === $this->contains($key)) {
$data[$key] = $value;
}
return $data;
}, []);
return $data;
}
public function saveMultiple(array $keysAndValues, $lifetime = 0)
{
if ($this->cache instanceof MultiPutCache) {
return $this->cache->saveMultiple($keysAndValues, $lifetime);
}
foreach ($keysAndValues as $key => $value) {
if (!$this->cache->save($key, $value, $lifetime)) {
return false;
}
}
return true;
}
}

View File

@@ -12,12 +12,8 @@ namespace Alchemy\Phrasea\Collection;
use Assert\Assertion; use Assert\Assertion;
class CollectionHelper final class CollectionHelper
{ {
private function __construct()
{
}
/** /**
* @param \collection[] $collections * @param \collection[] $collections
* @return \collection[] * @return \collection[]
@@ -31,7 +27,11 @@ class CollectionHelper
} }
usort($collections, function (\collection $left, \collection $right) { usort($collections, function (\collection $left, \collection $right) {
return ($left->get_ord() < $right->get_ord()) ? -1 : (($left->get_ord() < $right->get_ord()) ? 1 : 0); if ($left->get_ord() === $right->get_ord()) {
return 0;
}
return $left->get_ord() < $right->get_ord() ? -1 : 1;
}); });
return $collections; return $collections;

View File

@@ -179,13 +179,14 @@ class BuildSubdefs extends Command
if($this->databox !== null) { if($this->databox !== null) {
foreach ($this->databox->get_subdef_structure() as $sgname=>$sg) { /** @var SubdefGroup $sg */
if (empty($types) || in_array($sgname, $types)) { foreach ($this->databox->get_subdef_structure() as $sg) {
$this->subdefsNameByType[$sgname] = []; if (empty($types) || in_array($sg->getName(), $types)) {
$this->subdefsNameByType[$sg->getName()] = [];
/** @var databox_subdef $sd */ /** @var databox_subdef $sd */
foreach ($sg as $sd) { foreach ($sg as $sd) {
if (empty($names) || in_array($sd->get_name(), $names)) { if (empty($names) || in_array($sd->get_name(), $names)) {
$this->subdefsNameByType[$sgname][] = $sd->get_name(); $this->subdefsNameByType[$sg->getName()][] = $sd->get_name();
} }
} }
} }

View File

@@ -61,7 +61,7 @@ class RecordAdd extends Command
if (!$input->getOption('yes')) { if (!$input->getOption('yes')) {
do { do {
$continue = strtolower($dialog->ask($output, sprintf("Will add record <info>%s</info> (%s) on collection <info>%s</info>\n<question>Continue ? (y/N)</question>", $file, $media->getType(), $collection->get_label($this->container['locale'])), 'N')); $continue = strtolower($dialog->ask($output, sprintf("Will add record <info>%s</info> (%s) on collection <info>%s</info>\n<question>Continue ? (y/N)</question>", $file, $media->getType(), $collection->get_label($this->container['locale'])), 'N'));
} while ( ! in_array($continue, ['y', 'n'])); } while (!in_array($continue, ['y', 'n']));
if (strtolower($continue) !== 'y') { if (strtolower($continue) !== 'y') {
$output->writeln('Aborted !'); $output->writeln('Aborted !');
@@ -112,7 +112,10 @@ class RecordAdd extends Command
if ($elementCreated instanceof \record_adapter) { if ($elementCreated instanceof \record_adapter) {
$output->writeln( $output->writeln(
sprintf( sprintf(
"Record id <info>%d</info> on collection `%s` (databox `%s`) has been created", $elementCreated->get_record_id(), $elementCreated->get_collection()->get_label($this->container['locale']), $elementCreated->get_databox()->get_label($this->container['locale']) "Record id <info>%d</info> on collection `%s` (databox `%s`) has been created",
$elementCreated->getRecordId(),
$elementCreated->getCollection()->get_label($this->container['locale']),
$elementCreated->getDatabox()->get_label($this->container['locale'])
) )
); );
} elseif ($elementCreated instanceof LazaretFile) { } elseif ($elementCreated instanceof LazaretFile) {

View File

@@ -80,13 +80,13 @@ class Step35 implements DatasUpgraderInterface
try { try {
$this->updateMetadatas($record, $row['xml']); $this->updateMetadatas($record, $row['xml']);
} catch (\Exception $e) { } catch (\Exception $e) {
$this->app['monolog']->addError(sprintf("Error while upgrading metadatas for record %d on databox %d : %s", $record->get_record_id(), $record->get_sbas_id(), $e->getMessage())); $this->app['monolog']->addError(sprintf("Error while upgrading metadatas for record %d on databox %d : %s", $record->getRecordId(), $record->getDataboxId(), $e->getMessage()));
} }
try { try {
$record->set_binary_status($row['status']); $record->setStatus($row['status']);
} catch (\Exception $e) { } catch (\Exception $e) {
$this->app['monolog']->addError(sprintf("Error while upgrading status for record %d on databox %d : %s", $record->get_record_id(), $record->get_sbas_id(), $e->getMessage())); $this->app['monolog']->addError(sprintf("Error while upgrading status for record %d on databox %d : %s", $record->getRecordId(), $record->getDataboxId(), $e->getMessage()));
} }
unset($record); unset($record);
} }
@@ -131,7 +131,7 @@ class Step35 implements DatasUpgraderInterface
*/ */
protected function updateMetadatas(\record_adapter $record, $xml) protected function updateMetadatas(\record_adapter $record, $xml)
{ {
$metas = $record->get_databox()->get_meta_structure(); $metas = $record->getDatabox()->get_meta_structure();
$datas = $metadatas = []; $datas = $metadatas = [];

View File

@@ -62,7 +62,7 @@ abstract class AbstractDelivery
private function logView(\record_adapter $record, Request $request) private function logView(\record_adapter $record, Request $request)
{ {
try { try {
$logger = $this->getDataboxLogger($record->get_databox()); $logger = $this->getDataboxLogger($record->getDatabox());
$log_id = $logger->get_id(); $log_id = $logger->get_id();
$record->log_view( $record->log_view(
$log_id, $log_id,

View File

@@ -12,8 +12,8 @@ namespace Alchemy\Phrasea\Controller\Admin;
use Alchemy\Phrasea\Controller\Controller; use Alchemy\Phrasea\Controller\Controller;
use Alchemy\Phrasea\Metadata\TagProvider; use Alchemy\Phrasea\Metadata\TagProvider;
use Alchemy\Phrasea\Vocabulary\Controller as VocabularyController;
use Alchemy\Phrasea\Vocabulary\ControlProvider\ControlProviderInterface; use Alchemy\Phrasea\Vocabulary\ControlProvider\ControlProviderInterface;
use Assert\Assertion;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
@@ -96,24 +96,57 @@ class FieldsController extends Controller
public function listVocabularies() public function listVocabularies()
{ {
$vocabularies = VocabularyController::getAvailable($this->app); return $this->app->json(array_map(
[$this, 'getVocabularyAsArray'],
$this->fetchVocabularies()
));
}
return $this->app->json(array_map(function (ControlProviderInterface $vocabulary) { /**
return [ * @return ControlProviderInterface[]
'type' => $vocabulary->getType(), */
'name' => $vocabulary->getName(), private function fetchVocabularies()
]; {
}, $vocabularies)); $vocabularies = $this->getVocabularies();
$instances = array_map(
function ($type) use ($vocabularies) {
return $vocabularies[$type];
},
$vocabularies->keys()
);
Assertion::allIsInstanceOf($instances, ControlProviderInterface::class);
return $instances;
}
/**
* @param string $type
* @return ControlProviderInterface
*/
private function fetchVocabulary($type)
{
$vocabularies = $this->getVocabularies();
$vocabulary = $vocabularies[strtolower($type)];
Assertion::isInstanceOf($vocabulary, ControlProviderInterface::class);
return $vocabulary;
}
private function getVocabularyAsArray(ControlProviderInterface $vocabulary)
{
return [
'type' => $vocabulary->getType(),
'name' => $vocabulary->getName(),
];
} }
public function getVocabulary($type) public function getVocabulary($type)
{ {
$vocabulary = VocabularyController::get($this->app, $type); return $this->app->json($this->getVocabularyAsArray($this->fetchVocabulary($type)));
return $this->app->json([
'type' => $vocabulary->getType(),
'name' => $vocabulary->getName(),
]);
} }
public function searchTag(Request $request) public function searchTag(Request $request)
@@ -293,11 +326,11 @@ class FieldsController extends Controller
} }
try { try {
$vocabulary = VocabularyController::get($this->app, $data['vocabulary-type']); $vocabulary = $this->fetchVocabulary($data['vocabulary-type']);
$field->setVocabularyControl($vocabulary); $field->setVocabularyControl($vocabulary);
$field->setVocabularyRestricted($data['vocabulary-restricted']); $field->setVocabularyRestricted($data['vocabulary-restricted']);
} catch (\InvalidArgumentException $e) { } catch (\InvalidArgumentException $e) {
// Invalid vocabulary requested
} }
if ('' !== $dcesElement = (string) $data['dces-element']) { if ('' !== $dcesElement = (string) $data['dces-element']) {
@@ -347,4 +380,12 @@ class FieldsController extends Controller
return $data; return $data;
} }
/**
* @return ControlProviderInterface[]|\Pimple
*/
private function getVocabularies()
{
return $this->app['vocabularies'];
}
} }

View File

@@ -38,9 +38,28 @@ class SearchEngineController extends Controller
return $this->render('admin/search-engine/elastic-search.html.twig', [ return $this->render('admin/search-engine/elastic-search.html.twig', [
'form' => $form->createView(), 'form' => $form->createView(),
'indexer' => $this->app['elasticsearch.indexer']
]); ]);
} }
public function dropIndexAction(Request $request)
{
$indexer = $this->app['elasticsearch.indexer'];
if ($indexer->indexExists()) {
$indexer->deleteIndex();
}
return $this->app->redirectPath('admin_searchengine_form');
}
public function createIndexAction(Request $request)
{
$indexer = $this->app['elasticsearch.indexer'];
if (!$indexer->indexExists()) {
$indexer->createIndex();
}
return $this->app->redirectPath('admin_searchengine_form');
}
/** /**
* @return ElasticsearchOptions * @return ElasticsearchOptions
*/ */

View File

@@ -1,5 +1,5 @@
<?php <?php
/** /*
* This file is part of Phraseanet * This file is part of Phraseanet
* *
* (c) 2005-2016 Alchemy * (c) 2005-2016 Alchemy
@@ -49,14 +49,13 @@ class SearchController extends Controller
private function searchAndFormatEngineResult(Request $request) private function searchAndFormatEngineResult(Request $request)
{ {
$options = SearchEngineOptions::fromRequest($this->app, $request); $options = SearchEngineOptions::fromRequest($this->app, $request);
$options->setFirstResult($request->get('offset_start') ?: 0);
$offsetStart = (int) ($request->get('offset_start') ?: 0); $options->setMaxResults($request->get('per_page') ?: 10);
$perPage = (int) $request->get('per_page') ?: 10;
$query = (string) $request->get('query'); $query = (string) $request->get('query');
$this->getSearchEngine()->resetCache(); $this->getSearchEngine()->resetCache();
$search_result = $this->getSearchEngine()->query($query, $offsetStart, $perPage, $options); $search_result = $this->getSearchEngine()->query($query, $options);
$this->getUserManipulator()->logQuery($this->getAuthenticatedUser(), $search_result->getQuery()); $this->getUserManipulator()->logQuery($this->getAuthenticatedUser(), $search_result->getQuery());
@@ -74,8 +73,8 @@ class SearchController extends Controller
$this->getSearchEngine()->clearCache(); $this->getSearchEngine()->clearCache();
$ret = [ $ret = [
'offset_start' => $offsetStart, 'offset_start' => $options->getFirstResult(),
'per_page' => $perPage, 'per_page' => $options->getMaxResults(),
'available_results' => $search_result->getAvailable(), 'available_results' => $search_result->getAvailable(),
'total_results' => $search_result->getTotal(), 'total_results' => $search_result->getTotal(),
'error' => (string)$search_result->getError(), 'error' => (string)$search_result->getError(),

View File

@@ -47,7 +47,7 @@ use Alchemy\Phrasea\Model\Entities\ValidationData;
use Alchemy\Phrasea\Model\Entities\ValidationParticipant; use Alchemy\Phrasea\Model\Entities\ValidationParticipant;
use Alchemy\Phrasea\Model\Manipulator\TaskManipulator; use Alchemy\Phrasea\Model\Manipulator\TaskManipulator;
use Alchemy\Phrasea\Model\Manipulator\UserManipulator; use Alchemy\Phrasea\Model\Manipulator\UserManipulator;
use Alchemy\Phrasea\Model\Provider\SecretProvider; use Alchemy\Phrasea\Model\RecordReferenceInterface;
use Alchemy\Phrasea\Model\Repositories\BasketRepository; use Alchemy\Phrasea\Model\Repositories\BasketRepository;
use Alchemy\Phrasea\Model\Repositories\FeedEntryRepository; use Alchemy\Phrasea\Model\Repositories\FeedEntryRepository;
use Alchemy\Phrasea\Model\Repositories\FeedRepository; use Alchemy\Phrasea\Model\Repositories\FeedRepository;
@@ -61,8 +61,8 @@ use Alchemy\Phrasea\SearchEngine\SearchEngineResult;
use Alchemy\Phrasea\SearchEngine\SearchEngineSuggestion; use Alchemy\Phrasea\SearchEngine\SearchEngineSuggestion;
use Alchemy\Phrasea\Status\StatusStructure; use Alchemy\Phrasea\Status\StatusStructure;
use Alchemy\Phrasea\TaskManager\LiveInformation; use Alchemy\Phrasea\TaskManager\LiveInformation;
use Alchemy\Phrasea\Utilities\NullableDateTime;
use Doctrine\ORM\EntityManager; use Doctrine\ORM\EntityManager;
use Firebase\JWT\JWT;
use Symfony\Component\Form\Form; use Symfony\Component\Form\Form;
use Symfony\Component\HttpFoundation\File\UploadedFile; use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
@@ -139,10 +139,10 @@ class V1Controller extends Controller
'pid' => $data['process-id'], 'pid' => $data['process-id'],
'jobId' => $task->getJobId(), 'jobId' => $task->getJobId(),
'period' => $task->getPeriod(), 'period' => $task->getPeriod(),
'last_exec_time' => $task->getLastExecution() ? $task->getLastExecution()->format(DATE_ATOM) : null, 'last_exec_time' => NullableDateTime::format($task->getLastExecution()),
'last_execution' => $task->getLastExecution() ? $task->getLastExecution()->format(DATE_ATOM) : null, 'last_execution' => NullableDateTime::format($task->getLastExecution()),
'updated' => $task->getUpdated() ? $task->getUpdated()->format(DATE_ATOM) : null, 'updated' => NullableDateTime::format($task->getUpdated()),
'created' => $task->getCreated() ? $task->getCreated()->format(DATE_ATOM) : null, 'created' => NullableDateTime::format($task->getCreated()),
'auto_start' => $task->getStatus() === Task::STATUS_STARTED, 'auto_start' => $task->getStatus() === Task::STATUS_STARTED,
'crashed' => $task->getCrashed(), 'crashed' => $task->getCrashed(),
]; ];
@@ -710,9 +710,9 @@ class V1Controller extends Controller
'position' => $user->getActivity() ?: null, 'position' => $user->getActivity() ?: null,
'company' => $user->getCompany() ?: null, 'company' => $user->getCompany() ?: null,
'geoname_id' => $user->getGeonameId() ?: null, 'geoname_id' => $user->getGeonameId() ?: null,
'last_connection' => $user->getLastConnection() ? $user->getLastConnection()->format(DATE_ATOM) : null, 'last_connection' => NullableDateTime::format($user->getLastConnection()),
'created_on' => $user->getCreated() ? $user->getCreated()->format(DATE_ATOM) : null, 'created_on' => NullableDateTime::format($user->getCreated()),
'updated_on' => $user->getUpdated() ? $user->getUpdated()->format(DATE_ATOM) : null, 'updated_on' => NullableDateTime::format($user->getUpdated()),
'locale' => $user->getLocale() ?: null, 'locale' => $user->getLocale() ?: null,
]; ];
} }
@@ -1003,37 +1003,11 @@ class V1Controller extends Controller
'substituted' => $media->is_substituted(), 'substituted' => $media->is_substituted(),
'created_on' => $media->get_creation_date()->format(DATE_ATOM), 'created_on' => $media->get_creation_date()->format(DATE_ATOM),
'updated_on' => $media->get_modification_date()->format(DATE_ATOM), 'updated_on' => $media->get_modification_date()->format(DATE_ATOM),
'url' => $this->generateSubDefinitionUrl($issuer, $media, $urlTTL), 'url' => $this->app['media_accessor.subdef_url_generator']->generate($issuer, $media, $urlTTL),
'url_ttl' => $urlTTL, 'url_ttl' => $urlTTL,
]; ];
} }
/**
* @param User $issuer
* @param \media_subdef $subdef
* @param int $url_ttl
* @return string
*/
private function generateSubDefinitionUrl(User $issuer, \media_subdef $subdef, $url_ttl)
{
$payload = [
'iat' => time(),
'iss' => $issuer->getId(),
'sdef' => [$subdef->get_sbas_id(), $subdef->get_record_id(), $subdef->get_name()],
];
if ($url_ttl >= 0) {
$payload['exp'] = $payload['iat'] + $url_ttl;
}
/** @var SecretProvider $provider */
$provider = $this->app['provider.secrets'];
$secret = $provider->getSecretForUser($issuer);
return $this->app->url('media_accessor', [
'token' => JWT::encode($payload, $secret->getToken(), 'HS256', $secret->getId()),
]);
}
private function listPermalink(\media_Permalink_Adapter $permalink) private function listPermalink(\media_Permalink_Adapter $permalink)
{ {
$downloadUrl = $permalink->get_url(); $downloadUrl = $permalink->get_url();
@@ -1063,19 +1037,23 @@ class V1Controller extends Controller
{ {
list($ret, $search_result) = $this->prepareSearchRequest($request); list($ret, $search_result) = $this->prepareSearchRequest($request);
$ret['results'] = ['records' => [], 'stories' => []]; $records = [];
$stories = [];
/** @var SearchEngineResult $search_result */ /** @var SearchEngineResult $search_result */
$references = new RecordReferenceCollection($search_result->getResults()); foreach ($search_result->getResults() as $record) {
foreach ($references->toRecords($this->getApplicationBox()) as $record) {
if ($record->isStory()) { if ($record->isStory()) {
$ret['results']['stories'][] = $this->listStory($request, $record); $stories[] = $record;
} else { } else {
$ret['results']['records'][] = $this->listRecord($request, $record); $records[] = $record;
} }
} }
$ret['results'] = [
'records' => $this->listRecords($request, $records),
'stories' => $this->listStories($request, $stories),
];
return Result::create($request, $ret)->createResponse(); return Result::create($request, $ret)->createResponse();
} }
@@ -1110,13 +1088,13 @@ class V1Controller extends Controller
{ {
$options = SearchEngineOptions::fromRequest($this->app, $request); $options = SearchEngineOptions::fromRequest($this->app, $request);
$offsetStart = (int) ($request->get('offset_start') ?: 0); $options->setFirstResult((int) ($request->get('offset_start') ?: 0));
$perPage = (int) $request->get('per_page') ?: 10; $options->setMaxResults((int) $request->get('per_page') ?: 10);
$query = (string) $request->get('query'); $query = (string) $request->get('query');
$this->getSearchEngine()->resetCache(); $this->getSearchEngine()->resetCache();
$search_result = $this->getSearchEngine()->query($query, $offsetStart, $perPage, $options); $search_result = $this->getSearchEngine()->query($query, $options);
$this->getUserManipulator()->logQuery($this->getAuthenticatedUser(), $search_result->getQuery()); $this->getUserManipulator()->logQuery($this->getAuthenticatedUser(), $search_result->getQuery());
@@ -1134,8 +1112,8 @@ class V1Controller extends Controller
$this->getSearchEngine()->clearCache(); $this->getSearchEngine()->clearCache();
$ret = [ $ret = [
'offset_start' => $offsetStart, 'offset_start' => $options->getFirstResult(),
'per_page' => $perPage, 'per_page' => $options->getMaxResults(),
'available_results' => $search_result->getAvailable(), 'available_results' => $search_result->getAvailable(),
'total_results' => $search_result->getTotal(), 'total_results' => $search_result->getTotal(),
'error' => (string)$search_result->getError(), 'error' => (string)$search_result->getError(),
@@ -1154,6 +1132,30 @@ class V1Controller extends Controller
return [$ret, $search_result]; return [$ret, $search_result];
} }
/**
* @param Request $request
* @param RecordReferenceInterface[]|RecordReferenceCollection $records
* @return array
*/
public function listRecords(Request $request, $records)
{
if (!$records instanceof RecordReferenceCollection) {
$records = new RecordReferenceCollection($records);
}
$technicalData = $this->app['service.technical_data']->fetchRecordsTechnicalData($records);
$data = [];
foreach ($records->toRecords($this->getApplicationBox()) as $index => $record) {
$record->setTechnicalDataSet($technicalData[$index]);
$data[$index] = $this->listRecord($request, $record);
}
return $data;
}
/** /**
* Retrieve detailed information about one record * Retrieve detailed information about one record
* *
@@ -1197,6 +1199,27 @@ class V1Controller extends Controller
return $data; 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 * Retrieve detailed information about one story
* *
@@ -1211,10 +1234,6 @@ class V1Controller extends Controller
return Result::createError($request, 404, 'Story not found')->createResponse(); return Result::createError($request, 404, 'Story not found')->createResponse();
} }
$records = array_map(function (\record_adapter $record) use ($request) {
return $this->listRecord($request, $record);
}, array_values($story->get_children()->get_elements()));
$caption = $story->get_caption(); $caption = $story->get_caption();
$format = function (\caption_record $caption, $dcField) { $format = function (\caption_record $caption, $dcField) {
@@ -1256,7 +1275,7 @@ class V1Controller extends Controller
'dc:title' => $format($caption, \databox_Field_DCESAbstract::Title), 'dc:title' => $format($caption, \databox_Field_DCESAbstract::Title),
'dc:type' => $format($caption, \databox_Field_DCESAbstract::Type), 'dc:type' => $format($caption, \databox_Field_DCESAbstract::Type),
], ],
'records' => $records, 'records' => $this->listRecords($request, $story->getChildren()->get_elements()),
]; ];
} }
@@ -1448,11 +1467,7 @@ class V1Controller extends Controller
]; ];
}, iterator_to_array($basket->getValidation()->getParticipants())); }, iterator_to_array($basket->getValidation()->getParticipants()));
$expires_on_atom = $basket->getValidation()->getExpires(); $expires_on_atom = NullableDateTime::format($basket->getValidation()->getExpires());
if ($expires_on_atom instanceof \DateTime) {
$expires_on_atom = $expires_on_atom->format(DATE_ATOM);
}
$ret = array_merge([ $ret = array_merge([
'validation_users' => $users, 'validation_users' => $users,
@@ -1530,7 +1545,7 @@ class V1Controller extends Controller
$status = $request->get('status'); $status = $request->get('status');
$datas = strrev($record->get_status()); $datas = strrev($record->getStatus());
if (!is_array($status)) { if (!is_array($status)) {
return $this->getBadRequestAction($request); return $this->getBadRequestAction($request);
@@ -1549,7 +1564,7 @@ class V1Controller extends Controller
$datas = substr($datas, 0, ($n)) . $value . substr($datas, ($n + 2)); $datas = substr($datas, 0, ($n)) . $value . substr($datas, ($n + 2));
} }
$record->set_binary_status(strrev($datas)); $record->setStatus(strrev($datas));
// @todo Move event dispatch inside record_adapter class (keeps things encapsulated) // @todo Move event dispatch inside record_adapter class (keeps things encapsulated)
$this->dispatch(PhraseaEvents::RECORD_EDIT, new RecordEdit($record)); $this->dispatch(PhraseaEvents::RECORD_EDIT, new RecordEdit($record));
@@ -2170,7 +2185,7 @@ class V1Controller extends Controller
$story->removeChild($record); $story->removeChild($record);
} }
return $record->get_serialize_key(); return $record->getId();
} }
public function addRecordsToStoryAction(Request $request, $databox_id, $story_id) public function addRecordsToStoryAction(Request $request, $databox_id, $story_id)
@@ -2232,7 +2247,7 @@ class V1Controller extends Controller
$this->dispatch(PhraseaEvents::RECORD_EDIT, new RecordEdit($story)); $this->dispatch(PhraseaEvents::RECORD_EDIT, new RecordEdit($story));
return $record->get_serialize_key(); return $record->getId();
} }
public function getCurrentUserAction(Request $request) public function getCurrentUserAction(Request $request)

View File

@@ -68,7 +68,7 @@ class DatafileController extends AbstractDelivery
$stamp = false; $stamp = false;
$watermark = !$this->acl->get($this->authentication->getUser()) $watermark = !$this->acl->get($this->authentication->getUser())
->has_right_on_base($record->get_base_id(), 'nowatermark'); ->has_right_on_base($record->getBaseId(), 'nowatermark');
if ($watermark && !$all_access) { if ($watermark && !$all_access) {
$subdef_class = null; $subdef_class = null;

View File

@@ -122,7 +122,7 @@ class PermalinkController extends AbstractDelivery
$watermark = $stamp = false; $watermark = $stamp = false;
if ($this->authentication->isAuthenticated()) { if ($this->authentication->isAuthenticated()) {
$watermark = !$this->acl->get($this->authentication->getUser())->has_right_on_base($record->get_base_id(), 'nowatermark'); $watermark = !$this->acl->get($this->authentication->getUser())->has_right_on_base($record->getBaseId(), 'nowatermark');
if ($watermark) { if ($watermark) {
/** @var BasketElementRepository $repository */ /** @var BasketElementRepository $repository */
@@ -138,7 +138,7 @@ class PermalinkController extends AbstractDelivery
return $this->deliverContentWithCaptionLink($request, $record, $subdef, $watermark, $stamp, $token); return $this->deliverContentWithCaptionLink($request, $record, $subdef, $watermark, $stamp, $token);
} }
$collection = \collection::getByBaseId($this->app, $record->get_base_id()); $collection = \collection::getByBaseId($this->app, $record->getBaseId());
switch ($collection->get_pub_wm()) { switch ($collection->get_pub_wm()) {
default: default:
case 'none': case 'none':
@@ -169,8 +169,8 @@ class PermalinkController extends AbstractDelivery
$response = $this->deliverContent($request, $record, $subdef, $watermark, $stamp); $response = $this->deliverContent($request, $record, $subdef, $watermark, $stamp);
$response->headers->set('Link', $this->app->url("permalinks_caption", [ $response->headers->set('Link', $this->app->url("permalinks_caption", [
'sbas_id' => $record->get_sbas_id(), 'sbas_id' => $record->getDataboxId(),
'record_id' => $record->get_record_id(), 'record_id' => $record->getRecordId(),
'token' => $token, 'token' => $token,
])); ]));

View File

@@ -20,7 +20,7 @@ use Alchemy\Phrasea\Model\Entities\Preset;
use Alchemy\Phrasea\Model\Entities\User; use Alchemy\Phrasea\Model\Entities\User;
use Alchemy\Phrasea\Model\Manipulator\PresetManipulator; use Alchemy\Phrasea\Model\Manipulator\PresetManipulator;
use Alchemy\Phrasea\Model\Repositories\PresetRepository; use Alchemy\Phrasea\Model\Repositories\PresetRepository;
use Alchemy\Phrasea\Vocabulary\Controller as VocabularyController; use Alchemy\Phrasea\Vocabulary\ControlProvider\ControlProviderInterface;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
@@ -146,17 +146,17 @@ class EditController extends Controller
foreach ($records as $record) { foreach ($records as $record) {
$indice = $record->getNumber(); $indice = $record->getNumber();
$elements[$indice] = [ $elements[$indice] = [
'bid' => $record->get_base_id(), 'bid' => $record->getBaseId(),
'rid' => $record->get_record_id(), 'rid' => $record->getRecordId(),
'sselcont_id' => null, 'sselcont_id' => null,
'_selected' => false, '_selected' => false,
'fields' => $databox_fields, 'fields' => $databox_fields,
]; ];
$elements[$indice]['statbits'] = []; $elements[$indice]['statbits'] = [];
if ($this->getAclForUser()->has_right_on_base($record->get_base_id(), 'chgstatus')) { if ($this->getAclForUser()->has_right_on_base($record->getBaseId(), 'chgstatus')) {
foreach ($status as $n => $s) { foreach ($status as $n => $s) {
$tmp_val = substr(strrev($record->get_status()), $n, 1); $tmp_val = substr(strrev($record->getStatus()), $n, 1);
$elements[$indice]['statbits'][$n]['value'] = ($tmp_val == '1') ? '1' : '0'; $elements[$indice]['statbits'][$n]['value'] = ($tmp_val == '1') ? '1' : '0';
$elements[$indice]['statbits'][$n]['dirty'] = false; $elements[$indice]['statbits'][$n]['dirty'] = false;
} }
@@ -209,7 +209,7 @@ class EditController extends Controller
['record' => $record] ['record' => $record]
); );
$elements[$indice]['type'] = $record->get_type(); $elements[$indice]['type'] = $record->getType();
} }
} }
@@ -231,7 +231,7 @@ class EditController extends Controller
} }
public function searchVocabularyAction(Request $request, $vocabulary) { public function searchVocabularyAction(Request $request, $vocabulary) {
$datas = ['success' => false, 'message' => '', 'results' => []]; $data = ['success' => false, 'message' => '', 'results' => []];
$sbas_id = (int) $request->query->get('sbas_id'); $sbas_id = (int) $request->query->get('sbas_id');
@@ -240,33 +240,33 @@ class EditController extends Controller
throw new \Exception('Invalid sbas_id'); throw new \Exception('Invalid sbas_id');
} }
$VC = VocabularyController::get($this->app, $vocabulary); /** @var ControlProviderInterface $vocabularyProvider */
$vocabularyProvider = $this->app['vocabularies'][strtolower($vocabulary)];
$databox = $this->findDataboxById($sbas_id); $databox = $this->findDataboxById($sbas_id);
} catch (\Exception $e) { } catch (\Exception $e) {
$datas['message'] = $this->app->trans('Vocabulary not found'); $data['message'] = $this->app->trans('Vocabulary not found');
return $this->app->json($datas); return $this->app->json($data);
} }
$query = $request->query->get('query'); $query = $request->query->get('query');
$results = $VC->find($query, $this->getAuthenticatedUser(), $databox); $results = $vocabularyProvider->find($query, $this->getAuthenticatedUser(), $databox);
$list = []; $list = [];
foreach ($results as $Term) { foreach ($results as $term) {
/* @var \Alchemy\Phrasea\Vocabulary\Term $Term */
$list[] = [ $list[] = [
'id' => $Term->getId(), 'id' => $term->getId(),
'context' => $Term->getContext(), 'context' => $term->getContext(),
'value' => $Term->getValue(), 'value' => $term->getValue(),
]; ];
} }
$datas['success'] = true; $data['success'] = true;
$datas['results'] = $list; $data['results'] = $list;
return $this->app->json($datas); return $this->app->json($data);
} }
public function applyAction(Request $request) { public function applyAction(Request $request) {
@@ -287,7 +287,7 @@ class EditController extends Controller
try { try {
$reg_record = $records->singleStory(); $reg_record = $records->singleStory();
$newsubdef_reg = new \record_adapter($this->app, $reg_record->get_sbas_id(), $request->request->get('newrepresent')); $newsubdef_reg = new \record_adapter($this->app, $reg_record->getDataboxId(), $request->request->get('newrepresent'));
foreach ($newsubdef_reg->get_subdefs() as $name => $value) { foreach ($newsubdef_reg->get_subdefs() as $name => $value) {
if (!in_array($name, ['thumbnail', 'preview'])) { if (!in_array($name, ['thumbnail', 'preview'])) {
@@ -300,7 +300,7 @@ class EditController extends Controller
$media = $this->app->getMediaFromUri($value->getRealPath()); $media = $this->app->getMediaFromUri($value->getRealPath());
$this->getSubDefinitionSubstituer()->substitute($reg_record, $name, $media); $this->getSubDefinitionSubstituer()->substitute($reg_record, $name, $media);
$this->getDispatcher()->dispatch(PhraseaEvents::RECORD_EDIT, new RecordEdit($reg_record)); $this->getDispatcher()->dispatch(PhraseaEvents::RECORD_EDIT, new RecordEdit($reg_record));
$this->getDataboxLogger($reg_record->get_databox())->log( $this->getDataboxLogger($reg_record->getDatabox())->log(
$reg_record, $reg_record,
\Session_Logger::EVENT_SUBSTITUTE, \Session_Logger::EVENT_SUBSTITUTE,
$name == 'document' ? 'HD' : $name, $name == 'document' ? 'HD' : $name,
@@ -325,7 +325,7 @@ class EditController extends Controller
continue; continue;
} }
$key = $record->get_serialize_key(); $key = $record->getId();
if (!array_key_exists($key, $elements)) { if (!array_key_exists($key, $elements)) {
continue; continue;
@@ -345,7 +345,7 @@ class EditController extends Controller
$this->getDispatcher()->dispatch(PhraseaEvents::RECORD_EDIT, new RecordEdit($record)); $this->getDispatcher()->dispatch(PhraseaEvents::RECORD_EDIT, new RecordEdit($record));
} }
$newstat = $record->get_status(); $newstat = $record->getStatus();
$statbits = ltrim($statbits, 'x'); $statbits = ltrim($statbits, 'x');
if (!in_array($statbits, ['', 'null'])) { if (!in_array($statbits, ['', 'null'])) {
$mask_and = ltrim(str_replace(['x', '0', '1', 'z'], ['1', 'z', '0', '1'], $statbits), '0'); $mask_and = ltrim(str_replace(['x', '0', '1', 'z'], ['1', 'z', '0', '1'], $statbits), '0');
@@ -359,13 +359,13 @@ class EditController extends Controller
$newstat = \databox_status::operation_or($newstat, $mask_or); $newstat = \databox_status::operation_or($newstat, $mask_or);
} }
$record->set_binary_status($newstat); $record->setStatus($newstat);
} }
$record $record
->write_metas() ->write_metas()
->get_collection() ->getCollection()
->reset_stamp($record->get_record_id()); ->reset_stamp($record->getRecordId());
if ($statbits != '') { if ($statbits != '') {
$this->getDataboxLogger($databox) $this->getDataboxLogger($databox)

View File

@@ -81,8 +81,8 @@ class FeedController extends Controller
foreach ($publishing as $record) { foreach ($publishing as $record) {
$item = new FeedItem(); $item = new FeedItem();
$item->setEntry($entry) $item->setEntry($entry)
->setRecordId($record->get_record_id()) ->setRecordId($record->getRecordId())
->setSbasId($record->get_sbas_id()); ->setSbasId($record->getDataboxId());
$entry->addItem($item); $entry->addItem($item);
$manager->persist($item); $manager->persist($item);
} }

View File

@@ -206,7 +206,7 @@ class LazaretController extends Controller
//Check if the chosen record is eligible to the substitution //Check if the chosen record is eligible to the substitution
foreach ($lazaretFile->getRecordsToSubstitute($this->app) as $record) { foreach ($lazaretFile->getRecordsToSubstitute($this->app) as $record) {
if ($record->get_record_id() !== (int) $recordId) { if ($record->getRecordId() !== (int) $recordId) {
continue; continue;
} }
@@ -230,7 +230,7 @@ class LazaretController extends Controller
$record = $lazaretFile->getCollection($this->app)->get_databox()->get_record($recordId); $record = $lazaretFile->getCollection($this->app)->get_databox()->get_record($recordId);
$this->getSubDefinitionSubstituer() $this->getSubDefinitionSubstituer()
->substitute($record, 'document', $media); ->substitute($record, 'document', $media);
$this->getDataboxLogger($record->get_databox())->log( $this->getDataboxLogger($record->getDatabox())->log(
$record, $record,
\Session_Logger::EVENT_SUBSTITUTE, \Session_Logger::EVENT_SUBSTITUTE,
'HD', 'HD',

View File

@@ -70,8 +70,8 @@ class MoveCollectionController extends Controller
if ($request->request->get("chg_coll_son") == "1") { if ($request->request->get("chg_coll_son") == "1") {
/** @var \record_adapter $child */ /** @var \record_adapter $child */
foreach ($record->get_children() as $child) { foreach ($record->getChildren() as $child) {
if ($this->getAclForUser()->has_right_on_base($child->get_base_id(), 'candeleterecord')) { if ($this->getAclForUser()->has_right_on_base($child->getBaseId(), 'candeleterecord')) {
$child->move_to_collection($collection, $this->getApplicationBox()); $child->move_to_collection($collection, $this->getApplicationBox());
} }
} }

View File

@@ -34,7 +34,7 @@ class PrinterController extends Controller
$layout = $request->request->get('lay'); $layout = $request->request->get('lay');
foreach ($printer->get_elements() as $record) { foreach ($printer->get_elements() as $record) {
$this->getDataboxLogger($record->get_databox())->log($record, \Session_Logger::EVENT_PRINT, $layout, ''); $this->getDataboxLogger($record->getDatabox())->log($record, \Session_Logger::EVENT_PRINT, $layout, '');
} }
$PDF = new PDFExport($this->app, $printer->get_elements(), $layout); $PDF = new PDFExport($this->app, $printer->get_elements(), $layout);

View File

@@ -87,17 +87,17 @@ class PropertyController extends Controller
foreach ($records as $record) { foreach ($records as $record) {
//perform logic //perform logic
$sbasId = $record->get_databox()->get_sbas_id(); $sbasId = $record->getDataboxId();
if (!isset($recordsType[$sbasId])) { if (!isset($recordsType[$sbasId])) {
$recordsType[$sbasId] = []; $recordsType[$sbasId] = [];
} }
if (!isset($recordsType[$sbasId][$record->get_type()])) { if (!isset($recordsType[$sbasId][$record->getType()])) {
$recordsType[$sbasId][$record->get_type()] = []; $recordsType[$sbasId][$record->getType()] = [];
} }
$recordsType[$sbasId][$record->get_type()][] = $record; $recordsType[$sbasId][$record->getType()][] = $record;
} }
return new Response($this->render('prod/actions/Property/type.html.twig', [ return new Response($this->render('prod/actions/Property/type.html.twig', [
@@ -120,18 +120,18 @@ class PropertyController extends Controller
$postStatus = (array) $request->request->get('status'); $postStatus = (array) $request->request->get('status');
foreach ($records as $record) { foreach ($records as $record) {
$sbasId = $record->get_databox()->get_sbas_id(); $sbasId = $record->getDataboxId();
//update record //update record
if (null !== $updatedStatus = $this->updateRecordStatus($record, $postStatus)) { if (null !== $updatedStatus = $this->updateRecordStatus($record, $postStatus)) {
$updated[$record->get_serialize_key()] = $updatedStatus; $updated[$record->getId()] = $updatedStatus;
} }
//update children if current record is a story //update children if current record is a story
if (isset($applyStatusToChildren[$sbasId]) && $record->isStory()) { if (isset($applyStatusToChildren[$sbasId]) && $record->isStory()) {
foreach ($record->get_children() as $child) { foreach ($record->getChildren() as $child) {
if (null !== $updatedStatus = $this->updateRecordStatus($child, $postStatus)) { if (null !== $updatedStatus = $this->updateRecordStatus($child, $postStatus)) {
$updated[$record->get_serialize_key()] = $updatedStatus; $updated[$record->getId()] = $updatedStatus;
} }
} }
} }
@@ -156,17 +156,17 @@ class PropertyController extends Controller
foreach ($records as $record) { foreach ($records as $record) {
try { try {
$recordType = !empty($forceType) ? $forceType : (isset($typeLst[$record->get_serialize_key()]) ? $typeLst[$record->get_serialize_key()] : null); $recordType = !empty($forceType) ? $forceType : (isset($typeLst[$record->getId()]) ? $typeLst[$record->getId()] : null);
$mimeType = isset($mimeLst[$record->get_serialize_key()]) ? $mimeLst[$record->get_serialize_key()] : null; $mimeType = isset($mimeLst[$record->getId()]) ? $mimeLst[$record->getId()] : null;
if ($recordType) { if ($recordType) {
$record->set_type($recordType); $record->setType($recordType);
$updated[$record->get_serialize_key()]['record_type'] = $recordType; $updated[$record->getId()]['record_type'] = $recordType;
} }
if ($mimeType) { if ($mimeType) {
$record->set_mime($mimeType); $record->setMimeType($mimeType);
$updated[$record->get_serialize_key()]['mime_type'] = $mimeType; $updated[$record->getId()]['mime_type'] = $mimeType;
} }
} catch (\Exception $e) { } catch (\Exception $e) {
@@ -185,18 +185,18 @@ class PropertyController extends Controller
*/ */
private function updateRecordStatus(\record_adapter $record, array $postStatus) private function updateRecordStatus(\record_adapter $record, array $postStatus)
{ {
$sbasId = $record->get_databox()->get_sbas_id(); $sbasId = $record->getDataboxId();
if (isset($postStatus[$sbasId]) && is_array($postStatus[$sbasId])) { if (isset($postStatus[$sbasId]) && is_array($postStatus[$sbasId])) {
$postStatus = $postStatus[$sbasId]; $postStatus = $postStatus[$sbasId];
$currentStatus = strrev($record->get_status()); $currentStatus = strrev($record->getStatus());
$newStatus = ''; $newStatus = '';
foreach (range(0, 31) as $i) { foreach (range(0, 31) as $i) {
$newStatus .= isset($postStatus[$i]) ? ($postStatus[$i] ? '1' : '0') : $currentStatus[$i]; $newStatus .= isset($postStatus[$i]) ? ($postStatus[$i] ? '1' : '0') : $currentStatus[$i];
} }
$record->set_binary_status(strrev($newStatus)); $record->setStatus(strrev($newStatus));
return [ return [
'current_status' => $currentStatus, 'current_status' => $currentStatus,

View File

@@ -124,7 +124,7 @@ class PushController extends Controller
); );
} }
$this->getDataboxLogger($element->get_databox())->log( $this->getDataboxLogger($element->getDatabox())->log(
$element, $element,
\Session_Logger::EVENT_VALIDATE, \Session_Logger::EVENT_VALIDATE,
$user_receiver->getId(), $user_receiver->getId(),
@@ -322,7 +322,7 @@ class PushController extends Controller
$manager->merge($basketElement); $manager->merge($basketElement);
$manager->persist($validationData); $manager->persist($validationData);
$this->getDataboxLogger($basketElement->getRecord($this->app)->get_databox())->log( $this->getDataboxLogger($basketElement->getRecord($this->app)->getDatabox())->log(
$basketElement->getRecord($this->app), $basketElement->getRecord($this->app),
\Session_Logger::EVENT_PUSH, \Session_Logger::EVENT_PUSH,
$participantUser->getId(), $participantUser->getId(),

View File

@@ -43,8 +43,6 @@ class QueryController extends Controller
$options = SearchEngineOptions::fromRequest($this->app, $request); $options = SearchEngineOptions::fromRequest($this->app, $request);
$form = $options->serialize();
$perPage = (int) $this->getSettings()->getUserSetting($this->getAuthenticatedUser(), 'images_per_page'); $perPage = (int) $this->getSettings()->getUserSetting($this->getAuthenticatedUser(), 'images_per_page');
$page = (int) $request->request->get('pag'); $page = (int) $request->request->get('pag');
@@ -56,13 +54,15 @@ class QueryController extends Controller
$page = 1; $page = 1;
} }
$options->setFirstResult(($page - 1) * $perPage);
$options->setMaxResults($perPage);
$user = $this->getAuthenticatedUser(); $user = $this->getAuthenticatedUser();
$userManipulator = $this->getUserManipulator(); $userManipulator = $this->getUserManipulator();
$userManipulator->logQuery($user, $query); $userManipulator->logQuery($user, $query);
try { try {
/** @var SearchEngineResult $result */ $result = $engine->query($query, $options);
$result = $engine->query($query, (($page - 1) * $perPage), $perPage, $options);
if ($this->getSettings()->getUserSetting($user, 'start_page') === 'LAST_QUERY') { if ($this->getSettings()->getUserSetting($user, 'start_page') === 'LAST_QUERY') {
$userManipulator->setUserSetting($user, 'start_page_query', $query); $userManipulator->setUserSetting($user, 'start_page_query', $query);
@@ -216,7 +216,7 @@ class QueryController extends Controller
$json['total_answers'] = (int) $result->getAvailable(); $json['total_answers'] = (int) $result->getAvailable();
$json['next_page'] = ($page < $npages && $result->getAvailable() > 0) ? ($page + 1) : false; $json['next_page'] = ($page < $npages && $result->getAvailable() > 0) ? ($page + 1) : false;
$json['prev_page'] = ($page > 1 && $result->getAvailable() > 0) ? ($page - 1) : false; $json['prev_page'] = ($page > 1 && $result->getAvailable() > 0) ? ($page - 1) : false;
$json['form'] = $form; $json['form'] = $options->serialize();
} }
catch(\Exception $e) { catch(\Exception $e) {
// we'd like a message from the parser so get all the exceptions messages // we'd like a message from the parser so get all the exceptions messages

View File

@@ -106,8 +106,8 @@ class RecordController extends Controller
]), ]),
"pos" => $record->getNumber(), "pos" => $record->getNumber(),
"title" => str_replace(array('[[em]]', '[[/em]]'), array('<em>', '</em>'), $record->get_title($query, $searchEngine)), "title" => str_replace(array('[[em]]', '[[/em]]'), array('<em>', '</em>'), $record->get_title($query, $searchEngine)),
"collection_name" => $record->get_collection()->get_name(), "collection_name" => $record->getCollection()->get_name(),
"collection_logo" => $record->get_collection()->getLogo($record->get_base_id(), $this->app), "collection_logo" => $record->getCollection()->getLogo($record->getBaseId(), $this->app),
]); ]);
} }
@@ -119,7 +119,8 @@ class RecordController extends Controller
*/ */
public function doDeleteRecords(Request $request) public function doDeleteRecords(Request $request)
{ {
$records = RecordsRequest::fromRequest($this->app, $request, !!$request->request->get('del_children'), [ $flatten = (bool)($request->request->get('del_children')) ? RecordsRequest::FLATTEN_YES_PRESERVE_STORIES : RecordsRequest::FLATTEN_NO;
$records = RecordsRequest::fromRequest($this->app, $request, $flatten, [
'candeleterecord' 'candeleterecord'
]); ]);
@@ -135,7 +136,7 @@ class RecordController extends Controller
foreach ($basketElements as $element) { foreach ($basketElements as $element) {
$manager->remove($element); $manager->remove($element);
$deleted[] = $element->getRecord($this->app)->get_serialize_key(); $deleted[] = $element->getRecord($this->app)->getId();
} }
$attachedStories = $StoryWZRepository->findByRecord($this->app, $record); $attachedStories = $StoryWZRepository->findByRecord($this->app, $record);
@@ -144,7 +145,7 @@ class RecordController extends Controller
$manager->remove($attachedStory); $manager->remove($attachedStory);
} }
$deleted[] = $record->get_serialize_key(); $deleted[] = $record->getId();
$record->delete(); $record->delete();
} catch (\Exception $e) { } catch (\Exception $e) {
@@ -186,7 +187,7 @@ class RecordController extends Controller
$renewed = []; $renewed = [];
foreach ($records as $record) { foreach ($records as $record) {
$renewed[$record->get_serialize_key()] = (string) $record->get_preview()->renew_url(); $renewed[$record->getId()] = (string) $record->get_preview()->renew_url();
}; };
return $this->app->json($renewed); return $this->app->json($renewed);

View File

@@ -84,8 +84,8 @@ class StoryController extends Controller
'message' => $this->app->trans('Story created'), 'message' => $this->app->trans('Story created'),
'WorkZone' => $storyWZ->getId(), 'WorkZone' => $storyWZ->getId(),
'story' => [ 'story' => [
'sbas_id' => $story->get_sbas_id(), 'sbas_id' => $story->getDataboxId(),
'record_id' => $story->get_record_id(), 'record_id' => $story->getRecordId(),
], ],
]; ];
@@ -109,7 +109,7 @@ class StoryController extends Controller
{ {
$Story = new \record_adapter($this->app, $sbas_id, $record_id); $Story = new \record_adapter($this->app, $sbas_id, $record_id);
if (!$this->getAclForUser()->has_right_on_base($Story->get_base_id(), 'canmodifrecord')) { if (!$this->getAclForUser()->has_right_on_base($Story->getBaseId(), 'canmodifrecord')) {
throw new AccessDeniedHttpException('You can not add document to this Story'); throw new AccessDeniedHttpException('You can not add document to this Story');
} }
@@ -145,7 +145,7 @@ class StoryController extends Controller
$story = new \record_adapter($this->app, $sbas_id, $record_id); $story = new \record_adapter($this->app, $sbas_id, $record_id);
$record = new \record_adapter($this->app, $child_sbas_id, $child_record_id); $record = new \record_adapter($this->app, $child_sbas_id, $child_record_id);
if (!$this->getAclForUser()->has_right_on_base($story->get_base_id(), 'canmodifrecord')) { if (!$this->getAclForUser()->has_right_on_base($story->getBaseId(), 'canmodifrecord')) {
throw new AccessDeniedHttpException('You can not add document to this Story'); throw new AccessDeniedHttpException('You can not add document to this Story');
} }
@@ -188,17 +188,17 @@ class StoryController extends Controller
throw new \Exception('This is not a story'); throw new \Exception('This is not a story');
} }
if (!$this->getAclForUser()->has_right_on_base($story->get_base_id(), 'canmodifrecord')) { if (!$this->getAclForUser()->has_right_on_base($story->getBaseId(), 'canmodifrecord')) {
throw new ControllerException($this->app->trans('You can not edit this story')); throw new ControllerException($this->app->trans('You can not edit this story'));
} }
$sql = 'UPDATE regroup SET ord = :ord WHERE rid_parent = :parent_id AND rid_child = :children_id'; $sql = 'UPDATE regroup SET ord = :ord WHERE rid_parent = :parent_id AND rid_child = :children_id';
$stmt = $story->get_databox()->get_connection()->prepare($sql); $stmt = $story->getDatabox()->get_connection()->prepare($sql);
foreach ($request->request->get('element') as $record_id => $ord) { foreach ($request->request->get('element') as $record_id => $ord) {
$params = [ $params = [
':ord' => $ord, ':ord' => $ord,
':parent_id' => $story->get_record_id(), ':parent_id' => $story->getRecordId(),
':children_id' => $record_id ':children_id' => $record_id
]; ];
$stmt->execute($params); $stmt->execute($params);

View File

@@ -68,7 +68,7 @@ class ToolsController extends Controller
continue; continue;
} }
$label = $this->app->trans('prod::tools: document'); $label = $this->app->trans('prod::tools: document');
} elseif (isset($databoxSubdefs[$subdefName])) { } elseif ($databoxSubdefs->hasSubdef($subdefName)) {
if (!$acl->has_access_to_subdef($record, $subdefName)) { if (!$acl->has_access_to_subdef($record, $subdefName)) {
continue; continue;
} }
@@ -202,7 +202,7 @@ class ToolsController extends Controller
$record->insertTechnicalDatas($this->getMediaVorus()); $record->insertTechnicalDatas($this->getMediaVorus());
$this->getMetadataSetter()->replaceMetadata($this->getMetadataReader() ->read($media), $record); $this->getMetadataSetter()->replaceMetadata($this->getMetadataReader() ->read($media), $record);
$this->getDataboxLogger($record->get_databox()) $this->getDataboxLogger($record->getDatabox())
->log($record, \Session_Logger::EVENT_SUBSTITUTE, 'HD', '' ); ->log($record, \Session_Logger::EVENT_SUBSTITUTE, 'HD', '' );
if ((int) $request->request->get('ccfilename') === 1) { if ((int) $request->request->get('ccfilename') === 1) {
@@ -259,7 +259,7 @@ class ToolsController extends Controller
$media = $this->app->getMediaFromUri($tempoFile); $media = $this->app->getMediaFromUri($tempoFile);
$this->getSubDefinitionSubstituer()->substitute($record, 'thumbnail', $media); $this->getSubDefinitionSubstituer()->substitute($record, 'thumbnail', $media);
$this->getDataboxLogger($record->get_databox()) $this->getDataboxLogger($record->getDatabox())
->log($record, \Session_Logger::EVENT_SUBSTITUTE, 'thumbnail', ''); ->log($record, \Session_Logger::EVENT_SUBSTITUTE, 'thumbnail', '');
unlink($tempoFile); unlink($tempoFile);
@@ -415,7 +415,7 @@ class ToolsController extends Controller
{ {
$dataUri = Parser::parse($subDefDataUri); $dataUri = Parser::parse($subDefDataUri);
$name = sprintf('extractor_thumb_%s', $record->get_serialize_key()); $name = sprintf('extractor_thumb_%s', $record->getId());
$fileName = sprintf('%s/%s.png', sys_get_temp_dir(), $name); $fileName = sprintf('%s/%s.png', sys_get_temp_dir(), $name);
file_put_contents($fileName, $dataUri->getData()); file_put_contents($fileName, $dataUri->getData());
@@ -423,7 +423,7 @@ class ToolsController extends Controller
$media = $this->app->getMediaFromUri($fileName); $media = $this->app->getMediaFromUri($fileName);
$this->getSubDefinitionSubstituer()->substitute($record, $subDefName, $media); $this->getSubDefinitionSubstituer()->substitute($record, $subDefName, $media);
$this->getDataboxLogger($record->get_databox()) $this->getDataboxLogger($record->getDatabox())
->log($record, \Session_Logger::EVENT_SUBSTITUTE, $subDefName, ''); ->log($record, \Session_Logger::EVENT_SUBSTITUTE, $subDefName, '');
unset($media); unset($media);

View File

@@ -178,7 +178,7 @@ class UploadController extends Controller
} }
if ($elementCreated instanceof \record_adapter) { if ($elementCreated instanceof \record_adapter) {
$id = $elementCreated->get_serialize_key(); $id = $elementCreated->getId();
$element = 'record'; $element = 'record';
$message = $this->app->trans('The record was successfully created'); $message = $this->app->trans('The record was successfully created');
@@ -194,7 +194,7 @@ class UploadController extends Controller
$media = $this->app->getMediaFromUri($fileName); $media = $this->app->getMediaFromUri($fileName);
$this->getSubDefinitionSubstituer()->substitute($elementCreated, 'thumbnail', $media); $this->getSubDefinitionSubstituer()->substitute($elementCreated, 'thumbnail', $media);
$this->getDataboxLogger($elementCreated->get_databox()) $this->getDataboxLogger($elementCreated->getDatabox())
->log($elementCreated, \Session_Logger::EVENT_SUBSTITUTE, 'thumbnail', ''); ->log($elementCreated, \Session_Logger::EVENT_SUBSTITUTE, 'thumbnail', '');
unset($media); unset($media);

View File

@@ -100,7 +100,7 @@ class WorkzoneController extends Controller
throw new \Exception('You can only attach stories'); throw new \Exception('You can only attach stories');
} }
if (!$acl->has_access_to_base($story->get_base_id())) { if (!$acl->has_access_to_base($story->getBaseId())) {
throw new AccessDeniedHttpException('You do not have access to this Story'); throw new AccessDeniedHttpException('You do not have access to this Story');
} }

View File

@@ -14,6 +14,7 @@ namespace Alchemy\Phrasea\Controller;
use Alchemy\Phrasea\Model\Entities\Basket; use Alchemy\Phrasea\Model\Entities\Basket;
use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\ArrayCollection;
use Alchemy\Phrasea\Application; use Alchemy\Phrasea\Application;
use record_adapter;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
@@ -46,13 +47,14 @@ class RecordsRequest extends ArrayCollection
if (self::FLATTEN_NO !== $flatten) { if (self::FLATTEN_NO !== $flatten) {
$to_remove = []; $to_remove = [];
/** @var record_adapter $record */
foreach ($this as $key => $record) { foreach ($this as $key => $record) {
if ($record->isStory()) { if ($record->isStory()) {
if (self::FLATTEN_YES === $flatten) { if (self::FLATTEN_YES === $flatten) {
$to_remove[] = $key; $to_remove[] = $key;
} }
foreach ($record->get_children() as $child) { foreach ($record->getChildren() as $child) {
$this->set($child->get_serialize_key(), $child); $this->set($child->getId(), $child);
} }
} }
} }
@@ -180,7 +182,7 @@ class RecordsRequest extends ArrayCollection
public function serializedList() public function serializedList()
{ {
if ($this->isSingleStory()) { if ($this->isSingleStory()) {
return $this->singleStory()->get_serialize_key(); return $this->singleStory()->getId();
} }
$basrec = []; $basrec = [];
@@ -211,7 +213,7 @@ class RecordsRequest extends ArrayCollection
$app['acl.basket']->hasAccess($basket, $app->getAuthenticatedUser()); $app['acl.basket']->hasAccess($basket, $app->getAuthenticatedUser());
foreach ($basket->getElements() as $basket_element) { foreach ($basket->getElements() as $basket_element) {
$received[$basket_element->getRecord($app)->get_serialize_key()] = $basket_element->getRecord($app); $received[$basket_element->getRecord($app)->getId()] = $basket_element->getRecord($app);
} }
} elseif ($request->get('story')) { } elseif ($request->get('story')) {
$repository = $app['repo.story-wz']; $repository = $app['repo.story-wz'];
@@ -230,7 +232,7 @@ class RecordsRequest extends ArrayCollection
} }
try { try {
$record = new \record_adapter($app, (int) $basrec[0], (int) $basrec[1]); $record = new \record_adapter($app, (int) $basrec[0], (int) $basrec[1]);
$received[$record->get_serialize_key()] = $record; $received[$record->getId()] = $record;
unset($record); unset($record);
} catch (NotFoundHttpException $e) { } catch (NotFoundHttpException $e) {
continue; continue;

View File

@@ -325,8 +325,8 @@ class InformationController extends Controller
/** @var \record_adapter $record */ /** @var \record_adapter $record */
$reportArray = $what->buildTabUserWhat( $reportArray = $what->buildTabUserWhat(
$record->get_base_id(), $record->getBaseId(),
$record->get_record_id(), $record->getRecordId(),
$config $config
); );
@@ -393,7 +393,7 @@ class InformationController extends Controller
} }
} }
$filter->addfilter('record_id', '=', $record->get_record_id()); $filter->addfilter('record_id', '=', $record->getRecordId());
$download->setFilter($filter->getTabFilter()); $download->setFilter($filter->getTabFilter());
$download->setOrder('ddate', 'DESC'); $download->setOrder('ddate', 'DESC');

View File

@@ -14,6 +14,7 @@ namespace Alchemy\Phrasea\ControllerProvider\Admin;
use Alchemy\Phrasea\Application as PhraseaApplication; use Alchemy\Phrasea\Application as PhraseaApplication;
use Alchemy\Phrasea\Controller\Admin\FieldsController; use Alchemy\Phrasea\Controller\Admin\FieldsController;
use Alchemy\Phrasea\ControllerProvider\ControllerProviderTrait; use Alchemy\Phrasea\ControllerProvider\ControllerProviderTrait;
use Alchemy\Phrasea\Vocabulary\ControlProvider\UserProvider;
use Silex\Application; use Silex\Application;
use Silex\ControllerProviderInterface; use Silex\ControllerProviderInterface;
use Silex\ServiceProviderInterface; use Silex\ServiceProviderInterface;
@@ -24,6 +25,15 @@ class Fields implements ControllerProviderInterface, ServiceProviderInterface
public function register(Application $app) public function register(Application $app)
{ {
$app['vocabularies'] = $app->share(function (PhraseaApplication $app) {
$vocabularies = new \Pimple();
$vocabularies['user'] = $vocabularies->share(function () use ($app) {
return new UserProvider($app);
});
return $vocabularies;
});
$app['controller.admin.fields'] = $app->share(function (PhraseaApplication $app) { $app['controller.admin.fields'] = $app->share(function (PhraseaApplication $app) {
return new FieldsController($app); return new FieldsController($app);
}); });

View File

@@ -37,9 +37,15 @@ class SearchEngine implements ControllerProviderInterface, ServiceProviderInterf
/** @var ControllerCollection $controllers */ /** @var ControllerCollection $controllers */
$controllers = $app['controllers_factory']; $controllers = $app['controllers_factory'];
$controllers->post('/drop_index', 'controller.admin.search-engine:dropIndexAction')
->bind("admin_searchengine_drop_index");
$controllers->post('/create_index', 'controller.admin.search-engine:createIndexAction')
->bind("admin_searchengine_create_index");
$controllers->match('/', 'controller.admin.search-engine:formConfigurationPanelAction') $controllers->match('/', 'controller.admin.search-engine:formConfigurationPanelAction')
->method('GET|POST') ->method('GET|POST')
->bind('admin_searchengine_form'); ->bind('admin_searchengine_form');
return $controllers; return $controllers;
} }

View File

@@ -13,8 +13,10 @@ use Alchemy\Phrasea\Application as PhraseaApplication;
use Alchemy\Phrasea\Controller\Api\BasketController; use Alchemy\Phrasea\Controller\Api\BasketController;
use Alchemy\Phrasea\Controller\Api\LazaretController; use Alchemy\Phrasea\Controller\Api\LazaretController;
use Alchemy\Phrasea\Controller\Api\SearchController; use Alchemy\Phrasea\Controller\Api\SearchController;
use Alchemy\Phrasea\Controller\LazyLocator;
use Alchemy\Phrasea\ControllerProvider\ControllerProviderTrait; use Alchemy\Phrasea\ControllerProvider\ControllerProviderTrait;
use Alchemy\Phrasea\Core\Event\Listener\OAuthListener; use Alchemy\Phrasea\Core\Event\Listener\OAuthListener;
use Alchemy\Phrasea\Order\Controller\ApiOrderController;
use Silex\Application; use Silex\Application;
use Silex\Controller; use Silex\Controller;
use Silex\ControllerProviderInterface; use Silex\ControllerProviderInterface;
@@ -48,6 +50,15 @@ class V2 implements ControllerProviderInterface, ServiceProviderInterface
return new SearchController($app); return new SearchController($app);
} }
); );
$app['controller.api.v2.orders'] = $app->share(
function (PhraseaApplication $app) {
return (new ApiOrderController($app))
->setDispatcher($app['dispatcher'])
->setEntityManagerLocator(new LazyLocator($app, 'orm.em'))
->setJsonBodyHelper($app['json.body_helper']);
}
);
} }
public function boot(Application $app) public function boot(Application $app)
@@ -89,6 +100,22 @@ class V2 implements ControllerProviderInterface, ServiceProviderInterface
->bind('api_v2_quarantine_item_add'); ->bind('api_v2_quarantine_item_add');
$this->addQuarantineMiddleware($controller); $this->addQuarantineMiddleware($controller);
$controllers->post('/orders/', 'controller.api.v2.orders:createAction')
->bind('api_v2_orders_create');
$controllers->get('/orders/', 'controller.api.v2.orders:indexAction')
->bind('api_v2_orders_index');
$controllers->get('/orders/{orderId}', 'controller.api.v2.orders:showAction')
->assert('orderId', '\d+')
->bind('api_v2_orders_show');
$controllers->post('/orders/{orderId}/accept', 'controller.api.v2.orders:acceptElementsAction')
->assert('orderId', '\d+')
->bind('api_v2_orders_accept');
$controllers->post('/orders/{orderId}/deny', 'controller.api.v2.orders:denyElementsAction')
->assert('orderId', '\d+')
->bind('api_v2_orders_deny');
return $controllers; return $controllers;
} }

View File

@@ -10,6 +10,7 @@
namespace Alchemy\Phrasea\ControllerProvider; namespace Alchemy\Phrasea\ControllerProvider;
use Alchemy\Phrasea\Controller\MediaAccessorController; use Alchemy\Phrasea\Controller\MediaAccessorController;
use Alchemy\Phrasea\Media\MediaSubDefinitionUrlGenerator;
use Alchemy\Phrasea\Model\Entities\Secret; use Alchemy\Phrasea\Model\Entities\Secret;
use Alchemy\Phrasea\Model\Provider\DefaultSecretProvider; use Alchemy\Phrasea\Model\Provider\DefaultSecretProvider;
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityManagerInterface;
@@ -33,6 +34,13 @@ class MediaAccessor implements ServiceProviderInterface, ControllerProviderInter
return new DefaultSecretProvider($app['repo.secrets'], $app['random.medium']); return new DefaultSecretProvider($app['repo.secrets'], $app['random.medium']);
}); });
$app['media_accessor.subdef_url_generator'] = $app->share(function (Application $app) {
$defaultTTL = (int)$app['conf']->get(['registry', 'general', 'default-subdef-url-ttl'], 0);
return new MediaSubDefinitionUrlGenerator($app['url_generator'], $app['provider.secrets'], $defaultTTL);
});
$app['controller.media_accessor'] = $app->share(function (Application $app) { $app['controller.media_accessor'] = $app->share(function (Application $app) {
return (new MediaAccessorController($app)) return (new MediaAccessorController($app))
->setAllowedAlgorithms(['HS256']) ->setAllowedAlgorithms(['HS256'])

View File

@@ -14,6 +14,7 @@ namespace Alchemy\Phrasea\ControllerProvider;
use Alchemy\Phrasea\Application as PhraseaApplication; use Alchemy\Phrasea\Application as PhraseaApplication;
use Alchemy\Phrasea\Controller\LazyLocator; use Alchemy\Phrasea\Controller\LazyLocator;
use Alchemy\Phrasea\Controller\PermalinkController; use Alchemy\Phrasea\Controller\PermalinkController;
use Alchemy\Phrasea\Core\Event\Listener\OAuthListener;
use Silex\Application; use Silex\Application;
use Silex\ControllerCollection; use Silex\ControllerCollection;
use Silex\ControllerProviderInterface; use Silex\ControllerProviderInterface;
@@ -69,6 +70,7 @@ class Permalink implements ControllerProviderInterface, ServiceProviderInterface
->bind('permalinks_permaview_old'); ->bind('permalinks_permaview_old');
$controllers->get('/v1/{sbas_id}/{record_id}/{subdef}/{label}', 'controller.permalink:deliverPermalink') $controllers->get('/v1/{sbas_id}/{record_id}/{subdef}/{label}', 'controller.permalink:deliverPermalink')
->before(new OAuthListener(['exit_not_present' => false]))
->bind('permalinks_permalink'); ->bind('permalinks_permalink');
$controllers->match('/v1/{sbas_id}/{record_id}/{subdef}/{label}', 'controller.permalink:getOptionsResponse') $controllers->match('/v1/{sbas_id}/{record_id}/{subdef}/{label}', 'controller.permalink:getOptionsResponse')

View File

@@ -212,6 +212,9 @@ class Configuration implements ConfigurationInterface
private function writeCacheConfig($content) private function writeCacheConfig($content)
{ {
$this->dumpFile($this->compiled, $content, 0600); $this->dumpFile($this->compiled, $content, 0600);
if(function_exists("opcache_invalidate")) {
opcache_invalidate($this->compiled);
}
} }
private function isConfigFresh() private function isConfigFresh()

View File

@@ -1,9 +1,8 @@
<?php <?php
/* /*
* This file is part of Phraseanet * This file is part of Phraseanet
* *
* (c) 2005-2014 Alchemy * (c) 2005-2016 Alchemy
* *
* For the full copyright and license information, please view the LICENSE * For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code. * file that was distributed with this source code.
@@ -16,22 +15,19 @@ use Doctrine\DBAL\DriverManager;
class ConnectionPoolManager class ConnectionPoolManager
{ {
/** @var Connection[] */ /**
* @var Connection[]
*/
private $connections = []; private $connections = [];
public function __construct()
{
}
public function __destruct() public function __destruct()
{ {
$this->closeAll(); $this->closeAll();
$this->connections = [];
} }
public function closeAll() public function closeAll()
{ {
foreach ($this->connections as $key => $conn) { foreach ($this->connections as $conn) {
$conn->close(); $conn->close();
} }
} }
@@ -88,4 +84,9 @@ class ConnectionPoolManager
return $this->connections[$key] = DriverManager::getConnection($params); return $this->connections[$key] = DriverManager::getConnection($params);
} }
public function all()
{
return $this->connections;
}
} }

View File

@@ -26,8 +26,8 @@ class BridgeSubscriber extends AbstractNotificationSubscriber
'usr_id' => $user->getId(), 'usr_id' => $user->getId(),
'reason' => $event->getReason(), 'reason' => $event->getReason(),
'account_id' => $account->get_id(), 'account_id' => $account->get_id(),
'sbas_id' => $event->getElement()->get_record()->get_sbas_id(), 'sbas_id' => $event->getElement()->get_record()->getDataboxId(),
'record_id' => $event->getElement()->get_record()->get_record_id(), 'record_id' => $event->getElement()->get_record()->getRecordId(),
]; ];
$datas = json_encode($params); $datas = json_encode($params);

View File

@@ -1,53 +0,0 @@
<?php
/*
* This file is part of Phraseanet
*
* (c) 2005-2014 Alchemy
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Phrasea\Core\Event\Subscriber;
use Alchemy\Phrasea\Application;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\HttpKernel\KernelEvents;
class DoctrineQueriesLoggerSubscriber implements EventSubscriberInterface
{
private $app;
public function __construct(Application $app)
{
$this->app = $app;
}
public static function getSubscribedEvents()
{
return [
KernelEvents::RESPONSE => [
['logQueries', -255],
],
];
}
public function logQueries(GetResponseEvent $event)
{
if (Application::ENV_DEV !== $this->app->getEnvironment()) {
return;
}
foreach ($this->app['orm.query.logger']->queries as $query ) {
$this->app['orm.sql-logger']->debug($query['sql'], array(
'params' => $query['params'],
'types' => $query['types'],
'time' => $query['executionMS']
));
}
}
}

View File

@@ -1,4 +1,12 @@
<?php <?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\Core\MetaProvider; namespace Alchemy\Phrasea\Core\MetaProvider;
@@ -6,6 +14,7 @@ use Alchemy\Phrasea\Application as PhraseaApplication;
use Alchemy\Phrasea\Cache\Manager; use Alchemy\Phrasea\Cache\Manager;
use Alchemy\Phrasea\Core\Provider\ORMServiceProvider; use Alchemy\Phrasea\Core\Provider\ORMServiceProvider;
use Dflydev\Silex\Provider\DoctrineOrm\DoctrineOrmServiceProvider; use Dflydev\Silex\Provider\DoctrineOrm\DoctrineOrmServiceProvider;
use Doctrine\Common\EventManager;
use Doctrine\DBAL\Events; use Doctrine\DBAL\Events;
use Doctrine\ORM\Configuration; use Doctrine\ORM\Configuration;
use Gedmo\DoctrineExtensions as GedmoExtension; use Gedmo\DoctrineExtensions as GedmoExtension;
@@ -28,12 +37,14 @@ class DatabaseMetaProvider implements ServiceProviderInterface
private function setupDBAL(PhraseaApplication $app) private function setupDBAL(PhraseaApplication $app)
{ {
$app['dbs.config'] = $app->share($app->extend('dbs.config', function ($configs, $app) { $app['dbs.config'] = $app->share($app->extend('dbs.config', function ($configs, $app) {
if (! $app->isDebug()) { if (!isset($app['dbal.config.register.loggers'])) {
return $configs; return $configs;
} }
foreach($configs->keys() as $service) { $loggerRegisterCallable = $app['dbal.config.register.loggers'];
$app['dbal.config.register.loggers']($configs[$service]);
foreach ($configs->keys() as $service) {
$loggerRegisterCallable($configs[$service], $service);
} }
return $configs; return $configs;
@@ -56,7 +67,7 @@ class DatabaseMetaProvider implements ServiceProviderInterface
{ {
// Override "orm.cache.configurer" service provided for benefiting // Override "orm.cache.configurer" service provided for benefiting
// of "phraseanet.cache-service" // of "phraseanet.cache-service"
$app['orm.cache.configurer'] = $app->protect(function($name, Configuration $config, $options) use ($app) { $app['orm.cache.configurer'] = $app->protect(function ($name, Configuration $config, $options) use ($app) {
/** @var Manager $service */ /** @var Manager $service */
$service = $app['phraseanet.cache-service']; $service = $app['phraseanet.cache-service'];
@@ -74,7 +85,7 @@ class DatabaseMetaProvider implements ServiceProviderInterface
); );
}); });
$app['orm.proxies_dir'] = $app['root.path'].'/resources/proxies'; $app['orm.proxies_dir'] = $app['root.path'] . '/resources/proxies';
$app['orm.auto_generate_proxies'] = $app['debug']; $app['orm.auto_generate_proxies'] = $app['debug'];
$app['orm.proxies_namespace'] = 'Alchemy\Phrasea\Model\Proxies'; $app['orm.proxies_namespace'] = 'Alchemy\Phrasea\Model\Proxies';

View File

@@ -22,7 +22,6 @@ use Alchemy\Phrasea\Model\NativeQueryProvider;
use Doctrine\Common\Annotations\AnnotationReader; use Doctrine\Common\Annotations\AnnotationReader;
use Doctrine\Common\Annotations\CachedReader; use Doctrine\Common\Annotations\CachedReader;
use Doctrine\Common\Cache\ArrayCache; use Doctrine\Common\Cache\ArrayCache;
use Doctrine\DBAL\Logging\DebugStack;
use Doctrine\DBAL\Types\Type; use Doctrine\DBAL\Types\Type;
use Doctrine\ORM\Configuration as ORMConfig; use Doctrine\ORM\Configuration as ORMConfig;
use Doctrine\ORM\EntityManager; use Doctrine\ORM\EntityManager;
@@ -37,10 +36,11 @@ class ORMServiceProvider implements ServiceProviderInterface
public function register(Application $app) public function register(Application $app)
{ {
// Provides DSN string using database information // Provides DSN string using database information
$app['db.dsn'] = $app->protect(function(array $params) use ($app) { $app['db.dsn'] = $app->protect(function (array $params) use ($app) {
$params = $app['db.info']($params); $params = $app['db.info']($params);
switch ($params['driver']) { switch ($params['driver'])
{
case 'pdo_mysql': case 'pdo_mysql':
return sprintf('%s://%s:%s@%s:%s/%s', return sprintf('%s://%s:%s@%s:%s/%s',
$params['driver'], $params['driver'],
@@ -61,22 +61,22 @@ class ORMServiceProvider implements ServiceProviderInterface
}); });
// Hash a DSN string // Hash a DSN string
$app['hash.dsn'] = $app->protect(function($dsn) { $app['hash.dsn'] = $app->protect(function ($dsn) {
return md5($dsn); return md5($dsn);
}); });
// Return database test configuration // Return database test configuration
$app['db.test.info'] = $app->share(function() use ($app) { $app['db.test.info'] = $app->share(function () use ($app) {
return $app['conf']->get(['main', 'database-test'], array()); return $app['conf']->get(['main', 'database-test'], array());
}); });
// Return application box database configuration // Return application box database configuration
$app['db.appbox.info'] = $app->share(function() use ($app) { $app['db.appbox.info'] = $app->share(function () use ($app) {
return $app['conf']->get(['main', 'database'], array()); return $app['conf']->get(['main', 'database'], array());
}); });
// Return database fixture configuration // Return database fixture configuration
$app['db.fixture.info'] = $app->share(function() use ($app) { $app['db.fixture.info'] = $app->share(function () use ($app) {
return [ return [
'driver' => 'pdo_sqlite', 'driver' => 'pdo_sqlite',
'path' => sprintf('%s/%s', $app['tmp.path'], 'db-ref.sqlite'), 'path' => sprintf('%s/%s', $app['tmp.path'], 'db-ref.sqlite'),
@@ -85,7 +85,7 @@ class ORMServiceProvider implements ServiceProviderInterface
}); });
// Return databox database configuration // Return databox database configuration
$app['db.databox.info'] = $app->share(function() use ($app) { $app['db.databox.info'] = $app->share(function () use ($app) {
if (false === $app['phraseanet.configuration']->isSetup()) { if (false === $app['phraseanet.configuration']->isSetup()) {
return array(); return array();
} }
@@ -101,46 +101,46 @@ class ORMServiceProvider implements ServiceProviderInterface
}); });
// Return unique key for fixture database // Return unique key for fixture database
$app['db.fixture.hash.key'] = $app->share(function() use ($app) { $app['db.fixture.hash.key'] = $app->share(function () use ($app) {
$info = $app['db.fixture.info']; $info = $app['db.fixture.info'];
return $app['hash.dsn']($app['db.dsn']($info)); return $app['hash.dsn']($app['db.dsn']($info));
}); });
// Return unique key for test database // Return unique key for test database
$app['db.test.hash.key'] = $app->share(function() use ($app) { $app['db.test.hash.key'] = $app->share(function () use ($app) {
$info = $app['db.test.info']; $info = $app['db.test.info'];
return $app['hash.dsn']($app['db.dsn']($info)); return $app['hash.dsn']($app['db.dsn']($info));
}); });
// Return unique for appbox database // Return unique for appbox database
$app['db.appbox.hash.key'] = $app->share(function() use ($app) { $app['db.appbox.hash.key'] = $app->share(function () use ($app) {
$info = $app['db.appbox.info']; $info = $app['db.appbox.info'];
return $app['hash.dsn']($app['db.dsn']($info)); return $app['hash.dsn']($app['db.dsn']($info));
}); });
// Return configuration option for test database in DoctrineServiceProvider // Return configuration option for test database in DoctrineServiceProvider
$app['db.test.options'] = $app->share(function() use ($app) { $app['db.test.options'] = $app->share(function () use ($app) {
return array($app['db.test.hash.key'] => $app['db.test.info']); return array($app['db.test.hash.key'] => $app['db.test.info']);
}); });
// Return configuration option for test database in DoctrineServiceProvider // Return configuration option for test database in DoctrineServiceProvider
$app['db.fixture.options'] = $app->share(function() use ($app) { $app['db.fixture.options'] = $app->share(function () use ($app) {
return array($app['db.fixture.hash.key'] => $app['db.fixture.info']); return array($app['db.fixture.hash.key'] => $app['db.fixture.info']);
}); });
// Return configuration option for appbox database in DoctrineServiceProvider // Return configuration option for appbox database in DoctrineServiceProvider
$app['db.appbox.options'] = $app->share(function() use ($app) { $app['db.appbox.options'] = $app->share(function () use ($app) {
return array($app['db.appbox.hash.key'] => $app['db.appbox.info']); return array($app['db.appbox.hash.key'] => $app['db.appbox.info']);
}); });
// Return configuration option for databox databases in DoctrineServiceProvider // Return configuration option for databox databases in DoctrineServiceProvider
$app['dbs.databox.options'] = $app->share(function() use ($app) { $app['dbs.databox.options'] = $app->share(function () use ($app) {
$options = array(); $options = array();
foreach($app['db.databox.info'] as $info) { foreach ($app['db.databox.info'] as $info) {
$info = $app['db.info']($info); $info = $app['db.info']($info);
$key = $app['hash.dsn']($app['db.dsn']($info)); $key = $app['hash.dsn']($app['db.dsn']($info));
@@ -153,7 +153,7 @@ class ORMServiceProvider implements ServiceProviderInterface
// Return DoctrineServiceProvider database options, it merges all previous // Return DoctrineServiceProvider database options, it merges all previous
// set database configuration // set database configuration
$app['dbs.options'] = $app->share(function() use ($app) { $app['dbs.options'] = $app->share(function () use ($app) {
if (false === $app['phraseanet.configuration']->isSetup()) { if (false === $app['phraseanet.configuration']->isSetup()) {
return []; return [];
} }
@@ -167,7 +167,7 @@ class ORMServiceProvider implements ServiceProviderInterface
}); });
// Return DoctrineORMServiceProvider information for a database from its parameters // Return DoctrineORMServiceProvider information for a database from its parameters
$app['orm.em.options.from_info'] = $app->protect(function(array $info) use ($app) { $app['orm.em.options.from_info'] = $app->protect(function (array $info) use ($app) {
$info = $app['db.info']($info); $info = $app['db.info']($info);
$key = $app['hash.dsn']($app['db.dsn']($info)); $key = $app['hash.dsn']($app['db.dsn']($info));
@@ -176,7 +176,7 @@ class ORMServiceProvider implements ServiceProviderInterface
}); });
//Return DoctrineServiceProvider information for a database from its parameters //Return DoctrineServiceProvider information for a database from its parameters
$app['db.options.from_info'] = $app->protect(function(array $info) use ($app) { $app['db.options.from_info'] = $app->protect(function (array $info) use ($app) {
$info = $app['db.info']($info); $info = $app['db.info']($info);
$key = $app['hash.dsn']($app['db.dsn']($info)); $key = $app['hash.dsn']($app['db.dsn']($info));
@@ -188,7 +188,7 @@ class ORMServiceProvider implements ServiceProviderInterface
* Add orm on the fly, used only when a new databox is mounted. * Add orm on the fly, used only when a new databox is mounted.
* This allow to use new EM instance right after the database is mounted. * This allow to use new EM instance right after the database is mounted.
*/ */
$app['orm.add'] = $app->protect(function($info) use ($app) { $app['orm.add'] = $app->protect(function ($info) use ($app) {
$info = $app['db.info']($info); $info = $app['db.info']($info);
$key = $app['hash.dsn']($app['db.dsn']($info)); $key = $app['hash.dsn']($app['db.dsn']($info));
@@ -200,7 +200,7 @@ class ORMServiceProvider implements ServiceProviderInterface
$app['dbs.config'][$key] = new Configuration(); $app['dbs.config'][$key] = new Configuration();
$app['dbs'][$key] = $app['dbs']->share(function () use ($app, $info, $key) { $app['dbs'][$key] = $app['dbs']->share(function () use ($app, $info, $key) {
return DriverManager::getConnection($info,$app['dbs.config'][$key] ,$app['dbs.event_manager'][$key]); return DriverManager::getConnection($info, $app['dbs.config'][$key], $app['dbs.event_manager'][$key]);
}); });
$options = $app['orm.options']($key); $options = $app['orm.options']($key);
@@ -230,7 +230,7 @@ class ORMServiceProvider implements ServiceProviderInterface
return new \SplObjectStorage(); return new \SplObjectStorage();
}); });
$app['dbal.evm.register.listeners'] = $app->protect(function(EventManager $evm) use ($app) { $app['dbal.evm.register.listeners'] = $app->protect(function (EventManager $evm) use ($app) {
$evm->addEventSubscriber(new TimestampableListener()); $evm->addEventSubscriber(new TimestampableListener());
/** @var \SplObjectStorage $listeners */ /** @var \SplObjectStorage $listeners */
$listeners = $app['dbal.evm.listeners']; $listeners = $app['dbal.evm.listeners'];
@@ -239,23 +239,17 @@ class ORMServiceProvider implements ServiceProviderInterface
} }
}); });
$app['dbal.config.register.loggers'] = $app->protect(function(Configuration $config) use ($app) { $app['orm.annotation.register'] = $app->protect(function ($key) use($app) {
if ($app->getEnvironment() === PhraseaApplication::ENV_DEV) {
$config->setSQLLogger($app['orm.query.logger']);
}
});
$app['orm.annotation.register'] = $app->protect(function($key) use($app) {
$driver = new AnnotationDriver($app['orm.annotation.reader'], array( $driver = new AnnotationDriver($app['orm.annotation.reader'], array(
$app['root.path'].'/vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Entity/MappedSuperclass', $app['root.path'] . '/vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Entity/MappedSuperclass',
$app['root.path'].'/vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Entity/MappedSuperclass', $app['root.path'] . '/vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Entity/MappedSuperclass',
$app['root.path'].'/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Entity/MappedSuperclass', $app['root.path'] . '/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Entity/MappedSuperclass',
)); ));
$app['orm.add_mapping_driver']($driver, 'Gedmo', $key); $app['orm.add_mapping_driver']($driver, 'Gedmo', $key);
}); });
$app['dbal.type.register'] = $app->protect(function(Connection $connection, $types) { $app['dbal.type.register'] = $app->protect(function (Connection $connection, $types) {
$platform = $connection->getDatabasePlatform(); $platform = $connection->getDatabasePlatform();
foreach (array_keys((array) $types) as $type) { foreach (array_keys((array) $types) as $type) {
@@ -263,7 +257,7 @@ class ORMServiceProvider implements ServiceProviderInterface
} }
}); });
$app['orm.config.new'] = $app->protect(function($key, $options) use($app) { $app['orm.config.new'] = $app->protect(function ($key, $options) use($app) {
$config = new ORMConfig(); $config = new ORMConfig();
$app['orm.cache.configurer']($key, $config, $options); $app['orm.cache.configurer']($key, $config, $options);
@@ -287,7 +281,7 @@ class ORMServiceProvider implements ServiceProviderInterface
$chain = $app['orm.mapping_driver_chain.locator']($key); $chain = $app['orm.mapping_driver_chain.locator']($key);
foreach ((array)$options['mappings'] as $entity) { foreach ((array) $options['mappings'] as $entity) {
if (!is_array($entity)) { if (!is_array($entity)) {
throw new \InvalidArgumentException( throw new \InvalidArgumentException(
"The 'orm.em.options' option 'mappings' should be an array of arrays." "The 'orm.em.options' option 'mappings' should be an array of arrays."
@@ -302,7 +296,8 @@ class ORMServiceProvider implements ServiceProviderInterface
$config->addEntityNamespace($entity['alias'], $entity['namespace']); $config->addEntityNamespace($entity['alias'], $entity['namespace']);
} }
switch ($entity['type']) { switch ($entity['type'])
{
case 'annotation': case 'annotation':
$useSimpleAnnotationReader = $useSimpleAnnotationReader =
isset($entity['use_simple_annotation_reader']) isset($entity['use_simple_annotation_reader'])
@@ -349,7 +344,7 @@ class ORMServiceProvider implements ServiceProviderInterface
return $config; return $config;
}); });
$app['orm.ems.options'] = $app->share(function() use ($app) { $app['orm.ems.options'] = $app->share(function () use ($app) {
if (false === $app['phraseanet.configuration']->isSetup()) { if (false === $app['phraseanet.configuration']->isSetup()) {
return []; return [];
} }
@@ -366,7 +361,7 @@ class ORMServiceProvider implements ServiceProviderInterface
/** /**
* Check database connection information * Check database connection information
*/ */
$app['db.info'] = $app->protect(function(array $info){ $app['db.info'] = $app->protect(function (array $info) {
if (!isset($info['driver'])) { if (!isset($info['driver'])) {
$info['driver'] = 'pdo_mysql'; $info['driver'] = 'pdo_mysql';
} }
@@ -375,7 +370,8 @@ class ORMServiceProvider implements ServiceProviderInterface
$info['charset'] = 'utf8'; $info['charset'] = 'utf8';
} }
switch ($info['driver']) { switch ($info['driver'])
{
case 'pdo_mysql': case 'pdo_mysql':
foreach (array('user', 'password', 'host', 'dbname', 'port') as $param) { foreach (array('user', 'password', 'host', 'dbname', 'port') as $param) {
if (!array_key_exists($param, $info)) { if (!array_key_exists($param, $info)) {
@@ -396,8 +392,8 @@ class ORMServiceProvider implements ServiceProviderInterface
/** /**
* Return configuration option for appbox database in DoctrineORMServiceProvider * Return configuration option for appbox database in DoctrineORMServiceProvider
*/ */
$app['orm.em.appbox.options'] = $app->share(function() use ($app) { $app['orm.em.appbox.options'] = $app->share(function () use ($app) {
$key = $app['db.appbox.hash.key']; $key = $app['db.appbox.hash.key'];
return array($key => $app['orm.options']($key)); return array($key => $app['orm.options']($key));
}); });
@@ -405,8 +401,8 @@ class ORMServiceProvider implements ServiceProviderInterface
/** /**
* Return configuration option for fixture database in DoctrineORMServiceProvider * Return configuration option for fixture database in DoctrineORMServiceProvider
*/ */
$app['orm.em.fixture.options'] = $app->share(function() use ($app) { $app['orm.em.fixture.options'] = $app->share(function () use ($app) {
$key = $app['db.fixture.hash.key']; $key = $app['db.fixture.hash.key'];
return array($key => $app['orm.options']($key)); return array($key => $app['orm.options']($key));
}); });
@@ -414,8 +410,8 @@ class ORMServiceProvider implements ServiceProviderInterface
/** /**
* Return configuration option for test database in DoctrineORMServiceProvider * Return configuration option for test database in DoctrineORMServiceProvider
*/ */
$app['orm.em.test.options'] = $app->share(function() use ($app) { $app['orm.em.test.options'] = $app->share(function () use ($app) {
$key = $app['db.test.hash.key']; $key = $app['db.test.hash.key'];
return array($key => $app['orm.options']($key)); return array($key => $app['orm.options']($key));
}); });
@@ -423,7 +419,7 @@ class ORMServiceProvider implements ServiceProviderInterface
/** /**
* Return configuration option for databox databases in DoctrineORMServiceProvider * Return configuration option for databox databases in DoctrineORMServiceProvider
*/ */
$app['orm.ems.databox.options'] = $app->share(function() use ($app) { $app['orm.ems.databox.options'] = $app->share(function () use ($app) {
$options = array(); $options = array();
foreach ($app['db.databox.info'] as $base) { foreach ($app['db.databox.info'] as $base) {
@@ -444,13 +440,13 @@ class ORMServiceProvider implements ServiceProviderInterface
"alias" => "Phraseanet", "alias" => "Phraseanet",
"use_simple_annotation_reader" => false, "use_simple_annotation_reader" => false,
"namespace" => 'Alchemy\Phrasea\Model\Entities', "namespace" => 'Alchemy\Phrasea\Model\Entities',
"path" => $app['root.path'].'/lib/Alchemy/Phrasea/Model/Entities', "path" => $app['root.path'] . '/lib/Alchemy/Phrasea/Model/Entities',
) )
); );
}); });
// Return orm configuration for a connection given its unique id // Return orm configuration for a connection given its unique id
$app['orm.options'] = $app->protect(function($connection) use ($app) { $app['orm.options'] = $app->protect(function ($connection) use ($app) {
return array( return array(
"connection" => $connection, "connection" => $connection,
"mappings" => $app['orm.options.mappings'], "mappings" => $app['orm.options.mappings'],
@@ -469,7 +465,7 @@ class ORMServiceProvider implements ServiceProviderInterface
* Path to doctrine log file * Path to doctrine log file
*/ */
$app['orm.monolog.handler.file'] = $app->share(function (Application $app) { $app['orm.monolog.handler.file'] = $app->share(function (Application $app) {
return $app['log.path'].'/doctrine.log'; return $app['log.path'] . '/doctrine.log';
}); });
/** /**
@@ -480,7 +476,7 @@ class ORMServiceProvider implements ServiceProviderInterface
/** /**
* Monolog handler for doctrine * Monolog handler for doctrine
*/ */
$app['orm.monolog.handler'] = $app->share(function(Application $app) { $app['orm.monolog.handler'] = $app->share(function (Application $app) {
return new RotatingFileHandler($app['orm.monolog.handler.file'], $app['orm.monolog.handler.file.max-files']); return new RotatingFileHandler($app['orm.monolog.handler.file'], $app['orm.monolog.handler.file.max-files']);
}); });
@@ -495,13 +491,6 @@ class ORMServiceProvider implements ServiceProviderInterface
return $logger; return $logger;
}); });
/**
* Doctrine query logger
*/
$app['orm.query.logger'] = $app->share(function () {
return new DebugStack();
});
/** /**
* Return cache driver * Return cache driver
*/ */
@@ -548,7 +537,7 @@ class ORMServiceProvider implements ServiceProviderInterface
return $manager->get($info); return $manager->get($info);
}); });
$app['connection.pool.manager'] = $app->share(function() { $app['connection.pool.manager'] = $app->share(function () {
return new ConnectionPoolManager(); return new ConnectionPoolManager();
}); });
@@ -563,7 +552,7 @@ class ORMServiceProvider implements ServiceProviderInterface
/** /**
* Return an instance of annotation cache reader * Return an instance of annotation cache reader
*/ */
$app['orm.annotation.reader'] = $app->share(function() use ($app) { $app['orm.annotation.reader'] = $app->share(function () use ($app) {
$cache = new ArrayCache(); $cache = new ArrayCache();
if ($app->getEnvironment() !== PhraseaApplication::ENV_DEV) { if ($app->getEnvironment() !== PhraseaApplication::ENV_DEV) {
$cache = $app['phraseanet.cache-service']->factory( $cache = $app['phraseanet.cache-service']->factory(

View File

@@ -1,5 +1,5 @@
<?php <?php
/** /*
* This file is part of Phraseanet * This file is part of Phraseanet
* *
* (c) 2005-2016 Alchemy * (c) 2005-2016 Alchemy

View File

@@ -1,9 +1,8 @@
<?php <?php
/* /*
* This file is part of Phraseanet * This file is part of Phraseanet
* *
* (c) 2005-2014 Alchemy * (c) 2005-2016 Alchemy
* *
* For the full copyright and license information, please view the LICENSE * For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code. * file that was distributed with this source code.
@@ -11,6 +10,7 @@
namespace Alchemy\Phrasea\Core\Provider; namespace Alchemy\Phrasea\Core\Provider;
use Alchemy\Phrasea\Controller\LazyLocator;
use Alchemy\Phrasea\Model\Serializer\CaptionSerializer; use Alchemy\Phrasea\Model\Serializer\CaptionSerializer;
use Alchemy\Phrasea\Model\Serializer\ESRecordSerializer; use Alchemy\Phrasea\Model\Serializer\ESRecordSerializer;
use Silex\Application; use Silex\Application;
@@ -21,15 +21,16 @@ class SerializerServiceProvider implements ServiceProviderInterface
public function register(Application $app) public function register(Application $app)
{ {
$app['serializer.caption'] = $app->share(function (Application $app) { $app['serializer.caption'] = $app->share(function (Application $app) {
return new CaptionSerializer(); return new CaptionSerializer(new LazyLocator($app, 'service.technical_data'));
}); });
$app['serializer.es-record'] = $app->share(function (Application $app) { $app['serializer.es-record'] = $app->share(function () {
return new ESRecordSerializer(); return new ESRecordSerializer();
}); });
} }
public function boot(Application $app) public function boot(Application $app)
{ {
// No-op
} }
} }

View File

@@ -2,11 +2,13 @@
namespace Alchemy\Phrasea\Core\Provider; namespace Alchemy\Phrasea\Core\Provider;
use Alchemy\Phrasea\Controller\LazyLocator;
use Alchemy\Phrasea\Core\Event\Subscriber\CacheStatisticsSubscriber; use Alchemy\Phrasea\Core\Event\Subscriber\CacheStatisticsSubscriber;
use Alchemy\Phrasea\Core\Profiler\CacheDataCollector; use Alchemy\Phrasea\Core\Profiler\CacheDataCollector;
use Alchemy\Phrasea\Core\Profiler\TraceableCache; use Alchemy\Phrasea\Core\Profiler\TraceableCache;
use Alchemy\Phrasea\Utilities\LazyArrayAccess;
use Doctrine\Common\Cache\Cache; use Doctrine\Common\Cache\Cache;
use Doctrine\DBAL\Connection; use Doctrine\DBAL\Configuration;
use Doctrine\DBAL\Logging\DebugStack; use Doctrine\DBAL\Logging\DebugStack;
use Doctrine\DBAL\Logging\LoggerChain; use Doctrine\DBAL\Logging\LoggerChain;
use Silex\Application; use Silex\Application;
@@ -47,19 +49,33 @@ class WebProfilerServiceProvider implements ServiceProviderInterface
return $templates; return $templates;
})); }));
$app['data_collectors'] = $app->share($app->extend('data_collectors', function ($collectors) use ($app) { $app['data_collectors'] = $app->share($app->extend('data_collectors', function (array $collectors, $app) {
$collectors['ajax'] = function () { $collectors['ajax'] = $app->share(function () {
return new AjaxDataCollector(); return new AjaxDataCollector();
}; });
return $collectors; return $collectors;
})); }));
} }
$app['dbal.config.register.loggers'] = $app->protect(function (Configuration $config, $name) use ($app) {
$debugLogger = new DebugStack();
$loggerChain = new LoggerChain();
$loggerChain->addLogger($debugLogger);
$loggerChain->addLogger($app['data_collectors.doctrine.logger']);
$app['data_collectors.doctrine']->addLogger($name, $debugLogger);
$config->setSQLLogger($loggerChain);
});
$app['data_collectors.doctrine.logger'] = $app->share(function ($app) {
return new DbalLogger($app['logger'], $app['stopwatch']);
});
$app['data_collectors.doctrine'] = $app->share(function ($app) { $app['data_collectors.doctrine'] = $app->share(function ($app) {
/** @var Connection $db */ return new DoctrineDataCollector(new LazyArrayAccess(new LazyLocator($app, 'dbs')));
$db = $app['db'];
return new DoctrineDataCollector($db);
}); });
$app['cache'] = $app->share($app->extend('cache', function (Cache $cache, $app) { $app['cache'] = $app->share($app->extend('cache', function (Cache $cache, $app) {
@@ -75,24 +91,9 @@ class WebProfilerServiceProvider implements ServiceProviderInterface
return new CacheStatisticsSubscriber($app['cache']); return new CacheStatisticsSubscriber($app['cache']);
}); });
$app['data_collectors'] = $app->share($app->extend('data_collectors', function ($collectors) use ($app) { $app['data_collectors'] = $app->share($app->extend('data_collectors', function (array $collectors, $app) {
$collectors['db'] = $app->share(function ($app) { $collectors['db'] = $app->share(function ($app) {
/** @var DoctrineDataCollector $collector */ return $app['data_collectors.doctrine'];
$collector = $app['data_collectors.doctrine'];
$loggerChain = new LoggerChain();
$logger = new DebugStack();
$loggerChain->addLogger($logger);
$loggerChain->addLogger(new DbalLogger($app['logger'], $app['stopwatch']));
/** @var Connection $db */
$db = $app['db'];
$db->getConfiguration()->setSQLLogger($loggerChain);
$collector->addLogger($logger);
return $collector;
}); });
$collectors['cache'] = $app->share(function ($app) { $collectors['cache'] = $app->share(function ($app) {

View 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\Databox;
interface DataboxBoundRepositoryFactory
{
/**
* Creates a Repository bounded to given Databox
*
* @param $databoxId
* @return object
*/
public function createRepositoryFor($databoxId);
}

View 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\Databox;
class DataboxBoundRepositoryProvider
{
/**
* Repositories indexed by databox id
*
* @var object[]
*/
private $repositories = [];
/**
* @var DataboxBoundRepositoryFactory
*/
private $factory;
public function __construct(DataboxBoundRepositoryFactory $factory)
{
$this->factory = $factory;
}
/**
* @param int $databoxId
* @return object
*/
public function getRepositoryForDatabox($databoxId)
{
if (!isset($this->repositories[$databoxId])) {
$this->repositories[$databoxId] = $this->factory->createRepositoryFor($databoxId);
}
return $this->repositories[$databoxId];
}
}

View File

@@ -1,10 +1,20 @@
<?php <?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; namespace Alchemy\Phrasea\Databox;
class DataboxConnectionProvider class DataboxConnectionProvider
{ {
/**
* @var \appbox
*/
private $applicationBox; private $applicationBox;
public function __construct(\appbox $applicationBox) public function __construct(\appbox $applicationBox)
@@ -13,7 +23,7 @@ class DataboxConnectionProvider
} }
/** /**
* @param $databoxId * @param int $databoxId
* @return \Doctrine\DBAL\Connection * @return \Doctrine\DBAL\Connection
*/ */
public function getConnection($databoxId) public function getConnection($databoxId)

View File

@@ -125,7 +125,8 @@ class LegacyRecordRepository implements RecordRepository
$connection->quoteIdentifier('type'), $connection->quoteIdentifier('type'),
'originalname AS originalName', 'originalname AS originalName',
'sha256', 'sha256',
'mime' 'mime',
'LPAD(BIN(status), 32, \'0\') as status'
) )
->from('record', 'r'); ->from('record', 'r');
} }

View File

@@ -0,0 +1,244 @@
<?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\Subdef;
use Alchemy\Phrasea\Cache\MultiAdapter;
use Doctrine\Common\Cache\Cache;
use Doctrine\Common\Cache\MultiGetCache;
use Doctrine\Common\Cache\MultiPutCache;
class CachedMediaSubdefDataRepository implements MediaSubdefDataRepository
{
/**
* @var MediaSubdefDataRepository
*/
private $decorated;
/**
* @var Cache|MultiGetCache|MultiPutCache
*/
private $cache;
/**
* @var string
*/
private $baseKey;
/**
* @var int
*/
private $lifeTime = 0;
/**
* @param MediaSubdefDataRepository $decorated
* @param Cache $cache
* @param string $baseKey
*/
public function __construct(MediaSubdefDataRepository $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 int[] $recordIds
* @param string[]|null $names
* @return array
*/
public function findByRecordIdsAndNames(array $recordIds, array $names = null)
{
$keys = $this->computeKeys($recordIds, $names);
if ($keys) {
$data = $this->cache->fetchMultiple($keys);
if (count($keys) === count($data)) {
return $this->filterNonNull($data);
}
}
return $this->fetchAndSave($recordIds, $names, $keys);
}
/**
* @param array $data
* @return array
*/
private function filterNonNull(array $data)
{
return array_values(array_filter($data, function ($value) {
return null !== $value;
}));
}
public function delete(array $subdefIds)
{
$deleted = $this->decorated->delete($subdefIds);
$keys = array_map([$this, 'dataToKey'], $subdefIds);
$this->cache->saveMultiple(array_fill_keys($keys, null), $this->lifeTime);
return $deleted;
}
public function save(array $data)
{
$this->decorated->save($data);
// all saved keys are now stalled. decorated repository could modify values on store (update time for example)
$recordIds = [];
foreach ($data as $item) {
$recordIds[] = $item['record_id'];
}
$keys = array_merge(array_map([$this, 'dataToKey'], $data), $this->generateAllCacheKeys($recordIds));
array_walk($keys, [$this->cache, 'delete']);
}
/**
* @param array $data
* @return string
*/
private function dataToKey(array $data)
{
return $this->getCacheKey($data['record_id'], $data['name']);
}
/**
* @param int $recordId
* @param string|null $name
* @return string
*/
private function getCacheKey($recordId, $name = null)
{
return $this->baseKey . 'media_subdef' . json_encode([(int)$recordId, $name]);
}
/**
* @param int[] $recordIds
* @param string[] $names
* @return string[]
*/
private function generateCacheKeys(array $recordIds, array $names)
{
$namesCount = count($names);
$keys = array_map(function ($recordId) use ($namesCount, $names) {
return array_map([$this, 'getCacheKey'], array_fill(0, $namesCount, $recordId), $names);
}, $recordIds);
return $keys ? call_user_func_array('array_merge', $keys) : [];
}
/**
* @param int[] $recordIds
* @return string[]
*/
private function generateAllCacheKeys(array $recordIds)
{
$recordIds = array_unique($recordIds);
return array_map([$this, 'getCacheKey'], $recordIds, array_fill(0, count($recordIds), null));
}
/**
* @param int[] $recordIds
* @param string[]|null $names
* @param string[] $keys Known keys supposed to be fetched
* @return array
*/
private function fetchAndSave(array $recordIds, array $names = null, array $keys = [])
{
$retrieved = $this->decorated->findByRecordIdsAndNames($recordIds, $names);
$data = $this->normalizeRetrievedData($retrieved, $keys);
$toCache = null === $names ? $this->appendCacheExtraData($data, $retrieved, $recordIds) : $data;
$this->cache->saveMultiple($toCache, $this->lifeTime);
return $this->filterNonNull($data);
}
/**
* @param int[] $recordIds
* @param string[]|null $names
* @return string[]
*/
private function computeKeys(array $recordIds, array $names = null)
{
if (!$recordIds) {
return [];
} elseif (null !== $names) {
return $this->generateCacheKeys($recordIds, $names);
}
$keys = $this->generateAllCacheKeys($recordIds);
$data = $this->cache->fetchMultiple($keys);
return count($keys) === count($data) ? call_user_func_array('array_merge', $data) : [];
}
/**
* @param array $retrieved
* @param array $keys
* @return array
*/
private function normalizeRetrievedData(array $retrieved, array $keys)
{
$data = array_fill_keys($keys, null);
foreach ($retrieved as $item) {
$data[$this->dataToKey($item)] = $item;
}
return $data;
}
/**
* @param array $data
* @param array $retrieved
* @param array $recordIds
* @return array
*/
private function appendCacheExtraData(array $data, array $retrieved, array $recordIds)
{
$extra = array_fill_keys($this->generateAllCacheKeys($recordIds), []);
foreach ($retrieved as $item) {
$extra[$this->getCacheKey($item['record_id'])][] = $this->getCacheKey($item['record_id'], $item['name']);
}
return array_merge($data, $extra);
}
}

View File

@@ -0,0 +1,280 @@
<?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\Subdef;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\Exception\DriverException;
class DbalMediaSubdefDataRepository implements MediaSubdefDataRepository
{
/**
* @var Connection
*/
private $connection;
public function __construct(Connection $connection)
{
$this->connection = $connection;
}
/**
* @param int[] $recordIds
* @param string[]|null $names
* @return array[]
*/
public function findByRecordIdsAndNames(array $recordIds, array $names = null)
{
if (!$recordIds || (null !== $names && !$names)) {
return [];
}
$sql = $this->getSelectSQL() . ' WHERE record_id IN (:recordIds)';
$params = ['recordIds' => $recordIds];
$types = ['recordIds' => Connection::PARAM_INT_ARRAY];
if ($names) {
$sql .= ' AND name IN (:names)';
$params['names'] = $names;
$types['names'] = Connection::PARAM_STR_ARRAY;
}
return array_map([$this, 'sqlToPhp'], $this->connection->fetchAll($sql, $params, $types));
}
/**
* @param array[] $subdefIds
* @return int The number of affected rows
* @throws \Exception
*/
public function delete(array $subdefIds)
{
if (!$subdefIds) {
return 0;
}
$statement = $this->connection->prepare('DELETE FROM subdef WHERE record_id = :record_id AND name = :name');
$this->connection->beginTransaction();
try {
$deleted = array_reduce($subdefIds, function ($carry, $data) use ($statement) {
$carry += $statement->execute([
'record_id' => $data['record_id'],
'name' => $data['name'],
]);
return $carry;
}, 0);
$this->connection->commit();
} catch (\Exception $exception) {
$this->connection->rollBack();
throw $exception;
}
return $deleted;
}
/**
* @param array $data
* @throws \Exception
*/
public function save(array $data)
{
$this->connection->transactional(function () use ($data) {
$partitions = $this->partitionInsertAndUpdate($data);
$updateNeeded = $this->createMissing($partitions['insert']);
$this->updatePresent($partitions['update'] + $updateNeeded);
});
}
/**
* @param array $data
* @return array
*/
private function partitionInsertAndUpdate(array $data)
{
$partitions = [
'insert' => [],
'update' => [],
];
foreach ($data as $index => $item) {
$partitions[isset($item['subdef_id']) ? 'update' : 'insert'][$index] = $item;
}
return $partitions;
}
/**
* @param array $toInsert
* @return array
* @throws DBALException
*/
private function createMissing(array $toInsert)
{
if (!$toInsert) {
return [];
}
$statement = $this->connection->prepare($this->getInsertSql());
$updateNeeded = [];
foreach ($toInsert as $index => $data) {
try {
$statement->execute($this->phpToSql($data));
} catch (DriverException $exception) {
$updateNeeded[$index] = $data;
}
}
return $updateNeeded;
}
/**
* @param array $toUpdate
* @throws DBALException
*/
private function updatePresent(array $toUpdate)
{
if (!$toUpdate) {
return;
}
$statement = $this->connection->prepare($this->getUpdateSql());
foreach ($toUpdate as $data) {
$statement->execute($this->phpToSql($data));
}
}
private function getSelectSQL()
{
return <<<'SQL'
SELECT subdef_id, record_id, name, path, file, width, height, mime, size, substit, etag, created_on, updated_on
FROM subdef
SQL;
}
/**
* @param array $data
* @return array
*/
private function phpToSql(array $data)
{
return [
'record_id' => $data['record_id'],
'name' => $data['name'],
'path' => $data['path'],
'file' => $data['file'],
'width' => $data['width'],
'height' => $data['height'],
'mime' => $data['mime'],
'size' => $data['size'],
'substit' => $data['is_substituted'],
'etag' => $data['etag'],
];
}
private function sqlToPhp(array $data)
{
return [
'subdef_id' => (int)$data['subdef_id'],
'record_id' => (int)$data['record_id'],
'name' => $data['name'],
'path' => $data['path'],
'file' => $data['file'],
'width' => (int)$data['width'],
'height' => (int)$data['height'],
'mime' => $data['mime'],
'size' => (int)$data['size'],
'is_substituted' => (bool)$data['substit'],
'etag' => $data['etag'],
'created_on' => $data['created_on'],
'updated_on' => $data['updated_on'],
'physically_present' => true,
];
}
/**
* @return string
*/
private function getInsertSql()
{
static $sql;
if (null !== $sql) {
return $sql;
}
$values = [
'record_id' => ':record_id',
'name' => ':name',
'path' => ':path',
'file' => ':file',
'width' => ':width',
'height' => ':height',
'mime' => ':mime',
'size' => ':size',
'substit' => ':substit',
'etag' => ':etag',
'created_on' => 'NOW()',
'updated_on' => 'NOW()',
'dispatched' => '1',
];
$sql = sprintf(
'INSERT INTO subdef (%s) VALUES (%s)',
implode(', ', array_keys($values)),
implode(', ', array_values($values))
);
return $sql;
}
/**
* @return string
*/
private function getUpdateSql()
{
static $sql;
if (null !== $sql) {
return $sql;
}
$values = [
'path = :path',
'file = :file',
'width = :width',
'height = :height',
'mime = :mime',
'size = :size',
'substit = :substit',
'etag = :etag',
'updated_on = NOW()',
];
$where = [
'record_id = :record_id',
'name = :name',
];
$sql = sprintf('UPDATE subdef SET %s WHERE %s', implode(', ', $values), implode(' AND ', $where));
return $sql;
}
}

View File

@@ -0,0 +1,33 @@
<?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\Subdef;
interface MediaSubdefDataRepository
{
/**
* @param int[] $recordIds
* @param string[]|null $names null means to look for all available names
* @return array[]
*/
public function findByRecordIdsAndNames(array $recordIds, array $names = null);
/**
* @param array[] $subdefIds (should be a list of associative arrays with record_id and name keys)
* @return int The number of affected rows
*/
public function delete(array $subdefIds);
/**
* @param array $data
* @return void
*/
public function save(array $data);
}

View File

@@ -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\Databox\Subdef;
use Alchemy\Phrasea\Hydration\Hydrator;
use Assert\Assertion;
class MediaSubdefHydrator implements Hydrator
{
/**
* @param \media_subdef $instance
* @param array $data
* @throws \Assert\AssertionFailedException
*/
public function hydrate($instance, array $data)
{
Assertion::isInstanceOf($instance, \media_subdef::class);
$closure = \Closure::bind(function (array $data) {
$this->loadFromArray($data);
}, $instance, \media_subdef::class);
$closure($data);
}
/**
* @param \media_subdef $instance
* @return array
* @throws \Assert\AssertionFailedException
*/
public function extract($instance)
{
Assertion::isInstanceOf($instance, \media_subdef::class);
$closure = \Closure::bind(function () {
return $this->toArray();
}, $instance, \media_subdef::class);
return $closure();
}
}

View 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\Databox\Subdef;
use Alchemy\Phrasea\Hydration\Hydrator;
use Assert\Assertion;
class MediaSubdefRepository
{
/**
* @var MediaSubdefDataRepository
*/
private $repository;
/**
* @var \media_subdef[]
*/
private $idMap = [];
/**
* @var callable
*/
private $subdefFactory;
/**
* @var Hydrator
*/
private $hydrator;
public function __construct(MediaSubdefDataRepository $repository, callable $subdefFactory, Hydrator $hydrator = null)
{
$this->repository = $repository;
$this->subdefFactory = $subdefFactory;
$this->hydrator = $hydrator ?: new MediaSubdefHydrator();
}
/**
* @param int $recordId
* @param string $name
* @return \media_subdef|null
*/
public function findOneByRecordIdAndName($recordId, $name)
{
$subdefs = $this->repository->findByRecordIdsAndNames([$recordId], [$name]);
if (!$subdefs) {
return null;
}
$instances = $this->hydrateAll($subdefs);
return reset($instances);
}
/**
* @param int[] $recordIds
* @param string[] $names
* @return \media_subdef[]
*/
public function findByRecordIdsAndNames(array $recordIds, array $names = null)
{
if (!$recordIds) {
return [];
}
$data = $this->repository->findByRecordIdsAndNames($recordIds, $names);
return $this->hydrateAll($data);
}
/**
* @param \media_subdef|\media_subdef[] $subdefs
*/
public function save($subdefs)
{
if (!is_array($subdefs) || !$subdefs instanceof \Traversable) {
$subdefs = [$subdefs];
} elseif ($subdefs instanceof \Traversable) {
$subdefs = iterator_to_array($subdefs);
}
Assertion::allIsInstanceOf($subdefs, \media_subdef::class);
$data = array_map([$this->hydrator, 'extract'], $subdefs);
$this->repository->save($data);
}
public function clear()
{
$this->idMap = [];
}
/**
* @param string $index
* @param array $data
* @return \media_subdef
*/
private function hydrate($index, array $data)
{
if (isset($this->idMap[$index])) {
$this->hydrator->hydrate($this->idMap[$index], $data);
return $this->idMap[$index];
}
$factory = $this->subdefFactory;
$instance = $factory($data);
Assertion::isInstanceOf($instance, \media_subdef::class);
$this->idMap[$index] = $instance;
return $instance;
}
/**
* @param array $data
* @return \media_subdef[]
*/
private function hydrateAll(array $data)
{
$instances = [];
foreach ($data as $item) {
$instances[] = $this->hydrate(json_encode([$item['record_id'], $item['name']]), $item);
}
return $instances;
}
}

View File

@@ -0,0 +1,60 @@
<?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\Subdef;
use Alchemy\Phrasea\Databox\DataboxBoundRepositoryFactory;
use Alchemy\Phrasea\Databox\DataboxConnectionProvider;
use Doctrine\Common\Cache\Cache;
class MediaSubdefRepositoryFactory implements DataboxBoundRepositoryFactory
{
/**
* @var DataboxConnectionProvider
*/
private $connectionProvider;
/**
* @var Cache
*/
private $cache;
/**
* @var callable
*/
private $mediaSubdefFactoryProvider;
public function __construct(DataboxConnectionProvider $connectionProvider, Cache $cache, callable $mediaSubdefFactoryProvider)
{
$this->connectionProvider = $connectionProvider;
$this->cache = $cache;
$this->mediaSubdefFactoryProvider = $mediaSubdefFactoryProvider;
}
public function createRepositoryFor($databoxId)
{
$connection = $this->connectionProvider->getConnection($databoxId);
$dbalRepository = new DbalMediaSubdefDataRepository($connection);
$dataRepository = new CachedMediaSubdefDataRepository($dbalRepository, $this->cache, sprintf('databox%d:', $databoxId));
$provider = $this->mediaSubdefFactoryProvider;
$factory = $provider($databoxId);
if (!is_callable($factory)) {
throw new \UnexpectedValueException(sprintf(
'Media subdef factory is expected to be callable, got %s',
is_object($factory) ? get_class($factory) : gettype($factory)
));
}
return new MediaSubdefRepository($dataRepository, $factory);
}
}

View File

@@ -0,0 +1,120 @@
<?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\Subdef;
use Alchemy\Phrasea\Databox\DataboxBoundRepositoryProvider;
use Alchemy\Phrasea\Model\RecordReferenceInterface;
use Alchemy\Phrasea\Record\RecordReferenceCollection;
class MediaSubdefService
{
/**
* @var DataboxBoundRepositoryProvider
*/
private $repositoryProvider;
public function __construct(DataboxBoundRepositoryProvider $repositoryProvider)
{
$this->repositoryProvider = $repositoryProvider;
}
/**
* Returns all available subdefs grouped by each record reference and by its name
*
* @param RecordReferenceInterface[]|RecordReferenceCollection $records
* @return \media_subdef[][]
*/
public function findSubdefsByRecordReferenceFromCollection($records)
{
$subdefs = $this->reduceRecordReferenceCollection(
$records,
function (array &$carry, array $subdefs, array $indexes) {
/** @var \media_subdef $subdef */
foreach ($subdefs as $subdef) {
$index = $indexes[$subdef->get_record_id()];
$carry[$index][$subdef->get_name()] = $subdef;
}
},
array_fill_keys(array_keys(iterator_to_array($records)), [])
);
ksort($subdefs);
return $subdefs;
}
/**
* @param RecordReferenceInterface[]|RecordReferenceCollection $records
* @return \media_subdef[]
*/
public function findSubdefsFromRecordReferenceCollection($records)
{
$groups = $this->reduceRecordReferenceCollection(
$records,
function (array &$carry, array $subdefs) {
$carry[] = $subdefs;
return $carry;
},
[]
);
if ($groups) {
return call_user_func_array('array_merge', $groups);
}
return [];
}
/**
* @param RecordReferenceInterface[]|RecordReferenceCollection $records
* @param callable $process
* @param mixed $initialValue
* @return mixed
*/
private function reduceRecordReferenceCollection($records, callable $process, $initialValue)
{
$records = $this->normalizeRecordCollection($records);
$carry = $initialValue;
foreach ($records->groupPerDataboxId() as $databoxId => $indexes) {
$subdefs = $this->getRepositoryForDatabox($databoxId)->findByRecordIdsAndNames(array_keys($indexes));
$carry = $process($carry, $subdefs, $indexes, $databoxId);
}
return $carry;
}
/**
* @param int $databoxId
* @return MediaSubdefRepository
*/
private function getRepositoryForDatabox($databoxId)
{
return $this->repositoryProvider->getRepositoryForDatabox($databoxId);
}
/**
* @param RecordReferenceInterface[]|RecordReferenceCollection $records
* @return RecordReferenceCollection
*/
private function normalizeRecordCollection($records)
{
if ($records instanceof RecordReferenceCollection) {
return $records;
}
return new RecordReferenceCollection($records);
}
}

View File

@@ -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\Databox\Subdef;
use Alchemy\Phrasea\Databox\DataboxBoundRepositoryProvider;
use Alchemy\Phrasea\Databox\DataboxConnectionProvider;
use Alchemy\Phrasea\Record\RecordReference;
use Silex\Application;
use Silex\ServiceProviderInterface;
class MediaSubdefServiceProvider implements ServiceProviderInterface
{
public function register(Application $app)
{
$app['provider.factory.media_subdef'] = $app->protect(function ($databoxId) use ($app) {
return function (array $data) use ($app, $databoxId) {
$recordReference = RecordReference::createFromDataboxIdAndRecordId($databoxId, $data['record_id']);
return new \media_subdef($app, $recordReference, $data['name'], false, $data);
};
});
$app['provider.repo.media_subdef'] = $app->share(function (Application $app) {
$connectionProvider = new DataboxConnectionProvider($app['phraseanet.appbox']);
$factoryProvider = $app['provider.factory.media_subdef'];
$repositoryFactory = new MediaSubdefRepositoryFactory($connectionProvider, $app['cache'], $factoryProvider);
return new DataboxBoundRepositoryProvider($repositoryFactory);
});
$app['service.media_subdef'] = $app->share(function (Application $app) {
return new MediaSubdefService($app['provider.repo.media_subdef']);
});
}
public function boot(Application $app)
{
// no-op
}
}

View File

@@ -17,6 +17,7 @@ use Alchemy\Phrasea\Feed\FeedInterface;
use Alchemy\Phrasea\Feed\Link\FeedLink; use Alchemy\Phrasea\Feed\Link\FeedLink;
use Alchemy\Phrasea\Feed\Link\LinkGeneratorCollection; use Alchemy\Phrasea\Feed\Link\LinkGeneratorCollection;
use Alchemy\Phrasea\Model\Entities\User; use Alchemy\Phrasea\Model\Entities\User;
use Alchemy\Phrasea\Utilities\NullableDateTime;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
class AtomFormatter extends FeedFormatterAbstract implements FeedFormatterInterface class AtomFormatter extends FeedFormatterAbstract implements FeedFormatterInterface
@@ -48,8 +49,6 @@ class AtomFormatter extends FeedFormatterAbstract implements FeedFormatterInterf
*/ */
public function format(FeedInterface $feed, $page, User $user = null, $generator = 'Phraseanet', Application $app = null) public function format(FeedInterface $feed, $page, User $user = null, $generator = 'Phraseanet', Application $app = null)
{ {
$updated_on = $feed->getUpdatedOn();
$document = new \DOMDocument('1.0', 'UTF-8'); $document = new \DOMDocument('1.0', 'UTF-8');
$document->formatOutput = true; $document->formatOutput = true;
$document->standalone = true; $document->standalone = true;
@@ -59,8 +58,7 @@ class AtomFormatter extends FeedFormatterAbstract implements FeedFormatterInterf
$root->setAttribute('xmlns:media', 'http://search.yahoo.com/mrss/'); $root->setAttribute('xmlns:media', 'http://search.yahoo.com/mrss/');
$this->addTag($document, $root, 'title', $feed->getTitle()); $this->addTag($document, $root, 'title', $feed->getTitle());
if ($updated_on instanceof \DateTime) { if (null !== $updated_on = NullableDateTime::format($feed->getUpdatedOn())) {
$updated_on = $updated_on->format(DATE_ATOM);
$this->addTag($document, $root, 'updated', $updated_on); $this->addTag($document, $root, 'updated', $updated_on);
} }

View File

@@ -19,6 +19,7 @@ use Alchemy\Phrasea\Model\Entities\FeedEntry;
use Alchemy\Phrasea\Model\Entities\FeedItem; use Alchemy\Phrasea\Model\Entities\FeedItem;
use Alchemy\Phrasea\Feed\Link\LinkGeneratorCollection; use Alchemy\Phrasea\Feed\Link\LinkGeneratorCollection;
use Alchemy\Phrasea\Model\Entities\User; use Alchemy\Phrasea\Model\Entities\User;
use Alchemy\Phrasea\Utilities\NullableDateTime;
use DateTime; use DateTime;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
@@ -52,8 +53,6 @@ class CoolirisFormatter extends FeedFormatterAbstract implements FeedFormatterIn
*/ */
public function format(FeedInterface $feed, $page, User $user = null, $generator = 'Phraseanet', Application $app = null) public function format(FeedInterface $feed, $page, User $user = null, $generator = 'Phraseanet', Application $app = null)
{ {
$updated_on = $feed->getUpdatedOn();
$doc = new \DOMDocument('1.0', 'UTF-8'); $doc = new \DOMDocument('1.0', 'UTF-8');
$doc->formatOutput = true; $doc->formatOutput = true;
$doc->standalone = true; $doc->standalone = true;
@@ -89,8 +88,7 @@ class CoolirisFormatter extends FeedFormatterAbstract implements FeedFormatterIn
$this->addTag($doc, $channel, 'managingEditor', $this->managingEditor); $this->addTag($doc, $channel, 'managingEditor', $this->managingEditor);
if (isset($this->webMaster)) if (isset($this->webMaster))
$this->addTag($doc, $channel, 'webMaster', $this->webMaster); $this->addTag($doc, $channel, 'webMaster', $this->webMaster);
if ($updated_on instanceof DateTime) { if (null !== $updated_on = NullableDateTime::format($feed->getUpdatedOn(), DATE_RFC2822)) {
$updated_on = $updated_on->format(DATE_RFC2822);
$this->addTag($doc, $channel, 'pubDate', $updated_on); $this->addTag($doc, $channel, 'pubDate', $updated_on);
} }
if (isset($this->lastBuildDate) && $this->lastBuildDate instanceof DateTime) { if (isset($this->lastBuildDate) && $this->lastBuildDate instanceof DateTime) {
@@ -191,7 +189,7 @@ class CoolirisFormatter extends FeedFormatterAbstract implements FeedFormatterIn
$thumbnail_sd = $content->getRecord($app)->get_thumbnail(); $thumbnail_sd = $content->getRecord($app)->get_thumbnail();
$thumbnail_permalink = $thumbnail_sd->get_permalink(); $thumbnail_permalink = $thumbnail_sd->get_permalink();
$medium = strtolower($content->getRecord($app)->get_type()); $medium = strtolower($content->getRecord($app)->getType());
if ( ! in_array($medium, ['image', 'audio', 'video'])) { if ( ! in_array($medium, ['image', 'audio', 'video'])) {
return $this; return $this;

View File

@@ -52,7 +52,7 @@ abstract class FeedFormatterAbstract
$thumbnail_sd = $content->getRecord($app)->get_thumbnail(); $thumbnail_sd = $content->getRecord($app)->get_thumbnail();
$thumbnail_permalink = $thumbnail_sd->get_permalink(); $thumbnail_permalink = $thumbnail_sd->get_permalink();
$medium = strtolower($content->getRecord($app)->get_type()); $medium = strtolower($content->getRecord($app)->getType());
if ( ! in_array($medium, ['image', 'audio', 'video'])) { if ( ! in_array($medium, ['image', 'audio', 'video'])) {
return $this; return $this;

View File

@@ -17,6 +17,7 @@ use Alchemy\Phrasea\Feed\Link\FeedLink;
use Alchemy\Phrasea\Feed\Link\LinkGeneratorCollection; use Alchemy\Phrasea\Feed\Link\LinkGeneratorCollection;
use Alchemy\Phrasea\Feed\RSS\FeedRSSImage; use Alchemy\Phrasea\Feed\RSS\FeedRSSImage;
use Alchemy\Phrasea\Model\Entities\User; use Alchemy\Phrasea\Model\Entities\User;
use Alchemy\Phrasea\Utilities\NullableDateTime;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use Alchemy\Phrasea\Model\Entities\FeedEntry; use Alchemy\Phrasea\Model\Entities\FeedEntry;
use Alchemy\Phrasea\Feed\Link\FeedLinkGenerator; use Alchemy\Phrasea\Feed\Link\FeedLinkGenerator;
@@ -51,8 +52,6 @@ class RssFormatter extends FeedFormatterAbstract implements FeedFormatterInterfa
*/ */
public function format(FeedInterface $feed, $page, User $user = null, $generator = 'Phraseanet', Application $app = null) public function format(FeedInterface $feed, $page, User $user = null, $generator = 'Phraseanet', Application $app = null)
{ {
$updated_on = $feed->getUpdatedOn();
$next = $prev = null; $next = $prev = null;
if ($feed->hasPage($page + 1, self::PAGE_SIZE)) { if ($feed->hasPage($page + 1, self::PAGE_SIZE)) {
@@ -104,11 +103,10 @@ class RssFormatter extends FeedFormatterAbstract implements FeedFormatterInterfa
$this->addTag($doc, $channel, 'managingEditor', $this->managingEditor); $this->addTag($doc, $channel, 'managingEditor', $this->managingEditor);
if (isset($this->webMaster)) if (isset($this->webMaster))
$this->addTag($doc, $channel, 'webMaster', $this->webMaster); $this->addTag($doc, $channel, 'webMaster', $this->webMaster);
if ($updated_on instanceof \DateTime) { if (null !== $updated_on = NullableDateTime::format($feed->getUpdatedOn(), DATE_RFC2822)) {
$updated_on = $updated_on->format(DATE_RFC2822);
$this->addTag($doc, $channel, 'pubDate', $updated_on); $this->addTag($doc, $channel, 'pubDate', $updated_on);
} }
if (isset($this->lastBuildDate) && $this->lastBuildDate instanceof DateTime) { if (isset($this->lastBuildDate) && $this->lastBuildDate instanceof \DateTime) {
$last_build = $this->lastBuildDate->format(DATE_RFC2822); $last_build = $this->lastBuildDate->format(DATE_RFC2822);
$this->addTag($doc, $channel, 'lastBuildDate', $last_build); $this->addTag($doc, $channel, 'lastBuildDate', $last_build);
} }

View File

@@ -284,7 +284,7 @@ class Helper extends \Alchemy\Phrasea\Helper\Helper
public function get_serialize_list() public function get_serialize_list()
{ {
if ($this->is_single_grouping()) { if ($this->is_single_grouping()) {
return $this->get_grouping_head()->get_serialize_key(); return $this->get_grouping_head()->getId();
} else { } else {
return $this->selection->serialize_list(); return $this->selection->serialize_list();
} }

View 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\Hydration;
interface Hydrator
{
/**
* Hydrate an instance with provided data
*
* @param object $instance
* @param array $data
* @return void
*/
public function hydrate($instance, array $data);
/**
* Extracts data from an instance
*
* @param object $instance
* @return array
*/
public function extract($instance);
}

View File

@@ -0,0 +1,114 @@
<?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\Hydration;
use Assert\Assertion;
class IdentityMap implements \ArrayAccess, \IteratorAggregate, \Countable
{
/**
* @var object[]
*/
private $entities;
/**
* @var Hydrator
*/
private $hydrator;
/**
* @var object
*/
private $prototype;
/**
* @param Hydrator $hydrator
* @param object $prototype A clonable prototype of objects in idMap
*/
public function __construct(Hydrator $hydrator, $prototype)
{
$this->hydrator = $hydrator;
$this->prototype = $prototype;
}
/**
* @param string|int $index
* @param array $data
* @return object
*/
public function hydrate($index, array $data)
{
if (!isset($this->entities[$index])) {
$this->entities[$index] = clone $this->prototype;
}
$instance = $this->entities[$index];
$this->hydrator->hydrate($instance, $data);
return $instance;
}
/**
* @param array[] $data
* @return object[]
*/
public function hydrateAll(array $data)
{
Assertion::allIsArray($data);
$instances = [];
foreach ($data as $index => $item) {
$instances[$index] = $this->hydrate($index, $item);
}
return $instances;
}
public function clear()
{
$this->entities = [];
}
public function getIterator()
{
return new \ArrayIterator($this->entities);
}
public function offsetExists($offset)
{
return isset($this->entities[$offset]);
}
public function offsetGet($offset)
{
return $this->entities[$offset];
}
public function offsetSet($offset, $value)
{
Assertion::notNull($offset);
Assertion::isArray($value);
$this->hydrate($offset, $value);
}
public function offsetUnset($offset)
{
unset($this->entities[$offset]);
}
public function count()
{
return count($this->entities);
}
}

View File

@@ -0,0 +1,130 @@
<?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\Hydration;
use Assert\Assertion;
class ReflectionHydrator implements Hydrator
{
/**
* @var string
*/
private $className;
/**
* @var string[]
*/
private $properties;
/**
* @var null|\ReflectionClass
*/
private $reflectionClass;
/**
* @var null|\ReflectionProperty[]
*/
private $reflectionProperties;
/**
* @param string $className
* @param string[] $properties
*/
public function __construct($className, array $properties)
{
$this->className = $className;
$this->properties = $properties;
}
/**
* @param array $data
* @param object $instance
* @throws \Assert\AssertionFailedException
*/
public function hydrate($instance, array $data)
{
Assertion::isInstanceOf($instance, $this->className);
foreach ($data as $key => $value) {
$this->getReflectionProperty($key)->setValue($instance, $value);
}
}
/**
* @param object $instance
* @return array
* @throws \Assert\AssertionFailedException
*/
public function extract($instance)
{
Assertion::isInstanceOf($instance, $this->className);
$data = [];
foreach ($this->getReflectionProperties() as $name => $property) {
$data[$name] = $property->getValue($instance);
}
return $data;
}
/**
* @return \ReflectionClass
*/
private function getReflectionClass()
{
if (null === $this->reflectionClass) {
$this->reflectionClass = new \ReflectionClass($this->className);
}
return $this->reflectionClass;
}
/**
* @param string $name
* @return \ReflectionProperty
* @throws \RuntimeException
*/
private function getReflectionProperty($name)
{
$this->loadReflectionProperties();
return $this->reflectionProperties[$name];
}
/**
* @return \ReflectionProperty[]
*/
private function getReflectionProperties()
{
$this->loadReflectionProperties();
return $this->reflectionProperties;
}
private function loadReflectionProperties()
{
if (null !== $this->reflectionProperties) {
return;
}
$class = $this->getReflectionClass();
$properties = [];
foreach ($this->properties as $name) {
$property = $class->getProperty($name);
$property->setAccessible(true);
$properties[$name] = $property;
}
$this->reflectionProperties = $properties;
}
}

View File

@@ -11,22 +11,20 @@ namespace Alchemy\Phrasea\Media;
use Assert\Assertion; use Assert\Assertion;
final class ArrayTechnicalDataSet implements \IteratorAggregate, TechnicalDataSet class ArrayTechnicalDataSet implements \IteratorAggregate, TechnicalDataSet
{ {
/** @var TechnicalData[] */ /** @var TechnicalData[] */
private $data; private $data = [];
/** /**
* @param TechnicalData[] $data * @param TechnicalData[] $data
*/ */
public function __construct($data = []) public function __construct($data = [])
{ {
Assertion::allIsInstanceOf($data, TechnicalData::class); Assertion::isTraversable($data);
$this->data = [];
foreach ($data as $technicalData) { foreach ($data as $technicalData) {
$this->data[$technicalData->getName()] = $technicalData; $this[] = $technicalData;
} }
} }
@@ -41,7 +39,7 @@ final class ArrayTechnicalDataSet implements \IteratorAggregate, TechnicalDataSe
$offset = $offset->getName(); $offset = $offset->getName();
} }
return isset($this->data[$offset]) || array_key_exists($offset, $this->data); return isset($this->data[$offset]);
} }
public function offsetGet($offset) public function offsetGet($offset)
@@ -50,7 +48,7 @@ final class ArrayTechnicalDataSet implements \IteratorAggregate, TechnicalDataSe
} }
/** /**
* @param string $offset * @param null|string $offset
* @param TechnicalData $value * @param TechnicalData $value
*/ */
public function offsetSet($offset, $value) public function offsetSet($offset, $value)
@@ -58,6 +56,7 @@ final class ArrayTechnicalDataSet implements \IteratorAggregate, TechnicalDataSe
Assertion::isInstanceOf($value, TechnicalData::class); Assertion::isInstanceOf($value, TechnicalData::class);
$name = $value->getName(); $name = $value->getName();
if (null !== $offset) { if (null !== $offset) {
Assertion::eq($name, $offset); Assertion::eq($name, $offset);
} }
@@ -82,6 +81,7 @@ final class ArrayTechnicalDataSet implements \IteratorAggregate, TechnicalDataSe
public function getValues() public function getValues()
{ {
$values = []; $values = [];
foreach ($this->data as $key => $value) { foreach ($this->data as $key => $value) {
$values[$key] = $value->getValue(); $values[$key] = $value->getValue();
} }

View 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\Media\Factory;
use Alchemy\Phrasea\Databox\DataboxConnectionProvider;
use Alchemy\Phrasea\Media\RecordTechnicalDataSetRepositoryFactory;
use Alchemy\Phrasea\Media\Repository\DbalRecordTechnicalDataSetRepository;
class DbalRepositoryFactory implements RecordTechnicalDataSetRepositoryFactory
{
/**
* @var DataboxConnectionProvider
*/
private $connectionProvider;
public function __construct(DataboxConnectionProvider $connectionProvider)
{
$this->connectionProvider = $connectionProvider;
}
public function createRepositoryForDatabox($databoxId)
{
return new DbalRecordTechnicalDataSetRepository(
$this->connectionProvider->getConnection($databoxId),
new TechnicalDataFactory()
);
}
}

View 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\Media\Factory;
use Alchemy\Phrasea\Media\FloatTechnicalData;
use Alchemy\Phrasea\Media\IntegerTechnicalData;
use Alchemy\Phrasea\Media\StringTechnicalData;
use Alchemy\Phrasea\Media\TechnicalData;
class TechnicalDataFactory
{
/**
* @param string $name
* @param string $value
* @return TechnicalData
*/
public function createFromNameAndValue($name, $value)
{
if (ctype_digit($value)) {
return new IntegerTechnicalData($name, $value);
} elseif (preg_match('/[0-9]?\.[0-9]+/', $value)) {
return new FloatTechnicalData($name, $value);
}
return new StringTechnicalData($name, $value);
}
}

View File

@@ -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\Media;
use Alchemy\Phrasea\Model\Entities\User;
use Alchemy\Phrasea\Model\Provider\SecretProvider;
use Assert\Assertion;
use Firebase\JWT\JWT;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
class MediaSubDefinitionUrlGenerator
{
/**
* @var UrlGeneratorInterface
*/
private $urlGenerator;
/**
* @var SecretProvider
*/
private $secretProvider;
/**
* @var int
*/
private $defaultTTL;
public function __construct(UrlGeneratorInterface $urlGenerator, SecretProvider $secretProvider, $defaultTTL = 0)
{
Assertion::integer($defaultTTL);
$this->urlGenerator = $urlGenerator;
$this->secretProvider = $secretProvider;
$this->defaultTTL = (int)$defaultTTL;
}
/**
* @return int
*/
public function getDefaultTTL()
{
return $this->defaultTTL;
}
/**
* @param User $issuer
* @param \media_subdef $subdef
* @param int $url_ttl
* @return string
*/
public function generate(User $issuer, \media_subdef $subdef, $url_ttl = null)
{
$url_ttl = $url_ttl ?: $this->defaultTTL;
$payload = [
'iat' => time(),
'iss' => $issuer->getId(),
'sdef' => [$subdef->get_sbas_id(), $subdef->get_record_id(), $subdef->get_name()],
];
if ($url_ttl > 0) {
$payload['exp'] = $payload['iat'] + $url_ttl;
}
$secret = $this->secretProvider->getSecretForUser($issuer);
return $this->urlGenerator->generate('media_accessor', [
'token' => JWT::encode($payload, $secret->getToken(), 'HS256', $secret->getId()),
], UrlGeneratorInterface::ABSOLUTE_URL);
}
/**
* @param User $issuer
* @param \media_subdef[] $subdefs
* @param int $url_ttl
* @return string[]
*/
public function generateMany(User $issuer, $subdefs, $url_ttl = null)
{
Assertion::allIsInstanceOf($subdefs, \media_subdef::class);
$url_ttl = $url_ttl ?: $this->defaultTTL;
$payloads = [];
$payload = [
'iat' => time(),
'iss' => $issuer->getId(),
'sdef' => null,
];
if ($url_ttl > 0) {
$payload['exp'] = $payload['iat'] + $url_ttl;
}
foreach ($subdefs as $index => $subdef) {
$payload['sdef'] = [$subdef->get_sbas_id(), $subdef->get_record_id(), $subdef->get_name()];
$payloads[$index] = $payload;
}
$secret = $this->secretProvider->getSecretForUser($issuer);
return array_map(function ($payload) use ($secret) {
return $this->urlGenerator->generate('media_accessor', [
'token' => JWT::encode($payload, $secret->getToken(), 'HS256', $secret->getId()),
], UrlGeneratorInterface::ABSOLUTE_URL);
}, $payloads);
}
}

View 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\Media;
class RecordTechnicalDataSet extends ArrayTechnicalDataSet
{
/**
* @var int
*/
private $recordId;
/**
* @param int $recordId
* @param TechnicalData[] $technicalData
*/
public function __construct($recordId, $technicalData = [])
{
$this->recordId = (int)$recordId;
parent::__construct($technicalData);
}
/**
* @return int
*/
public function getRecordId()
{
return $this->recordId;
}
}

View 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\Media;
interface RecordTechnicalDataSetRepository
{
/**
* @param int[] $recordIds
* @return RecordTechnicalDataSet[]
*/
public function findByRecordIds(array $recordIds);
}

View 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\Media;
interface RecordTechnicalDataSetRepositoryFactory
{
/**
* @param int $databoxId
* @return RecordTechnicalDataSetRepository
*/
public function createRepositoryForDatabox($databoxId);
}

View File

@@ -0,0 +1,42 @@
<?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\Media;
class RecordTechnicalDataSetRepositoryProvider
{
/**
* @var RecordTechnicalDataSetRepository[]
*/
private $repositories = [];
/**
* @var RecordTechnicalDataSetRepositoryFactory
*/
private $factory;
public function __construct(RecordTechnicalDataSetRepositoryFactory $factory)
{
$this->factory = $factory;
}
/**
* @param int $databoxId
* @return RecordTechnicalDataSetRepository
*/
public function getRepositoryFor($databoxId)
{
if (!isset($this->repositories[$databoxId])) {
$this->repositories[$databoxId] = $this->factory->createRepositoryForDatabox($databoxId);
}
return $this->repositories[$databoxId];
}
}

View File

@@ -0,0 +1,75 @@
<?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\Media\Repository;
use Alchemy\Phrasea\Media\Factory\TechnicalDataFactory;
use Alchemy\Phrasea\Media\RecordTechnicalDataSet;
use Alchemy\Phrasea\Media\RecordTechnicalDataSetRepository;
use Doctrine\DBAL\Connection;
class DbalRecordTechnicalDataSetRepository implements RecordTechnicalDataSetRepository
{
/**
* @var Connection
*/
private $connection;
/**
* @var TechnicalDataFactory
*/
private $dataFactory;
public function __construct(Connection $connection, TechnicalDataFactory $dataFactory)
{
$this->connection = $connection;
$this->dataFactory = $dataFactory;
}
/**
* @param int[] $recordIds
* @return RecordTechnicalDataSet[]
*/
public function findByRecordIds(array $recordIds)
{
if (empty($recordIds)) {
return [];
}
$data = $this->connection->fetchAll(
'SELECT record_id, name, value FROM technical_datas WHERE record_id IN (:recordIds)',
['recordIds' => $recordIds],
['recordIds' => Connection::PARAM_INT_ARRAY]
);
return $this->mapSetsFromDatabaseResult($recordIds, $data);
}
/**
* @param array $recordIds
* @param array $data
* @return RecordTechnicalDataSet[]
*/
private function mapSetsFromDatabaseResult(array $recordIds, array $data)
{
$groups = [];
foreach ($recordIds as $recordId) {
$groups[$recordId] = new RecordTechnicalDataSet($recordId);
}
foreach ($data as $item) {
$group =& $groups[$item['record_id']];
$group[] = $this->dataFactory->createFromNameAndValue($item['name'], $item['value']);
}
return array_values($groups);
}
}

View File

@@ -0,0 +1,52 @@
<?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\Media;
use Alchemy\Phrasea\Record\RecordReference;
use Alchemy\Phrasea\Record\RecordReferenceCollection;
class TechnicalDataService
{
/**
* @var RecordTechnicalDataSetRepositoryProvider
*/
private $provider;
public function __construct(RecordTechnicalDataSetRepositoryProvider $provider)
{
$this->provider = $provider;
}
/**
* @param RecordReference[] $references
* @return RecordTechnicalDataSet[]
*/
public function fetchRecordsTechnicalData($references)
{
if (!$references instanceof RecordReferenceCollection) {
$references = new RecordReferenceCollection($references);
}
$sets = [];
foreach ($references->groupPerDataboxId() as $databoxId => $indexes) {
foreach ($this->provider->getRepositoryFor($databoxId)->findByRecordIds(array_keys($indexes)) as $set) {
$index = $indexes[$set->getRecordId()];
$sets[$index] = $set;
}
}
ksort($sets);
return $sets;
}
}

View 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\Media;
use Alchemy\Phrasea\Databox\DataboxConnectionProvider;
use Alchemy\Phrasea\Media\Factory\DbalRepositoryFactory;
use Silex\Application;
use Silex\ServiceProviderInterface;
class TechnicalDataServiceProvider implements ServiceProviderInterface
{
public function register(Application $app)
{
$app['service.technical_data'] = $app->share(function (Application $app) {
$connectionProvider = new DataboxConnectionProvider($app['phraseanet.appbox']);
$repositoryFactory = new DbalRepositoryFactory($connectionProvider);
return new TechnicalDataService(new RecordTechnicalDataSetRepositoryProvider($repositoryFactory));
});
}
public function boot(Application $app)
{
// no-op
}
}

View File

@@ -460,8 +460,8 @@ class Basket
foreach ($this->getElements() as $basket_element) { foreach ($this->getElements() as $basket_element) {
$bask_record = $basket_element->getRecord($app); $bask_record = $basket_element->getRecord($app);
if ($bask_record->get_record_id() == $record->get_record_id() if ($bask_record->getRecordId() == $record->getRecordId()
&& $bask_record->get_sbas_id() == $record->get_sbas_id()) { && $bask_record->getDataboxId() == $record->getDataboxId()) {
return true; return true;
} }
} }

View File

@@ -138,8 +138,8 @@ class BasketElement
public function setRecord(\record_adapter $record) public function setRecord(\record_adapter $record)
{ {
$this->setRecordId($record->get_record_id()); $this->setRecordId($record->getRecordId());
$this->setSbasId($record->get_sbas_id()); $this->setSbasId($record->getDataboxId());
} }
/** /**

View File

@@ -259,7 +259,7 @@ class FeedEntry
/** /**
* Get items * Get items
* *
* @return \Doctrine\Common\Collections\Collection * @return FeedItem[]|\Doctrine\Common\Collections\Collection
*/ */
public function getItems() public function getItems()
{ {

View File

@@ -80,7 +80,7 @@ class OrderElement
} }
/** /**
* @return mixed * @return User|null
*/ */
public function getOrderMaster() public function getOrderMaster()
{ {

View File

@@ -116,8 +116,8 @@ class StoryWZ
public function setRecord(\record_adapter $record) public function setRecord(\record_adapter $record)
{ {
$this->setRecordId($record->get_record_id()); $this->setRecordId($record->getRecordId());
$this->setSbasId($record->get_sbas_id()); $this->setSbasId($record->getDataboxId());
} }
/** /**
* @param User $user * @param User $user

View File

@@ -8,6 +8,7 @@
* file that was distributed with this source code. * file that was distributed with this source code.
*/ */
namespace Alchemy\Phrasea\Model\Manipulator; namespace Alchemy\Phrasea\Model\Manipulator;
use Alchemy\Phrasea\Application; use Alchemy\Phrasea\Application;
use Alchemy\Phrasea\Border; use Alchemy\Phrasea\Border;
use Alchemy\Phrasea\Border\Attribute\AttributeInterface; use Alchemy\Phrasea\Border\Attribute\AttributeInterface;
@@ -17,6 +18,8 @@ use Doctrine\ORM\EntityRepository;
use PHPExiftool\Driver\Metadata\Metadata; use PHPExiftool\Driver\Metadata\Metadata;
use Symfony\Component\Filesystem\Exception\IOException; use Symfony\Component\Filesystem\Exception\IOException;
use Symfony\Component\Filesystem\Filesystem; use Symfony\Component\Filesystem\Filesystem;
class LazaretManipulator class LazaretManipulator
{ {
/** @var Application */ /** @var Application */
@@ -31,6 +34,7 @@ class LazaretManipulator
* @var EntityManager * @var EntityManager
*/ */
private $entityManager; private $entityManager;
public function __construct(Application $app, EntityRepository $repository, Filesystem $fileSystem, EntityManager $entityManager) public function __construct(Application $app, EntityRepository $repository, Filesystem $fileSystem, EntityManager $entityManager)
{ {
$this->app = $app; $this->app = $app;
@@ -38,23 +42,29 @@ class LazaretManipulator
$this->fileSystem = $fileSystem; $this->fileSystem = $fileSystem;
$this->entityManager = $entityManager; $this->entityManager = $entityManager;
} }
public function deny($lazaret_id) public function deny($lazaret_id)
{ {
$ret = ['success' => false, 'message' => '', 'result' => []]; $ret = ['success' => false, 'message' => '', 'result' => []];
/** @var LazaretFile $lazaretFile */ /** @var LazaretFile $lazaretFile */
$lazaretFile = $this->repository->find($lazaret_id); $lazaretFile = $this->repository->find($lazaret_id);
if (null === $lazaretFile) { if (null === $lazaretFile) {
$ret['message'] = $this->app->trans('File is not present in quarantine anymore, please refresh'); $ret['message'] = $this->app->trans('File is not present in quarantine anymore, please refresh');
return $ret; return $ret;
} }
try { try {
$this->denyLazaretFile($lazaretFile); $this->denyLazaretFile($lazaretFile);
$ret['success'] = true; $ret['success'] = true;
} catch (\Exception $e) { } catch (\Exception $e) {
// No-op // No-op
} }
return $ret; return $ret;
} }
/** /**
* Empty lazaret * Empty lazaret
* *
@@ -74,14 +84,17 @@ class LazaretManipulator
'max' => '', 'max' => '',
) )
); );
if( $maxTodo <= 0) { if( $maxTodo <= 0) {
$maxTodo = -1; // all $maxTodo = -1; // all
} }
$ret['result']['max'] = $maxTodo; $ret['result']['max'] = $maxTodo;
$ret['result']['tobedone'] = (int) $this->repository->createQueryBuilder('id') $ret['result']['tobedone'] = (int) $this->repository->createQueryBuilder('id')
->select('COUNT(id)') ->select('COUNT(id)')
->getQuery() ->getQuery()
->getSingleScalarResult(); ->getSingleScalarResult();
if($maxTodo == -1) { if($maxTodo == -1) {
// all // all
$lazaretFiles = $this->repository->findAll(); $lazaretFiles = $this->repository->findAll();
@@ -89,7 +102,9 @@ class LazaretManipulator
// limit maxTodo // limit maxTodo
$lazaretFiles = $this->repository->findBy(array(), null, $maxTodo); $lazaretFiles = $this->repository->findBy(array(), null, $maxTodo);
} }
$this->entityManager->beginTransaction(); $this->entityManager->beginTransaction();
try { try {
foreach ($lazaretFiles as $lazaretFile) { foreach ($lazaretFiles as $lazaretFile) {
$this->denyLazaretFile($lazaretFile); $this->denyLazaretFile($lazaretFile);
@@ -102,20 +117,28 @@ class LazaretManipulator
$ret['message'] = $this->app->trans('An error occured'); $ret['message'] = $this->app->trans('An error occured');
} }
$ret['result']['todo'] = $ret['result']['tobedone'] - $ret['result']['done']; $ret['result']['todo'] = $ret['result']['tobedone'] - $ret['result']['done'];
return $ret; return $ret;
} }
public function add($file_id, $keepAttributes=true, Array $attributesToKeep=[]) public function add($file_id, $keepAttributes=true, Array $attributesToKeep=[])
{ {
$ret = ['success' => false, 'message' => '', 'result' => []]; $ret = ['success' => false, 'message' => '', 'result' => []];
/* @var LazaretFile $lazaretFile */ /* @var LazaretFile $lazaretFile */
$lazaretFile = $this->repository->find($file_id); $lazaretFile = $this->repository->find($file_id);
if (null === $lazaretFile) { if (null === $lazaretFile) {
$ret['message'] = $this->app->trans('File is not present in quarantine anymore, please refresh'); $ret['message'] = $this->app->trans('File is not present in quarantine anymore, please refresh');
return $ret; return $ret;
} }
$path = $this->app['tmp.lazaret.path']; $path = $this->app['tmp.lazaret.path'];
$lazaretFileName = $path .'/'.$lazaretFile->getFilename(); $lazaretFileName = $path .'/'.$lazaretFile->getFilename();
$lazaretThumbFileName = $path .'/'.$lazaretFile->getThumbFilename(); $lazaretThumbFileName = $path .'/'.$lazaretFile->getThumbFilename();
try { try {
$borderFile = Border\File::buildFromPathfile( $borderFile = Border\File::buildFromPathfile(
$lazaretFileName, $lazaretFileName,
@@ -123,12 +146,14 @@ class LazaretManipulator
$this->app, $this->app,
$lazaretFile->getOriginalName() $lazaretFile->getOriginalName()
); );
//Post record creation //Post record creation
/** @var \record_adapter $record */ /** @var \record_adapter $record */
$record = null; $record = null;
$callBack = function ($element) use (&$record) { $callBack = function ($element) use (&$record) {
$record = $element; $record = $element;
}; };
//Force creation record //Force creation record
$this->getBorderManager()->process( $this->getBorderManager()->process(
$lazaretFile->getSession(), $lazaretFile->getSession(),
@@ -136,10 +161,13 @@ class LazaretManipulator
$callBack, $callBack,
Border\Manager::FORCE_RECORD Border\Manager::FORCE_RECORD
); );
if ($keepAttributes) { if ($keepAttributes) {
//add attribute //add attribute
$metaFields = new Border\MetaFieldsBag(); $metaFields = new Border\MetaFieldsBag();
$metadataBag = new Border\MetadataBag(); $metadataBag = new Border\MetadataBag();
foreach ($lazaretFile->getAttributes() as $attr) { foreach ($lazaretFile->getAttributes() as $attr) {
//Check which ones to keep //Check which ones to keep
if (!!count($attributesToKeep)) { if (!!count($attributesToKeep)) {
@@ -147,11 +175,13 @@ class LazaretManipulator
continue; continue;
} }
} }
try { try {
$attribute = Border\Attribute\Factory::getFileAttribute($this->app, $attr->getName(), $attr->getValue()); $attribute = Border\Attribute\Factory::getFileAttribute($this->app, $attr->getName(), $attr->getValue());
} catch (\InvalidArgumentException $e) { } catch (\InvalidArgumentException $e) {
continue; continue;
} }
switch ($attribute->getName()) { switch ($attribute->getName()) {
case AttributeInterface::NAME_METADATA: case AttributeInterface::NAME_METADATA:
/** @var Metadata $value */ /** @var Metadata $value */
@@ -164,7 +194,7 @@ class LazaretManipulator
$value->appendChild($record); $value->appendChild($record);
break; break;
case AttributeInterface::NAME_STATUS: case AttributeInterface::NAME_STATUS:
$record->set_binary_status($attribute->getValue()); $record->setStatus($attribute->getValue());
break; break;
case AttributeInterface::NAME_METAFIELD: case AttributeInterface::NAME_METAFIELD:
/** @var Border\Attribute\MetaField $attribute */ /** @var Border\Attribute\MetaField $attribute */
@@ -172,25 +202,32 @@ class LazaretManipulator
break; break;
} }
} }
$data = $metadataBag->toMetadataArray($record->get_databox()->get_meta_structure());
$data = $metadataBag->toMetadataArray($record->getDatabox()->get_meta_structure());
$record->set_metadatas($data); $record->set_metadatas($data);
$fields = $metaFields->toMetadataArray($record->get_databox()->get_meta_structure());
$fields = $metaFields->toMetadataArray($record->getDatabox()->get_meta_structure());
$record->set_metadatas($fields); $record->set_metadatas($fields);
} }
//Delete lazaret file //Delete lazaret file
$this->entityManager->remove($lazaretFile); $this->entityManager->remove($lazaretFile);
$this->entityManager->flush(); $this->entityManager->flush();
$ret['success'] = true; $ret['success'] = true;
} catch (\Exception $e) { } catch (\Exception $e) {
$ret['message'] = $this->app->trans('An error occured'); $ret['message'] = $this->app->trans('An error occured');
} }
try { try {
$this->fileSystem->remove([$lazaretFileName, $lazaretThumbFileName]); $this->fileSystem->remove([$lazaretFileName, $lazaretThumbFileName]);
} catch (IOException $e) { } catch (IOException $e) {
// no-op // no-op
} }
return $ret; return $ret;
} }
/** /**
* @return Border\Manager * @return Border\Manager
*/ */
@@ -198,19 +235,24 @@ class LazaretManipulator
{ {
return $this->app['border-manager']; return $this->app['border-manager'];
} }
protected function denyLazaretFile(LazaretFile $lazaretFile) protected function denyLazaretFile(LazaretFile $lazaretFile)
{ {
$path = $this->app['tmp.lazaret.path']; $path = $this->app['tmp.lazaret.path'];
$lazaretFileName = $path .'/'.$lazaretFile->getFilename(); $lazaretFileName = $path .'/'.$lazaretFile->getFilename();
$lazaretThumbFileName = $path .'/'.$lazaretFile->getThumbFilename(); $lazaretThumbFileName = $path .'/'.$lazaretFile->getThumbFilename();
$this->entityManager->remove($lazaretFile); $this->entityManager->remove($lazaretFile);
$this->entityManager->flush(); $this->entityManager->flush();
try { try {
$this->fileSystem->remove([$lazaretFileName, $lazaretThumbFileName]); $this->fileSystem->remove([$lazaretFileName, $lazaretThumbFileName]);
} catch (IOException $e) { } catch (IOException $e) {
// no-op // no-op
} }
return $this; return $this;
} }
}
}

View File

@@ -196,7 +196,7 @@ class BasketRepository extends EntityRepository
AND b.user = :usr_id'; AND b.user = :usr_id';
$params = [ $params = [
'record_id' => $record->get_record_id(), 'record_id' => $record->getRecordId(),
'usr_id' => $user->getId() 'usr_id' => $user->getId()
]; ];

View File

@@ -99,8 +99,8 @@ class StoryWZRepository extends EntityRepository
{ {
$story = $this->findOneBy([ $story = $this->findOneBy([
'user' => $user->getId(), 'user' => $user->getId(),
'sbas_id' => $Story->get_sbas_id(), 'sbas_id' => $Story->getDataboxId(),
'record_id' => $Story->get_record_id(), 'record_id' => $Story->getRecordId(),
]); ]);
if ($story) { if ($story) {
@@ -129,8 +129,8 @@ class StoryWZRepository extends EntityRepository
$query = $this->_em->createQuery($dql); $query = $this->_em->createQuery($dql);
$query->setParameters([ $query->setParameters([
'sbas_id' => $Story->get_sbas_id(), 'sbas_id' => $Story->getDataboxId(),
'record_id' => $Story->get_record_id(), 'record_id' => $Story->getRecordId(),
]); ]);
/** @var StoryWZ[] $stories */ /** @var StoryWZ[] $stories */

View File

@@ -1,9 +1,8 @@
<?php <?php
/* /*
* This file is part of Phraseanet * This file is part of Phraseanet
* *
* (c) 2005-2014 Alchemy * (c) 2005-2016 Alchemy
* *
* For the full copyright and license information, please view the LICENSE * For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code. * file that was distributed with this source code.
@@ -11,6 +10,7 @@
namespace Alchemy\Phrasea\Model\Serializer; namespace Alchemy\Phrasea\Model\Serializer;
use Alchemy\Phrasea\Media\TechnicalDataService;
use Symfony\Component\Yaml\Dumper as YamlDumper; use Symfony\Component\Yaml\Dumper as YamlDumper;
class CaptionSerializer extends AbstractSerializer class CaptionSerializer extends AbstractSerializer
@@ -19,21 +19,46 @@ class CaptionSerializer extends AbstractSerializer
const SERIALIZE_YAML = 'yaml'; const SERIALIZE_YAML = 'yaml';
const SERIALIZE_JSON = 'json'; const SERIALIZE_JSON = 'json';
/**
* @var TechnicalDataService|callable
*/
private $technicalDataService;
/**
* @param callable|TechnicalDataService $technicalDataService
* @throws \InvalidArgumentException
*/
public function __construct($technicalDataService)
{
if (!$technicalDataService instanceof TechnicalDataService && !is_callable($technicalDataService)) {
throw new \InvalidArgumentException(sprintf(
'Expects a callable or %s, got %s',
TechnicalDataService::class,
is_object($technicalDataService) ? get_class($technicalDataService) : gettype($technicalDataService)
));
}
$this->technicalDataService = $technicalDataService;
}
/**
* @param \caption_record $caption
* @param string $format
* @param bool $includeBusinessFields
* @return string
* @throws \Exception
*/
public function serialize(\caption_record $caption, $format, $includeBusinessFields = false) public function serialize(\caption_record $caption, $format, $includeBusinessFields = false)
{ {
switch ($format) { switch ($format) {
case self::SERIALIZE_XML: case self::SERIALIZE_XML:
return $this->serializeXML($caption, (Boolean) $includeBusinessFields); return $this->serializeXML($caption, (bool) $includeBusinessFields);
break;
case self::SERIALIZE_YAML: case self::SERIALIZE_YAML:
return $this->serializeYAML($caption, (Boolean) $includeBusinessFields); return $this->serializeYAML($caption, (bool) $includeBusinessFields);
break;
case self::SERIALIZE_JSON: case self::SERIALIZE_JSON:
return $this->serializeJSON($caption, (Boolean) $includeBusinessFields); return $this->serializeJSON($caption, (bool) $includeBusinessFields);
break;
default: default:
throw new \Exception(sprintf('Unknown format %s', $format)); throw new \Exception(sprintf('Unknown format %s', $format));
break;
} }
} }
@@ -47,6 +72,11 @@ class CaptionSerializer extends AbstractSerializer
return \p4string::jsonencode($this->toArray($caption, $includeBusinessFields)); return \p4string::jsonencode($this->toArray($caption, $includeBusinessFields));
} }
/**
* @param \caption_record $caption
* @param bool $includeBusinessFields
* @return array
*/
public function toArray(\caption_record $caption, $includeBusinessFields) public function toArray(\caption_record $caption, $includeBusinessFields)
{ {
$buffer = []; $buffer = [];
@@ -77,7 +107,7 @@ class CaptionSerializer extends AbstractSerializer
$dom_doc->standalone = true; $dom_doc->standalone = true;
$record = $dom_doc->createElement('record'); $record = $dom_doc->createElement('record');
$record->setAttribute('record_id', $caption->get_record()->get_record_id()); $record->setAttribute('record_id', $caption->getRecordReference()->getRecordId());
$dom_doc->appendChild($record); $dom_doc->appendChild($record);
$description = $dom_doc->createElement('description'); $description = $dom_doc->createElement('description');
$record->appendChild($description); $record->appendChild($description);
@@ -96,7 +126,8 @@ class CaptionSerializer extends AbstractSerializer
$doc = $dom_doc->createElement('doc'); $doc = $dom_doc->createElement('doc');
$tc_datas = $caption->get_record()->get_technical_infos()->getValues(); $technicalData = $this->getTechnicalDataService()->fetchRecordsTechnicalData([$caption->getRecordReference()]);
$tc_datas = $technicalData[0]->getValues();
foreach ($tc_datas as $key => $data) { foreach ($tc_datas as $key => $data) {
$doc->setAttribute($key, $data); $doc->setAttribute($key, $data);
@@ -106,4 +137,27 @@ class CaptionSerializer extends AbstractSerializer
return $dom_doc->saveXML(); return $dom_doc->saveXML();
} }
/**
* @return TechnicalDataService
* @throws \UnexpectedValueException
*/
private function getTechnicalDataService()
{
if (!$this->technicalDataService instanceof TechnicalDataService) {
$instance = call_user_func($this->technicalDataService);
if (!$instance instanceof TechnicalDataService) {
throw new \UnexpectedValueException(sprintf(
'Expected a %s instance, got %s.',
TechnicalDataService::class,
is_object($instance) ? get_class($instance) : gettype($instance)
));
}
$this->technicalDataService = $instance;
}
return $this->technicalDataService;
}
} }

View File

@@ -50,7 +50,7 @@ class ESRecordSerializer extends AbstractSerializer
} }
$i = 0; $i = 0;
foreach (preg_split('//', strrev($record->get_status()), -1, PREG_SPLIT_NO_EMPTY) as $val) { foreach (preg_split('//', strrev($record->getStatus()), -1, PREG_SPLIT_NO_EMPTY) as $val) {
$status['status-'.$i] = (int) $val; $status['status-'.$i] = (int) $val;
$i++; $i++;
} }

View File

@@ -0,0 +1,260 @@
<?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\Order\Controller;
use Alchemy\Phrasea\Application\Helper\JsonBodyAware;
use Alchemy\Phrasea\Controller\Api\Result;
use Alchemy\Phrasea\Controller\RecordsRequest;
use Alchemy\Phrasea\Core\Event\OrderEvent;
use Alchemy\Phrasea\Core\PhraseaEvents;
use Alchemy\Phrasea\Model\Entities\BasketElement;
use Alchemy\Phrasea\Model\Entities\Order;
use Alchemy\Phrasea\Model\Entities\OrderElement;
use Alchemy\Phrasea\Model\RecordReferenceInterface;
use Alchemy\Phrasea\Order\OrderElementTransformer;
use Alchemy\Phrasea\Order\OrderElementView;
use Alchemy\Phrasea\Order\OrderFiller;
use Alchemy\Phrasea\Order\OrderTransformer;
use Alchemy\Phrasea\Order\OrderView;
use Alchemy\Phrasea\Order\OrderViewBuilder;
use Alchemy\Phrasea\Record\RecordReference;
use Alchemy\Phrasea\Record\RecordReferenceCollection;
use Assert\Assertion;
use Doctrine\Common\Collections\ArrayCollection;
use League\Fractal\Manager;
use League\Fractal\Pagination\PagerfantaPaginatorAdapter;
use League\Fractal\Resource\Collection;
use League\Fractal\Resource\Item;
use League\Fractal\Resource\ResourceInterface;
use Pagerfanta\Adapter\DoctrineORMAdapter;
use Pagerfanta\Pagerfanta;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
class ApiOrderController extends BaseOrderController
{
use JsonBodyAware;
public function createAction(Request $request)
{
$data = $this->decodeJsonBody($request, 'orders.json#/definitions/order_request');
$availableRecords = $this->toRequestedRecords($data->data->records);
$records = $this->filterOrderableRecords($availableRecords);
$recordRequest = new RecordsRequest($records, new ArrayCollection($availableRecords), null, RecordsRequest::FLATTEN_YES);
$filler = new OrderFiller($this->app['repo.collection-references'], $this->app['orm.em']);
$filler->assertAllRecordsHaveOrderMaster($recordRequest);
$order = new Order();
$order->setUser($this->getAuthenticatedUser());
$order->setDeadline(new \DateTime($data->data->deadline, new \DateTimeZone('UTC')));
$order->setOrderUsage($data->data->usage);
$filler->fillOrder($order, $recordRequest);
$this->dispatch(PhraseaEvents::ORDER_CREATE, new OrderEvent($order));
$resource = new Item($order, $this->getOrderTransformer());
return $this->returnResourceResponse($request, ['elements'], $resource);
}
public function indexAction(Request $request)
{
$page = max((int) $request->get('page', '1'), 1);
$perPage = min(max((int)$request->get('per_page', '10'), 1), 100);
$fractal = $this->buildFractalManager($request->get('includes', []));
$routeGenerator = function ($page) use ($perPage) {
return $this->app->path('api_v2_orders_index', [
'page' => $page,
'per_page' => $perPage,
]);
};
$builder = $this->app['repo.orders']->createQueryBuilder('o');
$builder
->where($builder->expr()->eq('o.user', $this->getAuthenticatedUser()->getId()))
;
if (in_array('elements', $fractal->getRequestedIncludes(), false)) {
$builder
->addSelect('e')
->leftJoin('o.elements', 'e')
;
}
$collection = $this->getViewBuilder()->buildViews(
$builder->getQuery()->getResult(),
$fractal->getRequestedIncludes()
);
$resource = new Collection($collection, $this->getOrderTransformer());
$pager = new Pagerfanta(new DoctrineORMAdapter($builder, false));
$pager->setCurrentPage($page);
$pager->setMaxPerPage($perPage);
$resource->setPaginator(new PagerfantaPaginatorAdapter($pager, $routeGenerator));
return $this->returnResourceResponse($request, $fractal, $resource);
}
/**
* @param Request $request
* @param int $orderId
* @return Response
*/
public function showAction(Request $request, $orderId)
{
$order = $this->findOr404($orderId);
$fractal = $this->buildFractalManager($request->get('includes', []));
if ($order->getUser()->getId() !== $this->getAuthenticatedUser()->getId()) {
throw new AccessDeniedHttpException(sprintf('Cannot access order "%d"', $order->getId()));
}
$model = $this->getViewBuilder()->buildView($order, $fractal->getRequestedIncludes());
$resource = new Item($model, $this->getOrderTransformer());
return $this->returnResourceResponse($request, $fractal, $resource);
}
public function acceptElementsAction(Request $request, $orderId)
{
$elementIds = $this->fetchElementIdsFromRequest($request);
$elements = $this->doAcceptElements($orderId, $elementIds, $this->getAuthenticatedUser());
$resource = new Collection($elements, function (BasketElement $element) {
return [
'id' => $element->getId(),
'created' => $element->getCreated(),
'databox_id' => $element->getSbasId(),
'record_id' => $element->getRecordId(),
'index' => $element->getOrd(),
];
});
return $this->returnResourceResponse($request, [], $resource);
}
public function denyElementsAction(Request $request, $orderId)
{
$elementIds = $this->fetchElementIdsFromRequest($request);
$this->doDenyElements($orderId, $elementIds, $this->getAuthenticatedUser());
return Result::create($request, [])->createResponse();
}
/**
* @param array $records
* @return \record_adapter[]
*/
private function toRequestedRecords(array $records)
{
$requestedRecords = [];
foreach ($records as $item) {
$requestedRecords[] = [
'databox_id' => $item->databox_id,
'record_id' => $item->record_id,
];
}
return RecordReferenceCollection::fromArrayOfArray($requestedRecords)->toRecords($this->getApplicationBox());
}
/**
* @param \record_adapter[] $records
* @return \record_adapter[]
*/
private function filterOrderableRecords(array $records)
{
$acl = $this->getAclForUser();
$filtered = [];
foreach ($records as $index => $record) {
if ($acl->has_right_on_base($record->getBaseId(), 'cancmd')) {
$filtered[$index] = $record;
}
}
return $filtered;
}
/**
* @return OrderTransformer
*/
private function getOrderTransformer()
{
return new OrderTransformer(new OrderElementTransformer($this->app['media_accessor.subdef_url_generator']));
}
/**
* @param string|array $includes
* @return Manager
*/
private function buildFractalManager($includes)
{
$fractal = new Manager();
$fractal->parseIncludes($includes ?: []);
return $fractal;
}
/**
* @param Request $request
* @param string|array|Manager $includes
* @param ResourceInterface $resource
* @return Response
*/
private function returnResourceResponse(Request $request, $includes, ResourceInterface $resource)
{
$fractal = $includes instanceof Manager ? $includes : $this->buildFractalManager($includes);
return Result::create($request, $fractal->createData($resource)->toArray())->createResponse();
}
/**
* @param Request $request
* @return array
*/
private function fetchElementIdsFromRequest(Request $request)
{
$data = $this->decodeJsonBody($request, 'orders.json#/definitions/order_element_collection');
$elementIds = [];
foreach ($data as $elementId) {
$elementIds[] = $elementId->id;
}
return $elementIds;
}
private function getViewBuilder()
{
return new OrderViewBuilder(
$this->app,
$this->getApplicationBox(),
$this->app['service.media_subdef']
);
}
}

View File

@@ -189,10 +189,12 @@ class BaseOrderController extends Controller
$references = new RecordReferenceCollection(); $references = new RecordReferenceCollection();
foreach ($elements as $element) { $basket->getElements()->forAll(function (BasketElement $element) use ($references) {
$reference = RecordReference::createFromDataboxIdAndRecordId($element->getSbasId(), $element->getRecordId()); $references->addRecordReference($element->getSbasId(), $element->getRecordId());
});
$references->addRecordReference($reference); foreach ($elements as $element) {
$references->addRecordReference($element->getSbasId(), $element->getRecordId());
} }
$groups = $references->groupPerDataboxId(); $groups = $references->groupPerDataboxId();

View File

@@ -0,0 +1,108 @@
<?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\Order;
use Alchemy\Phrasea\Media\MediaSubDefinitionUrlGenerator;
use League\Fractal\ParamBag;
use League\Fractal\TransformerAbstract;
class OrderElementTransformer extends TransformerAbstract
{
protected $availableIncludes = ['resource_links'];
private $validParams = ['ttl'];
/**
* @var MediaSubDefinitionUrlGenerator
*/
private $urlGenerator;
public function __construct(MediaSubDefinitionUrlGenerator $urlGenerator)
{
$this->urlGenerator = $urlGenerator;
}
public function transform(OrderElementView $model)
{
$element = $model->getElement();
$record = $model->getRecordReference();
$data = [
'id' => $element->getId(),
'record' => [
'databox_id' => $record->getDataboxId(),
'record_id' => $record->getRecordId(),
],
];
$data['status'] = 'pending';
if (null !== $element->getOrderMaster()) {
$data['validator_id'] = $element->getOrderMaster()->getId();
$data['status'] = $element->getDeny() ? 'rejected' : 'accepted';
}
return $data;
}
public function includeResourceLinks(OrderElementView $model, ParamBag $params = null)
{
$parameterArray = $this->extractParamBagValues($params);
$usedParams = array_keys(array_filter($parameterArray));
if (array_diff($usedParams, $this->validParams)) {
throw new \RuntimeException(sprintf(
'Invalid param(s): "%s". Valid param(s): "%s"',
implode(', ', $usedParams),
implode(', ', $this->validParams)
));
}
list ($ttl) = $parameterArray['ttl'];
if (null === $ttl) {
$ttl = $this->urlGenerator->getDefaultTTL();
}
$subdefs = $model->getOrderableMediaSubdefs();
$urls = $this->urlGenerator->generateMany($model->getAuthenticatedUser(), $subdefs, $ttl);
$data = array_map(null, $subdefs, $urls);
return $this->collection($data, function (array $data) use ($ttl) {
/** @var \media_subdef $subdef */
list($subdef, $url) = $data;
return [
'name' => $subdef->get_name(),
'url' => $url,
'url_ttl' => $ttl,
];
});
}
/**
* @param ParamBag|null $params
* @return array
*/
private function extractParamBagValues(ParamBag $params = null)
{
$array = array_fill_keys($this->validParams, null);
if ($params) {
array_walk($array, function (&$value, $key) use ($params) {
$value = $params[$key];
});
}
return $array;
}
}

View File

@@ -0,0 +1,95 @@
<?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\Order;
use Alchemy\Phrasea\Model\Entities\OrderElement;
use Alchemy\Phrasea\Model\Entities\User;
use Alchemy\Phrasea\Model\RecordReferenceInterface;
use Assert\Assertion;
class OrderElementView
{
/**
* @var OrderElement
*/
private $element;
/**
* @var RecordReferenceInterface
*/
private $record;
/**
* @var User
*/
private $user;
/**
* @var \media_subdef[]
*/
private $subdefs = [];
/**
* OrderElementViewModel constructor.
*
* @param OrderElement $element
* @param RecordReferenceInterface $record
* @param User $user
*/
public function __construct(OrderElement $element, RecordReferenceInterface $record, User $user)
{
$this->element = $element;
$this->record = $record;
$this->user = $user;
}
/**
* @return OrderElement
*/
public function getElement()
{
return $this->element;
}
/**
* @return RecordReferenceInterface
*/
public function getRecordReference()
{
return $this->record;
}
/**
* @return User
*/
public function getAuthenticatedUser()
{
return $this->user;
}
/**
* @param \media_subdef[] $subdefs
*/
public function setOrderableMediaSubdefs($subdefs)
{
Assertion::allIsInstanceOf($subdefs, \media_subdef::class);
$this->subdefs = $subdefs instanceof \Traversable ? iterator_to_array($subdefs) : $subdefs;
}
/**
* @return \media_subdef[]
*/
public function getOrderableMediaSubdefs()
{
return $this->subdefs;
}
}

View 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\Order;
use League\Fractal\TransformerAbstract;
class OrderTransformer extends TransformerAbstract
{
protected $availableIncludes = [
'elements',
];
/**
* @var OrderElementTransformer
*/
private $elementTransformer;
public function __construct(OrderElementTransformer $elementTransformer)
{
$this->elementTransformer = $elementTransformer;
}
public function transform(OrderView $view)
{
$order = $view->getOrder();
$data = [
'id' => (int)$order->getId(),
'owner_id' => (int)$order->getUser()->getId(),
'created' => $order->getCreatedOn()->format(DATE_ATOM),
'usage' => $order->getOrderUsage(),
'status' => 0 === $order->getTodo() ? 'finished' : 'pending'
];
if ($order->getDeadline()) {
$data['deadline'] = $order->getDeadline()->format(DATE_ATOM);
}
return $data;
}
public function includeElements(OrderView $order)
{
$elements = $order->getElements();
return $this->collection($elements, $this->elementTransformer);
}
}

View File

@@ -137,7 +137,7 @@ class OrderValidator
throw new \RuntimeException('At least one collection was not found.'); throw new \RuntimeException('At least one collection was not found.');
} }
$references->addRecordReference(RecordReference::createFromDataboxIdAndRecordId( $references->add(RecordReference::createFromDataboxIdAndRecordId(
$databoxIdMap[$orderElement->getBaseId()], $databoxIdMap[$orderElement->getBaseId()],
$orderElement->getRecordId() $orderElement->getRecordId()
)); ));

View 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\Order;
use Alchemy\Phrasea\Model\Entities\Order;
use Assert\Assertion;
class OrderView
{
/**
* @var Order
*/
private $order;
/**
* @var OrderElementView[]
*/
private $viewElements = [];
/**
* @param Order $order
*/
public function __construct(Order $order)
{
$this->order = $order;
}
/**
* @param OrderElementView[] $viewElements
*/
public function setViewElements($viewElements)
{
Assertion::allIsInstanceOf($viewElements, OrderElementView::class);
$this->viewElements = $viewElements instanceof \Traversable ? iterator_to_array($viewElements) : $viewElements;
}
/**
* @return Order
*/
public function getOrder()
{
return $this->order;
}
/**
* @return OrderElementView[]
*/
public function getElements()
{
return $this->viewElements;
}
}

View File

@@ -0,0 +1,221 @@
<?php
/*
* This file is part of alchemy/pipeline-component.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Phrasea\Order;
use Alchemy\Phrasea\Application;
use Alchemy\Phrasea\Databox\Subdef\MediaSubdefService;
use Alchemy\Phrasea\Model\Entities\Order;
use Alchemy\Phrasea\Model\Entities\OrderElement;
use Alchemy\Phrasea\Model\RecordReferenceInterface;
use Alchemy\Phrasea\Record\RecordReference;
use Alchemy\Phrasea\Record\RecordReferenceCollection;
use Assert\Assertion;
class OrderViewBuilder
{
/**
* @var Application
*/
private $application;
/**
* @var \appbox
*/
private $applicationBox;
/**
* @var MediaSubdefService
*/
private $mediaSubdefService;
/**
* @param Application $application
* @param \appbox $appbox
* @param MediaSubdefService $subdefService
*/
public function __construct(Application $application, \appbox $appbox, MediaSubdefService $subdefService)
{
$this->application = $application;
$this->applicationBox = $appbox;
$this->mediaSubdefService = $subdefService;
}
public function buildView(Order $order, array $includes)
{
$view = new OrderView($order);
$this->fillViews([$view], $includes);
return $view;
}
/**
* @param Order[] $orders
* @param string[] $includes
* @return OrderView[]
*/
public function buildViews(array $orders, array $includes)
{
Assertion::allIsInstanceOf($orders, Order::class);
$views = array_map(function (Order $order) {
return new OrderView($order);
}, $orders);
$this->fillViews($views, $includes);
return $views;
}
/**
* @param OrderView[] $views
* @param array $includes
* @return void
*/
private function fillViews(array $views, array $includes)
{
if (!in_array('elements', $includes, true)) {
return;
}
$elements = $this->gatherElements($views);
$allElements = $elements ? call_user_func_array('array_merge', $elements) : [];
$allElements = array_combine(
array_map(function (OrderElement $element) {
return $element->getId();
}, $allElements),
$allElements
);
if (!$allElements) {
return;
}
$collectionToDataboxMap = $this->mapBaseIdToDataboxId($allElements);
$records = RecordReferenceCollection::fromListExtractor(
$allElements,
function (OrderElement $element) use ($collectionToDataboxMap) {
return isset($collectionToDataboxMap[$element->getBaseId()])
? [$collectionToDataboxMap[$element->getBaseId()], $element->getRecordId()]
: null;
},
function (array $data) {
list ($databoxId, $recordId) = $data;
return RecordReference::createFromDataboxIdAndRecordId($databoxId, $recordId);
}
);
$this->createOrderElementViews($views, $elements, $records);
if (!in_array('elements.resource_links', $includes, true)) {
return;
}
// Load all records
$records->toRecords($this->applicationBox);
// Load all subdefs
$subdefs = $this->mediaSubdefService->findSubdefsFromRecordReferenceCollection($records);
\media_Permalink_Adapter::getMany($this->application, $subdefs);
$orderableSubdefs = [];
foreach ($subdefs as $subdef) {
$databoxId = $subdef->get_sbas_id();
$recordId = $subdef->get_record_id();
if (!isset($orderableSubdefs[$databoxId][$recordId])) {
$orderableSubdefs[$databoxId][$recordId] = [];
}
$orderableSubdefs[$databoxId][$recordId][] = $subdef;
}
foreach ($views as $model) {
foreach ($model->getElements() as $element) {
$databoxId = $collectionToDataboxMap[$element->getElement()->getBaseId()];
$recordId = $element->getElement()->getRecordId();
if (isset($orderableSubdefs[$databoxId][$recordId])) {
$element->setOrderableMediaSubdefs($orderableSubdefs[$databoxId][$recordId]);
}
}
}
}
/**
* @param OrderView[] $orderViews
* @return OrderElement[][]
*/
private function gatherElements(array $orderViews)
{
Assertion::allIsInstanceOf($orderViews, OrderView::class);
$elements = [];
foreach ($orderViews as $index => $orderView) {
$elements[$index] = $orderView->getOrder()->getElements()->toArray();
}
return $elements;
}
/**
* @param OrderElement[] $elements
* @return array
*/
private function mapBaseIdToDataboxId(array $elements)
{
$baseIds = array_keys(array_reduce($elements, function (array &$baseIds, OrderElement $element) {
$baseIds[$element->getBaseId()] = true;
return $baseIds;
}, []));
$collectionToDataboxMap = [];
foreach ($this->application['repo.collection-references']->findMany($baseIds) as $collectionReference) {
$collectionToDataboxMap[$collectionReference->getBaseId()] = $collectionReference->getDataboxId();
}
return $collectionToDataboxMap;
}
/**
* @param OrderView[] $orderViews
* @param OrderElement[][] $elements
* @param RecordReferenceInterface[]|RecordReferenceCollection $records
* @return void
*/
private function createOrderElementViews(array $orderViews, $elements, $records)
{
$user = $this->application->getAuthenticatedUser();
foreach ($orderViews as $index => $model) {
$models = [];
/** @var OrderElement $element */
foreach ($elements[$index] as $elementIndex => $element) {
if (isset($records[$element->getId()])) {
$models[$elementIndex] = new OrderElementView($element, $records[$element->getId()], $user);
}
}
$model->setViewElements($models);
}
}
}

View File

@@ -166,7 +166,7 @@ class PDF
$fimg = $subdef->getRealPath(); $fimg = $subdef->getRealPath();
if (!$this->app->getAclForUser($this->app->getAuthenticatedUser())->has_right_on_base($rec->get_base_id(), "nowatermark") if (!$this->app->getAclForUser($this->app->getAuthenticatedUser())->has_right_on_base($rec->getBaseId(), "nowatermark")
&& $subdef->get_type() == \media_subdef::TYPE_IMAGE) { && $subdef->get_type() == \media_subdef::TYPE_IMAGE) {
$fimg = \recordutils_image::watermark($this->app, $subdef); $fimg = \recordutils_image::watermark($this->app, $subdef);
} }
@@ -258,7 +258,7 @@ class PDF
$y = $this->pdf->GetY(); $y = $this->pdf->GetY();
$t = \phrasea::bas_labels($rec->get_base_id(), $this->app); $t = \phrasea::bas_labels($rec->getBaseId(), $this->app);
$this->pdf->SetFont(PhraseaPDF::FONT, '', 10); $this->pdf->SetFont(PhraseaPDF::FONT, '', 10);
$this->pdf->SetFillColor(220, 220, 220); $this->pdf->SetFillColor(220, 220, 220);
$this->pdf->SetLeftMargin($lmargin); $this->pdf->SetLeftMargin($lmargin);
@@ -339,10 +339,10 @@ class PDF
$RIGHT_TEXT = ""; $RIGHT_TEXT = "";
$RIGHT_IMG = NULL; $RIGHT_IMG = NULL;
$LEFT__IMG = $this->app['root.path'] . "/config/minilogos/logopdf_" . $rec->get_sbas_id() . ".jpg"; $LEFT__IMG = $this->app['root.path'] . "/config/minilogos/logopdf_" . $rec->getDataboxId() . ".jpg";
if (!is_file($LEFT__IMG)) { if (!is_file($LEFT__IMG)) {
$databox = $rec->get_databox(); $databox = $rec->getDatabox();
$str = $databox->get_sxml_structure(); $str = $databox->get_sxml_structure();
$vn = (string) ($str->pdfPrintLogo); $vn = (string) ($str->pdfPrintLogo);
if (($vn * 1) == 1) { if (($vn * 1) == 1) {
@@ -350,7 +350,7 @@ class PDF
} }
} }
$collection = \collection::getByBaseId($this->app, $rec->get_base_id()); $collection = \collection::getByBaseId($this->app, $rec->getBaseId());
$vn = ""; $vn = "";
if (false !== $str = simplexml_load_string($collection->get_prefs())) { if (false !== $str = simplexml_load_string($collection->get_prefs())) {
@@ -358,9 +358,9 @@ class PDF
} }
if ($vn == "" || $vn == "1") { if ($vn == "" || $vn == "1") {
$RIGHT_TEXT = \phrasea::bas_labels($rec->get_base_id(), $this->app); $RIGHT_TEXT = \phrasea::bas_labels($rec->getBaseId(), $this->app);
} elseif ($vn == "2") { } elseif ($vn == "2") {
$RIGHT_IMG = $this->app['root.path'] . "/config/minilogos/" . $rec->get_base_id(); $RIGHT_IMG = $this->app['root.path'] . "/config/minilogos/" . $rec->getBaseId();
} }
$xtmp = $this->pdf->GetX(); $xtmp = $this->pdf->GetX();
@@ -438,7 +438,7 @@ class PDF
$f = $subdef->getRealPath(); $f = $subdef->getRealPath();
if (!$this->app->getAclForUser($this->app->getAuthenticatedUser())->has_right_on_base($rec->get_base_id(), "nowatermark") if (!$this->app->getAclForUser($this->app->getAuthenticatedUser())->has_right_on_base($rec->getBaseId(), "nowatermark")
&& $subdef->get_type() == \media_subdef::TYPE_IMAGE) && $subdef->get_type() == \media_subdef::TYPE_IMAGE)
$f = \recordutils_image::watermark($this->app, $subdef); $f = \recordutils_image::watermark($this->app, $subdef);

View File

@@ -1,5 +1,5 @@
<?php <?php
/** /*
* This file is part of Phraseanet * This file is part of Phraseanet
* *
* (c) 2005-2016 Alchemy * (c) 2005-2016 Alchemy
@@ -13,7 +13,7 @@ namespace Alchemy\Phrasea\Record;
use Alchemy\Phrasea\Model\RecordReferenceInterface; use Alchemy\Phrasea\Model\RecordReferenceInterface;
use Assert\Assertion; use Assert\Assertion;
class RecordReferenceCollection implements \IteratorAggregate class RecordReferenceCollection implements \IteratorAggregate, \ArrayAccess
{ {
/** /**
* @param array<int|string,array> $records * @param array<int|string,array> $records
@@ -28,7 +28,7 @@ class RecordReferenceCollection implements \IteratorAggregate
foreach ($records as $index => $record) { foreach ($records as $index => $record) {
if (isset($record['id'])) { if (isset($record['id'])) {
$references[$index] = RecordReference::createFromRecordReference($record['id']); $references[$index] = RecordReference::createFromRecordReference($record['id']);
} elseif (isset($record['databox_id']) && isset($record['record_id'])) { } elseif (isset($record['databox_id'], $record['record_id'])) {
$references[$index] = RecordReference::createFromDataboxIdAndRecordId($record['databox_id'], $record['record_id']); $references[$index] = RecordReference::createFromDataboxIdAndRecordId($record['databox_id'], $record['record_id']);
} }
} }
@@ -36,6 +36,43 @@ class RecordReferenceCollection implements \IteratorAggregate
return new self($references); return new self($references);
} }
/**
* Append all RecordReferences extracted via call to extractor on each element
*
* @param array|\Traversable $list List of elements to process
* @param callable $extractor Extracts data from each element or return null if unavailable
* @param callable $creator Creates Reference from extracted data. no-op when null
* @return RecordReferenceCollection
*/
public static function fromListExtractor($list, callable $extractor, callable $creator = null)
{
Assertion::isTraversable($list);
$references = [];
if (null === $creator) {
$creator = function ($data) {
return $data;
};
}
foreach ($list as $index => $item) {
$data = $extractor($item);
if (null === $data) {
continue;
}
$reference = $creator($data);
if ($reference instanceof RecordReferenceInterface) {
$references[$index] = $reference;
}
}
return new self($references);
}
/** /**
* @var RecordReferenceInterface[] * @var RecordReferenceInterface[]
*/ */
@@ -53,13 +90,35 @@ class RecordReferenceCollection implements \IteratorAggregate
{ {
Assertion::allIsInstanceOf($references, RecordReferenceInterface::class); Assertion::allIsInstanceOf($references, RecordReferenceInterface::class);
$this->references = $references instanceof \Traversable ? iterator_to_array($references) : $references; $this->references = $references instanceof \Traversable ? iterator_to_array($references, true) : $references;
} }
public function addRecordReference(RecordReferenceInterface $reference) /**
* @param RecordReferenceInterface $reference
* @param null|string|int $index
*/
public function add(RecordReferenceInterface $reference, $index = null)
{ {
$this->references[] = $reference;
$this->groups = null; $this->groups = null;
if (null === $index) {
$this->references[] = $reference;
return;
}
$this->references[$index] = $reference;
}
/**
* @param int $databoxId
* @param int $recordId
* @param null|string|int $index
* @return void
*/
public function addRecordReference($databoxId, $recordId, $index = null)
{
$this->add(RecordReference::createFromDataboxIdAndRecordId($databoxId, $recordId), $index);
} }
public function getIterator() public function getIterator()
@@ -115,8 +174,48 @@ class RecordReferenceCollection implements \IteratorAggregate
} }
} }
ksort($records); $indexes = array_flip(array_keys($this->references));
return array_values($records); uksort($records, function ($keyA, $keyB) use ($indexes) {
$indexA = $indexes[$keyA];
$indexB = $indexes[$keyB];
if ($indexA < $indexB) {
return -1;
} elseif ($indexA > $indexB) {
return 1;
}
return 0;
});
return $records;
}
public function offsetExists($offset)
{
return isset($this->references[$offset]);
}
/**
* @param mixed $offset
* @return RecordReferenceInterface
*/
public function offsetGet($offset)
{
return $this->references[$offset];
}
public function offsetSet($offset, $value)
{
Assertion::isInstanceOf($value, RecordReferenceInterface::class);
$this->add($value, $offset);
}
public function offsetUnset($offset)
{
unset($this->references[$offset]);
$this->groups = null;
} }
} }

View File

@@ -268,7 +268,7 @@ class ElasticSearchEngine implements SearchEngineInterface
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function query($string, $offset, $perPage, SearchEngineOptions $options = null) public function query($string, SearchEngineOptions $options = null)
{ {
$options = $options ?: new SearchEngineOptions(); $options = $options ?: new SearchEngineOptions();
$context = $this->context_factory->createContext($options); $context = $this->context_factory->createContext($options);
@@ -282,13 +282,15 @@ class ElasticSearchEngine implements SearchEngineInterface
// ask ES to return field _version (incremental version number of document) // ask ES to return field _version (incremental version number of document)
$params['body']['version'] = true; $params['body']['version'] = true;
$params['body']['from'] = $offset; $params['body']['from'] = $options->getFirstResult();
$params['body']['size'] = $perPage; $params['body']['size'] = $options->getMaxResults();
if($this->options->getHighlight()) { if($this->options->getHighlight()) {
$params['body']['highlight'] = $this->buildHighlightRules($context); $params['body']['highlight'] = $this->buildHighlightRules($context);
} }
if ($aggs = $this->getAggregationQueryParams($options)) { $aggs = $this->getAggregationQueryParams($options);
if ($aggs) {
$params['body']['aggs'] = $aggs; $params['body']['aggs'] = $aggs;
} }
@@ -314,7 +316,7 @@ class ElasticSearchEngine implements SearchEngineInterface
$results, // ArrayCollection of results $results, // ArrayCollection of results
json_encode($query), json_encode($query),
$res['took'], // duration $res['took'], // duration
$offset, // offset start $options->getFirstResult(),
$res['hits']['total'], // available $res['hits']['total'], // available
$res['hits']['total'], // total $res['hits']['total'], // total
null, // error null, // error
@@ -369,16 +371,6 @@ class ElasticSearchEngine implements SearchEngineInterface
throw new RuntimeException('Elasticsearch engine currently does not support auto-complete.'); throw new RuntimeException('Elasticsearch engine currently does not support auto-complete.');
} }
/**
* {@inheritdoc}
*/
public function excerpt($query, $fields, \record_adapter $record, SearchEngineOptions $options = null)
{
//@todo implements
return array();
}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */

View File

@@ -30,6 +30,14 @@ class ElasticsearchSettingsFormType extends AbstractType
'label' => 'ElasticSearch index name', 'label' => 'ElasticSearch index name',
'constraints' => new NotBlank(), 'constraints' => new NotBlank(),
]) ])
->add('esSettingsDropIndexButton', 'button', [
'label' => "Drop index",
'attr' => ['data-id' => "esSettingsDropIndexButton"]
])
->add('esSettingsCreateIndexButton', 'button', [
'label' => "Create index",
'attr' => ['data-id' => "esSettingsCreateIndexButton"]
])
->add('shards', 'integer', [ ->add('shards', 'integer', [
'label' => 'Number of shards', 'label' => 'Number of shards',
'constraints' => new Range(['min' => 1]), 'constraints' => new Range(['min' => 1]),

Some files were not shown because too many files have changed in this diff Show More