Add elasticsearch reindex events

This commit is contained in:
Nicolas Le Goff
2015-01-02 18:47:21 +01:00
committed by Mathieu Darse
parent ad868dc70f
commit a01bf29c5b
73 changed files with 1708 additions and 182 deletions

View File

@@ -179,6 +179,7 @@ class Application extends SilexApplication
'fr' => 'Français', 'fr' => 'Français',
'nl' => 'Dutch', 'nl' => 'Dutch',
]; ];
private static $flashTypes = ['warning', 'info', 'success', 'error']; private static $flashTypes = ['warning', 'info', 'success', 'error'];
private $environment; private $environment;
@@ -204,11 +205,6 @@ class Application extends SilexApplication
$this['charset'] = 'UTF-8'; $this['charset'] = 'UTF-8';
mb_internal_encoding($this['charset']); 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) { $this['debug'] = $this->share(function (Application $app) {
return Application::ENV_PROD !== $app->getEnvironment(); return Application::ENV_PROD !== $app->getEnvironment();
}); });
@@ -241,6 +237,7 @@ class Application extends SilexApplication
$this->register(new FeedServiceProvider()); $this->register(new FeedServiceProvider());
$this->register(new FtpServiceProvider()); $this->register(new FtpServiceProvider());
$this->register(new GeonamesServiceProvider()); $this->register(new GeonamesServiceProvider());
$this['geonames.server-uri'] = $this->share(function (Application $app) { $this['geonames.server-uri'] = $this->share(function (Application $app) {
return $app['conf']->get(['registry', 'webservices', 'geonames-server'], 'http://geonames.alchemyasp.com/'); 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.maintenance-subscriber']);
$dispatcher->addSubscriber($app['phraseanet.cookie-disabler-subscriber']); $dispatcher->addSubscriber($app['phraseanet.cookie-disabler-subscriber']);
$dispatcher->addSubscriber($app['phraseanet.session-manager-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 PhraseaInstallSubscriber($app));
$dispatcher->addSubscriber(new FeedEntrySubscriber($app)); $dispatcher->addSubscriber(new FeedEntrySubscriber($app));
$dispatcher->addSubscriber(new RegistrationSubscriber($app)); $dispatcher->addSubscriber(new RegistrationSubscriber($app));

View File

@@ -13,6 +13,8 @@ namespace Alchemy\Phrasea\Border;
use Alchemy\Phrasea\Border\Checker\CheckerInterface; use Alchemy\Phrasea\Border\Checker\CheckerInterface;
use Alchemy\Phrasea\Border\Attribute\AttributeInterface; 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\TfArchivedate;
use Alchemy\Phrasea\Metadata\Tag\TfQuarantine; use Alchemy\Phrasea\Metadata\Tag\TfQuarantine;
use Alchemy\Phrasea\Metadata\Tag\TfBasename; use Alchemy\Phrasea\Metadata\Tag\TfBasename;
@@ -288,6 +290,8 @@ class Manager
case AttributeInterface::NAME_STATUS: case AttributeInterface::NAME_STATUS:
$element->set_binary_status(decbin(bindec($element->get_status()) | bindec($attribute->getValue()))); $element->set_binary_status(decbin(bindec($element->get_status()) | bindec($attribute->getValue())));
$this->app['dispatcher']->dispatch(PhraseaEvents::RECORD_CHANGE_STATUS, new ChangeStatusEvent($element));
break; break;
case AttributeInterface::NAME_STORY: case AttributeInterface::NAME_STORY:
@@ -304,7 +308,6 @@ class Manager
$this->app['phraseanet.metadata-setter']->replaceMetadata($newMetadata, $element); $this->app['phraseanet.metadata-setter']->replaceMetadata($newMetadata, $element);
$element->rebuild_subdefs(); $element->rebuild_subdefs();
$element->reindex();
return $element; return $element;
} }

View File

@@ -13,6 +13,8 @@ namespace Alchemy\Phrasea\Command;
use Alchemy\Phrasea\Border\File; use Alchemy\Phrasea\Border\File;
use Alchemy\Phrasea\Border\Manager; 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\LazaretFile;
use Alchemy\Phrasea\Model\Entities\LazaretSession; use Alchemy\Phrasea\Model\Entities\LazaretSession;
use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputArgument;
@@ -115,7 +117,7 @@ class RecordAdd extends Command
"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->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) { } elseif ($elementCreated instanceof LazaretFile) {
$output->writeln( $output->writeln(
sprintf("Quarantine item id <info>%d</info> has been created", $elementCreated->getId()) sprintf("Quarantine item id <info>%d</info> has been created", $elementCreated->getId())

View File

@@ -11,6 +11,8 @@
namespace Alchemy\Phrasea\Controller\Admin; namespace Alchemy\Phrasea\Controller\Admin;
use Alchemy\Phrasea\Core\Event\CollectionEvent\ChangeNameEvent;
use Alchemy\Phrasea\Core\PhraseaEvents;
use Alchemy\Phrasea\Exception\RuntimeException; use Alchemy\Phrasea\Exception\RuntimeException;
use Silex\Application; use Silex\Application;
use Silex\ControllerProviderInterface; use Silex\ControllerProviderInterface;
@@ -718,6 +720,8 @@ class Collection implements ControllerProviderInterface
try { try {
$collection->set_name($name); $collection->set_name($name);
$success = true; $success = true;
$app['dispatcher']->dispatch(PhraseaEvents::COLLECTION_CHANGE_NAME, new ChangeNameEvent($collection));
} catch (\Exception $e) { } catch (\Exception $e) {
} }

View File

@@ -11,6 +11,9 @@
namespace Alchemy\Phrasea\Controller\Admin; 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\Metadata\TagProvider;
use Alchemy\Phrasea\Vocabulary\Controller as VocabularyController; use Alchemy\Phrasea\Vocabulary\Controller as VocabularyController;
use JMS\TranslationBundle\Annotation\Ignore; use JMS\TranslationBundle\Annotation\Ignore;
@@ -114,6 +117,8 @@ class Fields implements ControllerProviderInterface
$this->updateFieldWithData($app, $field, $jsonField); $this->updateFieldWithData($app, $field, $jsonField);
$field->save(); $field->save();
$fields[] = $field->toArray(); $fields[] = $field->toArray();
$app['dispatcher']->dispatch(PhraseaEvents::DATABOX_UPDATE_FIELD, new UpdateStructureFieldEvent($field, $jsonField));
} catch (\Exception $e) { } catch (\Exception $e) {
$connection->rollback(); $connection->rollback();
$app->abort(500, $app->trans('Field %name% could not be saved, please try again or contact an admin.', ['%name%' => $jsonField['name']])); $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); $this->updateFieldWithData($app, $field, $data);
$field->save(); $field->save();
$app['dispatcher']->dispatch(PhraseaEvents::DATABOX_UPDATE_FIELD, new UpdateStructureFieldEvent($databox, $field, $jsonField));
return $app->json($field->toArray()); return $app->json($field->toArray());
} }
public function deleteField(Application $app, $sbas_id, $id) public function deleteField(Application $app, $sbas_id, $id)
{ {
$databox = $app['phraseanet.appbox']->get_databox((int) $sbas_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); return new Response('', 204);
} }
@@ -321,6 +331,7 @@ class Fields implements ControllerProviderInterface
private function updateFieldWithData(Application $app, \databox_field $field, array $data) private function updateFieldWithData(Application $app, \databox_field $field, array $data)
{ {
$field $field
->set_name($data['name']) ->set_name($data['name'])
->set_thumbtitle($data['thumbtitle']) ->set_thumbtitle($data['thumbtitle'])

View File

@@ -11,6 +11,9 @@
namespace Alchemy\Phrasea\Controller\Admin; 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\Exception\SessionNotFound;
use Alchemy\Phrasea\Helper\DatabaseHelper; use Alchemy\Phrasea\Helper\DatabaseHelper;
use Alchemy\Phrasea\Helper\PathHelper; use Alchemy\Phrasea\Helper\PathHelper;
@@ -322,12 +325,17 @@ class Root implements ControllerProviderInterface
$error = false; $error = false;
$status = \databox_status::getStatus($app, $databox_id)[$bit];
$databox = $app['phraseanet.appbox']->get_databox((int) $databox_id);
try { try {
\databox_status::deleteStatus($app, $app['phraseanet.appbox']->get_databox($databox_id), $bit); \databox_status::deleteStatus($app, $app['phraseanet.appbox']->get_databox($databox_id), $bit);
} catch (\Exception $e) { } catch (\Exception $e) {
$error = true; $error = true;
} }
$app['dispatcher']->dispatch(PhraseaEvents::DATABOX_DELETE_STATUS, new DeleteStatusEvent($databox, $status));
return $app->json(['success' => !$error]); return $app->json(['success' => !$error]);
}) })
->bind('admin_statusbit_delete') ->bind('admin_statusbit_delete')
@@ -349,6 +357,8 @@ class Root implements ControllerProviderInterface
'labels_off' => $request->request->get('labels_off', []), 'labels_off' => $request->request->get('labels_off', []),
]; ];
$databox = $app['phraseanet.appbox']->get_databox((int) $databox_id);
\databox_status::updateStatus($app, $databox_id, $bit, $properties); \databox_status::updateStatus($app, $databox_id, $bit, $properties);
if (null !== $request->request->get('delete_icon_off')) { 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]); return $app->redirectPath('database_display_statusbit', ['databox_id' => $databox_id, 'success' => 1]);
})->assert('databox_id', '\d+') })->assert('databox_id', '\d+')
->assert('bit', '\d+') ->assert('bit', '\d+')

View File

@@ -11,6 +11,9 @@
namespace Alchemy\Phrasea\Controller\Api; 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 Silex\ControllerProviderInterface;
use Alchemy\Phrasea\Cache\Cache as CacheInterface; use Alchemy\Phrasea\Cache\Cache as CacheInterface;
use Alchemy\Phrasea\Core\PhraseaEvents; use Alchemy\Phrasea\Core\PhraseaEvents;
@@ -558,7 +561,8 @@ class V1 implements ControllerProviderInterface
if ($output instanceof \record_adapter) { if ($output instanceof \record_adapter) {
$ret['entity'] = '0'; $ret['entity'] = '0';
$ret['url'] = '/records/' . $output->get_sbas_id() . '/' . $output->get_record_id() . '/'; $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) { if ($output instanceof LazaretFile) {
$ret['entity'] = '1'; $ret['entity'] = '1';
@@ -829,6 +833,7 @@ class V1 implements ControllerProviderInterface
}); });
$record->set_metadatas($metadatas); $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(); 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)); $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())]; $ret = ["status" => $this->list_record_status($databox, $record->get_status())];

View File

@@ -11,6 +11,9 @@
namespace Alchemy\Phrasea\Controller\Prod; 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\Vocabulary\Controller as VocabularyController;
use Alchemy\Phrasea\Controller\RecordsRequest; use Alchemy\Phrasea\Controller\RecordsRequest;
use Alchemy\Phrasea\Metadata\Tag\TfEditdate; use Alchemy\Phrasea\Metadata\Tag\TfEditdate;
@@ -347,6 +350,8 @@ class Edit implements ControllerProviderInterface
if (isset($rec['metadatas']) && is_array($rec['metadatas'])) { if (isset($rec['metadatas']) && is_array($rec['metadatas'])) {
$record->set_metadatas($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); $record->set_metadatas($metas, true);
$app['dispatcher']->dispatch(PhraseaEvents::RECORD_CHANGE_METADATA, new ChangeMetadataEvent($record));
} }
$newstat = $record->get_status(); $newstat = $record->get_status();
@@ -389,6 +396,8 @@ class Edit implements ControllerProviderInterface
} }
$record->set_binary_status($newstat); $record->set_binary_status($newstat);
$app['dispatcher']->dispatch(PhraseaEvents::RECORD_CHANGE_STATUS, new ChangeStatusEvent($record));
} }
$record $record

View File

@@ -11,6 +11,10 @@
namespace Alchemy\Phrasea\Controller\Prod; 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\Model\Entities\LazaretFile;
use Alchemy\Phrasea\Border; use Alchemy\Phrasea\Border;
use Alchemy\Phrasea\Border\Attribute\AttributeInterface; use Alchemy\Phrasea\Border\Attribute\AttributeInterface;
@@ -195,7 +199,7 @@ class Lazaret implements ControllerProviderInterface
$lazaretFile->getSession(), $borderFile, $callBack, Border\Manager::FORCE_RECORD $lazaretFile->getSession(), $borderFile, $callBack, Border\Manager::FORCE_RECORD
); );
$app['phraseanet.SE']->addRecord($record); $app['dispatcher']->dispatch(PhraseaEvents::RECORD_CREATE, new CreateRecordEvent($record));
if ($keepAttributes) { if ($keepAttributes) {
//add attribute //add attribute
@@ -229,6 +233,8 @@ class Lazaret implements ControllerProviderInterface
break; break;
case AttributeInterface::NAME_STATUS: case AttributeInterface::NAME_STATUS:
$record->set_binary_status($attribute->getValue()); $record->set_binary_status($attribute->getValue());
$app['dispatcher']->dispatch(PhraseaEvents::RECORD_CHANGE_STATUS, new ChangeStatusEvent($record));
break; break;
case AttributeInterface::NAME_METAFIELD: case AttributeInterface::NAME_METAFIELD:
$metaFields->set($attribute->getField()->get_name(), $attribute->getValue()); $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()); $fields = $metaFields->toMetadataArray($record->get_databox()->get_meta_structure());
$record->set_metadatas($fields); $record->set_metadatas($fields);
$app['dispatcher']->dispatch(PhraseaEvents::RECORD_CHANGE_METADATA, new ChangeMetadataEvent($record));
} }
//Delete lazaret file //Delete lazaret file

View File

@@ -12,6 +12,8 @@
namespace Alchemy\Phrasea\Controller\Prod; namespace Alchemy\Phrasea\Controller\Prod;
use Alchemy\Phrasea\Controller\RecordsRequest; use Alchemy\Phrasea\Controller\RecordsRequest;
use Alchemy\Phrasea\Core\Event\RecordEvent\ChangeCollectionEvent;
use Alchemy\Phrasea\Core\PhraseaEvents;
use Silex\Application; use Silex\Application;
use Silex\ControllerProviderInterface; use Silex\ControllerProviderInterface;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
@@ -93,10 +95,15 @@ class MoveCollection implements ControllerProviderInterface
foreach ($records as $record) { foreach ($records as $record) {
$record->move_to_collection($collection, $app['phraseanet.appbox']); $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") { if ($request->request->get("chg_coll_son") == "1") {
foreach ($record->get_children() as $child) { foreach ($record->get_children() as $child) {
if ($app['acl']->get($app['authentication']->getUser())->has_right_on_base($child->get_base_id(), 'candeleterecord')) { if ($app['acl']->get($app['authentication']->getUser())->has_right_on_base($child->get_base_id(), 'candeleterecord')) {
$child->move_to_collection($collection, $app['phraseanet.appbox']); $child->move_to_collection($collection, $app['phraseanet.appbox']);
$app['dispatcher']->dispatch(PhraseaEvents::RECORD_CHANGE_COLLECTION, new ChangeCollectionEvent($child));
} }
} }
} }

View File

@@ -12,6 +12,8 @@
namespace Alchemy\Phrasea\Controller\Prod; namespace Alchemy\Phrasea\Controller\Prod;
use Alchemy\Phrasea\Controller\RecordsRequest; use Alchemy\Phrasea\Controller\RecordsRequest;
use Alchemy\Phrasea\Core\Event\RecordEvent\ChangeStatusEvent;
use Alchemy\Phrasea\Core\PhraseaEvents;
use Silex\Application; use Silex\Application;
use Silex\ControllerProviderInterface; use Silex\ControllerProviderInterface;
use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\JsonResponse;
@@ -175,6 +177,7 @@ class Property implements ControllerProviderInterface
foreach ($record->get_children() as $child) { foreach ($record->get_children() as $child) {
if (null !== $updatedStatus = $this->updateRecordStatus($child, $postStatus)) { if (null !== $updatedStatus = $this->updateRecordStatus($child, $postStatus)) {
$updated[$record->get_serialize_key()] = $updatedStatus; $updated[$record->get_serialize_key()] = $updatedStatus;
$app['dispatcher']->dispatch(PhraseaEvents::RECORD_CHANGE_STATUS, new ChangeStatusEvent($record));
} }
} }
} }

View File

@@ -12,6 +12,8 @@
namespace Alchemy\Phrasea\Controller\Prod; namespace Alchemy\Phrasea\Controller\Prod;
use Alchemy\Phrasea\Controller\RecordsRequest; use Alchemy\Phrasea\Controller\RecordsRequest;
use Alchemy\Phrasea\Core\Event\RecordEvent\DeleteStoryEvent;
use Alchemy\Phrasea\Core\PhraseaEvents;
use Alchemy\Phrasea\SearchEngine\SearchEngineOptions; use Alchemy\Phrasea\SearchEngine\SearchEngineOptions;
use Silex\Application; use Silex\Application;
use Silex\ControllerProviderInterface; use Silex\ControllerProviderInterface;
@@ -174,6 +176,12 @@ class Records implements ControllerProviderInterface
$deleted[] = $record->get_serialize_key(); $deleted[] = $record->get_serialize_key();
$record->delete(); $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) { } catch (\Exception $e) {
} }

View File

@@ -13,6 +13,8 @@ namespace Alchemy\Phrasea\Controller\Prod;
use Alchemy\Phrasea\Controller\Exception as ControllerException; use Alchemy\Phrasea\Controller\Exception as ControllerException;
use Alchemy\Phrasea\Controller\RecordsRequest; use Alchemy\Phrasea\Controller\RecordsRequest;
use Alchemy\Phrasea\Core\Event\RecordEvent\ChangeMetadataEvent;
use Alchemy\Phrasea\Core\PhraseaEvents;
use Alchemy\Phrasea\Model\Entities\StoryWZ; use Alchemy\Phrasea\Model\Entities\StoryWZ;
use Silex\Application; use Silex\Application;
use Silex\ControllerProviderInterface; use Silex\ControllerProviderInterface;
@@ -74,6 +76,8 @@ class Story implements ControllerProviderInterface
$Story->set_metadatas($metadatas)->rebuild_subdefs(); $Story->set_metadatas($metadatas)->rebuild_subdefs();
$app['dispatcher']->dispatch(PhraseaEvents::RECORD_CHANGE_METADATA, new ChangeMetadataEvent($Story));
$StoryWZ = new StoryWZ(); $StoryWZ = new StoryWZ();
$StoryWZ->setUser($app['authentication']->getUser()); $StoryWZ->setUser($app['authentication']->getUser());
$StoryWZ->setRecord($Story); $StoryWZ->setRecord($Story);

View File

@@ -12,6 +12,8 @@
namespace Alchemy\Phrasea\Controller\Prod; namespace Alchemy\Phrasea\Controller\Prod;
use Alchemy\Phrasea\Controller\RecordsRequest; use Alchemy\Phrasea\Controller\RecordsRequest;
use Alchemy\Phrasea\Core\Event\ChangeOriginalNameEvent;
use Alchemy\Phrasea\Core\PhraseaEvents;
use Alchemy\Phrasea\Exception\RuntimeException; use Alchemy\Phrasea\Exception\RuntimeException;
use DataURI; use DataURI;
use PHPExiftool\Exception\ExceptionInterface as PHPExiftoolException; use PHPExiftool\Exception\ExceptionInterface as PHPExiftoolException;
@@ -149,7 +151,8 @@ class Tools implements ControllerProviderInterface
if ((int) $request->request->get('ccfilename') === 1) { if ((int) $request->request->get('ccfilename') === 1) {
$record->set_original_name($fileName); $record->set_original_name($fileName);
$app['phraseanet.SE']->updateRecord($record);
$app['dispatcher']->dispatch(PhraseaEvents::RECORD_CHANGE_ORIGINAL_NAME, new ChangeOriginalNameEvent($record));
} }
unlink($tempoFile); unlink($tempoFile);
rmdir($tempoDir); rmdir($tempoDir);

View File

@@ -14,6 +14,7 @@ namespace Alchemy\Phrasea\Controller\Prod;
use Alchemy\Phrasea\Border\File; use Alchemy\Phrasea\Border\File;
use Alchemy\Phrasea\Border\Attribute\Status; use Alchemy\Phrasea\Border\Attribute\Status;
use Alchemy\Phrasea\Core\Event\LazaretEvent; use Alchemy\Phrasea\Core\Event\LazaretEvent;
use Alchemy\Phrasea\Core\Event\RecordEvent\CreateRecordEvent;
use Alchemy\Phrasea\Core\PhraseaEvents; use Alchemy\Phrasea\Core\PhraseaEvents;
use DataURI\Parser; use DataURI\Parser;
use DataURI\Exception\Exception as DataUriException; use DataURI\Exception\Exception as DataUriException;
@@ -207,7 +208,8 @@ class Upload implements ControllerProviderInterface
$id = $elementCreated->get_serialize_key(); $id = $elementCreated->get_serialize_key();
$element = 'record'; $element = 'record';
$message = $app->trans('The record was successfully created'); $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 // try to create thumbnail from data URI
if ('' !== $b64Image = $request->request->get('b64_image', '')) { if ('' !== $b64Image = $request->request->get('b64_image', '')) {

View File

@@ -11,6 +11,8 @@
namespace Alchemy\Phrasea\Controller\Thesaurus; namespace Alchemy\Phrasea\Controller\Thesaurus;
use Alchemy\Phrasea\Core\Event\RecordEvent\ChangeMetadataEvent;
use Alchemy\Phrasea\Core\PhraseaEvents;
use Alchemy\Phrasea\Model\Entities\User; use Alchemy\Phrasea\Model\Entities\User;
use Silex\Application; use Silex\Application;
use Silex\ControllerProviderInterface; use Silex\ControllerProviderInterface;
@@ -1494,6 +1496,9 @@ class Xmlhttp implements ControllerProviderInterface
if (count($metadatasd) > 0) { if (count($metadatasd) > 0) {
if (!$request->get('debug')) { if (!$request->get('debug')) {
$record->set_metadatas($metadatasd, true); $record->set_metadatas($metadatasd, true);
$app['dispatcher']->dispatch(PhraseaEvents::RECORD_CHANGE_METADATA, new ChangeMetadataEvent($record));
$ret['nRecsUpdated']++; $ret['nRecsUpdated']++;
} }
} }

View File

@@ -0,0 +1,17 @@
<?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\CollectionEvent;
class ChangeNameEvent extends CollectionEvent
{
}

View File

@@ -0,0 +1,32 @@
<?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\CollectionEvent;
use Symfony\Component\EventDispatcher\Event as SfEvent;
class CollectionEvent extends SfEvent
{
private $collection;
public function __construct(\collection $collection)
{
$this->collection = $collection;
}
/**
* @return \collection
*/
public function getCollection()
{
return $this->collection;
}
}

View File

@@ -0,0 +1,17 @@
<?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\CollectionEvent;
class CollectionIndexEvent extends CollectionEvent
{
}

View File

@@ -0,0 +1,32 @@
<?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\DataboxEvent;
use Symfony\Component\EventDispatcher\Event as SfEvent;
class DataboxEvent extends SfEvent
{
private $databox;
public function __construct(\databox $databox)
{
$this->databox = $databox;
}
/**
* @return \databox
*/
public function getDatabox()
{
return $this->databox;
}
}

View File

@@ -0,0 +1,17 @@
<?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\DataboxEvent;
class DataboxIndexEvent extends DataboxEvent
{
}

View File

@@ -0,0 +1,29 @@
<?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\DataboxEvent;
class DeleteStatusEvent extends DataboxEvent
{
private $status;
public function __construct(\databox $databox, array $status)
{
$this->status = $status;
parent::__construct($databox);
}
public function getStatus()
{
return $this->status;
}
}

View File

@@ -0,0 +1,32 @@
<?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\DataboxEvent;
class DeleteStructureFieldEvent extends DataboxEvent
{
private $field;
public function __construct(\databox $databox, \databox_field $field)
{
$this->field = $field;
parent::__construct($databox);
}
/**
* @return \databox_field
*/
public function getField()
{
return $this->field;
}
}

View File

@@ -0,0 +1,29 @@
<?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\DataboxEvent;
class UpdateStatusEvent extends DataboxEvent
{
private $status;
public function __construct(\databox $databox, array $status)
{
$this->status = $status;
parent::__construct($databox);
}
public function getStatus()
{
return $this->status;
}
}

View File

@@ -0,0 +1,42 @@
<?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\DataboxEvent;
class UpdateStructureFieldEvent extends DataboxEvent
{
private $field;
private $data;
public function __construct(\databox $databox, \databox_field $field, $data)
{
$this->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;
}
}

View File

@@ -0,0 +1,31 @@
<?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\RecordEvent;
use Alchemy\Phrasea\Model\Entities\Basket;
use Symfony\Component\EventDispatcher\Event as SfEvent;
class BuildSubDefEvent extends RecordEvent
{
private $subDefName;
public function __construct(\record_adapter $record, $subDefName)
{
$this->subDefName = $subDefName;
parent::__construct($record);
}
public function getSubDefName()
{
return $this->subDefName;
}
}

View File

@@ -0,0 +1,19 @@
<?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\RecordEvent;
use Alchemy\Phrasea\Model\Entities\Basket;
use Symfony\Component\EventDispatcher\Event as SfEvent;
class ChangeCollectionEvent extends RecordEvent
{
}

View File

@@ -0,0 +1,19 @@
<?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\RecordEvent;
use Alchemy\Phrasea\Model\Entities\Basket;
use Symfony\Component\EventDispatcher\Event as SfEvent;
class ChangeMetadataEvent extends RecordEvent
{
}

View File

@@ -0,0 +1,19 @@
<?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\RecordEvent;
use Alchemy\Phrasea\Model\Entities\Basket;
use Symfony\Component\EventDispatcher\Event as SfEvent;
class ChangeOriginalNameEvent extends RecordEvent
{
}

View File

@@ -0,0 +1,20 @@
<?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\RecordEvent;
use Alchemy\Phrasea\Model\Entities\Basket;
use Symfony\Component\EventDispatcher\Event as SfEvent;
class ChangeStatusEvent extends RecordEvent
{
}

View File

@@ -0,0 +1,20 @@
<?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\RecordEvent;
use Alchemy\Phrasea\Model\Entities\Basket;
use Symfony\Component\EventDispatcher\Event as SfEvent;
class CreateRecordEvent extends RecordEvent
{
}

View File

@@ -0,0 +1,20 @@
<?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\RecordEvent;
use Alchemy\Phrasea\Model\Entities\Basket;
use Symfony\Component\EventDispatcher\Event as SfEvent;
class CreateStoryEvent extends RecordEvent
{
}

View File

@@ -0,0 +1,19 @@
<?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\RecordEvent;
use Alchemy\Phrasea\Model\Entities\Basket;
use Symfony\Component\EventDispatcher\Event as SfEvent;
class DeleteRecordEvent extends RecordEvent
{
}

View File

@@ -0,0 +1,19 @@
<?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\RecordEvent;
use Alchemy\Phrasea\Model\Entities\Basket;
use Symfony\Component\EventDispatcher\Event as SfEvent;
class DeleteStoryEvent extends RecordEvent
{
}

View File

@@ -0,0 +1,30 @@
<?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\RecordEvent;
use Symfony\Component\EventDispatcher\Event as SfEvent;
class RecordEvent extends SfEvent
{
public function __construct(\record_adapter $record)
{
$this->record = $record;
}
/**
* @return \record_adapter
*/
public function getRecord()
{
return $this->record;
}
}

View File

@@ -0,0 +1,17 @@
<?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\RecordEvent;
class RecordIndexEvent extends RecordEvent
{
}

View File

@@ -0,0 +1,19 @@
<?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\RecordEvent;
use Alchemy\Phrasea\Model\Entities\Basket;
use Symfony\Component\EventDispatcher\Event as SfEvent;
class SubstituteRecordEvent extends RecordEvent
{
}

View File

@@ -0,0 +1,36 @@
<?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\Core\Event\CollectionEvent\ChangeNameEvent;
use Alchemy\Phrasea\Core\Event\CollectionEvent\CollectionIndexEvent;
use Alchemy\Phrasea\Core\Event\DataboxEvent\IndexEvent;
use Alchemy\Phrasea\Core\PhraseaEvents;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class CollectionSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents()
{
return [
PhraseaEvents::COLLECTION_CHANGE_NAME => 'onChangeName',
];
}
public function onChangeName(ChangeNameEvent $event)
{
$collection = $event->getCollection();
$dispatcher = $event->getDispatcher();
$dispatcher->dispatch(PhraseaEvents::INDEX_COLLECTION, new CollectionIndexEvent($collection));
}
}

View File

@@ -0,0 +1,68 @@
<?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\Core\Event\DataboxEvent\DataboxIndexEvent;
use Alchemy\Phrasea\Core\Event\DataboxEvent\DeleteStatusEvent;
use Alchemy\Phrasea\Core\Event\DataboxEvent\UpdateStatusEvent;
use Alchemy\Phrasea\Core\Event\DataboxEvent\UpdateStructureFieldEvent;
use Alchemy\Phrasea\Core\PhraseaEvents;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class DataboxSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents()
{
return [
PhraseaEvents::DATABOX_UPDATE_FIELD => '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));
}
}

View File

@@ -0,0 +1,194 @@
<?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\Core\Event\CollectionEvent\CollectionIndexEvent;
use Alchemy\Phrasea\Core\Event\DataboxEvent\DataboxIndexEvent;
use Alchemy\Phrasea\Core\Event\DataboxEvent\IndexEvent;
use Alchemy\Phrasea\Core\Event\RecordEvent\RecordIndexEvent;
use Alchemy\Phrasea\Core\PhraseaEvents;
use Alchemy\Phrasea\Core\PhraseaTokens;
use Alchemy\Phrasea\SearchEngine\Elastic\BulkOperation;
use Alchemy\Phrasea\SearchEngine\Elastic\Fetcher\RecordFetcher;
use Alchemy\Phrasea\SearchEngine\Elastic\Fetcher\RecordPoolFetcher;
use Alchemy\Phrasea\SearchEngine\Elastic\Indexer\RecordIndexer;
use Alchemy\Phrasea\SearchEngine\Elastic\Indexer\TermIndexer;
use Alchemy\Phrasea\SearchEngine\Elastic\RecordHelper;
use Elasticsearch\Client;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\KernelEvents;
class ElasticSearchSubscriber implements EventSubscriberInterface
{
/** Pool of records to index or remove from ElasticSearch */
private static $recordsToAdd;
private static $recordsToRemove;
private static $recordsToUpdate;
private $recordIndexer;
private $termIndexer;
private $client;
private $appbox;
private $indexName;
public function __construct(RecordIndexer $recordIndexer, TermIndexer $termIndexer, Client $client, \appbox $appbox, $indexName)
{
$this->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 = <<<SQL
UPDATE record
SET jeton = (jeton | :token)
WHERE (jeton & :token) = 0
SQL;
$connection->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 = <<<SQL
UPDATE record
SET jeton = (jeton | :token)
WHERE coll_id = :coll_id
AND (jeton & :token) = 0
SQL;
$connection->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]);
}
}

View File

@@ -0,0 +1,83 @@
<?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\Core\Event\RecordEvent\BuildSubDefEvent;
use Alchemy\Phrasea\Core\Event\RecordEvent\ChangeCollectionEvent;
use Alchemy\Phrasea\Core\Event\RecordEvent\ChangeMetadataEvent;
use Alchemy\Phrasea\Core\Event\RecordEvent\ChangeOriginalNameEvent;
use Alchemy\Phrasea\Core\Event\RecordEvent\ChangeStatusEvent;
use Alchemy\Phrasea\Core\Event\RecordEvent\CreateRecordEvent;
use Alchemy\Phrasea\Core\Event\RecordEvent\DeleteRecordEvent;
use Alchemy\Phrasea\Core\Event\RecordEvent\RecordIndexEvent;
use Alchemy\Phrasea\Core\PhraseaEvents;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class RecordSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents()
{
return [
PhraseaEvents::RECORD_CHANGE_COLLECTION => '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()));
}
}

View File

@@ -0,0 +1,46 @@
<?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\Core\Event\RecordEvent\CreateStoryEvent;
use Alchemy\Phrasea\Core\Event\RecordEvent\DeleteStoryEvent;
use Alchemy\Phrasea\Core\PhraseaEvents;
use Alchemy\Phrasea\SearchEngine\Elastic\Indexer\RecordIndexer;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class StorySubscriber implements EventSubscriberInterface
{
private $indexer;
public function __construct(RecordIndexer $indexer)
{
$this->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());
}
}

View File

@@ -29,7 +29,6 @@ final class PhraseaEvents
const ORDER_CREATE = 'order.create'; const ORDER_CREATE = 'order.create';
const ORDER_DELIVER = 'order.deliver'; const ORDER_DELIVER = 'order.deliver';
const ORDER_DENY = 'order.deny'; const ORDER_DENY = 'order.deny';
const COLLECTION_CREATE = 'collection.create';
const FEED_ENTRY_CREATE = 'feed-entry.create'; const FEED_ENTRY_CREATE = 'feed-entry.create';
const REGISTRATION_CREATE = 'registration.create'; const REGISTRATION_CREATE = 'registration.create';
const REGISTRATION_AUTOREGISTER = 'registration.autoregister'; const REGISTRATION_AUTOREGISTER = 'registration.autoregister';
@@ -42,4 +41,31 @@ final class PhraseaEvents
const BRIDGE_UPLOAD_FAILURE = 'bridge.upload-failure'; const BRIDGE_UPLOAD_FAILURE = 'bridge.upload-failure';
const EXPORT_MAIL_FAILURE = 'export.mail-failure'; const EXPORT_MAIL_FAILURE = 'export.mail-failure';
const EXPORT_CREATE = 'export.create'; 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';
} }

View File

@@ -0,0 +1,22 @@
<?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;
final class PhraseaTokens
{
const TOKEN_MAKE_SUBDEF = 0x01;
const TOKEN_WRITE_META_DOC = 0x02;
const TOKEN_WRITE_META_SUBDEF = 0x04;
const TOKEN_WRITE_META = 0x06;
const TOKEN_INDEX = 0x08;
const TOKEN_INDEXING = 0x10;
}

View File

@@ -11,11 +11,16 @@
namespace Alchemy\Phrasea\Core\Provider; namespace Alchemy\Phrasea\Core\Provider;
use Alchemy\Phrasea\Core\Event\Subscriber\CollectionSubscriber;
use Alchemy\Phrasea\Core\Event\Subscriber\ContentNegotiationSubscriber; use Alchemy\Phrasea\Core\Event\Subscriber\ContentNegotiationSubscriber;
use Alchemy\Phrasea\Core\Event\Subscriber\CookiesDisablerSubscriber; use Alchemy\Phrasea\Core\Event\Subscriber\CookiesDisablerSubscriber;
use Alchemy\Phrasea\Core\Event\Subscriber\DataboxSubscriber;
use Alchemy\Phrasea\Core\Event\Subscriber\ElasticSearchSubscriber;
use Alchemy\Phrasea\Core\Event\Subscriber\LogoutSubscriber; use Alchemy\Phrasea\Core\Event\Subscriber\LogoutSubscriber;
use Alchemy\Phrasea\Core\Event\Subscriber\MaintenanceSubscriber; use Alchemy\Phrasea\Core\Event\Subscriber\MaintenanceSubscriber;
use Alchemy\Phrasea\Core\Event\Subscriber\PhraseaLocaleSubscriber; use Alchemy\Phrasea\Core\Event\Subscriber\PhraseaLocaleSubscriber;
use Alchemy\Phrasea\Core\Event\Subscriber\RecordSubscriber;
use Alchemy\Phrasea\Core\Event\Subscriber\StorySubscriber;
use Alchemy\Phrasea\Core\Event\Subscriber\SessionManagerSubscriber; use Alchemy\Phrasea\Core\Event\Subscriber\SessionManagerSubscriber;
use Silex\Application; use Silex\Application;
use Silex\ServiceProviderInterface; use Silex\ServiceProviderInterface;
@@ -42,6 +47,27 @@ class PhraseaEventServiceProvider implements ServiceProviderInterface
$app['phraseanet.content-negotiation-subscriber'] = $app->share(function (Application $app) { $app['phraseanet.content-negotiation-subscriber'] = $app->share(function (Application $app) {
return new ContentNegotiationSubscriber($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) public function boot(Application $app)

View File

@@ -69,7 +69,7 @@ class PhraseanetServiceProvider implements ServiceProviderInterface
}); });
$app['phraseanet.metadata-setter'] = $app->share(function (SilexApplication $app) { $app['phraseanet.metadata-setter'] = $app->share(function (SilexApplication $app) {
return new PhraseanetMetadataSetter(); return new PhraseanetMetadataSetter($app['dispatcher']);
}); });
$app['phraseanet.user-query'] = function (SilexApplication $app) { $app['phraseanet.user-query'] = function (SilexApplication $app) {

View File

@@ -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']); 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) { $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']);
}); });
} }

View File

@@ -15,6 +15,7 @@ use Alchemy\Phrasea\TaskManager\Job\FtpJob;
use Alchemy\Phrasea\TaskManager\Job\ArchiveJob; use Alchemy\Phrasea\TaskManager\Job\ArchiveJob;
use Alchemy\Phrasea\TaskManager\Job\BridgeJob; use Alchemy\Phrasea\TaskManager\Job\BridgeJob;
use Alchemy\Phrasea\TaskManager\Job\FtpPullJob; use Alchemy\Phrasea\TaskManager\Job\FtpPullJob;
use Alchemy\Phrasea\TaskManager\Job\IndexerJob;
use Alchemy\Phrasea\TaskManager\Job\PhraseanetIndexerJob; use Alchemy\Phrasea\TaskManager\Job\PhraseanetIndexerJob;
use Alchemy\Phrasea\TaskManager\Job\RecordMoverJob; use Alchemy\Phrasea\TaskManager\Job\RecordMoverJob;
use Alchemy\Phrasea\TaskManager\Job\SubdefsJob; use Alchemy\Phrasea\TaskManager\Job\SubdefsJob;
@@ -78,6 +79,7 @@ class TasksServiceProvider implements ServiceProviderInterface
new SubdefsJob($app['dispatcher'], $logger, $app['translator']), new SubdefsJob($app['dispatcher'], $logger, $app['translator']),
new WriteMetadataJob($app['dispatcher'], $logger, $app['translator']), new WriteMetadataJob($app['dispatcher'], $logger, $app['translator']),
new WebhookJob($app['dispatcher'], $logger, $app['translator']), new WebhookJob($app['dispatcher'], $logger, $app['translator']),
new IndexerJob($app['dispatcher'], $logger, $app['translator']),
]; ];
}); });
} }

View File

@@ -12,7 +12,8 @@
namespace Alchemy\Phrasea\Media; namespace Alchemy\Phrasea\Media;
use Alchemy\Phrasea\Application; 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\Alchemyst;
use MediaAlchemyst\Specification\SpecificationInterface; use MediaAlchemyst\Specification\SpecificationInterface;
use MediaVorus\MediaVorus; use MediaVorus\MediaVorus;
@@ -74,9 +75,7 @@ class SubdefGenerator
$record->clearSubdefCache($subdefname); $record->clearSubdefCache($subdefname);
if ($this->app['phraseanet.SE'] instanceof ElasticSearchEngine && in_array($subdefname, ['thumbnail', 'thumbnailgif', 'preview'])) { $this->app['dispatcher']->dispatch(PhraseaEvents::RECORD_BUILD_SUB_DEFINITION, new BuildSubDefEvent($record, $subDefName));
$this->app['phraseanet.SE']->updateRecord($record);
}
} }
return $this; return $this;

View File

@@ -12,10 +12,12 @@
namespace Alchemy\Phrasea\Media; namespace Alchemy\Phrasea\Media;
use Alchemy\Phrasea\Application; use Alchemy\Phrasea\Application;
use Alchemy\Phrasea\Core\PhraseaEvents;
use MediaAlchemyst\Alchemyst; use MediaAlchemyst\Alchemyst;
use MediaAlchemyst\Exception\ExceptionInterface as MediaAlchemystException; use MediaAlchemyst\Exception\ExceptionInterface as MediaAlchemystException;
use MediaVorus\Media\MediaInterface; use MediaVorus\Media\MediaInterface;
use MediaVorus\MediaVorus; use MediaVorus\MediaVorus;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Filesystem\Filesystem; use Symfony\Component\Filesystem\Filesystem;
class SubdefSubstituer class SubdefSubstituer
@@ -24,12 +26,13 @@ class SubdefSubstituer
private $fs; private $fs;
private $mediavorus; 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->alchemyst = $alchemyst;
$this->app = $app; $this->app = $app;
$this->fs = $fs; $this->fs = $fs;
$this->mediavorus = $mediavorus; $this->mediavorus = $mediavorus;
$this->dispatcher = $dispatcher;
} }
public function substitute(\record_adapter $record, $name, MediaInterface $media) public function substitute(\record_adapter $record, $name, MediaInterface $media)
@@ -92,5 +95,7 @@ class SubdefSubstituer
if ($name == 'document') { if ($name == 'document') {
$record->rebuild_subdefs(); $record->rebuild_subdefs();
} }
$this->dispatcher->dispatch(PhraseaEvents::RECORD_SUBSTITUTE, new SubstituteRecordEvent($record));
} }
} }

View File

@@ -12,9 +12,17 @@
namespace Alchemy\Phrasea\Metadata; namespace Alchemy\Phrasea\Metadata;
use PHPExiftool\Driver\Metadata\Metadata; use PHPExiftool\Driver\Metadata\Metadata;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
class PhraseanetMetadataSetter class PhraseanetMetadataSetter
{ {
private $dispatcher;
public function __construct(EventDispatcherInterface $dispatcher)
{
$this->disptatcher = $dispatcher;
}
public function replaceMetadata($metadataCollection, \record_adapter $record) public function replaceMetadata($metadataCollection, \record_adapter $record)
{ {
$metadatas = []; $metadatas = [];
@@ -93,6 +101,8 @@ class PhraseanetMetadataSetter
if (count($metas) > 0) { if (count($metas) > 0) {
$record->set_metadatas($metas, true); $record->set_metadatas($metas, true);
$this->disptatcher->dispatch(PhraseaEvents::RECORD_CHANGE_METADATA, new ChangeMetadataEvent($record));
} }
} }
} }

View File

@@ -49,11 +49,7 @@ class BulkOperation
public function index(array $params) public function index(array $params)
{ {
$header = array(); $this->stack[] = ['index' => $this->getBulkHeader($params)];
$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[] = igorw\get_in($params, ['body']); $this->stack[] = igorw\get_in($params, ['body']);
if ($this->flushLimit === count($this->stack) / 2) { 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() public function flush()
{ {
// Do not try to flush an empty stack // Do not try to flush an empty stack
@@ -76,7 +91,11 @@ class BulkOperation
} }
} }
$params['body'] = $this->stack; $params['body'] = $this->stack;
if (php_sapi_name() === 'cli') {
printf("ES Bulk query with %d items\n", count($this->stack) / 2); printf("ES Bulk query with %d items\n", count($this->stack) / 2);
}
$response = $this->client->bulk($params); $response = $this->client->bulk($params);
$this->stack = array(); $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'); 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;
}
} }

View File

@@ -188,7 +188,7 @@ class ElasticSearchEngine implements SearchEngineInterface
*/ */
public function addRecord(\record_adapter $record) 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; return $this;
} }

View File

@@ -9,28 +9,30 @@
* file that was distributed with this source code. * 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 Alchemy\Phrasea\SearchEngine\SearchEngineInterface;
use Doctrine\DBAL\Connection; use Doctrine\DBAL\Connection;
use databox; use databox;
use PDO;
class RecordFetcher abstract class AbstractRecordFetcher
{ {
private $connection; protected $statementRecords;
private $statementRecords; protected $connection;
protected $offset = 0;
protected $batchSize = 1;
private $helper; private $helper;
private $databox;
private $offset = 0; private $postFetch;
private $batchSize = 1;
private $databoxId;
public function __construct(databox $databox, RecordHelper $helper) public function __construct(databox $databox, RecordHelper $helper)
{ {
$this->connection = $databox->get_connection(); $this->connection = $databox->get_connection();
$this->databoxId = $databox->get_sbas_id(); $this->databox = $databox;
$this->helper = $helper; $this->helper = $helper;
} }
@@ -38,50 +40,44 @@ class RecordFetcher
{ {
$statementRecords = $this->statementRecords(); $statementRecords = $this->statementRecords();
// Fetch records rows if (php_sapi_name() === 'cli' && ($this->offset !== 0 || $statementRecords->rowCount() <= 0)) {
$statementRecords->execute(); printf("Query %d/%d -> %d rows on database %s\n", $this->offset, $this->batchSize, $statementRecords->rowCount(), $this->databox->get_dbname());
printf("Query %d/%d -> %d rows\n", $this->offset, $this->batchSize, $statementRecords->rowCount()); }
$records = []; $records = [];
while ($record = $statementRecords->fetch()) { while ($record = $statementRecords->fetch()) {
$records[$record['record_id']] = $record; $records[$record['record_id']] = $record;
printf("Record found (#%d)\n", $record['record_id']);
$this->offset++; $this->offset++;
} }
if (count($records) < 1) { if (count($records) < 1) {
if (php_sapi_name() === 'cli') {
printf("End of records\n"); printf("End of records\n");
}
return false; // End return false;
} }
$this->addTitleToRecord($records); $this->addTitleToRecord($records);
$this->addMetadataToRecords($records); $this->addMetadataToRecords($records);
$this->addSubdefsToRecord($records); $this->addSubDefinitionsToRecord($records);
// Hydrate records // Hydrate records
foreach ($records as $key => $record) { foreach ($records as $key => $record) {
$records[$key] = $this->hydrate($record); $records[$key] = $this->hydrate($record);
} }
if (is_callable($this->postFetch)) {
call_user_func($this->postFetch, $records);
}
return $records; return $records;
} }
public function fetchOne(\record_adapter $record_adapter) public function setPostFetch(\Closure $callable)
{ {
$stmt = $this->statementRecord($record_adapter->get_record_id()); $this->postFetch = $callable;
$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);
} }
public function setBatchSize($size) public function setBatchSize($size)
@@ -98,9 +94,9 @@ class RecordFetcher
$record['record_id'] = (int) $record['record_id']; $record['record_id'] = (int) $record['record_id'];
$record['collection_id'] = (int) $record['collection_id']; $record['collection_id'] = (int) $record['collection_id'];
// Some identifiers // Some identifiers
$record['id'] = $this->helper->getUniqueRecordId($this->databoxId, $record['record_id']); $record['id'] = $this->helper->getUniqueRecordId($this->databox->get_sbas_id(), $record['record_id']);
$record['base_id'] = $this->helper->getUniqueCollectionId($this->databoxId, $record['collection_id']); $record['base_id'] = $this->helper->getUniqueCollectionId($this->databox->get_sbas_id(), $record['collection_id']);
$record['databox_id'] = $this->databoxId; $record['databox_id'] = $this->databox->get_sbas_id();
if ((int) $record['parent_record_id'] === 1) { if ((int) $record['parent_record_id'] === 1) {
$record['record_type'] = SearchEngineInterface::GEM_TYPE_STORY; $record['record_type'] = SearchEngineInterface::GEM_TYPE_STORY;
@@ -117,63 +113,6 @@ class RecordFetcher
return $record; return $record;
} }
private function statementRecords()
{
if (!$this->statementRecords) {
$sql = <<<SQL
SELECT r.record_id
, r.coll_id as collection_id
, c.asciiname as collection_name
, r.uuid
, r.status as flags_bitmask
, r.sha256 -- TODO rename in "hash"
, r.originalname as original_name
, r.mime
, r.type
, r.parent_record_id
, r.credate as created_on
, r.moddate as updated_on
FROM record r
INNER JOIN coll c ON (c.coll_id = r.coll_id)
ORDER BY r.record_id ASC
LIMIT :offset, :limit
SQL;
$statement = $this->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 = <<<SQL
SELECT r.record_id
, r.coll_id as collection_id
, c.asciiname as collection_name
, r.uuid
, r.status as flags_bitmask
, r.sha256 -- TODO rename in "hash"
, r.originalname as original_name
, r.mime
, r.type
, r.parent_record_id
, r.credate as created_on
, r.moddate as updated_on
FROM record r
INNER JOIN coll c ON (c.coll_id = r.coll_id)
WHERE r.record_id = :id
SQL;
$statement = $this->connection->prepare($sql);
$statement->bindValue(':id', $id, PDO::PARAM_INT);
return $statement;
}
private function execStatementMetadata($ids) private function execStatementMetadata($ids)
{ {
$sql = <<<SQL $sql = <<<SQL
@@ -255,20 +194,20 @@ SQL;
} }
} }
private function addSubdefsToRecord(&$records) private function addSubDefinitionsToRecord(&$records)
{ {
$statementSubdef = $this->execStatementSubdefs(array_keys($records)); $statementSubDef = $this->execStatementSubDefinitions(array_keys($records));
while ($subdefs = $statementSubdef->fetch()) { while ($subDefinitions = $statementSubDef->fetch()) {
$records[$subdefs['record_id']]['subdefs'][$subdefs['name']] = array( $records[$subDefinitions['record_id']]['subdefs'][$subDefinitions['name']] = array(
'path' => $subdefs['path'], 'path' => $subDefinitions['path'],
'width' => $subdefs['width'], 'width' => $subDefinitions['width'],
'height' => $subdefs['height'], 'height' => $subDefinitions['height'],
); );
} }
} }
private function execStatementSubdefs($ids) private function execStatementSubDefinitions($ids)
{ {
$sql = <<<SQL $sql = <<<SQL
SELECT SELECT
@@ -285,4 +224,7 @@ SQL;
return $this->connection->executeQuery($sql, array($ids), array(Connection::PARAM_INT_ARRAY)); return $this->connection->executeQuery($sql, array($ids), array(Connection::PARAM_INT_ARRAY));
} }
/** Provides PDO Statement that fetches records */
abstract protected function statementRecords();
} }

View File

@@ -0,0 +1,47 @@
<?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\SearchEngine\Elastic\Fetcher;
use PDO;
class RecordFetcher extends AbstractRecordFetcher
{
protected function statementRecords()
{
$sql = <<<SQL
SELECT r.record_id
, r.coll_id as collection_id
, c.asciiname as collection_name
, r.uuid
, r.status as flags_bitmask
, r.sha256 -- TODO rename in "hash"
, r.originalname as original_name
, r.mime
, r.type
, r.parent_record_id
, r.credate as created_on
, r.moddate as updated_on
FROM record r
INNER JOIN coll c ON (c.coll_id = r.coll_id)
ORDER BY r.record_id ASC
LIMIT ?, ?
SQL;
return $this->connection->executeQuery($sql, [
$this->offset,
$this->batchSize,
], [
PDO::PARAM_INT,
PDO::PARAM_INT,
]);
}
}

View File

@@ -0,0 +1,72 @@
<?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\SearchEngine\Elastic\Fetcher;
use Alchemy\Phrasea\SearchEngine\Elastic\RecordHelper;
use Doctrine\DBAL\Connection;
use databox;
use PDO;
class RecordPoolFetcher extends AbstractRecordFetcher
{
/**
* @var \record_adapter[]
*/
private $pool;
public function __construct(databox $databox, RecordHelper $helper, array $records)
{
if (count($records) === 0) {
throw new \InvalidArgumentException('Pool of records must at least contain one record');
}
$this->pool = $records;
parent::__construct($databox, $helper);
}
protected function statementRecords()
{
$sql = <<<SQL
SELECT r.record_id
, r.coll_id as collection_id
, c.asciiname as collection_name
, r.uuid
, r.status as flags_bitmask
, r.sha256 -- TODO rename in "hash"
, r.originalname as original_name
, r.mime
, r.type
, r.parent_record_id
, r.credate as created_on
, r.moddate as updated_on
FROM record r
INNER JOIN coll c ON (c.coll_id = r.coll_id)
WHERE r.record_id IN (?)
ORDER BY r.record_id ASC
LIMIT ?, ?
SQL;
$records = array_map(function($record) {
return $record->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,
]);
}
}

View File

@@ -0,0 +1,56 @@
<?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\SearchEngine\Elastic\Fetcher;
use Alchemy\Phrasea\Core\PhraseaTokens;
use PDO;
class ScheduledIndexationRecordFetcher extends AbstractRecordFetcher
{
protected function statementRecords()
{
$sql = <<<SQL
SELECT r.record_id
, r.coll_id as collection_id
, c.asciiname as collection_name
, r.uuid
, r.status as flags_bitmask
, r.sha256 -- TODO rename in "hash"
, r.originalname as original_name
, r.mime
, r.type
, r.parent_record_id
, r.credate as created_on
, r.moddate as updated_on
FROM record r
INNER JOIN coll c ON (c.coll_id = r.coll_id)
WHERE (jeton & ?) > 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,
]);
}
}

View File

@@ -0,0 +1,63 @@
<?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\SearchEngine\Elastic\Fetcher;
use Alchemy\Phrasea\SearchEngine\Elastic\RecordHelper;
use databox;
use record_adapter;
use PDO;
class SingleRecordFetcher extends AbstractRecordFetcher
{
/**
* @var \record_adapter
*/
private $record;
public function __construct(databox $databox, RecordHelper $helper, record_adapter $record)
{
$this->record = $record;
parent::__construct($databox, $helper);
}
public function fetch()
{
$records = parent::fetch();
return array_pop($records);
}
protected function statementRecords()
{
$sql = <<<SQL
SELECT r.record_id
, r.coll_id as collection_id
, c.asciiname as collection_name
, r.uuid
, r.status as flags_bitmask
, r.sha256 -- TODO rename in "hash"
, r.originalname as original_name
, r.mime
, r.type
, r.parent_record_id
, r.credate as created_on
, r.moddate as updated_on
FROM record r
INNER JOIN coll c ON (c.coll_id = r.coll_id)
WHERE r.record_id = :record_id
ORDER BY r.record_id ASC
SQL;
return $this->connection->executeQuery($sql, [':record_id' => $this->record->get_record_id()], [PDO::PARAM_INT,]);
}
}

View File

@@ -122,7 +122,7 @@ class Indexer
} }
$event = $stopwatch->stop('populate'); $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() private function disableShardRefreshing()

View File

@@ -15,8 +15,11 @@ use Alchemy\Phrasea\SearchEngine\Elastic\BulkOperation;
use Alchemy\Phrasea\SearchEngine\Elastic\ElasticSearchEngine; use Alchemy\Phrasea\SearchEngine\Elastic\ElasticSearchEngine;
use Alchemy\Phrasea\SearchEngine\Elastic\Exception\Exception; use Alchemy\Phrasea\SearchEngine\Elastic\Exception\Exception;
use Alchemy\Phrasea\SearchEngine\Elastic\Exception\MergeException; 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\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\RecordHelper;
use Alchemy\Phrasea\SearchEngine\Elastic\StringUtils; use Alchemy\Phrasea\SearchEngine\Elastic\StringUtils;
use Alchemy\Phrasea\SearchEngine\Elastic\Thesaurus; use Alchemy\Phrasea\SearchEngine\Elastic\Thesaurus;
@@ -64,6 +67,7 @@ class RecordIndexer
$params = array(); $params = array();
$params['id'] = $record['id']; $params['id'] = $record['id'];
$params['type'] = self::TYPE_NAME; $params['type'] = self::TYPE_NAME;
$params['index'] = $this->elasticSearchEngine->getIndexName();
$params['body'] = $this->transform($record); $params['body'] = $this->transform($record);
$bulk->index($params); $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); while ($records = $fetcher->fetch()) {
$record = $fetcher->fetchOne($record_adapter); foreach ($records as $record) {
$params = array(); $params = array();
// header
$params['id'] = $record['id']; $params['id'] = $record['id'];
$params['type'] = self::TYPE_NAME; $params['type'] = self::TYPE_NAME;
$params['index'] = $indexName; $params['index'] = $this->elasticSearchEngine->getIndexName();
// body
$params['body'] = $this->transform($record); $params['body'] = $this->transform($record);
$bulk->index($params);
}
}
$bulk->flush();
}
return $this->elasticSearchEngine->getClient()->index($params); 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();
}
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) private function findLinkedConcepts($structure, array $record)
@@ -258,6 +298,7 @@ class RecordIndexer
private function transform($record) private function transform($record)
{ {
$dateFields = $this->elasticSearchEngine->getAvailableDateFields(); $dateFields = $this->elasticSearchEngine->getAvailableDateFields();
$structure = $this->getFieldsStructure(); $structure = $this->getFieldsStructure();
$databox = $this->appbox->get_databox($record['databox_id']); $databox = $this->appbox->get_databox($record['databox_id']);

View File

@@ -0,0 +1,52 @@
<?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\TaskManager\Editor;
use Alchemy\Phrasea\Core\Configuration\PropertyAccess;
class IndexerEditor extends AbstractEditor
{
/**
* {@inheritdoc}
*/
public function getTemplatePath()
{
return 'admin/task-manager/task-editor/indexer.html.twig';
}
/**
* {@inheritdoc}
*/
public function getDefaultPeriod()
{
return 10;
}
/**
* {@inheritdoc}
*/
public function getDefaultSettings(PropertyAccess $config = null)
{
return <<<XML
<?xml version="1.0" encoding="UTF-8"?>
<tasksettings></tasksettings>
XML;
}
/**
* {@inheritdoc}
*/
protected function getFormProperties()
{
return [];
}
}

View File

@@ -12,6 +12,8 @@
namespace Alchemy\Phrasea\TaskManager\Job; namespace Alchemy\Phrasea\TaskManager\Job;
use Alchemy\Phrasea\Application; use Alchemy\Phrasea\Application;
use Alchemy\Phrasea\Core\Event\RecordEvent\CreateStoryEvent;
use Alchemy\Phrasea\Core\PhraseaEvents;
use Alchemy\Phrasea\Exception\RuntimeException; use Alchemy\Phrasea\Exception\RuntimeException;
use Alchemy\Phrasea\Border\File; use Alchemy\Phrasea\Border\File;
use Alchemy\Phrasea\TaskManager\Editor\ArchiveEditor; 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->set_binary_status(\databox_status::operation_or($app, $stat0, $stat1));
$story->rebuild_subdefs(); $story->rebuild_subdefs();
$app['phraseanet.SE']->addStory($story); $app['dispatcher']->dispatch(PhraseaEvents::STORY_CREATE, new CreateStoryEvent($story));
unset($media); unset($media);

View File

@@ -0,0 +1,110 @@
<?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\TaskManager\Job;
use Alchemy\Phrasea\Core\PhraseaTokens;
use Alchemy\Phrasea\SearchEngine\Elastic\BulkOperation;
use Alchemy\Phrasea\SearchEngine\Elastic\Fetcher\ScheduledIndexationRecordFetcher;
use Alchemy\Phrasea\SearchEngine\Elastic\RecordHelper;
use Alchemy\Phrasea\TaskManager\Editor\IndexerEditor;
use Alchemy\Phrasea\TaskManager\Editor\SubdefsEditor;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Platforms\SQLAnywhere11Platform;
use Doctrine\DBAL\SQLParserUtils;
use MediaAlchemyst\Transmuter\Image2Image;
class IndexerJob extends AbstractJob
{
/**
* {@inheritdoc}
*/
public function getName()
{
return $this->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 = <<<SQL
UPDATE record
SET jeton = ((jeton | ?) & (jeton & ~ ?))
WHERE record_id IN (?)
SQL;
$records = array_map(function($record) {
return $record['record_id'];
}, $records);
$connection->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 = <<<SQL
UPDATE record
SET jeton = (jeton & ~ ?)
SQL;
$connection->executeQuery($sql, [PhraseaTokens::TOKEN_INDEXING], [\PDO::PARAM_INT]);
}
}
}

View File

@@ -12,7 +12,12 @@
namespace Alchemy\Phrasea\TaskManager\Job; namespace Alchemy\Phrasea\TaskManager\Job;
use Alchemy\Phrasea\Application; 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 Alchemy\Phrasea\TaskManager\Editor\RecordMoverEditor;
use JMS\Serializer\EventDispatcher\EventDispatcher;
class RecordMoverJob extends AbstractJob class RecordMoverJob extends AbstractJob
{ {
@@ -78,7 +83,9 @@ class RecordMoverJob extends AbstractJob
if (array_key_exists('coll', $row)) { if (array_key_exists('coll', $row)) {
$coll = \collection::get_from_coll_id($app, $databox, $row['coll']); $coll = \collection::get_from_coll_id($app, $databox, $row['coll']);
$rec->move_to_collection($coll, $app['phraseanet.appbox']); $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) { 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())); $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)); $rec->set_binary_status(implode('', $status));
$app['phraseanet.SE']->updateRecord($rec);
$app['dispatcher']->dispatch(PhraseaEvents::RECORD_CHANGE_STATUS, new ChangeStatusEvent($rec));
if ($logsql) { if ($logsql) {
$this->log('debug', sprintf("on sbas %s set rid %s status to %s \n", $row['sbas_id'], $row['record_id'], $status)); $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()) { if ($row['deletechildren'] && $rec->is_grouping()) {
foreach ($rec->get_children() as $child) { foreach ($rec->get_children() as $child) {
$child->delete(); $child->delete();
$app['phraseanet.SE']->removeRecord($child);
$app['dispatcher']->dispatch(PhraseaEvents::RECORD_DELETE, new DeleteRecordEvent($child));
if ($logsql) { if ($logsql) {
$this->log('debug', sprintf("on sbas %s delete (grp child) rid %s \n", $row['sbas_id'], $child->get_record_id())); $this->log('debug', sprintf("on sbas %s delete (grp child) rid %s \n", $row['sbas_id'], $child->get_record_id()));
} }
} }
} }
$rec->delete(); $rec->delete();
$app['phraseanet.SE']->removeRecord($rec); $app['dispatcher']->dispatch(PhraseaEvents::RECORD_DELETE, new DeleteRecordEvent($rec));
if ($logsql) { if ($logsql) {
$this->log('debug', sprintf("on sbas %s delete rid %s \n", $row['sbas_id'], $rec->get_record_id())); $this->log('debug', sprintf("on sbas %s delete rid %s \n", $row['sbas_id'], $rec->get_record_id()));
} }

View File

@@ -11,6 +11,7 @@
namespace Alchemy\Phrasea\TaskManager\Job; namespace Alchemy\Phrasea\TaskManager\Job;
use Alchemy\Phrasea\Core\PhraseaTokens;
use Alchemy\Phrasea\TaskManager\Editor\SubdefsEditor; use Alchemy\Phrasea\TaskManager\Editor\SubdefsEditor;
use MediaAlchemyst\Transmuter\Image2Image; use MediaAlchemyst\Transmuter\Image2Image;
@@ -67,7 +68,7 @@ class SubdefsJob extends AbstractJob
$sql = 'SELECT coll_id, record_id $sql = 'SELECT coll_id, record_id
FROM record FROM record
WHERE jeton & ' . JETON_MAKE_SUBDEF . ' > 0 WHERE jeton & ' . PhraseaTokens::TOKEN_MAKE_SUBDEF . ' > 0
ORDER BY record_id DESC LIMIT 0, 30'; ORDER BY record_id DESC LIMIT 0, 30';
$stmt = $conn->prepare($sql); $stmt = $conn->prepare($sql);
$stmt->execute(); $stmt->execute();
@@ -88,7 +89,7 @@ class SubdefsJob extends AbstractJob
} }
$sql = 'UPDATE record $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'; WHERE record_id=:record_id';
$stmt = $conn->prepare($sql); $stmt = $conn->prepare($sql);
@@ -98,7 +99,7 @@ class SubdefsJob extends AbstractJob
// rewrite metadata // rewrite metadata
$sql = 'UPDATE record $sql = 'UPDATE record
SET status=(status & ~0x03), SET status=(status & ~0x03),
jeton=(jeton | ' . JETON_WRITE_META_SUBDEF . ') jeton=(jeton | ' . PhraseaTokens::TOKEN_MAKE_SUBDEF . ')
WHERE record_id=:record_id'; WHERE record_id=:record_id';
$stmt = $conn->prepare($sql); $stmt = $conn->prepare($sql);
$stmt->execute([':record_id' => $row['record_id']]); $stmt->execute([':record_id' => $row['record_id']]);

View File

@@ -11,6 +11,7 @@
namespace Alchemy\Phrasea\TaskManager\Job; namespace Alchemy\Phrasea\TaskManager\Job;
use Alchemy\Phrasea\Core\PhraseaTokens;
use Alchemy\Phrasea\TaskManager\Editor\WriteMetadataEditor; use Alchemy\Phrasea\TaskManager\Editor\WriteMetadataEditor;
use PHPExiftool\Driver\Metadata; use PHPExiftool\Driver\Metadata;
use PHPExiftool\Driver\Value; 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 = $conn->prepare($sql);
$stmt->execute(); $stmt->execute();
@@ -98,8 +99,8 @@ class WriteMetadataJob extends AbstractJob
$subdefs = []; $subdefs = [];
foreach ($record->get_subdefs() as $name => $subdef) { foreach ($record->get_subdefs() as $name => $subdef) {
$write_document = (($token & JETON_WRITE_META_DOC) && $name == 'document'); $write_document = (($token & PhraseaTokens::TOKEN_WRITE_META_DOC) && $name == 'document');
$write_subdef = (($token & JETON_WRITE_META_SUBDEF) && isset($metaSubdefs[$name . '_' . $type])); $write_subdef = (($token & PhraseaTokens::TOKEN_WRITE_META_SUBDEF) && isset($metaSubdefs[$name . '_' . $type]));
if (($write_document || $write_subdef) && $subdef->is_physically_present()) { if (($write_document || $write_subdef) && $subdef->is_physically_present()) {
$subdefs[$name] = $subdef->get_pathfile(); $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 = $conn->prepare($sql);
$stmt->execute([':record_id' => $record_id]); $stmt->execute([':record_id' => $record_id]);
$stmt->closeCursor(); $stmt->closeCursor();

View File

@@ -10,6 +10,9 @@
*/ */
use Alchemy\Phrasea\Application; 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 class caption_field implements cache_cacheableInterface
{ {
@@ -282,7 +285,7 @@ class caption_field implements cache_cacheableInterface
return $values; 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 $sql = 'SELECT count(id) as count_id FROM metadatas
WHERE meta_struct_id = :meta_struct_id'; 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 = $databox_field->get_databox()->get_record($row['record_id']);
$record->set_metadatas([]); $record->set_metadatas([]);
/** $dispatcher->dispatch(PhraseaEvents::RECORD_CHANGE_METADATA, new ChangeMetadataEvent($record));
* TODO NEUTRON add App
*/
$app['phraseanet.SE']->updateRecord($record);
unset($record); unset($record);
} catch (\Exception $e) { } catch (\Exception $e) {
@@ -374,7 +375,8 @@ class caption_field implements cache_cacheableInterface
$caption_field->delete(); $caption_field->delete();
$record->set_metadatas([]); $record->set_metadatas([]);
$app['phraseanet.SE']->updateRecord($record); $app['dispatcher']->dispatch(PhraseaEvents::RECORD_CHANGE_METADATA, new ChangeMetadataEvent($record));
unset($caption_field); unset($caption_field);
unset($record); unset($record);
} catch (\Exception $e) { } catch (\Exception $e) {

View File

@@ -16,6 +16,7 @@ use Doctrine\DBAL\Driver\Connection;
use Symfony\Component\Filesystem\Filesystem; use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Translation\TranslatorInterface; use Symfony\Component\Translation\TranslatorInterface;
use Alchemy\Phrasea\Core\PhraseaTokens;
class databox extends base class databox extends base
{ {
@@ -439,7 +440,7 @@ class databox extends base
$ret['thesaurus_indexed'] += $row['n']; $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 = $this->get_connection()->prepare($sql);
$stmt->execute(); $stmt->execute();
$rs = $stmt->fetchAll(PDO::FETCH_ASSOC); $rs = $stmt->fetchAll(PDO::FETCH_ASSOC);

View File

@@ -422,7 +422,7 @@ class databox_field implements cache_cacheableInterface
$stmt->closeCursor(); $stmt->closeCursor();
if ($this->renamed) { if ($this->renamed) {
caption_field::rename_all_metadatas($this); caption_field::rename_all_metadata($this->app['dispatcher'], $this);
$this->renamed = false; $this->renamed = false;
} }

View File

@@ -128,6 +128,7 @@ class databox_status
if ( ! isset(self::$_status[$sbas_id])) if ( ! isset(self::$_status[$sbas_id]))
self::$_status[$sbas_id] = new databox_status($app, $sbas_id); self::$_status[$sbas_id] = new databox_status($app, $sbas_id);
return self::$_status[$sbas_id]->status; return self::$_status[$sbas_id]->status;
} }

View File

@@ -14,6 +14,8 @@ use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Output\OutputInterface;
use Alchemy\Phrasea\Command\Command; use Alchemy\Phrasea\Command\Command;
use Alchemy\Phrasea\Core\PhraseaEvents;
use Alchemy\Phrasea\Core\Event\RecordEvent\ChangeMetadataEvent;
class module_console_fieldsMerge extends Command class module_console_fieldsMerge extends Command
{ {
@@ -196,7 +198,7 @@ class module_console_fieldsMerge extends Command
]], true); ]], true);
} }
$this->getService('phraseanet.SE')->updateRecord($record); $this->getService('dispatcher')->dispatch(PhraseaEvents::RECORD_CHANGE_METADATA, new ChangeMetadataEvent($record));
unset($record); unset($record);
} }

View File

@@ -23,6 +23,7 @@ use MediaVorus\MediaVorus;
use Rhumsaa\Uuid\Uuid; use Rhumsaa\Uuid\Uuid;
use Alchemy\Phrasea\Model\RecordInterface; use Alchemy\Phrasea\Model\RecordInterface;
use Symfony\Component\HttpFoundation\File\File as SymfoFile; use Symfony\Component\HttpFoundation\File\File as SymfoFile;
use Alchemy\Phrasea\Core\PhraseaTokens;
class record_adapter implements RecordInterface, cache_cacheableInterface 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->base_id = $collection->get_base_id();
$this->app['phraseanet.SE']->updateRecord($this);
$this->app['phraseanet.logger']($this->get_databox()) $this->app['phraseanet.logger']($this->get_databox())
->log($this, Session_Logger::EVENT_MOVE, $collection->get_coll_id(), ''); ->log($this, Session_Logger::EVENT_MOVE, $collection->get_coll_id(), '');
@@ -972,8 +971,6 @@ class record_adapter implements RecordInterface, cache_cacheableInterface
); );
$stmt->closeCursor(); $stmt->closeCursor();
$this->reindex();
return $this; 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)); $xml->loadXML($this->app['serializer.caption']->serialize($this->get_caption(), CaptionSerializer::SERIALIZE_XML, true));
$this->set_xml($xml); $this->set_xml($xml);
$this->reindex();
unset($xml); unset($xml);
return $this; 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 * @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()); $databox = $this->app['phraseanet.appbox']->get_databox($this->get_sbas_id());
$connbas = $databox->get_connection(); $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 = $connbas->prepare($sql);
$stmt->execute([':record_id' => $this->get_record_id()]); $stmt->execute([':record_id' => $this->get_record_id()]);
$stmt->closeCursor(); $stmt->closeCursor();
@@ -1112,7 +1094,7 @@ class record_adapter implements RecordInterface, cache_cacheableInterface
$databox = $this->app['phraseanet.appbox']->get_databox($this->get_sbas_id()); $databox = $this->app['phraseanet.appbox']->get_databox($this->get_sbas_id());
$connbas = $databox->get_connection(); $connbas = $databox->get_connection();
$sql = 'UPDATE record $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'; WHERE record_id= :record_id';
$stmt = $connbas->prepare($sql); $stmt = $connbas->prepare($sql);
$stmt->execute([':record_id' => $this->record_id]); $stmt->execute([':record_id' => $this->record_id]);
@@ -1928,6 +1910,8 @@ class record_adapter implements RecordInterface, cache_cacheableInterface
public function setStatus($status) public function setStatus($status)
{ {
$this->set_binary_status($status); $this->set_binary_status($status);
$this->delete_data_from_cache(self::CACHE_STATUS);
} }
/** {@inheritdoc} */ /** {@inheritdoc} */

View File

@@ -0,0 +1 @@
{% extends 'admin/task-manager/task-editor/task.html.twig' %}