Merge branch 'master' into PHRAS-2680-webhook-privacy-and-security

This commit is contained in:
Nicolas Maillat
2019-11-26 17:46:31 +01:00
committed by GitHub
84 changed files with 3445 additions and 1777 deletions

View File

@@ -123,8 +123,8 @@ workflows:
dockerfile: Dockerfile
extra-build-args: "--target phraseanet-fpm"
region: AWS_DEFAULT_REGION
repo: "${AWS_RESOURCE_NAME_PREFIX}/phraseanet"
tag: "alpha-0.1"
repo: "phraseanet-fpm"
tag: "${CIRCLE_BRANCH}"
- aws-ecr/build_and_push_image:
account-url: AWS_ACCOUNT_URL
aws-access-key-id: AWS_ACCESS_KEY_ID
@@ -134,8 +134,8 @@ workflows:
dockerfile: Dockerfile
extra-build-args: "--target phraseanet-nginx"
region: AWS_DEFAULT_REGION
repo: "${AWS_RESOURCE_NAME_PREFIX}/phraseanet-nginx"
tag: "alpha-0.1"
repo: "phraseanet-nginx"
tag: "${CIRCLE_BRANCH}"
- aws-ecr/build_and_push_image:
account-url: AWS_ACCOUNT_URL
aws-access-key-id: AWS_ACCESS_KEY_ID
@@ -145,5 +145,5 @@ workflows:
dockerfile: Dockerfile
extra-build-args: "--target phraseanet-worker"
region: AWS_DEFAULT_REGION
repo: "${AWS_RESOURCE_NAME_PREFIX}/phraseanet"
tag: "alpha-0.1"
repo: "phraseanet-worker"
tag: "${CIRCLE_BRANCH}"

View File

@@ -86,6 +86,18 @@ COPY grammar /var/alchemy/grammar
COPY templates-profiler /var/alchemy/templates-profiler
COPY templates /var/alchemy/templates
COPY tests /var/alchemy/tests
RUN mkdir -p /var/alchemy/Phraseanet/logs \
&& chmod -R 777 /var/alchemy/Phraseanet/logs \
&& mkdir -p /var/alchemy/Phraseanet/cache \
&& chmod -R 777 /var/alchemy/Phraseanet/cache \
&& mkdir -p /var/alchemy/Phraseanet/datas \
&& chmod -R 777 /var/alchemy/Phraseanet/datas \
&& mkdir -p /var/alchemy/Phraseanet/tmp \
&& chmod -R 777 /var/alchemy/Phraseanet/tmp \
&& mkdir -p /var/alchemy/Phraseanet/www/custom \
&& chmod -R 777 /var/alchemy/Phraseanet/www/custom \
&& mkdir -p /var/alchemy/Phraseanet/config \
&& chmod -R 777 /var/alchemy/Phraseanet/config
# Phraseanet
FROM php:7.0-fpm-stretch as phraseanet-fpm
@@ -129,6 +141,23 @@ RUN apt-get update \
&& docker-php-source delete \
&& rm -rf /var/lib/apt/lists/*
RUN php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" \
&& php -r "if (hash_file('sha384', 'composer-setup.php') === 'a5c698ffe4b8e849a443b120cd5ba38043260d5c4023dbf93e1558871f1f07f58274fc6f4c93bcfd858c6bd0775cd8d1') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;" \
&& php composer-setup.php --install-dir=/usr/local/bin --filename=composer \
&& php -r "unlink('composer-setup.php');"
# Node Installation (node + yarn)
# Reference :
# https://linuxize.com/post/how-to-install-node-js-on-ubuntu-18.04/
# https://yarnpkg.com/lang/en/docs/install/#debian-stable
RUN curl -sL https://deb.nodesource.com/setup_10.x | bash - \
&& apt install -y nodejs \
&& curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \
&& echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list \
&& apt-get update && apt-get install -y --no-install-recommends yarn \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/
RUN mkdir /entrypoint /var/alchemy \
&& useradd -u 1000 app \
&& mkdir -p /home/app/.composer \
@@ -136,18 +165,6 @@ RUN mkdir /entrypoint /var/alchemy \
COPY --from=builder --chown=app /var/alchemy /var/alchemy/Phraseanet
ADD ./docker/phraseanet/ /
RUN mkdir -p /var/alchemy/Phraseanet/logs \
&& chmod -R 777 /var/alchemy/Phraseanet/logs \
&& mkdir -p /var/alchemy/Phraseanet/cache \
&& chmod -R 777 /var/alchemy/Phraseanet/cache \
&& mkdir -p /var/alchemy/Phraseanet/datas \
&& chmod -R 777 /var/alchemy/Phraseanet/datas \
&& mkdir -p /var/alchemy/Phraseanet/tmp \
&& chmod -R 777 /var/alchemy/Phraseanet/tmp \
&& mkdir -p /var/alchemy/Phraseanet/www/custom \
&& chmod -R 777 /var/alchemy/Phraseanet/www/custom \
&& mkdir -p /var/alchemy/Phraseanet/config \
&& chmod -R 777 /var/alchemy/Phraseanet/config
WORKDIR /var/alchemy/Phraseanet
ENTRYPOINT ["/phraseanet-entrypoint.sh"]
CMD ["/boot.sh"]

View File

@@ -63,14 +63,16 @@ The docker distribution come with 3 differents containers :
## How to build
The three images can be built respectively with these commands :
You can build all the images with the following command at the root directory, choosing an arbirary TAG name :
# nginx server
docker build --target phraseanet-nginx -t local/phraseanet-nginx .
./build.sh <TAG>
# php-fpm application
docker build --target phraseanet-fpm -t local/phraseanet-fpm .
It will build and tag the following images :
# worker
docker build --target phraseanet-worker -t local/phraseanet-worker .
local/phraseanet-worker:<TAG>
local/phraseanet-fpm:<TAG>
local/phraseanet-nginx:<TAG>
# Deploy the application
Once the images are built, you can deploy the entire phraseanet stack using the repository : https://github.com/alchemy-fr/phraseanet-docker and follow the instruction inside its `README.md` file.

11
build.sh Executable file
View File

@@ -0,0 +1,11 @@
#!/bin/bash
# nginx server
docker build --target phraseanet-nginx -t local/phraseanet-nginx:$1 .
# php-fpm application
docker build --target phraseanet-fpm -t local/phraseanet-fpm:$1 .
# worker
docker build --target phraseanet-worker -t local/phraseanet-worker:$1 .

0
cache/.gitkeep vendored Normal file
View File

View File

@@ -1,4 +1,6 @@
#!/bin/bash
cat nginx.conf.sample | sed "s/\$MAX_BODY_SIZE/$MAX_BODY_SIZE/g" > /etc/nginx/nginx.conf
set -xe
cat nginx.conf.sample | sed "s/\$MAX_BODY_SIZE/$MAX_BODY_SIZE/g" > /etc/nginx/conf.d/default.conf
nginx -g "daemon off;"

View File

@@ -0,0 +1,31 @@
user app;
worker_processes 1;
error_log /var/log/ngnix_error.log info;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
#tcp_nopush on;
keepalive_timeout 65;
#gzip on;
include /etc/nginx/conf.d/*.conf;
}

View File

@@ -1,54 +1,9 @@
user app;
worker_processes auto;
#error_log /var/log/ngnix_error.log info;
error_log /dev/stdout info;
pid /var/run/nginx.pid;
#daemon off;
events {
worker_connections 1024;
multi_accept on;
upstream backend {
server phraseanet:9000;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
server_tokens off;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /dev/stdout main;
sendfile on;
#tcp_nopush on;
keepalive_timeout 65;
#gzip on;
reset_timedout_connection on;
proxy_connect_timeout 300s;
proxy_send_timeout 300s;
proxy_read_timeout 300s;
fastcgi_send_timeout 300s;
fastcgi_read_timeout 300;
resolver 127.0.0.11;
upstream backend {
server phraseanet:9000;
}
server {
server {
listen 80;
server_name localhost;
error_log on;
access_log on;
root /var/alchemy/Phraseanet/www;
index index.php;
@@ -81,5 +36,4 @@ http {
include fastcgi_params;
fastcgi_pass backend;
}
}
}

View File

@@ -2,6 +2,16 @@
set -xe
if [ $INSTALL_ACCOUNT_EMAIL = ""]; then
echo "INSTALL_ACCOUNT_EMAIL var is not set."
exit 1
fi
if [ $INSTALL_ACCOUNT_PASSWORD = ""]; then
echo "INSTALL_ACCOUNT_PASSWORD var is not set."
exit 1
fi
/var/alchemy/Phraseanet/bin/setup system:install \
--email=$INSTALL_ACCOUNT_EMAIL \
--password=$INSTALL_ACCOUNT_PASSWORD \

View File

@@ -1,5 +1,11 @@
#!/bin/bash
set -xe
chown -R app:app /var/alchemy/Phraseanet/config
chown -R app:app /var/alchemy/Phraseanet/datas
chown -R app:app /var/alchemy/Phraseanet/tmp
chown -R app:app /var/alchemy/Phraseanet/www/thumbnails
FILE=/var/alchemy/Phraseanet/config/configuration.yml
if [ -f "$FILE" ]; then
echo "$FILE exist, skip setup."

View File

@@ -5,4 +5,4 @@ set -e
envsubst < /php.ini.sample > /usr/local/etc/php/php.ini
envsubst < /php-fpm.conf.sample > /usr/local/etc/php-fpm.conf
docker-php-entrypoint $@
bash -e docker-php-entrypoint $@

View File

@@ -1,3 +1,4 @@
#!/bin/bash
mkdir /var/alchemy/Phraseanet/tmp/locks && chown -R app:app /var/alchemy/Phraseanet/tmp
runuser app -c 'php /var/alchemy/Phraseanet/bin/console task-manager:scheduler:run'

View File

@@ -128,7 +128,7 @@ key:
| quoted_string()
group:
::space::? ::parenthese_:: primary() ::_parenthese:: ::space::?
::space::? ::parenthese_:: ::space::? primary() ::space::? ::_parenthese:: ::space::?
// Thesaurus terms

View File

@@ -73,7 +73,7 @@ class RedisCache extends CacheProvider implements Cache
*/
protected function doDelete($id)
{
return $this->_redis->delete($id);
return $this->_redis->del($id);
}
/**

View File

@@ -13,6 +13,7 @@ namespace Alchemy\Phrasea\Command\Setup;
use Alchemy\Phrasea\Command\Command;
use Alchemy\Phrasea\Core\Configuration\StructureTemplate;
use Alchemy\Phrasea\SearchEngine\Elastic\ElasticsearchOptions;
use Doctrine\DBAL\Driver\Connection;
use Symfony\Component\Console\Helper\DialogHelper;
use Symfony\Component\Console\Input\ArrayInput;
@@ -51,7 +52,9 @@ class Install extends Command
->addOption('db-template', null, InputOption::VALUE_OPTIONAL, 'Databox template (' . $this->structureTemplate->toString() . ')', null)
->addOption('data-path', null, InputOption::VALUE_OPTIONAL, 'Path to data repository', realpath(__DIR__ . '/../../../../../datas'))
->addOption('server-name', null, InputOption::VALUE_OPTIONAL, 'Server name')
->addOption('indexer', null, InputOption::VALUE_OPTIONAL, 'Path to Phraseanet Indexer', 'auto')
->addOption('es-host', null, InputOption::VALUE_OPTIONAL, 'ElasticSearch server HTTP host', 'localhost')
->addOption('es-port', null, InputOption::VALUE_OPTIONAL, 'ElasticSearch server HTTP port', 9200)
->addOption('es-index', null, InputOption::VALUE_OPTIONAL, 'ElasticSearch index name', null)
->addOption('yes', 'y', InputOption::VALUE_NONE, 'Answer yes to all questions');
return $this;
@@ -121,6 +124,21 @@ class Install extends Command
list($email, $password) = $this->getCredentials($input, $output, $dialog);
$dataPath = $this->getDataPath($input, $output, $dialog);
if (! $input->getOption('yes')) {
$output->writeln("<info>--- ElasticSearch connection settings ---</info>");
}
list($esHost, $esPort) = $this->getESHost($input, $output, $dialog);
$esIndexName = $this->getESIndexName($input, $output, $dialog);
$esOptions = ElasticsearchOptions::fromArray([
'host' => $esHost,
'port' => $esPort,
'index' => $esIndexName
]);
$output->writeln('');
if (!$input->getOption('yes')) {
$continue = $dialog->askConfirmation($output, "<question>Phraseanet is going to be installed, continue ? (N/y)</question>", false);
@@ -132,6 +150,7 @@ class Install extends Command
}
$this->container['phraseanet.installer']->install($email, $password, $abConn, $serverName, $dataPath, $dbConn, $templateName, $this->detectBinaries());
$this->container['conf']->set(['main', 'search-engine', 'options'], $esOptions->toArray());
if (null !== $this->getApplication()) {
$command = $this->getApplication()->find('crossdomain:generate');
@@ -339,6 +358,35 @@ class Install extends Command
return $serverName;
}
private function getESHost(InputInterface $input, OutputInterface $output, DialogHelper $dialog)
{
$host = $input->getOption('es-host');
$port = (int) $input->getOption('es-port');
if (! $input->getOption('yes')) {
while (! $host) {
$host = $dialog->ask($output, 'ElasticSearch server host : ', null);
};
while ($port <= 0 || $port >= 65535) {
$port = (int) $dialog->ask($output, 'ElasticSearch server port : ', null);
};
}
return [ $host, $port ];
}
private function getESIndexName(InputInterface $input, OutputInterface $output, DialogHelper $dialog)
{
$index = $input->getOption('es-index');
if (! $input->getOption('yes')) {
$index = $dialog->ask($output, 'ElasticSearch server index name (blank to autogenerate) : ', null);
}
return $index;
}
private function detectBinaries()
{
return [

View File

@@ -316,6 +316,7 @@ class FieldsController extends Controller
->set_tbranch($data['tbranch'])
->set_generate_cterms($data['generate_cterms'])
->set_gui_editable($data['gui_editable'])
->set_gui_visible($data['gui_visible'])
->set_report($data['report'])
->setVocabularyControl(null)
->setVocabularyRestricted(false);
@@ -351,7 +352,7 @@ class FieldsController extends Controller
{
return [
'name', 'multi', 'thumbtitle', 'tag', 'business', 'indexable', 'aggregable',
'required', 'separator', 'readonly', 'gui_editable', 'type', 'tbranch', 'generate_cterms', 'report',
'required', 'separator', 'readonly', 'gui_editable', 'gui_visible' , 'type', 'tbranch', 'generate_cterms', 'report',
'vocabulary-type', 'vocabulary-restricted', 'dces-element', 'labels'
];
}

View File

@@ -596,6 +596,7 @@ class V1Controller extends Controller
'thesaurus_branch' => $databox_field->get_tbranch(),
'generate_cterms' => $databox_field->get_generate_cterms(),
'gui_editable' => $databox_field->get_gui_editable(),
'gui_visible' => $databox_field->get_gui_visible(),
'type' => $databox_field->get_type(),
'indexable' => $databox_field->is_indexable(),
'multivalue' => $databox_field->is_multi(),

View File

@@ -77,6 +77,7 @@ class EditController extends Controller
'tbranch' => $meta->get_tbranch(),
'generate_cterms' => $meta->get_generate_cterms(),
'gui_editable' => $meta->get_gui_editable(),
'gui_visible' => $meta->get_gui_visible(),
'maxLength' => $meta->get_tag()
->getMaxLength(),
'minLength' => $meta->get_tag()

View File

@@ -126,6 +126,16 @@ class LazaretController extends Controller
$ret = $lazaretManipulator->add($file_id, $keepAttributes, $attributesToKeep);
try{
// get the new record
$record = \Collection::getByBaseId($this->app, $request->request->get('bas_id'))->get_databox()->get_record($ret['result']['record_id']);
$postStatus = (array) $request->request->get('status');
// update status
$this->updateRecordStatus($record, $postStatus);
}catch(\Exception $e){
$ret['message'] = $this->app->trans('An error occured when wanting to change status!');
}
return $this->app->json($ret);
}
@@ -216,6 +226,7 @@ class LazaretController extends Controller
return $this->app->json($ret);
}
$postStatus = (array) $request->request->get('status');
$path = $this->app['tmp.lazaret.path'] . '/';
$lazaretFileName = $path .$lazaretFile->getFilename();
@@ -233,6 +244,9 @@ class LazaretController extends Controller
''
);
// update status
$this->updateRecordStatus($record, $postStatus);
//Delete lazaret file
$manager = $this->getEntityManager();
$manager->remove($lazaretFile);
@@ -278,6 +292,35 @@ class LazaretController extends Controller
);
}
/**
* @param Request $request
* @param $databox_id
* @param $record_id
* @return \Symfony\Component\HttpFoundation\JsonResponse
*/
public function getDestinationStatus(Request $request, $databox_id, $record_id)
{
if (!$request->isXmlHttpRequest()) {
$this->app->abort(400);
}
$record = new \record_adapter($this->app, (int) $databox_id, (int) $record_id);
$databox = $this->findDataboxById($databox_id);
$statusStructure = $databox->getStatusStructure();
$recordsStatuses = [];
foreach ($statusStructure as $status) {
// make the key as a string for the json usage in javascript
$bit = "'".$status['bit']."'";
if (!isset($recordsStatuses[$bit])) {
$recordsStatuses[$bit] = $status;
}
$statusSet = \databox_status::bitIsSet($record->getStatusBitField(), $status['bit']);
if (!isset($recordsStatuses[$bit]['flag'])) {
$recordsStatuses[$bit]['flag'] = (int) $statusSet;
}
}
return $this->app->json(['status' => $recordsStatuses]);
}
/**
* @return LazaretFileRepository
*/
@@ -293,4 +336,32 @@ class LazaretController extends Controller
{
return $this->app['border-manager'];
}
/**
* Set new status to selected record
*
* @param \record_adapter $record
* @param array $postStatus
* @return array|null
*/
private function updateRecordStatus(\record_adapter $record, array $postStatus)
{
$sbasId = $record->getDataboxId();
if (isset($postStatus[$sbasId]) && is_array($postStatus[$sbasId])) {
$postStatus = $postStatus[$sbasId];
$currentStatus = strrev($record->getStatus());
$newStatus = '';
foreach (range(0, 31) as $i) {
$newStatus .= isset($postStatus[$i]) ? ($postStatus[$i] ? '1' : '0') : $currentStatus[$i];
}
$record->setStatus(strrev($newStatus));
$this->getDataboxLogger($record->getDatabox())
->log($record, \Session_Logger::EVENT_STATUS, '', '');
return [
'current_status' => $currentStatus,
'new_status' => $newStatus,
];
}
return null;
}
}

View File

@@ -341,10 +341,20 @@ class QueryController extends Controller
if ($result->getTotal() === 0) {
$template = 'prod/results/help.html.twig';
} else {
}
else {
$template = 'prod/results/records.html.twig';
}
$json['results'] = $this->render($template, ['results'=> $result]);
/** @var \Closure $filter */
$filter = $this->app['plugin.filter_by_authorization'];
$plugins = [
'workzone' => $filter('workzone'),
'actionbar' => $filter('actionbar'),
];
$json['results'] = $this->render($template, ['results'=> $result, 'plugins'=>$plugins]);
// add technical fields
@@ -382,7 +392,6 @@ class QueryController extends Controller
'labels' => $field->get_labels(),
'type' => $field->get_type(),
'field' => $field->get_name(),
'query' => "field." . $field->get_name() . ":%s",
'trans_label' => $field->get_label($this->app['locale']),
];
$field->get_label($this->app['locale']);

View File

@@ -82,6 +82,11 @@ class Lazaret implements ControllerProviderInterface, ServiceProviderInterface
->assert('file_id', '\d+')
->bind('lazaret_thumbnail');
$controllers->get('/{databox_id}/{record_id}/status', 'controller.prod.lazaret:getDestinationStatus')
->assert('databox_id', '\d+')
->assert('record_id', '\d+')
->bind('lazaret_destination_status');
return $controllers;
}
}

View File

@@ -38,6 +38,7 @@ final class DbalDataboxFieldRepository implements DataboxFieldRepository
'label_nl',
'generate_cterms',
'gui_editable',
'gui_visible',
];
/** @var DataboxFieldFactory */

View File

@@ -12,6 +12,7 @@
namespace Alchemy\Phrasea\Model\Entities;
use Alchemy\Phrasea\Application;
use Alchemy\Phrasea\Border\Attribute\AttributeInterface;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation as Gedmo;
use \record_adapter;
@@ -474,4 +475,32 @@ class LazaretFile
return $merged;
}
/**
* @param Application $app
* @return array|null
*/
public function getStatus(Application $app)
{
/**@var LazaretAttribute $atribute*/
foreach ($this->attributes as $atribute) {
if ($atribute->getName() == AttributeInterface::NAME_STATUS) {
$databox = $this->getCollection($app)->get_databox();
$statusStructure = $databox->getStatusStructure();
$recordsStatuses = [];
foreach ($statusStructure as $status) {
$bit = $status['bit'];
if (!isset($recordsStatuses[$bit])) {
$recordsStatuses[$bit] = $status;
}
$statusSet = \databox_status::bitIsSet(bindec($atribute->getValue()), $bit);
if (!isset($recordsStatuses[$bit]['flag'])) {
$recordsStatuses[$bit]['flag'] = (int) $statusSet;
}
}
return $recordsStatuses;
}
}
return null;
}
}

View File

@@ -226,6 +226,8 @@ class LazaretManipulator
$this->entityManager->remove($lazaretFile);
$this->entityManager->flush();
$ret['result']['record_id'] = $record->getRecordId();
$ret['success'] = true;
} catch (\Exception $e) {
$ret['message'] = $this->app->trans('An error occured');

View File

@@ -45,6 +45,9 @@ class TextNode extends AbstractTermNode implements ContextAbleInterface
foreach ($context->localizeField($field) as $f) {
$index_fields[] = $f;
}
foreach ($context->truncationField($field) as $f) {
$index_fields[] = $f;
}
}
if (!$index_fields) {
return null;

View File

@@ -323,7 +323,7 @@ class ElasticsearchOptions
"aggregated (2 values: fired = 0 or 1)" => -1,
],
'output_formatter' => function($value) {
static $map = ['0'=>"No flash", '1'=>"Flash"];
static $map = ["false"=>"No flash", "true"=>"Flash", '0'=>"No flash", '1'=>"Flash"];
return array_key_exists($value, $map) ? $map[$value] : $value;
},
],

View File

@@ -90,6 +90,16 @@ class Index
// TODO Maybe replace nfkc_normalizer + asciifolding with icu_folding
'filter' => ['nfkc_normalizer', 'asciifolding']
],
'truncation_analyzer' => [
'type' => 'custom',
'tokenizer' => 'truncation_tokenizer',
'filter' => ['lowercase', 'stop', 'kstem']
],
'truncation_analyzer#search' => [
'type' => 'custom',
'tokenizer' => 'truncation_tokenizer',
'filter' => ['lowercase', 'stop', 'kstem']
],
// Lang specific
'fr_full' => [
'type' => 'custom',
@@ -145,6 +155,12 @@ class Index
]
],
'tokenizer' => [
'truncation_tokenizer' => [
"type" => "edgeNGram",
"min_gram" => "2",
"max_gram" => "15",
"token_chars" => [ "letter", "digit", "punctuation", "symbol" ]
],
'thesaurus_path' => [
'type' => 'path_hierarchy'
]

View File

@@ -13,6 +13,7 @@ namespace Alchemy\Phrasea\SearchEngine\Elastic\Indexer\Record\Hydrator;
use Alchemy\Phrasea\SearchEngine\Elastic\Exception\Exception;
use Alchemy\Phrasea\SearchEngine\Elastic\RecordHelper;
use Alchemy\Phrasea\SearchEngine\Elastic\Structure\GlobalStructure;
use Alchemy\Phrasea\SearchEngine\Elastic\Thesaurus;
use Alchemy\Phrasea\SearchEngine\Elastic\Thesaurus\CandidateTerms;
use Alchemy\Phrasea\SearchEngine\Elastic\Thesaurus\Concept;
@@ -27,7 +28,7 @@ class ThesaurusHydrator implements HydratorInterface
private $thesaurus;
private $candidate_terms;
public function __construct(Structure $structure, Thesaurus $thesaurus, CandidateTerms $candidate_terms)
public function __construct(GlobalStructure $structure, Thesaurus $thesaurus, CandidateTerms $candidate_terms)
{
$this->structure = $structure;
$this->thesaurus = $thesaurus;
@@ -67,7 +68,13 @@ class ThesaurusHydrator implements HydratorInterface
$terms = array();
$filters = array();
$field_names = array();
/** @var Field[] $dbFields */
$dbFields = $this->structure->getAllFieldsByDatabox($record['databox_id']);
foreach ($fields as $name => $field) {
if(!array_key_exists($name, $dbFields) || !$dbFields[$name]->get_generate_cterms()) {
continue;
}
$root_concepts = $field->getThesaurusRoots();
// Loop through all values to prepare bulk query
$field_values = \igorw\get_in($record, explode('.', $index_fields[$name]));
@@ -98,11 +105,8 @@ class ThesaurusHydrator implements HydratorInterface
}
}
else {
$field = $fields[$name];
if($field->get_generate_cterms()) {
$this->candidate_terms->insert($field_names[$offset], $values[$offset]);
}
}
}
}
}

View File

@@ -55,8 +55,13 @@ class StringFieldMapping extends ComplexFieldMapping
{
$child = new StringFieldMapping('light');
$child->setAnalyzer('general_light');
$this->addChild($child);
$child = new StringFieldMapping('truncated');
$child->setAnalyzer('truncation_analyzer', 'indexing');
$child->setAnalyzer('truncation_analyzer#search', 'searching');
$this->addChild($child);
$this->addLocalizedChildren($locales);
return $this;

View File

@@ -4,6 +4,11 @@ namespace Alchemy\Phrasea\SearchEngine\Elastic\Search;
class Escaper
{
public function quoteWord($value)
{
return '"' . $this->escapeRaw($value) . '"';
}
public function escapeWord($value)
{
// Strip double quotes from values to prevent broken queries

View File

@@ -46,22 +46,23 @@ class FacetsResponse
if (!isset($bucket['key']) || !isset($bucket['doc_count'])) {
$this->throwAggregationResponseError();
}
$key = array_key_exists('key_as_string', $bucket) ? $bucket['key_as_string'] : $bucket['key'];
if($tf) {
// the field is one of the hardcoded tech fields
$value = [
'value' => $valueFormatter($bucket['key']),
'raw_value' => $bucket['key'],
'value' => $valueFormatter($key),
'raw_value' => $key,
'count' => $bucket['doc_count'],
'query' => sprintf($tf['query'], $this->escaper->escapeWord($bucket['key']))
'query' => sprintf($tf['query'], $this->escaper->escapeWord($key))
];
}
else {
// the field is a normal field
$value = [
'value' => $bucket['key'],
'raw_value' => $bucket['key'],
'value' => $key,
'raw_value' => $key,
'count' => $bucket['doc_count'],
'query' => sprintf('field.%s:%s', $this->escaper->escapeWord($name), $this->escaper->escapeWord($bucket['key']))
'query' => sprintf('field.%s=%s', $this->escaper->escapeWord($name), $this->escaper->quoteWord($key))
];
}

View File

@@ -9,6 +9,7 @@ use Alchemy\Phrasea\SearchEngine\Elastic\Structure\Field;
use Alchemy\Phrasea\SearchEngine\Elastic\AST\Field as ASTField;
use Alchemy\Phrasea\SearchEngine\Elastic\AST\Flag;
use Alchemy\Phrasea\SearchEngine\Elastic\Structure\Structure;
use Alchemy\Phrasea\SearchEngine\SearchEngineOptions;
/**
* @todo Check for private fields and only search on them if allowed
@@ -23,13 +24,23 @@ class QueryContext
private $queryLocale;
/** @var array */
private $fields;
/** @var SearchEngineOptions */
private $options;
public function __construct(Structure $structure, array $locales, $queryLocale, array $fields = null)
/**
* @param SearchEngineOptions|null $options
* @param Structure $structure
* @param array $locales
* @param $queryLocale
* @param array $fields
*/
public function __construct($options, Structure $structure, array $locales, $queryLocale, array $fields = null)
{
$this->structure = $structure;
$this->locales = $locales;
$this->queryLocale = $queryLocale;
$this->fields = $fields;
$this->options = $options;
}
public function narrowToFields(array $fields)
@@ -43,7 +54,7 @@ class QueryContext
}
}
return new static($this->structure, $this->locales, $this->queryLocale, $fields);
return new static($this->options, $this->structure, $this->locales, $this->queryLocale, $fields);
}
/**
@@ -131,6 +142,16 @@ class QueryContext
return $ret;
}
public function truncationField(Field $field)
{
if($this->options && $this->options->useTruncation()) {
return [sprintf('%s.truncated', $field->getIndexField())];
}
else {
return [];
}
}
private function localizeFieldName($field)
{
$fields = array();

View File

@@ -23,7 +23,7 @@ class QueryContextFactory
? $this->getLimitedStructure($options)
: $this->structure;
$context = new QueryContext($structure, $this->locales, $this->current_locale);
$context = new QueryContext($options, $structure, $this->locales, $this->current_locale);
if ($options) {
$fields = $this->getSearchedFields($options);

View File

@@ -63,4 +63,26 @@ class StringUtils
return self::$transliterator->transliterate($string);
}
/**
* replace bad chars (ascii 0...31 except 9,10,13)
*
* @param $s
* @param string $replace
* @return mixed
*/
public static function substituteCtrlCharacters($s, $replace = '_')
{
static $bad_chars = null;
if($bad_chars === null) {
$bad_chars = [];
for($i=0; $i<32; $i++) {
if($i != 9 && $i != 10 && $i != 13) {
$bad_chars[] = chr($i);
}
}
}
return str_replace($bad_chars, $replace, $s);
}
}

View File

@@ -24,6 +24,11 @@ class Field implements Typed
*/
private $name;
/**
* @var int
*/
private $databox_id;
/**
* @var string
*/
@@ -73,6 +78,7 @@ class Field implements Typed
}
return new self($field->get_name(), $type, [
'databox_id' => $databox->get_sbas_id(),
'searchable' => $field->is_indexable(),
'private' => $field->isBusiness(),
'facet' => $facet,
@@ -102,6 +108,7 @@ class Field implements Typed
{
$this->name = (string) $name;
$this->type = $type;
$this->databox_id = \igorw\get_in($options, ['databox_id'], 0);
$this->is_searchable = \igorw\get_in($options, ['searchable'], true);
$this->is_private = \igorw\get_in($options, ['private'], false);
$this->facet = \igorw\get_in($options, ['facet']);
@@ -126,6 +133,7 @@ class Field implements Typed
public function withOptions(array $options)
{
return new self($this->name, $this->type, $options + [
'databox_id' => $this->databox_id,
'searchable' => $this->is_searchable,
'private' => $this->is_private,
'facet' => $this->facet,
@@ -155,6 +163,11 @@ class Field implements Typed
return sprintf('concept_path.%s', $this->name);
}
public function get_databox_id()
{
return $this->databox_id;
}
public function getType()
{
return $this->type;

View File

@@ -14,6 +14,12 @@ final class GlobalStructure implements Structure
*/
private $fields = array();
/**
* @var Field[][]
*/
private $fieldsByDatabox = [];
/**
* @var Field[]
* */
@@ -119,6 +125,10 @@ final class GlobalStructure implements Structure
public function add(Field $field)
{
// store info for each field, not still merged by databox
$this->fieldsByDatabox[$field->get_databox_id()][$field->getName()] = $field;
// store merged infos (same field name)
$name = $field->getName();
if (isset($this->fields[$name])) {
@@ -152,6 +162,11 @@ final class GlobalStructure implements Structure
return $this->fields;
}
public function getAllFieldsByDatabox($databox_id)
{
return $this->fieldsByDatabox[$databox_id];
}
/**
* @return Field[]
*/

View File

@@ -32,6 +32,7 @@ class CandidateTerms
public function insert($field, $value)
{
$value = StringUtils::substituteCtrlCharacters($value, '');
$this->ensureVisitorSetup();
if (!$this->visitor->hasTerm($field, $value)) {
$this->new_candidates[$value] = $field;

View File

@@ -71,6 +71,8 @@ class SearchEngineOptions
protected $i18n;
/** @var bool */
protected $stemming = true;
/** @var bool */
protected $use_truncation = false;
/** @var string */
protected $sort_by;
@@ -105,7 +107,8 @@ class SearchEngineOptions
'sort_ord',
'business_fields',
'max_results',
'first_result'
'first_result',
'use_truncation',
];
/**
@@ -217,6 +220,29 @@ class SearchEngineOptions
return $this;
}
/**
* Tells whether to use truncation or not
*
* @param boolean $boolean
* @return $this
*/
public function setUseTruncation($boolean)
{
$this->use_truncation = !!$boolean;
return $this;
}
/**
* Return wheter the use of truncation is enabled or not
*
* @return boolean
*/
public function useTruncation()
{
return $this->use_truncation;
}
/**
* Return wheter the use of stemming is enabled or not
*
@@ -542,6 +568,8 @@ class SearchEngineOptions
$options->setFields($databoxFields);
$options->setDateFields($databoxDateFields);
$options->setUseTruncation((Boolean) $request->get('truncation'));
return $options;
}
@@ -628,6 +656,7 @@ class SearchEngineOptions
}
},
'stemming' => $optionSetter('setStemming'),
'use_truncation' => $optionSetter('setUseTruncation'),
'date_fields' => function ($value, SearchEngineOptions $options) use ($fieldNormalizer) {
$options->setDateFields($fieldNormalizer($value));
},

View File

@@ -48,6 +48,7 @@ class PhraseanetExtension extends \Twig_Extension
new \Twig_SimpleFunction('border_checker_from_fqcn', array($this, 'getCheckerFromFQCN')),
new \Twig_SimpleFunction('caption_field', array($this, 'getCaptionField')),
new \Twig_SimpleFunction('caption_field_label', array($this, 'getCaptionFieldLabel')),
new \Twig_SimpleFunction('caption_field_gui_visible', array($this, 'getCaptionFieldGuiVisible')),
new \Twig_SimpleFunction('caption_field_order', array($this, 'getCaptionFieldOrder')),
new \Twig_SimpleFunction('flag_slugify', array(Flag::class, 'normalizeName')),
@@ -77,6 +78,29 @@ class PhraseanetExtension extends \Twig_Extension
return '';
}
/**
* get localized field's gui_visible
* @param RecordInterface $record
* @param $fieldName
* @return string - the name gui_visible
*/
public function getCaptionFieldGuiVisible(RecordInterface $record, $fieldName)
{
if ($record) {
/** @var \appbox $appbox */
$appbox = $this->app['phraseanet.appbox'];
$databox = $appbox->get_databox($record->getDataboxId());
foreach ($databox->get_meta_structure() as $meta) {
/** @var \databox_field $meta */
if ($meta->get_name() === $fieldName) {
return $meta->get_gui_visible($this->app['locale']);
}
}
}
return '';
}
public function getCaptionField(RecordInterface $record, $field, $value)
{
if ($record instanceof ElasticsearchRecord) {

View File

@@ -80,7 +80,7 @@ class RedisSessionHandler implements \SessionHandlerInterface
*/
public function destroy($sessionId)
{
return 1 === $this->redis->delete($this->prefix.$sessionId);
return 1 === $this->redis->del($this->prefix.$sessionId);
}
/**

View File

@@ -463,7 +463,8 @@ class databox extends base implements ThumbnailedElement
->set_type($type)
->set_tbranch(isset($field['tbranch']) ? (string) $field['tbranch'] : '')
->set_generate_cterms((isset($field['generate_cterms']) && (string) $field['generate_cterms'] == 1))
->set_gui_editable((isset($field['gui_editable']) && (string) $field['gui_editable'] == 1))
->set_gui_editable((!isset($field['gui_editable']) || (isset($field['gui_editable']) && (string) $field['gui_editable'] == 1)))
->set_gui_visible((!isset($field['gui_visible']) || (isset($field['gui_visible']) && (string) $field['gui_visible'] == 1)))
->set_thumbtitle(isset($field['thumbtitle']) ? (string) $field['thumbtitle'] : (isset($field['thumbTitle']) ? $field['thumbTitle'] : '0'))
->set_report(isset($field['report']) ? (string) $field['report'] : '1')
->save();

View File

@@ -45,6 +45,7 @@ class databox_field implements cache_cacheableInterface
protected $tbranch;
protected $generate_cterms;
protected $gui_editable;
protected $gui_visible;
protected $separator;
protected $thumbtitle;
@@ -170,6 +171,7 @@ class databox_field implements cache_cacheableInterface
$this->tbranch = $row['tbranch'];
$this->generate_cterms = (bool)$row['generate_cterms'];
$this->gui_editable = (bool)$row['gui_editable'];
$this->gui_visible = (bool)$row['gui_visible'];
$this->VocabularyType = $row['VocabularyControlType'];
$this->VocabularyRestriction = (bool)$row['RestrictToVocabularyControl'];
@@ -312,6 +314,7 @@ class databox_field implements cache_cacheableInterface
`tbranch` = :tbranch,
`generate_cterms` = :generate_cterms,
`gui_editable` = :gui_editable,
`gui_visible` = :gui_visible,
`sorter` = :position,
`thumbtitle` = :thumbtitle,
`VocabularyControlType` = :VocabularyControlType,
@@ -337,6 +340,7 @@ class databox_field implements cache_cacheableInterface
':tbranch' => $this->tbranch,
':generate_cterms' => $this->generate_cterms ? '1' : '0',
':gui_editable' => $this->gui_editable ? '1' : '0',
':gui_visible' => $this->gui_visible ? '1' : '0',
':position' => $this->position,
':thumbtitle' => $this->thumbtitle,
':VocabularyControlType' => $this->getVocabularyControl() ? $this->getVocabularyControl()->getType() : null,
@@ -390,6 +394,7 @@ class databox_field implements cache_cacheableInterface
$meta->setAttribute('tbranch', $this->tbranch);
$meta->setAttribute('generate_cterms', $this->generate_cterms ? '1' : '0');
$meta->setAttribute('gui_editable', $this->gui_editable ? '1' : '0');
$meta->setAttribute('gui_visible', $this->gui_visible ? '1' : '0');
if ($this->multi) {
$meta->setAttribute('separator', $this->separator);
}
@@ -743,6 +748,17 @@ class databox_field implements cache_cacheableInterface
return $this;
}
/**
* @param boolean $gui_visible
* @return databox_field
*/
public function set_gui_visible($gui_visible)
{
$this->gui_visible = $gui_visible;
return $this;
}
/**
*
* @param string $separator
@@ -845,6 +861,15 @@ class databox_field implements cache_cacheableInterface
return $this->gui_editable;
}
/**
*
* @return boolean
*/
public function get_gui_visible()
{
return $this->gui_visible;
}
/**
* @param Boolean $all If set to false, returns a one-char separator to use for serialiation
*
@@ -957,6 +982,7 @@ class databox_field implements cache_cacheableInterface
'tbranch' => $this->tbranch,
'generate_cterms' => $this->generate_cterms,
'gui_editable' => $this->gui_editable,
'gui_visible' => $this->gui_visible,
'separator' => $this->separator,
'required' => $this->required,
'report' => $this->report,
@@ -995,10 +1021,10 @@ class databox_field implements cache_cacheableInterface
}
$sql = "INSERT INTO metadatas_structure
(`id`, `name`, `src`, `readonly`, `gui_editable`, `required`, `indexable`, `type`, `tbranch`, `generate_cterms`,
(`id`, `name`, `src`, `readonly`, `gui_editable`,`gui_visible`, `required`, `indexable`, `type`, `tbranch`, `generate_cterms`,
`thumbtitle`, `multi`, `business`, `aggregable`,
`report`, `sorter`, `separator`)
VALUES (null, :name, '', 0, 1, 0, 1, 'string', '', 1,
VALUES (null, :name, '', 0, 1, 1, 0, 1, 'string', '', 1,
null, 0, 0, 0,
1, :sorter, '')";

View File

@@ -2049,6 +2049,14 @@
<default>1</default>
<comment></comment>
</field>
<field>
<name>gui_visible</name>
<type>int(1) unsigned</type>
<null></null>
<extra></extra>
<default>1</default>
<comment></comment>
</field>
</fields>
<indexes>
<index>

View File

@@ -79,7 +79,7 @@
<size>748</size>
<mediatype>video</mediatype>
<writeDatas>yes</writeDatas>
<acodec>libfaac</acodec>
<acodec>libmp3lame</acodec>
<vcodec>libx264</vcodec>
<devices>screen</devices>
<bitrate>1000</bitrate>

View File

@@ -79,7 +79,7 @@
<size>748</size>
<mediatype>video</mediatype>
<writeDatas>yes</writeDatas>
<acodec>libfaac</acodec>
<acodec>libmp3lame</acodec>
<vcodec>libx264</vcodec>
<devices>screen</devices>
<bitrate>1000</bitrate>

View File

@@ -79,7 +79,7 @@
<size>748</size>
<mediatype>video</mediatype>
<writeDatas>yes</writeDatas>
<acodec>libfaac</acodec>
<acodec>libmp3lame</acodec>
<vcodec>libx264</vcodec>
<devices>screen</devices>
<bitrate>1000</bitrate>

View File

@@ -65,7 +65,7 @@
"normalize-css": "^2.1.0",
"npm": "^6.0.0",
"npm-modernizr": "^2.8.3",
"phraseanet-production-client": "0.34.72-d",
"phraseanet-production-client": "0.34.80-d",
"requirejs": "^2.3.5",
"tinymce": "^4.0.28",
"underscore": "^1.8.3",

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:jms="urn:jms:translation" version="1.2">
<file date="2019-05-21T05:53:02Z" source-language="en" target-language="de" datatype="plaintext" original="not.available">
<file date="2019-11-19T08:48:53Z" source-language="en" target-language="de" datatype="plaintext" original="not.available">
<header>
<tool tool-id="JMSTranslationBundle" tool-name="JMSTranslationBundle" tool-version="1.1.0-DEV"/>
<note>The source node in most cases contains the sample message as written by the developer. If it looks like a dot-delimitted string such as "form.label.firstname", then the developer has not provided a default message.</note>

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:jms="urn:jms:translation" version="1.2">
<file date="2019-05-21T05:54:16Z" source-language="en" target-language="en" datatype="plaintext" original="not.available">
<file date="2019-11-19T08:49:17Z" source-language="en" target-language="en" datatype="plaintext" original="not.available">
<header>
<tool tool-id="JMSTranslationBundle" tool-name="JMSTranslationBundle" tool-version="1.1.0-DEV"/>
<note>The source node in most cases contains the sample message as written by the developer. If it looks like a dot-delimitted string such as "form.label.firstname", then the developer has not provided a default message.</note>

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:jms="urn:jms:translation" version="1.2">
<file date="2019-05-21T05:55:28Z" source-language="en" target-language="fr" datatype="plaintext" original="not.available">
<file date="2019-11-15T08:03:23Z" source-language="en" target-language="fr" datatype="plaintext" original="not.available">
<header>
<tool tool-id="JMSTranslationBundle" tool-name="JMSTranslationBundle" tool-version="1.1.0-DEV"/>
<note>The source node in most cases contains the sample message as written by the developer. If it looks like a dot-delimitted string such as "form.label.firstname", then the developer has not provided a default message.</note>

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:jms="urn:jms:translation" version="1.2">
<file date="2019-05-21T05:56:47Z" source-language="en" target-language="nl" datatype="plaintext" original="not.available">
<file date="2019-11-19T08:50:13Z" source-language="en" target-language="nl" datatype="plaintext" original="not.available">
<header>
<tool tool-id="JMSTranslationBundle" tool-name="JMSTranslationBundle" tool-version="1.1.0-DEV"/>
<note>The source node in most cases contains the sample message as written by the developer. If it looks like a dot-delimitted string such as "form.label.firstname", then the developer has not provided a default message.</note>

View File

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="32px" height="32px" viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: sketchtool 58 (101010) - https://sketch.com -->
<title>5609DDD5-6B9C-411B-B926-EEA284949013</title>
<desc>Created with sketchtool.</desc>
<defs>
<polygon id="path-1" points="0 0 22.3169609 16.2062701 0 32.4125403"></polygon>
<filter x="-31.4%" y="-15.4%" width="162.7%" height="143.2%" filterUnits="objectBoundingBox" id="filter-3">
<feOffset dx="0" dy="2" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
<feGaussianBlur stdDeviation="2" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0" type="matrix" in="shadowBlurOuter1"></feColorMatrix>
</filter>
</defs>
<g id="Pictos" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Pictos/Arrow/Pleine">
<g id="Colors/Black" transform="translate(5.000000, 0.000000)">
<mask id="mask-2" fill="white">
<use xlink:href="#path-1"></use>
</mask>
<g id="Mask" fill="black" fill-opacity="1">
<use filter="url(#filter-3)" xlink:href="#path-1"></use>
</g>
<g id="Colors/Rectangular/Black" mask="url(#mask-2)" fill="#000000">
<g transform="translate(-5.000000, 0.000000)" id="Rectangle">
<rect x="0" y="0" width="32" height="32"></rect>
</g>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -0,0 +1 @@
<svg aria-hidden="true" focusable="false" data-prefix="far" data-icon="times-circle" class="svg-inline--fa fa-times-circle fa-w-16" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="#aaa" d="M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zm0 448c-110.5 0-200-89.5-200-200S145.5 56 256 56s200 89.5 200 200-89.5 200-200 200zm101.8-262.2L295.6 256l62.2 62.2c4.7 4.7 4.7 12.3 0 17l-22.6 22.6c-4.7 4.7-12.3 4.7-17 0L256 295.6l-62.2 62.2c-4.7 4.7-12.3 4.7-17 0l-22.6-22.6c-4.7-4.7-4.7-12.3 0-17l62.2-62.2-62.2-62.2c-4.7-4.7-4.7-12.3 0-17l22.6-22.6c4.7-4.7 12.3-4.7 17 0l62.2 62.2 62.2-62.2c4.7-4.7 12.3-4.7 17 0l22.6 22.6c4.7 4.7 4.7 12.3 0 17z"></path></svg>

After

Width:  |  Height:  |  Size: 705 B

View File

@@ -0,0 +1 @@
<svg aria-hidden="true" focusable="false" data-prefix="far" data-icon="check-circle" class="svg-inline--fa fa-check-circle fa-w-16" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="#aaa" d="M256 8C119.033 8 8 119.033 8 256s111.033 248 248 248 248-111.033 248-248S392.967 8 256 8zm0 48c110.532 0 200 89.451 200 200 0 110.532-89.451 200-200 200-110.532 0-200-89.451-200-200 0-110.532 89.451-200 200-200m140.204 130.267l-22.536-22.718c-4.667-4.705-12.265-4.736-16.97-.068L215.346 303.697l-59.792-60.277c-4.667-4.705-12.265-4.736-16.97-.069l-22.719 22.536c-4.705 4.667-4.736 12.265-.068 16.971l90.781 91.516c4.667 4.705 12.265 4.736 16.97.068l172.589-171.204c4.704-4.668 4.734-12.266.067-16.971z"></path></svg>

After

Width:  |  Height:  |  Size: 738 B

View File

@@ -50,7 +50,9 @@ $mainMenuLinkBackgroundHoverColor: transparent;
}
}
[class*=" icon-"].fa, [class^=icon-].fa, .fa {
font-family: Fontawesome!important;
}
[class^="icon-"], [class*=" icon-"].icomoon {
display: inline-block;
width: inherit;

View File

@@ -1,4 +1,5 @@
$iconsPath: '../../../assets/common/images/icons/';
ul.image_set{
padding:0;
margin:0;
@@ -6,26 +7,57 @@ ul.image_set{
width:100%;
border:none;
list-style-type: none;
display: flex;
flex-wrap: wrap;
}
li.image_box a img{
position:relative;
vertical-align:middle;
border:none;
width: auto!important;
height: 100% !important;
top: 0 !important;
object-fit: contain;
}
li.image_box a{
padding:0;
text-shadow:0;
margin:0;
width: 100%;
}
li.image_box{
width:80px;
height:80px;
position:relative;
float:left;
margin:0px 15px 15px 0px;
padding:0px;
text-align:center;
width: 18%;
position: relative;
margin: 0 2.5% 15px 0;
padding: 0;
display: flex;
align-items: center;
text-align: center;
background: #ededed;
&:nth-child(5n) {
margin-right: 0;
}
.thumb_wrapper {
background: #ededed;
width: 100%!important;
height: 160px!important;
@media screen and (max-width: 767px) {
height: 130px !important;
}
}
}
@media screen and (max-width: 767px) {
li.image_box{
width: 32%;
margin: 0 2% 15px 0;
&:nth-child(5n) {
margin-right: 2% ;
}
&:nth-child(3n) {
margin-right: 0;
}
}
}
a.no.active_choice{
background-color:red;
color: #fff;
@@ -45,10 +77,12 @@ a.active_choice{
}
.valid_choice.agree{
background-image: url('#{$iconsPath}ok.png');
background: #7ed321;
border-top-left-radius: 100px;
}
.valid_choice.disagree{
background-image: url('#{$iconsPath}delete.png');
background: #d0021b;
border-top-left-radius: 100px;
}
.thumb_wrapper {
@@ -70,3 +104,7 @@ a.active_choice{
}
}
.ui-footer .ui-title, .ui-header .ui-title {
font-size: 18px;
}

View File

@@ -1,5 +1,28 @@
@import './jquery-mobile/jquery-validator';
@font-face {
font-family: 'icomoon';
src: url("../../common/fonts/PhraseanetIcomoon/fonts/icomoon.eot?xt8hfo");
src: url("../../common/fonts/PhraseanetIcomoon/fonts/icomoon.eot?xt8hfo#iefix") format("embedded-opentype"), url("../../common/fonts/PhraseanetIcomoon/fonts/icomoon.ttf?xt8hfo") format("truetype"), url("../../common/fonts/PhraseanetIcomoon/fonts/icomoon.woff?xt8hfo") format("woff"), url("../../common/fonts/PhraseanetIcomoon/fonts/icomoon.svg?xt8hfo#icomoon") format("svg");
font-weight: normal;
font-style: normal;
}
[class^="icon-"], [class*=" icon-"] {
/* use !important to prevent issues with browser extensions that change fonts */
font-family: 'icomoon' !important;
speak: none;
font-style: normal;
font-weight: normal;
font-variant: normal;
text-transform: none;
line-height: 1;
/* Better Font Rendering =========== */
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.nav_button {
width: 90%;
margin: 0px auto;
@@ -25,3 +48,668 @@
#right-btn {
float: right;
}
.report-modal, .ui-dialog .ui-dialog-content {
background: linear-gradient(#3c3c3c, #111);
}
.ui-bar-c, .ui-body-c, .ui-btn-down-c, .ui-btn-hover-c, .ui-btn-up-c, .ui-overlay-c {
text-shadow: none;
}
@import '../../_shared/styles/variables';
$lightboxPath: '../images/';
$imagesPath: '/assets/vendors/jquery-ui/images/dark-hive/';
$mainMenuBackgroundColor: #c7c7c7; //BFBFBF;
$mainMenuBottomBorder: 1px solid #c7c7c7;
$mainMenuLinkColor: #212121;
$mainMenuLinkHoverColor: #000000;
$mainMenuLinkActiveColor: #BFBFBF;
$mainMenuLinkBackgroundHoverColor: transparent;
body {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 13px;
}
ul {
list-style-type: none;
margin: 0;
padding: 0;
}
img {
border: none;
vertical-align: middle;
}
.title15 {
font-size: 15px;
font-weight: bold;
}
.record_display_box {
width: 100%;
height: 100%;
display: block;
table {
vertical-align: middle;
table-layout: fixed;
}
.title {
margin: 0 10px;
}
.record {
position: relative;
max-width: none;
max-height: none;
}
.header {
color: #BFBFBF;
height: 30px;
bottom: auto;
overflow: hidden;
.title {
overflow: hidden;
line-height: 20px;
height: 20px;
}
}
.lightbox_container {
top: 30px;
overflow: hidden;
}
.display_id {
top: 5px;
margin: 0 0 0 5px;
background: #bebebe;
}
}
#sc_wrapper {
left: 20px;
right: 20px;
overflow-x: scroll;
overflow-y: hidden;
}
#sc_container {
position: relative;
height: 100%;
}
.basket_element_wrapper {
position: relative;
float: left;
padding: 0 5px;
}
.basket_element {
position: relative;
float: left;
width: 114px;
height: 130px;
border: 1px solid #212121;
text-align: left;
padding: 5px 8px;
&.selected {
background-color: #212121;
}
.display_id {
top: 4px;
left: 8px;
}
.agreement {
position: absolute;
top: 4px;
right: 8px;
z-index: 99;
}
.image {
position: relative;
z-index: 90;
}
.previewTips {
background-image: url('#{$iconsPath}zoom.gif');
background-position: center center;
background-repeat: no-repeat;
cursor: help;
position: absolute;
bottom: 4px;
right: 8px;
height: 18px;
width: 18px;
z-index: 99;
}
}
#report .display_id {
top: 0;
left: 0;
border-radius: 0;
background-color: rgba(216, 216, 216, 0.7);
font-weight: 700;
z-index: 99;
color: #333333;
position: absolute;
width: 22px;
padding: 2px 0;
font-size: 12px;
}
#report .CHIM.diapo {
text-align: center;
}
.display_id {
background-color: #FFFFFF;
padding: 3px 6px;
font-weight: bold;
z-index: 99;
color: #212121;
-moz-border-radius: 50%;
-webkit-border-radius: 50%;
border-radius: 50%;
position: absolute;
}
.not_decided {
opacity: 0.30;
filter: alpha(opacity=30);
}
#sc_wrapper .not_decided {
display: none;
}
#right_scroller {
width: 30px;
right: -10px;
left: auto;
background-image: url('#{$lightboxPath}right_arrow.png');
background-position: center center;
background-repeat: no-repeat;
}
#left_scroller {
width: 30px;
left: -10px;
right: auto;
background-image: url('#{$lightboxPath}left_arrow.png');
background-position: center center;
background-repeat: no-repeat;
}
#basket_infos {
overflow: hidden;
background-color: #1F1E1B;
color: #BFBFBF;
}
#basket_options {
height: 35px;
top: auto;
background-color: #1F1E1B;
text-align: center;
}
#basket_options .confirm_report {
margin: 5px auto;
}
#basket_infos .user_infos {
height: 120px;
top: auto;
}
#basket_infos {
table {
width: 100%;
margin: 5px 0;
.title {
width: 100%;
}
.report_wrapper {
text-align: right;
}
}
}
#right_column {
left: auto;
right: 0;
width: 240px;
}
#right_column_validation_toggle {
bottom: 45px;
background-color: #1F1E1B;
height: 30px;
top: auto;
text-align: center;
display: none;
line-height: 25px;
color: #BFBFBF;
}
#right_column .right_column_title {
height: 30px;
bottom: auto;
}
#right_column .right_column_title img.expanded {
display: none;
}
#right_column .right_column_title img.collapsed {
display: inline;
}
#right_column .right_column_title.expanded img.expanded {
display: inline;
}
#right_column .right_column_title.expanded img.collapsed {
display: none;
}
#right_column .right_column_wrapper {
top: 30px;
bottom: 45px;
}
#right_column .right_column_wrapper.caption {
bottom: 85px;
}
#record_infos {
overflow-x: hidden;
overflow-y: auto;
}
#record_compare {
visibility: hidden;
top: auto;
left: auto;
}
#record_compare .header, #record_compare .lightbox_container {
left: 5px;
}
#record_wrapper.comparison #record_main .header, #record_wrapper.comparison #record_main .lightbox_container {
right: 5px;
}
.agreement_selector {
position: absolute;
bottom: 10px;
right: 10px;
}
.clickable {
cursor: pointer;
}
.big_box.agree {
border: 2px solid #35AC00;
background-color: #35AC00;
}
.big_box.disagree {
border: 2px solid #DE1200;
background-color: #DE1200;
}
.big_box, .big_box.not_decided {
width: 95px;
margin: 0 auto;
padding: 6px 10px;
height: 30px;
color: #1F1E1B;
background-color: #353430;
border: 2px solid #353430;
text-align: center;
opacity: 1;
filter: alpha(opacity=100);
}
.big_box span {
margin: 0 10px;
}
li.userchoice {
margin: 5px 0 0px 20px;
}
.userchoice.disagree {
color: #DE1200;
}
.userchoice.agree {
color: #35AC00;
}
.basket_report_user {
padding: 7px 10px;
}
.basket_report_user_wrapper {
margin: 0;
border-bottom: 1px solid #b2b2b2;
border-radius: 0;
display: inline-block;
padding: 10px 15px;
width: 100%;
box-sizing: border-box;
background: #e5e5e5;
text-shadow: none;
float: left;
}
.ui-button-text-icon-primary .ui-button-text, .ui-button-text-icons .ui-button-text {
padding: 0.2em 1em 0.2em 2.1em;
}
/*******
*
* Index
*
* ******/
#main_index {
position: relative;
width: 600px;
margin: 0 auto;
}
#main_wrapper {
width: 100%;
}
#main_wrapper h1 {
font-weight: bold;
font-size: 25px;
}
table th {
text-align: right;
vertical-align: bottom;
height: 60px;
border-bottom: 1px solid #8F8F8F;
}
table th.title {
text-align: left;
}
table th h1 {
margin-left: 20px;
}
table th i {
margin-right: 20px;
}
.ui-state-default.note_closer, .ui-state-default.note_saver {
background-color: #353430;
padding: 5px;
margin: 0 10px;
}
.ui-state-default.note_saver {
background-color: #1F1E1B;
}
.record_display_box form .buttons {
margin: 5px;
text-align: right;
}
.record_display_box form {
margin: 15px 0;
width: 90%;
}
.record_display_box form textarea {
width: 100%;
height: 75px;
}
#navigation {
width: 200px;
}
.basket_downloader {
background-color: #1F1E1B;
margin: 0 10px;
padding: 2px;
border: 0;
}
hr {
width: 100%;
height: 1px;
background-color: #212121;
border: none;
}
.report {
margin: 0 10px;
vertical-align: bottom;
}
/*
*
* Overrides JqueryUI
*
*/
.ui-button {
background: transparent;
border: none;
margin: 0;
padding: 0;
}
.ui-button-text-only .ui-button-text {
padding: 0.15em;
}
.ui-dialog {
.ui-dialog-titlebar {
padding: 1em 1em;
}
}
.videoTips {
width: 100%;
height: 100%;
}
#basket_infos {
margin-bottom: 20px;
.mobile_aggreement_box {
padding: 20px;
}
}
.ui-button-icon.ui-icon.ui-icon-closethick {
background-position: -73px 0px;
background-color: rgba(0, 0, 0, .9);
padding: 0;
}
.btn-container {
padding: 0 20px;
}
button.confirm_report {
background: #38c !important;
color: #fff !important;
font-size: 16px;
font-weight: bold;
padding: 6px;
display: inline-block;
border: 1px solid #38c;
text-shadow: 0 1px 0 #111;
border-radius: 16px;
font-family: Roboto, sans-serif;
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2) !important;
}
.report_wrapper {
position: relative;
margin-bottom: 20px;
}
.report_btn {
position: absolute;
top: -41px;
right: 0;
z-index: 1;
background: #8bc34a;
border: 1px solid #8bc34a;
box-sizing: border-box;
padding: 8px 13px;
min-width: 110px;
border-radius: 4px;
color: #fff !important;
text-decoration: none;
text-align: center;
transition: all 0.3s;
&:hover, &:focus {
background: darken(#8bc34a, 0.2);
border-color: darken(#8bc34a, 0.3);
}
&.report_list {
background: #f44336;
border-color: #f44336;
&:hover, &:focus {
background: darken(#f44336, 0.2);
border-color: darken(#f44336, 0.3);
}
}
}
.report_list {
display: none;
}
.report_summary_backup, .report_list_backup {
display: none;
}
.chim-wrapper-block {
margin-bottom: 15px;
.chim-block {
margin: 0;
}
.chim-inner {
width: 100%;
display: inline-block;
min-width: 296px;
}
.chim-left {
display: inline-block;
width: 100%;
background: #ededed;
text-align: center;
position: relative;
.diapo {
display: inline-block;
div {
max-width: 100%;
}
}
}
.chim-right {
width: 100%;
.validate-icon {
float: left;
height: 25px;
vertical-align: middle;
display: flex;
flex-wrap: wrap;
align-items: center;
}
h3 {
font-size: 16px;
margin: 0;
padding-top: 3px;
}
}
}
.thumb_wrapper img.record_image {
max-width: 100%;
}
/*header lightbox*/
.ui-footer .ui-title, .ui-header .ui-title {
min-height: 26px;
padding: 10px 0;
}
.ui-listview.lightbox-list-view {
& > .ui-li-static {
padding: .7em 1em;
text-overflow: inherit;
white-space: inherit;
}
& > li {
border-color: #b2b2b2;
background: none;
p {
text-overflow: inherit;
white-space: inherit;
line-height: 15px;
font-size: 13px;
color: #141414;
}
}
h3 {
font-family: Roboto, sans-serif;;
font-size: 16px;
font-weight: bold;
font-stretch: normal;
font-style: normal;
line-height: 0.94;
letter-spacing: normal;
color: #141414;
}
}
.ui-footer.ui-bar-inherit {
border: 0;
}
.center-image {
text-align: center;
}
/*validate page*/
.validate-icon {
float: left;
position: absolute;
.icomoon {
color: #fff;
font-size: 17px;
padding: 3px;
border-radius: 50px;
display: inline-block;
}
.icon-disagree {
background: #cd2f2f;
}
.icon-agree {
background: #8bc34a;
}
}
.validate-info {
float: left;
padding-left: 36px;
}

View File

@@ -109,3 +109,48 @@ dd {
float: left;
}
}
/*Help menu*/
.contextMenu.helpcontextmenu {
display: none;
&.shown {
display: block;
position: absolute;
z-index: 101;
}
}
.help-trigger {
display:inline-block;
cursor:pointer;
padding: 0 4px;
border:none;
margin-left: -12px;
margin-right: 94px;
.fa-caret-down:before {
content: "\25BC";
font-size: 11px;
}
}
.context-menu-theme-vista {
background-image: none;
background-color: #414141;
border-bottom-left-radius: 2px;
border-bottom-right-radius: 2px;
.context-menu-item {
transition: 500ms;
&:hover {
background-image: none;
background-color: #75abff;
border: none;
}
.context-menu-item-inner {
padding: 4px 12px;
margin-left: 0;
font-family: Helvetica;
font-size: 12px;
font-weight: 700;
color: #fff;
}
}
}

View File

@@ -50,7 +50,6 @@
class="ui-btn-right jqm-home">{{ 'Home' | trans }}</a>
</div>
<div id="content" data-role="content">
{{ thumbnail.format100percent(record.get_preview()) }}
<div class="nav_button">
{% if prevId != NULL %}
<a data-ajax="false" id="left-btn"
@@ -64,13 +63,14 @@
{% endif %}
<div style="clear: both;"></div>
</div>
<div class="center-image">{{ thumbnail.format100percent(record.get_preview()) }}</div>
{% if basket_element.getBasket().getValidation() %}
{% if basket_element.getBasket().getValidation().getParticipant(app.getAuthenticatedUser()).getCanAgree() %}
<fieldset data-role="controlgroup" data-type="horizontal" style="text-align:center;">
<input {% if basket_element.getUserValidationDatas(app.getAuthenticatedUser()).getAgreement() == true%}checked="checked"{% endif %} type="radio" name="radio-view" id="radio-view-yes_{{basket_element.getId()}}" value="yes" />
<input onclick="window.location.reload();" {% if basket_element.getUserValidationDatas(app.getAuthenticatedUser()).getAgreement() == true%}checked="checked"{% endif %} type="radio" name="radio-view" id="radio-view-yes_{{basket_element.getId()}}" value="yes" />
<label class="agreement_radio" style="width:110px;text-align:center;"
for="radio-view-yes_{{ basket_element.getId() }}">{{ 'validation:: OUI' | trans }}</label>
<input {% if basket_element.getUserValidationDatas(app.getAuthenticatedUser()).getAgreement() == false and basket_element.getUserValidationDatas(app.getAuthenticatedUser()).getAgreement() is not null %}checked="checked"{% endif %} type="radio" name="radio-view" id="radio-view-no_{{basket_element.getId()}}" value="no" />
<input onclick="window.location.reload();" {% if basket_element.getUserValidationDatas(app.getAuthenticatedUser()).getAgreement() == false and basket_element.getUserValidationDatas(app.getAuthenticatedUser()).getAgreement() is not null %}checked="checked"{% endif %} type="radio" name="radio-view" id="radio-view-no_{{basket_element.getId()}}" value="no" />
<label class="agreement_radio" style="width:110px;text-align:center;"
for="radio-view-no_{{ basket_element.getId() }}">{{ 'validation:: NON' | trans }}</label>
</fieldset>
@@ -81,7 +81,7 @@
</a>
</div>
<div>
<ul data-role="listview" id="notes_{{basket_element.getId()}}">
<ul data-role="listview" class="lightbox-list-view" id="notes_{{basket_element.getId()}}">
{% include 'lightbox/sc_note.html.twig' %}
</ul>
</div>

View File

@@ -1,17 +1,21 @@
{% for validationDatas in basket_element.getValidationDatas() %}
{% set is_mine = validationDatas.getParticipant().getUser().getId() == app.getAuthenticatedUser().getId() %}
{% if validationDatas.getNote() != '' or (validationDatas.getAgreement() is not null and is_mine) %}
<li>
<h3 style="text-align:left;">
{% if is_mine == false and validationDatas.getAgreement() is not null %}
<img style="vertical-align:middle;"
src="/assets/lightbox/images/{% if validationDatas.getAgreement() == true %}agree.png{% else %}disagree.png{% endif %}" />
<div class="validate-icon">
{% if basket_element.getBasket().getValidation().getParticipant(app.getAuthenticatedUser()).getCanSeeOthers() or validationDatas.getParticipant().getUser() == app.getAuthenticatedUser() %}
{% if validationDatas.getAgreement() == true %}<span class="icomoon icon-agree">&#xe95f;</span>{% endif %}
{% if validationDatas.getAgreement() == false and validationDatas.getAgreement() is not null %}<span class="icomoon icon-disagree">&#xe971; {{ validationDatas.getAgreement() }}</span>{% endif %}
{% endif %}
</div>
<div class="validate-info">
<h3>
{{ validationDatas.getParticipant().getUser().getDisplayName() }}
</h3>
{% if basket_element.getBasket().getValidation().getParticipant(app.getAuthenticatedUser()).getCanSeeOthers() or validationDatas.getParticipant().getUser() == app.getAuthenticatedUser() %}
{% if validationDatas.getNote() != '' %}
<p style="text-align:left;">{{ 'validation:: note' | trans }} : {{ validationDatas.getNote()|nl2br }}</p>
<p style="text-align:left;">{{ 'validation:: note' | trans }} : {{ validationDatas.getNote()|nl2br }} </p>
{% endif %}
{% endif %}
</div>
</li>
{% endif %}
{% endfor %}

View File

@@ -38,22 +38,56 @@
{% set basket_length = basket.getElements().count() %}
{% trans with {'%basket_length%' : basket_length} %}%basket_length% documents{% endtrans %}
</p>
{% if basket.getValidation() %}
<div class="report_wrapper">
<a id="report_summary" class="report_btn report_summary" href="#" onclick="$.ajax({
type: 'GET',
url: '/lightbox/ajax/LOAD_REPORT/' + $('#navigation').val() + '/',
dataType: 'html',
success: function (data) {
$('#report_list_backup').empty().append($('#report').html());
$('#report').empty().append(data);
$('#report .record_image').css('height','auto');
return;
}
});
$(this).hide();
$('#report_list').show();">
{{ 'lightbox::recaptitulatif' | trans }}
</a>
<a id="report_list" class="report_btn report_list" href="#" onclick=" $(this).hide();
$('#report_summary').show();
$('#report').empty().append($('#report_list_backup').html());">
{{ 'lightbox::list' | trans }}
</a>
</div>
<div class="report_list_backup" id="report_list_backup">&nbsp;</div>
<div class="report_summary_backup " id="report_summary_backup">&nbsp;</div>
{% endif %}
<input type="hidden" id="navigation" value="{{ basket.getId() }}">
<div id="report" class="summary_block">
<ul class="image_set">
{% for basket_element in basket.getElements() %}
<li class="image_box" id="sselcontid_{{basket_element.getId()}}">
<div class="display_id">
{{basket_element.getOrd()}}
</div>
{% if basket_element.getBasket().getValidation() and basket_element.getBasket().getValidation().getParticipant(app.getAuthenticatedUser()).getCanAgree() %}
<div class="valid_choice valid_choice_{{basket_element.getId()}} {% if basket_element.getUserValidationDatas(app.getAuthenticatedUser()).getAgreement() == true %}agree{% elseif basket_element.getUserValidationDatas(app.getAuthenticatedUser()).getAgreement() == false and basket_element.getUserValidationDatas(app.getAuthenticatedUser()).getAgreement() is not null %}disagree{% endif %}">
</div>
{% endif %}
<a href="{{ path('lightbox_ajax_load_basketelement', { 'sselcont_id' : basket_element.getId() }) }}">
{{thumbnail.format(basket_element.getRecord(app).get_thumbnail(), 80, 80, '', true, false)}}
{{thumbnail.format(basket_element.getRecord(app).get_thumbnail(), 300, 300, '', true, false)}}
</a>
<input type="hidden" class="display_id" name="display_id" value="{{basket_element.getOrd()}}" />
</li>
{% endfor %}
</ul>
</div>
<div data-role="footer">
</div>
<div class="btn-container">
{% if basket.getValidation() and basket.getValidation().getParticipant(app.getAuthenticatedUser()).getCanAgree() %}
<button class="confirm_report" style="width:100%;" title="{{ 'validation::envoyer mon rapport' | trans }}">
{{ 'validation::envoyer mon rapport' | trans }}
@@ -61,6 +95,8 @@
</button>
{% endif %}
</div>
</div>
</div>
{% endblock %}

View File

@@ -193,7 +193,15 @@
<td>
<label for="gui_editable" class="checkbox">
<input id="gui_editable" type="checkbox" <%= field.gui_editable ? "checked='checked'" : "" %> />
{% trans %}gui_editable{% endtrans %}
{% trans %}Gui-editable{% endtrans %}
</label>
</td>
</tr>
<tr>
<td>
<label for="gui_visible" class="checkbox">
<input id="gui_visible" type="checkbox" <%= field.gui_visible ? "checked='checked'" : "" %> />
{% trans %}Gui-visible{% endtrans %}
</label>
</td>
</tr>
@@ -256,7 +264,7 @@
<td><input id="tbranch" type="text" value="<%= field.tbranch %>"/></td>
</tr>
<tr>
<td><label for="generate_cterms" class="checkbox">{% trans %}generate_cterms{% endtrans %}</label></td>
<td><label for="generate_cterms" class="checkbox">{% trans %}Generate-cterms{% endtrans %}</label></td>
<td><input id="generate_cterms" type="checkbox" <%= field.generate_cterms ? "checked='checked'" : "" %> /></td>
</tr>
</table>

View File

@@ -1,8 +1,10 @@
{% macro caption(record, can_see_business, display_exif, limitedWidth = false) %}
<dl class="{% if limitedWidth %}{% else %}dl-horizontal{% endif %}">
{% for name, value in record.getCaption(caption_field_order(record, can_see_business)) %}
{% if caption_field_gui_visible(record, name) == 1 %}
<dt>{{ caption_field_label(record, name) }}</dt>
<dd>{{ caption_field(record, name, value)|e|highlight|linkify|parseColor }}</dd>
{% endif %}
{% endfor %}
</dl>
{% if display_exif|default(true) and app.getAuthenticator().user is not none and user_setting('technical_display') == 'group' %}

View File

@@ -302,8 +302,18 @@
$(document).ready(function () {
$('body').on('click', '#help-trigger', function (event) {
$('#mainMenu .helpcontextmenu').toggleClass('shown');
console.log('mety');
});
$(document).on('click', function (e) {
if($('#mainMenu .helpcontextmenu').hasClass('shown')) {
var $this = $(e.target);
if ($this.closest('#mainMenu .helpcontextmenu.shown, .help-trigger').length == 0 && $this[0].id != "toggle") {
$('#mainMenu .helpcontextmenu').removeClass('shown');
}
}
});
// var key = 'help';
var configurationSettingLinks = {{ configuration|json_encode|raw }};
//seperate array based on location of link

View File

@@ -4,48 +4,50 @@
<div style="margin:0 7px;overflow:hidden;">
{% for basket_element in basket.getElements() %}
{% set record = basket_element.getRecord(app) %}
<div class="chim-wrapper wrapCHIM_{{basket_element.getId()}} valid">
<div style="margin:20px;">
<table style="width: 100%; min-width: 330px;">
<tr valign="top">
<td style="width:165px;">
<div id="CHIM_{{basket_element.getId()}}_{{record.get_serialize_key()}}"
class="CHIM diapo">
<div class="chim-wrapper-block chim-wrapper wrapCHIM_{{basket_element.getId()}} valid">
<div class="chim-block">
<div class="chim-inner">
<div class="chim-left">
<div id="CHIM_{{basket_element.getId()}}_{{record.get_serialize_key()}}" class="CHIM diapo">
<div class="display_id">
{{basket_element.getOrd()}}
</div>
{{thumbnail.format(record.get_thumbnail(),165, 125, '', true, false)}}
</div>
</td>
<td style="width:10px;">
</td>
<td>
</div>
<div class="chim-right">
{% for validationDatas in basket_element.getValidationDatas()%}
<div class="basket_report_user_wrapper ui-corner-all">
<div class="basket_report_user">
{% if validationDatas.getAgreement() == true %}
{% set imguser = '<img src="/assets/lightbox/images/agree.png" />' %}
{% set imguser = '<span class="icomoon icon-agree">&#xe95f;</span>' %}
{% set styleuser = '' %}
{% elseif validationDatas.getAgreement() is null %}
{% set imguser = '' %}
{% set imguser = '&nbsp;' %}
{% set styleuser = 'margin-left:18px;' %}
{% else %}
{% set imguser = '<img src="/assets/lightbox/images/disagree.png" />' %}
{% set imguser = '<span class="icomoon icon-disagree">&#xe971;</span>' %}
{% set styleuser = '' %}
{% endif %}
<b style="{{styleuser}}">{{imguser|raw}} {{validationDatas.getParticipant().getUser().getDisplayName()}}</b>
<div class="validate-icon">
{% if basket_element.getBasket().getValidation().getParticipant(app.getAuthenticatedUser()).getCanSeeOthers() or validationDatas.getParticipant().getUser() == app.getAuthenticatedUser() %}
{{imguser|raw}}
{% endif %}
</div>
<div class="validate-info">
<h3> {{validationDatas.getParticipant().getUser().getDisplayName()}}</h3>
{% if validationDatas.getNote() != '' %}
: {{validationDatas.getNote()|nl2br}}
{% if basket_element.getBasket().getValidation().getParticipant(app.getAuthenticatedUser()).getCanSeeOthers() or validationDatas.getParticipant().getUser() == app.getAuthenticatedUser() %}
<p> {{validationDatas.getNote()|nl2br}}</p>
{% endif %}
{% endif %}
</div>
</div>
{% endfor %}
</td>
</tr>
</table>
</div>
</div>
<div><hr/></div>
</div>
</div>
{% endfor %}
</div>

View File

@@ -90,7 +90,7 @@
{% endif %}
{% for field in fields %}
{% set i = field.get_id() %}
{% if field.is_readonly() is empty %}
{% if field.is_readonly() is empty and field.get_gui_editable()== 1 %}
<div class="edit_field edit-field-action" id="EditFieldBox_{{i}}"
data-id="{{i}}"
data-name="{{field.get_name()}}">

View File

@@ -1,4 +1,5 @@
{% include 'prod/results/record.html.twig' with {
'record': record,
'settings': settings
'settings': settings,
'plugins': plugins
} %}

View File

@@ -7,7 +7,8 @@
<td valign="top" style='width:{{ settings.images_size + 50 }}px'>
{% include 'prod/results/record.html.twig' with {
'record': record,
'settings': settings
'settings': settings,
'plugins': plugins
} %}
</td>
<td valign="middle">

View File

@@ -155,6 +155,29 @@
</div>
{% endif %}
{% endif %}
{# add plugins entries #}
{% for plugin in plugins.actionbar|default([]) %}
{% set menu = plugin.ActionBar['_context_']|default([]) %}
{% for k, action in menu %}
{% if plugin.isContextMenuOptionAvailable(k, record) %}
<div title="" class="context-menu-item"
onclick="{{ plugin.getContextMenuJS(k) }}('{{ k }}', '{{record.baseId}}', '{{record.recordId}}', '{{ record.id }}');">
<div class="context-menu-item-inner">
{% set icon = (action.icon ?? false) ? plugin_asset(plugin.PluginName, action.icon) : null %}
{% set label = (action.label ?? false) ? (action.label|trans({}, plugin.PluginLocale)) : 'undefined label' %}
<a title="{{ label }}" href="#">
{% if icon %}
<img style="cursor:pointer;" src="{{ icon }}" alt="{{ label }}">
{% endif %}
<span>{{ label }}</span>
</a>
</div>
</div>
{% endif %}
{% endfor %}
{% endfor %}
</div>
</td>
</tr>

View File

@@ -31,7 +31,8 @@
'doctype_display': doctype_display,
'handle_dblclick' : true,
'show_context_menu': true
}
},
'plugins': plugins
} %}
{% endblock %}
{% endfor %}

View File

@@ -44,270 +44,159 @@
</div>
</span>
<span class="dropdownButton">
<div class="btn-group">
<button id="TOOL_disktt" class="default_action TOOL_disktt_btn results_window btn btn-inverse" data-selection-source="search-result">
<img src="/assets/common/images/icons/disktt_history.png" height="16" width="16" class="btn-image"/> {{ 'action : exporter' | trans }}
</button>
<button class="trigger btn btn-inverse dropdown-toggle" data-toggle="dropdown"><span class="caret"></span>
</button>
<ul class="dropdown-menu">
<li>
<a class="TOOL_print_btn results_window" data-selection-source="search-result">
<img src="/assets/common/images/icons/print_history.png" height="16" width="16" class="btn-image"/>
{{ 'action : print' | trans }}
</a>
</li>
</ul>
</div>
</span>
{% set menus = {
'export': [
{
'class':'TOOL_disktt_btn',
'icon':'/assets/common/images/icons/disktt_history.png',
'label':'action : exporter' | trans },
{
'class':'TOOL_print_btn',
'icon':'/assets/common/images/icons/print_history.png',
'label':'action : print' | trans }
],
'edit': [],
'push': [],
'tools': [],
'delete': []
} %}
{% set actions = {} %}
{# menu "edit" #}
{% set m = [] %}
{% if acl.has_right(constant('\\ACL::CANMODIFRECORD')) %}
{% set label %}
{{ 'action : editer' | trans }}
{% endset %}
{% set actions = actions|merge( { 'edit' : {'icon': "/assets/common/images/icons/ppen_history.png", 'class':'TOOL_ppen_btn', 'label' : label} }) %}
{% set label %}
{{ 'prod::toolbar : video editor' | trans }}
{% endset %}
{% set actions = actions|merge( { 'video' : {'icon': "/assets/common/images/icons/icon-video-editor.png", 'class':'TOOL_videoeditor_btn', 'label' : label} }) %}
{% set m = m|merge([
{
'class':'TOOL_ppen_btn',
'icon':'/assets/common/images/icons/ppen_history.png',
'label':'action : editer' | trans },
{
'class':'TOOL_videoeditor_btn',
'icon':'/assets/common/images/icons/icon-video-editor.png',
'label':'prod::toolbar : video editor' | trans }
]) %}
{% endif %}
{% if acl.has_right(constant('\\ACL::CHGSTATUS')) %}
{% set label %}
{{ 'action : status' | trans }}
{% endset %}
{% set actions = actions|merge( { 'status' : {'icon': "/assets/common/images/icons/chgstatus_history.png", 'class':'TOOL_chgstatus_btn', 'label' : label} }) %}
{% if acl.has_right(constant('\\\ACL::CHGSTATUS')) %}
{% set m = m|merge([
{
'class':'TOOL_chgstatus_btn',
'icon':'/assets/common/images/icons/chgstatus_history.png',
'label':'action : status' | trans }
]) %}
{% endif %}
{% if acl.has_right(constant('\\ACL::CANDELETERECORD')) and acl.has_right(constant('\\ACL::CANADDRECORD')) %}
{% set label %}
{{ 'action : collection' | trans }}
{% endset %}
{% set actions = actions|merge( { 'move' : {'icon': "/assets/common/images/icons/chgcoll_history.png", 'class':'TOOL_chgcoll_btn', 'label' : label} }) %}
{% set m = m|merge([
{
'class':'TOOL_chgcoll_btn',
'icon':'/assets/common/images/icons/chgcoll_history.png',
'label':'action : collection' | trans }
]) %}
{% endif %}
{% set menus = menus|merge({'edit' : m}) %}
{% set n_actions = actions|length %}
{% if n_actions > 1 %}
<span class="dropdownButton">
<div class="btn-group">
{% for action in actions %}
{% if loop.first %}
<button class="default_action {{ action.class }} results_window btn btn-inverse" data-selection-source="search-result">
<img src="{{ action.icon }}" height="16" width="16" class="btn-image"/> {{ action.label }}
</button>
{# menu "push" #}
{% set m = [] %}
{% if acl.has_right(constant('\\ACL::CANPUSH')) %}
{% set m = m|merge([
{
'class':'TOOL_pushdoc_btn',
'icon':'/assets/common/images/icons/push16.png',
'label':'action : push' | trans },
{
'class':'TOOL_feedback_btn',
'icon':'/assets/common/images/icons/feedback16.png',
'label':'Feedback' | trans }
]) %}
{% endif %}
{% endfor %}
<button class="trigger btn btn-inverse dropdown-toggle" data-toggle="dropdown"><span
class="caret"></span></button>
<ul class="dropdown-menu">
{% for action in actions %}
{% if not loop.first %}
<li>
<a class="{{ action.class }} results_window" data-selection-source="search-result">
<img src="{{ action.icon }}" height="16" width="16" class="btn-image"/>
{{ action.label }}
</a>
</li>
{% if not loop.last %}
<li class="divider"></li>
{% endif %}
{% endif %}
{% endfor %}
</ul>
</div>
</span>
{% elseif n_actions == 1 %}
<span class="classicButton">
<div class="btn-group">
{% for action in actions %}
<button class="{{ action.class }} results_window btn btn-inverse" data-selection-source="search-result">
<img src="{{ action.icon }}" height="16" width="16" class="btn-image"/> {{ action.label }}
</button>
{% endfor %}
</div>
</span>
{% endif %}
{% if acl.has_right(constant('\\ACL::CANPUSH')) and acl.has_right(constant('\\ACL::BAS_CHUPUB')) %}
<span class="dropdownButton">
<div class="btn-group">
<button class="TOOL_pushdoc_btn default_action results_window btn btn-inverse" data-selection-source="search-result">
<img src="/assets/common/images/icons/push16.png" height="16" width="16" class="btn-image"/> {{ 'action : push' | trans }}
</button>
<button class="trigger btn btn-inverse dropdown-toggle" data-toggle="dropdown"><span
class="caret"></span></button>
<ul class="dropdown-menu">
<li>
<a class="TOOL_feedback_btn results_window" data-selection-source="search-result">
<img src="/assets/common/images/icons/feedback16.png" height="16" width="16" class="btn-image"/>
{{ 'Feedback' | trans }}
</a>
</li>
{#<li class="divider"></li>#}
{#<li>#}
{#<a class="TOOL_bridge_btn results_window" href="{{ path("prod_bridge_manager") }}" data-selection-source="search-result">#}
{#<img src="/assets/common/images/icons/door.png" height="16" width="16" class="btn-image"/>#}
{#{{ 'action : bridge' | trans }}#}
{#</a>#}
{#</li>#}
<li class="divider"></li>
<li>
<a class="TOOL_publish_btn results_window" data-selection-source="search-result">
<img src="/assets/common/images/icons/rss16.png" height="16" width="16" class="btn-image"/>
{{ 'action : publier' | trans }}
</a>
</li>
{% if plugins.actionbar is not empty %}
{% for plugin in plugins.actionbar %}
<li class="divider"></li>
{% for key, action in plugin.getActionBar().push|default([]) %}
<li>
<a class="results_window {{ action.classes|default('') }}" data-selection-source="search-result">
{% if action.icon %}
<img src="{{ plugin_asset(plugin.PluginName, action.icon) }}" height="16" width="16" class="btn-image"/>
{% endif %}
{{ action.label|trans({}, plugin.PluginLocale) }}
</a>
</li>
{% endfor %}
{% endfor %}
{% endif %}
</ul>
</div>
</span>
{% elseif acl.has_right(constant('\\ACL::CANPUSH')) %}
<span class="dropdownButton">
<div class="btn-group">
<button class="TOOL_pushdoc_btn default_action results_window btn btn-inverse" data-selection-source="search-result">
<img src="/assets/common/images/icons/push16.png" height="16" width="16" class="btn-image"/> {{ 'action : push' | trans }}
</button>
<button class="trigger btn btn-inverse dropdown-toggle" data-toggle="dropdown"><span
class="caret"></span></button>
<ul class="submenu dropdown-menu">
<li>
<a class="TOOL_feedback_btn results_window" data-selection-source="search-result">
<img src="/assets/common/images/icons/feedback16.png" height="16" width="16" class="btn-image"/>
{{ 'Feedback' | trans }}
</a>
</li>
{% if plugins.actionbar is not empty %}
{% for plugin in plugins.actionbar %}
<li class="divider"></li>
{% for key, action in plugin.getActionBar().push|default([]) %}
<li>
<a class="results_window {{ action.classes|default('') }}" data-selection-source="search-result">
{% if action.icon %}
<img src="{{ plugin_asset(plugin.PluginName, action.icon) }}" height="16" width="16" class="btn-image"/>
{% endif %}
{{ action.label|trans({}, plugin.PluginLocale) }}
</a>
</li>
{% endfor %}
{% endfor %}
{% endif %}
</ul>
</div>
</span>
{% elseif acl.has_right(constant('\\ACL::BAS_CHUPUB')) %}
<span class="dropdownButton">
<div class="btn-group">
{#<button class="TOOL_pushdoc_btn default_action results_window btn btn-inverse" data-selection-source="search-result">#}
{#<img src="/assets/common/images/icons/door.png" height="16" width="16" class="btn-image"/> {{ 'action : bridge' | trans }}#}
{#</button>#}
<button class="TOOL_publish_btn results_window btn btn-inverse">
<img src="/assets/common/images/icons/rss16.png" height="16" width="16" class="btn-image"/>
{{ 'action : publier' | trans }}
</button>
<button class="trigger btn btn-inverse dropdown-toggle" data-toggle="dropdown"><span
class="caret"></span></button>
{% if plugins.actionbar is not empty %}
<button class="trigger btn btn-inverse dropdown-toggle" data-toggle="dropdown"><span
class="caret"></span></button>
<ul class="submenu dropdown-menu">
{% for plugin in plugins.actionbar %}
<li class="divider"></li>
{% for key, action in plugin.getActionBar().push|default([]) %}
<li>
<a class="results_window {{ action.classes|default('') }}">
{% if action.icon %}
<img src="{{ plugin_asset(plugin.PluginName, action.icon) }}"
height="16" width="16" class="btn-image"/>
{% endif %}
{{ action.label|trans({}, plugin.PluginLocale) }}
</a>
</li>
{% endfor %}
{% endfor %}
</ul>
{% endif %}
</div>
</span>
{% elseif plugins.actionbar is not empty %}
<span class="dropdownButton">
<div class="btn-group">
{% set firstButton = true %}
{% for plugin in plugins.actionbar %}
{% for key, action in plugin.getActionBar().push|default([]) %}
{% if firstButton %}
<button class="default_action results_window btn btn-inverse" data-selection-source="search-result">
<a class="results_window {{ action.classes|default('') }}" data-selection-source="search-result">
{% if action.icon %}
<img src="{{ plugin_asset(plugin.PluginName, action.icon) }}" height="16" width="16"/>
{% endif %}
{{ action.label|trans({}, plugin.PluginLocale) }}
</a>
</button>
{% if not (loop.last and loop.parent.loop.last) %}
<button class="trigger btn btn-inverse dropdown-toggle" data-toggle="dropdown"><span class="caret"></span></button>
<ul class="submenu dropdown-menu">
{% endif %}
{% if not loop.first %}<li class="divider"></li>{% endif %}
{% if not firstButton %}
<li>
<a class="results_window {{ action.classes|default('') }}" data-selection-source="search-result">
{% if action.icon %}
<img src="{{ plugin_asset(plugin.PluginName, action.icon) }}" height="16" width="16" class="btn-image"/>
{% endif %}
{{ action.label|trans({}, plugin.PluginLocale) }}
</a>
</li>
{% else %}
{% set firstButton = false %}
{% endif %}
{% endif %}
{% endfor %}
{% endfor %}
{% if not firstButton %}
</ul>
{% endif %}
</div>
</span>
{% if acl.has_right(constant('\\ACL::BAS_CHUPUB')) %}
{% set m = m|merge([
{
'class':'TOOL_publish_btn',
'icon':'/assets/common/images/icons/rss16.png',
'label':'action : publier' | trans }
]) %}
{% endif %}
{% set menus = menus|merge({'push' : m}) %}
{# menu "tools" #}
{% if acl.has_right(constant('\\ACL::IMGTOOLS')) %}
<span class="classicButton">
<div class="btn-group">
<button class="TOOL_imgtools_btn results_window btn btn-inverse" data-selection-source="search-result">
<img src="/assets/common/images/icons/imgtools_history.png" height="16" width="16" class="btn-image"/> {{ 'action : outils' | trans }}
</button>
</div>
</span>
{% set menus = menus|merge({'tools' : [
{
'class':'TOOL_imgtools_btn',
'icon':'/assets/common/images/icons/imgtools_history.png',
'label':'action : outils' | trans }
]}) %}
{% endif %}
{# menu "delete" #}
{% if acl.has_right(constant('\\ACL::CANDELETERECORD')) %}
<span class="classicButton">
{% set menus = menus|merge({'delete' : [
{
'class':'TOOL_trash_btn',
'icon':'/assets/common/images/icons/delete.png',
'label':'action : supprimer' | trans }
]}) %}
{% endif %}
{# add plugins entries #}
{% if plugins.actionbar is not empty %}
{% for plugin in plugins.actionbar %}
{% for k, menu in plugin.getActionBar() %}
{% if k != '_context_' %}
{% set m = menus[k]|default([]) %}
{% for action in menu %}
{% set m = m|merge([{
'class':action.classes|default(''),
'icon':plugin_asset(plugin.PluginName, action.icon),
'label':action.label|trans({}, plugin.PluginLocale) }])
%}
{% endfor %}
{% set menus = menus|merge({(k) : m}) %}
{% endif %}
{% endfor %}
{% endfor %}
{% endif %}
{# print #}
{% for menu_entries in menus %}
{% if menu_entries|length > 0 %}
<span class="dropdownButton">
<div class="btn-group">
<button class="TOOL_trash_btn results_window btn btn-inverse" data-selection-source="search-result">
<img src="/assets/common/images/icons/delete.png" height="16" width="16" class="btn-image"/> {{ 'action : supprimer' | trans }}
{% set menu_entry = menu_entries[0] %}
<button class="results_window btn btn-inverse {{ menu_entry.class }}" data-selection-source="search-result">
<img src="{{ menu_entry.icon }}" class="btn-image"/>
{{ menu_entry.label }}
</button>
{% set menu_entries = menu_entries|slice(1) %}
{% if menu_entries|length > 0 %}
<button class="trigger btn btn-inverse dropdown-toggle" data-toggle="dropdown"><span
class="caret"></span></button>
<ul class="dropdown-menu">
{% for menu_entry in menu_entries %}
<li>
<a class="results_window {{ menu_entry.class }}" data-selection-source="search-result">
<img src="{{ menu_entry.icon }}" class="btn-image"/>
{{ menu_entry.label }}
</a>
</li>
{% endfor %}
</ul>
{% endif %}
</div>
</span>
{% endif %}
{% endfor %}
{% endblock %}
{% block toolbarJS %}
{% if plugins.actionbar is not empty %}
{% for plugin in plugins.actionbar %}
{{ (plugin.JS|default(''))|raw }}
{% for pluginId, plugin in plugins.actionbar %}
{% if plugin.getActionBarTemplate is defined %}
{% set template = plugin.getActionBarTemplate() %}
{% if template %}
{% include template with {'app': app, 'plugin_id': pluginId, 'plugin': plugin} only %}
{% endif %}
{% endif %}
{% endfor %}
{% endif %}
{% endblock %}

View File

@@ -48,56 +48,56 @@
</div>
<script>
$('document').ready(function(){
$('document').ready(function () {
$(".btn-group.emptying").hide();
var scope = $('#lazaretBox');
$("#tab-lazaret").scrollTop(0);
function getLazaretId(el){
function getLazaretId(el) {
return el.closest('div.lazaret-file').find('input[name=lazaret_file]').val();
}
function getDestinationId(el){
function getDestinationId(el) {
return el.closest('div.lazaret-file').find('input[name=lazaret_file_destination]').val();
}
function startAjax(button){
function startAjax(button) {
button.closest(".btn-group").find('button').prop('disabled', true);
button.closest(".btn-group").addClass('loading');
}
function stopAjax(button){
function stopAjax(button) {
button.closest(".btn-group").find('button').prop('disabled', false);
button.closest(".btn-group").removeClass('loading');
}
function reloadContent(force){
function reloadContent(force) {
var nbItems = scope.find(".wrapper-item").length;
if( nbItems === 0 || force === true) {
if (nbItems === 0 || force === true) {
$.ajax({
url:'/prod/lazaret/',
beforeSend: function(){
$("#lazaretBox").empty().append(language.loading );
url: '/prod/lazaret/',
beforeSend: function () {
$("#lazaretBox").empty().append(language.loading);
},
success: function(data) {
success: function (data) {
scope.empty().append(data);
},
error : function() {
error: function () {
$("#lazaretBox").empty().append(language.errorAjaxRequest);
}
});
}
}
$(".records-subititution .diapo", scope)
/* $(".records-subititution .diapo", scope)
.bind('click', function(e){
$(this).closest('.lazaret-proposals').find('.diapo').removeClass('selected');
$(this).addClass('selected');
}
);
);*/
$(".records-subititution .captionTips", scope).tooltip();
$(".records-subititution .infoTips", scope).tooltip();
@@ -106,23 +106,23 @@
var emptying = false; // true=emptying, set to false to stop
// stop emptying lazaret
$('button.stopempty-lazaret', scope).bind('click', function() {
$('button.stopempty-lazaret', scope).bind('click', function () {
emptying = false;
});
// empty lazaret
$('button.empty-lazaret', scope).bind('click', function(){
$('button.empty-lazaret', scope).bind('click', function () {
var that = $(this);
if(!confirm("{{ "Empty quarantine will remove all items, are you sure you want to continue ?" | trans }}")) {
if (!confirm("{{ "Empty quarantine will remove all items, are you sure you want to continue ?" | trans }}")) {
return false;
}
$(".btn-group.empty").hide();
$(".btn-group.emptying").show();
var f = function() {
var f = function () {
var todo = 0;
var msg_html = "";
$.ajax({
@@ -135,7 +135,7 @@
success: function (data) {
if (data.success) {
todo = data.result.todo;
$("#QUARANTINE_TOOLBAR_EMPTYING_TODO").text(""+todo);
$("#QUARANTINE_TOOLBAR_EMPTYING_TODO").text("" + todo);
} else {
emptying = false; // force stop
msg_html = _.template($("#alert_error_tpl").html(), {
@@ -150,19 +150,19 @@
});
}
})
.done(function(data) {
if(emptying && todo > 0) {
.done(function (data) {
if (emptying && todo > 0) {
window.setTimeout(f, 500); // wait between loops
}
})
.fail(function() {
.fail(function () {
emptying = false; // force stop
})
.always(function(){
if(!emptying || todo <= 0) {
.always(function () {
if (!emptying || todo <= 0) {
$(".btn-group.emptying").hide();
$(".btn-group.empty").show();
if(msg_html != "") {
if (msg_html != "") {
$("#QUARANTINE_TOOLBAR_EMPTYING_MSG").html(msg_html);
}
else {
@@ -176,41 +176,46 @@
emptying = true;
f();
});
var data;
//add lazaret file click action
$("button.add-lazaret", scope).bind('click', function(){
$("button.add-lazaret", scope).bind('click', function () {
var that = $(this);
var lazaretId = getLazaretId(that);
var destinationCollectionId = getDestinationId(that);
var container = $(this).closest('.wrapper-item');
var form = $(this).closest("form");
/*fix POST on firefox*/
data = form.serializeArray();
var allData = that.parent().closest('.wrapper-item').find(".change-record-wrapper").html();
that.closest(".form-backup ").append(allData);
that.parent().closest('.wrapper-item').find(".change-record ").remove();
$.ajax({
type : 'POST',
url: '/prod/lazaret/'+lazaretId+'/force-add/',
type: 'POST',
url: '/prod/lazaret/' + lazaretId + '/force-add/',
dataType: 'json',
data : {
bas_id:destinationCollectionId,
keep_attributes: 1
},
beforeSend: function(){
data: data,
beforeSend: function () {
startAjax(that);
},
success: function(data){
if(data.success){
that.closest(".wrapper-item").remove();
}else{
success: function (data) {
if (data.success) {
container.remove();
} else {
var html = _.template($("#alert_error_tpl").html(), {
content:data.message
content: data.message
});
that.closest(".diapo").append(html);
}
},
error: function(){
error: function () {
var html = _.template($("#alert_error_tpl").html(), {
content:language.errorAjaxRequest
content: language.errorAjaxRequest
});
that.closest(".diapo").append(html);
},
complete: function(){
complete: function () {
stopAjax(that);
reloadContent();
}
@@ -218,42 +223,131 @@
});
//delete lazaret file click action
$("button.delete-lazaret", scope).bind('click', function(){
$("button.delete-lazaret", scope).bind('click', function () {
var that = $(this);
var lazaretId = getLazaretId(that);
var container = $(this).closest('.wrapper-item');
var form = $(this).closest("form");
data = form.serializeArray();
var allData = that.parent().closest('.wrapper-item').find(".change-record-wrapper").html();
that.closest(".form-backup ").append(allData);
that.parent().closest('.wrapper-item').find(".change-record ").remove();
$.ajax({
type : 'POST',
url: '/prod/lazaret/'+lazaretId+'/deny/',
type: 'POST',
url: '/prod/lazaret/' + lazaretId + '/deny/',
dataType: 'json',
beforeSend: function(){
beforeSend: function () {
startAjax(that);
},
success: function(data){
if(data.success){
that.closest(".wrapper-item").remove();
}else{
success: function (data) {
if (data.success) {
container.remove();
} else {
var html = _.template($("#alert_error_tpl").html(), {
content:data.message
content: data.message
});
that.closest(".diapo").append(html);
}
},
error: function(){
error: function () {
var html = _.template($("#alert_error_tpl").html(), {
content:language.errorAjaxRequest
content: language.errorAjaxRequest
});
that.closest(".diapo").append(html);
},
complete: function(){
complete: function () {
stopAjax(that);
reloadContent();
}
});
});
//update status list
function resetStatus(that) {
that.removeClass('selected');
var html = that.parent().closest('.wrapper-item').find(".status-backup").html();
that.parent().closest('.wrapper-item').find(".status-container").html('');
that.parent().closest('.wrapper-item').find(".status-container").append(html);
};
$(".span12 img, .reset-status").click(function () {
var that = $(this).closest('.wrapper-item').find('.lazaret-proposals .diapo');
resetStatus(that);
});
$(".records-subititution .diapo", scope).bind('click', function () {
var that = $(this);
var diapo = that.closest('.lazaret-proposals').find('.diapo');
var container = that.closest('.wrapper-item');
diapo.not(this).removeClass('selected');
/*Set selected or not to check for sending ajax request*/
if (that.hasClass("selected")) {
resetStatus(that);
} else {
that.addClass('selected');
var elements = $(".selected", container);
var recordId = elements.first().attr("data-record_id");
var sBas = elements.first().attr("sbas");
$.ajax({
type: 'GET',
url: '/prod/lazaret/' + sBas + '/' + recordId + '/status',
dataType: 'json',
beforeSend: function () {
startAjax(that);
},
success: function (data) {
if (data.status) {
html = '';
for ([key, value] of Object.entries(data.status)) {
if (value.flag == 1) {
checkValOff = '';
checkValOn = 'checked=checked';
} else {
checkValOff = 'checked=checked';
checkValOn = '';
}
var labelOff = value['labels_off_i18n']["{{ app['locale'] }}"];
var labelOn = value['labels_on_i18n']["{{ app['locale'] }}"];
if (labelOff == null || labelOff == "") {
labelOff = 'off';
}
if (labelOn == null || labelOn == "") {
labelOn = 'on';
}
html += '<tr>';
html += '<td class="status-tab-left">';
if (value['img_off'] != null) {
html += ' <img src="{{ value['img_off'] }}" width="16" height="16" />';
}
;
html += '<span>' + labelOff + '</span>';
html += '<input type="radio" name="status[' + sBas + '][' + value.bit + ']" value="0" ' + checkValOff + ' />';
html += '</td>';
html += '<td class="status-tab-right">';
html += '<input type="radio" name="status[' + sBas + '][' + value.bit + ']" value="1" ' + checkValOn + ' />';
html += '<span>' + labelOn + '</span>';
if (value['img_on'] != null) {
html += '<img src="' + value['img_on'] + '" width="16" height="16" />';
}
;
html += '</td>';
html += '</tr>';
}
;
that.parent().closest('.wrapper-item').find(".status-container").html('');
that.parent().closest('.wrapper-item').find(".status-container").append(html);
}
},
complete: function () {
stopAjax(that);
reloadContent();
}
});
}
});
//substitute lazaret file click action
$("button.subtitute-lazaret", scope).bind('click', function(){
$("button.subtitute-lazaret", scope).bind('click', function () {
var that = $(this);
var lazaretId = getLazaretId(that);
var container = $(this).closest('.wrapper-item');
@@ -262,70 +356,77 @@
var elements = [];
var nbElement = 0;
if(nbProposals >= 1){
if (nbProposals >= 1) {
elements = $(".selected", container);
nbElement = elements.length;
}else{
} else {
return false;
}
if(nbElement === 0){
if (nbElement === 0) {
alert(language.selectOneRecord);
return false;
}else if(nbElement > 1){
} else if (nbElement > 1) {
alert(language.onlyOneRecord);
return false;
}
var recordId = elements.first().attr("data-record_id");
var form = $(this).closest("form");
$(".record_id").val(recordId);
var form = $(this).closest("form");
data = form.serializeArray();
var allData = that.parent().closest('.wrapper-item').find(".change-record-wrapper").html();
that.closest(".form-backup ").append(allData);
that.parent().closest('.wrapper-item').find(".change-record ").remove();
$.ajax({
type : 'POST',
url: '/prod/lazaret/'+lazaretId+'/accept/',
type: 'POST',
url: '/prod/lazaret/' + lazaretId + '/accept/',
dataType: 'json',
data:{
record_id: recordId
},
beforeSend: function(){
data: data,
beforeSend: function () {
startAjax(that);
},
success: function(data){
if(data.success){
that.closest(".wrapper-item").remove();
}else{
success: function (data) {
if (data.success) {
container.remove();
} else {
var html = _.template($("#alert_error_tpl").html(), {
content:data.message
content: data.message
});
that.closest(".diapo").append(html);
}
},
error: function(){
error: function () {
var html = _.template($("#alert_error_tpl").html(), {
content:language.errorAjaxRequest
content: language.errorAjaxRequest
});
that.closest(".diapo").append(html);
},
complete: function(){
complete: function () {
stopAjax(that);
reloadContent();
}
});
});
/*Toggle status block*/
$(".toggle-status").click(function () {
$(this).nextAll('.status-wrapper').first().toggleClass('hidden');
});
});
</script>
<style>
.lazaret-proposals .diapo {
float:none;
}
</style>
{% macro lazaretElement(app, file) %}
{% import "common/thumbnail.html.twig" as thumb %}
{% set records = file.getRecordsToSubstitute(app, true) %}
<div class="lazaret-file span4">
<h5>{{ "Last uploaded version" | trans }}</h5>
<ul class="thumbnails">
<li class="span12" >
<li class="span12">
<div class="thumbnail">
<img src="/prod/lazaret/{{ file.getId() }}/thumbnail/"/>
<div class="caption">
@@ -344,6 +445,66 @@
<p>{{ border_checker_from_fqcn(check.getCheckClassname()).getMessage(app['translator']) }}</p>
{% endfor %}
</div>
<form class="change-record" name="change-records-status" action="/" method="POST">
<div class="change-record-wrapper">
{% set collection = file.getCollection(app) %}
<input type="hidden" name="bas_id" value="{{ collection.get_base_id() }}">
<input type="hidden" name="keep_attributes" value="1">
<input class="record_id" type="hidden" name="record_id">
<div class="update-status">
<div id="status-{{ collection.get_base_id() }}" class='collection-status'>
<a href="#" class="reset-status btn">Reset status</a>
<h5 class="toggle-status">{{ 'upload:: Status :' | trans }} <img src="/assets/common/images/icons/icon-right-arrow.svg" width="10" height="10" class="btn-status"></h5>
<div class="status-wrapper hidden">
<table style="margin: auto">
<tbody class="status-container">
{% if file.getStatus(app) is not null %}
{% for bit, status in file.getStatus(app) %}
<tr>
<td class="status-tab-left">
{% if status['img_off'] is not empty %}
<img src="{{ status['img_off'] }}" width="16" height="16" />
{% endif %}
<span>{{ status['labels_off_i18n'][app['locale']]|default('off') }}</span>
<input type="radio" name="status[{{ collection.get_sbas_id() }}][{{ bit }}]" value="0" {% if status['flag'] == 0 %}checked="checked"{% endif%}/>
</td>
<td class="status-tab-right">
<input type="radio" name="status[{{ collection.get_sbas_id() }}][{{ bit }}]" value="1" {% if status['flag'] == 1 %}checked="checked"{% endif%} />
<span for="labelon">{{ status['labels_on_i18n'][app['locale']]|default('on') }}</span>
{% if status['img_on'] is not empty %}
<img src="{{ status['img_on'] }}" width="16" height="16" />
{% endif %}
</td>
</tr>
{% endfor %}
{% else %}
{% for bit, status in collection.get_databox().getStatusStructure() %}
<tr>
<td class="status-tab-left">
{% if status['img_off'] is not empty %}
<img src="{{ status['img_off'] }}" width="16" height="16" />
{% endif %}
<span>{{ status['labels_off_i18n'][app['locale']]|default('off') }}</span>
<input type="radio" name="status[{{ collection.get_sbas_id() }}][{{ bit }}]" value="0" checked="checked"/>
</td>
<td class="status-tab-right">
<input type="radio" name="status[{{ collection.get_sbas_id() }}][{{ bit }}]" value="1" />
<span for="labelon">{{ status['labels_on_i18n'][app['locale']]|default('on') }}</span>
{% if status['img_on'] is not empty %}
<img src="{{ status['img_on'] }}" width="16" height="16" />
{% endif %}
</td>
</tr>
{% endfor %}
{% endif %}
</tbody>
</table>
</div>
</div>
</div>
<div class="btn-group" style="text-align:center; padding:5px;">
<button class="btn add-lazaret" title="{{ "Add" | trans }}">
<img src="/assets/common/images/icons/add.png" width="16" height="16" class="btn-image">{{ "Add" | trans }}
@@ -358,6 +519,55 @@
{% endif %}
</div>
</div>
</form>
<div class="hidden form-backup"></div>
{# bloc to backup initial value of status list#}
<table class="hidden">
<tbody class="status-backup">
{% if file.getStatus(app) is not null %}
{% for bit, status in file.getStatus(app) %}
<tr>
<td class="status-tab-left">
{% if status['img_off'] is not empty %}
<img src="{{ status['img_off'] }}" width="16" height="16"/>
{% endif %}
<span>{{ status['labels_off_i18n'][app['locale']]|default('off') }}</span>
<input type="radio" name="status[{{ collection.get_sbas_id() }}][{{ bit }}]" value="0" {% if status['flag'] == 0 %}checked="checked"{% endif%}/>
</td>
<td class="status-tab-right">
<input type="radio" name="status[{{ collection.get_sbas_id() }}][{{ bit }}]" value="1" {% if status['flag'] == 1 %}checked="checked"{% endif%} />
<span for="labelon">{{ status['labels_on_i18n'][app['locale']]|default('on') }}</span>
{% if status['img_on'] is not empty %}
<img src="{{ status['img_on'] }}" width="16" height="16"/>
{% endif %}
</td>
</tr>
{% endfor %}
{% else %}
{% for bit, status in collection.get_databox().getStatusStructure() %}
<tr>
<td class="status-tab-left">
{% if status['img_off'] is not empty %}
<img src="{{ status['img_off'] }}" width="16" height="16" />
{% endif %}
<span>{{ status['labels_off_i18n'][app['locale']]|default('off') }}</span>
<input type="radio" name="status[{{ collection.get_sbas_id() }}][{{ bit }}]" value="0" checked="checked"/>
</td>
<td class="status-tab-right">
<input type="radio" name="status[{{ collection.get_sbas_id() }}][{{ bit }}]" value="1" />
<span for="labelon">{{ status['labels_on_i18n'][app['locale']]|default('on') }}</span>
{% if status['img_on'] is not empty %}
<img src="{{ status['img_on'] }}" width="16" height="16" />
{% endif %}
</td>
</tr>
{% endfor %}
{% endif %}
</tbody>
</table>
</div>
</li>
</ul>
{# Store lazaret file id in hidden input #}
@@ -406,3 +616,30 @@
{% endif %}
{% endmacro %}
<style>
.lazaret-proposals .diapo {
float:none;
}
.collection-status {
margin: 0 11px 14px;
background: #f5f5f5;
padding-bottom: 10px;
color: #151515;
}
.collection-status h5 {
padding: 10px 10px 0 10px;
cursor: pointer;
margin-bottom: 0;
}
.reset-status {
display: inline-block;
float: right;
background: #fff;
border: 1px solid;
padding: 2px 5px;
margin-top: -4px;
margin-right: -6px;
}
</style>

View File

@@ -230,6 +230,7 @@ class FieldsTest extends \PhraseanetAuthenticatedWebTestCase
'vocabulary-type' => 'User',
'vocabulary-restricted' => true,
'gui_editable' => true,
'gui_visible' => true,
'generate_cterms' => true,
]);

View File

@@ -48,6 +48,7 @@ class TextNodeTest extends \PHPUnit_Framework_TestCase
$query_context->getUnrestrictedFields()->willReturn([$field]);
$query_context->getPrivateFields()->willReturn([]);
$query_context->localizeField($field)->willReturn(['foo.fr', 'foo.en']);
$query_context->truncationField($field)->willReturn([]);
$node = new TextNode('bar', new Context('baz'));
$query = $node->buildQuery($query_context->reveal());
@@ -80,12 +81,18 @@ class TextNodeTest extends \PHPUnit_Framework_TestCase
$query_context
->localizeField($public_field)
->willReturn(['foo.fr', 'foo.en']);
$query_context
->truncationField($public_field)
->willReturn([]);
$query_context
->getPrivateFields()
->willReturn([$private_field]);
$query_context
->localizeField($private_field)
->willReturn(['private_caption.bar.fr', 'private_caption.bar.en']);
$query_context
->truncationField($private_field)
->willReturn([]);
$node = new TextNode('baz');
$query = $node->buildQuery($query_context->reveal());
@@ -136,6 +143,7 @@ class TextNodeTest extends \PHPUnit_Framework_TestCase
$query_context->getUnrestrictedFields()->willReturn([$field]);
$query_context->getPrivateFields()->willReturn([]);
$query_context->localizeField($field)->willReturn(['foo.fr', 'foo.en']);
$query_context->truncationField($field)->willReturn([]);
$node = new TextNode('bar');
$node->setConcepts([
@@ -180,12 +188,18 @@ class TextNodeTest extends \PHPUnit_Framework_TestCase
$query_context
->localizeField($public_field)
->willReturn(['foo.fr', 'foo.en']);
$query_context
->truncationField($public_field)
->willReturn([]);
$query_context
->getPrivateFields()
->willReturn([$private_field]);
$query_context
->localizeField($private_field)
->willReturn(['private_caption.bar.fr', 'private_caption.bar.en']);
$query_context
->truncationField($private_field)
->willReturn([]);
$node = new TextNode('baz');
$node->setConcepts([

View File

@@ -18,7 +18,7 @@ class QueryContextTest extends \PHPUnit_Framework_TestCase
{
$structure = $this->prophesize(Structure::class)->reveal();
$available_locales = ['ab', 'cd', 'ef'];
$context = new QueryContext($structure, $available_locales, 'fr');
$context = new QueryContext(null, $structure, $available_locales, 'fr');
$narrowed = $context->narrowToFields(['some_field']);
$this->assertEquals(['some_field'], $narrowed->getFields());
}
@@ -33,10 +33,10 @@ class QueryContextTest extends \PHPUnit_Framework_TestCase
'bar' => $bar_field
]);
$context = new QueryContext($structure->reveal(), [], 'fr');
$context = new QueryContext(null, $structure->reveal(), [], 'fr');
$this->assertEquals([$foo_field, $bar_field], $context->getUnrestrictedFields());
$narrowed_context = new QueryContext($structure->reveal(), [], 'fr', ['foo']);
$narrowed_context = new QueryContext(null, $structure->reveal(), [], 'fr', ['foo']);
$this->assertEquals([$foo_field], $narrowed_context->getUnrestrictedFields());
}
@@ -50,10 +50,10 @@ class QueryContextTest extends \PHPUnit_Framework_TestCase
'bar' => $bar_field
]);
$context = new QueryContext($structure->reveal(), [], 'fr');
$context = new QueryContext(null, $structure->reveal(), [], 'fr');
$this->assertEquals([$foo_field, $bar_field], $context->getPrivateFields());
$narrowed_context = new QueryContext($structure->reveal(), [], 'fr', ['foo']);
$narrowed_context = new QueryContext(null, $structure->reveal(), [], 'fr', ['foo']);
$this->assertEquals([$foo_field], $narrowed_context->getPrivateFields());
}
}

View File

@@ -36,6 +36,7 @@ class StructureTest extends \PHPUnit_Framework_TestCase
$field->isFacet()->willReturn(false);
$field->hasConceptInference()->willReturn(false);
$field->getDependantCollections()->willReturn(['1']);
$field->get_databox_id()->willReturn('1');
$structure->add($field->reveal());
$this->assertCount(1, $structure->getAllFields());
@@ -60,6 +61,7 @@ class StructureTest extends \PHPUnit_Framework_TestCase
$field->isPrivate()->willReturn(false);
$field->isFacet()->willReturn(false);
$field->hasConceptInference()->willReturn(false);
$field->get_databox_id()->willReturn('1');
$other = new Field('foo', FieldMapping::TYPE_STRING);

View File

@@ -14,14 +14,14 @@ use Alchemy\Phrasea\SearchEngine\Elastic\Structure\Typed;
class ValueCheckerTest extends \PHPUnit_Framework_TestCase
{
/**
* @dataProvider escapeRawProvider
* @dataProvider valueCheckerProvider
*/
public function testValueCompatibility($subject, $value, $compatible)
{
$this->assertEquals($compatible, ValueChecker::isValueCompatible($subject, $value));
}
public function escapeRawProvider()
public function valueCheckerProvider()
{
$values = [
[FieldMapping::TYPE_FLOAT , 42 , true ],

View File

@@ -28,6 +28,7 @@ define([
"tbranch": "",
"generate_cterms": false,
"gui_editable": true,
"gui_visible": true,
"separator": "",
"required": false,
"report": true,

View File

@@ -52,6 +52,10 @@ define([
this.field.get('gui_editable').should.equal("1");
});
it("should default gui_visible property to '1'", function () {
this.field.get('gui_visible').should.equal("1");
});
it("should default separator property to 'empty'", function () {
this.field.get('separator').should.equal("");
});

View File

@@ -3815,9 +3815,9 @@ fsevents@^1.0.0, fsevents@^1.1.2, fsevents@^1.2.7:
node-pre-gyp "^0.12.0"
fstream@^1.0.0, fstream@^1.0.2:
version "1.0.11"
resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.11.tgz#5c1fb1f117477114f0632a0eb4b71b3cb0fd3171"
integrity sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=
version "1.0.12"
resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.12.tgz#4e8ba8ee2d48be4f7d0de505455548eae5932045"
integrity sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==
dependencies:
graceful-fs "^4.1.2"
inherits "~2.0.0"
@@ -4049,7 +4049,7 @@ glob@^5.0.15:
once "^1.3.0"
path-is-absolute "^1.0.0"
glob@^7.0.0, glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@~7.1.1:
glob@^7.0.0, glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@~7.1.1:
version "7.1.4"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.4.tgz#aa608a2f6c577ad357e1ae5a5c26d9a8d1969255"
integrity sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==
@@ -4061,6 +4061,18 @@ glob@^7.0.0, glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@~7.1.1:
once "^1.3.0"
path-is-absolute "^1.0.0"
glob@^7.1.3:
version "7.1.6"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6"
integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==
dependencies:
fs.realpath "^1.0.0"
inflight "^1.0.4"
inherits "2"
minimatch "^3.0.4"
once "^1.3.0"
path-is-absolute "^1.0.0"
glob@~3.1.21:
version "3.1.21"
resolved "https://registry.yarnpkg.com/glob/-/glob-3.1.21.tgz#d29e0a055dea5138f4d07ed40e8982e83c2066cd"
@@ -4166,10 +4178,10 @@ graceful-fs@^3.0.0:
dependencies:
natives "^1.1.0"
graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@~4.1.4:
version "4.1.15"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.15.tgz#ffb703e1066e8a0eeaa4c8b80ba9253eeefbfb00"
integrity sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==
graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9:
version "4.2.3"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.3.tgz#4a12ff1b60376ef09862c2093edd908328be8423"
integrity sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==
graceful-fs@~1.2.0:
version "1.2.3"
@@ -4181,6 +4193,11 @@ graceful-fs@~2.0.0:
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-2.0.3.tgz#7cd2cdb228a4a3f36e95efa6cc142de7d1a136d0"
integrity sha1-fNLNsiiko/Nule+mzBQt59GhNtA=
graceful-fs@~4.1.4:
version "4.1.15"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.15.tgz#ffb703e1066e8a0eeaa4c8b80ba9253eeefbfb00"
integrity sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==
"graceful-readlink@>= 1.0.0":
version "1.0.1"
resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725"
@@ -4757,7 +4774,12 @@ inherits@1:
resolved "https://registry.yarnpkg.com/inherits/-/inherits-1.0.2.tgz#ca4309dadee6b54cc0b8d247e8d7c7a0975bdc9b"
integrity sha1-ykMJ2t7mtUzAuNJH6NfHoJdb3Js=
inherits@2, inherits@2.0.3, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3:
inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3:
version "2.0.4"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
inherits@2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=
@@ -5974,9 +5996,9 @@ lodash.keys@~2.4.1:
lodash.isobject "~2.4.1"
lodash.merge@^4.6.0:
version "4.6.1"
resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.1.tgz#adc25d9cb99b9391c59624f379fbba60d7111d54"
integrity sha512-AOYza4+Hf5z1/0Hztxpm2/xiPZgi/cjMqdnKTUWTBSKchJlxXXuUSxCCl8rJlf4g6yww/j6mA8nC8Hw/EZWxKQ==
version "4.6.2"
resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
lodash.restparam@^3.0.0:
version "3.6.1"
@@ -6483,9 +6505,9 @@ mitt@^1.1.3:
integrity sha512-mUDCnVNsAi+eD6qA0HkRkwYczbLHJ49z17BGe2PYRhZL4wpZUFZGJHU7/5tmvohoma+Hdn0Vh/oJTiPEmgSruA==
mixin-deep@^1.2.0:
version "1.3.1"
resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.1.tgz#a49e7268dce1a0d9698e45326c5626df3543d0fe"
integrity sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==
version "1.3.2"
resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566"
integrity sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==
dependencies:
for-in "^1.0.2"
is-extendable "^1.0.1"
@@ -7555,10 +7577,10 @@ phraseanet-common@^0.4.1:
js-cookie "^2.1.0"
pym.js "^1.3.1"
phraseanet-production-client@0.34.72-d:
version "0.34.72-d"
resolved "https://registry.yarnpkg.com/phraseanet-production-client/-/phraseanet-production-client-0.34.72-d.tgz#028a5ccd589e696b5433eea9d53d9367966613c8"
integrity sha512-IPaDRqXwyJegoKmzr56bggxTzN4TnmuAqU4O7rDEhh0aqdCiuC8rlH/yzKoLeEIMSrESCw5mBhrI//ccntvv9w==
phraseanet-production-client@0.34.80-d:
version "0.34.80-d"
resolved "https://registry.yarnpkg.com/phraseanet-production-client/-/phraseanet-production-client-0.34.80-d.tgz#1e54bee4306ab11528377cb63c19d8c7491ef0f3"
integrity sha512-ilGs7ndDNztwlyeW9MA2TMiMhZC+P1/lNNeIsIuh+KuJH8M3Y3SWBcmsN2lqu5iM1Xg5FbWJ6iXcCSrLWHTqsw==
dependencies:
"@mapbox/mapbox-gl-language" "^0.9.2"
"@turf/turf" "^5.1.6"
@@ -8296,9 +8318,9 @@ right-align@^0.1.1:
align-text "^0.1.1"
rimraf@2, rimraf@^2.2.8, rimraf@^2.5.0, rimraf@^2.5.2, rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.2, rimraf@^2.6.3:
version "2.6.3"
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab"
integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==
version "2.7.1"
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec"
integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==
dependencies:
glob "^7.1.3"