Merge branch 'master' of https://github.com/alchemy-fr/Phraseanet into PHRAS-2741-worker-service-part1

This commit is contained in:
aynsix
2019-12-19 16:09:47 +04:00
135 changed files with 5869 additions and 2251 deletions

View File

@@ -107,6 +107,53 @@ jobs:
- store_artifacts: - store_artifacts:
path: /tmp/circleci-test-results path: /tmp/circleci-test-results
build_phraseanet-fpm:
machine:
image: ubuntu-1604:201903-01
docker_layer_caching: true
working_directory: ~/alchemy-fr/Phraseanet
steps:
- checkout
- aws-ecr/ecr-login:
region: AWS_DEFAULT_REGION
- run: docker build --target phraseanet-fpm -t ${AWS_ACCOUNT_URL}/phraseanet-fpm:${CIRCLE_BRANCH} .
- aws-ecr/push-image:
account-url: AWS_ACCOUNT_URL
repo: "phraseanet-fpm"
tag: "${CIRCLE_BRANCH}"
build_phraseanet-worker:
machine:
image: ubuntu-1604:201903-01
docker_layer_caching: true
working_directory: ~/alchemy-fr/Phraseanet
steps:
- checkout
- aws-ecr/ecr-login:
region: AWS_DEFAULT_REGION
- run: docker build --target phraseanet-worker -t ${AWS_ACCOUNT_URL}/phraseanet-worker:${CIRCLE_BRANCH} .
- aws-ecr/push-image:
account-url: AWS_ACCOUNT_URL
repo: "phraseanet-worker"
tag: "${CIRCLE_BRANCH}"
build_phraseanet-nginx:
machine:
image: ubuntu-1604:201903-01
docker_layer_caching: true
working_directory: ~/alchemy-fr/Phraseanet
steps:
- checkout
- aws-ecr/ecr-login:
region: AWS_DEFAULT_REGION
- run: docker build --target phraseanet-nginx -t ${AWS_ACCOUNT_URL}/phraseanet-nginx:${CIRCLE_BRANCH} .
- aws-ecr/push-image:
account-url: AWS_ACCOUNT_URL
repo: "phraseanet-nginx"
tag: "${CIRCLE_BRANCH}"
workflows: workflows:
version: 2 version: 2
oldfashion: oldfashion:
@@ -114,36 +161,9 @@ workflows:
- build - build
newfashion: newfashion:
jobs: jobs:
- aws-ecr/build_and_push_image: - build_phraseanet-fpm:
account-url: AWS_ACCOUNT_URL
aws-access-key-id: AWS_ACCESS_KEY_ID
aws-secret-access-key: AWS_SECRET_ACCESS_KEY
context: "AWS London" context: "AWS London"
create-repo: true - build_phraseanet-worker:
dockerfile: Dockerfile
extra-build-args: "--target phraseanet-fpm"
region: AWS_DEFAULT_REGION
repo: "${AWS_RESOURCE_NAME_PREFIX}/phraseanet"
tag: "alpha-0.1"
- aws-ecr/build_and_push_image:
account-url: AWS_ACCOUNT_URL
aws-access-key-id: AWS_ACCESS_KEY_ID
aws-secret-access-key: AWS_SECRET_ACCESS_KEY
context: "AWS London" context: "AWS London"
create-repo: true - build_phraseanet-nginx:
dockerfile: Dockerfile
extra-build-args: "--target phraseanet-nginx"
region: AWS_DEFAULT_REGION
repo: "${AWS_RESOURCE_NAME_PREFIX}/phraseanet-nginx"
tag: "alpha-0.1"
- aws-ecr/build_and_push_image:
account-url: AWS_ACCOUNT_URL
aws-access-key-id: AWS_ACCESS_KEY_ID
aws-secret-access-key: AWS_SECRET_ACCESS_KEY
context: "AWS London" context: "AWS London"
create-repo: true
dockerfile: Dockerfile
extra-build-args: "--target phraseanet-worker"
region: AWS_DEFAULT_REGION
repo: "${AWS_RESOURCE_NAME_PREFIX}/phraseanet"
tag: "alpha-0.1"

12
.dockerignore Normal file
View File

@@ -0,0 +1,12 @@
.circleci
.git
.settings
nodes_modules
vendor

View File

@@ -1,5 +1,10 @@
FROM php:7.0-fpm-stretch as builder
#########################################################################
# This image contains every build tools that will be used by the builder and
# the app images (usefull in dev mode)
#########################################################################
FROM php:7.0-fpm-stretch as phraseanet-system
RUN apt-get update \ RUN apt-get update \
&& apt-get install -y \ && apt-get install -y \
apt-transport-https \ apt-transport-https \
@@ -23,6 +28,7 @@ RUN apt-get update \
libxslt-dev \ libxslt-dev \
libzmq3-dev \ libzmq3-dev \
locales \ locales \
gettext \
mcrypt \ mcrypt \
swftools \ swftools \
unoconv \ unoconv \
@@ -40,7 +46,7 @@ RUN apt-get update \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*
RUN php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" \ 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 -r "if (hash_file('sha384', 'composer-setup.php') === 'baf1608c33254d00611ac1705c1d9958c817a1a33bce370c0595974b342601bd80b92a3f46067da89e3b06bff421f182') { 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 composer-setup.php --install-dir=/usr/local/bin --filename=composer \
&& php -r "unlink('composer-setup.php');" && php -r "unlink('composer-setup.php');"
@@ -61,9 +67,19 @@ RUN mkdir /entrypoint /var/alchemy \
&& mkdir -p /home/app/.composer \ && mkdir -p /home/app/.composer \
&& chown -R app: /home/app /var/alchemy && chown -R app: /home/app /var/alchemy
#########################################################################
# This image is used to build the apps
#########################################################################
FROM phraseanet-system as builder
WORKDIR /var/alchemy/ WORKDIR /var/alchemy/
# Files that are needed at build stage
COPY gulpfile.js /var/alchemy/ COPY gulpfile.js /var/alchemy/
COPY www/include /var/alchemy/www/include
COPY www/scripts/apps /var/alchemy/www/scripts/apps
COPY Makefile /var/alchemy/ COPY Makefile /var/alchemy/
COPY package.json /var/alchemy/ COPY package.json /var/alchemy/
COPY phpunit.xml.dist /var/alchemy/ COPY phpunit.xml.dist /var/alchemy/
@@ -73,11 +89,16 @@ COPY composer.json /var/alchemy/
COPY composer.lock /var/alchemy/ COPY composer.lock /var/alchemy/
RUN make install_composer RUN make install_composer
COPY resources /var/alchemy/resources COPY resources /var/alchemy/resources
COPY www /var/alchemy/www
# Application build phase
RUN make clean_assets RUN make clean_assets
RUN make install_asset_dependencies RUN make install_asset_dependencies
RUN make install_assets RUN make install_assets
# Application code
COPY www /var/alchemy/www
ADD ./docker/phraseanet/ / ADD ./docker/phraseanet/ /
COPY lib /var/alchemy/lib COPY lib /var/alchemy/lib
COPY tmp /var/alchemy/tmp COPY tmp /var/alchemy/tmp
@@ -87,55 +108,8 @@ COPY templates-profiler /var/alchemy/templates-profiler
COPY templates /var/alchemy/templates COPY templates /var/alchemy/templates
COPY tests /var/alchemy/tests COPY tests /var/alchemy/tests
# Phraseanet # Create needed folders
FROM php:7.0-fpm-stretch as phraseanet-fpm
RUN apt-get update \
&& apt-get install -y \
apt-transport-https \
ca-certificates \
gnupg2 \
&& apt-get update \
&& apt-get install -y --no-install-recommends zlib1g-dev \
gettext \
git \
ghostscript \
gpac \
imagemagick \
libav-tools \
libfreetype6-dev \
libicu-dev \
libjpeg62-turbo-dev \
libmagickwand-dev \
libmcrypt-dev \
libpng-dev \
librabbitmq-dev \
libssl-dev \
libxslt-dev \
libzmq3-dev \
locales \
mcrypt \
swftools \
unoconv \
unzip \
xpdf \
&& update-locale "LANG=fr_FR.UTF-8 UTF-8" \
&& dpkg-reconfigure --frontend noninteractive locales \
&& docker-php-ext-configure gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ \
&& docker-php-ext-install -j$(nproc) gd \
&& docker-php-ext-install zip exif iconv mbstring pcntl sockets xsl intl pdo_mysql gettext bcmath mcrypt \
&& pecl install redis amqp-1.9.3 zmq-beta imagick-beta \
&& docker-php-ext-enable redis amqp zmq imagick \
&& pecl clear-cache \
&& docker-php-source delete \
&& rm -rf /var/lib/apt/lists/*
RUN mkdir /entrypoint /var/alchemy \
&& useradd -u 1000 app \
&& mkdir -p /home/app/.composer \
&& chown -R app: /home/app /var/alchemy
COPY --from=builder --chown=app /var/alchemy /var/alchemy/Phraseanet
ADD ./docker/phraseanet/ /
RUN mkdir -p /var/alchemy/Phraseanet/logs \ RUN mkdir -p /var/alchemy/Phraseanet/logs \
&& chmod -R 777 /var/alchemy/Phraseanet/logs \ && chmod -R 777 /var/alchemy/Phraseanet/logs \
&& mkdir -p /var/alchemy/Phraseanet/cache \ && mkdir -p /var/alchemy/Phraseanet/cache \
@@ -148,15 +122,30 @@ RUN mkdir -p /var/alchemy/Phraseanet/logs \
&& chmod -R 777 /var/alchemy/Phraseanet/www/custom \ && chmod -R 777 /var/alchemy/Phraseanet/www/custom \
&& mkdir -p /var/alchemy/Phraseanet/config \ && mkdir -p /var/alchemy/Phraseanet/config \
&& chmod -R 777 /var/alchemy/Phraseanet/config && chmod -R 777 /var/alchemy/Phraseanet/config
#########################################################################
# Phraseanet web application image
#########################################################################
FROM phraseanet-system as phraseanet-fpm
COPY --from=builder --chown=app /var/alchemy /var/alchemy/Phraseanet
ADD ./docker/phraseanet/ /
WORKDIR /var/alchemy/Phraseanet WORKDIR /var/alchemy/Phraseanet
ENTRYPOINT ["/phraseanet-entrypoint.sh"] ENTRYPOINT ["/phraseanet-entrypoint.sh"]
CMD ["/boot.sh"] CMD ["/boot.sh"]
# phraseanet-worker #########################################################################
# Phraseanet worker application image
#########################################################################
FROM phraseanet-fpm as phraseanet-worker FROM phraseanet-fpm as phraseanet-worker
CMD ["/worker-boot.sh"] CMD ["/worker-boot.sh"]
#########################################################################
# phraseanet-nginx # phraseanet-nginx
#########################################################################
FROM nginx:1.15 as phraseanet-nginx FROM nginx:1.15 as phraseanet-nginx
RUN useradd -u 1000 app RUN useradd -u 1000 app
ADD ./docker/nginx/ / ADD ./docker/nginx/ /

View File

@@ -1,18 +0,0 @@
ARG phraseanet
FROM $phraseanet
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
iproute2 \
&& rm -rf /var/lib/apt/lists/* \
&& pecl install xdebug \
&& docker-php-ext-enable xdebug \
&& pecl clear-cache
ADD ./docker/phraseanet-debug/ /
RUN chmod +x /entrypoint.sh /usr/local/bin/docker-*
ENTRYPOINT ["/entrypoint.sh"]
CMD ["php-fpm"]

View File

@@ -63,14 +63,16 @@ The docker distribution come with 3 differents containers :
## How to build ## 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 ./build.sh <TAG>
docker build --target phraseanet-nginx -t local/phraseanet-nginx .
# php-fpm application It will build and tag the following images :
docker build --target phraseanet-fpm -t local/phraseanet-fpm .
# worker local/phraseanet-worker:<TAG>
docker build --target phraseanet-worker -t local/phraseanet-worker . 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.

13
build.sh Executable file
View File

@@ -0,0 +1,13 @@
#!/bin/bash
set -xe
# 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

@@ -84,7 +84,7 @@
"league/flysystem": "^1.0", "league/flysystem": "^1.0",
"league/flysystem-aws-s3-v2": "^1.0", "league/flysystem-aws-s3-v2": "^1.0",
"league/fractal": "dev-webgalleries#af1acc0275438571bc8c1d08a05a4b5af92c9f97 as 0.13.0", "league/fractal": "dev-webgalleries#af1acc0275438571bc8c1d08a05a4b5af92c9f97 as 0.13.0",
"media-alchemyst/media-alchemyst": "^0.5", "media-alchemyst/media-alchemyst": "^0.5.5",
"monolog/monolog": "~1.3", "monolog/monolog": "~1.3",
"mrclay/minify": "~2.1.6", "mrclay/minify": "~2.1.6",
"neutron/process-manager": "2.0.x-dev@dev", "neutron/process-manager": "2.0.x-dev@dev",

337
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,6 @@
#!/bin/bash #!/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;" 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,85 +1,39 @@
user app; upstream backend {
worker_processes auto; server phraseanet:9000;
#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;
} }
http { server {
include /etc/nginx/mime.types; listen 80;
default_type application/octet-stream; root /var/alchemy/Phraseanet/www;
server_tokens off;
log_format main '$remote_addr - $remote_user [$time_local] "$request" ' index index.php;
'$status $body_bytes_sent "$http_referer" ' client_max_body_size $MAX_BODY_SIZE;
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /dev/stdout main; location /api {
rewrite ^(.*)$ /api.php/$1 last;
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 { location / {
listen 80; # First attempt to serve request as file, then
server_name localhost; # as directory, then fall back to index.html
error_log on; try_files $uri $uri/ @rewriteapp;
access_log on; }
root /var/alchemy/Phraseanet/www;
index index.php; location @rewriteapp {
client_max_body_size $MAX_BODY_SIZE; rewrite ^(.*)$ /index.php/$1 last;
}
location /api { # PHP scripts -> PHP-FPM server listening on 127.0.0.1:9000
rewrite ^(.*)$ /api.php/$1 last; location ~ ^/(index|index_dev|api)\.php(/|$) {
} fastcgi_pass backend;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
location / { location ~ ^/(status|ping)$ {
# First attempt to serve request as file, then fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
# as directory, then fall back to index.html fastcgi_index index.php;
try_files $uri $uri/ @rewriteapp; include fastcgi_params;
} fastcgi_pass backend;
location @rewriteapp {
rewrite ^(.*)$ /index.php/$1 last;
}
# PHP scripts -> PHP-FPM server listening on 127.0.0.1:9000
location ~ ^/(index|index_dev|api)\.php(/|$) {
fastcgi_pass backend;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
location ~ ^/(status|ping)$ {
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_pass backend;
}
} }
} }

View File

@@ -2,6 +2,16 @@
set -xe 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 \ /var/alchemy/Phraseanet/bin/setup system:install \
--email=$INSTALL_ACCOUNT_EMAIL \ --email=$INSTALL_ACCOUNT_EMAIL \
--password=$INSTALL_ACCOUNT_PASSWORD \ --password=$INSTALL_ACCOUNT_PASSWORD \

View File

@@ -1,5 +1,11 @@
#!/bin/bash #!/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 FILE=/var/alchemy/Phraseanet/config/configuration.yml
if [ -f "$FILE" ]; then if [ -f "$FILE" ]; then
echo "$FILE exist, skip setup." 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.ini.sample > /usr/local/etc/php/php.ini
envsubst < /php-fpm.conf.sample > /usr/local/etc/php-fpm.conf 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 #!/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' runuser app -c 'php /var/alchemy/Phraseanet/bin/console task-manager:scheduler:run'

View File

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

View File

@@ -15,6 +15,7 @@ use Alchemy\Phrasea\Controller\Api\Result;
use Alchemy\Phrasea\ControllerProvider\Api\OAuth2; use Alchemy\Phrasea\ControllerProvider\Api\OAuth2;
use Alchemy\Phrasea\ControllerProvider\Api\V1; use Alchemy\Phrasea\ControllerProvider\Api\V1;
use Alchemy\Phrasea\ControllerProvider\Api\V2; use Alchemy\Phrasea\ControllerProvider\Api\V2;
use Alchemy\Phrasea\ControllerProvider\Api\V3;
use Alchemy\Phrasea\ControllerProvider\Datafiles; use Alchemy\Phrasea\ControllerProvider\Datafiles;
use Alchemy\Phrasea\ControllerProvider\MediaAccessor; use Alchemy\Phrasea\ControllerProvider\MediaAccessor;
use Alchemy\Phrasea\ControllerProvider\Minifier; use Alchemy\Phrasea\ControllerProvider\Minifier;
@@ -36,6 +37,7 @@ class ApiApplicationLoader extends BaseApplicationLoader
$app->register(new OAuth2()); $app->register(new OAuth2());
$app->register(new V1()); $app->register(new V1());
$app->register(new V2()); $app->register(new V2());
$app->register(new V3());
$app->register(new ApiReportControllerProvider()); $app->register(new ApiReportControllerProvider());
$app->register(new JsonSchemaServiceProvider()); $app->register(new JsonSchemaServiceProvider());
} }
@@ -119,6 +121,16 @@ class ApiApplicationLoader extends BaseApplicationLoader
'access_token' => '/api/oauthv2/token' 'access_token' => '/api/oauthv2/token'
], ],
], ],
'3' => [
'number' => V3::VERSION,
'uri' => '/api/v3/',
'authenticationProtocol' => 'OAuth2',
'authenticationVersion' => 'draft#v9',
'authenticationEndPoints' => [
'authorization_token' => '/api/oauthv2/authorize',
'access_token' => '/api/oauthv2/token'
]
],
] ]
])->createResponse(); ])->createResponse();
}); });
@@ -135,6 +147,7 @@ class ApiApplicationLoader extends BaseApplicationLoader
$app->mount('/datafiles/', new Datafiles()); $app->mount('/datafiles/', new Datafiles());
$app->mount('/api/v1', new V1()); $app->mount('/api/v1', new V1());
$app->mount('/api/v2', new V2()); $app->mount('/api/v2', new V2());
$app->mount('/api/v3', new V3());
$app->mount('/api/report', new ApiReportControllerProvider()); $app->mount('/api/report', new ApiReportControllerProvider());
$app->mount('/permalink/', new Permalink()); $app->mount('/permalink/', new Permalink());
$app->mount($app['controller.media_accessor.route_prefix'], new MediaAccessor()); $app->mount($app['controller.media_accessor.route_prefix'], new MediaAccessor());

View File

@@ -34,6 +34,11 @@ abstract class AbstractChecker implements CheckerInterface
*/ */
protected $collections = []; protected $collections = [];
/**
* @var \collection[]
*/
protected $compareIgnoreCollections = [];
public function __construct(Application $app) public function __construct(Application $app)
{ {
$this->app = $app; $this->app = $app;
@@ -44,7 +49,7 @@ abstract class AbstractChecker implements CheckerInterface
* Warning, you can not restrict on both databoxes and collections * Warning, you can not restrict on both databoxes and collections
* *
* @param \databox[] $databoxes A databox or an array of databoxes * @param \databox[] $databoxes A databox or an array of databoxes
* @return bool * @return \databox[]
* *
* @throws \LogicException If already restricted to collections * @throws \LogicException If already restricted to collections
* @throws \InvalidArgumentException In case invalid databoxes are provided * @throws \InvalidArgumentException In case invalid databoxes are provided
@@ -72,7 +77,7 @@ abstract class AbstractChecker implements CheckerInterface
* Warning, you can not restrict on both databoxes and collections * Warning, you can not restrict on both databoxes and collections
* *
* @param \collection[] $collections * @param \collection[] $collections
* @return bool * @return \collection[]
* *
* @throws \LogicException If already restricted to databoxes * @throws \LogicException If already restricted to databoxes
* @throws \InvalidArgumentException In case invalid collections are provided * @throws \InvalidArgumentException In case invalid collections are provided
@@ -95,6 +100,11 @@ abstract class AbstractChecker implements CheckerInterface
return $this->collections; return $this->collections;
} }
public function setCompareIgnoreCollections($collections)
{
$this->compareIgnoreCollections = $collections;
}
/** /**
* Returns true if the checker should be executed against the current file * Returns true if the checker should be executed against the current file
* *

View File

@@ -45,8 +45,18 @@ class Filename extends AbstractChecker
*/ */
public function check(EntityManager $em, File $file) public function check(EntityManager $em, File $file)
{ {
$boolean = empty(\record_adapter::get_records_by_originalname( $excludedCollIds = [];
$file->getCollection()->get_databox(), $file->getOriginalName(), $this->sensitive, 0, 1 if (!empty($this->compareIgnoreCollections)) {
foreach ($this->compareIgnoreCollections as $collection) {
// use only collection in the same databox and retrieve the coll_id
if ($collection->get_sbas_id() === $file->getCollection()->get_sbas_id()) {
$excludedCollIds[] = $collection->get_coll_id();
}
}
}
$boolean = empty(\record_adapter::getRecordsByOriginalnameWithExcludedCollIds(
$file->getCollection()->get_databox(), $file->getOriginalName(), $this->sensitive, 0, 1, $excludedCollIds
)); ));
return new Response($boolean, $this); return new Response($boolean, $this);

View File

@@ -34,7 +34,17 @@ class Sha256 extends AbstractChecker
*/ */
public function check(EntityManager $em, File $file) public function check(EntityManager $em, File $file)
{ {
$boolean = empty($file->getCollection()->get_databox()->getRecordRepository()->findBySha256($file->getSha256())); $excludedCollIds = [];
if (!empty($this->compareIgnoreCollections)) {
foreach ($this->compareIgnoreCollections as $collection) {
// use only collection in the same databox and retrieve the coll_id
if ($collection->get_sbas_id() === $file->getCollection()->get_sbas_id()) {
$excludedCollIds[] = $collection->get_coll_id();
}
}
}
$boolean = empty($file->getCollection()->get_databox()->getRecordRepository()->findBySha256WithExcludedCollIds($file->getSha256(), $excludedCollIds));
return new Response($boolean, $this); return new Response($boolean, $this);
} }

View File

@@ -33,7 +33,17 @@ class UUID extends AbstractChecker
*/ */
public function check(EntityManager $em, File $file) public function check(EntityManager $em, File $file)
{ {
$boolean = empty($file->getCollection()->get_databox()->getRecordRepository()->findByUuid($file->getUUID())); $excludedCollIds = [];
if (!empty($this->compareIgnoreCollections)) {
foreach ($this->compareIgnoreCollections as $collection) {
// use only collection in the same databox and retrieve the coll_id
if ($collection->get_sbas_id() === $file->getCollection()->get_sbas_id()) {
$excludedCollIds[] = $collection->get_coll_id();
}
}
}
$boolean = empty($file->getCollection()->get_databox()->getRecordRepository()->findByUuidWithExcludedCollIds($file->getUUID(), $excludedCollIds));
return new Response($boolean, $this); return new Response($boolean, $this);
} }

View File

@@ -73,7 +73,7 @@ class RedisCache extends CacheProvider implements Cache
*/ */
protected function doDelete($id) 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\Command\Command;
use Alchemy\Phrasea\Core\Configuration\StructureTemplate; use Alchemy\Phrasea\Core\Configuration\StructureTemplate;
use Alchemy\Phrasea\SearchEngine\Elastic\ElasticsearchOptions;
use Doctrine\DBAL\Driver\Connection; use Doctrine\DBAL\Driver\Connection;
use Symfony\Component\Console\Helper\DialogHelper; use Symfony\Component\Console\Helper\DialogHelper;
use Symfony\Component\Console\Input\ArrayInput; 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('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('data-path', null, InputOption::VALUE_OPTIONAL, 'Path to data repository', realpath(__DIR__ . '/../../../../../datas'))
->addOption('server-name', null, InputOption::VALUE_OPTIONAL, 'Server name') ->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'); ->addOption('yes', 'y', InputOption::VALUE_NONE, 'Answer yes to all questions');
return $this; return $this;
@@ -121,6 +124,21 @@ class Install extends Command
list($email, $password) = $this->getCredentials($input, $output, $dialog); list($email, $password) = $this->getCredentials($input, $output, $dialog);
$dataPath = $this->getDataPath($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')) { if (!$input->getOption('yes')) {
$continue = $dialog->askConfirmation($output, "<question>Phraseanet is going to be installed, continue ? (N/y)</question>", false); $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['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()) { if (null !== $this->getApplication()) {
$command = $this->getApplication()->find('crossdomain:generate'); $command = $this->getApplication()->find('crossdomain:generate');
@@ -339,6 +358,35 @@ class Install extends Command
return $serverName; 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() private function detectBinaries()
{ {
return [ return [

View File

@@ -314,6 +314,9 @@ class FieldsController extends Controller
->set_readonly($data['readonly']) ->set_readonly($data['readonly'])
->set_type($data['type']) ->set_type($data['type'])
->set_tbranch($data['tbranch']) ->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']) ->set_report($data['report'])
->setVocabularyControl(null) ->setVocabularyControl(null)
->setVocabularyRestricted(false); ->setVocabularyRestricted(false);
@@ -349,7 +352,7 @@ class FieldsController extends Controller
{ {
return [ return [
'name', 'multi', 'thumbtitle', 'tag', 'business', 'indexable', 'aggregable', 'name', 'multi', 'thumbtitle', 'tag', 'business', 'indexable', 'aggregable',
'required', 'separator', 'readonly', 'type', 'tbranch', 'report', 'required', 'separator', 'readonly', 'gui_editable', 'gui_visible' , 'type', 'tbranch', 'generate_cterms', 'report',
'vocabulary-type', 'vocabulary-restricted', 'dces-element', 'labels' 'vocabulary-type', 'vocabulary-restricted', 'dces-element', 'labels'
]; ];
} }

View File

@@ -354,7 +354,7 @@ class SubdefsController extends Controller
Subdef::TYPE_VIDEO => [ Subdef::TYPE_VIDEO => [
"definitions" => [ "definitions" => [
"video codec H264" => null, "video codec H264" => null,
"144P H264 128 kbps ACC 128kbps" => [ "144P H264 128 kbps MP3 128kbps" => [
Video::OPTION_AUDIOBITRATE => "128", Video::OPTION_AUDIOBITRATE => "128",
Video::OPTION_AUDIOSAMPLERATE => "44100", Video::OPTION_AUDIOSAMPLERATE => "44100",
Video::OPTION_BITRATE => "128", Video::OPTION_BITRATE => "128",
@@ -362,10 +362,10 @@ class SubdefsController extends Controller
Video::OPTION_SIZE => "256", Video::OPTION_SIZE => "256",
Video::OPTION_FRAMERATE => "25", Video::OPTION_FRAMERATE => "25",
Video::OPTION_VCODEC => "libx264", Video::OPTION_VCODEC => "libx264",
Video::OPTION_ACODEC => "libfaac", Video::OPTION_ACODEC => "libmp3lame",
Subdef::OPTION_DEVICE => ["all"] Subdef::OPTION_DEVICE => ["all"]
], ],
"240P H264 256 kbps ACC 128kbps" => [ "240P H264 256 kbps MP3 128kbps" => [
Video::OPTION_AUDIOBITRATE => "128", Video::OPTION_AUDIOBITRATE => "128",
Video::OPTION_AUDIOSAMPLERATE => "44100", Video::OPTION_AUDIOSAMPLERATE => "44100",
Video::OPTION_BITRATE => "256", Video::OPTION_BITRATE => "256",
@@ -373,10 +373,10 @@ class SubdefsController extends Controller
Video::OPTION_SIZE => "426", Video::OPTION_SIZE => "426",
Video::OPTION_FRAMERATE => "25", Video::OPTION_FRAMERATE => "25",
Video::OPTION_VCODEC => "libx264", Video::OPTION_VCODEC => "libx264",
Video::OPTION_ACODEC => "libfaac", Video::OPTION_ACODEC => "libmp3lame",
Subdef::OPTION_DEVICE => ["all"] Subdef::OPTION_DEVICE => ["all"]
], ],
"360P H264 576 kbps ACC 128kbps" => [ "360P H264 576 kbps MP3 128kbps" => [
Video::OPTION_AUDIOBITRATE => "128", Video::OPTION_AUDIOBITRATE => "128",
Video::OPTION_AUDIOSAMPLERATE => "44100", Video::OPTION_AUDIOSAMPLERATE => "44100",
Video::OPTION_BITRATE => "576", Video::OPTION_BITRATE => "576",
@@ -384,10 +384,10 @@ class SubdefsController extends Controller
Video::OPTION_SIZE => "480", Video::OPTION_SIZE => "480",
Video::OPTION_FRAMERATE => "25", Video::OPTION_FRAMERATE => "25",
Video::OPTION_VCODEC => "libtheora", Video::OPTION_VCODEC => "libtheora",
Video::OPTION_ACODEC => "libfaac", Video::OPTION_ACODEC => "libmp3lame",
Subdef::OPTION_DEVICE => ["all"] Subdef::OPTION_DEVICE => ["all"]
], ],
"480P H264 750 kbps ACC 128kbps" => [ "480P H264 750 kbps MP3 128kbps" => [
Video::OPTION_AUDIOBITRATE => "128", Video::OPTION_AUDIOBITRATE => "128",
Video::OPTION_AUDIOSAMPLERATE => "44100", Video::OPTION_AUDIOSAMPLERATE => "44100",
Video::OPTION_BITRATE => "750", Video::OPTION_BITRATE => "750",
@@ -395,10 +395,10 @@ class SubdefsController extends Controller
Video::OPTION_SIZE => "854", Video::OPTION_SIZE => "854",
Video::OPTION_FRAMERATE => "25", Video::OPTION_FRAMERATE => "25",
Video::OPTION_VCODEC => "libx264", Video::OPTION_VCODEC => "libx264",
Video::OPTION_ACODEC => "libfaac", Video::OPTION_ACODEC => "libmp3lame",
Subdef::OPTION_DEVICE => ["all"] Subdef::OPTION_DEVICE => ["all"]
], ],
"720P H264 1492 kbps ACC 128kbps" => [ "720P H264 1492 kbps MP3 128kbps" => [
Video::OPTION_AUDIOBITRATE => "128", Video::OPTION_AUDIOBITRATE => "128",
Video::OPTION_AUDIOSAMPLERATE => "44100", Video::OPTION_AUDIOSAMPLERATE => "44100",
Video::OPTION_BITRATE => "1492", Video::OPTION_BITRATE => "1492",
@@ -406,10 +406,10 @@ class SubdefsController extends Controller
Video::OPTION_SIZE => "1280", Video::OPTION_SIZE => "1280",
Video::OPTION_FRAMERATE => "25", Video::OPTION_FRAMERATE => "25",
Video::OPTION_VCODEC => "libx264", Video::OPTION_VCODEC => "libx264",
Video::OPTION_ACODEC => "libfaac", Video::OPTION_ACODEC => "libmp3lame",
Subdef::OPTION_DEVICE => ["all"] Subdef::OPTION_DEVICE => ["all"]
], ],
"1080P H264 2420 kbps ACC 128kbps" => [ "1080P H264 2420 kbps MP3 128kbps" => [
Video::OPTION_AUDIOBITRATE => "128", Video::OPTION_AUDIOBITRATE => "128",
Video::OPTION_AUDIOSAMPLERATE => "44100", Video::OPTION_AUDIOSAMPLERATE => "44100",
Video::OPTION_BITRATE => "2420", Video::OPTION_BITRATE => "2420",
@@ -417,11 +417,77 @@ class SubdefsController extends Controller
Video::OPTION_SIZE => "1920", Video::OPTION_SIZE => "1920",
Video::OPTION_FRAMERATE => "25", Video::OPTION_FRAMERATE => "25",
Video::OPTION_VCODEC => "libx264", Video::OPTION_VCODEC => "libx264",
Video::OPTION_ACODEC => "libfaac", Video::OPTION_ACODEC => "libmp3lame",
Subdef::OPTION_DEVICE => ["all"]
],
"144P H264 128 kbps AAC 128kbps" => [
Video::OPTION_AUDIOBITRATE => "128",
Video::OPTION_AUDIOSAMPLERATE => "44100",
Video::OPTION_BITRATE => "128",
Video::OPTION_GOPSIZE => "25",
Video::OPTION_SIZE => "256",
Video::OPTION_FRAMERATE => "25",
Video::OPTION_VCODEC => "libx264",
Video::OPTION_ACODEC => "libfdk_aac",
Subdef::OPTION_DEVICE => ["all"]
],
"240P H264 256 kbps AAC 128kbps" => [
Video::OPTION_AUDIOBITRATE => "128",
Video::OPTION_AUDIOSAMPLERATE => "44100",
Video::OPTION_BITRATE => "256",
Video::OPTION_GOPSIZE => "25",
Video::OPTION_SIZE => "426",
Video::OPTION_FRAMERATE => "25",
Video::OPTION_VCODEC => "libx264",
Video::OPTION_ACODEC => "libfdk_aac",
Subdef::OPTION_DEVICE => ["all"]
],
"360P H264 576 kbps AAC 128kbps" => [
Video::OPTION_AUDIOBITRATE => "128",
Video::OPTION_AUDIOSAMPLERATE => "44100",
Video::OPTION_BITRATE => "576",
Video::OPTION_GOPSIZE => "25",
Video::OPTION_SIZE => "480",
Video::OPTION_FRAMERATE => "25",
Video::OPTION_VCODEC => "libtheora",
Video::OPTION_ACODEC => "libfdk_aac",
Subdef::OPTION_DEVICE => ["all"]
],
"480P H264 750 kbps AAC 128kbps" => [
Video::OPTION_AUDIOBITRATE => "128",
Video::OPTION_AUDIOSAMPLERATE => "44100",
Video::OPTION_BITRATE => "750",
Video::OPTION_GOPSIZE => "25",
Video::OPTION_SIZE => "854",
Video::OPTION_FRAMERATE => "25",
Video::OPTION_VCODEC => "libx264",
Video::OPTION_ACODEC => "libfdk_aac",
Subdef::OPTION_DEVICE => ["all"]
],
"720P H264 1492 kbps AAC 128kbps" => [
Video::OPTION_AUDIOBITRATE => "128",
Video::OPTION_AUDIOSAMPLERATE => "44100",
Video::OPTION_BITRATE => "1492",
Video::OPTION_GOPSIZE => "25",
Video::OPTION_SIZE => "1280",
Video::OPTION_FRAMERATE => "25",
Video::OPTION_VCODEC => "libx264",
Video::OPTION_ACODEC => "libfdk_aac",
Subdef::OPTION_DEVICE => ["all"]
],
"1080P H264 2420 kbps AAC 128kbps" => [
Video::OPTION_AUDIOBITRATE => "128",
Video::OPTION_AUDIOSAMPLERATE => "44100",
Video::OPTION_BITRATE => "2420",
Video::OPTION_GOPSIZE => "25",
Video::OPTION_SIZE => "1920",
Video::OPTION_FRAMERATE => "25",
Video::OPTION_VCODEC => "libx264",
Video::OPTION_ACODEC => "libfdk_aac",
Subdef::OPTION_DEVICE => ["all"] Subdef::OPTION_DEVICE => ["all"]
], ],
"video codec libvpx" => null, "video codec libvpx" => null,
"144P webm 128 kbps ACC 128kbps" => [ "144P webm 128 kbps MP3 128kbps" => [
Video::OPTION_AUDIOBITRATE => "128", Video::OPTION_AUDIOBITRATE => "128",
Video::OPTION_AUDIOSAMPLERATE => "44100", Video::OPTION_AUDIOSAMPLERATE => "44100",
Video::OPTION_BITRATE => "128", Video::OPTION_BITRATE => "128",
@@ -429,10 +495,10 @@ class SubdefsController extends Controller
Video::OPTION_SIZE => "256", Video::OPTION_SIZE => "256",
Video::OPTION_FRAMERATE => "25", Video::OPTION_FRAMERATE => "25",
Video::OPTION_VCODEC => "libvpx", Video::OPTION_VCODEC => "libvpx",
Video::OPTION_ACODEC => "libfaac", Video::OPTION_ACODEC => "libmp3lame",
Subdef::OPTION_DEVICE => ["all"] Subdef::OPTION_DEVICE => ["all"]
], ],
"240P webm 256 kbps ACC 128kbps" => [ "240P webm 256 kbps MP3 128kbps" => [
Video::OPTION_AUDIOBITRATE => "128", Video::OPTION_AUDIOBITRATE => "128",
Video::OPTION_AUDIOSAMPLERATE => "44100", Video::OPTION_AUDIOSAMPLERATE => "44100",
Video::OPTION_BITRATE => "256", Video::OPTION_BITRATE => "256",
@@ -440,10 +506,10 @@ class SubdefsController extends Controller
Video::OPTION_SIZE => "426", Video::OPTION_SIZE => "426",
Video::OPTION_FRAMERATE => "25", Video::OPTION_FRAMERATE => "25",
Video::OPTION_VCODEC => "libvpx", Video::OPTION_VCODEC => "libvpx",
Video::OPTION_ACODEC => "libfaac", Video::OPTION_ACODEC => "libmp3lame",
Subdef::OPTION_DEVICE => ["all"] Subdef::OPTION_DEVICE => ["all"]
], ],
"360P webm 576 kbps ACC 128kbps" => [ "360P webm 576 kbps MP3 128kbps" => [
Video::OPTION_AUDIOBITRATE => "128", Video::OPTION_AUDIOBITRATE => "128",
Video::OPTION_AUDIOSAMPLERATE => "44100", Video::OPTION_AUDIOSAMPLERATE => "44100",
Video::OPTION_BITRATE => "576", Video::OPTION_BITRATE => "576",
@@ -451,10 +517,10 @@ class SubdefsController extends Controller
Video::OPTION_SIZE => "480", Video::OPTION_SIZE => "480",
Video::OPTION_FRAMERATE => "25", Video::OPTION_FRAMERATE => "25",
Video::OPTION_VCODEC => "libvpx", Video::OPTION_VCODEC => "libvpx",
Video::OPTION_ACODEC => "libfaac", Video::OPTION_ACODEC => "libmp3lame",
Subdef::OPTION_DEVICE => ["all"] Subdef::OPTION_DEVICE => ["all"]
], ],
"480P webm 750 kbps ACC 128kbps" => [ "480P webm 750 kbps MP3 128kbps" => [
Video::OPTION_AUDIOBITRATE => "128", Video::OPTION_AUDIOBITRATE => "128",
Video::OPTION_AUDIOSAMPLERATE => "44100", Video::OPTION_AUDIOSAMPLERATE => "44100",
Video::OPTION_BITRATE => "750", Video::OPTION_BITRATE => "750",
@@ -462,10 +528,10 @@ class SubdefsController extends Controller
Video::OPTION_SIZE => "854", Video::OPTION_SIZE => "854",
Video::OPTION_FRAMERATE => "25", Video::OPTION_FRAMERATE => "25",
Video::OPTION_VCODEC => "libvpx", Video::OPTION_VCODEC => "libvpx",
Video::OPTION_ACODEC => "libfaac", Video::OPTION_ACODEC => "libmp3lame",
Subdef::OPTION_DEVICE => ["all"] Subdef::OPTION_DEVICE => ["all"]
], ],
"720P webm 1492 kbps ACC 128kbps" => [ "720P webm 1492 kbps MP3 128kbps" => [
Video::OPTION_AUDIOBITRATE => "128", Video::OPTION_AUDIOBITRATE => "128",
Video::OPTION_AUDIOSAMPLERATE => "44100", Video::OPTION_AUDIOSAMPLERATE => "44100",
Video::OPTION_BITRATE => "1492", Video::OPTION_BITRATE => "1492",
@@ -473,10 +539,10 @@ class SubdefsController extends Controller
Video::OPTION_SIZE => "1280", Video::OPTION_SIZE => "1280",
Video::OPTION_FRAMERATE => "25", Video::OPTION_FRAMERATE => "25",
Video::OPTION_VCODEC => "libvpx", Video::OPTION_VCODEC => "libvpx",
Video::OPTION_ACODEC => "libfaac", Video::OPTION_ACODEC => "libmp3lame",
Subdef::OPTION_DEVICE => ["all"] Subdef::OPTION_DEVICE => ["all"]
], ],
"1080P webm 2420 kbps ACC 128kbps" => [ "1080P webm 2420 kbps MP3 128kbps" => [
Video::OPTION_AUDIOBITRATE => "128", Video::OPTION_AUDIOBITRATE => "128",
Video::OPTION_AUDIOSAMPLERATE => "44100", Video::OPTION_AUDIOSAMPLERATE => "44100",
Video::OPTION_BITRATE => "2420", Video::OPTION_BITRATE => "2420",
@@ -484,7 +550,73 @@ class SubdefsController extends Controller
Video::OPTION_SIZE => "1920", Video::OPTION_SIZE => "1920",
Video::OPTION_FRAMERATE => "25", Video::OPTION_FRAMERATE => "25",
Video::OPTION_VCODEC => "libvpx", Video::OPTION_VCODEC => "libvpx",
Video::OPTION_ACODEC => "libfaac", Video::OPTION_ACODEC => "libmp3lame",
Subdef::OPTION_DEVICE => ["all"]
],
"144P webm 128 kbps AAC 128kbps" => [
Video::OPTION_AUDIOBITRATE => "128",
Video::OPTION_AUDIOSAMPLERATE => "44100",
Video::OPTION_BITRATE => "128",
Video::OPTION_GOPSIZE => "25",
Video::OPTION_SIZE => "256",
Video::OPTION_FRAMERATE => "25",
Video::OPTION_VCODEC => "libvpx",
Video::OPTION_ACODEC => "libfdk_aac",
Subdef::OPTION_DEVICE => ["all"]
],
"240P webm 256 kbps AAC 128kbps" => [
Video::OPTION_AUDIOBITRATE => "128",
Video::OPTION_AUDIOSAMPLERATE => "44100",
Video::OPTION_BITRATE => "256",
Video::OPTION_GOPSIZE => "25",
Video::OPTION_SIZE => "426",
Video::OPTION_FRAMERATE => "25",
Video::OPTION_VCODEC => "libvpx",
Video::OPTION_ACODEC => "libfdk_aac",
Subdef::OPTION_DEVICE => ["all"]
],
"360P webm 576 kbps AAC 128kbps" => [
Video::OPTION_AUDIOBITRATE => "128",
Video::OPTION_AUDIOSAMPLERATE => "44100",
Video::OPTION_BITRATE => "576",
Video::OPTION_GOPSIZE => "25",
Video::OPTION_SIZE => "480",
Video::OPTION_FRAMERATE => "25",
Video::OPTION_VCODEC => "libvpx",
Video::OPTION_ACODEC => "libfdk_aac",
Subdef::OPTION_DEVICE => ["all"]
],
"480P webm 750 kbps AAC 128kbps" => [
Video::OPTION_AUDIOBITRATE => "128",
Video::OPTION_AUDIOSAMPLERATE => "44100",
Video::OPTION_BITRATE => "750",
Video::OPTION_GOPSIZE => "25",
Video::OPTION_SIZE => "854",
Video::OPTION_FRAMERATE => "25",
Video::OPTION_VCODEC => "libvpx",
Video::OPTION_ACODEC => "libfdk_aac",
Subdef::OPTION_DEVICE => ["all"]
],
"720P webm 1492 kbps AAC 128kbps" => [
Video::OPTION_AUDIOBITRATE => "128",
Video::OPTION_AUDIOSAMPLERATE => "44100",
Video::OPTION_BITRATE => "1492",
Video::OPTION_GOPSIZE => "25",
Video::OPTION_SIZE => "1280",
Video::OPTION_FRAMERATE => "25",
Video::OPTION_VCODEC => "libvpx",
Video::OPTION_ACODEC => "libfdk_aac",
Subdef::OPTION_DEVICE => ["all"]
],
"1080P webm 2420 kbps AAC 128kbps" => [
Video::OPTION_AUDIOBITRATE => "128",
Video::OPTION_AUDIOSAMPLERATE => "44100",
Video::OPTION_BITRATE => "2420",
Video::OPTION_GOPSIZE => "25",
Video::OPTION_SIZE => "1920",
Video::OPTION_FRAMERATE => "25",
Video::OPTION_VCODEC => "libvpx",
Video::OPTION_ACODEC => "libfdk_aac",
Subdef::OPTION_DEVICE => ["all"] Subdef::OPTION_DEVICE => ["all"]
], ],
], ],

View File

@@ -12,6 +12,7 @@
namespace Alchemy\Phrasea\Controller\Api; namespace Alchemy\Phrasea\Controller\Api;
use Alchemy\Phrasea\ControllerProvider\Api\V1; use Alchemy\Phrasea\ControllerProvider\Api\V1;
use Alchemy\Phrasea\ControllerProvider\Api\V3;
use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
@@ -268,11 +269,13 @@ class Result
public function getVersion() public function getVersion()
{ {
if (null === $this->version) { if (null === $this->version) {
if($this->request->attributes->get('api_version')){ if ($this->request->attributes->get('api_version')) {
$this->version = $this->request->attributes->get('api_version'); $this->version = $this->request->attributes->get('api_version');
}elseif(mb_strpos($this->request->getPathInfo(), '/api/v1') !== FALSE){ } elseif (mb_strpos($this->request->getPathInfo(), '/api/v1') !== FALSE) {
$this->version = V1::VERSION; $this->version = V1::VERSION;
}else{ } elseif (mb_strpos($this->request->getPathInfo(), '/api/v3') !== FALSE) {
$this->version = V3::VERSION;
} else {
$this->version = self::$defaultVersion; $this->version = self::$defaultVersion;
} }
} }

View File

@@ -594,6 +594,9 @@ class V1Controller extends Controller
], ],
'separator' => $databox_field->get_separator(), 'separator' => $databox_field->get_separator(),
'thesaurus_branch' => $databox_field->get_tbranch(), '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(), 'type' => $databox_field->get_type(),
'indexable' => $databox_field->is_indexable(), 'indexable' => $databox_field->is_indexable(),
'multivalue' => $databox_field->is_multi(), 'multivalue' => $databox_field->is_multi(),

View File

@@ -0,0 +1,807 @@
<?php
namespace Alchemy\Phrasea\Controller\Api;
use Alchemy\Phrasea\Collection\Reference\CollectionReference;
use Alchemy\Phrasea\Controller\Controller;
use Alchemy\Phrasea\Databox\DataboxGroupable;
use Alchemy\Phrasea\Fractal\CallbackTransformer;
use Alchemy\Phrasea\Fractal\IncludeResolver;
use Alchemy\Phrasea\Fractal\SearchResultTransformerResolver;
use Alchemy\Phrasea\Fractal\TraceableArraySerializer;
use Alchemy\Phrasea\Model\Manipulator\UserManipulator;
use Alchemy\Phrasea\Model\RecordReferenceInterface;
use Alchemy\Phrasea\Record\RecordCollection;
use Alchemy\Phrasea\Record\RecordReferenceCollection;
use Alchemy\Phrasea\Search\CaptionView;
use Alchemy\Phrasea\Search\PermalinkTransformer;
use Alchemy\Phrasea\Search\PermalinkView;
use Alchemy\Phrasea\Search\RecordTransformer;
use Alchemy\Phrasea\Search\RecordView;
use Alchemy\Phrasea\Search\SearchResultView;
use Alchemy\Phrasea\Search\StoryTransformer;
use Alchemy\Phrasea\Search\StoryView;
use Alchemy\Phrasea\Search\SubdefTransformer;
use Alchemy\Phrasea\Search\SubdefView;
use Alchemy\Phrasea\Search\TechnicalDataTransformer;
use Alchemy\Phrasea\Search\TechnicalDataView;
use Alchemy\Phrasea\Search\V1SearchCompositeResultTransformer;
use Alchemy\Phrasea\Search\V1SearchResultTransformer;
use Alchemy\Phrasea\SearchEngine\SearchEngineInterface;
use Alchemy\Phrasea\SearchEngine\SearchEngineLogger;
use Alchemy\Phrasea\SearchEngine\SearchEngineOptions;
use Alchemy\Phrasea\SearchEngine\SearchEngineResult;
use League\Fractal\Resource\Item;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
class V3Controller extends Controller
{
/**
* Return detailed information about one story
*
* @param Request $request
* @param int $databox_id
* @param int $record_id
*
* @return Response
*/
public function getStoryAction(Request $request, $databox_id, $record_id)
{
try {
$story = $this->findDataboxById($databox_id)->get_record($record_id);
return Result::create($request, ['story' => $this->listStory($request, $story)])->createResponse();
} catch (NotFoundHttpException $e) {
return Result::createError($request, 404, $this->app->trans('Story Not Found'))->createResponse();
} catch (\Exception $e) {
return $this->app['controller.api.v1']->getBadRequestAction($request, $this->app->trans('An error occurred'));
}
}
/**
* Search for results
*
* @param Request $request
*
* @return Response
*/
public function searchAction(Request $request)
{
$subdefTransformer = new SubdefTransformer($this->app['acl'], $this->getAuthenticatedUser(), new PermalinkTransformer());
$technicalDataTransformer = new TechnicalDataTransformer();
$recordTransformer = new RecordTransformer($subdefTransformer, $technicalDataTransformer);
$storyTransformer = new StoryTransformer($subdefTransformer, $recordTransformer);
$compositeTransformer = new V1SearchCompositeResultTransformer($recordTransformer, $storyTransformer);
$searchTransformer = new V1SearchResultTransformer($compositeTransformer);
$transformerResolver = new SearchResultTransformerResolver([
'' => $searchTransformer,
'results' => $compositeTransformer,
'results.stories' => $storyTransformer,
'results.stories.thumbnail' => $subdefTransformer,
'results.stories.metadatas' => new CallbackTransformer(),
'results.stories.caption' => new CallbackTransformer(),
'results.stories.records' => $recordTransformer,
'results.stories.records.thumbnail' => $subdefTransformer,
'results.stories.records.technical_informations' => $technicalDataTransformer,
'results.stories.records.subdefs' => $subdefTransformer,
'results.stories.records.metadata' => new CallbackTransformer(),
'results.stories.records.status' => new CallbackTransformer(),
'results.stories.records.caption' => new CallbackTransformer(),
'results.records' => $recordTransformer,
'results.records.thumbnail' => $subdefTransformer,
'results.records.technical_informations' => $technicalDataTransformer,
'results.records.subdefs' => $subdefTransformer,
'results.records.metadata' => new CallbackTransformer(),
'results.records.status' => new CallbackTransformer(),
'results.records.caption' => new CallbackTransformer(),
]);
$includeResolver = new IncludeResolver($transformerResolver);
$fractal = new \League\Fractal\Manager();
$fractal->setSerializer(new TraceableArraySerializer($this->app['dispatcher']));
$fractal->parseIncludes($this->resolveSearchIncludes($request));
$result = $this->doSearch($request);
$story_max_records = null;
// if search on story
if ($request->get('search_type') == 1) {
$story_max_records = (int)$request->get('story_max_records') ?: 10;
}
$searchView = $this->buildSearchView(
$result,
$includeResolver->resolve($fractal),
$this->resolveSubdefUrlTTL($request),
$story_max_records
);
$ret = $fractal->createData(new Item($searchView, $searchTransformer))->toArray();
return Result::create($request, $ret)->createResponse();
}
/**
* Retrieve detailed information about one story
*
* @param Request $request
* @param \record_adapter $story
* @return array
* @throws \Exception
*/
private function listStory(Request $request, \record_adapter $story)
{
if (!$story->isStory()) {
return Result::createError($request, 404, 'Story not found')->createResponse();
}
$per_page = (int)$request->get('per_page')?:10;
$page = (int)$request->get('page')?:1;
$offset = ($per_page * ($page - 1)) + 1;
$caption = $story->get_caption();
$format = function (\caption_record $caption, $dcField) {
$field = $caption->get_dc_field($dcField);
if (!$field) {
return null;
}
return $field->get_serialized_values();
};
return [
'@entity@' => V1Controller::OBJECT_TYPE_STORY,
'databox_id' => $story->getDataboxId(),
'story_id' => $story->getRecordId(),
'updated_on' => $story->getUpdated()->format(DATE_ATOM),
'created_on' => $story->getCreated()->format(DATE_ATOM),
'collection_id' => $story->getCollectionId(),
'base_id' => $story->getBaseId(),
'thumbnail' => $this->listEmbeddableMedia($request, $story, $story->get_thumbnail()),
'uuid' => $story->getUuid(),
'metadatas' => [
'@entity@' => V1Controller::OBJECT_TYPE_STORY_METADATA_BAG,
'dc:contributor' => $format($caption, \databox_Field_DCESAbstract::Contributor),
'dc:coverage' => $format($caption, \databox_Field_DCESAbstract::Coverage),
'dc:creator' => $format($caption, \databox_Field_DCESAbstract::Creator),
'dc:date' => $format($caption, \databox_Field_DCESAbstract::Date),
'dc:description' => $format($caption, \databox_Field_DCESAbstract::Description),
'dc:format' => $format($caption, \databox_Field_DCESAbstract::Format),
'dc:identifier' => $format($caption, \databox_Field_DCESAbstract::Identifier),
'dc:language' => $format($caption, \databox_Field_DCESAbstract::Language),
'dc:publisher' => $format($caption, \databox_Field_DCESAbstract::Publisher),
'dc:relation' => $format($caption, \databox_Field_DCESAbstract::Relation),
'dc:rights' => $format($caption, \databox_Field_DCESAbstract::Rights),
'dc:source' => $format($caption, \databox_Field_DCESAbstract::Source),
'dc:subject' => $format($caption, \databox_Field_DCESAbstract::Subject),
'dc:title' => $format($caption, \databox_Field_DCESAbstract::Title),
'dc:type' => $format($caption, \databox_Field_DCESAbstract::Type),
],
'records' => $this->listRecords($request, array_values($story->getChildren($offset, $per_page)->get_elements())),
];
}
private function listEmbeddableMedia(Request $request, \record_adapter $record, \media_subdef $media)
{
if (!$media->is_physically_present()) {
return null;
}
if ($this->getAuthenticator()->isAuthenticated()) {
$acl = $this->getAclForUser();
if ($media->get_name() !== 'document'
&& false === $acl->has_access_to_subdef($record, $media->get_name())
) {
return null;
}
if ($media->get_name() === 'document'
&& !$acl->has_right_on_base($record->getBaseId(), \ACL::CANDWNLDHD)
&& !$acl->has_hd_grant($record)
) {
return null;
}
}
if ($media->get_permalink() instanceof \media_Permalink_Adapter) {
$permalink = $this->listPermalink($media->get_permalink());
} else {
$permalink = null;
}
$urlTTL = (int) $request->get(
'subdef_url_ttl',
$this->getConf()->get(['registry', 'general', 'default-subdef-url-ttl'])
);
if ($urlTTL < 0) {
$urlTTL = -1;
}
$issuer = $this->getAuthenticatedUser();
return [
'name' => $media->get_name(),
'permalink' => $permalink,
'height' => $media->get_height(),
'width' => $media->get_width(),
'filesize' => $media->get_size(),
'devices' => $media->getDevices(),
'player_type' => $media->get_type(),
'mime_type' => $media->get_mime(),
'substituted' => $media->is_substituted(),
'created_on' => $media->get_creation_date()->format(DATE_ATOM),
'updated_on' => $media->get_modification_date()->format(DATE_ATOM),
'url' => $this->app['media_accessor.subdef_url_generator']->generate($issuer, $media, $urlTTL),
'url_ttl' => $urlTTL,
];
}
private function listPermalink(\media_Permalink_Adapter $permalink)
{
$downloadUrl = $permalink->get_url();
$downloadUrl->getQuery()->set('download', '1');
return [
'created_on' => $permalink->get_created_on()->format(DATE_ATOM),
'id' => $permalink->get_id(),
'is_activated' => $permalink->get_is_activated(),
/** @Ignore */
'label' => $permalink->get_label(),
'updated_on' => $permalink->get_last_modified()->format(DATE_ATOM),
'page_url' => $permalink->get_page(),
'download_url' => (string)$downloadUrl,
'url' => (string)$permalink->get_url(),
];
}
/**
* @param Request $request
* @param RecordReferenceInterface[]|RecordReferenceCollection $records
* @return array
*/
private function listRecords(Request $request, $records)
{
if (!$records instanceof RecordReferenceCollection) {
$records = new RecordReferenceCollection($records);
}
$technicalData = $this->app['service.technical_data']->fetchRecordsTechnicalData($records);
$data = [];
foreach ($records->toRecords($this->getApplicationBox()) as $index => $record) {
$record->setTechnicalDataSet($technicalData[$index]);
$data[$index] = $this->listRecord($request, $record);
}
return $data;
}
/**
* Retrieve detailed information about one record
*
* @param Request $request
* @param \record_adapter $record
* @return array
*/
private function listRecord(Request $request, \record_adapter $record)
{
$technicalInformation = [];
foreach ($record->get_technical_infos()->getValues() as $name => $value) {
$technicalInformation[] = ['name' => $name, 'value' => $value];
}
$data = [
'databox_id' => $record->getDataboxId(),
'record_id' => $record->getRecordId(),
'mime_type' => $record->getMimeType(),
'title' => $record->get_title(),
'original_name' => $record->get_original_name(),
'updated_on' => $record->getUpdated()->format(DATE_ATOM),
'created_on' => $record->getCreated()->format(DATE_ATOM),
'collection_id' => $record->getCollectionId(),
'base_id' => $record->getBaseId(),
'sha256' => $record->getSha256(),
'thumbnail' => $this->listEmbeddableMedia($request, $record, $record->get_thumbnail()),
'technical_informations' => $technicalInformation,
'phrasea_type' => $record->getType(),
'uuid' => $record->getUuid(),
];
if ($request->attributes->get('_extended', false)) {
$data = array_merge($data, [
'subdefs' => $this->listRecordEmbeddableMedias($request, $record),
'metadata' => $this->listRecordMetadata($record),
'status' => $this->listRecordStatus($record),
'caption' => $this->listRecordCaption($record),
]);
}
return $data;
}
/**
* @param Request $request
* @param \record_adapter $record
* @return array
*/
private function listRecordEmbeddableMedias(Request $request, \record_adapter $record)
{
$subdefs = [];
foreach ($record->get_embedable_medias([], []) as $name => $media) {
if (null !== $subdef = $this->listEmbeddableMedia($request, $record, $media)) {
$subdefs[] = $subdef;
}
}
return $subdefs;
}
/**
* List all fields of given record
*
* @param \record_adapter $record
* @return array
*/
private function listRecordMetadata(\record_adapter $record)
{
$includeBusiness = $this->getAclForUser()->can_see_business_fields($record->getDatabox());
return $this->listRecordCaptionFields($record->get_caption()->get_fields(null, $includeBusiness));
}
/**
* @param \caption_field[] $fields
* @return array
*/
private function listRecordCaptionFields($fields)
{
$ret = [];
foreach ($fields as $field) {
$databox_field = $field->get_databox_field();
$fieldData = [
'meta_structure_id' => $field->get_meta_struct_id(),
'name' => $field->get_name(),
'labels' => [
'fr' => $databox_field->get_label('fr'),
'en' => $databox_field->get_label('en'),
'de' => $databox_field->get_label('de'),
'nl' => $databox_field->get_label('nl'),
],
];
foreach ($field->get_values() as $value) {
$data = [
'meta_id' => $value->getId(),
'value' => $value->getValue(),
];
$ret[] = $fieldData + $data;
}
}
return $ret;
}
/**
* Retrieve detailed information about one status
*
* @param \record_adapter $record
* @return array
*/
private function listRecordStatus(\record_adapter $record)
{
$ret = [];
foreach ($record->getStatusStructure() as $bit => $status) {
$ret[] = [
'bit' => $bit,
'state' => \databox_status::bitIsSet($record->getStatusBitField(), $bit),
];
}
return $ret;
}
/**
* @param \record_adapter $record
* @return array
*/
private function listRecordCaption(\record_adapter $record)
{
$includeBusiness = $this->getAclForUser()->can_see_business_fields($record->getDatabox());
$caption = [];
foreach ($record->get_caption()->get_fields(null, $includeBusiness) as $field) {
$caption[] = [
'meta_structure_id' => $field->get_meta_struct_id(),
'name' => $field->get_name(),
'value' => $field->get_serialized_values(';'),
];
}
return $caption;
}
/**
* Returns requested includes
*
* @param Request $request
* @return string[]
*/
private function resolveSearchIncludes(Request $request)
{
$includes = [
'results.stories.records'
];
if ($request->attributes->get('_extended', false)) {
if ($request->get('search_type') != SearchEngineOptions::RECORD_STORY) {
$includes = array_merge($includes, [
'results.stories.records.subdefs',
'results.stories.records.metadata',
'results.stories.records.caption',
'results.stories.records.status'
]);
}
else {
$includes = [ 'results.stories.caption' ];
}
$includes = array_merge($includes, [
'results.records.subdefs',
'results.records.metadata',
'results.records.caption',
'results.records.status'
]);
}
return $includes;
}
/**
* @param SearchEngineResult $result
* @param string[] $includes
* @param int $urlTTL
* @param int|null $story_max_records
* @return SearchResultView
*/
private function buildSearchView(SearchEngineResult $result, array $includes, $urlTTL, $story_max_records = null)
{
$references = new RecordReferenceCollection($result->getResults());
$records = new RecordCollection();
$stories = new RecordCollection();
foreach ($references->toRecords($this->getApplicationBox()) as $record) {
if ($record->isStory()) {
$stories[$record->getId()] = $record;
} else {
$records[$record->getId()] = $record;
}
}
$resultView = new SearchResultView($result);
if ($stories->count() > 0) {
$user = $this->getAuthenticatedUser();
$children = [];
foreach ($stories->getDataboxIds() as $databoxId) {
$storyIds = $stories->getDataboxRecordIds($databoxId);
$selections = $this->findDataboxById($databoxId)
->getRecordRepository()
->findChildren($storyIds, $user,1, $story_max_records);
$children[$databoxId] = array_combine($storyIds, $selections);
}
/** @var StoryView[] $storyViews */
$storyViews = [];
/** @var RecordView[] $childrenViews */
$childrenViews = [];
foreach ($stories as $index => $story) {
$storyView = new StoryView($story);
$selection = $children[$story->getDataboxId()][$story->getRecordId()];
$childrenView = $this->buildRecordViews($selection);
foreach ($childrenView as $view) {
$childrenViews[spl_object_hash($view)] = $view;
}
$storyView->setChildren($childrenView);
$storyViews[$index] = $storyView;
}
if (in_array('results.stories.thumbnail', $includes, true)) {
$subdefViews = $this->buildSubdefsViews($stories, ['thumbnail'], $urlTTL);
foreach ($storyViews as $index => $storyView) {
$storyView->setSubdefs($subdefViews[$index]);
}
}
if (in_array('results.stories.metadatas', $includes, true) ||
in_array('results.stories.caption', $includes, true)) {
$captions = $this->app['service.caption']->findByReferenceCollection($stories);
$canSeeBusiness = $this->retrieveSeeBusinessPerDatabox($stories);
$this->buildCaptionViews($storyViews, $captions, $canSeeBusiness);
}
$allChildren = new RecordCollection();
foreach ($childrenViews as $index => $childrenView) {
$allChildren[$index] = $childrenView->getRecord();
}
$names = in_array('results.stories.records.subdefs', $includes, true) ? null : ['thumbnail'];
$subdefViews = $this->buildSubdefsViews($allChildren, $names, $urlTTL);
$technicalDatasets = $this->app['service.technical_data']->fetchRecordsTechnicalData($allChildren);
foreach ($childrenViews as $index => $recordView) {
$recordView->setSubdefs($subdefViews[$index]);
$recordView->setTechnicalDataView(new TechnicalDataView($technicalDatasets[$index]));
}
if (array_intersect($includes, ['results.stories.records.metadata', 'results.stories.records.caption'])) {
$captions = $this->app['service.caption']->findByReferenceCollection($allChildren);
$canSeeBusiness = $this->retrieveSeeBusinessPerDatabox($allChildren);
$this->buildCaptionViews($childrenViews, $captions, $canSeeBusiness);
}
$resultView->setStories($storyViews);
}
if ($records->count() > 0) {
$names = in_array('results.records.subdefs', $includes, true) ? null : ['thumbnail'];
$recordViews = $this->buildRecordViews($records);
$subdefViews = $this->buildSubdefsViews($records, $names, $urlTTL);
$technicalDatasets = $this->app['service.technical_data']->fetchRecordsTechnicalData($records);
foreach ($recordViews as $index => $recordView) {
$recordView->setSubdefs($subdefViews[$index]);
$recordView->setTechnicalDataView(new TechnicalDataView($technicalDatasets[$index]));
}
if (array_intersect($includes, ['results.records.metadata', 'results.records.caption'])) {
$captions = $this->app['service.caption']->findByReferenceCollection($records);
$canSeeBusiness = $this->retrieveSeeBusinessPerDatabox($records);
$this->buildCaptionViews($recordViews, $captions, $canSeeBusiness);
}
$resultView->setRecords($recordViews);
}
return $resultView;
}
/**
* @param Request $request
* @return SearchEngineResult
*/
private function doSearch(Request $request)
{
$options = SearchEngineOptions::fromRequest($this->app, $request);
$options->setFirstResult((int)($request->get('offset_start') ?: 0));
$options->setMaxResults((int)$request->get('per_page') ?: 10);
$this->getSearchEngine()->resetCache();
$search_result = $this->getSearchEngine()->query((string)$request->get('query'), $options);
$this->getUserManipulator()->logQuery($this->getAuthenticatedUser(), $search_result->getQueryText());
// log array of collectionIds (from $options) for each databox
$collectionsReferencesByDatabox = $options->getCollectionsReferencesByDatabox();
foreach ($collectionsReferencesByDatabox as $sbid => $references) {
$databox = $this->findDataboxById($sbid);
$collectionsIds = array_map(function(CollectionReference $ref){return $ref->getCollectionId();}, $references);
$this->getSearchEngineLogger()->log($databox, $search_result->getQueryText(), $search_result->getTotal(), $collectionsIds);
}
$this->getSearchEngine()->clearCache();
return $search_result;
}
/**
* @return SearchEngineInterface
*/
private function getSearchEngine()
{
return $this->app['phraseanet.SE'];
}
/**
* @return UserManipulator
*/
private function getUserManipulator()
{
return $this->app['manipulator.user'];
}
/**
* @return SearchEngineLogger
*/
private function getSearchEngineLogger()
{
return $this->app['phraseanet.SE.logger'];
}
/**
* @param Request $request
* @return int
*/
private function resolveSubdefUrlTTL(Request $request)
{
$urlTTL = $request->query->get('subdef_url_ttl');
if (null !== $urlTTL) {
return (int)$urlTTL;
}
return $this->getConf()->get(['registry', 'general', 'default-subdef-url-ttl']);
}
/**
* @param RecordCollection|\record_adapter[] $references
* @return RecordView[]
*/
private function buildRecordViews($references)
{
if (!$references instanceof RecordCollection) {
$references = new RecordCollection($references);
}
$recordViews = [];
foreach ($references as $index => $record) {
$recordViews[$index] = new RecordView($record);
}
return $recordViews;
}
/**
* @param RecordReferenceInterface[]|RecordReferenceCollection|DataboxGroupable $references
* @param array|null $names
* @param int $urlTTL
* @return SubdefView[][]
*/
private function buildSubdefsViews($references, array $names = null, $urlTTL)
{
$subdefGroups = $this->app['service.media_subdef']
->findSubdefsByRecordReferenceFromCollection($references, $names);
$fakeSubdefs = [];
foreach ($subdefGroups as $index => $subdefGroup) {
if (!isset($subdefGroup['thumbnail'])) {
$fakeSubdef = new \media_subdef($this->app, $references[$index], 'thumbnail', true, []);
$fakeSubdefs[spl_object_hash($fakeSubdef)] = $fakeSubdef;
$subdefGroups[$index]['thumbnail'] = $fakeSubdef;
}
}
$allSubdefs = $this->mergeGroupsIntoOneList($subdefGroups);
$allPermalinks = \media_Permalink_Adapter::getMany(
$this->app,
array_filter($allSubdefs, function (\media_subdef $subdef) use ($fakeSubdefs) {
return !isset($fakeSubdefs[spl_object_hash($subdef)]);
})
);
$urls = $this->app['media_accessor.subdef_url_generator']
->generateMany($this->getAuthenticatedUser(), $allSubdefs, $urlTTL);
$subdefViews = [];
/** @var \media_subdef $subdef */
foreach ($allSubdefs as $index => $subdef) {
$subdefView = new SubdefView($subdef);
if (isset($allPermalinks[$index])) {
$subdefView->setPermalinkView(new PermalinkView($allPermalinks[$index]));
}
$subdefView->setUrl($urls[$index]);
$subdefView->setUrlTTL($urlTTL);
$subdefViews[spl_object_hash($subdef)] = $subdefView;
}
$reorderedGroups = [];
/** @var \media_subdef[] $subdefGroup */
foreach ($subdefGroups as $index => $subdefGroup) {
$reordered = [];
foreach ($subdefGroup as $subdef) {
$reordered[] = $subdefViews[spl_object_hash($subdef)];
}
$reorderedGroups[$index] = $reordered;
}
return $reorderedGroups;
}
/**
* @param array $groups
* @return array|mixed
*/
private function mergeGroupsIntoOneList(array $groups)
{
// Strips keys from the internal array
array_walk($groups, function (array &$group) {
$group = array_values($group);
});
if ($groups) {
return call_user_func_array('array_merge', $groups);
}
return [];
}
/**
* @param RecordReferenceInterface[]|DataboxGroupable $references
* @return array<int, bool>
*/
private function retrieveSeeBusinessPerDatabox($references)
{
if (!$references instanceof DataboxGroupable) {
$references = new RecordReferenceCollection($references);
}
$acl = $this->getAclForUser();
$canSeeBusiness = [];
foreach ($references->getDataboxIds() as $databoxId) {
$canSeeBusiness[$databoxId] = $acl->can_see_business_fields($this->findDataboxById($databoxId));
}
$rights = [];
foreach ($references as $index => $reference) {
$rights[$index] = $canSeeBusiness[$reference->getDataboxId()];
}
return $rights;
}
/**
* @param RecordView[] $recordViews
* @param \caption_record[] $captions
* @param bool[] $canSeeBusiness
*/
private function buildCaptionViews($recordViews, $captions, $canSeeBusiness)
{
foreach ($recordViews as $index => $recordView) {
$caption = $captions[$index];
$captionView = new CaptionView($caption);
$captionView->setFields($caption->get_fields(null, isset($canSeeBusiness[$index]) && (bool)$canSeeBusiness[$index]));
$recordView->setCaption($captionView);
}
}
}

View File

@@ -75,6 +75,9 @@ class EditController extends Controller
'format' => '', 'format' => '',
'explain' => '', 'explain' => '',
'tbranch' => $meta->get_tbranch(), '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() 'maxLength' => $meta->get_tag()
->getMaxLength(), ->getMaxLength(),
'minLength' => $meta->get_tag() 'minLength' => $meta->get_tag()

View File

@@ -70,7 +70,7 @@ class LazaretController extends Controller
public function getElement($file_id) public function getElement($file_id)
{ {
$ret = ['success' => false, 'message' => '', 'result' => []]; $ret = ['success' => false, 'message' => '', 'result' => []];
/* @var LazaretFile $lazaretFile */ /* @var LazaretFile $lazaretFile */
$lazaretFile = $this->getLazaretFileRepository()->find($file_id); $lazaretFile = $this->getLazaretFileRepository()->find($file_id);
@@ -126,6 +126,16 @@ class LazaretController extends Controller
$ret = $lazaretManipulator->add($file_id, $keepAttributes, $attributesToKeep); $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); return $this->app->json($ret);
} }
@@ -216,6 +226,7 @@ class LazaretController extends Controller
return $this->app->json($ret); return $this->app->json($ret);
} }
$postStatus = (array) $request->request->get('status');
$path = $this->app['tmp.lazaret.path'] . '/'; $path = $this->app['tmp.lazaret.path'] . '/';
$lazaretFileName = $path .$lazaretFile->getFilename(); $lazaretFileName = $path .$lazaretFile->getFilename();
@@ -233,6 +244,9 @@ class LazaretController extends Controller
'' ''
); );
// update status
$this->updateRecordStatus($record, $postStatus);
//Delete lazaret file //Delete lazaret file
$manager = $this->getEntityManager(); $manager = $this->getEntityManager();
$manager->remove($lazaretFile); $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 * @return LazaretFileRepository
*/ */
@@ -293,4 +336,32 @@ class LazaretController extends Controller
{ {
return $this->app['border-manager']; 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

@@ -193,7 +193,7 @@ class PushController extends Controller
'Validation from %user%', [ 'Validation from %user%', [
'%user%' => $this->getAuthenticatedUser()->getDisplayName(), '%user%' => $this->getAuthenticatedUser()->getDisplayName(),
])); ]));
$validation_description = $request->request->get('validation_description'); $validation_description = $request->request->get('message');
$participants = $request->request->get('participants'); $participants = $request->request->get('participants');

View File

@@ -341,10 +341,20 @@ class QueryController extends Controller
if ($result->getTotal() === 0) { if ($result->getTotal() === 0) {
$template = 'prod/results/help.html.twig'; $template = 'prod/results/help.html.twig';
} else { }
else {
$template = 'prod/results/records.html.twig'; $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 // add technical fields
@@ -382,7 +392,6 @@ class QueryController extends Controller
'labels' => $field->get_labels(), 'labels' => $field->get_labels(),
'type' => $field->get_type(), 'type' => $field->get_type(),
'field' => $field->get_name(), 'field' => $field->get_name(),
'query' => "field." . $field->get_name() . ":%s",
'trans_label' => $field->get_label($this->app['locale']), 'trans_label' => $field->get_label($this->app['locale']),
]; ];
$field->get_label($this->app['locale']); $field->get_label($this->app['locale']);

View File

@@ -0,0 +1,48 @@
<?php
namespace Alchemy\Phrasea\ControllerProvider\Api;
use Alchemy\Phrasea\Application as PhraseaApplication;
use Alchemy\Phrasea\Controller\Api\V3Controller;
use Alchemy\Phrasea\Core\Event\Listener\OAuthListener;
use Silex\Application;
use Silex\ControllerCollection;
use Silex\ControllerProviderInterface;
use Silex\ServiceProviderInterface;
class V3 extends Api implements ControllerProviderInterface, ServiceProviderInterface
{
const VERSION = '3.0.0';
public function register(Application $app)
{
$app['controller.api.v3'] = $app->share(function (PhraseaApplication $app) {
return (new V3Controller($app));
});
}
public function boot(Application $app)
{
}
public function connect(Application $app)
{
if (! $this->isApiEnabled($app)) {
return $app['controllers_factory'];
}
/** @var ControllerCollection $controllers */
$controllers = $app['controllers_factory'];
$controllers->before(new OAuthListener());
$controllers->get('/stories/{databox_id}/{record_id}/', 'controller.api.v3:getStoryAction')
->before('controller.api.v1:ensureCanAccessToRecord')
->assert('databox_id', '\d+')
->assert('record_id', '\d+');
$controllers->match('/search/', 'controller.api.v3:searchAction');
return $controllers;
}
}

View File

@@ -82,6 +82,11 @@ class Lazaret implements ControllerProviderInterface, ServiceProviderInterface
->assert('file_id', '\d+') ->assert('file_id', '\d+')
->bind('lazaret_thumbnail'); ->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; return $controllers;
} }
} }

View File

@@ -15,6 +15,7 @@ use Alchemy\Phrasea\Authentication\Context;
use Alchemy\Phrasea\Controller\Api\Result; use Alchemy\Phrasea\Controller\Api\Result;
use Alchemy\Phrasea\ControllerProvider\Api\V1; use Alchemy\Phrasea\ControllerProvider\Api\V1;
use Alchemy\Phrasea\ControllerProvider\Api\V2; use Alchemy\Phrasea\ControllerProvider\Api\V2;
use Alchemy\Phrasea\ControllerProvider\Api\V3;
use Alchemy\Phrasea\Core\Configuration\PropertyAccess; use Alchemy\Phrasea\Core\Configuration\PropertyAccess;
use Alchemy\Phrasea\Core\Event\ApiOAuth2EndEvent; use Alchemy\Phrasea\Core\Event\ApiOAuth2EndEvent;
use Alchemy\Phrasea\Core\Event\ApiOAuth2StartEvent; use Alchemy\Phrasea\Core\Event\ApiOAuth2StartEvent;
@@ -76,11 +77,13 @@ class OAuthListener
// Sets the Api Version // Sets the Api Version
$CalledController = $request->attributes->get('_controller'); $CalledController = $request->attributes->get('_controller');
if(mb_strpos($CalledController, 'controller.api.v1') !== FALSE){ if (mb_strpos($CalledController, 'controller.api.v1') !== FALSE) {
$request->attributes->set('api_version', V1::VERSION); $request->attributes->set('api_version', V1::VERSION);
}elseif(mb_strpos($CalledController, 'controller.api.v2') !== FALSE){ } elseif(mb_strpos($CalledController, 'controller.api.v2') !== FALSE) {
$request->attributes->set('api_version', V2::VERSION); $request->attributes->set('api_version', V2::VERSION);
}else{ } elseif(mb_strpos($CalledController, 'controller.api.v3') !== FALSE) {
$request->attributes->set('api_version', V3::VERSION);
} else {
$request->attributes->set('api_version', $oAuth2Account->getApiVersion()); $request->attributes->set('api_version', $oAuth2Account->getApiVersion());
} }

View File

@@ -78,6 +78,20 @@ class BorderManagerServiceProvider implements ServiceProviderInterface
$checkerObj->restrictToCollections($collections); $checkerObj->restrictToCollections($collections);
} }
if (isset($checker['compare-ignore-collections'])) {
$collections = [];
foreach ($checker['compare-ignore-collections'] as $base_id) {
try {
$collections[] = \collection::getByBaseId($app, $base_id);
} catch (\Exception $e) {
throw new \InvalidArgumentException('Invalid collection option');
}
}
$checkerObj->setCompareIgnoreCollections($collections);
}
$registeredCheckers[] = $checkerObj; $registeredCheckers[] = $checkerObj;
} catch (\InvalidArgumentException $e) { } catch (\InvalidArgumentException $e) {
$app['monolog']->error( $app['monolog']->error(

View File

@@ -16,7 +16,7 @@ class Version
/** /**
* @var string * @var string
*/ */
private $number = '4.1.0-alpha.15a'; private $number = '4.1.0-alpha.19a';
/** /**
* @var string * @var string

View File

@@ -36,6 +36,9 @@ final class DbalDataboxFieldRepository implements DataboxFieldRepository
'label_fr', 'label_fr',
'label_de', 'label_de',
'label_nl', 'label_nl',
'generate_cterms',
'gui_editable',
'gui_visible',
]; ];
/** @var DataboxFieldFactory */ /** @var DataboxFieldFactory */

View File

@@ -82,6 +82,34 @@ class LegacyRecordRepository implements RecordRepository
return $this->mapRecordsFromResultSet($result); return $this->mapRecordsFromResultSet($result);
} }
public function findBySha256WithExcludedCollIds($sha256, $excludedCollIds = [])
{
static $sql;
if (!$sql) {
$qb = $this->createSelectBuilder()
->where('sha256 = :sha256');
if (!empty($excludedCollIds)) {
$qb->andWhere($qb->expr()->notIn('coll_id', ':coll_id'));
}
$sql = $qb->getSQL();
}
$result = $this->databox->get_connection()->fetchAll($sql,
[
'sha256' => $sha256,
'coll_id' => $excludedCollIds
],
[
':coll_id' => Connection::PARAM_INT_ARRAY
]
);
return $this->mapRecordsFromResultSet($result);
}
/** /**
* @param string $uuid * @param string $uuid
* @return \record_adapter[] * @return \record_adapter[]
@@ -99,6 +127,40 @@ class LegacyRecordRepository implements RecordRepository
return $this->mapRecordsFromResultSet($result); return $this->mapRecordsFromResultSet($result);
} }
/**
* @param string $uuid
* @param array $excludedCollIds
* @return \record_adapter[]
*/
public function findByUuidWithExcludedCollIds($uuid, $excludedCollIds = [])
{
static $sql;
if (!$sql) {
$qb = $this->createSelectBuilder()
->where('uuid = :uuid')
;
if (!empty($excludedCollIds)) {
$qb->andWhere($qb->expr()->notIn('coll_id', ':coll_id'));
}
$sql = $qb->getSQL();
}
$result = $this->databox->get_connection()->fetchAll($sql,
[
'uuid' => $uuid,
'coll_id' => $excludedCollIds
],
[
':coll_id' => Connection::PARAM_INT_ARRAY
]
);
return $this->mapRecordsFromResultSet($result);
}
public function findByRecordIds(array $recordIds) public function findByRecordIds(array $recordIds)
{ {
static $sql; static $sql;
@@ -120,7 +182,7 @@ class LegacyRecordRepository implements RecordRepository
return $this->mapRecordsFromResultSet($result); return $this->mapRecordsFromResultSet($result);
} }
public function findChildren(array $storyIds, $user = null) public function findChildren(array $storyIds, $user = null, $offset = 1, $max_items = null)
{ {
if (!$storyIds) { if (!$storyIds) {
return []; return [];
@@ -129,25 +191,73 @@ class LegacyRecordRepository implements RecordRepository
$connection = $this->databox->get_connection(); $connection = $this->databox->get_connection();
$selects = $this->getRecordSelects(); $selects = $this->getRecordSelects();
array_unshift($selects, 's.rid_parent as story_id');
$builder = $connection->createQueryBuilder(); if ($max_items) {
$builder array_unshift($selects, 'sr.rid_parent as story_id');
->select($selects)
->from('regroup', 's')
->innerJoin('s', 'record', 'r', 'r.record_id = s.rid_child')
->where(
's.rid_parent IN (:storyIds)',
'r.parent_record_id = 0'
)
->setParameter('storyIds', $storyIds, Connection::PARAM_INT_ARRAY)
;
if (null !== $user) { $subBuilder = $connection->createQueryBuilder();
$this->addUserFilter($builder, $user);
$subBuilder
->select('s.*,
IF(@old_rid_parent != s.rid_parent, @cpt := 1, @cpt := @cpt+1) AS CPT')
->addSelect("IF(@old_rid_parent != s.rid_parent, IF(@old_rid_parent:=s.rid_parent,'NEW PARENT',0), '----------') AS Y")
->from('regroup', 's')
->where('s.rid_parent IN (:storyIds)')
->setParameter('storyIds', $storyIds, Connection::PARAM_INT_ARRAY)
->orderBy('s.rid_parent, s.ord')
;
$builder = $subBuilder->getConnection()->createQueryBuilder();
$builder->select($selects)
->from(sprintf('( %s )', $subBuilder->getSQL()), 'sr')
->innerJoin('sr', 'record', 'r', 'r.record_id = sr.rid_child')
->where('sr.CPT BETWEEN :offset AND :maxresult')
->andWhere('r.parent_record_id = 0')
->setParameter('offset', $offset)
->setParameter('maxresult', ($offset + $max_items -1))
->orderBy('story_id, sr.CPT')
;
if (null !== $user) {
$this->addUserFilter($builder, $user);
}
$connection->executeQuery('SET @cpt = 1');
$connection->executeQuery('SET @old_rid_parent = -1');
$data = $connection->fetchAll(
$builder->getSQL(),
array_merge($subBuilder->getParameters(), $builder->getParameters()),
array_merge($subBuilder->getParameterTypes(), $builder->getParameterTypes())
);
} else {
array_unshift($selects, 's.rid_parent as story_id');
$builder = $connection->createQueryBuilder();
$builder
->select($selects)
->from('regroup', 's')
->innerJoin('s', 'record', 'r', 'r.record_id = s.rid_child')
->where(
's.rid_parent IN (:storyIds)',
'r.parent_record_id = 0'
)
->orderBy('s.ord', 'ASC')
->setParameter('storyIds', $storyIds, Connection::PARAM_INT_ARRAY)
;
if (null !== $user) {
$this->addUserFilter($builder, $user);
}
$data = $connection->fetchAll($builder->getSQL(), $builder->getParameters(), $builder->getParameterTypes());
} }
$data = $connection->fetchAll($builder->getSQL(), $builder->getParameters(), $builder->getParameterTypes());
$records = $this->mapRecordsFromResultSet($data); $records = $this->mapRecordsFromResultSet($data);
$selections = array_map( $selections = array_map(

View File

@@ -26,12 +26,26 @@ interface RecordRepository
*/ */
public function findBySha256($sha256); public function findBySha256($sha256);
/**
* @param string $sha256
* @param array $excludedCollIds
* @return \record_adapter[]
*/
public function findBySha256WithExcludedCollIds($sha256, $excludedCollIds = []);
/** /**
* @param string $uuid * @param string $uuid
* @return \record_adapter[] * @return \record_adapter[]
*/ */
public function findByUuid($uuid); public function findByUuid($uuid);
/**
* @param string $uuid
* @param array $excludedCollIds
* @return \record_adapter[]
*/
public function findByUuidWithExcludedCollIds($uuid, $excludedCollIds = []);
/** /**
* @param array $recordIds * @param array $recordIds
* @return \record_adapter[] * @return \record_adapter[]
@@ -43,9 +57,11 @@ interface RecordRepository
* *
* @param int[] $storyIds * @param int[] $storyIds
* @param null|int|User $user * @param null|int|User $user
* @param int $offset
* @param null|int $max_items
* @return \set_selection[] * @return \set_selection[]
*/ */
public function findChildren(array $storyIds, $user = null); public function findChildren(array $storyIds, $user = null, $offset = 1, $max_items = null);
/** /**

View File

@@ -33,7 +33,7 @@ class Video extends Audio
$this->registerOption(new OptionType\Range($this->translator->trans('Frame Rate'), self::OPTION_FRAMERATE, 1, 200, 20)); $this->registerOption(new OptionType\Range($this->translator->trans('Frame Rate'), self::OPTION_FRAMERATE, 1, 200, 20));
$this->registerOption(new OptionType\Enum($this->translator->trans('Video Codec'), self::OPTION_VCODEC, ['libx264', 'libvpx', 'libtheora'], 'libx264')); $this->registerOption(new OptionType\Enum($this->translator->trans('Video Codec'), self::OPTION_VCODEC, ['libx264', 'libvpx', 'libtheora'], 'libx264'));
$this->unregisterOption(self::OPTION_ACODEC); $this->unregisterOption(self::OPTION_ACODEC);
$this->registerOption(new OptionType\Enum($this->translator->trans('Audio Codec'), self::OPTION_ACODEC, ['libfaac', 'libvo_aacenc', 'libmp3lame', 'libvorbis'], 'libfaac')); $this->registerOption(new OptionType\Enum($this->translator->trans('Audio Codec'), self::OPTION_ACODEC, ['libfaac', 'libvo_aacenc', 'libmp3lame', 'libvorbis', 'libfdk_aac'], 'libmp3lame'));
} }
public function getType() public function getType()

View File

@@ -12,6 +12,7 @@
namespace Alchemy\Phrasea\Model\Entities; namespace Alchemy\Phrasea\Model\Entities;
use Alchemy\Phrasea\Application; use Alchemy\Phrasea\Application;
use Alchemy\Phrasea\Border\Attribute\AttributeInterface;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation as Gedmo; use Gedmo\Mapping\Annotation as Gedmo;
use \record_adapter; use \record_adapter;
@@ -474,4 +475,32 @@ class LazaretFile
return $merged; 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->remove($lazaretFile);
$this->entityManager->flush(); $this->entityManager->flush();
$ret['result']['record_id'] = $record->getRecordId();
$ret['success'] = true; $ret['success'] = true;
} catch (\Exception $e) { } catch (\Exception $e) {
$ret['message'] = $this->app->trans('An error occured'); $ret['message'] = $this->app->trans('An error occured');

View File

@@ -232,7 +232,7 @@ class BasketRepository extends EntityRepository
$dql = "SELECT b\n" $dql = "SELECT b\n"
. "FROM Phraseanet:Basket b\n" . "FROM Phraseanet:Basket b\n"
. " JOIN b.elements e\n" . " JOIN b.elements e\n"
. "WHERE b.user = :usr_id AND b.pusher_id IS NOT NULL"; . "WHERE b.user = :usr_id AND b.pusher IS NOT NULL";
$params = [ $params = [
'usr_id' => $user->getId() 'usr_id' => $user->getId()
]; ];

View File

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

View File

@@ -323,7 +323,7 @@ class ElasticsearchOptions
"aggregated (2 values: fired = 0 or 1)" => -1, "aggregated (2 values: fired = 0 or 1)" => -1,
], ],
'output_formatter' => function($value) { '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; 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 // TODO Maybe replace nfkc_normalizer + asciifolding with icu_folding
'filter' => ['nfkc_normalizer', 'asciifolding'] '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 // Lang specific
'fr_full' => [ 'fr_full' => [
'type' => 'custom', 'type' => 'custom',
@@ -145,6 +155,12 @@ class Index
] ]
], ],
'tokenizer' => [ 'tokenizer' => [
'truncation_tokenizer' => [
"type" => "edgeNGram",
"min_gram" => "2",
"max_gram" => "15",
"token_chars" => [ "letter", "digit", "punctuation", "symbol" ]
],
'thesaurus_path' => [ 'thesaurus_path' => [
'type' => 'path_hierarchy' '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\Exception\Exception;
use Alchemy\Phrasea\SearchEngine\Elastic\RecordHelper; 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;
use Alchemy\Phrasea\SearchEngine\Elastic\Thesaurus\CandidateTerms; use Alchemy\Phrasea\SearchEngine\Elastic\Thesaurus\CandidateTerms;
use Alchemy\Phrasea\SearchEngine\Elastic\Thesaurus\Concept; use Alchemy\Phrasea\SearchEngine\Elastic\Thesaurus\Concept;
@@ -27,7 +28,7 @@ class ThesaurusHydrator implements HydratorInterface
private $thesaurus; private $thesaurus;
private $candidate_terms; 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->structure = $structure;
$this->thesaurus = $thesaurus; $this->thesaurus = $thesaurus;
@@ -42,7 +43,7 @@ class ThesaurusHydrator implements HydratorInterface
$fields = []; $fields = [];
$index_fields = []; $index_fields = [];
foreach ($structure as $name => $field) { foreach ($structure as $name => $field) {
$fields[$name] = $field->getThesaurusRoots(); $fields[$name] = $field; // ->getThesaurusRoots();
$index_fields[$name] = $field->getIndexField(); $index_fields[$name] = $field->getIndexField();
} }
// Hydrate records with concepts // Hydrate records with concepts
@@ -51,7 +52,13 @@ class ThesaurusHydrator implements HydratorInterface
} }
} }
private function hydrate(array &$record, array $fields, array $index_fields) /**
* @param array $record
* @param Field[] $fields
* @param array $index_fields
* @throws Exception
*/
private function hydrate(array &$record, $fields, array $index_fields)
{ {
if (!isset($record['databox_id'])) { if (!isset($record['databox_id'])) {
throw new Exception('Expected a record with the "databox_id" key set.'); throw new Exception('Expected a record with the "databox_id" key set.');
@@ -61,7 +68,14 @@ class ThesaurusHydrator implements HydratorInterface
$terms = array(); $terms = array();
$filters = array(); $filters = array();
$field_names = array(); $field_names = array();
foreach ($fields as $name => $root_concepts) { /** @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 // Loop through all values to prepare bulk query
$field_values = \igorw\get_in($record, explode('.', $index_fields[$name])); $field_values = \igorw\get_in($record, explode('.', $index_fields[$name]));
if ($field_values !== null) { if ($field_values !== null) {
@@ -84,12 +98,13 @@ class ThesaurusHydrator implements HydratorInterface
$bulk = $this->thesaurus->findConceptsBulk($terms, null, $filters, true); $bulk = $this->thesaurus->findConceptsBulk($terms, null, $filters, true);
foreach ($bulk as $offset => $item_concepts) { foreach ($bulk as $offset => $item_concepts) {
$name = $field_names[$offset];
if ($item_concepts && is_array($item_concepts) && count($item_concepts)>0) { if ($item_concepts && is_array($item_concepts) && count($item_concepts)>0) {
$name = $field_names[$offset];
foreach ($item_concepts as $concept) { foreach ($item_concepts as $concept) {
$record['concept_path'][$name][] = $concept->getPath(); $record['concept_path'][$name][] = $concept->getPath();
} }
} else { }
else {
$this->candidate_terms->insert($field_names[$offset], $values[$offset]); $this->candidate_terms->insert($field_names[$offset], $values[$offset]);
} }
} }

View File

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

View File

@@ -4,6 +4,11 @@ namespace Alchemy\Phrasea\SearchEngine\Elastic\Search;
class Escaper class Escaper
{ {
public function quoteWord($value)
{
return '"' . $this->escapeRaw($value) . '"';
}
public function escapeWord($value) public function escapeWord($value)
{ {
// Strip double quotes from values to prevent broken queries // 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'])) { if (!isset($bucket['key']) || !isset($bucket['doc_count'])) {
$this->throwAggregationResponseError(); $this->throwAggregationResponseError();
} }
$key = array_key_exists('key_as_string', $bucket) ? $bucket['key_as_string'] : $bucket['key'];
if($tf) { if($tf) {
// the field is one of the hardcoded tech fields // the field is one of the hardcoded tech fields
$value = [ $value = [
'value' => $valueFormatter($bucket['key']), 'value' => $valueFormatter($key),
'raw_value' => $bucket['key'], 'raw_value' => $key,
'count' => $bucket['doc_count'], 'count' => $bucket['doc_count'],
'query' => sprintf($tf['query'], $this->escaper->escapeWord($bucket['key'])) 'query' => sprintf($tf['query'], $this->escaper->escapeWord($key))
]; ];
} }
else { else {
// the field is a normal field // the field is a normal field
$value = [ $value = [
'value' => $bucket['key'], 'value' => $key,
'raw_value' => $bucket['key'], 'raw_value' => $key,
'count' => $bucket['doc_count'], '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\Field as ASTField;
use Alchemy\Phrasea\SearchEngine\Elastic\AST\Flag; use Alchemy\Phrasea\SearchEngine\Elastic\AST\Flag;
use Alchemy\Phrasea\SearchEngine\Elastic\Structure\Structure; use Alchemy\Phrasea\SearchEngine\Elastic\Structure\Structure;
use Alchemy\Phrasea\SearchEngine\SearchEngineOptions;
/** /**
* @todo Check for private fields and only search on them if allowed * @todo Check for private fields and only search on them if allowed
@@ -23,13 +24,23 @@ class QueryContext
private $queryLocale; private $queryLocale;
/** @var array */ /** @var array */
private $fields; 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->structure = $structure;
$this->locales = $locales; $this->locales = $locales;
$this->queryLocale = $queryLocale; $this->queryLocale = $queryLocale;
$this->fields = $fields; $this->fields = $fields;
$this->options = $options;
} }
public function narrowToFields(array $fields) 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; 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) private function localizeFieldName($field)
{ {
$fields = array(); $fields = array();

View File

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

View File

@@ -63,4 +63,26 @@ class StringUtils
return self::$transliterator->transliterate($string); 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; private $name;
/**
* @var int
*/
private $databox_id;
/** /**
* @var string * @var string
*/ */
@@ -43,6 +48,8 @@ class Field implements Typed
private $thesaurus_roots; private $thesaurus_roots;
private $generate_cterms;
private $used_by_collections; private $used_by_collections;
public static function createFromLegacyField(databox_field $field, $with = Structure::WITH_EVERYTHING) public static function createFromLegacyField(databox_field $field, $with = Structure::WITH_EVERYTHING)
@@ -71,10 +78,12 @@ class Field implements Typed
} }
return new self($field->get_name(), $type, [ return new self($field->get_name(), $type, [
'databox_id' => $databox->get_sbas_id(),
'searchable' => $field->is_indexable(), 'searchable' => $field->is_indexable(),
'private' => $field->isBusiness(), 'private' => $field->isBusiness(),
'facet' => $facet, 'facet' => $facet,
'thesaurus_roots' => $roots, 'thesaurus_roots' => $roots,
'generate_cterms' => $field->get_generate_cterms(),
'used_by_collections' => $databox->get_collection_unique_ids() 'used_by_collections' => $databox->get_collection_unique_ids()
]); ]);
} }
@@ -99,10 +108,12 @@ class Field implements Typed
{ {
$this->name = (string) $name; $this->name = (string) $name;
$this->type = $type; $this->type = $type;
$this->databox_id = \igorw\get_in($options, ['databox_id'], 0);
$this->is_searchable = \igorw\get_in($options, ['searchable'], true); $this->is_searchable = \igorw\get_in($options, ['searchable'], true);
$this->is_private = \igorw\get_in($options, ['private'], false); $this->is_private = \igorw\get_in($options, ['private'], false);
$this->facet = \igorw\get_in($options, ['facet']); $this->facet = \igorw\get_in($options, ['facet']);
$this->thesaurus_roots = \igorw\get_in($options, ['thesaurus_roots'], null); $this->thesaurus_roots = \igorw\get_in($options, ['thesaurus_roots'], null);
$this->generate_cterms = \igorw\get_in($options, ['generate_cterms'], false);
$this->used_by_collections = \igorw\get_in($options, ['used_by_collections'], []); $this->used_by_collections = \igorw\get_in($options, ['used_by_collections'], []);
Assertion::boolean($this->is_searchable); Assertion::boolean($this->is_searchable);
@@ -122,10 +133,12 @@ class Field implements Typed
public function withOptions(array $options) public function withOptions(array $options)
{ {
return new self($this->name, $this->type, $options + [ return new self($this->name, $this->type, $options + [
'databox_id' => $this->databox_id,
'searchable' => $this->is_searchable, 'searchable' => $this->is_searchable,
'private' => $this->is_private, 'private' => $this->is_private,
'facet' => $this->facet, 'facet' => $this->facet,
'thesaurus_roots' => $this->thesaurus_roots, 'thesaurus_roots' => $this->thesaurus_roots,
'generate_cterms' => $this->generate_cterms,
'used_by_collections' => $this->used_by_collections 'used_by_collections' => $this->used_by_collections
]); ]);
} }
@@ -150,6 +163,11 @@ class Field implements Typed
return sprintf('concept_path.%s', $this->name); return sprintf('concept_path.%s', $this->name);
} }
public function get_databox_id()
{
return $this->databox_id;
}
public function getType() public function getType()
{ {
return $this->type; return $this->type;
@@ -190,6 +208,11 @@ class Field implements Typed
return $this->thesaurus_roots; return $this->thesaurus_roots;
} }
public function get_generate_cterms()
{
return $this->generate_cterms;
}
/** /**
* Merge with another field, returning the new instance * Merge with another field, returning the new instance
* *

View File

@@ -14,6 +14,12 @@ final class GlobalStructure implements Structure
*/ */
private $fields = array(); private $fields = array();
/**
* @var Field[][]
*/
private $fieldsByDatabox = [];
/** /**
* @var Field[] * @var Field[]
* */ * */
@@ -119,6 +125,10 @@ final class GlobalStructure implements Structure
public function add(Field $field) 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(); $name = $field->getName();
if (isset($this->fields[$name])) { if (isset($this->fields[$name])) {
@@ -152,6 +162,11 @@ final class GlobalStructure implements Structure
return $this->fields; return $this->fields;
} }
public function getAllFieldsByDatabox($databox_id)
{
return $this->fieldsByDatabox[$databox_id];
}
/** /**
* @return Field[] * @return Field[]
*/ */

View File

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

View File

@@ -71,6 +71,8 @@ class SearchEngineOptions
protected $i18n; protected $i18n;
/** @var bool */ /** @var bool */
protected $stemming = true; protected $stemming = true;
/** @var bool */
protected $use_truncation = false;
/** @var string */ /** @var string */
protected $sort_by; protected $sort_by;
@@ -105,7 +107,8 @@ class SearchEngineOptions
'sort_ord', 'sort_ord',
'business_fields', 'business_fields',
'max_results', 'max_results',
'first_result' 'first_result',
'use_truncation',
]; ];
/** /**
@@ -217,6 +220,29 @@ class SearchEngineOptions
return $this; 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 * Return wheter the use of stemming is enabled or not
* *
@@ -542,6 +568,8 @@ class SearchEngineOptions
$options->setFields($databoxFields); $options->setFields($databoxFields);
$options->setDateFields($databoxDateFields); $options->setDateFields($databoxDateFields);
$options->setUseTruncation((Boolean) $request->get('truncation'));
return $options; return $options;
} }
@@ -628,6 +656,7 @@ class SearchEngineOptions
} }
}, },
'stemming' => $optionSetter('setStemming'), 'stemming' => $optionSetter('setStemming'),
'use_truncation' => $optionSetter('setUseTruncation'),
'date_fields' => function ($value, SearchEngineOptions $options) use ($fieldNormalizer) { 'date_fields' => function ($value, SearchEngineOptions $options) use ($fieldNormalizer) {
$options->setDateFields($fieldNormalizer($value)); $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('border_checker_from_fqcn', array($this, 'getCheckerFromFQCN')),
new \Twig_SimpleFunction('caption_field', array($this, 'getCaptionField')), new \Twig_SimpleFunction('caption_field', array($this, 'getCaptionField')),
new \Twig_SimpleFunction('caption_field_label', array($this, 'getCaptionFieldLabel')), 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('caption_field_order', array($this, 'getCaptionFieldOrder')),
new \Twig_SimpleFunction('flag_slugify', array(Flag::class, 'normalizeName')), new \Twig_SimpleFunction('flag_slugify', array(Flag::class, 'normalizeName')),
@@ -77,6 +78,29 @@ class PhraseanetExtension extends \Twig_Extension
return ''; 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) public function getCaptionField(RecordInterface $record, $field, $value)
{ {
if ($record instanceof ElasticsearchRecord) { if ($record instanceof ElasticsearchRecord) {

View File

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

View File

@@ -462,6 +462,9 @@ class databox extends base implements ThumbnailedElement
->set_aggregable((isset($field['aggregable']) ? (string) $field['aggregable'] : 0)) ->set_aggregable((isset($field['aggregable']) ? (string) $field['aggregable'] : 0))
->set_type($type) ->set_type($type)
->set_tbranch(isset($field['tbranch']) ? (string) $field['tbranch'] : '') ->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']) || (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_thumbtitle(isset($field['thumbtitle']) ? (string) $field['thumbtitle'] : (isset($field['thumbTitle']) ? $field['thumbTitle'] : '0'))
->set_report(isset($field['report']) ? (string) $field['report'] : '1') ->set_report(isset($field['report']) ? (string) $field['report'] : '1')
->save(); ->save();
@@ -1214,21 +1217,40 @@ class databox extends base implements ThumbnailedElement
$domct = $this->get_dom_cterms(); $domct = $this->get_dom_cterms();
if ($domct !== false) { if ($domct !== false) {
$changed = false;
$nodesToDel = []; $nodesToDel = [];
// loop on first level : "fields"
for($n = $domct->documentElement->firstChild; $n; $n = $n->nextSibling) { for($n = $domct->documentElement->firstChild; $n; $n = $n->nextSibling) {
if($n->nodeType == XML_ELEMENT_NODE && !($n->getAttribute('delbranch'))){ if($n->nodeType == XML_ELEMENT_NODE && !($n->getAttribute('delbranch'))){
$nodesToDel[] = $n; $nodesToDel2 = [];
// loop on 2nd level : "terms"
for($n2 = $n->firstChild; $n2; $n2 = $n2->nextSibling) {
// do not remove "rejected" candidates
if(substr($n2->getAttribute('id'), 0, 1) != 'R') {
$nodesToDel2[] = $n2;
}
}
foreach($nodesToDel2 as $n2) {
$n->removeChild($n2);
$changed = true;
}
// if a field has no more candidates, we can remove it
if(!($n->firstChild)) {
$nodesToDel[] = $n;
}
} }
} }
foreach($nodesToDel as $n) { foreach($nodesToDel as $n) {
$n->parentNode->removeChild($n); $n->parentNode->removeChild($n);
$changed = true;
} }
if(!empty($nodesToDel)) { if($changed) {
$this->saveCterms($domct); $this->saveCterms($domct);
} }
} }
} catch (\Exception $e) { }
catch (\Exception $e) {
// no-op
} }
} }

View File

@@ -43,6 +43,9 @@ class databox_field implements cache_cacheableInterface
protected $report; protected $report;
protected $type; protected $type;
protected $tbranch; protected $tbranch;
protected $generate_cterms;
protected $gui_editable;
protected $gui_visible;
protected $separator; protected $separator;
protected $thumbtitle; protected $thumbtitle;
@@ -166,6 +169,9 @@ class databox_field implements cache_cacheableInterface
$this->position = (int)$row['sorter']; $this->position = (int)$row['sorter'];
$this->type = $row['type'] ?: self::TYPE_STRING; $this->type = $row['type'] ?: self::TYPE_STRING;
$this->tbranch = $row['tbranch']; $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->VocabularyType = $row['VocabularyControlType'];
$this->VocabularyRestriction = (bool)$row['RestrictToVocabularyControl']; $this->VocabularyRestriction = (bool)$row['RestrictToVocabularyControl'];
@@ -306,6 +312,9 @@ class databox_field implements cache_cacheableInterface
`report` = :report, `report` = :report,
`type` = :type, `type` = :type,
`tbranch` = :tbranch, `tbranch` = :tbranch,
`generate_cterms` = :generate_cterms,
`gui_editable` = :gui_editable,
`gui_visible` = :gui_visible,
`sorter` = :position, `sorter` = :position,
`thumbtitle` = :thumbtitle, `thumbtitle` = :thumbtitle,
`VocabularyControlType` = :VocabularyControlType, `VocabularyControlType` = :VocabularyControlType,
@@ -329,6 +338,9 @@ class databox_field implements cache_cacheableInterface
':report' => $this->report ? '1' : '0', ':report' => $this->report ? '1' : '0',
':type' => $this->type, ':type' => $this->type,
':tbranch' => $this->tbranch, ':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, ':position' => $this->position,
':thumbtitle' => $this->thumbtitle, ':thumbtitle' => $this->thumbtitle,
':VocabularyControlType' => $this->getVocabularyControl() ? $this->getVocabularyControl()->getType() : null, ':VocabularyControlType' => $this->getVocabularyControl() ? $this->getVocabularyControl()->getType() : null,
@@ -380,6 +392,9 @@ class databox_field implements cache_cacheableInterface
$meta->setAttribute('aggregable', $this->aggregable); $meta->setAttribute('aggregable', $this->aggregable);
$meta->setAttribute('type', $this->type); $meta->setAttribute('type', $this->type);
$meta->setAttribute('tbranch', $this->tbranch); $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) { if ($this->multi) {
$meta->setAttribute('separator', $this->separator); $meta->setAttribute('separator', $this->separator);
} }
@@ -711,6 +726,39 @@ class databox_field implements cache_cacheableInterface
return $this; return $this;
} }
/**
* @param boolean $generate_cterms
* @return databox_field
*/
public function set_generate_cterms($generate_cterms)
{
$this->generate_cterms = $generate_cterms;
return $this;
}
/**
* @param boolean $gui_editable
* @return databox_field
*/
public function set_gui_editable($gui_editable)
{
$this->gui_editable = $gui_editable;
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 * @param string $separator
@@ -795,6 +843,33 @@ class databox_field implements cache_cacheableInterface
return $this->tbranch; return $this->tbranch;
} }
/**
*
* @return boolean
*/
public function get_generate_cterms()
{
return $this->generate_cterms;
}
/**
*
* @return boolean
*/
public function get_gui_editable()
{
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 * @param Boolean $all If set to false, returns a one-char separator to use for serialiation
* *
@@ -905,6 +980,9 @@ class databox_field implements cache_cacheableInterface
'sorter' => $this->position, 'sorter' => $this->position,
'thumbtitle' => $this->thumbtitle, 'thumbtitle' => $this->thumbtitle,
'tbranch' => $this->tbranch, 'tbranch' => $this->tbranch,
'generate_cterms' => $this->generate_cterms,
'gui_editable' => $this->gui_editable,
'gui_visible' => $this->gui_visible,
'separator' => $this->separator, 'separator' => $this->separator,
'required' => $this->required, 'required' => $this->required,
'report' => $this->report, 'report' => $this->report,
@@ -943,10 +1021,10 @@ class databox_field implements cache_cacheableInterface
} }
$sql = "INSERT INTO metadatas_structure $sql = "INSERT INTO metadatas_structure
(`id`, `name`, `src`, `readonly`, `required`, `indexable`, `type`, `tbranch`, (`id`, `name`, `src`, `readonly`, `gui_editable`,`gui_visible`, `required`, `indexable`, `type`, `tbranch`, `generate_cterms`,
`thumbtitle`, `multi`, `business`, `aggregable`, `thumbtitle`, `multi`, `business`, `aggregable`,
`report`, `sorter`, `separator`) `report`, `sorter`, `separator`)
VALUES (null, :name, '', 0, 0, 1, 'string', '', VALUES (null, :name, '', 0, 1, 1, 0, 1, 'string', '', 1,
null, 0, 0, 0, null, 0, 0, 0,
1, :sorter, '')"; 1, :sorter, '')";

View File

@@ -0,0 +1,67 @@
<?php
/*
* This file is part of Phraseanet
*
* (c) 2005-2016 Alchemy
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use Alchemy\Phrasea\Application;
class patch_410alpha17a implements patchInterface
{
/** @var string */
private $release = '4.1.0-alpha.17a';
/** @var array */
private $concern = [base::DATA_BOX];
/**
* {@inheritdoc}
*/
public function get_release()
{
return $this->release;
}
/**
* {@inheritdoc}
*/
public function getDoctrineMigrations()
{
return [];
}
/**
* {@inheritdoc}
*/
public function require_all_upgrades()
{
return false;
}
/**
* {@inheritdoc}
*/
public function concern()
{
return $this->concern;
}
/**
* {@inheritdoc}
*/
public function apply(base $databox, Application $app)
{
// -- done by xml schema --
// $sql = "ALTER TABLE `metadatas_structure` ADD `generate_cterms` INT(1) UNSIGNED NOT NULL DEFAULT '0' AFTER `tbranch`";
// $databox->get_connection()->executeQuery($sql);
// $sql = "ALTER TABLE `metadatas_structure` ADD `gui_editable` INT(1) UNSIGNED NOT NULL DEFAULT '0' AFTER `readonly`";
// $databox->get_connection()->executeQuery($sql);
return true;
}
}

View File

@@ -0,0 +1,69 @@
<?php
/*
* This file is part of Phraseanet
*
* (c) 2005-2019 Alchemy
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use Alchemy\Phrasea\Application;
class patch_410alpha19a implements patchInterface
{
/** @var string */
private $release = '4.1.0-alpha.19a';
/** @var array */
private $concern = [base::APPLICATION_BOX];
/**
* Returns the release version.
*
* @return string
*/
public function get_release()
{
return $this->release;
}
/**
* {@inheritdoc}
*/
public function concern()
{
return $this->concern;
}
/**
* {@inheritdoc}
*/
public function require_all_upgrades()
{
return false;
}
/**
* {@inheritdoc}
*/
public function getDoctrineMigrations()
{
return [];
}
/**
* {@inheritdoc}
*/
public function apply(base $appbox, Application $app)
{
// remove all and last in default query
$sql = "UPDATE UserSettings SET value = '' WHERE name = 'start_page_query' AND lower(trim(value)) in ('all','last')";
$stmt = $appbox->get_connection()->prepare($sql);
$stmt->execute();
$stmt->closeCursor();
return true;
}
}

View File

@@ -1685,6 +1685,43 @@ class record_adapter implements RecordInterface, cache_cacheableInterface
return $records; return $records;
} }
public static function getRecordsByOriginalnameWithExcludedCollIds(databox $databox, $original_name, $caseSensitive = false, $offset_start = 0, $how_many = 10, $excludedCollIds = [])
{
$offset_start = max(0, (int)$offset_start);
$how_many = max(1, (int)$how_many);
$collate = $caseSensitive ? 'utf8_bin' : 'utf8_unicode_ci';
$qb = $databox->get_connection()->createQueryBuilder()
->select('record_id')
->from('record')
->where('originalname = :original_name COLLATE :collate')
;
$params = ['original_name' => $original_name, 'collate' => $collate];
$types = [];
if (!empty($excludedCollIds)) {
$qb->andWhere($qb->expr()->notIn('coll_id', ':coll_id'));
$params['coll_id'] = $excludedCollIds;
$types[':coll_id'] = Connection::PARAM_INT_ARRAY;
}
$sql = $qb->setFirstResult($offset_start)
->setMaxResults($how_many)
->getSQL()
;
$rs = $databox->get_connection()->fetchAll($sql, $params, $types);
$records = [];
foreach ($rs as $row) {
$records[] = $databox->get_record($row['record_id']);
}
return $records;
}
/** /**
* @return set_selection|record_adapter[] * @return set_selection|record_adapter[]
* @throws Exception * @throws Exception
@@ -1697,17 +1734,20 @@ class record_adapter implements RecordInterface, cache_cacheableInterface
} }
/** /**
* @param int $offset
* @param null|int $max_items
*
* @return set_selection|record_adapter[] * @return set_selection|record_adapter[]
* @throws Exception * @throws Exception
* @throws \Doctrine\DBAL\DBALException * @throws \Doctrine\DBAL\DBALException
*/ */
public function getChildren() public function getChildren($offset = 1, $max_items = null)
{ {
if (!$this->isStory()) { if (!$this->isStory()) {
throw new Exception('This record is not a grouping'); throw new Exception('This record is not a grouping');
} }
$selections = $this->getDatabox()->getRecordRepository()->findChildren([$this->getRecordId()]); $selections = $this->getDatabox()->getRecordRepository()->findChildren([$this->getRecordId()], null, $offset, $max_items);
return reset($selections); return reset($selections);
} }

View File

@@ -2033,6 +2033,30 @@
<default></default> <default></default>
<comment></comment> <comment></comment>
</field> </field>
<field>
<name>generate_cterms</name>
<type>int(1) unsigned</type>
<null></null>
<extra></extra>
<default>1</default>
<comment></comment>
</field>
<field>
<name>gui_editable</name>
<type>int(1) unsigned</type>
<null></null>
<extra></extra>
<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> </fields>
<indexes> <indexes>
<index> <index>

View File

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

View File

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

View File

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

View File

@@ -65,7 +65,7 @@
"normalize-css": "^2.1.0", "normalize-css": "^2.1.0",
"npm": "^6.0.0", "npm": "^6.0.0",
"npm-modernizr": "^2.8.3", "npm-modernizr": "^2.8.3",
"phraseanet-production-client": "0.34.72-d", "phraseanet-production-client": "^0.34.86-d",
"requirejs": "^2.3.5", "requirejs": "^2.3.5",
"tinymce": "^4.0.28", "tinymce": "^4.0.28",
"underscore": "^1.8.3", "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"?> <?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"> <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> <header>
<tool tool-id="JMSTranslationBundle" tool-name="JMSTranslationBundle" tool-version="1.1.0-DEV"/> <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> <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"?> <?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"> <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> <header>
<tool tool-id="JMSTranslationBundle" tool-name="JMSTranslationBundle" tool-version="1.1.0-DEV"/> <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> <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"?> <?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"> <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> <header>
<tool tool-id="JMSTranslationBundle" tool-name="JMSTranslationBundle" tool-version="1.1.0-DEV"/> <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> <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"?> <?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"> <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> <header>
<tool tool-id="JMSTranslationBundle" tool-name="JMSTranslationBundle" tool-version="1.1.0-DEV"/> <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> <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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

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

@@ -1,13 +1,3 @@
@font-face {
font-family: 'icomoon';
src: url('fonts/icomoon.eot?81kpnc');
src: url('fonts/icomoon.eot?81kpnc#iefix') format('embedded-opentype'),
url('fonts/icomoon.ttf?81kpnc') format('truetype'),
url('fonts/icomoon.woff?81kpnc') format('woff'),
url('fonts/icomoon.svg?81kpnc#icomoon') format('svg');
font-weight: normal;
font-style: normal;
}
[class^="icon-"], [class*=" icon-"] { [class^="icon-"], [class*=" icon-"] {
/* use !important to prevent issues with browser extensions that change fonts */ /* use !important to prevent issues with browser extensions that change fonts */

View File

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

View File

@@ -1,58 +1,99 @@
$iconsPath: '../../../assets/common/images/icons/'; $iconsPath: '../../../assets/common/images/icons/';
ul.image_set{ $feedbackColor : #8bc34a;
padding:0; $basketColor : #2196f3 ;
margin:0; ul.image_set {
border:none; padding: 0;
width:100%; margin: 0;
border:none; border: none;
width: 100%;
border: none;
list-style-type: none; list-style-type: none;
display: flex;
flex-wrap: wrap;
} }
li.image_box a img{
position:relative; li.image_box a img {
vertical-align:middle; position: relative;
border:none; border: none;
width: auto !important;
height: 100% !important;
top: 0 !important;
object-fit: contain;
} }
li.image_box a{
padding:0; li.image_box a {
text-shadow:0; padding: 0;
margin:0; text-shadow: 0;
margin: 0;
width: 100%;
} }
li.image_box{
width:80px; li.image_box {
height:80px; width: 18%;
position:relative; position: relative;
float:left; margin: 0 2.5% 15px 0;
margin:0px 15px 15px 0px; padding: 0;
padding:0px; display: flex;
text-align:center; 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;
}
}
} }
a.no.active_choice{
background-color:red; @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; color: #fff;
} }
a.active_choice{
a.active_choice {
background-color: #53b401; background-color: #53b401;
color: #fff; color: #fff;
} }
.valid_choice{ .valid_choice {
position:absolute; position: absolute;
bottom:0; bottom: 0;
right:0; right: 0;
width:16px; width: 16px;
height:16px; height: 16px;
z-index:2; z-index: 2;
} }
.valid_choice.agree{ .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'); .valid_choice.disagree {
background: #d0021b;
border-top-left-radius: 100px;
} }
.thumb_wrapper { .thumb_wrapper {
text-align:center; text-align: center;
} }
@media screen and (orientation: landscape) { @media screen and (orientation: landscape) {
@@ -70,3 +111,211 @@ a.active_choice{
} }
} }
.ui-footer .ui-title, .ui-header .ui-title {
font-size: 18px;
}
.ui-listview .ui-li-has-thumb .ui-li-thumb, .ui-listview .ui-li-has-thumb > .ui-btn > img:first-child, .ui-listview .ui-li-has-thumb > img:first-child {
top: 50%;
transform: translateY(-50%);
}
/*user status*/
.menu-bar-item {
background: #f0f0f0;
padding: 12px 15px;
text-align: left;
position: relative;
.icomoon {
font-size: 17px;
position: absolute;
left: 23px;
top: 50%;
transform: translateY(-50%);
}
.ui-link {
color: #4f4f4f !important;
text-decoration: none !important;
.text {
padding-left: 31px;
color: #4f4f4f !important;
text-decoration: none !important;
font-weight: 700;
font-size: 13px;
}
}
}
/*new design of lightbox*/
/*main nav */
#lightbox-menu {
li {
margin-bottom: 30px;
.ui-li-count {
border-radius: 3px !important;
border-top-left-radius: 0 !important;
border-bottom-left-radius: 0 !important;
border: 0;
color: #fff;
text-shadow: none;
padding: 18px 23px;
top: 0;
right: 0;
margin: 0;
font-size: 22px;
min-width: 25px;
background: $feedbackColor;
}
&.ui-first-child {
margin-top: 15px;
}
&.ui-last-child {
a {
&:before {
background: $basketColor;
}
&:hover, &:active, &:focus {
color: $basketColor;
}
}
.ui-li-count {
background: $basketColor;
}
}
}
a {
padding: 22px 25px;
font-size: 20px;
border-radius: 3px !important;
box-shadow: 0 0 6px 0 rgba(0, 0, 0, 0.19);
background-color: #eeeeee;
&:hover, &:active, &:focus {
color: $feedbackColor;
}
&:after {
content: none !important;
}
&:before {
content: "";
position: absolute;
display: block;
width: 100%;
height: 4px;
bottom: 0;
background: $feedbackColor;
left: 0;
border-radius: 3px;
}
}
}
.lightbox-bottom-btn {
margin-top: 50px;
.ui-btn {
font-size: 15px;
}
}
#validation, #baskets {
.ui-content {
padding-left: 0;
padding-right: 0;
}
}
.basket-title {
background: $feedbackColor;
padding: 12px 30px;
font-size: 17px;
margin: 0;
text-shadow: none;
color: #fff;
font-weight: 700;
#baskets & {
background: $basketColor;
}
span {
float: right;
}
}
.feed-list {
li {
height: 71px;
padding-left: 109px!important;
padding-right: 80px!important;
border-color: #d4d4d4!important;
text-overflow: inherit!important;
white-space: inherit!important;
}
.lightbox-img {
width: 90px;
height: 90px;
background-color: #ededed;
position: absolute;
left: 0;
top: 0;
display: flex;
flex-wrap: wrap;
img {
display: flex;
align-items: center;
width: 90px;
max-width: 100%;
max-height: 100%;
object-fit: contain;
}
}
h3 {
display: flex!important;
align-items: center;
height: 71px;
margin: 0!important;
font-size: 15px;
font-weight: bold!important;
text-overflow: inherit!important;
white-space: inherit!important;
a {
position: relative;
color: #313131!important;
width: 100%;
padding-right: 18px;
&:after {
content: "\e96c";
font-family: icomoon;
position: absolute;
right: 0;
font-size: 20px;
line-height: 1;
top: 50%;
transform: translateY(-50%);
}
}
}
span {
font-size: 17px;
color: #fff!important;
display: inline-block;
text-shadow: none!important;
top: 0;
width: 52px;
right: 0;
line-height: 25px;
padding: 31px 0px;
margin: 0;
border-radius: 0;
&.validation {
background: $feedbackColor!important;
}
&.baskets {
background: $basketColor!important;
}
}
}
.lightbox-msg {
font-size: 14px;
color: #313131;
padding: 6px 0 6px 30px;
}

View File

@@ -1,5 +1,28 @@
@import './jquery-mobile/jquery-validator'; @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 { .nav_button {
width: 90%; width: 90%;
margin: 0px auto; margin: 0px auto;
@@ -24,4 +47,677 @@
#right-btn { #right-btn {
float: right; 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-controlgroup, fieldset.ui-controlgroup {
margin: 10px 0!important;
}
.ui-listview.lightbox-list-view {
margin-top: 0;
margin-bottom: 0;
& > .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;
iframe {
min-height: 240px;
width: auto;
}
}
/*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; 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

@@ -0,0 +1,17 @@
{% set configuration = app['conf'].get(['registry', 'custom-links']) %}
{% if app.getAuthenticator().isAuthenticated() %}
<div class="menu-bar-item">
{% if app.getAuthenticatedUser().isGuest %}
<span class="icomoon icon-agree">&#xe950;</span>
<span class="text">{{ 'Guest' | trans }}</span>
{% else %}
<a target="_blank" href="{{ path('account') }}"
title="{{ 'login:: Mon compte' | trans }}">
<span class="icomoon icon-agree">&#xe950;</span>
<span class="text">{{ app.getAuthenticatedUser().getDisplayName() }}</span>
</a>
{% endif %}
</div>
{% endif %}

View File

@@ -26,7 +26,7 @@
#content { #content {
position: fixed; position: fixed;
top:50px; top:90px;
bottom: 0; bottom: 0;
right: 0; right: 0;
left: 0; left: 0;
@@ -40,18 +40,19 @@
{% block content %} {% block content %}
{% set record = basket_element.getRecord(app) %} {% set record = basket_element.getRecord(app) %}
<div data-role="page" id="page"> <div data-role="page" id="page">
<div data-role="header"> <div data-role="header">
<a href="{{ path('lightbox_validation', { 'basket' : basket_element.getBasket().getId() }) }}" <a href="{{ path('lightbox_validation', { 'basket' : basket_element.getBasket().getId() }) }}"
data-icon="arrow-l">{{ 'Back' | trans }}</a> data-icon="arrow-l">{{ 'Back' | trans }}</a>
<h1>{{basket_element.getOrd()}} - {{record.get_title()}}</h1> <h1>{{basket_element.getOrd()}} - {{record.get_title()}}</h1>
<a rel="external" href="{{ path('lightbox') }}" data-icon="home" data-iconpos="notext" data-direction="reverse" <a rel="external" href="{{ path('lightbox') }}" data-icon="home" data-iconpos="notext" data-direction="reverse"
class="ui-btn-right jqm-home">{{ 'Home' | trans }}</a> class="ui-btn-right jqm-home">{{ 'Home' | trans }}</a>
</div> </div>
<div id="content" data-role="content"> <div class="lightbox-user-info">
{{ thumbnail.format100percent(record.get_preview()) }} {% include 'common/menubar.html.twig' %}
<div class="nav_button"> </div>
<div id="content" data-role="content">
<div class="nav_button">
{% if prevId != NULL %} {% if prevId != NULL %}
<a data-ajax="false" id="left-btn" <a data-ajax="false" id="left-btn"
href="{{ path('lightbox_ajax_load_basketelement', { 'sselcont_id' : prevId }) }}" href="{{ path('lightbox_ajax_load_basketelement', { 'sselcont_id' : prevId }) }}"
@@ -63,30 +64,31 @@
class="ui-btn ui-shadow ui-corner-all ui-icon-carat-r ui-btn-icon-notext">Right</a> class="ui-btn ui-shadow ui-corner-all ui-icon-carat-r ui-btn-icon-notext">Right</a>
{% endif %} {% endif %}
<div style="clear: both;"></div> <div style="clear: both;"></div>
</div> </div>
{% if basket_element.getBasket().getValidation() %} <div class="center-image">{{ thumbnail.format100percent(record.get_preview()) }}</div>
{% if basket_element.getBasket().getValidation().getParticipant(app.getAuthenticatedUser()).getCanAgree() %} {% if basket_element.getBasket().getValidation() %}
<fieldset data-role="controlgroup" data-type="horizontal" style="text-align:center;"> {% if basket_element.getBasket().getValidation().getParticipant(app.getAuthenticatedUser()).getCanAgree() %}
<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" /> <fieldset data-role="controlgroup" data-type="horizontal" style="text-align:center;">
<label class="agreement_radio" style="width:110px;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" />
for="radio-view-yes_{{ basket_element.getId() }}">{{ 'validation:: OUI' | trans }}</label> <label class="agreement_radio" style="width:110px;text-align:center;"
<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" /> for="radio-view-yes_{{ basket_element.getId() }}">{{ 'validation:: OUI' | trans }}</label>
<label class="agreement_radio" style="width:110px;text-align:center;" <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" />
for="radio-view-no_{{ basket_element.getId() }}">{{ 'validation:: NON' | trans }}</label> <label class="agreement_radio" style="width:110px;text-align:center;"
</fieldset> for="radio-view-no_{{ basket_element.getId() }}">{{ 'validation:: NON' | trans }}</label>
{% endif %} </fieldset>
<div style="text-align:center;margin:0 0 1em 0"> {% endif %}
<a href="{{ path('lightbox_ajax_note_form', { 'sselcont_id' : basket_element.getId() }) }}" data-role="button" data-inline="true" data-rel="dialog" data-transition="slidedown"> <div style="text-align:center;margin:20px 0">
{{ 'validation:: editer ma note' | trans }} <a style="margin: 0" href="{{ path('lightbox_ajax_note_form', { 'sselcont_id' : basket_element.getId() }) }}" data-role="button" data-inline="true" data-rel="dialog" data-transition="slidedown">
</a> {{ 'validation:: editer ma note' | trans }}
</div> </a>
<div> </div>
<ul data-role="listview" id="notes_{{basket_element.getId()}}"> <div>
{% include 'lightbox/sc_note.html.twig' %} <ul data-role="listview" class="lightbox-list-view" id="notes_{{basket_element.getId()}}">
</ul> {% include 'lightbox/sc_note.html.twig' %}
</div> </ul>
{% endif %} </div>
</div> {% endif %}
</div>
<div data-role="footer"> <div data-role="footer">
</div> </div>

View File

@@ -17,7 +17,7 @@
#content { #content {
position: fixed; position: fixed;
top:50px; top:90px;
bottom: 0; bottom: 0;
right: 0; right: 0;
left: 0; left: 0;
@@ -36,9 +36,12 @@
<h1>{{feed_element.getOrd()}} - {{record.get_title()}}</h1> <h1>{{feed_element.getOrd()}} - {{record.get_title()}}</h1>
<a rel="external" href="{{ path('lightbox') }}" data-icon="home" data-iconpos="notext" data-direction="reverse" class="ui-btn-right jqm-home">Home</a> <a rel="external" href="{{ path('lightbox') }}" data-icon="home" data-iconpos="notext" data-direction="reverse" class="ui-btn-right jqm-home">Home</a>
</div> </div>
<div class="lightbox-user-info">
{% include 'common/menubar.html.twig' %}
</div>
<div id="content" data-role="content"> <div id="content" data-role="content">
{{ thumbnail.format100percent(record.get_preview()) }} {{ thumbnail.format100percent(record.get_preview()) }}
</div> </div>
<div data-role="footer"> <div data-role="footer">
</div> </div>
{% endblock %} {% endblock %}

View File

@@ -40,18 +40,21 @@
<h1>{{module_name}}</h1> <h1>{{module_name}}</h1>
<a href="#about" class="ui-btn-right">{{ 'a propos' | trans }}</a> <a href="#about" class="ui-btn-right">{{ 'a propos' | trans }}</a>
</div> </div>
<div class="lightbox-user-info">
{% include 'common/menubar.html.twig' %}
</div>
<div data-role="content"> <div data-role="content">
<ul data-role="listview" style="width:100%;margin: 0;"> <ul id="lightbox-menu" data-role="listview" style="width:100%;margin: 0;">
<li> <li class="lightbox-item">
<a href="#validation">{{ 'Validations' | trans }}</a> <a href="#validation">{{ 'Validations' | trans }}</a>
<span class="ui-li-count">{{_self.valid_baskets_length(baskets_collection)}}</span> <span class="ui-li-count">{{_self.valid_baskets_length(baskets_collection)}}</span>
</li> </li>
<li> <li class="lightbox-item">
<a href="#baskets">{{ 'Paniers' | trans }}</a> <a href="#baskets">{{ 'Paniers' | trans }}</a>
<span class="ui-li-count">{{_self.baskets_length(baskets_collection)}}</span> <span class="ui-li-count">{{_self.baskets_length(baskets_collection)}}</span>
</li> </li>
</ul> </ul>
<div style="margin:2em 0 0 0"> <div class="lightbox-bottom-btn">
<a href="{{ path ('logout') }}" data-role="button" rel="external">{{ 'Deconnexion' | trans }}</a> <a href="{{ path ('logout') }}" data-role="button" rel="external">{{ 'Deconnexion' | trans }}</a>
</div> </div>
</div> </div>
@@ -65,6 +68,9 @@
<a href="#home" data-rel="back" data-icon="arrow-l">{{ 'Back' | trans }}</a> <a href="#home" data-rel="back" data-icon="arrow-l">{{ 'Back' | trans }}</a>
<h1>{{ 'a propos' | trans }}</h1> <h1>{{ 'a propos' | trans }}</h1>
</div> </div>
<div class="lightbox-user-info">
{% include 'common/menubar.html.twig' %}
</div>
<div data-role="content"> <div data-role="content">
<p>Phraseanet Version {{ app['phraseanet.version'].getName() }} - {{ app['phraseanet.version'].getNumber() }}</p> <p>Phraseanet Version {{ app['phraseanet.version'].getName() }} - {{ app['phraseanet.version'].getNumber() }}</p>
<p> <p>
@@ -83,27 +89,38 @@
<div id="validation" data-role="page"> <div id="validation" data-role="page">
<div data-role="header"> <div data-role="header">
<a rel="external" href="#home" data-icon="arrow-l">{{ 'Back' | trans }}</a> <a rel="external" href="#home" data-icon="arrow-l">{{ 'Back' | trans }}</a>
<h1>{{ 'Validations' | trans }}</h1> <h1>{{ module_name }}</h1>
</div> </div>
<div class="lightbox-user-info">
{% include 'common/menubar.html.twig' %}
</div>
<div data-role="content"> <div data-role="content">
<p> <p class="basket-title">
{{ 'Voici vos validations en cours' | trans }} {{ 'Validations' | trans }}
</p> <span>{{_self.valid_baskets_length(baskets_collection)}}</span>
<ul data-role="listview" style="width:100%;margin: 0;"> </p>
{% for basket in baskets_collection %} <p class="lightbox-msg">
{% if basket.getValidation() %} {{ 'Voici vos validations en cours' | trans }}
{% set basket_length = basket.getElements().count() %} </p>
<li> <div class="feed-list">
{% if basket.getElements().first() %} <ul data-role="listview" style="width:100%;margin: 0;">
<img src="{{ basket.getElements().first().getRecord(app).get_thumbnail.get_url()}}" /> {% for basket in baskets_collection | sort | reverse%}
{% endif %} {% if basket.getValidation() %}
<h3><a href="{{ path('lightbox_validation', { 'basket' : basket.getId() }) }}">{{basket.getName()}}</a></h3> {% set basket_length = basket.getElements().count() %}
<p>{{ basket.getDescription() }}</p> <li>
<span class="ui-li-count">{{ basket_length }}</span> {% if basket.getElements().first() %}
</li> <div class="lightbox-img">
{% endif %} <img src="{{ basket.getElements().first().getRecord(app).get_thumbnail.get_url()}}" />
{% endfor %} </div>
</ul> {% endif %}
<h3><a href="{{ path('lightbox_validation', { 'basket' : basket.getId() }) }}">{{basket.getName()}}</a></h3>
{#<p>{{ basket.getDescription() }}</p>#}
<span class="ui-li-count validation">{{ basket_length }}</span>
</li>
{% endif %}
{% endfor %}
</ul>
</div>
</div> </div>
<div data-role="footer"> <div data-role="footer">
@@ -113,27 +130,38 @@
<div id="baskets" data-role="page"> <div id="baskets" data-role="page">
<div data-role="header"> <div data-role="header">
<a rel="external" href="#home" data-icon="arrow-l">{{ 'Back' | trans }}</a> <a rel="external" href="#home" data-icon="arrow-l">{{ 'Back' | trans }}</a>
<h1>{{ 'Paniers' | trans }}</h1> <h1>{{module_name}}</h1>
</div> </div>
<div class="lightbox-user-info">
{% include 'common/menubar.html.twig' %}
</div>
<div data-role="content"> <div data-role="content">
<p> <p class="basket-title">
{{ 'Paniers' | trans }}
<span>{{_self.baskets_length(baskets_collection)}}</span>
</p>
<p class="lightbox-msg">
{{ 'Voici vos paniers' | trans }} {{ 'Voici vos paniers' | trans }}
</p> </p>
<ul data-role="listview" style="width:100%;margin: 0;"> <div class="feed-list">
{% for basket in baskets_collection %} <ul data-role="listview" style="width:100%;margin: 0;">
{% if basket.getValidation() is empty %} {% for basket in baskets_collection | sort | reverse %}
{% set basket_length = basket.getElements().count() %} {% if basket.getValidation() is empty %}
<li> {% set basket_length = basket.getElements().count() %}
{% if basket.getElements().first() %} <li>
<img src="{{ basket.getElements().first().getRecord(app).get_thumbnail.get_url()}}" /> {% if basket.getElements().first() %}
{% endif %} <div class="lightbox-img">
<h3><a href="{{ path('lightbox_validation', { 'basket' : basket.getId() }) }}">{{ basket.getName() }}</a></h3> <img src="{{ basket.getElements().first().getRecord(app).get_thumbnail.get_url()}}" />
<p>{{ basket.getDescription() }}</p> </div>
<span class="ui-li-count">{{basket_length}}</span> {% endif %}
</li> <h3><a href="{{ path('lightbox_validation', { 'basket' : basket.getId() }) }}">{{ basket.getName() }}</a></h3>
{% endif %} {#<p>{{ basket.getDescription() }}</p>#}
{% endfor %} <span class="ui-li-count baskets">{{basket_length}}</span>
</ul> </li>
{% endif %}
{% endfor %}
</ul>
</div>
</div> </div>
<div data-role="footer"> <div data-role="footer">

View File

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

View File

@@ -32,35 +32,74 @@
<h1>{{basket.getName()}}</h1> <h1>{{basket.getName()}}</h1>
<a rel="external" href="{{ path('lightbox') }}" data-icon="home" data-iconpos="notext" data-direction="reverse" class="ui-btn-right jqm-home">{{ 'Home' | trans }}</a> <a rel="external" href="{{ path('lightbox') }}" data-icon="home" data-iconpos="notext" data-direction="reverse" class="ui-btn-right jqm-home">{{ 'Home' | trans }}</a>
</div> </div>
<div class="lightbox-user-info">
{% include 'common/menubar.html.twig' %}
</div>
<div data-role="content"> <div data-role="content">
<input type="hidden" id="basket_validation_id" value="{{ basket.getId() }}"> <input type="hidden" id="basket_validation_id" value="{{ basket.getId() }}">
<p> <p>
{% set basket_length = basket.getElements().count() %} {% set basket_length = basket.getElements().count() %}
{% trans with {'%basket_length%' : basket_length} %}%basket_length% documents{% endtrans %} {% trans with {'%basket_length%' : basket_length} %}%basket_length% documents{% endtrans %}
</p> </p>
<ul class="image_set"> {% if basket.getValidation() %}
{% for basket_element in basket.getElements() %} <div class="report_wrapper">
<li class="image_box" id="sselcontid_{{basket_element.getId()}}"> <a id="report_summary" class="report_btn report_summary" href="#" onclick="$.ajax({
{% if basket_element.getBasket().getValidation() and basket_element.getBasket().getValidation().getParticipant(app.getAuthenticatedUser()).getCanAgree() %} type: 'GET',
<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 %}"> url: '/lightbox/ajax/LOAD_REPORT/' + $('#navigation').val() + '/',
</div> 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 %} {% endif %}
<a href="{{ path('lightbox_ajax_load_basketelement', { 'sselcont_id' : basket_element.getId() }) }}"> <input type="hidden" id="navigation" value="{{ basket.getId() }}">
{{thumbnail.format(basket_element.getRecord(app).get_thumbnail(), 80, 80, '', true, false)}} <div id="report" class="summary_block">
</a> <ul class="image_set">
<input type="hidden" class="display_id" name="display_id" value="{{basket_element.getOrd()}}" /> {% for basket_element in basket.getElements() %}
</li>
{% endfor %} <li class="image_box" id="sselcontid_{{basket_element.getId()}}">
</ul> <div class="display_id">
</div> {{basket_element.getOrd()}}
<div data-role="footer"> </div>
{% if basket.getValidation() and basket.getValidation().getParticipant(app.getAuthenticatedUser()).getCanAgree() %} {% if basket_element.getBasket().getValidation() and basket_element.getBasket().getValidation().getParticipant(app.getAuthenticatedUser()).getCanAgree() %}
<button class="confirm_report" style="width:100%;" title="{{ 'validation::envoyer mon rapport' | trans }}"> <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 %}">
{{ 'validation::envoyer mon rapport' | trans }} </div>
<img src="/assets/common/images/icons/loader1F1E1B.gif" style="visibility:hidden;" class="loader"/> {% endif %}
</button> <a href="{{ path('lightbox_ajax_load_basketelement', { 'sselcont_id' : basket_element.getId() }) }}">
{% endif %} {{thumbnail.format(basket_element.getRecord(app).get_thumbnail(), 300, 300, '', true, false)}}
</div> </a>
<input type="hidden" class="display_id" name="display_id" value="{{basket_element.getOrd()}}" />
</li>
{% endfor %}
</ul>
</div>
</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 }}
<img src="/assets/common/images/icons/loader1F1E1B.gif" style="visibility:hidden;" class="loader"/>
</button>
{% endif %}
</div>
</div>
</div> </div>
{% endblock %} {% endblock %}

View File

@@ -189,6 +189,22 @@
</label> </label>
</td> </td>
</tr> </tr>
<tr>
<td>
<label for="gui_editable" class="checkbox">
<input id="gui_editable" type="checkbox" <%= field.gui_editable ? "checked='checked'" : "" %> />
{% 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>
<tr> <tr>
<td colspan="2"> <td colspan="2">
<label for="business" class="checkbox"> <label for="business" class="checkbox">
@@ -247,6 +263,10 @@
<td><label for="tbranch">{% trans %}Thesaurus branch{% endtrans %}</label></td> <td><label for="tbranch">{% trans %}Thesaurus branch{% endtrans %}</label></td>
<td><input id="tbranch" type="text" value="<%= field.tbranch %>"/></td> <td><input id="tbranch" type="text" value="<%= field.tbranch %>"/></td>
</tr> </tr>
<tr>
<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> </table>
</div> </div>
</div> </div>

View File

@@ -1,8 +1,10 @@
{% macro caption(record, can_see_business, display_exif, limitedWidth = false) %} {% macro caption(record, can_see_business, display_exif, limitedWidth = false) %}
<dl class="{% if limitedWidth %}{% else %}dl-horizontal{% endif %}"> <dl class="{% if limitedWidth %}{% else %}dl-horizontal{% endif %}">
{% for name, value in record.getCaption(caption_field_order(record, can_see_business)) %} {% for name, value in record.getCaption(caption_field_order(record, can_see_business)) %}
<dt>{{ caption_field_label(record, name) }}</dt> {% if caption_field_gui_visible(record, name) == 1 %}
<dd>{{ caption_field(record, name, value)|e|highlight|linkify|parseColor }}</dd> <dt>{{ caption_field_label(record, name) }}</dt>
<dd>{{ caption_field(record, name, value)|e|highlight|linkify|parseColor }}</dd>
{% endif %}
{% endfor %} {% endfor %}
</dl> </dl>
{% if display_exif|default(true) and app.getAuthenticator().user is not none and user_setting('technical_display') == 'group' %} {% 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 () { $(document).ready(function () {
$('body').on('click', '#help-trigger', function (event) { $('body').on('click', '#help-trigger', function (event) {
$('#mainMenu .helpcontextmenu').toggleClass('shown'); $('#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 key = 'help';
var configurationSettingLinks = {{ configuration|json_encode|raw }}; var configurationSettingLinks = {{ configuration|json_encode|raw }};
//seperate array based on location of link //seperate array based on location of link

View File

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

View File

@@ -16,9 +16,10 @@
<a class="workzone-menu-title" href="{{ path('prod_baskets_basket', { 'basket' : basket.getId() }) }}"> <a class="workzone-menu-title" href="{{ path('prod_baskets_basket', { 'basket' : basket.getId() }) }}">
<span> <span>
{% if not basket.isRead() %} {% if not basket.isRead() %}
<img src='/assets/common/images/icons/basket_push_unread.png' title=''/> <img src='/assets/common/images/icons/basket_feedback_unread.png' title=''/>
{% else %}
<img src='/assets/common/images/icons/basket_feedback_read.png' title=''/>
{% endif %} {% endif %}
<img src='/assets/common/images/icons/basket.png' title=''/>
{{basket.getName()|e}} {{basket.getName()|e}}
</span> </span>
</a> </a>
@@ -26,11 +27,9 @@
<table> <table>
<tr> <tr>
<td> <td>
<img <img title="{{ basket.getDescription()|e }}"
title="{{ basket.getDescription() }}" class="basket_title"
class="basketTips basket_title" src="/assets/common/images/icons/valid.png" />
src="/assets/common/images/icons/valid.png"
/>
</td> </td>
<td> <td>
<a class="contextMenuTrigger icon-display-grid" href="#"></a> <a class="contextMenuTrigger icon-display-grid" href="#"></a>

Some files were not shown because too many files have changed in this diff Show More