diff --git a/lib/Alchemy/Phrasea/Application.php b/lib/Alchemy/Phrasea/Application.php
index d8ff6ec9f3..9c41c8465d 100644
--- a/lib/Alchemy/Phrasea/Application.php
+++ b/lib/Alchemy/Phrasea/Application.php
@@ -179,6 +179,7 @@ class Application extends SilexApplication
'fr' => 'Français',
'nl' => 'Dutch',
];
+
private static $flashTypes = ['warning', 'info', 'success', 'error'];
private $environment;
@@ -204,11 +205,6 @@ class Application extends SilexApplication
$this['charset'] = 'UTF-8';
mb_internal_encoding($this['charset']);
- !defined('JETON_MAKE_SUBDEF') ? define('JETON_MAKE_SUBDEF', 0x01) : '';
- !defined('JETON_WRITE_META_DOC') ? define('JETON_WRITE_META_DOC', 0x02) : '';
- !defined('JETON_WRITE_META_SUBDEF') ? define('JETON_WRITE_META_SUBDEF', 0x04) : '';
- !defined('JETON_WRITE_META') ? define('JETON_WRITE_META', 0x06) : '';
-
$this['debug'] = $this->share(function (Application $app) {
return Application::ENV_PROD !== $app->getEnvironment();
});
@@ -241,6 +237,7 @@ class Application extends SilexApplication
$this->register(new FeedServiceProvider());
$this->register(new FtpServiceProvider());
$this->register(new GeonamesServiceProvider());
+
$this['geonames.server-uri'] = $this->share(function (Application $app) {
return $app['conf']->get(['registry', 'webservices', 'geonames-server'], 'http://geonames.alchemyasp.com/');
});
@@ -487,6 +484,11 @@ class Application extends SilexApplication
$dispatcher->addSubscriber($app['phraseanet.maintenance-subscriber']);
$dispatcher->addSubscriber($app['phraseanet.cookie-disabler-subscriber']);
$dispatcher->addSubscriber($app['phraseanet.session-manager-subscriber']);
+ $dispatcher->addSubscriber($app['phraseanet.record-subscriber']);
+ $dispatcher->addSubscriber($app['phraseanet.story-subscriber']);
+ $dispatcher->addSubscriber($app['phraseanet.elasticsearch-subscriber']);
+ $dispatcher->addSubscriber($app['phraseanet.databox-subscriber']);
+ $dispatcher->addSubscriber($app['phraseanet.collection-subscriber']);
$dispatcher->addSubscriber(new PhraseaInstallSubscriber($app));
$dispatcher->addSubscriber(new FeedEntrySubscriber($app));
$dispatcher->addSubscriber(new RegistrationSubscriber($app));
diff --git a/lib/Alchemy/Phrasea/Border/Manager.php b/lib/Alchemy/Phrasea/Border/Manager.php
index aa99917690..8a5b18c1bb 100644
--- a/lib/Alchemy/Phrasea/Border/Manager.php
+++ b/lib/Alchemy/Phrasea/Border/Manager.php
@@ -13,6 +13,8 @@ namespace Alchemy\Phrasea\Border;
use Alchemy\Phrasea\Border\Checker\CheckerInterface;
use Alchemy\Phrasea\Border\Attribute\AttributeInterface;
+use Alchemy\Phrasea\Core\Event\RecordEvent\ChangeStatusEvent;
+use Alchemy\Phrasea\Core\PhraseaEvents;
use Alchemy\Phrasea\Metadata\Tag\TfArchivedate;
use Alchemy\Phrasea\Metadata\Tag\TfQuarantine;
use Alchemy\Phrasea\Metadata\Tag\TfBasename;
@@ -288,6 +290,8 @@ class Manager
case AttributeInterface::NAME_STATUS:
$element->set_binary_status(decbin(bindec($element->get_status()) | bindec($attribute->getValue())));
+ $this->app['dispatcher']->dispatch(PhraseaEvents::RECORD_CHANGE_STATUS, new ChangeStatusEvent($element));
+
break;
case AttributeInterface::NAME_STORY:
@@ -304,7 +308,6 @@ class Manager
$this->app['phraseanet.metadata-setter']->replaceMetadata($newMetadata, $element);
$element->rebuild_subdefs();
- $element->reindex();
return $element;
}
diff --git a/lib/Alchemy/Phrasea/Command/RecordAdd.php b/lib/Alchemy/Phrasea/Command/RecordAdd.php
index baaa01dabb..80e9103c18 100644
--- a/lib/Alchemy/Phrasea/Command/RecordAdd.php
+++ b/lib/Alchemy/Phrasea/Command/RecordAdd.php
@@ -13,6 +13,8 @@ namespace Alchemy\Phrasea\Command;
use Alchemy\Phrasea\Border\File;
use Alchemy\Phrasea\Border\Manager;
+use Alchemy\Phrasea\Core\Event\RecordEvent\CreateRecordEvent;
+use Alchemy\Phrasea\Core\PhraseaEvents;
use Alchemy\Phrasea\Model\Entities\LazaretFile;
use Alchemy\Phrasea\Model\Entities\LazaretSession;
use Symfony\Component\Console\Input\InputArgument;
@@ -115,7 +117,7 @@ class RecordAdd extends Command
"Record id %d on collection `%s` (databox `%s`) has been created", $elementCreated->get_record_id(), $elementCreated->get_collection()->get_label($this->container['locale']), $elementCreated->get_databox()->get_label($this->container['locale'])
)
);
- $this->container['phraseanet.SE']->addRecord($elementCreated);
+ $this->container['dispatcher']->dispatch(PhraseaEvents::RECORD_CREATE, new CreateRecordEvent($elementCreated));
} elseif ($elementCreated instanceof LazaretFile) {
$output->writeln(
sprintf("Quarantine item id %d has been created", $elementCreated->getId())
diff --git a/lib/Alchemy/Phrasea/Controller/Admin/Collection.php b/lib/Alchemy/Phrasea/Controller/Admin/Collection.php
index 1e937d8c7e..cbf662d64b 100644
--- a/lib/Alchemy/Phrasea/Controller/Admin/Collection.php
+++ b/lib/Alchemy/Phrasea/Controller/Admin/Collection.php
@@ -11,6 +11,8 @@
namespace Alchemy\Phrasea\Controller\Admin;
+use Alchemy\Phrasea\Core\Event\CollectionEvent\ChangeNameEvent;
+use Alchemy\Phrasea\Core\PhraseaEvents;
use Alchemy\Phrasea\Exception\RuntimeException;
use Silex\Application;
use Silex\ControllerProviderInterface;
@@ -718,6 +720,8 @@ class Collection implements ControllerProviderInterface
try {
$collection->set_name($name);
$success = true;
+
+ $app['dispatcher']->dispatch(PhraseaEvents::COLLECTION_CHANGE_NAME, new ChangeNameEvent($collection));
} catch (\Exception $e) {
}
diff --git a/lib/Alchemy/Phrasea/Controller/Admin/Fields.php b/lib/Alchemy/Phrasea/Controller/Admin/Fields.php
index 015dd54d8d..339e019522 100644
--- a/lib/Alchemy/Phrasea/Controller/Admin/Fields.php
+++ b/lib/Alchemy/Phrasea/Controller/Admin/Fields.php
@@ -11,6 +11,9 @@
namespace Alchemy\Phrasea\Controller\Admin;
+use Alchemy\Phrasea\Core\Event\DataboxEvent\UpdateStructureFieldEvent;
+use Alchemy\Phrasea\Core\Event\DataboxEvent\DeleteStructureFieldEvent;
+use Alchemy\Phrasea\Core\PhraseaEvents;
use Alchemy\Phrasea\Metadata\TagProvider;
use Alchemy\Phrasea\Vocabulary\Controller as VocabularyController;
use JMS\TranslationBundle\Annotation\Ignore;
@@ -114,6 +117,8 @@ class Fields implements ControllerProviderInterface
$this->updateFieldWithData($app, $field, $jsonField);
$field->save();
$fields[] = $field->toArray();
+
+ $app['dispatcher']->dispatch(PhraseaEvents::DATABOX_UPDATE_FIELD, new UpdateStructureFieldEvent($field, $jsonField));
} catch (\Exception $e) {
$connection->rollback();
$app->abort(500, $app->trans('Field %name% could not be saved, please try again or contact an admin.', ['%name%' => $jsonField['name']]));
@@ -278,13 +283,18 @@ class Fields implements ControllerProviderInterface
$this->updateFieldWithData($app, $field, $data);
$field->save();
+ $app['dispatcher']->dispatch(PhraseaEvents::DATABOX_UPDATE_FIELD, new UpdateStructureFieldEvent($databox, $field, $jsonField));
+
return $app->json($field->toArray());
}
public function deleteField(Application $app, $sbas_id, $id)
{
$databox = $app['phraseanet.appbox']->get_databox((int) $sbas_id);
- \databox_field::get_instance($app, $databox, $id)->delete();
+ $field = \databox_field::get_instance($app, $databox, $id);
+ $field->delete();
+
+ $app['dispatcher']->dispatch(PhraseaEvents::DATABOX_DELETE_FIELD, new DeleteStructureFieldEvent($databox, $field));
return new Response('', 204);
}
@@ -321,6 +331,7 @@ class Fields implements ControllerProviderInterface
private function updateFieldWithData(Application $app, \databox_field $field, array $data)
{
+
$field
->set_name($data['name'])
->set_thumbtitle($data['thumbtitle'])
diff --git a/lib/Alchemy/Phrasea/Controller/Admin/Root.php b/lib/Alchemy/Phrasea/Controller/Admin/Root.php
index 8379fca5cd..ee486062b2 100644
--- a/lib/Alchemy/Phrasea/Controller/Admin/Root.php
+++ b/lib/Alchemy/Phrasea/Controller/Admin/Root.php
@@ -11,6 +11,9 @@
namespace Alchemy\Phrasea\Controller\Admin;
+use Alchemy\Phrasea\Core\Event\DataboxEvent\DeleteStatusEvent;
+use Alchemy\Phrasea\Core\Event\DataboxEvent\UpdateStatusEvent;
+use Alchemy\Phrasea\Core\PhraseaEvents;
use Alchemy\Phrasea\Exception\SessionNotFound;
use Alchemy\Phrasea\Helper\DatabaseHelper;
use Alchemy\Phrasea\Helper\PathHelper;
@@ -322,12 +325,17 @@ class Root implements ControllerProviderInterface
$error = false;
+ $status = \databox_status::getStatus($app, $databox_id)[$bit];
+ $databox = $app['phraseanet.appbox']->get_databox((int) $databox_id);
+
try {
\databox_status::deleteStatus($app, $app['phraseanet.appbox']->get_databox($databox_id), $bit);
} catch (\Exception $e) {
$error = true;
}
+ $app['dispatcher']->dispatch(PhraseaEvents::DATABOX_DELETE_STATUS, new DeleteStatusEvent($databox, $status));
+
return $app->json(['success' => !$error]);
})
->bind('admin_statusbit_delete')
@@ -349,6 +357,8 @@ class Root implements ControllerProviderInterface
'labels_off' => $request->request->get('labels_off', []),
];
+ $databox = $app['phraseanet.appbox']->get_databox((int) $databox_id);
+
\databox_status::updateStatus($app, $databox_id, $bit, $properties);
if (null !== $request->request->get('delete_icon_off')) {
@@ -443,6 +453,10 @@ class Root implements ControllerProviderInterface
}
}
+ $status = \databox_status::getStatus($app, $databox_id)[$bit];
+
+ $app['dispatcher']->dispatch(PhraseaEvents::DATABOX_UPDATE_STATUS, new UpdateStatusEvent($databox, $status));
+
return $app->redirectPath('database_display_statusbit', ['databox_id' => $databox_id, 'success' => 1]);
})->assert('databox_id', '\d+')
->assert('bit', '\d+')
diff --git a/lib/Alchemy/Phrasea/Controller/Api/V1.php b/lib/Alchemy/Phrasea/Controller/Api/V1.php
index b95fca3071..ab892e58a3 100644
--- a/lib/Alchemy/Phrasea/Controller/Api/V1.php
+++ b/lib/Alchemy/Phrasea/Controller/Api/V1.php
@@ -11,6 +11,9 @@
namespace Alchemy\Phrasea\Controller\Api;
+use Alchemy\Phrasea\Core\Event\ChangeStatusEvent;
+use Alchemy\Phrasea\Core\Event\RecordEvent\ChangeMetadataEvent;
+use Alchemy\Phrasea\Core\Event\RecordEvent\CreateRecordEvent;
use Silex\ControllerProviderInterface;
use Alchemy\Phrasea\Cache\Cache as CacheInterface;
use Alchemy\Phrasea\Core\PhraseaEvents;
@@ -558,7 +561,8 @@ class V1 implements ControllerProviderInterface
if ($output instanceof \record_adapter) {
$ret['entity'] = '0';
$ret['url'] = '/records/' . $output->get_sbas_id() . '/' . $output->get_record_id() . '/';
- $app['phraseanet.SE']->addRecord($output);
+
+ $app['dispatcher']->dispatch(PhraseaEvents::RECORD_CREATE, new CreateRecordEvent($output));
}
if ($output instanceof LazaretFile) {
$ret['entity'] = '1';
@@ -829,6 +833,7 @@ class V1 implements ControllerProviderInterface
});
$record->set_metadatas($metadatas);
+ $app['dispatcher']->dispatch(PhraseaEvents::RECORD_CHANGE_METADATA, new ChangeMetadataEvent($record));
return Result::create($request, ["record_metadatas" => $this->list_record_caption($record->get_caption())])->createResponse();
}
@@ -861,7 +866,8 @@ class V1 implements ControllerProviderInterface
}
$record->set_binary_status(strrev($datas));
- $app['phraseanet.SE']->updateRecord($record);
+
+ $app['dispatcher']->dispatch(PhraseaEvents::RECORD_CHANGE_STATUS, new ChangeStatusEvent($record));
$ret = ["status" => $this->list_record_status($databox, $record->get_status())];
diff --git a/lib/Alchemy/Phrasea/Controller/Prod/Edit.php b/lib/Alchemy/Phrasea/Controller/Prod/Edit.php
index 5f31d96430..2db3b34742 100644
--- a/lib/Alchemy/Phrasea/Controller/Prod/Edit.php
+++ b/lib/Alchemy/Phrasea/Controller/Prod/Edit.php
@@ -11,6 +11,9 @@
namespace Alchemy\Phrasea\Controller\Prod;
+use Alchemy\Phrasea\Core\Event\RecordEvent\ChangeMetadataEvent;
+use Alchemy\Phrasea\Core\Event\RecordEvent\ChangeStatusEvent;
+use Alchemy\Phrasea\Core\PhraseaEvents;
use Alchemy\Phrasea\Vocabulary\Controller as VocabularyController;
use Alchemy\Phrasea\Controller\RecordsRequest;
use Alchemy\Phrasea\Metadata\Tag\TfEditdate;
@@ -347,6 +350,8 @@ class Edit implements ControllerProviderInterface
if (isset($rec['metadatas']) && is_array($rec['metadatas'])) {
$record->set_metadatas($rec['metadatas']);
+
+ $app['dispatcher']->dispatch(PhraseaEvents::RECORD_CHANGE_METADATA, new ChangeMetadataEvent($record));
}
/**
@@ -372,6 +377,8 @@ class Edit implements ControllerProviderInterface
];
$record->set_metadatas($metas, true);
+
+ $app['dispatcher']->dispatch(PhraseaEvents::RECORD_CHANGE_METADATA, new ChangeMetadataEvent($record));
}
$newstat = $record->get_status();
@@ -389,6 +396,8 @@ class Edit implements ControllerProviderInterface
}
$record->set_binary_status($newstat);
+
+ $app['dispatcher']->dispatch(PhraseaEvents::RECORD_CHANGE_STATUS, new ChangeStatusEvent($record));
}
$record
diff --git a/lib/Alchemy/Phrasea/Controller/Prod/Lazaret.php b/lib/Alchemy/Phrasea/Controller/Prod/Lazaret.php
index 44f63ff287..dd547b21a5 100644
--- a/lib/Alchemy/Phrasea/Controller/Prod/Lazaret.php
+++ b/lib/Alchemy/Phrasea/Controller/Prod/Lazaret.php
@@ -11,6 +11,10 @@
namespace Alchemy\Phrasea\Controller\Prod;
+use Alchemy\Phrasea\Core\Event\RecordEvent\ChangeMetadataEvent;
+use Alchemy\Phrasea\Core\Event\RecordEvent\ChangeStatusEvent;
+use Alchemy\Phrasea\Core\Event\RecordEvent\CreateRecordEvent;
+use Alchemy\Phrasea\Core\PhraseaEvents;
use Alchemy\Phrasea\Model\Entities\LazaretFile;
use Alchemy\Phrasea\Border;
use Alchemy\Phrasea\Border\Attribute\AttributeInterface;
@@ -195,7 +199,7 @@ class Lazaret implements ControllerProviderInterface
$lazaretFile->getSession(), $borderFile, $callBack, Border\Manager::FORCE_RECORD
);
- $app['phraseanet.SE']->addRecord($record);
+ $app['dispatcher']->dispatch(PhraseaEvents::RECORD_CREATE, new CreateRecordEvent($record));
if ($keepAttributes) {
//add attribute
@@ -229,6 +233,8 @@ class Lazaret implements ControllerProviderInterface
break;
case AttributeInterface::NAME_STATUS:
$record->set_binary_status($attribute->getValue());
+
+ $app['dispatcher']->dispatch(PhraseaEvents::RECORD_CHANGE_STATUS, new ChangeStatusEvent($record));
break;
case AttributeInterface::NAME_METAFIELD:
$metaFields->set($attribute->getField()->get_name(), $attribute->getValue());
@@ -241,6 +247,8 @@ class Lazaret implements ControllerProviderInterface
$fields = $metaFields->toMetadataArray($record->get_databox()->get_meta_structure());
$record->set_metadatas($fields);
+
+ $app['dispatcher']->dispatch(PhraseaEvents::RECORD_CHANGE_METADATA, new ChangeMetadataEvent($record));
}
//Delete lazaret file
diff --git a/lib/Alchemy/Phrasea/Controller/Prod/MoveCollection.php b/lib/Alchemy/Phrasea/Controller/Prod/MoveCollection.php
index 69eea98c43..6051036c2d 100644
--- a/lib/Alchemy/Phrasea/Controller/Prod/MoveCollection.php
+++ b/lib/Alchemy/Phrasea/Controller/Prod/MoveCollection.php
@@ -12,6 +12,8 @@
namespace Alchemy\Phrasea\Controller\Prod;
use Alchemy\Phrasea\Controller\RecordsRequest;
+use Alchemy\Phrasea\Core\Event\RecordEvent\ChangeCollectionEvent;
+use Alchemy\Phrasea\Core\PhraseaEvents;
use Silex\Application;
use Silex\ControllerProviderInterface;
use Symfony\Component\HttpFoundation\Request;
@@ -93,10 +95,15 @@ class MoveCollection implements ControllerProviderInterface
foreach ($records as $record) {
$record->move_to_collection($collection, $app['phraseanet.appbox']);
+ $app['dispatcher']->dispatch(PhraseaEvents::RECORD_CHANGE_COLLECTION, new ChangeCollectionEvent($record));
+
if ($request->request->get("chg_coll_son") == "1") {
foreach ($record->get_children() as $child) {
if ($app['acl']->get($app['authentication']->getUser())->has_right_on_base($child->get_base_id(), 'candeleterecord')) {
$child->move_to_collection($collection, $app['phraseanet.appbox']);
+
+
+ $app['dispatcher']->dispatch(PhraseaEvents::RECORD_CHANGE_COLLECTION, new ChangeCollectionEvent($child));
}
}
}
diff --git a/lib/Alchemy/Phrasea/Controller/Prod/Property.php b/lib/Alchemy/Phrasea/Controller/Prod/Property.php
index fde68a2d82..56e458050c 100644
--- a/lib/Alchemy/Phrasea/Controller/Prod/Property.php
+++ b/lib/Alchemy/Phrasea/Controller/Prod/Property.php
@@ -12,6 +12,8 @@
namespace Alchemy\Phrasea\Controller\Prod;
use Alchemy\Phrasea\Controller\RecordsRequest;
+use Alchemy\Phrasea\Core\Event\RecordEvent\ChangeStatusEvent;
+use Alchemy\Phrasea\Core\PhraseaEvents;
use Silex\Application;
use Silex\ControllerProviderInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
@@ -175,6 +177,7 @@ class Property implements ControllerProviderInterface
foreach ($record->get_children() as $child) {
if (null !== $updatedStatus = $this->updateRecordStatus($child, $postStatus)) {
$updated[$record->get_serialize_key()] = $updatedStatus;
+ $app['dispatcher']->dispatch(PhraseaEvents::RECORD_CHANGE_STATUS, new ChangeStatusEvent($record));
}
}
}
diff --git a/lib/Alchemy/Phrasea/Controller/Prod/Records.php b/lib/Alchemy/Phrasea/Controller/Prod/Records.php
index 40b02a4ecb..9cdbb1fd17 100644
--- a/lib/Alchemy/Phrasea/Controller/Prod/Records.php
+++ b/lib/Alchemy/Phrasea/Controller/Prod/Records.php
@@ -12,6 +12,8 @@
namespace Alchemy\Phrasea\Controller\Prod;
use Alchemy\Phrasea\Controller\RecordsRequest;
+use Alchemy\Phrasea\Core\Event\RecordEvent\DeleteStoryEvent;
+use Alchemy\Phrasea\Core\PhraseaEvents;
use Alchemy\Phrasea\SearchEngine\SearchEngineOptions;
use Silex\Application;
use Silex\ControllerProviderInterface;
@@ -174,6 +176,12 @@ class Records implements ControllerProviderInterface
$deleted[] = $record->get_serialize_key();
$record->delete();
+
+ if ($record->isStory()) {
+ $app['dispatcher']->dispatch(PhraseaEvents::STORY_DELETE, new DeleteStoryEvent($record));
+ } else {
+ $app['dispatcher']->dispatch(PhraseaEvents::RECORD_DELETE, new DeleteRecordEvent($record));
+ }
} catch (\Exception $e) {
}
diff --git a/lib/Alchemy/Phrasea/Controller/Prod/Story.php b/lib/Alchemy/Phrasea/Controller/Prod/Story.php
index 242d51ab9c..48b5f91deb 100644
--- a/lib/Alchemy/Phrasea/Controller/Prod/Story.php
+++ b/lib/Alchemy/Phrasea/Controller/Prod/Story.php
@@ -13,6 +13,8 @@ namespace Alchemy\Phrasea\Controller\Prod;
use Alchemy\Phrasea\Controller\Exception as ControllerException;
use Alchemy\Phrasea\Controller\RecordsRequest;
+use Alchemy\Phrasea\Core\Event\RecordEvent\ChangeMetadataEvent;
+use Alchemy\Phrasea\Core\PhraseaEvents;
use Alchemy\Phrasea\Model\Entities\StoryWZ;
use Silex\Application;
use Silex\ControllerProviderInterface;
@@ -74,6 +76,8 @@ class Story implements ControllerProviderInterface
$Story->set_metadatas($metadatas)->rebuild_subdefs();
+ $app['dispatcher']->dispatch(PhraseaEvents::RECORD_CHANGE_METADATA, new ChangeMetadataEvent($Story));
+
$StoryWZ = new StoryWZ();
$StoryWZ->setUser($app['authentication']->getUser());
$StoryWZ->setRecord($Story);
diff --git a/lib/Alchemy/Phrasea/Controller/Prod/Tools.php b/lib/Alchemy/Phrasea/Controller/Prod/Tools.php
index 3252a9f382..5faaf321b0 100644
--- a/lib/Alchemy/Phrasea/Controller/Prod/Tools.php
+++ b/lib/Alchemy/Phrasea/Controller/Prod/Tools.php
@@ -12,6 +12,8 @@
namespace Alchemy\Phrasea\Controller\Prod;
use Alchemy\Phrasea\Controller\RecordsRequest;
+use Alchemy\Phrasea\Core\Event\ChangeOriginalNameEvent;
+use Alchemy\Phrasea\Core\PhraseaEvents;
use Alchemy\Phrasea\Exception\RuntimeException;
use DataURI;
use PHPExiftool\Exception\ExceptionInterface as PHPExiftoolException;
@@ -149,7 +151,8 @@ class Tools implements ControllerProviderInterface
if ((int) $request->request->get('ccfilename') === 1) {
$record->set_original_name($fileName);
- $app['phraseanet.SE']->updateRecord($record);
+
+ $app['dispatcher']->dispatch(PhraseaEvents::RECORD_CHANGE_ORIGINAL_NAME, new ChangeOriginalNameEvent($record));
}
unlink($tempoFile);
rmdir($tempoDir);
diff --git a/lib/Alchemy/Phrasea/Controller/Prod/Upload.php b/lib/Alchemy/Phrasea/Controller/Prod/Upload.php
index 532810004b..e112c68bb8 100644
--- a/lib/Alchemy/Phrasea/Controller/Prod/Upload.php
+++ b/lib/Alchemy/Phrasea/Controller/Prod/Upload.php
@@ -14,6 +14,7 @@ namespace Alchemy\Phrasea\Controller\Prod;
use Alchemy\Phrasea\Border\File;
use Alchemy\Phrasea\Border\Attribute\Status;
use Alchemy\Phrasea\Core\Event\LazaretEvent;
+use Alchemy\Phrasea\Core\Event\RecordEvent\CreateRecordEvent;
use Alchemy\Phrasea\Core\PhraseaEvents;
use DataURI\Parser;
use DataURI\Exception\Exception as DataUriException;
@@ -207,7 +208,8 @@ class Upload implements ControllerProviderInterface
$id = $elementCreated->get_serialize_key();
$element = 'record';
$message = $app->trans('The record was successfully created');
- $app['phraseanet.SE']->addRecord($elementCreated);
+
+ $app['dispatcher']->dispatch(PhraseaEvents::RECORD_CREATE, new CreateRecordEvent($elementCreated));
// try to create thumbnail from data URI
if ('' !== $b64Image = $request->request->get('b64_image', '')) {
diff --git a/lib/Alchemy/Phrasea/Controller/Thesaurus/Xmlhttp.php b/lib/Alchemy/Phrasea/Controller/Thesaurus/Xmlhttp.php
index 34a3d7c809..a40bdd064f 100644
--- a/lib/Alchemy/Phrasea/Controller/Thesaurus/Xmlhttp.php
+++ b/lib/Alchemy/Phrasea/Controller/Thesaurus/Xmlhttp.php
@@ -11,6 +11,8 @@
namespace Alchemy\Phrasea\Controller\Thesaurus;
+use Alchemy\Phrasea\Core\Event\RecordEvent\ChangeMetadataEvent;
+use Alchemy\Phrasea\Core\PhraseaEvents;
use Alchemy\Phrasea\Model\Entities\User;
use Silex\Application;
use Silex\ControllerProviderInterface;
@@ -1494,6 +1496,9 @@ class Xmlhttp implements ControllerProviderInterface
if (count($metadatasd) > 0) {
if (!$request->get('debug')) {
$record->set_metadatas($metadatasd, true);
+
+ $app['dispatcher']->dispatch(PhraseaEvents::RECORD_CHANGE_METADATA, new ChangeMetadataEvent($record));
+
$ret['nRecsUpdated']++;
}
}
diff --git a/lib/Alchemy/Phrasea/Core/Event/CollectionEvent/ChangeNameEvent.php b/lib/Alchemy/Phrasea/Core/Event/CollectionEvent/ChangeNameEvent.php
new file mode 100644
index 0000000000..9885571905
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Core/Event/CollectionEvent/ChangeNameEvent.php
@@ -0,0 +1,17 @@
+collection = $collection;
+ }
+
+ /**
+ * @return \collection
+ */
+ public function getCollection()
+ {
+ return $this->collection;
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Core/Event/CollectionEvent/CollectionIndexEvent.php b/lib/Alchemy/Phrasea/Core/Event/CollectionEvent/CollectionIndexEvent.php
new file mode 100644
index 0000000000..b3f98ce46e
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Core/Event/CollectionEvent/CollectionIndexEvent.php
@@ -0,0 +1,17 @@
+databox = $databox;
+ }
+
+ /**
+ * @return \databox
+ */
+ public function getDatabox()
+ {
+ return $this->databox;
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Core/Event/DataboxEvent/DataboxIndexEvent.php b/lib/Alchemy/Phrasea/Core/Event/DataboxEvent/DataboxIndexEvent.php
new file mode 100644
index 0000000000..d8aba82669
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Core/Event/DataboxEvent/DataboxIndexEvent.php
@@ -0,0 +1,17 @@
+status = $status;
+
+ parent::__construct($databox);
+ }
+
+ public function getStatus()
+ {
+ return $this->status;
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Core/Event/DataboxEvent/DeleteStructureFieldEvent.php b/lib/Alchemy/Phrasea/Core/Event/DataboxEvent/DeleteStructureFieldEvent.php
new file mode 100644
index 0000000000..57f2b24372
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Core/Event/DataboxEvent/DeleteStructureFieldEvent.php
@@ -0,0 +1,32 @@
+field = $field;
+
+ parent::__construct($databox);
+ }
+
+ /**
+ * @return \databox_field
+ */
+ public function getField()
+ {
+ return $this->field;
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Core/Event/DataboxEvent/UpdateStatusEvent.php b/lib/Alchemy/Phrasea/Core/Event/DataboxEvent/UpdateStatusEvent.php
new file mode 100644
index 0000000000..04d5a011e9
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Core/Event/DataboxEvent/UpdateStatusEvent.php
@@ -0,0 +1,29 @@
+status = $status;
+
+ parent::__construct($databox);
+ }
+
+ public function getStatus()
+ {
+ return $this->status;
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Core/Event/DataboxEvent/UpdateStructureFieldEvent.php b/lib/Alchemy/Phrasea/Core/Event/DataboxEvent/UpdateStructureFieldEvent.php
new file mode 100644
index 0000000000..a04b65134b
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Core/Event/DataboxEvent/UpdateStructureFieldEvent.php
@@ -0,0 +1,42 @@
+field = $field;
+ $this->data = $data;
+
+ parent::__construct($databox);
+ }
+
+ /**
+ * @return \databox_field
+ */
+ public function getField()
+ {
+ return $this->field;
+ }
+
+ /**
+ * @return array
+ */
+ public function getData()
+ {
+ return $this->data;
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Core/Event/RecordEvent/BuildSubdefEvent.php b/lib/Alchemy/Phrasea/Core/Event/RecordEvent/BuildSubdefEvent.php
new file mode 100644
index 0000000000..f11d448de9
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Core/Event/RecordEvent/BuildSubdefEvent.php
@@ -0,0 +1,31 @@
+subDefName = $subDefName;
+ parent::__construct($record);
+ }
+
+ public function getSubDefName()
+ {
+ return $this->subDefName;
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Core/Event/RecordEvent/ChangeCollectionEvent.php b/lib/Alchemy/Phrasea/Core/Event/RecordEvent/ChangeCollectionEvent.php
new file mode 100644
index 0000000000..748c9780d0
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Core/Event/RecordEvent/ChangeCollectionEvent.php
@@ -0,0 +1,19 @@
+record = $record;
+ }
+
+ /**
+ * @return \record_adapter
+ */
+ public function getRecord()
+ {
+ return $this->record;
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Core/Event/RecordEvent/RecordIndexEvent.php b/lib/Alchemy/Phrasea/Core/Event/RecordEvent/RecordIndexEvent.php
new file mode 100644
index 0000000000..6f1a7e4f0b
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Core/Event/RecordEvent/RecordIndexEvent.php
@@ -0,0 +1,17 @@
+ 'onChangeName',
+ ];
+ }
+
+ public function onChangeName(ChangeNameEvent $event)
+ {
+ $collection = $event->getCollection();
+
+ $dispatcher = $event->getDispatcher();
+ $dispatcher->dispatch(PhraseaEvents::INDEX_COLLECTION, new CollectionIndexEvent($collection));
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Core/Event/Subscriber/DataboxSubscriber.php b/lib/Alchemy/Phrasea/Core/Event/Subscriber/DataboxSubscriber.php
new file mode 100644
index 0000000000..61bdce6e2b
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Core/Event/Subscriber/DataboxSubscriber.php
@@ -0,0 +1,68 @@
+ 'onUpdateField',
+ PhraseaEvents::DATABOX_DELETE_FIELD => 'onDeleteField',
+ PhraseaEvents::DATABOX_UPDATE_STATUS => 'onUpdateStatus',
+ PhraseaEvents::DATABOX_DELETE_STATUS => 'onDeleteStatus'
+ ];
+ }
+
+ public function onUpdateField(UpdateStructureFieldEvent $event)
+ {
+ $databox = $event->getDatabox();
+ $field = $event->getField();
+ $data = $event->getData();
+
+ if ((bool) $field->get_thumbtitle() !== (bool) $data['thumbtitle']) {
+ $dispatcher = $event->getDispatcher();
+ $dispatcher->dispatch(PhraseaEvents::INDEX_DATABOX, new DataboxIndexEvent($databox));
+ }
+ }
+
+ public function onDeleteField(UpdateStructureFieldEvent $event)
+ {
+ $databox = $event->getDatabox();
+
+ $dispatcher = $event->getDispatcher();
+ $dispatcher->dispatch(PhraseaEvents::INDEX_DATABOX, new DataboxIndexEvent($databox));
+ }
+
+ public function onUpdateStatus(UpdateStatusEvent $event)
+ {
+ $databox = $event->getDatabox();
+
+ $dispatcher = $event->getDispatcher();
+ $dispatcher->dispatch(PhraseaEvents::INDEX_DATABOX, new DataboxIndexEvent($databox));
+ }
+
+ public function onDeleteStatus(DeleteStatusEvent $event)
+ {
+ $databox = $event->getDatabox();
+
+ $dispatcher = $event->getDispatcher();
+ $dispatcher->dispatch(PhraseaEvents::INDEX_DATABOX, new DataboxIndexEvent($databox));
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Core/Event/Subscriber/ElasticsearchSubscriber.php b/lib/Alchemy/Phrasea/Core/Event/Subscriber/ElasticsearchSubscriber.php
new file mode 100644
index 0000000000..5eb8231460
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Core/Event/Subscriber/ElasticsearchSubscriber.php
@@ -0,0 +1,194 @@
+recordIndexer = $recordIndexer;
+ $this->termIndexer = $termIndexer;
+ $this->client = $client;
+ $this->appbox = $appbox;
+ $this->indexName = $indexName;
+
+ self::$recordsToAdd = new \SplObjectStorage();
+ self::$recordsToRemove = new \SplObjectStorage();
+ self::$recordsToUpdate = new \SplObjectStorage();
+ }
+
+ public static function getSubscribedEvents()
+ {
+ return [
+ PhraseaEvents::INDEX_DATABOX => ['onIndexDatabox', -12],
+ PhraseaEvents::INDEX_COLLECTION => ['onIndexCollection', -24],
+ PhraseaEvents::INDEX_NEW_RECORD => ['onIndexNewRecord', -48],
+ PhraseaEvents::INDEX_UPDATE_RECORD => ['onIndexUpdateRecord', -48],
+ PhraseaEvents::INDEX_REMOVE_RECORD => ['onIndexRemoveRecord', -48],
+ KernelEvents::FINISH_REQUEST => 'doFlushIndex',
+ ];
+ }
+
+ public function onIndexDatabox(DataboxIndexEvent $event)
+ {
+ $databox = $event->getDatabox();
+ $connection = $databox->get_connection();
+
+ // set 'to_index' fag where 'to_index' flag is not already set
+ // for given databox
+ // @todo sql query this should not be done right here
+ $sql = <<executeUpdate($sql, [
+ ':token' => PhraseaTokens::TOKEN_INDEX
+ ], [
+ \PDO::PARAM_INT
+ ]);
+ }
+
+ public function onIndexCollection(CollectionIndexEvent $event)
+ {
+ $collection = $event->getCollection();
+ $connection = $collection->get_connection();
+
+ // set 'to_index' fag where 'to_index' flag is not already set
+ // for given collection
+ // @todo sql query this should not be done right here
+ $sql = <<executeUpdate($sql, [
+ ':coll_id' => $collection->get_coll_id(),
+ ':token' => PhraseaTokens::TOKEN_INDEX
+ ], [
+ \PDO::PARAM_INT,
+ \PDO::PARAM_INT
+ ]);
+ }
+
+ public function onIndexUpdateRecord(RecordIndexEvent $event)
+ {
+ $record = $event->getRecord();
+ if (self::$recordsToUpdate->contains($record)) {
+ return;
+ }
+ self::$recordsToUpdate->attach($record);
+ }
+
+ public function onIndexNewRecord(RecordIndexEvent $event)
+ {
+ $record = $event->getRecord();
+ if (self::$recordsToAdd->contains($record)) {
+ return;
+ }
+ self::$recordsToAdd->attach($record);
+ }
+
+ public function onIndexRemoveRecord(RecordIndexEvent $event)
+ {
+ $record = $event->getRecord();
+ if (self::$recordsToRemove->contains($record)) {
+ return;
+ }
+ self::$recordsToRemove->attach($record);
+ }
+
+ public function doFlushIndex()
+ {
+ if (self::$recordsToUpdate->count() > 0) {
+ $this->doUpdateIndex();
+ }
+
+ if (self::$recordsToAdd->count() > 0) {
+ $this->doInsertIndex();
+ }
+
+ if (self::$recordsToRemove->count() > 0) {
+ $this->doDeleteIndex();
+ }
+ }
+
+ private function doUpdateIndex()
+ {
+ $this->doRecordAction(self::$recordsToUpdate, 'update');
+ }
+
+ private function doInsertIndex()
+ {
+ $this->doRecordAction(self::$recordsToAdd, 'index');
+ }
+
+ private function doDeleteIndex()
+ {
+ $this->doRecordAction(self::$recordsToRemove, 'delete');
+ }
+
+ private function doRecordAction(\SplObjectStorage $poolOfRecords, $action)
+ {
+ // filter by databox
+ $toIndex = [];
+ foreach ($poolOfRecords as $record) {
+ $toIndex[$record->get_sbas_id()][] = $record;
+ }
+
+ $bulk = new BulkOperation($this->client);
+ $bulk->setDefaultIndex($this->indexName);
+ $bulk->setAutoFlushLimit(200);
+
+ $recordHelper = new RecordHelper($this->appbox);
+
+ foreach($toIndex as $databoxId => $records) {
+ $databox = $this->appbox->get_databox($databoxId);
+ $fetcher = new RecordPoolFetcher($databox, $recordHelper, $records);
+
+ call_user_func_array([$this->recordIndexer, $action], [$bulk, $fetcher]);
+ }
+
+ // should we refresh ?
+ $this->client->indices()->refresh(['index' => $this->indexName]);
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Core/Event/Subscriber/RecordSubscriber.php b/lib/Alchemy/Phrasea/Core/Event/Subscriber/RecordSubscriber.php
new file mode 100644
index 0000000000..56fa008a03
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Core/Event/Subscriber/RecordSubscriber.php
@@ -0,0 +1,83 @@
+ 'onChangeCollection',
+ PhraseaEvents::RECORD_CREATE => 'onCreate',
+ PhraseaEvents::RECORD_DELETE => 'onDelete',
+ PhraseaEvents::RECORD_CHANGE_METADATA => 'onChangeMetadata',
+ PhraseaEvents::RECORD_CHANGE_STATUS => 'onChangeStatus',
+ PhraseaEvents::RECORD_BUILD_SUB_DEFINITION => 'onBuildSubDef',
+ PhraseaEvents::RECORD_CHANGE_ORIGINAL_NAME => 'onChangeOriginalName',
+ ];
+ }
+
+ public function onChangeCollection(ChangeCollectionEvent $event)
+ {
+ $dispatcher = $event->getDispatcher();
+ $dispatcher->dispatch(PhraseaEvents::INDEX_UPDATE_RECORD, new RecordIndexEvent($event->getRecord()));
+ }
+
+ public function onCreate(CreateRecordEvent $event)
+ {
+ $dispatcher = $event->getDispatcher();
+ $dispatcher->dispatch(PhraseaEvents::INDEX_NEW_RECORD, new RecordIndexEvent($event->getRecord()));
+ }
+
+ public function onDelete(DeleteRecordEvent $event)
+ {
+ $dispatcher = $event->getDispatcher();
+ $dispatcher->dispatch(PhraseaEvents::INDEX_REMOVE_RECORD, new RecordIndexEvent($event->getRecord()));
+ }
+
+ public function onChangeMetadata(ChangeMetadataEvent $event)
+ {
+ $dispatcher = $event->getDispatcher();
+ $dispatcher->dispatch(PhraseaEvents::INDEX_UPDATE_RECORD, new RecordIndexEvent($event->getRecord()));
+ }
+
+ public function onChangeStatus(ChangeStatusEvent $event)
+ {
+ $dispatcher = $event->getDispatcher();
+ $dispatcher->dispatch(PhraseaEvents::INDEX_UPDATE_RECORD, new RecordIndexEvent($event->getRecord()));
+ }
+
+ public function onBuildSubDef(BuildSubDefEvent $event)
+ {
+ if (in_array($event->getSubDefName(), ['thumbnail', 'thumbnailgif', 'preview'])) {
+ $dispatcher = $event->getDispatcher();
+ $dispatcher->dispatch(PhraseaEvents::INDEX_UPDATE_RECORD, new RecordIndexEvent($event->getRecord()));
+ }
+ }
+
+ public function onChangeOriginalName(ChangeOriginalNameEvent $event)
+ {
+ $dispatcher = $event->getDispatcher();
+ $dispatcher->dispatch(PhraseaEvents::INDEX_UPDATE_RECORD, new RecordIndexEvent($event->getRecord()));
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Core/Event/Subscriber/StorySubscriber.php b/lib/Alchemy/Phrasea/Core/Event/Subscriber/StorySubscriber.php
new file mode 100644
index 0000000000..e9b4bb16d3
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Core/Event/Subscriber/StorySubscriber.php
@@ -0,0 +1,46 @@
+indexer = $indexer;
+ }
+
+ public static function getSubscribedEvents()
+ {
+ return [
+ PhraseaEvents::STORY_CREATE => 'onCreate',
+ PhraseaEvents::STORY_DELETE => 'onDelete',
+ ];
+ }
+
+ public function onCreate(CreateStoryEvent $event)
+ {
+ $this->indexer->indexSingleRecord($event->getRecord());
+ }
+
+ public function onDelete(DeleteStoryEvent $event)
+ {
+ $this->indexer->deleteSingleRecord($event->getRecord());
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Core/PhraseaEvents.php b/lib/Alchemy/Phrasea/Core/PhraseaEvents.php
index e2c19c821f..5aed176147 100644
--- a/lib/Alchemy/Phrasea/Core/PhraseaEvents.php
+++ b/lib/Alchemy/Phrasea/Core/PhraseaEvents.php
@@ -29,7 +29,6 @@ final class PhraseaEvents
const ORDER_CREATE = 'order.create';
const ORDER_DELIVER = 'order.deliver';
const ORDER_DENY = 'order.deny';
- const COLLECTION_CREATE = 'collection.create';
const FEED_ENTRY_CREATE = 'feed-entry.create';
const REGISTRATION_CREATE = 'registration.create';
const REGISTRATION_AUTOREGISTER = 'registration.autoregister';
@@ -42,4 +41,31 @@ final class PhraseaEvents
const BRIDGE_UPLOAD_FAILURE = 'bridge.upload-failure';
const EXPORT_MAIL_FAILURE = 'export.mail-failure';
const EXPORT_CREATE = 'export.create';
+
+ const RECORD_CREATE = 'record.create';
+ const RECORD_DELETE = 'record.delete';
+ const STORY_CREATE = 'story.create';
+ const STORY_DELETE = 'story.delete';
+ const RECORD_CHANGE_COLLECTION = 'record.collection';
+ const RECORD_CHANGE_METADATA = 'record.metadata';
+ const RECORD_CHANGE_ORIGINAL_NAME = 'record.original_name';
+ const RECORD_CHANGE_STATUS = 'record.status';
+ const RECORD_BUILD_SUB_DEFINITION = 'record.sub_definition';
+ const RECORD_SUBSTITUTE = 'record.substitute';
+
+ const DATABOX_UPDATE_FIELD = 'databox.update.field';
+ const DATABOX_DELETE_FIELD = 'databox.delete.field';
+
+ const DATABOX_UPDATE_STATUS = 'databox.create.status';
+ const DATABOX_DELETE_STATUS = 'databox.delete.status';
+
+ const COLLECTION_CHANGE_NAME = 'collection.name';
+ const COLLECTION_CREATE = 'collection.create';
+
+ const INDEX_NEW_RECORD = 'index.new.record';
+ const INDEX_UPDATE_RECORD = 'index.update.record';
+ const INDEX_REMOVE_RECORD = 'index.remove.record';
+ const INDEX_COLLECTION = 'index.collection';
+ const INDEX_DATABOX = 'index.databox';
+ const INDEX_THESAURUS = 'index.thesaurus';
}
diff --git a/lib/Alchemy/Phrasea/Core/PhraseaTokens.php b/lib/Alchemy/Phrasea/Core/PhraseaTokens.php
new file mode 100644
index 0000000000..e0155bec6f
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Core/PhraseaTokens.php
@@ -0,0 +1,22 @@
+share(function (Application $app) {
return new ContentNegotiationSubscriber($app);
});
+ $app['phraseanet.record-subscriber'] = $app->share(function (Application $app) {
+ return new RecordSubscriber($app['elasticsearch.indexer.record_indexer']);
+ });
+ $app['phraseanet.story-subscriber'] = $app->share(function (Application $app) {
+ return new StorySubscriber($app['elasticsearch.indexer.record_indexer']);
+ });
+ $app['phraseanet.elasticsearch-subscriber'] = $app->share(function (Application $app) {
+ return new ElasticSearchSubscriber(
+ $app['elasticsearch.indexer.record_indexer'],
+ $app['elasticsearch.indexer.term_indexer'],
+ $app['elasticsearch.client'],
+ $app['phraseanet.appbox'],
+ $app['elasticsearch.options']['index']
+ );
+ });
+ $app['phraseanet.databox-subscriber'] = $app->share(function (Application $app) {
+ return new DataboxSubscriber();
+ });
+ $app['phraseanet.collection-subscriber'] = $app->share(function (Application $app) {
+ return new CollectionSubscriber();
+ });
}
public function boot(Application $app)
diff --git a/lib/Alchemy/Phrasea/Core/Provider/PhraseanetServiceProvider.php b/lib/Alchemy/Phrasea/Core/Provider/PhraseanetServiceProvider.php
index e718cc0854..f333e4074f 100644
--- a/lib/Alchemy/Phrasea/Core/Provider/PhraseanetServiceProvider.php
+++ b/lib/Alchemy/Phrasea/Core/Provider/PhraseanetServiceProvider.php
@@ -69,7 +69,7 @@ class PhraseanetServiceProvider implements ServiceProviderInterface
});
$app['phraseanet.metadata-setter'] = $app->share(function (SilexApplication $app) {
- return new PhraseanetMetadataSetter();
+ return new PhraseanetMetadataSetter($app['dispatcher']);
});
$app['phraseanet.user-query'] = function (SilexApplication $app) {
diff --git a/lib/Alchemy/Phrasea/Core/Provider/SubdefServiceProvider.php b/lib/Alchemy/Phrasea/Core/Provider/SubdefServiceProvider.php
index 2aa5907454..6210c72e5e 100644
--- a/lib/Alchemy/Phrasea/Core/Provider/SubdefServiceProvider.php
+++ b/lib/Alchemy/Phrasea/Core/Provider/SubdefServiceProvider.php
@@ -25,7 +25,7 @@ class SubdefServiceProvider implements ServiceProviderInterface
return new SubdefGenerator($app, $app['media-alchemyst'], $app['filesystem'], $app['mediavorus'], isset($app['task-manager.logger']) ? $app['task-manager.logger'] : $app['monolog']);
});
$app['subdef.substituer'] = $app->share(function (SilexApplication $app) {
- return new SubdefSubstituer($app, $app['filesystem'], $app['media-alchemyst'], $app['mediavorus']);
+ return new SubdefSubstituer($app, $app['filesystem'], $app['media-alchemyst'], $app['mediavorus'], $app['dispatcher']);
});
}
diff --git a/lib/Alchemy/Phrasea/Core/Provider/TasksServiceProvider.php b/lib/Alchemy/Phrasea/Core/Provider/TasksServiceProvider.php
index 3d8f610783..6cbaccba01 100644
--- a/lib/Alchemy/Phrasea/Core/Provider/TasksServiceProvider.php
+++ b/lib/Alchemy/Phrasea/Core/Provider/TasksServiceProvider.php
@@ -15,6 +15,7 @@ use Alchemy\Phrasea\TaskManager\Job\FtpJob;
use Alchemy\Phrasea\TaskManager\Job\ArchiveJob;
use Alchemy\Phrasea\TaskManager\Job\BridgeJob;
use Alchemy\Phrasea\TaskManager\Job\FtpPullJob;
+use Alchemy\Phrasea\TaskManager\Job\IndexerJob;
use Alchemy\Phrasea\TaskManager\Job\PhraseanetIndexerJob;
use Alchemy\Phrasea\TaskManager\Job\RecordMoverJob;
use Alchemy\Phrasea\TaskManager\Job\SubdefsJob;
@@ -78,6 +79,7 @@ class TasksServiceProvider implements ServiceProviderInterface
new SubdefsJob($app['dispatcher'], $logger, $app['translator']),
new WriteMetadataJob($app['dispatcher'], $logger, $app['translator']),
new WebhookJob($app['dispatcher'], $logger, $app['translator']),
+ new IndexerJob($app['dispatcher'], $logger, $app['translator']),
];
});
}
diff --git a/lib/Alchemy/Phrasea/Media/SubdefGenerator.php b/lib/Alchemy/Phrasea/Media/SubdefGenerator.php
index b77269543a..c30f0c6fdd 100644
--- a/lib/Alchemy/Phrasea/Media/SubdefGenerator.php
+++ b/lib/Alchemy/Phrasea/Media/SubdefGenerator.php
@@ -12,7 +12,8 @@
namespace Alchemy\Phrasea\Media;
use Alchemy\Phrasea\Application;
-use Alchemy\Phrasea\SearchEngine\Elastic\ElasticSearchEngine;
+use Alchemy\Phrasea\Core\Event\RecordEvent\BuildSubDefEvent;
+use Alchemy\Phrasea\Core\PhraseaEvents;
use MediaAlchemyst\Alchemyst;
use MediaAlchemyst\Specification\SpecificationInterface;
use MediaVorus\MediaVorus;
@@ -74,9 +75,7 @@ class SubdefGenerator
$record->clearSubdefCache($subdefname);
- if ($this->app['phraseanet.SE'] instanceof ElasticSearchEngine && in_array($subdefname, ['thumbnail', 'thumbnailgif', 'preview'])) {
- $this->app['phraseanet.SE']->updateRecord($record);
- }
+ $this->app['dispatcher']->dispatch(PhraseaEvents::RECORD_BUILD_SUB_DEFINITION, new BuildSubDefEvent($record, $subDefName));
}
return $this;
diff --git a/lib/Alchemy/Phrasea/Media/SubdefSubstituer.php b/lib/Alchemy/Phrasea/Media/SubdefSubstituer.php
index f5ac1a134c..aaa542fc91 100644
--- a/lib/Alchemy/Phrasea/Media/SubdefSubstituer.php
+++ b/lib/Alchemy/Phrasea/Media/SubdefSubstituer.php
@@ -12,10 +12,12 @@
namespace Alchemy\Phrasea\Media;
use Alchemy\Phrasea\Application;
+use Alchemy\Phrasea\Core\PhraseaEvents;
use MediaAlchemyst\Alchemyst;
use MediaAlchemyst\Exception\ExceptionInterface as MediaAlchemystException;
use MediaVorus\Media\MediaInterface;
use MediaVorus\MediaVorus;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Filesystem\Filesystem;
class SubdefSubstituer
@@ -24,12 +26,13 @@ class SubdefSubstituer
private $fs;
private $mediavorus;
- public function __construct(Application $app, Filesystem $fs, Alchemyst $alchemyst, MediaVorus $mediavorus)
+ public function __construct(Application $app, Filesystem $fs, Alchemyst $alchemyst, MediaVorus $mediavorus, EventDispatcherInterface $dispatcher)
{
$this->alchemyst = $alchemyst;
$this->app = $app;
$this->fs = $fs;
$this->mediavorus = $mediavorus;
+ $this->dispatcher = $dispatcher;
}
public function substitute(\record_adapter $record, $name, MediaInterface $media)
@@ -92,5 +95,7 @@ class SubdefSubstituer
if ($name == 'document') {
$record->rebuild_subdefs();
}
+
+ $this->dispatcher->dispatch(PhraseaEvents::RECORD_SUBSTITUTE, new SubstituteRecordEvent($record));
}
}
diff --git a/lib/Alchemy/Phrasea/Metadata/PhraseanetMetadataSetter.php b/lib/Alchemy/Phrasea/Metadata/PhraseanetMetadataSetter.php
index 153d87bcf5..4f6c11fa90 100644
--- a/lib/Alchemy/Phrasea/Metadata/PhraseanetMetadataSetter.php
+++ b/lib/Alchemy/Phrasea/Metadata/PhraseanetMetadataSetter.php
@@ -12,9 +12,17 @@
namespace Alchemy\Phrasea\Metadata;
use PHPExiftool\Driver\Metadata\Metadata;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
class PhraseanetMetadataSetter
{
+ private $dispatcher;
+
+ public function __construct(EventDispatcherInterface $dispatcher)
+ {
+ $this->disptatcher = $dispatcher;
+ }
+
public function replaceMetadata($metadataCollection, \record_adapter $record)
{
$metadatas = [];
@@ -93,6 +101,8 @@ class PhraseanetMetadataSetter
if (count($metas) > 0) {
$record->set_metadatas($metas, true);
+
+ $this->disptatcher->dispatch(PhraseaEvents::RECORD_CHANGE_METADATA, new ChangeMetadataEvent($record));
}
}
}
diff --git a/lib/Alchemy/Phrasea/SearchEngine/Elastic/BulkOperation.php b/lib/Alchemy/Phrasea/SearchEngine/Elastic/BulkOperation.php
index 1e8eb5ed21..9d6b69638c 100644
--- a/lib/Alchemy/Phrasea/SearchEngine/Elastic/BulkOperation.php
+++ b/lib/Alchemy/Phrasea/SearchEngine/Elastic/BulkOperation.php
@@ -49,11 +49,7 @@ class BulkOperation
public function index(array $params)
{
- $header = array();
- $header['_id'] = igorw\get_in($params, ['id']);
- $header['_index'] = igorw\get_in($params, ['index']);
- $header['_type'] = igorw\get_in($params, ['type']);
- $this->stack[] = ['index' => $header];
+ $this->stack[] = ['index' => $this->getBulkHeader($params)];
$this->stack[] = igorw\get_in($params, ['body']);
if ($this->flushLimit === count($this->stack) / 2) {
@@ -61,6 +57,25 @@ class BulkOperation
}
}
+ public function update(array $params)
+ {
+ $this->stack[] = ['update' => $this->getBulkHeader($params)];
+ $this->stack[] = ['doc' => igorw\get_in($params, ['doc'])];
+
+ if ($this->flushLimit === count($this->stack) / 2) {
+ $this->flush();
+ }
+ }
+
+ public function delete(array $params)
+ {
+ $this->stack[] = ['delete' => $this->getBulkHeader($params)];
+
+ if ($this->flushLimit === count($this->stack) / 2) {
+ $this->flush();
+ }
+ }
+
public function flush()
{
// Do not try to flush an empty stack
@@ -76,7 +91,11 @@ class BulkOperation
}
}
$params['body'] = $this->stack;
- printf("ES Bulk query with %d items\n", count($this->stack) / 2);
+
+ if (php_sapi_name() === 'cli') {
+ printf("ES Bulk query with %d items\n", count($this->stack) / 2);
+ }
+
$response = $this->client->bulk($params);
$this->stack = array();
@@ -89,4 +108,15 @@ class BulkOperation
throw new Exception('Errors occurred during bulk indexing request, index may be in an inconsistent state');
}
}
+
+ private function getBulkHeader(array $params)
+ {
+ $header = [];
+ $header['_id'] = igorw\get_in($params, ['id']);
+ $header['_index'] = igorw\get_in($params, ['index']);
+ $header['_type'] = igorw\get_in($params, ['type']);
+
+ return $header;
+ }
+
}
diff --git a/lib/Alchemy/Phrasea/SearchEngine/Elastic/ElasticSearchEngine.php b/lib/Alchemy/Phrasea/SearchEngine/Elastic/ElasticSearchEngine.php
index 590d125a71..77ba9ce774 100644
--- a/lib/Alchemy/Phrasea/SearchEngine/Elastic/ElasticSearchEngine.php
+++ b/lib/Alchemy/Phrasea/SearchEngine/Elastic/ElasticSearchEngine.php
@@ -188,7 +188,7 @@ class ElasticSearchEngine implements SearchEngineInterface
*/
public function addRecord(\record_adapter $record)
{
- $this->app['elasticsearch.indexer.record_indexer']->indexSingleRecord($record, $this->indexName);
+ $this->app['elasticsearch.indexer.record_indexer']->indexSingleRecord($record);
return $this;
}
diff --git a/lib/Alchemy/Phrasea/SearchEngine/Elastic/RecordFetcher.php b/lib/Alchemy/Phrasea/SearchEngine/Elastic/Fetcher/AbstractRecordFetcher.php
similarity index 61%
rename from lib/Alchemy/Phrasea/SearchEngine/Elastic/RecordFetcher.php
rename to lib/Alchemy/Phrasea/SearchEngine/Elastic/Fetcher/AbstractRecordFetcher.php
index f2e9aeace6..b85ef84f78 100644
--- a/lib/Alchemy/Phrasea/SearchEngine/Elastic/RecordFetcher.php
+++ b/lib/Alchemy/Phrasea/SearchEngine/Elastic/Fetcher/AbstractRecordFetcher.php
@@ -9,79 +9,75 @@
* file that was distributed with this source code.
*/
-namespace Alchemy\Phrasea\SearchEngine\Elastic;
+namespace Alchemy\Phrasea\SearchEngine\Elastic\Fetcher;
+use Alchemy\Phrasea\SearchEngine\Elastic\RecordHelper;
use Alchemy\Phrasea\SearchEngine\SearchEngineInterface;
use Doctrine\DBAL\Connection;
use databox;
-use PDO;
-class RecordFetcher
+abstract class AbstractRecordFetcher
{
- private $connection;
- private $statementRecords;
+ protected $statementRecords;
+ protected $connection;
+
+ protected $offset = 0;
+ protected $batchSize = 1;
+
private $helper;
+ private $databox;
- private $offset = 0;
- private $batchSize = 1;
-
- private $databoxId;
+ private $postFetch;
public function __construct(databox $databox, RecordHelper $helper)
{
$this->connection = $databox->get_connection();
- $this->databoxId = $databox->get_sbas_id();
- $this->helper = $helper;
+ $this->databox = $databox;
+ $this->helper = $helper;
}
public function fetch()
{
$statementRecords = $this->statementRecords();
- // Fetch records rows
- $statementRecords->execute();
- printf("Query %d/%d -> %d rows\n", $this->offset, $this->batchSize, $statementRecords->rowCount());
+ if (php_sapi_name() === 'cli' && ($this->offset !== 0 || $statementRecords->rowCount() <= 0)) {
+ printf("Query %d/%d -> %d rows on database %s\n", $this->offset, $this->batchSize, $statementRecords->rowCount(), $this->databox->get_dbname());
+ }
+
$records = [];
while ($record = $statementRecords->fetch()) {
$records[$record['record_id']] = $record;
- printf("Record found (#%d)\n", $record['record_id']);
$this->offset++;
}
if (count($records) < 1) {
- printf("End of records\n");
+ if (php_sapi_name() === 'cli') {
+ printf("End of records\n");
+ }
- return false; // End
+ return false;
}
$this->addTitleToRecord($records);
$this->addMetadataToRecords($records);
- $this->addSubdefsToRecord($records);
+ $this->addSubDefinitionsToRecord($records);
// Hydrate records
foreach ($records as $key => $record) {
$records[$key] = $this->hydrate($record);
}
+ if (is_callable($this->postFetch)) {
+ call_user_func($this->postFetch, $records);
+ }
+
return $records;
}
- public function fetchOne(\record_adapter $record_adapter)
+ public function setPostFetch(\Closure $callable)
{
- $stmt = $this->statementRecord($record_adapter->get_record_id());
- $stmt->execute();
-
- $records = $stmt->fetchAll();
- $this->addTitleToRecord($records);
- $this->addMetadataToRecords($records);
- $this->addSubdefsToRecord($records);
-
- foreach ($records as $key => $record) {
- $records[$key] = $this->hydrate($record);
- }
-
- return array_pop($records);
+ $this->postFetch = $callable;
}
public function setBatchSize($size)
@@ -98,9 +94,9 @@ class RecordFetcher
$record['record_id'] = (int) $record['record_id'];
$record['collection_id'] = (int) $record['collection_id'];
// Some identifiers
- $record['id'] = $this->helper->getUniqueRecordId($this->databoxId, $record['record_id']);
- $record['base_id'] = $this->helper->getUniqueCollectionId($this->databoxId, $record['collection_id']);
- $record['databox_id'] = $this->databoxId;
+ $record['id'] = $this->helper->getUniqueRecordId($this->databox->get_sbas_id(), $record['record_id']);
+ $record['base_id'] = $this->helper->getUniqueCollectionId($this->databox->get_sbas_id(), $record['collection_id']);
+ $record['databox_id'] = $this->databox->get_sbas_id();
if ((int) $record['parent_record_id'] === 1) {
$record['record_type'] = SearchEngineInterface::GEM_TYPE_STORY;
@@ -117,63 +113,6 @@ class RecordFetcher
return $record;
}
- private function statementRecords()
- {
- if (!$this->statementRecords) {
- $sql = <<connection->prepare($sql);
- $statement->bindParam(':offset', $this->offset, PDO::PARAM_INT);
- $statement->bindParam(':limit', $this->batchSize, PDO::PARAM_INT);
- $this->statementRecords = $statement;
- }
-
- return $this->statementRecords;
- }
-
- private function statementRecord($id)
- {
- $sql = <<connection->prepare($sql);
- $statement->bindValue(':id', $id, PDO::PARAM_INT);
-
- return $statement;
- }
-
private function execStatementMetadata($ids)
{
$sql = <<execStatementSubdefs(array_keys($records));
+ $statementSubDef = $this->execStatementSubDefinitions(array_keys($records));
- while ($subdefs = $statementSubdef->fetch()) {
- $records[$subdefs['record_id']]['subdefs'][$subdefs['name']] = array(
- 'path' => $subdefs['path'],
- 'width' => $subdefs['width'],
- 'height' => $subdefs['height'],
+ while ($subDefinitions = $statementSubDef->fetch()) {
+ $records[$subDefinitions['record_id']]['subdefs'][$subDefinitions['name']] = array(
+ 'path' => $subDefinitions['path'],
+ 'width' => $subDefinitions['width'],
+ 'height' => $subDefinitions['height'],
);
}
}
- private function execStatementSubdefs($ids)
+ private function execStatementSubDefinitions($ids)
{
$sql = <<connection->executeQuery($sql, array($ids), array(Connection::PARAM_INT_ARRAY));
}
+
+ /** Provides PDO Statement that fetches records */
+ abstract protected function statementRecords();
}
diff --git a/lib/Alchemy/Phrasea/SearchEngine/Elastic/Fetcher/RecordFetcher.php b/lib/Alchemy/Phrasea/SearchEngine/Elastic/Fetcher/RecordFetcher.php
new file mode 100644
index 0000000000..9293ab73c9
--- /dev/null
+++ b/lib/Alchemy/Phrasea/SearchEngine/Elastic/Fetcher/RecordFetcher.php
@@ -0,0 +1,47 @@
+connection->executeQuery($sql, [
+ $this->offset,
+ $this->batchSize,
+ ], [
+ PDO::PARAM_INT,
+ PDO::PARAM_INT,
+ ]);
+ }
+}
diff --git a/lib/Alchemy/Phrasea/SearchEngine/Elastic/Fetcher/RecordPoolFetcher.php b/lib/Alchemy/Phrasea/SearchEngine/Elastic/Fetcher/RecordPoolFetcher.php
new file mode 100644
index 0000000000..01d442b428
--- /dev/null
+++ b/lib/Alchemy/Phrasea/SearchEngine/Elastic/Fetcher/RecordPoolFetcher.php
@@ -0,0 +1,72 @@
+pool = $records;
+
+ parent::__construct($databox, $helper);
+ }
+
+ protected function statementRecords()
+ {
+ $sql = <<get_record_id();
+ }, $this->pool);
+
+ return $this->connection->executeQuery($sql, [
+ $records,
+ $this->offset,
+ $this->batchSize
+ ], [
+ Connection::PARAM_INT_ARRAY,
+ PDO::PARAM_INT,
+ PDO::PARAM_INT,
+ ]);
+ }
+}
diff --git a/lib/Alchemy/Phrasea/SearchEngine/Elastic/Fetcher/ScheduledIndexationRecordFetcher.php b/lib/Alchemy/Phrasea/SearchEngine/Elastic/Fetcher/ScheduledIndexationRecordFetcher.php
new file mode 100644
index 0000000000..6a5a4e0d98
--- /dev/null
+++ b/lib/Alchemy/Phrasea/SearchEngine/Elastic/Fetcher/ScheduledIndexationRecordFetcher.php
@@ -0,0 +1,56 @@
+ 0
+ AND (jeton & ?) = 0
+ ORDER BY r.record_id ASC
+ LIMIT ?, ?
+SQL;
+
+ $params = [
+ PhraseaTokens::TOKEN_INDEX,
+ PhraseaTokens::TOKEN_INDEXING,
+ $this->offset,
+ $this->batchSize,
+ ];
+
+ return $this->connection->executeQuery($sql, $params, [
+ PDO::PARAM_INT,
+ PDO::PARAM_INT,
+ PDO::PARAM_INT,
+ PDO::PARAM_INT,
+ ]);
+ }
+}
diff --git a/lib/Alchemy/Phrasea/SearchEngine/Elastic/Fetcher/SingleRecordFetcher.php b/lib/Alchemy/Phrasea/SearchEngine/Elastic/Fetcher/SingleRecordFetcher.php
new file mode 100644
index 0000000000..a6145b087d
--- /dev/null
+++ b/lib/Alchemy/Phrasea/SearchEngine/Elastic/Fetcher/SingleRecordFetcher.php
@@ -0,0 +1,63 @@
+record = $record;
+
+ parent::__construct($databox, $helper);
+ }
+
+ public function fetch()
+ {
+ $records = parent::fetch();
+
+ return array_pop($records);
+ }
+
+ protected function statementRecords()
+ {
+ $sql = <<connection->executeQuery($sql, [':record_id' => $this->record->get_record_id()], [PDO::PARAM_INT,]);
+ }
+}
diff --git a/lib/Alchemy/Phrasea/SearchEngine/Elastic/Indexer.php b/lib/Alchemy/Phrasea/SearchEngine/Elastic/Indexer.php
index 785827700c..e4058a143b 100644
--- a/lib/Alchemy/Phrasea/SearchEngine/Elastic/Indexer.php
+++ b/lib/Alchemy/Phrasea/SearchEngine/Elastic/Indexer.php
@@ -122,7 +122,7 @@ class Indexer
}
$event = $stopwatch->stop('populate');
- printf("Indexation finished in %s min (Mem. %s)", ($event->getDuration()/1000/60), $event->getMemory());
+ printf("Indexation finished in %s min (Mem. %s Mo)", ($event->getDuration()/1000/60), bcdiv($event->getMemory(), 1048576, 2));
}
private function disableShardRefreshing()
diff --git a/lib/Alchemy/Phrasea/SearchEngine/Elastic/Indexer/RecordIndexer.php b/lib/Alchemy/Phrasea/SearchEngine/Elastic/Indexer/RecordIndexer.php
index a2d8d05d61..d4d706741e 100644
--- a/lib/Alchemy/Phrasea/SearchEngine/Elastic/Indexer/RecordIndexer.php
+++ b/lib/Alchemy/Phrasea/SearchEngine/Elastic/Indexer/RecordIndexer.php
@@ -15,8 +15,11 @@ use Alchemy\Phrasea\SearchEngine\Elastic\BulkOperation;
use Alchemy\Phrasea\SearchEngine\Elastic\ElasticSearchEngine;
use Alchemy\Phrasea\SearchEngine\Elastic\Exception\Exception;
use Alchemy\Phrasea\SearchEngine\Elastic\Exception\MergeException;
+use Alchemy\Phrasea\SearchEngine\Elastic\Fetcher\AbstractRecordFetcher;
+use Alchemy\Phrasea\SearchEngine\Elastic\Fetcher\RecordPoolFetcher;
+use Alchemy\Phrasea\SearchEngine\Elastic\Fetcher\SingleRecordFetcher;
use Alchemy\Phrasea\SearchEngine\Elastic\Mapping;
-use Alchemy\Phrasea\SearchEngine\Elastic\RecordFetcher;
+use Alchemy\Phrasea\SearchEngine\Elastic\Fetcher\RecordFetcher;
use Alchemy\Phrasea\SearchEngine\Elastic\RecordHelper;
use Alchemy\Phrasea\SearchEngine\Elastic\StringUtils;
use Alchemy\Phrasea\SearchEngine\Elastic\Thesaurus;
@@ -64,6 +67,7 @@ class RecordIndexer
$params = array();
$params['id'] = $record['id'];
$params['type'] = self::TYPE_NAME;
+ $params['index'] = $this->elasticSearchEngine->getIndexName();
$params['body'] = $this->transform($record);
$bulk->index($params);
}
@@ -71,18 +75,54 @@ class RecordIndexer
}
}
- public function indexSingleRecord(\record_adapter $record_adapter, $indexName)
+ public function index(BulkOperation $bulk, AbstractRecordFetcher $fetcher)
{
- $fetcher = new RecordFetcher($record_adapter->get_databox(), $this->helper);
- $record = $fetcher->fetchOne($record_adapter);
+ while ($records = $fetcher->fetch()) {
+ foreach ($records as $record) {
+ $params = array();
+ // header
+ $params['id'] = $record['id'];
+ $params['type'] = self::TYPE_NAME;
+ $params['index'] = $this->elasticSearchEngine->getIndexName();
+ // body
+ $params['body'] = $this->transform($record);
+ $bulk->index($params);
+ }
+ }
+ $bulk->flush();
+ }
- $params = array();
- $params['id'] = $record['id'];
- $params['type'] = self::TYPE_NAME;
- $params['index'] = $indexName;
- $params['body'] = $this->transform($record);
+ public function update(BulkOperation $bulk, AbstractRecordFetcher $fetcher)
+ {
+ while ($records = $fetcher->fetch()) {
+ foreach ($records as $record) {
+ $params = array();
+ // header
+ $params['id'] = $record['id'];
+ $params['type'] = self::TYPE_NAME;
+ $params['index'] = $this->elasticSearchEngine->getIndexName();
+ // doc
+ $params['doc'] = $this->transform($record);
+ $bulk->update($params);
+ }
+ }
+ $bulk->flush();
+ }
- return $this->elasticSearchEngine->getClient()->index($params);
+ public function delete(BulkOperation $bulk, AbstractRecordFetcher $fetcher)
+ {
+ while ($records = $fetcher->fetch()) {
+ foreach ($records as $record) {
+ $params = array();
+ // header
+ $params['id'] = $record['id'];
+ $params['type'] = self::TYPE_NAME;
+ $params['index'] = $this->elasticSearchEngine->getIndexName();
+
+ $bulk->delete($params);
+ }
+ }
+ $bulk->flush();
}
private function findLinkedConcepts($structure, array $record)
@@ -258,6 +298,7 @@ class RecordIndexer
private function transform($record)
{
$dateFields = $this->elasticSearchEngine->getAvailableDateFields();
+
$structure = $this->getFieldsStructure();
$databox = $this->appbox->get_databox($record['databox_id']);
diff --git a/lib/Alchemy/Phrasea/TaskManager/Editor/IndexerEditor.php b/lib/Alchemy/Phrasea/TaskManager/Editor/IndexerEditor.php
new file mode 100644
index 0000000000..d2d9ca013a
--- /dev/null
+++ b/lib/Alchemy/Phrasea/TaskManager/Editor/IndexerEditor.php
@@ -0,0 +1,52 @@
+
+
+XML;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getFormProperties()
+ {
+ return [];
+ }
+}
diff --git a/lib/Alchemy/Phrasea/TaskManager/Job/ArchiveJob.php b/lib/Alchemy/Phrasea/TaskManager/Job/ArchiveJob.php
index 1fcd73bd94..d6c17fab18 100644
--- a/lib/Alchemy/Phrasea/TaskManager/Job/ArchiveJob.php
+++ b/lib/Alchemy/Phrasea/TaskManager/Job/ArchiveJob.php
@@ -12,6 +12,8 @@
namespace Alchemy\Phrasea\TaskManager\Job;
use Alchemy\Phrasea\Application;
+use Alchemy\Phrasea\Core\Event\RecordEvent\CreateStoryEvent;
+use Alchemy\Phrasea\Core\PhraseaEvents;
use Alchemy\Phrasea\Exception\RuntimeException;
use Alchemy\Phrasea\Border\File;
use Alchemy\Phrasea\TaskManager\Editor\ArchiveEditor;
@@ -1016,7 +1018,7 @@ class ArchiveJob extends AbstractJob
$story->set_binary_status(\databox_status::operation_or($app, $stat0, $stat1));
$story->rebuild_subdefs();
- $app['phraseanet.SE']->addStory($story);
+ $app['dispatcher']->dispatch(PhraseaEvents::STORY_CREATE, new CreateStoryEvent($story));
unset($media);
diff --git a/lib/Alchemy/Phrasea/TaskManager/Job/IndexerJob.php b/lib/Alchemy/Phrasea/TaskManager/Job/IndexerJob.php
new file mode 100644
index 0000000000..306cbffe16
--- /dev/null
+++ b/lib/Alchemy/Phrasea/TaskManager/Job/IndexerJob.php
@@ -0,0 +1,110 @@
+translator->trans('Indexation task');
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getJobId()
+ {
+ return 'Indexer';
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getDescription()
+ {
+ return $this->translator->trans("Indexing Batch (collections/databox)");
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getEditor()
+ {
+ return new IndexerEditor($this->translator);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function doJob(JobData $data)
+ {
+ $app = $data->getApplication();
+
+ $recordHelper = new RecordHelper($app['phraseanet.appbox']);
+
+ // set bulk
+ $bulk = new BulkOperation($app['elasticsearch.client']);
+ $bulk->setDefaultIndex($app['elasticsearch.options']['index']);
+ $bulk->setAutoFlushLimit(1000);
+
+ foreach ($app['phraseanet.appbox']->get_databoxes() as $databox) {
+ if (!$this->isStarted()) {
+ break;
+ }
+
+ $connection = $databox->get_connection();
+
+ // fetch records with 'to_index' flag set and 'indexing' flag not set
+ $fetcher = new ScheduledIndexationRecordFetcher($databox, $recordHelper);
+ $fetcher->setBatchSize(200);
+
+ // set 'indexing' flag, unset 'to_index' flag once
+ // records have been fetched
+ $fetcher->setPostFetch(function($records) use ($connection) {
+ $sql = <<executeQuery($sql, [PhraseaTokens::TOKEN_INDEXING, PhraseaTokens::TOKEN_INDEX, $records], [\PDO::PARAM_INT, \PDO::PARAM_INT, Connection::PARAM_INT_ARRAY]);
+ });
+
+ // update es index
+ $app['elasticsearch.indexer.record_indexer']->update($bulk, $fetcher);
+
+ // unset 'indexing' flag
+ $sql = <<executeQuery($sql, [PhraseaTokens::TOKEN_INDEXING], [\PDO::PARAM_INT]);
+ }
+ }
+}
diff --git a/lib/Alchemy/Phrasea/TaskManager/Job/RecordMoverJob.php b/lib/Alchemy/Phrasea/TaskManager/Job/RecordMoverJob.php
index 8e8e0cd449..ad504907cf 100644
--- a/lib/Alchemy/Phrasea/TaskManager/Job/RecordMoverJob.php
+++ b/lib/Alchemy/Phrasea/TaskManager/Job/RecordMoverJob.php
@@ -12,7 +12,12 @@
namespace Alchemy\Phrasea\TaskManager\Job;
use Alchemy\Phrasea\Application;
+use Alchemy\Phrasea\Core\Event\RecordEvent\ChangeCollectionEvent;
+use Alchemy\Phrasea\Core\Event\RecordEvent\ChangeStatusEvent;
+use Alchemy\Phrasea\Core\Event\RecordEvent\DeleteRecordEvent;
+use Alchemy\Phrasea\Core\PhraseaEvents;
use Alchemy\Phrasea\TaskManager\Editor\RecordMoverEditor;
+use JMS\Serializer\EventDispatcher\EventDispatcher;
class RecordMoverJob extends AbstractJob
{
@@ -78,7 +83,9 @@ class RecordMoverJob extends AbstractJob
if (array_key_exists('coll', $row)) {
$coll = \collection::get_from_coll_id($app, $databox, $row['coll']);
$rec->move_to_collection($coll, $app['phraseanet.appbox']);
- $app['phraseanet.SE']->updateRecord($rec);
+
+ $app['dispatcher']->dispatch(PhraseaEvents::RECORD_CHANGE_COLLECTION, new ChangeCollectionEvent($rec));
+
if ($logsql) {
$this->log('debug', sprintf("on sbas %s move rid %s to coll %s \n", $row['sbas_id'], $row['record_id'], $coll->get_coll_id()));
}
@@ -93,7 +100,9 @@ class RecordMoverJob extends AbstractJob
}
}
$rec->set_binary_status(implode('', $status));
- $app['phraseanet.SE']->updateRecord($rec);
+
+ $app['dispatcher']->dispatch(PhraseaEvents::RECORD_CHANGE_STATUS, new ChangeStatusEvent($rec));
+
if ($logsql) {
$this->log('debug', sprintf("on sbas %s set rid %s status to %s \n", $row['sbas_id'], $row['record_id'], $status));
}
@@ -104,14 +113,16 @@ class RecordMoverJob extends AbstractJob
if ($row['deletechildren'] && $rec->is_grouping()) {
foreach ($rec->get_children() as $child) {
$child->delete();
- $app['phraseanet.SE']->removeRecord($child);
+
+ $app['dispatcher']->dispatch(PhraseaEvents::RECORD_DELETE, new DeleteRecordEvent($child));
+
if ($logsql) {
$this->log('debug', sprintf("on sbas %s delete (grp child) rid %s \n", $row['sbas_id'], $child->get_record_id()));
}
}
}
$rec->delete();
- $app['phraseanet.SE']->removeRecord($rec);
+ $app['dispatcher']->dispatch(PhraseaEvents::RECORD_DELETE, new DeleteRecordEvent($rec));
if ($logsql) {
$this->log('debug', sprintf("on sbas %s delete rid %s \n", $row['sbas_id'], $rec->get_record_id()));
}
diff --git a/lib/Alchemy/Phrasea/TaskManager/Job/SubdefsJob.php b/lib/Alchemy/Phrasea/TaskManager/Job/SubdefsJob.php
index 3a7f0e0626..6c5bb4d599 100644
--- a/lib/Alchemy/Phrasea/TaskManager/Job/SubdefsJob.php
+++ b/lib/Alchemy/Phrasea/TaskManager/Job/SubdefsJob.php
@@ -11,6 +11,7 @@
namespace Alchemy\Phrasea\TaskManager\Job;
+use Alchemy\Phrasea\Core\PhraseaTokens;
use Alchemy\Phrasea\TaskManager\Editor\SubdefsEditor;
use MediaAlchemyst\Transmuter\Image2Image;
@@ -67,7 +68,7 @@ class SubdefsJob extends AbstractJob
$sql = 'SELECT coll_id, record_id
FROM record
- WHERE jeton & ' . JETON_MAKE_SUBDEF . ' > 0
+ WHERE jeton & ' . PhraseaTokens::TOKEN_MAKE_SUBDEF . ' > 0
ORDER BY record_id DESC LIMIT 0, 30';
$stmt = $conn->prepare($sql);
$stmt->execute();
@@ -88,7 +89,7 @@ class SubdefsJob extends AbstractJob
}
$sql = 'UPDATE record
- SET jeton=(jeton & ~' . JETON_MAKE_SUBDEF . '), moddate=NOW()
+ SET jeton=(jeton & ~' . PhraseaTokens::TOKEN_MAKE_SUBDEF . '), moddate=NOW()
WHERE record_id=:record_id';
$stmt = $conn->prepare($sql);
@@ -98,7 +99,7 @@ class SubdefsJob extends AbstractJob
// rewrite metadata
$sql = 'UPDATE record
SET status=(status & ~0x03),
- jeton=(jeton | ' . JETON_WRITE_META_SUBDEF . ')
+ jeton=(jeton | ' . PhraseaTokens::TOKEN_MAKE_SUBDEF . ')
WHERE record_id=:record_id';
$stmt = $conn->prepare($sql);
$stmt->execute([':record_id' => $row['record_id']]);
diff --git a/lib/Alchemy/Phrasea/TaskManager/Job/WriteMetadataJob.php b/lib/Alchemy/Phrasea/TaskManager/Job/WriteMetadataJob.php
index bcd90b38f5..ca98a06f7b 100644
--- a/lib/Alchemy/Phrasea/TaskManager/Job/WriteMetadataJob.php
+++ b/lib/Alchemy/Phrasea/TaskManager/Job/WriteMetadataJob.php
@@ -11,6 +11,7 @@
namespace Alchemy\Phrasea\TaskManager\Job;
+use Alchemy\Phrasea\Core\PhraseaTokens;
use Alchemy\Phrasea\TaskManager\Editor\WriteMetadataEditor;
use PHPExiftool\Driver\Metadata;
use PHPExiftool\Driver\Value;
@@ -82,7 +83,7 @@ class WriteMetadataJob extends AbstractJob
}
}
- $sql = 'SELECT record_id, coll_id, jeton FROM record WHERE (jeton & ' . JETON_WRITE_META . ' > 0)';
+ $sql = 'SELECT record_id, coll_id, jeton FROM record WHERE (jeton & ' . PhraseaTokens::TOKEN_WRITE_META . ' > 0)';
$stmt = $conn->prepare($sql);
$stmt->execute();
@@ -98,8 +99,8 @@ class WriteMetadataJob extends AbstractJob
$subdefs = [];
foreach ($record->get_subdefs() as $name => $subdef) {
- $write_document = (($token & JETON_WRITE_META_DOC) && $name == 'document');
- $write_subdef = (($token & JETON_WRITE_META_SUBDEF) && isset($metaSubdefs[$name . '_' . $type]));
+ $write_document = (($token & PhraseaTokens::TOKEN_WRITE_META_DOC) && $name == 'document');
+ $write_subdef = (($token & PhraseaTokens::TOKEN_WRITE_META_SUBDEF) && isset($metaSubdefs[$name . '_' . $type]));
if (($write_document || $write_subdef) && $subdef->is_physically_present()) {
$subdefs[$name] = $subdef->get_pathfile();
@@ -196,7 +197,7 @@ class WriteMetadataJob extends AbstractJob
}
}
- $sql = 'UPDATE record SET jeton=jeton & ~' . JETON_WRITE_META . ' WHERE record_id = :record_id';
+ $sql = 'UPDATE record SET jeton=jeton & ~' . PhraseaTokens::TOKEN_WRITE_META . ' WHERE record_id = :record_id';
$stmt = $conn->prepare($sql);
$stmt->execute([':record_id' => $record_id]);
$stmt->closeCursor();
diff --git a/lib/classes/caption/field.php b/lib/classes/caption/field.php
index 12fcd542f9..7b65f40d2d 100644
--- a/lib/classes/caption/field.php
+++ b/lib/classes/caption/field.php
@@ -10,6 +10,9 @@
*/
use Alchemy\Phrasea\Application;
+use Alchemy\Phrasea\Core\PhraseaEvents;
+use Alchemy\Phrasea\Core\Event\RecordEvent\ChangeMetadataEvent;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
class caption_field implements cache_cacheableInterface
{
@@ -282,7 +285,7 @@ class caption_field implements cache_cacheableInterface
return $values;
}
- public static function rename_all_metadatas(databox_field $databox_field)
+ public static function rename_all_metadata(EventDispatcherInterface $dispatcher, databox_field $databox_field)
{
$sql = 'SELECT count(id) as count_id FROM metadatas
WHERE meta_struct_id = :meta_struct_id';
@@ -318,10 +321,8 @@ class caption_field implements cache_cacheableInterface
$record = $databox_field->get_databox()->get_record($row['record_id']);
$record->set_metadatas([]);
- /**
- * TODO NEUTRON add App
- */
- $app['phraseanet.SE']->updateRecord($record);
+ $dispatcher->dispatch(PhraseaEvents::RECORD_CHANGE_METADATA, new ChangeMetadataEvent($record));
+
unset($record);
} catch (\Exception $e) {
@@ -374,7 +375,8 @@ class caption_field implements cache_cacheableInterface
$caption_field->delete();
$record->set_metadatas([]);
- $app['phraseanet.SE']->updateRecord($record);
+ $app['dispatcher']->dispatch(PhraseaEvents::RECORD_CHANGE_METADATA, new ChangeMetadataEvent($record));
+
unset($caption_field);
unset($record);
} catch (\Exception $e) {
diff --git a/lib/classes/databox.php b/lib/classes/databox.php
index 8f8c0ed585..c7a96ab5bb 100644
--- a/lib/classes/databox.php
+++ b/lib/classes/databox.php
@@ -16,6 +16,7 @@ use Doctrine\DBAL\Driver\Connection;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Translation\TranslatorInterface;
+use Alchemy\Phrasea\Core\PhraseaTokens;
class databox extends base
{
@@ -439,7 +440,7 @@ class databox extends base
$ret['thesaurus_indexed'] += $row['n'];
}
- $sql = "SELECT type, COUNT(record_id) AS n FROM record WHERE jeton & ".JETON_MAKE_SUBDEF." GROUP BY type";
+ $sql = "SELECT type, COUNT(record_id) AS n FROM record WHERE jeton & ".PhraseaTokens::TOKEN_MAKE_SUBDEF." GROUP BY type";
$stmt = $this->get_connection()->prepare($sql);
$stmt->execute();
$rs = $stmt->fetchAll(PDO::FETCH_ASSOC);
diff --git a/lib/classes/databox/field.php b/lib/classes/databox/field.php
index 193ff36a4f..eab6ea0d6b 100644
--- a/lib/classes/databox/field.php
+++ b/lib/classes/databox/field.php
@@ -422,7 +422,7 @@ class databox_field implements cache_cacheableInterface
$stmt->closeCursor();
if ($this->renamed) {
- caption_field::rename_all_metadatas($this);
+ caption_field::rename_all_metadata($this->app['dispatcher'], $this);
$this->renamed = false;
}
diff --git a/lib/classes/databox/status.php b/lib/classes/databox/status.php
index 7d736e9e38..8bc19dee48 100644
--- a/lib/classes/databox/status.php
+++ b/lib/classes/databox/status.php
@@ -128,6 +128,7 @@ class databox_status
if ( ! isset(self::$_status[$sbas_id]))
self::$_status[$sbas_id] = new databox_status($app, $sbas_id);
+
return self::$_status[$sbas_id]->status;
}
@@ -265,7 +266,7 @@ class databox_status
public static function updateStatus(Application $app, $sbas_id, $bit, $properties)
{
- self::getStatus($app, $sbas_id);
+ self::getStatus($app, $sbas_id);
$databox = $app['phraseanet.appbox']->get_databox((int) $sbas_id);
diff --git a/lib/classes/module/console/fieldsMerge.php b/lib/classes/module/console/fieldsMerge.php
index 61a78ddc5f..a535ff15fa 100644
--- a/lib/classes/module/console/fieldsMerge.php
+++ b/lib/classes/module/console/fieldsMerge.php
@@ -14,6 +14,8 @@ use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Alchemy\Phrasea\Command\Command;
+use Alchemy\Phrasea\Core\PhraseaEvents;
+use Alchemy\Phrasea\Core\Event\RecordEvent\ChangeMetadataEvent;
class module_console_fieldsMerge extends Command
{
@@ -196,7 +198,7 @@ class module_console_fieldsMerge extends Command
]], true);
}
- $this->getService('phraseanet.SE')->updateRecord($record);
+ $this->getService('dispatcher')->dispatch(PhraseaEvents::RECORD_CHANGE_METADATA, new ChangeMetadataEvent($record));
unset($record);
}
diff --git a/lib/classes/record/adapter.php b/lib/classes/record/adapter.php
index c1f91d6caa..ba14d59264 100644
--- a/lib/classes/record/adapter.php
+++ b/lib/classes/record/adapter.php
@@ -23,6 +23,7 @@ use MediaVorus\MediaVorus;
use Rhumsaa\Uuid\Uuid;
use Alchemy\Phrasea\Model\RecordInterface;
use Symfony\Component\HttpFoundation\File\File as SymfoFile;
+use Alchemy\Phrasea\Core\PhraseaTokens;
class record_adapter implements RecordInterface, cache_cacheableInterface
{
@@ -486,8 +487,6 @@ class record_adapter implements RecordInterface, cache_cacheableInterface
$this->base_id = $collection->get_base_id();
- $this->app['phraseanet.SE']->updateRecord($this);
-
$this->app['phraseanet.logger']($this->get_databox())
->log($this, Session_Logger::EVENT_MOVE, $collection->get_coll_id(), '');
@@ -972,8 +971,6 @@ class record_adapter implements RecordInterface, cache_cacheableInterface
);
$stmt->closeCursor();
- $this->reindex();
-
return $this;
}
@@ -1067,26 +1064,11 @@ class record_adapter implements RecordInterface, cache_cacheableInterface
$xml->loadXML($this->app['serializer.caption']->serialize($this->get_caption(), CaptionSerializer::SERIALIZE_XML, true));
$this->set_xml($xml);
- $this->reindex();
-
unset($xml);
return $this;
}
- /**
- * Reindex the record
- *
- * @return record_adapter
- */
- public function reindex()
- {
- $this->app['phraseanet.SE']->updateRecord($this);
- $this->delete_data_from_cache(self::CACHE_STATUS);
-
- return $this;
- }
-
/**
*
* @return record_adapter
@@ -1095,7 +1077,7 @@ class record_adapter implements RecordInterface, cache_cacheableInterface
{
$databox = $this->app['phraseanet.appbox']->get_databox($this->get_sbas_id());
$connbas = $databox->get_connection();
- $sql = 'UPDATE record SET jeton=(jeton | ' . JETON_MAKE_SUBDEF . ') WHERE record_id = :record_id';
+ $sql = 'UPDATE record SET jeton=(jeton | ' . PhraseaTokens::TOKEN_MAKE_SUBDEF . ') WHERE record_id = :record_id';
$stmt = $connbas->prepare($sql);
$stmt->execute([':record_id' => $this->get_record_id()]);
$stmt->closeCursor();
@@ -1112,7 +1094,7 @@ class record_adapter implements RecordInterface, cache_cacheableInterface
$databox = $this->app['phraseanet.appbox']->get_databox($this->get_sbas_id());
$connbas = $databox->get_connection();
$sql = 'UPDATE record
- SET jeton = jeton | (' . (JETON_WRITE_META_DOC | JETON_WRITE_META_SUBDEF) . ')
+ SET jeton = jeton | (' . (PhraseaTokens::TOKEN_WRITE_META_DOC | PhraseaTokens::TOKEN_WRITE_META_SUBDEF) . ')
WHERE record_id= :record_id';
$stmt = $connbas->prepare($sql);
$stmt->execute([':record_id' => $this->record_id]);
@@ -1928,6 +1910,8 @@ class record_adapter implements RecordInterface, cache_cacheableInterface
public function setStatus($status)
{
$this->set_binary_status($status);
+
+ $this->delete_data_from_cache(self::CACHE_STATUS);
}
/** {@inheritdoc} */
diff --git a/templates/web/admin/task-manager/task-editor/indexer.html.twig b/templates/web/admin/task-manager/task-editor/indexer.html.twig
new file mode 100644
index 0000000000..13e24cb9f4
--- /dev/null
+++ b/templates/web/admin/task-manager/task-editor/indexer.html.twig
@@ -0,0 +1 @@
+{% extends 'admin/task-manager/task-editor/task.html.twig' %}