Refactor status display

This commit is contained in:
Nicolas Le Goff
2015-02-02 11:34:44 +01:00
parent 47e9605aab
commit 111755fa9b
33 changed files with 928 additions and 646 deletions

View File

@@ -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/');
});

View File

@@ -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');

View File

@@ -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;

View File

@@ -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;
}
}

View File

@@ -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];
if (count($records->databoxes()) > 1) {
return new Response($app['twig']->render('prod/actions/Property/index.html.twig', [
'records' => $records
]));
}
$nRec[$sbasId]['records']++;
$databox = current($records->databoxes());
$statusStructure = $databox->getStatusStructure();
$recordsStatuses = [];
if ($record->is_grouping()) {
$nRec[$sbasId]['stories']++;
foreach ($records->received() as $record) {
foreach ($statusStructure as $status) {
$bit = $status['bit'];
if (!isset($recordsStatuses[$bit])) {
$recordsStatuses[$bit] = $status;
}
if (!isset($statusBit[$sbasId])) {
$statusBit[$sbasId] = isset($databoxStatus[$sbasId]) ? $databoxStatus[$sbasId] : [];
$statusSet = \databox_status::bitIsSet($record->getStatusBitField(), $bit);
foreach (array_keys($statusBit[$sbasId]) as $bit) {
$statusBit[$sbasId][$bit]['nset'] = 0;
}
if (!isset($recordsStatuses[$bit]['flag'])) {
$recordsStatuses[$bit]['flag'] = (int) $statusSet;
}
$status = strrev($record->get_status());
foreach (array_keys($statusBit[$sbasId]) as $bit) {
$statusBit[$sbasId][$bit]["nset"] += substr($status, $bit, 1) !== "0" ? 1 : 0;
// 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;
}
}
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);
}
}
return new Response($app['twig']->render('prod/actions/Property/index.html.twig', [
'records' => $records,
'statusBit' => $statusBit,
'nRec' => $nRec
'status' => $recordsStatuses
]));
}

View File

@@ -0,0 +1,39 @@
<?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\Provider;
use Alchemy\Phrasea\Status\CacheStatusStructureProvider;
use Alchemy\Phrasea\Status\StatusStructureFactory;
use Alchemy\Phrasea\Status\XmlStatusStructureProvider;
use Silex\Application;
use Silex\ServiceProviderInterface;
class StatusServiceProvider implements ServiceProviderInterface
{
public function register(Application $app)
{
$app['status.provider'] = $app->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)
{
}
}

View File

@@ -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";

View File

@@ -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;
}

View File

@@ -426,7 +426,7 @@ class User
self::GENDER_MISS,
self::GENDER_MR,
self::GENDER_MRS
], true)) {
])) {
throw new InvalidArgumentException(sprintf("Invalid gender %s.", $gender));
}

View File

@@ -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);

View File

@@ -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();

View File

@@ -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,

View File

@@ -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'], [])));

View File

@@ -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) {

View File

@@ -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]))

View File

@@ -0,0 +1,72 @@
<?php
/*
* This file is part of Phraseanet
*
* (c) 2005-2015 Alchemy
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Phrasea\Status;
use Alchemy\Phrasea\Application;
use Doctrine\Common\Cache\Cache;
use Doctrine\Common\Collections\ArrayCollection;
/**
* Provides status structure definition using cache if possible
*/
class CacheStatusStructureProvider implements StatusStructureProviderInterface
{
private $cache;
private $provider;
public function __construct(Cache $cache, StatusStructureProviderInterface $provider)
{
$this->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);
}
}

View File

@@ -0,0 +1,145 @@
<?php
/*
* This file is part of Phraseanet
*
* (c) 2005-2015 Alchemy
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Phrasea\Status;
use Doctrine\Common\Collections\ArrayCollection;
/**
* Aim to represent status structure of a databox
* which is the combination of a databox and a collection of status
*/
class StatusStructure implements \IteratorAggregate
{
private $databox;
private $status;
private $url;
private $path;
public function __construct(\databox $databox, ArrayCollection $status = null)
{
$this->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();
}
}

View File

@@ -0,0 +1,57 @@
<?php
/*
* This file is part of Phraseanet
*
* (c) 2005-2015 Alchemy
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Phrasea\Status;
/**
* This class is used to get databox status structure
*/
class StatusStructureFactory
{
/**
* Keep trace of already instantiated databox status structure
*
* @var array
*/
private $statusStructure = [];
/**
* A provider that gives the definition of a structure
*
* @var StatusStructureProviderInterface
*/
protected $provider;
public function __construct(StatusStructureProviderInterface $provider)
{
$this->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];
}
}

View File

@@ -0,0 +1,47 @@
<?php
/*
* This file is part of Phraseanet
*
* (c) 2005-2015 Alchemy
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Phrasea\Status;
/**
* Interface defined for status definition providers
*/
interface StatusStructureProviderInterface
{
/**
* Returns a status structure for a given databox
*
* @param \databox $databox
*
* @return StatusStructure
*/
public function getStructure(\databox $databox);
/**
* Deletes status at nth bit from given status structure
*
* @param StatusStructure $structure
* @param int $bit
*
* @return StatusStructure
*/
public function deleteStatus(StatusStructure $structure, $bit);
/**
* Updates status at nth bit from given status structure
*
* @param StatusStructure $structure
* @param int $bit
*
* @return StatusStructure
*/
public function updateStatus(StatusStructure $structure, $bit, array $properties);
}

View File

@@ -0,0 +1,235 @@
<?php
/*
* This file is part of Phraseanet
*
* (c) 2005-2015 Alchemy
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Phrasea\Status;
use Symfony\Component\Filesystem\Filesystem;
/**
* Provides status structure definition from XML
*/
class XmlStatusStructureProvider implements StatusStructureProviderInterface
{
protected $rootPath;
protected $locales;
public function __construct($rootPath, $locales, $fs = null)
{
$this->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;
}
}

View File

@@ -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)) {

View File

@@ -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)

View File

@@ -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);
}
/**

View File

@@ -11,356 +11,62 @@
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 ($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');
$canAccess = $canSeeAll || $canSeeThis;
foreach ($status as $bit => $props) {
if (!$props['searchable'] && !$canAccess) {
continue;
}
$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']
);
}
foreach ($structures as $databox_id => $structure) {
if (false === $see_all[$databox_id]) {
$structure = array_filter(function($status) {
return (bool) $status['searchable'];
}, $structure->toArray());
}
foreach($structure as $bit => $status) {
$key = RecordHelper::normalizeFlagKey($status['labelon']);
if (isset($stats[$key])) {
$status = $stats[$key];
}
$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;
@@ -368,28 +74,38 @@ class databox_status
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) {
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));
}
}

View File

@@ -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 .= '<img style="margin:1px;' . $style1 . '" ' .
'class="STAT_' . $this->base_id . '_'
. $this->record_id . '_' . $n . '_1" ' .
'src="' . $source1 . '" title="' . $statbit['labels_on_i18n'][$this->app['locale']] . '"/>';
$status .= '<img style="margin:1px;' . $style0 . '" ' .
'class="STAT_' . $this->base_id . '_'
. $this->record_id . '_' . $n . '_0" ' .
'src="' . $source0 . '" title="' . $statbit['labels_off_i18n'][$this->app['locale']] . '"/>';
}
}
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} */

View File

@@ -1,4 +1,4 @@
{% set status = databox.get_statusbits() %}
{% set statusStructure = databox.getStatusStructure() %}
{% set databox_id = databox.get_sbas_id() %}
<h1>
@@ -19,7 +19,7 @@
<td>
{{ bit }}
</td>
{% if attribute(status, bit) is defined %}
{% if statusStructure.hasStatus(bit) %}
<td>
<a target="right" href="{{ path('database_display_statusbit_form', { 'databox_id' : databox_id, 'bit' : bit }) }}" class="ajax">
<img class="editer" src="/skins/icons/edit_0.gif" />
@@ -38,8 +38,8 @@
</td>
{% endif %}
{% if attribute(status, bit) is defined %}
{% set statusbit = attribute(status, bit) %}
{% if statusStructure.hasStatus(bit) %}
{% set statusbit = statusStructure.getStatus(bit) %}
<td>
{% if statusbit['img_off'] %}
<img title='{{ statusbit['labels_off_i18n'][app['locale']] }}' src='{{ statusbit['img_off'] }}' />

View File

@@ -195,7 +195,7 @@
<img onclick="removeFilters({{ sbasId }});" id="filter_danger{{ sbasId }}" class="filter_danger" src="/skins/icons/alert.png" title="{{ "client::recherche: cliquez ici pour desactiver tous les filtres de cette base" | trans }}" style="vertical-align:bottom;width:12px;height:12px;display:none;"/>
</div>
{% 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 %}
<div style="text-align:center;overflow:hidden;">
<table style="table-layout:fixed;width:90%;text-align:left;" cellspacing="0" cellpadding="0">

View File

@@ -4,6 +4,14 @@
<div id='tabs-records-property'>
{# if multi databox edit abort #}
{% if records.databoxes() | length > 1 %}
<div class="well-small">
{{ 'prod::Les enregistrements ne provienent pas tous de la meme base et ne peuvent donc etre traites ensemble' | trans }}
</div>
{% else %}
{% set databox = records.databoxes|first %}
{# This value is fetched when click on 2nd tab #}
<input type="hidden" name='original_selection' value="{{ app.request.query.get('lst') }}">
@@ -38,11 +46,8 @@
<form style='padding:15px;' name="change-records-status" action="{{ path('change_status') }}" method="POST">
<input name="lst" type="hidden" value="{{ records.serializedList() }}"/>
{% for databox in records.databoxes() %}
{% set sbasId = databox.get_sbas_id() %}
{% set nbItems = attribute(nRec, sbasId) %}
{% set nbRecords = nbItems['records'] %}
{% set nbStories = nbItems['stories'] %}
<table style='width:auto;margin:0 auto'>
<thead>
<tr>
@@ -54,22 +59,10 @@
<tbody>
<tr style='border-bottom:1px solid #FFF;'>
<td colspan="6" style='font-size:11px;text-align:center'>
{% if nbRecords == 0 and nbStories > 0 %}
<i>({% trans %}Stories status edition{% endtrans %})</i>
{% elseif nbRecords > 0 and nbStories == 0 %}
<i>({% trans %}Records status edition{% endtrans %})</i>
{% endif %}
{% trans %}Status edition{% endtrans %}</i>
</td>
</tr>
{% 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 %}
<tr>
<td style='text-align:right'>
{% if values['img_off'] is not empty %}
@@ -79,18 +72,18 @@
{% endif %}
</td>
<td style='text-align:right'>
<label for="status-radio-{{ sbasId ~ "-" ~ bit ~ '-off'}}" {% if inverse == 2 %}style='color:#FF3333'{% endif%}>
<label for="status-radio-{{ sbasId ~ "-" ~ bit ~ '-off'}}" {% if values['flag'] == 2 %}style='color:#FF3333'{% endif%}>
{{ values['labels_off_i18n'][app['locale']] }}
</label>
</td>
<td style='text-align:right'>
<input id="status-radio-{{ sbasId ~ "-" ~ bit ~ '-off'}}" type="radio" name="status[{{ sbasId }}][{{ bit }}]" value="0" {% if inverse == 1 %}checked="checked"{% endif %}/>
<input id="status-radio-{{ sbasId ~ "-" ~ bit ~ '-off'}}" type="radio" name="status[{{ sbasId }}][{{ bit }}]" {% if values['flag'] == 0 %}checked="checked"{% endif%} value="0"/>
</td>
<td style='text-align:left'>
<input id="status-radio-{{ sbasId ~ "-" ~ bit ~ '-on'}}" type="radio" name="status[{{ sbasId }}][{{ bit }}]" value="1" {% if inverse == 0 %}checked="checked"{% endif %}/>
<input id="status-radio-{{ sbasId ~ "-" ~ bit ~ '-on'}}" type="radio" name="status[{{ sbasId }}][{{ bit }}]" {% if values['flag'] == 1 %}checked="checked"{% endif%} value="1"/>
</td>
<td style='text-align:left'>
<label for="status-radio-{{ sbasId ~ "-" ~ bit ~ '-on'}}" {% if inverse == 2 %}style='color:#FF3333'{% endif%}>
<label for="status-radio-{{ sbasId ~ "-" ~ bit ~ '-on'}}" {% if values['flag'] == 2 %}style='color:#FF3333'{% endif%}>
{{ values['labels_on_i18n'][app['locale']] }}
</label>
</td>
@@ -105,21 +98,16 @@
{% endfor %}
</tbody>
<tfoot>
{% if nbStories > 0 %}
{% if records.stories.count > 0 %}
<tr>
<td colspan="6">
<input type="checkbox" name="apply_to_children[{{ sbasId }}]"/>
{% if nbStories == 1 %}
{% trans %}Apply status on stories children.{% endtrans %}
{% elseif nbStories > 0 %}
{% trans %}Apply status on story children.{% endtrans %}
{% endif %}
</td>
</tr>
{% endif %}
</tfoot>
</table>
{% endfor %}
<div class="form-actions" style="background:transparent">
{% if nbEditableDocuments > 0 %}
<button type="button" class="btn btn-primary submiter">{{ "Apply changes" | trans }}</button>
@@ -191,3 +179,4 @@
});
});
</script>
{% endif %}

View File

@@ -379,34 +379,32 @@
<div class="status_filter">
<span>{{ 'Status des documents a rechercher' | trans }}</span>
<table style="width: 100%;">
{% for sbas_id, bits in search_status %}
{% for n, bit in bits %}
{% for databox_id, flag in search_status %}
<tr>
<td>
{% if bit['imgoff'] %}
<img src="{{bit['imgoff']}}" title="{{bit['labels_off_i18n'][app['locale']]}}" />
{% endif %}
<label class="checkbox inline">
<input onchange="checkFilters(true);" class="field_switch field_{{sbas_id}}"
{% if flag['img_off'] %}
<img src="{{flag['img_off']}}" title="{{flag['labels_off_i18n'][app['locale']]}}" />
{% endif %}
<input onchange="checkFilters(true);" class="field_switch field_{{databox_id}}"
type="checkbox" value="0"
n="{{n}}" name="status[{{sbas_id}}][{{n}}]" />
{{bit['labels_off_i18n'][app['locale']]}}
n="{{flag['bit']}}" name="status[{{databox_id}}][{{flag['bit']}}]" />
{{flag['labels_off_i18n'][app['locale']]}}
</label>
</td>
<td>
{% if bit['imgon'] %}
<img src="{{bit['imgon']}}" title="{{bit['labels_on_i18n'][app['locale']]}}" />
{% endif %}
<label class="checkbox inline">
<input onchange="checkFilters(true);" class="field_switch field_{{sbas_id}}"
{% if flag['img_on'] %}
<img src="{{flag['img_on']}}" title="{{flag['labels_on_i18n'][app['locale']]}}" />
{% endif %}
<input onchange="checkFilters(true);" class="field_switch field_{{databox_id}}"
type="checkbox" value="1"
n="{{n}}" name="status[{{sbas_id}}][{{n}}]"/>
{{bit['labels_on_i18n'][app['locale']]}}
n="{{flag['bit']}}" name="status[{{databox_id}}][{{flag['bit']}}]"/>
{{flag['labels_on_i18n'][app['locale']]}}
</label>
</td>
</tr>
{% endfor %}
{% endfor %}
</table>
</div>
{% endif %}

View File

@@ -12,8 +12,9 @@
{{ record.title }}
</div>
<div class="status">
{# @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) %}
<img src="{{ flag.path }}" title="{{ attribute(flag.labels, app.locale) }}" />
{% endfor %}
</div>
</div>

View File

@@ -66,7 +66,7 @@
<h5>{{ 'upload:: Status :' | trans }} :</h5>
<table class="status-tab">
<tbody>
{% for bit, status in availableCollections['databox'].get_statusbits() %}
{% for bit, status in availableCollections['databox'].getStatusStructure() %}
<tr>
<td class="status-tab-left">
{% if status['img_off'] is not empty %}

View File

@@ -71,7 +71,7 @@
<h5>{{ 'upload:: Status :' | trans }} :</h5>
<table class="status-tab">
<tbody>
{% for bit, status in availableCollections['databox'].get_statusbits() %}
{% for bit, status in availableCollections['databox'].getStatusStructure() %}
<tr>
<td class="status-tab-left">
{% if status['img_off'] is not empty %}

View File

@@ -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;