diff --git a/lib/Alchemy/Phrasea/Application.php b/lib/Alchemy/Phrasea/Application.php index bfe2070327..bd109378f8 100644 --- a/lib/Alchemy/Phrasea/Application.php +++ b/lib/Alchemy/Phrasea/Application.php @@ -114,6 +114,7 @@ use Alchemy\Phrasea\Core\Provider\RepositoriesServiceProvider; use Alchemy\Phrasea\Core\Provider\SearchEngineServiceProvider; use Alchemy\Phrasea\Core\Provider\SerializerServiceProvider; use Alchemy\Phrasea\Core\Provider\SessionHandlerServiceProvider; +use Alchemy\Phrasea\Core\Provider\StatusServiceProvider; use Alchemy\Phrasea\Core\Provider\SubdefServiceProvider; use Alchemy\Phrasea\Core\Provider\TasksServiceProvider; use Alchemy\Phrasea\Core\Provider\TemporaryFilesystemServiceProvider; @@ -237,7 +238,7 @@ class Application extends SilexApplication $this->register(new FeedServiceProvider()); $this->register(new FtpServiceProvider()); $this->register(new GeonamesServiceProvider()); - + $this->register(new StatusServiceProvider()); $this['geonames.server-uri'] = $this->share(function (Application $app) { return $app['conf']->get(['registry', 'webservices', 'geonames-server'], 'http://geonames.alchemyasp.com/'); }); diff --git a/lib/Alchemy/Phrasea/Controller/Admin/Root.php b/lib/Alchemy/Phrasea/Controller/Admin/Root.php index 8379fca5cd..302a314f63 100644 --- a/lib/Alchemy/Phrasea/Controller/Admin/Root.php +++ b/lib/Alchemy/Phrasea/Controller/Admin/Root.php @@ -263,7 +263,7 @@ class Root implements ControllerProviderInterface $databox = $app['phraseanet.appbox']->get_databox($databox_id); - $status = $databox->get_statusbits(); + $statusStructure = $databox->getStatusStructure(); switch ($errorMsg = $request->query->get('error')) { case 'rights': @@ -283,8 +283,8 @@ class Root implements ControllerProviderInterface break; } - if (isset($status[$bit])) { - $status = $status[$bit]; + if ($statusStructure->hasStatus($bit)) { + $status = $statusStructure->getStatus($bit); } else { $status = [ "labeloff" => '', @@ -320,10 +320,12 @@ class Root implements ControllerProviderInterface $app->abort(403); } + $databox = $app['phraseanet.appbox']->get_databox($databox_id); + $error = false; try { - \databox_status::deleteStatus($app, $app['phraseanet.appbox']->get_databox($databox_id), $bit); + $app['status.provider']->deleteStatus($databox->getStatusStructure(), $bit); } catch (\Exception $e) { $error = true; } @@ -349,7 +351,9 @@ class Root implements ControllerProviderInterface 'labels_off' => $request->request->get('labels_off', []), ]; - \databox_status::updateStatus($app, $databox_id, $bit, $properties); + $databox = $app['phraseanet.appbox']->get_databox($databox_id); + + $app['status.provider']->updateStatus($databox->getStatusStructure(), $bit, $properties); if (null !== $request->request->get('delete_icon_off')) { \databox_status::deleteIcon($app, $databox_id, $bit, 'off'); diff --git a/lib/Alchemy/Phrasea/Controller/Api/V1.php b/lib/Alchemy/Phrasea/Controller/Api/V1.php index 364e780651..55bee046b5 100644 --- a/lib/Alchemy/Phrasea/Controller/Api/V1.php +++ b/lib/Alchemy/Phrasea/Controller/Api/V1.php @@ -11,6 +11,7 @@ namespace Alchemy\Phrasea\Controller\Api; +use Alchemy\Phrasea\Status\StatusStructure; use Silex\ControllerProviderInterface; use Alchemy\Phrasea\Cache\Cache as CacheInterface; use Alchemy\Phrasea\Core\PhraseaEvents; @@ -437,7 +438,7 @@ class V1 implements ControllerProviderInterface */ public function get_databox_status(Application $app, Request $request, $databox_id) { - $ret = ["status" => $this->list_databox_status($app['phraseanet.appbox']->get_databox($databox_id)->get_statusbits())]; + $ret = ["status" => $this->list_databox_status($app['phraseanet.appbox']->get_databox($databox_id)->getStatusStructure())]; return Result::create($request, $ret)->createResponse(); } @@ -757,7 +758,7 @@ class V1 implements ControllerProviderInterface { $record = $app['phraseanet.appbox']->get_databox($databox_id)->get_record($record_id); - $ret = ["status" => $this->list_record_status($app['phraseanet.appbox']->get_databox($databox_id), $record->get_status())]; + $ret = ["status" => $this->list_record_status($record)]; return Result::create($request, $ret)->createResponse(); } @@ -836,7 +837,7 @@ class V1 implements ControllerProviderInterface { $databox = $app['phraseanet.appbox']->get_databox($databox_id); $record = $databox->get_record($record_id); - $status_bits = $databox->get_statusbits(); + $statusStructure = $databox->getStatusStructure(); $status = $request->get('status'); @@ -852,7 +853,7 @@ class V1 implements ControllerProviderInterface if (!in_array($value, ['0', '1'])) { return $this->getBadRequest($app, $request); } - if (!isset($status_bits[$n])) { + if (!$statusStructure->hasStatus($n)) { return $this->getBadRequest($app, $request); } @@ -861,7 +862,7 @@ class V1 implements ControllerProviderInterface $record->set_binary_status(strrev($datas)); - $ret = ["status" => $this->list_record_status($databox, $record->get_status())]; + $ret = ["status" => $this->list_record_status($record)]; return Result::create($request, $ret)->createResponse(); } @@ -1304,13 +1305,11 @@ class V1 implements ControllerProviderInterface * * @return array */ - private function list_record_status(\databox $databox, $status) + private function list_record_status(\record_adapter $record) { - $status = strrev($status); - $ret = []; - foreach ($databox->get_statusbits() as $bit => $status_datas) { - $ret[] = ['bit' => $bit, 'state' => !!substr($status, ($bit - 1), 1)]; + foreach ($record->getStatusStructure() as $bit => $status) { + $ret[] = ['bit' => $bit, 'state' => \databox_status::bitIsSet($record->getStatusBitField(), $bit)]; } return $ret; @@ -1502,11 +1501,25 @@ class V1 implements ControllerProviderInterface * * @return array */ - private function list_databox_status(array $status) + private function list_databox_status(StatusStructure $statusStructure) { $ret = []; - foreach ($status as $n => $datas) { - $ret[] = ['bit' => $n, 'label_on' => $datas['labelon'], 'label_off' => $datas['labeloff'], 'labels' => ['en' => $datas['labels_on_i18n']['en'], 'fr' => $datas['labels_on_i18n']['fr'], 'de' => $datas['labels_on_i18n']['de'], 'nl' => $datas['labels_on_i18n']['nl'],], 'img_on' => $datas['img_on'], 'img_off' => $datas['img_off'], 'searchable' => !!$datas['searchable'], 'printable' => !!$datas['printable'],]; + foreach ($statusStructure as $bit => $status) { + $ret[] = [ + 'bit' => $bit, + 'label_on' => $status['labelon'], + 'label_off' => $status['labeloff'], + 'labels' => [ + 'en' => $status['labels_on_i18n']['en'], + 'fr' => $status['labels_on_i18n']['fr'], + 'de' => $status['labels_on_i18n']['de'], + 'nl' => $status['labels_on_i18n']['nl'], + ], + 'img_on' => $status['img_on'], + 'img_off' => $status['img_off'], + 'searchable' => (bool) $status['searchable'], + 'printable' => (bool) $status['printable'], + ]; } return $ret; diff --git a/lib/Alchemy/Phrasea/Controller/Prod/Edit.php b/lib/Alchemy/Phrasea/Controller/Prod/Edit.php index 5f31d96430..4d91f935d7 100644 --- a/lib/Alchemy/Phrasea/Controller/Prod/Edit.php +++ b/lib/Alchemy/Phrasea/Controller/Prod/Edit.php @@ -39,8 +39,7 @@ class Edit implements ControllerProviderInterface $records = RecordsRequest::fromRequest($app, $request, RecordsRequest::FLATTEN_YES_PRESERVE_STORIES, ['canmodifrecord']); $thesaurus = false; - $status = $ids = $elements = $suggValues = - $fields = $JSFields = []; + $status = $ids = $elements = $suggValues = $fields = $JSFields = []; $databox = null; $multipleDataboxes = count($records->databoxes()) > 1; @@ -120,16 +119,16 @@ class Edit implements ControllerProviderInterface * generate javascript status */ if ($app['acl']->get($app['authentication']->getUser())->has_right('changestatus')) { - $dbstatus = \databox_status::getDisplayStatus($app); - if (isset($dbstatus[$databox->get_sbas_id()])) { - foreach ($dbstatus[$databox->get_sbas_id()] as $n => $statbit) { - $status[$n] = []; - $status[$n]['label0'] = $statbit['labels_off_i18n'][$app['locale']]; - $status[$n]['label1'] = $statbit['labels_on_i18n'][$app['locale']]; - $status[$n]['img_off'] = $statbit['img_off']; - $status[$n]['img_on'] = $statbit['img_on']; - $status[$n]['_value'] = 0; - } + $statusStructure = $databox->getStatusStructure(); + foreach ($statusStructure as $statbit) { + $bit = $statbit['bit']; + + $status[$bit] = []; + $status[$bit]['label0'] = $statbit['labels_off_i18n'][$app['locale']]; + $status[$bit]['label1'] = $statbit['labels_on_i18n'][$app['locale']]; + $status[$bit]['img_off'] = $statbit['img_off']; + $status[$bit]['img_on'] = $statbit['img_on']; + $status[$bit]['_value'] = 0; } } diff --git a/lib/Alchemy/Phrasea/Controller/Prod/Property.php b/lib/Alchemy/Phrasea/Controller/Prod/Property.php index fde68a2d82..afdf8910c8 100644 --- a/lib/Alchemy/Phrasea/Controller/Prod/Property.php +++ b/lib/Alchemy/Phrasea/Controller/Prod/Property.php @@ -64,49 +64,42 @@ class Property implements ControllerProviderInterface } $records = RecordsRequest::fromRequest($app, $request, false, ['chgstatus']); - $databoxStatus = \databox_status::getDisplayStatus($app); - $statusBit = $nRec = []; - foreach ($records as $record) { - //perform logic - $sbasId = $record->get_databox()->get_sbas_id(); - - if (!isset($nRec[$sbasId])) { - $nRec[$sbasId] = ['stories' => 0, 'records' => 0]; - } - - $nRec[$sbasId]['records']++; - - if ($record->is_grouping()) { - $nRec[$sbasId]['stories']++; - } - - if (!isset($statusBit[$sbasId])) { - $statusBit[$sbasId] = isset($databoxStatus[$sbasId]) ? $databoxStatus[$sbasId] : []; - - foreach (array_keys($statusBit[$sbasId]) as $bit) { - $statusBit[$sbasId][$bit]['nset'] = 0; - } - } - - $status = strrev($record->get_status()); - - foreach (array_keys($statusBit[$sbasId]) as $bit) { - $statusBit[$sbasId][$bit]["nset"] += substr($status, $bit, 1) !== "0" ? 1 : 0; - } + if (count($records->databoxes()) > 1) { + return new Response($app['twig']->render('prod/actions/Property/index.html.twig', [ + 'records' => $records + ])); } - foreach ($records->databoxes() as $databox) { - $sbasId = $databox->get_sbas_id(); - foreach ($statusBit[$sbasId] as $bit => $values) { - $statusBit[$sbasId][$bit]["status"] = $values["nset"] == 0 ? 0 : ($values["nset"] == $nRec[$sbasId]['records'] ? 1 : 2); + $databox = current($records->databoxes()); + $statusStructure = $databox->getStatusStructure(); + $recordsStatuses = []; + + foreach ($records->received() as $record) { + foreach ($statusStructure as $status) { + $bit = $status['bit']; + + if (!isset($recordsStatuses[$bit])) { + $recordsStatuses[$bit] = $status; + } + + $statusSet = \databox_status::bitIsSet($record->getStatusBitField(), $bit); + + if (!isset($recordsStatuses[$bit]['flag'])) { + $recordsStatuses[$bit]['flag'] = (int) $statusSet; + } + + // if flag property was already set and the value is different from the previous one + // it means that records share different value for the same flag + if ($recordsStatuses[$bit]['flag'] !== (int) $statusSet) { + $recordsStatuses[$bit]['flag'] = 2; + } } } return new Response($app['twig']->render('prod/actions/Property/index.html.twig', [ 'records' => $records, - 'statusBit' => $statusBit, - 'nRec' => $nRec + 'status' => $recordsStatuses ])); } diff --git a/lib/Alchemy/Phrasea/Core/Provider/StatusServiceProvider.php b/lib/Alchemy/Phrasea/Core/Provider/StatusServiceProvider.php new file mode 100644 index 0000000000..f95d7c94cb --- /dev/null +++ b/lib/Alchemy/Phrasea/Core/Provider/StatusServiceProvider.php @@ -0,0 +1,39 @@ +share(function() use ($app) { + return new CacheStatusStructureProvider( + $app['cache'], + new XmlStatusStructureProvider($app['root.path'], $app['locales.available']) + ); + }); + + $app['factory.status-structure'] = $app->share(function() use ($app) { + return new StatusStructureFactory($app['status.provider']); + }); + } + + public function boot(Application $app) + { + } +} diff --git a/lib/Alchemy/Phrasea/Helper/User/Edit.php b/lib/Alchemy/Phrasea/Helper/User/Edit.php index d7732d5790..8359eed2a8 100644 --- a/lib/Alchemy/Phrasea/Helper/User/Edit.php +++ b/lib/Alchemy/Phrasea/Helper/User/Edit.php @@ -256,16 +256,16 @@ class Edit extends \Alchemy\Phrasea\Helper\Helper $sbas_id = \phrasea::sbasFromBas($this->app, $this->base_id); $databox = $this->app['phraseanet.appbox']->get_databox($sbas_id); - $status = $databox->get_statusbits(); + $statusStructure = $databox->getStatusStructure(); - foreach ($status as $bit => $datas) { + foreach ($statusStructure as $bit => $status) { $tbits_left[$bit]["nset"] = 0; - $tbits_left[$bit]["name"] = $datas['labels_off_i18n'][$this->app['locale']]; - $tbits_left[$bit]["icon"] = $datas["img_off"]; + $tbits_left[$bit]["name"] = $status['labels_off_i18n'][$this->app['locale']]; + $tbits_left[$bit]["icon"] = $status["img_off"]; $tbits_right[$bit]["nset"] = 0; - $tbits_right[$bit]["name"] = $datas['labels_on_i18n'][$this->app['locale']]; - $tbits_right[$bit]["icon"] = $datas["img_on"]; + $tbits_right[$bit]["name"] = $status['labels_on_i18n'][$this->app['locale']]; + $tbits_right[$bit]["icon"] = $status["img_on"]; } $vand_and = $vand_or = $vxor_and = $vxor_or = "0000"; diff --git a/lib/Alchemy/Phrasea/Model/Entities/ElasticsearchRecord.php b/lib/Alchemy/Phrasea/Model/Entities/ElasticsearchRecord.php index 0d5a7ed9e5..817a1ddc22 100644 --- a/lib/Alchemy/Phrasea/Model/Entities/ElasticsearchRecord.php +++ b/lib/Alchemy/Phrasea/Model/Entities/ElasticsearchRecord.php @@ -293,17 +293,16 @@ class ElasticsearchRecord implements RecordInterface, MutableRecordInterface $this->flags = $flags; } - public function setStatus($status) + public function setStatusBitField($status) { $this->status = $status; } /** - * Get status of current current as 32 bits binary string * - * Eg: 00000000001011100000000000011111 + * @return integer */ - public function getStatus() + public function getStatusBitField() { return $this->status; } diff --git a/lib/Alchemy/Phrasea/Model/Entities/User.php b/lib/Alchemy/Phrasea/Model/Entities/User.php index 4c91f2ecc8..30930e7a19 100644 --- a/lib/Alchemy/Phrasea/Model/Entities/User.php +++ b/lib/Alchemy/Phrasea/Model/Entities/User.php @@ -426,7 +426,7 @@ class User self::GENDER_MISS, self::GENDER_MR, self::GENDER_MRS - ], true)) { + ])) { throw new InvalidArgumentException(sprintf("Invalid gender %s.", $gender)); } diff --git a/lib/Alchemy/Phrasea/Model/MutableRecordInterface.php b/lib/Alchemy/Phrasea/Model/MutableRecordInterface.php index 6c6ed8d30f..f2aacdb9e5 100644 --- a/lib/Alchemy/Phrasea/Model/MutableRecordInterface.php +++ b/lib/Alchemy/Phrasea/Model/MutableRecordInterface.php @@ -34,8 +34,8 @@ interface MutableRecordInterface /** @param string $uuid */ public function setUuid($uuid); - /** @param string $status */ - public function setStatus($status); + /** @param integer $status */ + public function setStatusBitField($status); /** @param \DateTime $updated */ public function setUpdated(\DateTime $updated = null); diff --git a/lib/Alchemy/Phrasea/Model/RecordInterface.php b/lib/Alchemy/Phrasea/Model/RecordInterface.php index f964fa7603..ddc4542632 100644 --- a/lib/Alchemy/Phrasea/Model/RecordInterface.php +++ b/lib/Alchemy/Phrasea/Model/RecordInterface.php @@ -55,8 +55,8 @@ interface RecordInterface /** @return string */ public function getUuid(); - /** @return string */ - public function getStatus(); + /** @return integer */ + public function getStatusBitField(); /** @return ArrayCollection */ public function getExif(); diff --git a/lib/Alchemy/Phrasea/SearchEngine/Elastic/ElasticSearchEngine.php b/lib/Alchemy/Phrasea/SearchEngine/Elastic/ElasticSearchEngine.php index d9c6bd4299..48ac97c96b 100644 --- a/lib/Alchemy/Phrasea/SearchEngine/Elastic/ElasticSearchEngine.php +++ b/lib/Alchemy/Phrasea/SearchEngine/Elastic/ElasticSearchEngine.php @@ -435,9 +435,11 @@ class ElasticSearchEngine implements SearchEngineInterface return ['bool' => ['must_not' => ['match_all' => new \stdClass()]]]; } - $flagNamesMap = $this->getFlagsKey($this->app['phraseanet.appbox']); + $appbox = $this->app['phraseanet.appbox']; + + $flagNamesMap = $this->getFlagsKey($appbox); // Get flags rules - $flagRules = $this->getFlagsRules($acl, $grantedCollections); + $flagRules = $this->getFlagsRules($appbox, $acl, $grantedCollections); // Get intersection between collection ACLs and collection chosen by end user $aclRules = $this->getACLsByCollection($flagRules, $flagNamesMap); @@ -531,23 +533,25 @@ class ElasticSearchEngine implements SearchEngineInterface $flags = []; foreach ($appbox->get_databoxes() as $databox) { $databoxId = $databox->get_sbas_id(); - $status = $databox->get_statusbits(); - foreach($status as $bit => $stat) { - $flags[$databoxId][$bit] = RecordHelper::normalizeFlagKey($stat['labelon']); + $statusStructure = $databox->getStatusStructure(); + foreach($statusStructure as $bit => $status) { + $flags[$databoxId][$bit] = RecordHelper::normalizeFlagKey($status['labelon']); } } return $flags; } - private function getFlagsRules(\ACL $acl, array $collections) + private function getFlagsRules(\appbox $appbox, \ACL $acl, array $collections) { $rules = []; foreach ($collections as $collectionId) { $databoxId = \phrasea::sbasFromBas($this->app, $collectionId); + $databox = $appbox->get_databox($databoxId); + $mask_xor = $acl->get_mask_xor($collectionId); $mask_and = $acl->get_mask_and($collectionId); - foreach (range(0, 31) as $bit) { + foreach ($databox->getStatusStructure()->getBits() as $bit) { $rules[$databoxId][$collectionId][$bit] = $this->computeAccess( $mask_xor, $mask_and, diff --git a/lib/Alchemy/Phrasea/SearchEngine/Elastic/ElasticsearchRecordHydrator.php b/lib/Alchemy/Phrasea/SearchEngine/Elastic/ElasticsearchRecordHydrator.php index a105926f4e..75b4372318 100644 --- a/lib/Alchemy/Phrasea/SearchEngine/Elastic/ElasticsearchRecordHydrator.php +++ b/lib/Alchemy/Phrasea/SearchEngine/Elastic/ElasticsearchRecordHydrator.php @@ -37,7 +37,7 @@ class ElasticsearchRecordHydrator $updatedOn = igorw\get_in($data, ['updated_on']); $record->setUpdated($updatedOn ? new \DateTime($updatedOn) : $updatedOn); $record->setUuid(igorw\get_in($data, ['uuid'], '')); - $record->setStatus(igorw\get_in($data, ['flags_bitmask'], 0)); + $record->setStatusBitField(igorw\get_in($data, ['flags_bit_value'], 0)); $record->setTitles(new ArrayCollection((array) igorw\get_in($data, ['title'], []))); $record->setCaption(new ArrayCollection((array) igorw\get_in($data, ['caption'], []))); $record->setExif(new ArrayCollection((array) igorw\get_in($data, ['exif'], []))); diff --git a/lib/Alchemy/Phrasea/SearchEngine/Elastic/Indexer/RecordIndexer.php b/lib/Alchemy/Phrasea/SearchEngine/Elastic/Indexer/RecordIndexer.php index d8dc9d58e0..ccef87c4e2 100644 --- a/lib/Alchemy/Phrasea/SearchEngine/Elastic/Indexer/RecordIndexer.php +++ b/lib/Alchemy/Phrasea/SearchEngine/Elastic/Indexer/RecordIndexer.php @@ -250,6 +250,7 @@ class RecordIndexer $mapping->add('caption', $captionMapping); $privateCaptionMapping = new Mapping(); $mapping->add('private_caption', $privateCaptionMapping); + foreach ($this->getFieldsStructure() as $name => $params) { $m = $params['private'] ? $privateCaptionMapping : $captionMapping; $m->add($name, $params['type']); @@ -319,7 +320,7 @@ class RecordIndexer $mapping = new Mapping(); foreach ($this->appbox->get_databoxes() as $databox) { - foreach ($databox->get_statusbits() as $bit => $status) { + foreach ($databox->getStatusStructure() as $bit => $status) { $key = RecordHelper::normalizeFlagKey($status['labelon']); // We only add to mapping new statuses if (!$mapping->has($key)) { @@ -345,10 +346,10 @@ class RecordIndexer $structure = $this->getFieldsStructure(); $databox = $this->appbox->get_databox($record['databox_id']); - foreach ($databox->get_statusbits() as $bit => $status) { + foreach ($databox->getStatusStructure() as $bit => $status) { $key = RecordHelper::normalizeFlagKey($status['labelon']); - $record['flags'][$key] = \databox_status::bitIsSet($record['flags_bitmask'], $bit); + $record['flags'][$key] = \databox_status::bitIsSet($record['flags_bit_value'], $bit); } foreach ($dateFields as $field) { diff --git a/lib/Alchemy/Phrasea/SearchEngine/SphinxSearch/SphinxSearchEngine.php b/lib/Alchemy/Phrasea/SearchEngine/SphinxSearch/SphinxSearchEngine.php index 4eb9c78618..686e125f92 100644 --- a/lib/Alchemy/Phrasea/SearchEngine/SphinxSearch/SphinxSearchEngine.php +++ b/lib/Alchemy/Phrasea/SearchEngine/SphinxSearch/SphinxSearchEngine.php @@ -647,7 +647,7 @@ class SphinxSearchEngine implements SearchEngineInterface */ $status_opts = $options->getStatus(); foreach ($options->getDataboxes() as $databox) { - foreach ($databox->get_statusbits() as $n => $status) { + foreach ($databox->getStatusStructure() as $n => $status) { if (!array_key_exists($n, $status_opts)) continue; if (!array_key_exists($databox->get_sbas_id(), $status_opts[$n])) diff --git a/lib/Alchemy/Phrasea/Status/CacheStatusStructureProvider.php b/lib/Alchemy/Phrasea/Status/CacheStatusStructureProvider.php new file mode 100644 index 0000000000..77e81bcfbe --- /dev/null +++ b/lib/Alchemy/Phrasea/Status/CacheStatusStructureProvider.php @@ -0,0 +1,72 @@ +cache = $cache; + $this->provider = $provider; + } + + public function getStructure(\databox $databox) + { + if (false !== ($status = $this->cache->fetch($this->get_cache_key($databox->get_sbas_id())))) { + + return new StatusStructure($databox, new ArrayCollection(json_decode($status, true))); + } + + $structure = $this->provider->getStructure($databox); + + $this->cache->save($this->get_cache_key($databox->get_sbas_id()), json_encode($structure->toArray())); + + return $structure; + } + + public function deleteStatus(StatusStructure $structure, $bit) + { + $databox = $structure->getDatabox(); + + $this->provider->deleteStatus($structure, $bit); + + $this->cache->save($this->get_cache_key($databox->get_sbas_id()), json_encode($structure->toArray())); + + return $structure; + } + + public function updateStatus(StatusStructure $structure, $bit, array $properties) + { + $databox = $structure->getDatabox(); + + $this->provider->updateStatus($structure, $bit, $properties); + + $this->cache->save($this->get_cache_key($databox->get_sbas_id()), json_encode($structure->toArray())); + + return $structure; + } + + private function get_cache_key($databox_id) + { + return sprintf('status_%s', $databox_id); + } +} diff --git a/lib/Alchemy/Phrasea/Status/StatusStructure.php b/lib/Alchemy/Phrasea/Status/StatusStructure.php new file mode 100644 index 0000000000..6496536267 --- /dev/null +++ b/lib/Alchemy/Phrasea/Status/StatusStructure.php @@ -0,0 +1,145 @@ +databox = $databox; + $this->status = $status ?: new ArrayCollection(); + + $unique_id = md5(implode('-', array( + $this->databox->get_host(), + $this->databox->get_port(), + $this->databox->get_dbname() + ))); + + // path to status icon + $this->path = __DIR__ . "/../../../../config/status/" . $unique_id; + // url to status icon + $this->url = "/custom/status/" . $unique_id; + } + + public function getIterator() + { + return $this->status->getIterator(); + } + + /** + * Get url to status icons + * + * @return string + */ + public function getUrl() + { + return $this->url; + } + + /** + * Get path to status icons + * + * @return string + */ + public function getPath() + { + return $this->path; + } + + /** + * Get databox where belong status + * + * @return \databox + */ + public function getDatabox() + { + return $this->databox; + } + + /** + * Get the bits used in structure + * $ + * @return array[] int + */ + public function getBits() + { + return $this->status->getKeys(); + } + + /** + * Set new status at nth position + * + * @param int $nthBit + * @param array $status + * + * @return $this + */ + public function setStatus($nthBit, array $status) + { + $this->status->set($nthBit, $status); + + return $this; + } + + /** + * Check whether structure has nth status set + * + * @param int $nthBit + * + * @return bool + */ + public function hasStatus($nthBit) + { + return $this->status->containsKey($nthBit); + } + + /** + * Get status at nth position + * + * @param int $nthBit + * + * @return array|null + */ + public function getStatus($nthBit) + { + return $this->status->get($nthBit); + } + + /** + * Remove status at nth position + * + * @param int $nthBit + * + * @return $this + */ + public function removeStatus($nthBit) + { + $this->status->remove($nthBit); + + return $this; + } + + public function toArray() + { + return $this->status->toArray(); + } +} diff --git a/lib/Alchemy/Phrasea/Status/StatusStructureFactory.php b/lib/Alchemy/Phrasea/Status/StatusStructureFactory.php new file mode 100644 index 0000000000..73f9963a1a --- /dev/null +++ b/lib/Alchemy/Phrasea/Status/StatusStructureFactory.php @@ -0,0 +1,57 @@ +provider = $provider; + } + + /** + * Get the status structure according to the given databox + * + * @param \databox $databox + * + * @return StatusStructure + */ + public function getStructure(\databox $databox) + { + $databox_id = $databox->get_sbas_id(); + + if (isset($this->statusStructure[$databox_id])) { + return $this->statusStructure[$databox_id]; + } + + $this->statusStructure[$databox_id] = $this->provider->getStructure($databox); + + return $this->statusStructure[$databox_id]; + } +} diff --git a/lib/Alchemy/Phrasea/Status/StatusStructureProviderInterface.php b/lib/Alchemy/Phrasea/Status/StatusStructureProviderInterface.php new file mode 100644 index 0000000000..3311072a5f --- /dev/null +++ b/lib/Alchemy/Phrasea/Status/StatusStructureProviderInterface.php @@ -0,0 +1,47 @@ +rootPath = $rootPath; + $this->locales = $locales; + $this->fs = $fs ?: new Filesystem(); + } + + public function getStructure(\databox $databox) + { + $statusStructure = new StatusStructure($databox); + + $xmlPref = $databox->get_structure(); + $sxe = simplexml_load_string($xmlPref); + + if ($sxe === false) { + throw new \Exception('Failed to load database XML structure'); + } + + foreach ($sxe->statbits->bit as $sb) { + $bit = (int) ($sb['n']); + if ($bit < 4 && $bit > 31) { + continue; + } + + $status = []; + + $status['bit'] = $bit; + + $status['labeloff'] = (string) $sb['labelOff']; + $status['labelon'] = (string) $sb['labelOn']; + + foreach ($this->locales as $code => $language) { + $status['labels_on'][$code] = null; + $status['labels_off'][$code] = null; + } + + foreach ($sb->label as $label) { + $status['labels_'.$label['switch']][(string) $label['code']] = (string) $label; + } + + foreach ($this->locales as $code => $language) { + $status['labels_on_i18n'][$code] = '' !== trim($status['labels_on'][$code]) ? $status['labels_on'][$code] : $status['labelon']; + $status['labels_off_i18n'][$code] = '' !== trim($status['labels_off'][$code]) ? $status['labels_off'][$code] : $status['labeloff']; + } + + $status['img_off'] = null; + $status['img_on'] = null; + + if (is_file($statusStructure->getPath() . '-stat_' . $bit . '_0.gif')) { + $status['img_off'] = $statusStructure->getUrl() . '-stat_' . $bit . '_0.gif?etag='.md5_file($statusStructure->getPath() . '-stat_' . $bit . '_0.gif'); + $status['path_off'] = $statusStructure->getPath() . '-stat_' . $bit . '_0.gif'; + } + if (is_file($statusStructure->getPath() . '-stat_' . $bit . '_1.gif')) { + $status['img_on'] = $statusStructure->getUrl() . '-stat_' . $bit . '_1.gif?etag='.md5_file($statusStructure->getPath() . '-stat_' . $bit . '_1.gif'); + $status['path_on'] = $statusStructure->getPath() . '-stat_' . $bit . '_1.gif'; + } + + $status['searchable'] = isset($sb['searchable']) ? (int) $sb['searchable'] : 0; + $status['printable'] = isset($sb['printable']) ? (int) $sb['printable'] : 0; + + $statusStructure->setStatus($bit, $status); + } + + return $statusStructure; + } + + public function deleteStatus(StatusStructure $statusStructure, $bit) + { + $databox = $statusStructure->getDatabox(); + + if (false === $statusStructure->hasStatus($bit)) { + return false; + } + + $doc = $databox->get_dom_structure(); + + if (!$doc) { + return false; + } + + $xpath = $databox->get_xpath_structure(); + $entries = $xpath->query('/record/statbits/bit[@n=' . $bit . ']'); + + foreach ($entries as $sbit) { + if ($p = $sbit->previousSibling) { + if ($p->nodeType == XML_TEXT_NODE && $p->nodeValue == '\n\t\t') + $p->parentNode->removeChild($p); + } + if ($sbit->parentNode->removeChild($sbit)) { + $sql = 'UPDATE record SET status = status&(~(1<<' . $bit . '))'; + $stmt = $databox->get_connection()->prepare($sql); + $stmt->execute(); + $stmt->closeCursor(); + } + } + + $databox->saveStructure($doc); + + $status = $statusStructure->getStatus($bit); + + if (null !== $status['img_off']) { + $this->fs->remove($status['path_off']); + } + + if (null !== $status['img_on']) { + $this->fs->remove($status['path_on']); + } + + $statusStructure->removeStatus($bit); + + return $statusStructure; + } + + public function updateStatus(StatusStructure $statusStructure, $bit, array $properties) + { + $databox = $statusStructure->getDatabox(); + + if (false === $statusStructure->hasStatus($bit)) { + $statusStructure->setStatus($bit, []); + } + + $doc = $databox->get_dom_structure(); + + if (!$doc) { + return false; + } + + $xpath = $databox->get_xpath_structure(); + $entries = $xpath->query('/record/statbits'); + + if ($entries->length == 0) { + $statbits = $doc->documentElement->appendChild($doc->createElement('statbits')); + } else { + $statbits = $entries->item(0); + } + + if ($statbits) { + $entries = $xpath->query('/record/statbits/bit[@n=' . $bit . ']'); + + if ($entries->length >= 1) { + foreach ($entries as $k => $sbit) { + if ($p = $sbit->previousSibling) { + if ($p->nodeType == XML_TEXT_NODE && $p->nodeValue == '\n\t\t') + $p->parentNode->removeChild($p); + } + $sbit->parentNode->removeChild($sbit); + } + } + + $sbit = $statbits->appendChild($doc->createElement('bit')); + + if ($n = $sbit->appendChild($doc->createAttribute('n'))) { + $n->value = $bit; + } + + if ($labOn = $sbit->appendChild($doc->createAttribute('labelOn'))) { + $labOn->value = $properties['labelon']; + } + + if ($searchable = $sbit->appendChild($doc->createAttribute('searchable'))) { + $searchable->value = $properties['searchable']; + } + + if ($printable = $sbit->appendChild($doc->createAttribute('printable'))) { + $printable->value = $properties['printable']; + } + + if ($labOff = $sbit->appendChild($doc->createAttribute('labelOff'))) { + $labOff->value = $properties['labeloff']; + } + + foreach ($properties['labels_off'] as $code => $label) { + $labelTag = $sbit->appendChild($doc->createElement('label')); + $switch = $labelTag->appendChild($doc->createAttribute('switch')); + $switch->value = 'off'; + $codeTag = $labelTag->appendChild($doc->createAttribute('code')); + $codeTag->value = $code; + $labelTag->appendChild($doc->createTextNode($label)); + } + + foreach ($properties['labels_on'] as $code => $label) { + $labelTag = $sbit->appendChild($doc->createElement('label')); + $switch = $labelTag->appendChild($doc->createAttribute('switch')); + $switch->value = 'on'; + $codeTag = $labelTag->appendChild($doc->createAttribute('code')); + $codeTag->value = $code; + $labelTag->appendChild($doc->createTextNode($label)); + } + } + + $databox->saveStructure($doc); + + $status = $statusStructure->getStatus($bit); + + $status['labelon'] = $properties['labelon']; + $status['labeloff'] = $properties['labeloff']; + $status['searchable'] = (Boolean) $properties['searchable']; + $status['printable'] = (Boolean) $properties['printable']; + + if (!isset($properties['img_on'])) { + $status['img_on'] = null; + } + + if (!isset($properties['img_off'])) { + $status['img_off'] = null; + } + + $statusStructure->setStatus($bit, $status); + + return $statusStructure; + } +} diff --git a/lib/Alchemy/Phrasea/Twig/PhraseanetExtension.php b/lib/Alchemy/Phrasea/Twig/PhraseanetExtension.php index 0624d7d3c2..d42c14cb76 100644 --- a/lib/Alchemy/Phrasea/Twig/PhraseanetExtension.php +++ b/lib/Alchemy/Phrasea/Twig/PhraseanetExtension.php @@ -37,9 +37,41 @@ class PhraseanetExtension extends \Twig_Extension new \Twig_SimpleFunction('collection_logo', array($this, 'getCollectionLogo'), array( 'is_safe' => array('html') )), + new \Twig_SimpleFunction('record_flags', array($this, 'getRecordFlags')) ); } + public function getRecordFlags(RecordInterface $record) + { + $recordStatuses = []; + $databox = $this->app['phraseanet.appbox']->get_databox($record->getDataboxId()); + + $structure = $databox->getStatusStructure()->toArray(); + + if (!$this->isGrantedOnCollection($record->getBaseId(), 'chgstatus')) { + $structure = array_filter($structure, function($status) { + return (bool) $status['printable']; + }); + } + + $bitValue = $record->getStatusBitField(); + + foreach ($structure as $status) { + $on = \databox_status::bitIsSet($bitValue, $status['bit']); + + if (null === ($on ? $status['img_on'] : $status['img_off'])) { + continue; + } + + $recordStatuses[] = [ + 'path' => ($on ? $status['img_on'] : $status['img_off']), + 'labels' => ($on ? $status['labels_on_i18n'] : $status['labels_off_i18n']) + ]; + } + + return $recordStatuses; + } + public function isGrantedOnDatabox($databoxId, $rights) { if (false === ($this->app['authentication']->getUser() instanceof User)) { diff --git a/lib/classes/ACL.php b/lib/classes/ACL.php index 303a888abb..b855ebfe1b 100644 --- a/lib/classes/ACL.php +++ b/lib/classes/ACL.php @@ -210,7 +210,7 @@ class ACL implements cache_cacheableInterface public function has_status_access_to_record(RecordInterface $record) { - return 0 === ((bindec($record->getStatus()) ^ $this->get_mask_xor($record->getBaseId())) & $this->get_mask_and($record->getBaseId())); + return 0 === ((bindec($record->getStatusBitField()) ^ $this->get_mask_xor($record->getBaseId())) & $this->get_mask_and($record->getBaseId())); } public function has_access_to_subdef(RecordInterface $record, $subdef_name) diff --git a/lib/classes/databox.php b/lib/classes/databox.php index fbcb27922d..3e865e9709 100644 --- a/lib/classes/databox.php +++ b/lib/classes/databox.php @@ -307,9 +307,9 @@ class databox extends base * * @return databox_status */ - public function get_statusbits() + public function getStatusStructure() { - return databox_status::getStatus($this->app, $this->id); + return $this->app['factory.status-structure']->getStructure($this); } /** diff --git a/lib/classes/databox/status.php b/lib/classes/databox/status.php index 575762879d..a3d429ebed 100644 --- a/lib/classes/databox/status.php +++ b/lib/classes/databox/status.php @@ -11,385 +11,101 @@ use Alchemy\Phrasea\Application; use Alchemy\Phrasea\SearchEngine\Elastic\RecordHelper; -use MediaAlchemyst\Specification\Image as ImageSpecification; use Symfony\Component\HttpFoundation\File\UploadedFile; +use Alchemy\Phrasea\Exception\InvalidArgumentException; +use MediaAlchemyst\Specification\Image as ImageSpecification; use Symfony\Component\HttpFoundation\File\Exception\FileException; +use Alchemy\Phrasea\Model\RecordInterface; class databox_status { - /** - * - * @var Array - */ - private static $_status = []; - - /** - * - * @var Array - */ - protected static $_statuses; - - /** - * - * @var Array - */ - private $status = []; - - /** - * - * @var string - */ - private $path = ''; - - /** - * - * @var string - */ - private $url = ''; - - /** - * - * @param int $sbas_id - * @return status - */ - private function __construct(Application $app, $sbas_id) - { - $this->status = []; - - $path = $url = false; - - $sbas_params = phrasea::sbas_params($app); - - if ( ! isset($sbas_params[$sbas_id])) { - return; - } - - $uniqid = md5(implode('-', [ - $sbas_params[$sbas_id]["host"], - $sbas_params[$sbas_id]["port"], - $sbas_params[$sbas_id]["dbname"] - ])); - - $path = $this->path = $app['root.path'] . "/config/status/" . $uniqid; - $url = $this->url = "/custom/status/" . $uniqid; - - $databox = $app['phraseanet.appbox']->get_databox((int) $sbas_id); - $xmlpref = $databox->get_structure(); - $sxe = simplexml_load_string($xmlpref); - - if ($sxe !== false) { - - foreach ($sxe->statbits->bit as $sb) { - $bit = (int) ($sb["n"]); - if ($bit < 4 && $bit > 31) - continue; - - $this->status[$bit]["labeloff"] = (string) $sb['labelOff']; - $this->status[$bit]["labelon"] = (string) $sb['labelOn']; - - foreach ($app['locales.available'] as $code => $language) { - $this->status[$bit]['labels_on'][$code] = null; - $this->status[$bit]['labels_off'][$code] = null; - } - - foreach ($sb->label as $label) { - $this->status[$bit]['labels_'.$label['switch']][(string) $label['code']] = (string) $label; - } - - foreach ($app['locales.available'] as $code => $language) { - $this->status[$bit]['labels_on_i18n'][$code] = '' !== trim($this->status[$bit]['labels_on'][$code]) ? $this->status[$bit]['labels_on'][$code] : $this->status[$bit]["labelon"]; - $this->status[$bit]['labels_off_i18n'][$code] = '' !== trim($this->status[$bit]['labels_off'][$code]) ? $this->status[$bit]['labels_off'][$code] : $this->status[$bit]["labeloff"]; - } - - $this->status[$bit]["img_off"] = null; - $this->status[$bit]["img_on"] = null; - - if (is_file($path . "-stat_" . $bit . "_0.gif")) { - $this->status[$bit]["img_off"] = $url . "-stat_" . $bit . "_0.gif?etag=".md5_file($path . "-stat_" . $bit . "_0.gif"); - $this->status[$bit]["path_off"] = $path . "-stat_" . $bit . "_0.gif"; - } - if (is_file($path . "-stat_" . $bit . "_1.gif")) { - $this->status[$bit]["img_on"] = $url . "-stat_" . $bit . "_1.gif?etag=".md5_file($path . "-stat_" . $bit . "_1.gif"); - $this->status[$bit]["path_on"] = $path . "-stat_" . $bit . "_1.gif"; - } - - $this->status[$bit]["searchable"] = isset($sb['searchable']) ? (int) $sb['searchable'] : 0; - $this->status[$bit]["printable"] = isset($sb['printable']) ? (int) $sb['printable'] : 0; - } - } - ksort($this->status); - - return $this; - } - - public static function getStatus(Application $app, $sbas_id) - { - - if ( ! isset(self::$_status[$sbas_id])) - self::$_status[$sbas_id] = new databox_status($app, $sbas_id); - - - return self::$_status[$sbas_id]->status; - } - - public static function getDisplayStatus(Application $app) - { - if (self::$_statuses) { - return self::$_statuses; - } - - $sbas_ids = $app['acl']->get($app['authentication']->getUser())->get_granted_sbas(); - - $statuses = []; - - foreach ($sbas_ids as $databox) { - try { - $statuses[$databox->get_sbas_id()] = $databox->get_statusbits(); - } catch (\Exception $e) { - - } - } - - self::$_statuses = $statuses; - - return self::$_statuses; - } - public static function getSearchStatus(Application $app) { - $statuses = $see_all = []; - $databoxes = $app['acl']->get($app['authentication']->getUser())->get_granted_sbas(); - - foreach ($databoxes as $databox) { + $see_all = $structures = $stats = []; + foreach ($app['acl']->get($app['authentication']->getUser())->get_granted_sbas() as $databox) { $see_all[$databox->get_sbas_id()] = false; - foreach ($databox->get_collections() as $collection) { if ($app['acl']->get($app['authentication']->getUser())->has_right_on_base($collection->get_base_id(), 'chgstatus')) { $see_all[$databox->get_sbas_id()] = true; break; } } - - try { - $statuses[$databox->get_sbas_id()] = $databox->get_statusbits(); - } catch (\Exception $e) { - - } + $structures[$databox->get_sbas_id()] = $databox->getStatusStructure(); } - $stats = []; + foreach ($structures as $databox_id => $structure) { + if (false === $see_all[$databox_id]) { + $structure = array_filter(function($status) { + return (bool) $status['searchable']; + }, $structure->toArray()); + } - foreach ($statuses as $databox_id => $status) { - $canSeeAll = isset($see_all[$databox_id]) ? $see_all[$databox_id] : false; - $canSeeThis = $app['acl']->get($app['authentication']->getUser())->has_right_on_sbas($databox_id, 'bas_modify_struct'); + foreach($structure as $bit => $status) { + $key = RecordHelper::normalizeFlagKey($status['labelon']); - $canAccess = $canSeeAll || $canSeeThis; - - foreach ($status as $bit => $props) { - if (!$props['searchable'] && !$canAccess) { - continue; + if (isset($stats[$key])) { + $status = $stats[$key]; } - $stats[$databox_id][$bit] = array( - 'name' => RecordHelper::normalizeFlagKey($props['labelon']), - 'labeloff' => $props['labeloff'], - 'labelon' => $props['labelon'], - 'labels_on_i18n' => $props['labels_on_i18n'], - 'labels_off_i18n' => $props['labels_off_i18n'], - 'imgoff' => $props['img_off'], - 'imgon' => $props['img_on'] - ); + + $status['sbas'][] = $databox_id; + $status['bit'] = $bit; + + $stats[$key] = $status; } } - return $stats; } - public static function getPath(Application $app, $sbas_id) + public static function deleteIcon(Application $app, $databox_id, $bit, $switch) { - if ( ! isset(self::$_status[$sbas_id])) { - self::$_status[$sbas_id] = new databox_status($app, $sbas_id); + $databox = $app['phraseanet.appbox']->get_databox($databox_id); + + $statusStructure = $app['factory.status-structure']->getStructure($databox); + + if (!$statusStructure->hasStatus($bit)) { + throw new InvalidArgumentException(sprintf('bit %s does not exists on database %s', $bit, $statusStructure->getDatabox()->get_dbname())); } - return self::$_status[$sbas_id]->path; - } - - public static function getUrl(Application $app, $sbas_id) - { - if ( ! isset(self::$_status[$sbas_id])) { - self::$_status[$sbas_id] = new databox_status($app, $sbas_id); - } - - return self::$_status[$sbas_id]->url; - } - - public static function deleteStatus(Application $app, \databox $databox, $bit) - { - $status = self::getStatus($app, $databox->get_sbas_id()); - - if (isset($status[$bit])) { - $doc = $databox->get_dom_structure(); - if ($doc) { - $xpath = $databox->get_xpath_structure(); - $entries = $xpath->query("/record/statbits/bit[@n=" . $bit . "]"); - - foreach ($entries as $sbit) { - if ($p = $sbit->previousSibling) { - if ($p->nodeType == XML_TEXT_NODE && $p->nodeValue == "\n\t\t") - $p->parentNode->removeChild($p); - } - if ($sbit->parentNode->removeChild($sbit)) { - $sql = 'UPDATE record SET status = status&(~(1<<' . $bit . '))'; - $stmt = $databox->get_connection()->prepare($sql); - $stmt->execute(); - $stmt->closeCursor(); - } - } - - $databox->saveStructure($doc); - - if (null !== $status[$bit]['img_off']) { - $app['filesystem']->remove($status[$bit]['path_off']); - } - - if (null !== $status[$bit]['img_on']) { - $app['filesystem']->remove($status[$bit]['path_on']); - } - - unset(self::$_status[$databox->get_sbas_id()]->status[$bit]); - - $app['dispatcher']->dispatch(RecordStructureEvents::STATUS_BIT_DELETED, new StatusBitDeletedEvent($databox, $bit)); - - return true; - } - } - - return false; - } - - public static function updateStatus(Application $app, $sbas_id, $bit, $properties) - { - self::getStatus($app, $sbas_id); - - $databox = $app['phraseanet.appbox']->get_databox((int) $sbas_id); - - $doc = $databox->get_dom_structure($sbas_id); - if ($doc) { - $xpath = $databox->get_xpath_structure($sbas_id); - $entries = $xpath->query("/record/statbits"); - if ($entries->length == 0) { - $statbits = $doc->documentElement->appendChild($doc->createElement("statbits")); - } else { - $statbits = $entries->item(0); - } - - if ($statbits) { - $entries = $xpath->query("/record/statbits/bit[@n=" . $bit . "]"); - - if ($entries->length >= 1) { - foreach ($entries as $k => $sbit) { - if ($p = $sbit->previousSibling) { - if ($p->nodeType == XML_TEXT_NODE && $p->nodeValue == "\n\t\t") - $p->parentNode->removeChild($p); - } - $sbit->parentNode->removeChild($sbit); - } - } - - $sbit = $statbits->appendChild($doc->createElement("bit")); - - if ($n = $sbit->appendChild($doc->createAttribute("n"))) { - $n->value = $bit; - } - - if ($labOn = $sbit->appendChild($doc->createAttribute("labelOn"))) { - $labOn->value = $properties['labelon']; - } - - if ($searchable = $sbit->appendChild($doc->createAttribute("searchable"))) { - $searchable->value = $properties['searchable']; - } - - if ($printable = $sbit->appendChild($doc->createAttribute("printable"))) { - $printable->value = $properties['printable']; - } - - if ($labOff = $sbit->appendChild($doc->createAttribute("labelOff"))) { - $labOff->value = $properties['labeloff']; - } - - foreach ($properties['labels_off'] as $code => $label) { - $labelTag = $sbit->appendChild($doc->createElement("label")); - $switch = $labelTag->appendChild($doc->createAttribute("switch")); - $switch->value = 'off'; - $codeTag = $labelTag->appendChild($doc->createAttribute("code")); - $codeTag->value = $code; - $labelTag->appendChild($doc->createTextNode($label)); - } - - foreach ($properties['labels_on'] as $code => $label) { - $labelTag = $sbit->appendChild($doc->createElement("label")); - $switch = $labelTag->appendChild($doc->createAttribute("switch")); - $switch->value = 'on'; - $codeTag = $labelTag->appendChild($doc->createAttribute("code")); - $codeTag->value = $code; - $labelTag->appendChild($doc->createTextNode($label)); - } - } - - $databox->saveStructure($doc); - - self::$_status[$sbas_id]->status[$bit]["labelon"] = $properties['labelon']; - self::$_status[$sbas_id]->status[$bit]["labeloff"] = $properties['labeloff']; - self::$_status[$sbas_id]->status[$bit]["searchable"] = (Boolean) $properties['searchable']; - self::$_status[$sbas_id]->status[$bit]["printable"] = (Boolean) $properties['printable']; - - if ( ! isset(self::$_status[$sbas_id]->status[$bit]['img_on'])) { - self::$_status[$sbas_id]->status[$bit]['img_on'] = null; - } - - if ( ! isset(self::$_status[$sbas_id]->status[$bit]['img_off'])) { - self::$_status[$sbas_id]->status[$bit]['img_off'] = null; - } - - $properties = self::$_status[$sbas_id]->status[$bit]; - $app['dispatcher']->dispatch(RecordStructureEvents::STATUS_BIT_UPDATED, new StatusBitUpdatedEvent($databox, $bit, $properties)); - } - - return false; - } - - public static function deleteIcon(Application $app, $sbas_id, $bit, $switch) - { - $status = self::getStatus($app, $sbas_id); + $status = $statusStructure->getStatus($bit); $switch = in_array($switch, ['on', 'off']) ? $switch : false; - if (! $switch) { + if (!$switch) { return false; } - if ($status[$bit]['img_' . $switch]) { - if (isset($status[$bit]['path_' . $switch])) { - $app['filesystem']->remove($status[$bit]['path_' . $switch]); + if ($status['img_' . $switch]) { + if (isset($status['path_' . $switch])) { + $app['filesystem']->remove($status['path_' . $switch]); } - $status[$bit]['img_' . $switch] = false; - unset($status[$bit]['path_' . $switch]); + $status['img_' . $switch] = false; + unset($status['path_' . $switch]); } return true; } - public static function updateIcon(Application $app, $sbas_id, $bit, $switch, UploadedFile $file) + public static function updateIcon(Application $app, $databox_id, $bit, $switch, UploadedFile $file) { + $databox = $app['phraseanet.appbox']->get_databox($databox_id); + + $statusStructure = $app['factory.status-structure']->getStructure($databox); + + if (!$statusStructure->hasStatus($bit)) { + throw new InvalidArgumentException(sprintf('bit %s does not exists', $bit)); + } + + $status = $statusStructure->getStatus($bit); + $switch = in_array($switch, ['on', 'off']) ? $switch : false; - if (! $switch) { + if (!$switch) { throw new Exception_InvalidArgument(); } - $url = self::getUrl($app, $sbas_id); - $path = self::getPath($app, $sbas_id); + $url = $statusStructure->getUrl(); + $path = $statusStructure->getPath(); if ($file->getSize() >= 65535) { throw new Exception_Upload_FileTooBig(); @@ -399,7 +115,7 @@ class databox_status throw new Exception_Upload_Error(); } - self::deleteIcon($app, $sbas_id, $bit, $switch); + self::deleteIcon($app, $databox_id, $bit, $switch); $name = "-stat_" . $bit . "_" . ($switch == 'on' ? '1' : '0') . ".gif"; @@ -427,8 +143,8 @@ class databox_status } - self::$_status[$sbas_id]->status[$bit]['img_' . $switch] = $url . $name; - self::$_status[$sbas_id]->status[$bit]['path_' . $switch] = $filePath; + $status['img_' . $switch] = $url . $name; + $status['path_' . $switch] = $filePath; return true; } @@ -607,13 +323,8 @@ class databox_status return $status; } - public static function purge() + public static function bitIsSet($bitValue, $nthBit) { - self::$_status = self::$_statuses = []; - } - - public static function bitIsSet($bitMask, $nthBit) - { - return (bool) ($bitMask & (1 << $nthBit)); + return (bool) ($bitValue & (1 << $nthBit)); } } diff --git a/lib/classes/record/adapter.php b/lib/classes/record/adapter.php index 2d40a6aee3..6ba47873ec 100644 --- a/lib/classes/record/adapter.php +++ b/lib/classes/record/adapter.php @@ -29,6 +29,7 @@ use Doctrine\ORM\EntityManager; use Doctrine\Common\Collections\ArrayCollection; use MediaVorus\MediaVorus; use Rhumsaa\Uuid\Uuid; +use Alchemy\Phrasea\Status\StatusStructure; use Alchemy\Phrasea\Model\RecordInterface; use Symfony\Component\HttpFoundation\File\File as SymfoFile; use Alchemy\Phrasea\Core\PhraseaTokens; @@ -381,61 +382,6 @@ class record_adapter implements RecordInterface, cache_cacheableInterface return $this->getSubdfefByDeviceAndMime($devices, $mimes); } - /** - * - * @return string - */ - public function get_status_icons() - { - $dstatus = databox_status::getDisplayStatus($this->app); - $sbas_id = $this->get_sbas_id(); - - $status = ''; - - if (isset($dstatus[$sbas_id])) { - foreach ($dstatus[$sbas_id] as $n => $statbit) { - if ($statbit['printable'] == '0' && - !$this->app['acl']->get($this->app['authentication']->getUser())->has_right_on_base($this->base_id, 'chgstatus')) { - continue; - } - - $x = (substr((strrev($this->get_status())), $n, 1)); - - $source0 = "/skins/icons/spacer.gif"; - $style0 = "visibility:hidden;display:none;"; - $source1 = "/skins/icons/spacer.gif"; - $style1 = "visibility:hidden;display:none;"; - if ($statbit["img_on"]) { - $source1 = $statbit["img_on"]; - $style1 = "visibility:auto;display:none;"; - } - if ($statbit["img_off"]) { - $source0 = $statbit["img_off"]; - $style0 = "visibility:auto;display:none;"; - } - if ($x == '1') { - if ($statbit["img_on"]) { - $style1 = "visibility:auto;display:inline;"; - } - } else { - if ($statbit["img_off"]) { - $style0 = "visibility:auto;display:inline;"; - } - } - $status .= ''; - $status .= ''; - } - } - - return $status; - } - /** * Returns the type of the document * @@ -1601,8 +1547,6 @@ class record_adapter implements RecordInterface, cache_cacheableInterface } $databox = $this->get_databox(); - \cache_databox::update($this->app, $this->get_sbas_id(), 'record', $this->get_record_id()); - return $databox->delete_data_from_cache($this->get_cache_key($option)); } @@ -1947,9 +1891,9 @@ class record_adapter implements RecordInterface, cache_cacheableInterface } /** {@inheritdoc} */ - public function getStatus() + public function getStatusBitField() { - return $this->get_status(); + return bindec($this->get_status()); } /** {@inheritdoc} */ diff --git a/templates/web/admin/statusbit.html.twig b/templates/web/admin/statusbit.html.twig index 0a15ca07d8..f410040bdd 100644 --- a/templates/web/admin/statusbit.html.twig +++ b/templates/web/admin/statusbit.html.twig @@ -1,4 +1,4 @@ -{% set status = databox.get_statusbits() %} +{% set statusStructure = databox.getStatusStructure() %} {% set databox_id = databox.get_sbas_id() %}

@@ -19,7 +19,7 @@ {{ bit }} - {% if attribute(status, bit) is defined %} + {% if statusStructure.hasStatus(bit) %} @@ -38,8 +38,8 @@ {% endif %} - {% if attribute(status, bit) is defined %} - {% set statusbit = attribute(status, bit) %} + {% if statusStructure.hasStatus(bit) %} + {% set statusbit = statusStructure.getStatus(bit) %} {% if statusbit['img_off'] %} diff --git a/templates/web/client/index.html.twig b/templates/web/client/index.html.twig index 63a083c94b..0372a4cb18 100644 --- a/templates/web/client/index.html.twig +++ b/templates/web/client/index.html.twig @@ -195,7 +195,7 @@ {% set status_bit_filters = '' %} - {% for bit, data in databox.get_statusbits() if data['searchable'] %} + {% for bit, data in databox.getStatusStructure.getStatuses if data['searchable'] %} {% set html_status_bit %}
diff --git a/templates/web/prod/actions/Property/index.html.twig b/templates/web/prod/actions/Property/index.html.twig index 9019865f58..799d85529b 100644 --- a/templates/web/prod/actions/Property/index.html.twig +++ b/templates/web/prod/actions/Property/index.html.twig @@ -4,45 +4,50 @@
- {# This value is fetched when click on 2nd tab #} - + {# if multi databox edit abort #} + {% if records.databoxes() | length > 1 %} +
+ {{ 'prod::Les enregistrements ne provienent pas tous de la meme base et ne peuvent donc etre traites ensemble' | trans }} +
+ {% else %} - + {% set databox = records.databoxes|first %} + {# This value is fetched when click on 2nd tab #} + -
-

-

- {% if nbReceivedDocuments == 1 %} - {% trans %}You have selected one record.{% endtrans %} - {% else %} - {% trans with {'%nbReceivedDocuments%' : nbReceivedDocuments} %}You have selected %nbReceivedDocuments% records.{% endtrans %} - {% endif %} + - {% if nbEditableDocuments == 0 %} - {% trans %}None of the records can be modified.{% endtrans %} - {% else %} - {% if nbEditableDocuments < nbReceivedDocuments %} - {% if nbEditableDocuments == 1 %} - {% trans %}Only one record can be modified.{% endtrans %} - {% else %} - {% trans with {'%nbEditableDocuments%' : nbEditableDocuments} %}Only %nbEditableDocuments% records can be modified.{% endtrans %} - {% endif %} - {% endif %} - {% endif %} -

-

+
+

+

+ {% if nbReceivedDocuments == 1 %} + {% trans %}You have selected one record.{% endtrans %} + {% else %} + {% trans with {'%nbReceivedDocuments%' : nbReceivedDocuments} %}You have selected %nbReceivedDocuments% records.{% endtrans %} + {% endif %} -
- - {% for databox in records.databoxes() %} + {% if nbEditableDocuments == 0 %} + {% trans %}None of the records can be modified.{% endtrans %} + {% else %} + {% if nbEditableDocuments < nbReceivedDocuments %} + {% if nbEditableDocuments == 1 %} + {% trans %}Only one record can be modified.{% endtrans %} + {% else %} + {% trans with {'%nbEditableDocuments%' : nbEditableDocuments} %}Only %nbEditableDocuments% records can be modified.{% endtrans %} + {% endif %} + {% endif %} + {% endif %} +

+

+ + + {% set sbasId = databox.get_sbas_id() %} - {% set nbItems = attribute(nRec, sbasId) %} - {% set nbRecords = nbItems['records'] %} - {% set nbStories = nbItems['stories'] %} +
@@ -54,22 +59,10 @@ - {% set databoxStatus = attribute(statusBit, sbasId) %} - {% for bit,values in databoxStatus %} - {% set inverse = 0 %} - - {% if values["status"] == "2" %} - {% set inverse = 2 %} - {% elseif values["status"] == "0" %} - {% set inverse = 1 %} - {% endif %} + {% for bit,values in status %} @@ -105,89 +98,85 @@ {% endfor %} - {% if nbStories > 0 %} + {% if records.stories.count > 0 %} {% endif %}
- {% if nbRecords == 0 and nbStories > 0 %} - ({% trans %}Stories status edition{% endtrans %}) - {% elseif nbRecords > 0 and nbStories == 0 %} - ({% trans %}Records status edition{% endtrans %}) - {% endif %} + {% trans %}Status edition{% endtrans %}
{% if values['img_off'] is not empty %} @@ -79,18 +72,18 @@ {% endif %} - - + - + -
- {% if nbStories == 1 %} - {% trans %}Apply status on stories children.{% endtrans %} - {% elseif nbStories > 0 %} - {% trans %}Apply status on story children.{% endtrans %} - {% endif %} + {% trans %}Apply status on story children.{% endtrans %}
- {% endfor %} -
- {% if nbEditableDocuments > 0 %} - - {% endif %} - - -
- +
+ {% if nbEditableDocuments > 0 %} + + {% endif %} + + +
+ +
- - + +{% endif %} diff --git a/templates/web/prod/index.html.twig b/templates/web/prod/index.html.twig index 3db70269c3..362b29e973 100644 --- a/templates/web/prod/index.html.twig +++ b/templates/web/prod/index.html.twig @@ -379,33 +379,31 @@
{{ 'Status des documents a rechercher' | trans }} - {% for sbas_id, bits in search_status %} - {% for n, bit in bits %} + {% for databox_id, flag in search_status %} - {% endfor %} {% endfor %}
- {% if bit['imgoff'] %} - - {% endif %} - {% if bit['imgon'] %} - - {% endif %}
diff --git a/templates/web/prod/results/record.html.twig b/templates/web/prod/results/record.html.twig index 9ce305d6ce..a21e32fae8 100644 --- a/templates/web/prod/results/record.html.twig +++ b/templates/web/prod/results/record.html.twig @@ -12,8 +12,9 @@ {{ record.title }}
- {# @todo find a proper way to map lifted flag to status icon img path #} - {#{{record.get_status_icons|raw}}#} + {% for flag in record_flags(record) %} + + {% endfor %}
diff --git a/templates/web/prod/upload/upload-flash.html.twig b/templates/web/prod/upload/upload-flash.html.twig index 84f800e169..730aeb2e43 100644 --- a/templates/web/prod/upload/upload-flash.html.twig +++ b/templates/web/prod/upload/upload-flash.html.twig @@ -66,7 +66,7 @@

{{ 'upload:: Status :' | trans }} :
- {% for bit, status in availableCollections['databox'].get_statusbits() %} + {% for bit, status in availableCollections['databox'].getStatusStructure() %}
{% if status['img_off'] is not empty %} diff --git a/templates/web/prod/upload/upload.html.twig b/templates/web/prod/upload/upload.html.twig index c80e0ee55d..a5e26f450f 100644 --- a/templates/web/prod/upload/upload.html.twig +++ b/templates/web/prod/upload/upload.html.twig @@ -71,7 +71,7 @@
{{ 'upload:: Status :' | trans }} :
- {% for bit, status in availableCollections['databox'].get_statusbits() %} + {% for bit, status in availableCollections['databox'].getStatusStructure() %}
{% if status['img_off'] is not empty %} diff --git a/www/skins/prod/jquery.main-prod.js b/www/skins/prod/jquery.main-prod.js index a7e8719248..c1944d6f8b 100644 --- a/www/skins/prod/jquery.main-prod.js +++ b/www/skins/prod/jquery.main-prod.js @@ -197,8 +197,6 @@ function checkFilters(save) { $('input.field_switch:checked', container).removeAttr('checked'); - $('input.field_switch:checkbox', container).parent().hide(); - filters.removeClass('danger'); var nbSelectedColls = 0;