mirror of
https://github.com/alchemy-fr/Phraseanet.git
synced 2025-10-23 18:03:17 +00:00
Merge branch 'master' of https://github.com/alchemy-fr/Phraseanet into PHRAS-2741-worker-service-part1
This commit is contained in:
@@ -107,6 +107,53 @@ jobs:
|
||||
- store_artifacts:
|
||||
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:
|
||||
version: 2
|
||||
oldfashion:
|
||||
@@ -114,36 +161,9 @@ workflows:
|
||||
- build
|
||||
newfashion:
|
||||
jobs:
|
||||
- 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
|
||||
- build_phraseanet-fpm:
|
||||
context: "AWS London"
|
||||
create-repo: true
|
||||
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
|
||||
- build_phraseanet-worker:
|
||||
context: "AWS London"
|
||||
create-repo: true
|
||||
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
|
||||
- build_phraseanet-nginx:
|
||||
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
12
.dockerignore
Normal file
@@ -0,0 +1,12 @@
|
||||
.circleci
|
||||
.git
|
||||
.settings
|
||||
nodes_modules
|
||||
vendor
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
93
Dockerfile
93
Dockerfile
@@ -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 \
|
||||
&& apt-get install -y \
|
||||
apt-transport-https \
|
||||
@@ -23,6 +28,7 @@ RUN apt-get update \
|
||||
libxslt-dev \
|
||||
libzmq3-dev \
|
||||
locales \
|
||||
gettext \
|
||||
mcrypt \
|
||||
swftools \
|
||||
unoconv \
|
||||
@@ -40,7 +46,7 @@ RUN apt-get update \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
RUN php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" \
|
||||
&& php -r "if (hash_file('sha384', 'composer-setup.php') === 'a5c698ffe4b8e849a443b120cd5ba38043260d5c4023dbf93e1558871f1f07f58274fc6f4c93bcfd858c6bd0775cd8d1') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;" \
|
||||
&& php -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 -r "unlink('composer-setup.php');"
|
||||
|
||||
@@ -61,9 +67,19 @@ RUN mkdir /entrypoint /var/alchemy \
|
||||
&& mkdir -p /home/app/.composer \
|
||||
&& chown -R app: /home/app /var/alchemy
|
||||
|
||||
#########################################################################
|
||||
# This image is used to build the apps
|
||||
#########################################################################
|
||||
|
||||
FROM phraseanet-system as builder
|
||||
|
||||
WORKDIR /var/alchemy/
|
||||
|
||||
# Files that are needed at build stage
|
||||
|
||||
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 package.json /var/alchemy/
|
||||
COPY phpunit.xml.dist /var/alchemy/
|
||||
@@ -73,11 +89,16 @@ COPY composer.json /var/alchemy/
|
||||
COPY composer.lock /var/alchemy/
|
||||
RUN make install_composer
|
||||
COPY resources /var/alchemy/resources
|
||||
COPY www /var/alchemy/www
|
||||
|
||||
# Application build phase
|
||||
|
||||
RUN make clean_assets
|
||||
RUN make install_asset_dependencies
|
||||
RUN make install_assets
|
||||
|
||||
# Application code
|
||||
|
||||
COPY www /var/alchemy/www
|
||||
ADD ./docker/phraseanet/ /
|
||||
COPY lib /var/alchemy/lib
|
||||
COPY tmp /var/alchemy/tmp
|
||||
@@ -87,55 +108,8 @@ COPY templates-profiler /var/alchemy/templates-profiler
|
||||
COPY templates /var/alchemy/templates
|
||||
COPY tests /var/alchemy/tests
|
||||
|
||||
# Phraseanet
|
||||
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/*
|
||||
# Create needed folders
|
||||
|
||||
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 \
|
||||
&& chmod -R 777 /var/alchemy/Phraseanet/logs \
|
||||
&& 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 \
|
||||
&& mkdir -p /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
|
||||
ENTRYPOINT ["/phraseanet-entrypoint.sh"]
|
||||
CMD ["/boot.sh"]
|
||||
|
||||
# phraseanet-worker
|
||||
#########################################################################
|
||||
# Phraseanet worker application image
|
||||
#########################################################################
|
||||
|
||||
FROM phraseanet-fpm as phraseanet-worker
|
||||
CMD ["/worker-boot.sh"]
|
||||
|
||||
#########################################################################
|
||||
# phraseanet-nginx
|
||||
#########################################################################
|
||||
|
||||
FROM nginx:1.15 as phraseanet-nginx
|
||||
RUN useradd -u 1000 app
|
||||
ADD ./docker/nginx/ /
|
||||
|
@@ -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"]
|
16
README.md
16
README.md
@@ -63,14 +63,16 @@ The docker distribution come with 3 differents containers :
|
||||
|
||||
## How to build
|
||||
|
||||
The three images can be built respectively with these commands :
|
||||
You can build all the images with the following command at the root directory, choosing an arbirary TAG name :
|
||||
|
||||
# nginx server
|
||||
docker build --target phraseanet-nginx -t local/phraseanet-nginx .
|
||||
./build.sh <TAG>
|
||||
|
||||
# php-fpm application
|
||||
docker build --target phraseanet-fpm -t local/phraseanet-fpm .
|
||||
It will build and tag the following images :
|
||||
|
||||
# worker
|
||||
docker build --target phraseanet-worker -t local/phraseanet-worker .
|
||||
local/phraseanet-worker:<TAG>
|
||||
local/phraseanet-fpm:<TAG>
|
||||
local/phraseanet-nginx:<TAG>
|
||||
|
||||
# Deploy the application
|
||||
|
||||
Once the images are built, you can deploy the entire phraseanet stack using the repository : https://github.com/alchemy-fr/phraseanet-docker and follow the instruction inside its `README.md` file.
|
||||
|
13
build.sh
Executable file
13
build.sh
Executable 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
0
cache/.gitkeep
vendored
Normal file
@@ -84,7 +84,7 @@
|
||||
"league/flysystem": "^1.0",
|
||||
"league/flysystem-aws-s3-v2": "^1.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",
|
||||
"mrclay/minify": "~2.1.6",
|
||||
"neutron/process-manager": "2.0.x-dev@dev",
|
||||
|
337
composer.lock
generated
337
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,6 @@
|
||||
#!/bin/bash
|
||||
|
||||
cat nginx.conf.sample | sed "s/\$MAX_BODY_SIZE/$MAX_BODY_SIZE/g" > /etc/nginx/nginx.conf
|
||||
set -xe
|
||||
|
||||
cat nginx.conf.sample | sed "s/\$MAX_BODY_SIZE/$MAX_BODY_SIZE/g" > /etc/nginx/conf.d/default.conf
|
||||
nginx -g "daemon off;"
|
||||
|
31
docker/nginx/etc/nginx/nginx.conf
Executable file
31
docker/nginx/etc/nginx/nginx.conf
Executable 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;
|
||||
|
||||
}
|
@@ -1,54 +1,9 @@
|
||||
user app;
|
||||
worker_processes auto;
|
||||
|
||||
#error_log /var/log/ngnix_error.log info;
|
||||
error_log /dev/stdout info;
|
||||
|
||||
pid /var/run/nginx.pid;
|
||||
#daemon off;
|
||||
|
||||
events {
|
||||
worker_connections 1024;
|
||||
multi_accept on;
|
||||
}
|
||||
|
||||
http {
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
server_tokens off;
|
||||
|
||||
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
||||
'$status $body_bytes_sent "$http_referer" '
|
||||
'"$http_user_agent" "$http_x_forwarded_for"';
|
||||
|
||||
access_log /dev/stdout main;
|
||||
|
||||
sendfile on;
|
||||
#tcp_nopush on;
|
||||
|
||||
keepalive_timeout 65;
|
||||
|
||||
#gzip on;
|
||||
|
||||
reset_timedout_connection on;
|
||||
|
||||
proxy_connect_timeout 300s;
|
||||
proxy_send_timeout 300s;
|
||||
proxy_read_timeout 300s;
|
||||
fastcgi_send_timeout 300s;
|
||||
fastcgi_read_timeout 300;
|
||||
|
||||
resolver 127.0.0.11;
|
||||
|
||||
upstream backend {
|
||||
server phraseanet:9000;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name localhost;
|
||||
error_log on;
|
||||
access_log on;
|
||||
root /var/alchemy/Phraseanet/www;
|
||||
|
||||
index index.php;
|
||||
@@ -82,4 +37,3 @@ http {
|
||||
fastcgi_pass backend;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -2,6 +2,16 @@
|
||||
|
||||
set -xe
|
||||
|
||||
if [ $INSTALL_ACCOUNT_EMAIL = ""]; then
|
||||
echo "INSTALL_ACCOUNT_EMAIL var is not set."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ $INSTALL_ACCOUNT_PASSWORD = ""]; then
|
||||
echo "INSTALL_ACCOUNT_PASSWORD var is not set."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
/var/alchemy/Phraseanet/bin/setup system:install \
|
||||
--email=$INSTALL_ACCOUNT_EMAIL \
|
||||
--password=$INSTALL_ACCOUNT_PASSWORD \
|
||||
|
@@ -1,5 +1,11 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -xe
|
||||
|
||||
chown -R app:app /var/alchemy/Phraseanet/config
|
||||
chown -R app:app /var/alchemy/Phraseanet/datas
|
||||
chown -R app:app /var/alchemy/Phraseanet/tmp
|
||||
chown -R app:app /var/alchemy/Phraseanet/www/thumbnails
|
||||
FILE=/var/alchemy/Phraseanet/config/configuration.yml
|
||||
if [ -f "$FILE" ]; then
|
||||
echo "$FILE exist, skip setup."
|
||||
|
@@ -5,4 +5,4 @@ set -e
|
||||
envsubst < /php.ini.sample > /usr/local/etc/php/php.ini
|
||||
envsubst < /php-fpm.conf.sample > /usr/local/etc/php-fpm.conf
|
||||
|
||||
docker-php-entrypoint $@
|
||||
bash -e docker-php-entrypoint $@
|
||||
|
@@ -1,3 +1,4 @@
|
||||
#!/bin/bash
|
||||
|
||||
mkdir /var/alchemy/Phraseanet/tmp/locks && chown -R app:app /var/alchemy/Phraseanet/tmp
|
||||
runuser app -c 'php /var/alchemy/Phraseanet/bin/console task-manager:scheduler:run'
|
||||
|
@@ -128,7 +128,7 @@ key:
|
||||
| quoted_string()
|
||||
|
||||
group:
|
||||
::space::? ::parenthese_:: primary() ::_parenthese:: ::space::?
|
||||
::space::? ::parenthese_:: ::space::? primary() ::space::? ::_parenthese:: ::space::?
|
||||
|
||||
|
||||
// Thesaurus terms
|
||||
|
@@ -15,6 +15,7 @@ use Alchemy\Phrasea\Controller\Api\Result;
|
||||
use Alchemy\Phrasea\ControllerProvider\Api\OAuth2;
|
||||
use Alchemy\Phrasea\ControllerProvider\Api\V1;
|
||||
use Alchemy\Phrasea\ControllerProvider\Api\V2;
|
||||
use Alchemy\Phrasea\ControllerProvider\Api\V3;
|
||||
use Alchemy\Phrasea\ControllerProvider\Datafiles;
|
||||
use Alchemy\Phrasea\ControllerProvider\MediaAccessor;
|
||||
use Alchemy\Phrasea\ControllerProvider\Minifier;
|
||||
@@ -36,6 +37,7 @@ class ApiApplicationLoader extends BaseApplicationLoader
|
||||
$app->register(new OAuth2());
|
||||
$app->register(new V1());
|
||||
$app->register(new V2());
|
||||
$app->register(new V3());
|
||||
$app->register(new ApiReportControllerProvider());
|
||||
$app->register(new JsonSchemaServiceProvider());
|
||||
}
|
||||
@@ -119,6 +121,16 @@ class ApiApplicationLoader extends BaseApplicationLoader
|
||||
'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();
|
||||
});
|
||||
@@ -135,6 +147,7 @@ class ApiApplicationLoader extends BaseApplicationLoader
|
||||
$app->mount('/datafiles/', new Datafiles());
|
||||
$app->mount('/api/v1', new V1());
|
||||
$app->mount('/api/v2', new V2());
|
||||
$app->mount('/api/v3', new V3());
|
||||
$app->mount('/api/report', new ApiReportControllerProvider());
|
||||
$app->mount('/permalink/', new Permalink());
|
||||
$app->mount($app['controller.media_accessor.route_prefix'], new MediaAccessor());
|
||||
|
@@ -34,6 +34,11 @@ abstract class AbstractChecker implements CheckerInterface
|
||||
*/
|
||||
protected $collections = [];
|
||||
|
||||
/**
|
||||
* @var \collection[]
|
||||
*/
|
||||
protected $compareIgnoreCollections = [];
|
||||
|
||||
public function __construct(Application $app)
|
||||
{
|
||||
$this->app = $app;
|
||||
@@ -44,7 +49,7 @@ abstract class AbstractChecker implements CheckerInterface
|
||||
* Warning, you can not restrict on both databoxes and collections
|
||||
*
|
||||
* @param \databox[] $databoxes A databox or an array of databoxes
|
||||
* @return bool
|
||||
* @return \databox[]
|
||||
*
|
||||
* @throws \LogicException If already restricted to collections
|
||||
* @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
|
||||
*
|
||||
* @param \collection[] $collections
|
||||
* @return bool
|
||||
* @return \collection[]
|
||||
*
|
||||
* @throws \LogicException If already restricted to databoxes
|
||||
* @throws \InvalidArgumentException In case invalid collections are provided
|
||||
@@ -95,6 +100,11 @@ abstract class AbstractChecker implements CheckerInterface
|
||||
return $this->collections;
|
||||
}
|
||||
|
||||
public function setCompareIgnoreCollections($collections)
|
||||
{
|
||||
$this->compareIgnoreCollections = $collections;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the checker should be executed against the current file
|
||||
*
|
||||
|
@@ -45,8 +45,18 @@ class Filename extends AbstractChecker
|
||||
*/
|
||||
public function check(EntityManager $em, File $file)
|
||||
{
|
||||
$boolean = empty(\record_adapter::get_records_by_originalname(
|
||||
$file->getCollection()->get_databox(), $file->getOriginalName(), $this->sensitive, 0, 1
|
||||
$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(\record_adapter::getRecordsByOriginalnameWithExcludedCollIds(
|
||||
$file->getCollection()->get_databox(), $file->getOriginalName(), $this->sensitive, 0, 1, $excludedCollIds
|
||||
));
|
||||
|
||||
return new Response($boolean, $this);
|
||||
|
@@ -34,7 +34,17 @@ class Sha256 extends AbstractChecker
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
@@ -33,7 +33,17 @@ class UUID extends AbstractChecker
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
@@ -73,7 +73,7 @@ class RedisCache extends CacheProvider implements Cache
|
||||
*/
|
||||
protected function doDelete($id)
|
||||
{
|
||||
return $this->_redis->delete($id);
|
||||
return $this->_redis->del($id);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -13,6 +13,7 @@ namespace Alchemy\Phrasea\Command\Setup;
|
||||
|
||||
use Alchemy\Phrasea\Command\Command;
|
||||
use Alchemy\Phrasea\Core\Configuration\StructureTemplate;
|
||||
use Alchemy\Phrasea\SearchEngine\Elastic\ElasticsearchOptions;
|
||||
use Doctrine\DBAL\Driver\Connection;
|
||||
use Symfony\Component\Console\Helper\DialogHelper;
|
||||
use Symfony\Component\Console\Input\ArrayInput;
|
||||
@@ -51,7 +52,9 @@ class Install extends Command
|
||||
->addOption('db-template', null, InputOption::VALUE_OPTIONAL, 'Databox template (' . $this->structureTemplate->toString() . ')', null)
|
||||
->addOption('data-path', null, InputOption::VALUE_OPTIONAL, 'Path to data repository', realpath(__DIR__ . '/../../../../../datas'))
|
||||
->addOption('server-name', null, InputOption::VALUE_OPTIONAL, 'Server name')
|
||||
->addOption('indexer', null, InputOption::VALUE_OPTIONAL, 'Path to Phraseanet Indexer', 'auto')
|
||||
->addOption('es-host', null, InputOption::VALUE_OPTIONAL, 'ElasticSearch server HTTP host', 'localhost')
|
||||
->addOption('es-port', null, InputOption::VALUE_OPTIONAL, 'ElasticSearch server HTTP port', 9200)
|
||||
->addOption('es-index', null, InputOption::VALUE_OPTIONAL, 'ElasticSearch index name', null)
|
||||
->addOption('yes', 'y', InputOption::VALUE_NONE, 'Answer yes to all questions');
|
||||
|
||||
return $this;
|
||||
@@ -121,6 +124,21 @@ class Install extends Command
|
||||
list($email, $password) = $this->getCredentials($input, $output, $dialog);
|
||||
$dataPath = $this->getDataPath($input, $output, $dialog);
|
||||
|
||||
if (! $input->getOption('yes')) {
|
||||
$output->writeln("<info>--- ElasticSearch connection settings ---</info>");
|
||||
}
|
||||
|
||||
list($esHost, $esPort) = $this->getESHost($input, $output, $dialog);
|
||||
$esIndexName = $this->getESIndexName($input, $output, $dialog);
|
||||
|
||||
$esOptions = ElasticsearchOptions::fromArray([
|
||||
'host' => $esHost,
|
||||
'port' => $esPort,
|
||||
'index' => $esIndexName
|
||||
]);
|
||||
|
||||
$output->writeln('');
|
||||
|
||||
if (!$input->getOption('yes')) {
|
||||
$continue = $dialog->askConfirmation($output, "<question>Phraseanet is going to be installed, continue ? (N/y)</question>", false);
|
||||
|
||||
@@ -132,6 +150,7 @@ class Install extends Command
|
||||
}
|
||||
|
||||
$this->container['phraseanet.installer']->install($email, $password, $abConn, $serverName, $dataPath, $dbConn, $templateName, $this->detectBinaries());
|
||||
$this->container['conf']->set(['main', 'search-engine', 'options'], $esOptions->toArray());
|
||||
|
||||
if (null !== $this->getApplication()) {
|
||||
$command = $this->getApplication()->find('crossdomain:generate');
|
||||
@@ -339,6 +358,35 @@ class Install extends Command
|
||||
return $serverName;
|
||||
}
|
||||
|
||||
private function getESHost(InputInterface $input, OutputInterface $output, DialogHelper $dialog)
|
||||
{
|
||||
$host = $input->getOption('es-host');
|
||||
$port = (int) $input->getOption('es-port');
|
||||
|
||||
if (! $input->getOption('yes')) {
|
||||
while (! $host) {
|
||||
$host = $dialog->ask($output, 'ElasticSearch server host : ', null);
|
||||
};
|
||||
|
||||
while ($port <= 0 || $port >= 65535) {
|
||||
$port = (int) $dialog->ask($output, 'ElasticSearch server port : ', null);
|
||||
};
|
||||
}
|
||||
|
||||
return [ $host, $port ];
|
||||
}
|
||||
|
||||
private function getESIndexName(InputInterface $input, OutputInterface $output, DialogHelper $dialog)
|
||||
{
|
||||
$index = $input->getOption('es-index');
|
||||
|
||||
if (! $input->getOption('yes')) {
|
||||
$index = $dialog->ask($output, 'ElasticSearch server index name (blank to autogenerate) : ', null);
|
||||
}
|
||||
|
||||
return $index;
|
||||
}
|
||||
|
||||
private function detectBinaries()
|
||||
{
|
||||
return [
|
||||
|
@@ -314,6 +314,9 @@ class FieldsController extends Controller
|
||||
->set_readonly($data['readonly'])
|
||||
->set_type($data['type'])
|
||||
->set_tbranch($data['tbranch'])
|
||||
->set_generate_cterms($data['generate_cterms'])
|
||||
->set_gui_editable($data['gui_editable'])
|
||||
->set_gui_visible($data['gui_visible'])
|
||||
->set_report($data['report'])
|
||||
->setVocabularyControl(null)
|
||||
->setVocabularyRestricted(false);
|
||||
@@ -349,7 +352,7 @@ class FieldsController extends Controller
|
||||
{
|
||||
return [
|
||||
'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'
|
||||
];
|
||||
}
|
||||
|
@@ -354,7 +354,7 @@ class SubdefsController extends Controller
|
||||
Subdef::TYPE_VIDEO => [
|
||||
"definitions" => [
|
||||
"video codec H264" => null,
|
||||
"144P H264 128 kbps ACC 128kbps" => [
|
||||
"144P H264 128 kbps MP3 128kbps" => [
|
||||
Video::OPTION_AUDIOBITRATE => "128",
|
||||
Video::OPTION_AUDIOSAMPLERATE => "44100",
|
||||
Video::OPTION_BITRATE => "128",
|
||||
@@ -362,10 +362,10 @@ class SubdefsController extends Controller
|
||||
Video::OPTION_SIZE => "256",
|
||||
Video::OPTION_FRAMERATE => "25",
|
||||
Video::OPTION_VCODEC => "libx264",
|
||||
Video::OPTION_ACODEC => "libfaac",
|
||||
Video::OPTION_ACODEC => "libmp3lame",
|
||||
Subdef::OPTION_DEVICE => ["all"]
|
||||
],
|
||||
"240P H264 256 kbps ACC 128kbps" => [
|
||||
"240P H264 256 kbps MP3 128kbps" => [
|
||||
Video::OPTION_AUDIOBITRATE => "128",
|
||||
Video::OPTION_AUDIOSAMPLERATE => "44100",
|
||||
Video::OPTION_BITRATE => "256",
|
||||
@@ -373,10 +373,10 @@ class SubdefsController extends Controller
|
||||
Video::OPTION_SIZE => "426",
|
||||
Video::OPTION_FRAMERATE => "25",
|
||||
Video::OPTION_VCODEC => "libx264",
|
||||
Video::OPTION_ACODEC => "libfaac",
|
||||
Video::OPTION_ACODEC => "libmp3lame",
|
||||
Subdef::OPTION_DEVICE => ["all"]
|
||||
],
|
||||
"360P H264 576 kbps ACC 128kbps" => [
|
||||
"360P H264 576 kbps MP3 128kbps" => [
|
||||
Video::OPTION_AUDIOBITRATE => "128",
|
||||
Video::OPTION_AUDIOSAMPLERATE => "44100",
|
||||
Video::OPTION_BITRATE => "576",
|
||||
@@ -384,10 +384,10 @@ class SubdefsController extends Controller
|
||||
Video::OPTION_SIZE => "480",
|
||||
Video::OPTION_FRAMERATE => "25",
|
||||
Video::OPTION_VCODEC => "libtheora",
|
||||
Video::OPTION_ACODEC => "libfaac",
|
||||
Video::OPTION_ACODEC => "libmp3lame",
|
||||
Subdef::OPTION_DEVICE => ["all"]
|
||||
],
|
||||
"480P H264 750 kbps ACC 128kbps" => [
|
||||
"480P H264 750 kbps MP3 128kbps" => [
|
||||
Video::OPTION_AUDIOBITRATE => "128",
|
||||
Video::OPTION_AUDIOSAMPLERATE => "44100",
|
||||
Video::OPTION_BITRATE => "750",
|
||||
@@ -395,10 +395,10 @@ class SubdefsController extends Controller
|
||||
Video::OPTION_SIZE => "854",
|
||||
Video::OPTION_FRAMERATE => "25",
|
||||
Video::OPTION_VCODEC => "libx264",
|
||||
Video::OPTION_ACODEC => "libfaac",
|
||||
Video::OPTION_ACODEC => "libmp3lame",
|
||||
Subdef::OPTION_DEVICE => ["all"]
|
||||
],
|
||||
"720P H264 1492 kbps ACC 128kbps" => [
|
||||
"720P H264 1492 kbps MP3 128kbps" => [
|
||||
Video::OPTION_AUDIOBITRATE => "128",
|
||||
Video::OPTION_AUDIOSAMPLERATE => "44100",
|
||||
Video::OPTION_BITRATE => "1492",
|
||||
@@ -406,10 +406,10 @@ class SubdefsController extends Controller
|
||||
Video::OPTION_SIZE => "1280",
|
||||
Video::OPTION_FRAMERATE => "25",
|
||||
Video::OPTION_VCODEC => "libx264",
|
||||
Video::OPTION_ACODEC => "libfaac",
|
||||
Video::OPTION_ACODEC => "libmp3lame",
|
||||
Subdef::OPTION_DEVICE => ["all"]
|
||||
],
|
||||
"1080P H264 2420 kbps ACC 128kbps" => [
|
||||
"1080P H264 2420 kbps MP3 128kbps" => [
|
||||
Video::OPTION_AUDIOBITRATE => "128",
|
||||
Video::OPTION_AUDIOSAMPLERATE => "44100",
|
||||
Video::OPTION_BITRATE => "2420",
|
||||
@@ -417,11 +417,77 @@ class SubdefsController extends Controller
|
||||
Video::OPTION_SIZE => "1920",
|
||||
Video::OPTION_FRAMERATE => "25",
|
||||
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"]
|
||||
],
|
||||
"video codec libvpx" => null,
|
||||
"144P webm 128 kbps ACC 128kbps" => [
|
||||
"144P webm 128 kbps MP3 128kbps" => [
|
||||
Video::OPTION_AUDIOBITRATE => "128",
|
||||
Video::OPTION_AUDIOSAMPLERATE => "44100",
|
||||
Video::OPTION_BITRATE => "128",
|
||||
@@ -429,10 +495,10 @@ class SubdefsController extends Controller
|
||||
Video::OPTION_SIZE => "256",
|
||||
Video::OPTION_FRAMERATE => "25",
|
||||
Video::OPTION_VCODEC => "libvpx",
|
||||
Video::OPTION_ACODEC => "libfaac",
|
||||
Video::OPTION_ACODEC => "libmp3lame",
|
||||
Subdef::OPTION_DEVICE => ["all"]
|
||||
],
|
||||
"240P webm 256 kbps ACC 128kbps" => [
|
||||
"240P webm 256 kbps MP3 128kbps" => [
|
||||
Video::OPTION_AUDIOBITRATE => "128",
|
||||
Video::OPTION_AUDIOSAMPLERATE => "44100",
|
||||
Video::OPTION_BITRATE => "256",
|
||||
@@ -440,10 +506,10 @@ class SubdefsController extends Controller
|
||||
Video::OPTION_SIZE => "426",
|
||||
Video::OPTION_FRAMERATE => "25",
|
||||
Video::OPTION_VCODEC => "libvpx",
|
||||
Video::OPTION_ACODEC => "libfaac",
|
||||
Video::OPTION_ACODEC => "libmp3lame",
|
||||
Subdef::OPTION_DEVICE => ["all"]
|
||||
],
|
||||
"360P webm 576 kbps ACC 128kbps" => [
|
||||
"360P webm 576 kbps MP3 128kbps" => [
|
||||
Video::OPTION_AUDIOBITRATE => "128",
|
||||
Video::OPTION_AUDIOSAMPLERATE => "44100",
|
||||
Video::OPTION_BITRATE => "576",
|
||||
@@ -451,10 +517,10 @@ class SubdefsController extends Controller
|
||||
Video::OPTION_SIZE => "480",
|
||||
Video::OPTION_FRAMERATE => "25",
|
||||
Video::OPTION_VCODEC => "libvpx",
|
||||
Video::OPTION_ACODEC => "libfaac",
|
||||
Video::OPTION_ACODEC => "libmp3lame",
|
||||
Subdef::OPTION_DEVICE => ["all"]
|
||||
],
|
||||
"480P webm 750 kbps ACC 128kbps" => [
|
||||
"480P webm 750 kbps MP3 128kbps" => [
|
||||
Video::OPTION_AUDIOBITRATE => "128",
|
||||
Video::OPTION_AUDIOSAMPLERATE => "44100",
|
||||
Video::OPTION_BITRATE => "750",
|
||||
@@ -462,10 +528,10 @@ class SubdefsController extends Controller
|
||||
Video::OPTION_SIZE => "854",
|
||||
Video::OPTION_FRAMERATE => "25",
|
||||
Video::OPTION_VCODEC => "libvpx",
|
||||
Video::OPTION_ACODEC => "libfaac",
|
||||
Video::OPTION_ACODEC => "libmp3lame",
|
||||
Subdef::OPTION_DEVICE => ["all"]
|
||||
],
|
||||
"720P webm 1492 kbps ACC 128kbps" => [
|
||||
"720P webm 1492 kbps MP3 128kbps" => [
|
||||
Video::OPTION_AUDIOBITRATE => "128",
|
||||
Video::OPTION_AUDIOSAMPLERATE => "44100",
|
||||
Video::OPTION_BITRATE => "1492",
|
||||
@@ -473,10 +539,10 @@ class SubdefsController extends Controller
|
||||
Video::OPTION_SIZE => "1280",
|
||||
Video::OPTION_FRAMERATE => "25",
|
||||
Video::OPTION_VCODEC => "libvpx",
|
||||
Video::OPTION_ACODEC => "libfaac",
|
||||
Video::OPTION_ACODEC => "libmp3lame",
|
||||
Subdef::OPTION_DEVICE => ["all"]
|
||||
],
|
||||
"1080P webm 2420 kbps ACC 128kbps" => [
|
||||
"1080P webm 2420 kbps MP3 128kbps" => [
|
||||
Video::OPTION_AUDIOBITRATE => "128",
|
||||
Video::OPTION_AUDIOSAMPLERATE => "44100",
|
||||
Video::OPTION_BITRATE => "2420",
|
||||
@@ -484,7 +550,73 @@ class SubdefsController extends Controller
|
||||
Video::OPTION_SIZE => "1920",
|
||||
Video::OPTION_FRAMERATE => "25",
|
||||
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"]
|
||||
],
|
||||
],
|
||||
|
@@ -12,6 +12,7 @@
|
||||
namespace Alchemy\Phrasea\Controller\Api;
|
||||
|
||||
use Alchemy\Phrasea\ControllerProvider\Api\V1;
|
||||
use Alchemy\Phrasea\ControllerProvider\Api\V3;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
@@ -272,6 +273,8 @@ class Result
|
||||
$this->version = $this->request->attributes->get('api_version');
|
||||
} elseif (mb_strpos($this->request->getPathInfo(), '/api/v1') !== FALSE) {
|
||||
$this->version = V1::VERSION;
|
||||
} elseif (mb_strpos($this->request->getPathInfo(), '/api/v3') !== FALSE) {
|
||||
$this->version = V3::VERSION;
|
||||
} else {
|
||||
$this->version = self::$defaultVersion;
|
||||
}
|
||||
|
@@ -594,6 +594,9 @@ class V1Controller extends Controller
|
||||
],
|
||||
'separator' => $databox_field->get_separator(),
|
||||
'thesaurus_branch' => $databox_field->get_tbranch(),
|
||||
'generate_cterms' => $databox_field->get_generate_cterms(),
|
||||
'gui_editable' => $databox_field->get_gui_editable(),
|
||||
'gui_visible' => $databox_field->get_gui_visible(),
|
||||
'type' => $databox_field->get_type(),
|
||||
'indexable' => $databox_field->is_indexable(),
|
||||
'multivalue' => $databox_field->is_multi(),
|
||||
|
807
lib/Alchemy/Phrasea/Controller/Api/V3Controller.php
Normal file
807
lib/Alchemy/Phrasea/Controller/Api/V3Controller.php
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
@@ -75,6 +75,9 @@ class EditController extends Controller
|
||||
'format' => '',
|
||||
'explain' => '',
|
||||
'tbranch' => $meta->get_tbranch(),
|
||||
'generate_cterms' => $meta->get_generate_cterms(),
|
||||
'gui_editable' => $meta->get_gui_editable(),
|
||||
'gui_visible' => $meta->get_gui_visible(),
|
||||
'maxLength' => $meta->get_tag()
|
||||
->getMaxLength(),
|
||||
'minLength' => $meta->get_tag()
|
||||
|
@@ -126,6 +126,16 @@ class LazaretController extends Controller
|
||||
|
||||
$ret = $lazaretManipulator->add($file_id, $keepAttributes, $attributesToKeep);
|
||||
|
||||
try{
|
||||
// get the new record
|
||||
$record = \Collection::getByBaseId($this->app, $request->request->get('bas_id'))->get_databox()->get_record($ret['result']['record_id']);
|
||||
$postStatus = (array) $request->request->get('status');
|
||||
// update status
|
||||
$this->updateRecordStatus($record, $postStatus);
|
||||
}catch(\Exception $e){
|
||||
$ret['message'] = $this->app->trans('An error occured when wanting to change status!');
|
||||
}
|
||||
|
||||
return $this->app->json($ret);
|
||||
}
|
||||
|
||||
@@ -216,6 +226,7 @@ class LazaretController extends Controller
|
||||
|
||||
return $this->app->json($ret);
|
||||
}
|
||||
$postStatus = (array) $request->request->get('status');
|
||||
|
||||
$path = $this->app['tmp.lazaret.path'] . '/';
|
||||
$lazaretFileName = $path .$lazaretFile->getFilename();
|
||||
@@ -233,6 +244,9 @@ class LazaretController extends Controller
|
||||
''
|
||||
);
|
||||
|
||||
// update status
|
||||
$this->updateRecordStatus($record, $postStatus);
|
||||
|
||||
//Delete lazaret file
|
||||
$manager = $this->getEntityManager();
|
||||
$manager->remove($lazaretFile);
|
||||
@@ -278,6 +292,35 @@ class LazaretController extends Controller
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @param $databox_id
|
||||
* @param $record_id
|
||||
* @return \Symfony\Component\HttpFoundation\JsonResponse
|
||||
*/
|
||||
public function getDestinationStatus(Request $request, $databox_id, $record_id)
|
||||
{
|
||||
if (!$request->isXmlHttpRequest()) {
|
||||
$this->app->abort(400);
|
||||
}
|
||||
$record = new \record_adapter($this->app, (int) $databox_id, (int) $record_id);
|
||||
$databox = $this->findDataboxById($databox_id);
|
||||
$statusStructure = $databox->getStatusStructure();
|
||||
$recordsStatuses = [];
|
||||
foreach ($statusStructure as $status) {
|
||||
// make the key as a string for the json usage in javascript
|
||||
$bit = "'".$status['bit']."'";
|
||||
if (!isset($recordsStatuses[$bit])) {
|
||||
$recordsStatuses[$bit] = $status;
|
||||
}
|
||||
$statusSet = \databox_status::bitIsSet($record->getStatusBitField(), $status['bit']);
|
||||
if (!isset($recordsStatuses[$bit]['flag'])) {
|
||||
$recordsStatuses[$bit]['flag'] = (int) $statusSet;
|
||||
}
|
||||
}
|
||||
return $this->app->json(['status' => $recordsStatuses]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return LazaretFileRepository
|
||||
*/
|
||||
@@ -293,4 +336,32 @@ class LazaretController extends Controller
|
||||
{
|
||||
return $this->app['border-manager'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set new status to selected record
|
||||
*
|
||||
* @param \record_adapter $record
|
||||
* @param array $postStatus
|
||||
* @return array|null
|
||||
*/
|
||||
private function updateRecordStatus(\record_adapter $record, array $postStatus)
|
||||
{
|
||||
$sbasId = $record->getDataboxId();
|
||||
if (isset($postStatus[$sbasId]) && is_array($postStatus[$sbasId])) {
|
||||
$postStatus = $postStatus[$sbasId];
|
||||
$currentStatus = strrev($record->getStatus());
|
||||
$newStatus = '';
|
||||
foreach (range(0, 31) as $i) {
|
||||
$newStatus .= isset($postStatus[$i]) ? ($postStatus[$i] ? '1' : '0') : $currentStatus[$i];
|
||||
}
|
||||
$record->setStatus(strrev($newStatus));
|
||||
$this->getDataboxLogger($record->getDatabox())
|
||||
->log($record, \Session_Logger::EVENT_STATUS, '', '');
|
||||
return [
|
||||
'current_status' => $currentStatus,
|
||||
'new_status' => $newStatus,
|
||||
];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@@ -193,7 +193,7 @@ class PushController extends Controller
|
||||
'Validation from %user%', [
|
||||
'%user%' => $this->getAuthenticatedUser()->getDisplayName(),
|
||||
]));
|
||||
$validation_description = $request->request->get('validation_description');
|
||||
$validation_description = $request->request->get('message');
|
||||
|
||||
$participants = $request->request->get('participants');
|
||||
|
||||
|
@@ -341,10 +341,20 @@ class QueryController extends Controller
|
||||
|
||||
if ($result->getTotal() === 0) {
|
||||
$template = 'prod/results/help.html.twig';
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
$template = 'prod/results/records.html.twig';
|
||||
}
|
||||
$json['results'] = $this->render($template, ['results'=> $result]);
|
||||
|
||||
/** @var \Closure $filter */
|
||||
$filter = $this->app['plugin.filter_by_authorization'];
|
||||
|
||||
$plugins = [
|
||||
'workzone' => $filter('workzone'),
|
||||
'actionbar' => $filter('actionbar'),
|
||||
];
|
||||
|
||||
$json['results'] = $this->render($template, ['results'=> $result, 'plugins'=>$plugins]);
|
||||
|
||||
|
||||
// add technical fields
|
||||
@@ -382,7 +392,6 @@ class QueryController extends Controller
|
||||
'labels' => $field->get_labels(),
|
||||
'type' => $field->get_type(),
|
||||
'field' => $field->get_name(),
|
||||
'query' => "field." . $field->get_name() . ":%s",
|
||||
'trans_label' => $field->get_label($this->app['locale']),
|
||||
];
|
||||
$field->get_label($this->app['locale']);
|
||||
|
48
lib/Alchemy/Phrasea/ControllerProvider/Api/V3.php
Normal file
48
lib/Alchemy/Phrasea/ControllerProvider/Api/V3.php
Normal 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;
|
||||
}
|
||||
}
|
@@ -82,6 +82,11 @@ class Lazaret implements ControllerProviderInterface, ServiceProviderInterface
|
||||
->assert('file_id', '\d+')
|
||||
->bind('lazaret_thumbnail');
|
||||
|
||||
$controllers->get('/{databox_id}/{record_id}/status', 'controller.prod.lazaret:getDestinationStatus')
|
||||
->assert('databox_id', '\d+')
|
||||
->assert('record_id', '\d+')
|
||||
->bind('lazaret_destination_status');
|
||||
|
||||
return $controllers;
|
||||
}
|
||||
}
|
||||
|
@@ -15,6 +15,7 @@ use Alchemy\Phrasea\Authentication\Context;
|
||||
use Alchemy\Phrasea\Controller\Api\Result;
|
||||
use Alchemy\Phrasea\ControllerProvider\Api\V1;
|
||||
use Alchemy\Phrasea\ControllerProvider\Api\V2;
|
||||
use Alchemy\Phrasea\ControllerProvider\Api\V3;
|
||||
use Alchemy\Phrasea\Core\Configuration\PropertyAccess;
|
||||
use Alchemy\Phrasea\Core\Event\ApiOAuth2EndEvent;
|
||||
use Alchemy\Phrasea\Core\Event\ApiOAuth2StartEvent;
|
||||
@@ -80,6 +81,8 @@ class OAuthListener
|
||||
$request->attributes->set('api_version', V1::VERSION);
|
||||
} elseif(mb_strpos($CalledController, 'controller.api.v2') !== FALSE) {
|
||||
$request->attributes->set('api_version', V2::VERSION);
|
||||
} elseif(mb_strpos($CalledController, 'controller.api.v3') !== FALSE) {
|
||||
$request->attributes->set('api_version', V3::VERSION);
|
||||
} else {
|
||||
$request->attributes->set('api_version', $oAuth2Account->getApiVersion());
|
||||
}
|
||||
|
@@ -78,6 +78,20 @@ class BorderManagerServiceProvider implements ServiceProviderInterface
|
||||
|
||||
$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;
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
$app['monolog']->error(
|
||||
|
@@ -16,7 +16,7 @@ class Version
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $number = '4.1.0-alpha.15a';
|
||||
private $number = '4.1.0-alpha.19a';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
|
@@ -36,6 +36,9 @@ final class DbalDataboxFieldRepository implements DataboxFieldRepository
|
||||
'label_fr',
|
||||
'label_de',
|
||||
'label_nl',
|
||||
'generate_cterms',
|
||||
'gui_editable',
|
||||
'gui_visible',
|
||||
];
|
||||
|
||||
/** @var DataboxFieldFactory */
|
||||
|
@@ -82,6 +82,34 @@ class LegacyRecordRepository implements RecordRepository
|
||||
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
|
||||
* @return \record_adapter[]
|
||||
@@ -99,6 +127,40 @@ class LegacyRecordRepository implements RecordRepository
|
||||
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)
|
||||
{
|
||||
static $sql;
|
||||
@@ -120,7 +182,7 @@ class LegacyRecordRepository implements RecordRepository
|
||||
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) {
|
||||
return [];
|
||||
@@ -129,9 +191,54 @@ class LegacyRecordRepository implements RecordRepository
|
||||
$connection = $this->databox->get_connection();
|
||||
|
||||
$selects = $this->getRecordSelects();
|
||||
|
||||
if ($max_items) {
|
||||
array_unshift($selects, 'sr.rid_parent as story_id');
|
||||
|
||||
$subBuilder = $connection->createQueryBuilder();
|
||||
|
||||
$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')
|
||||
@@ -140,6 +247,7 @@ class LegacyRecordRepository implements RecordRepository
|
||||
's.rid_parent IN (:storyIds)',
|
||||
'r.parent_record_id = 0'
|
||||
)
|
||||
->orderBy('s.ord', 'ASC')
|
||||
->setParameter('storyIds', $storyIds, Connection::PARAM_INT_ARRAY)
|
||||
;
|
||||
|
||||
@@ -148,6 +256,8 @@ class LegacyRecordRepository implements RecordRepository
|
||||
}
|
||||
|
||||
$data = $connection->fetchAll($builder->getSQL(), $builder->getParameters(), $builder->getParameterTypes());
|
||||
}
|
||||
|
||||
$records = $this->mapRecordsFromResultSet($data);
|
||||
|
||||
$selections = array_map(
|
||||
|
@@ -26,12 +26,26 @@ interface RecordRepository
|
||||
*/
|
||||
public function findBySha256($sha256);
|
||||
|
||||
/**
|
||||
* @param string $sha256
|
||||
* @param array $excludedCollIds
|
||||
* @return \record_adapter[]
|
||||
*/
|
||||
public function findBySha256WithExcludedCollIds($sha256, $excludedCollIds = []);
|
||||
|
||||
/**
|
||||
* @param string $uuid
|
||||
* @return \record_adapter[]
|
||||
*/
|
||||
public function findByUuid($uuid);
|
||||
|
||||
/**
|
||||
* @param string $uuid
|
||||
* @param array $excludedCollIds
|
||||
* @return \record_adapter[]
|
||||
*/
|
||||
public function findByUuidWithExcludedCollIds($uuid, $excludedCollIds = []);
|
||||
|
||||
/**
|
||||
* @param array $recordIds
|
||||
* @return \record_adapter[]
|
||||
@@ -43,9 +57,11 @@ interface RecordRepository
|
||||
*
|
||||
* @param int[] $storyIds
|
||||
* @param null|int|User $user
|
||||
* @param int $offset
|
||||
* @param null|int $max_items
|
||||
* @return \set_selection[]
|
||||
*/
|
||||
public function findChildren(array $storyIds, $user = null);
|
||||
public function findChildren(array $storyIds, $user = null, $offset = 1, $max_items = null);
|
||||
|
||||
|
||||
/**
|
||||
|
@@ -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\Enum($this->translator->trans('Video Codec'), self::OPTION_VCODEC, ['libx264', 'libvpx', 'libtheora'], 'libx264'));
|
||||
$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()
|
||||
|
@@ -12,6 +12,7 @@
|
||||
namespace Alchemy\Phrasea\Model\Entities;
|
||||
|
||||
use Alchemy\Phrasea\Application;
|
||||
use Alchemy\Phrasea\Border\Attribute\AttributeInterface;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Gedmo\Mapping\Annotation as Gedmo;
|
||||
use \record_adapter;
|
||||
@@ -474,4 +475,32 @@ class LazaretFile
|
||||
return $merged;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Application $app
|
||||
* @return array|null
|
||||
*/
|
||||
public function getStatus(Application $app)
|
||||
{
|
||||
/**@var LazaretAttribute $atribute*/
|
||||
foreach ($this->attributes as $atribute) {
|
||||
if ($atribute->getName() == AttributeInterface::NAME_STATUS) {
|
||||
$databox = $this->getCollection($app)->get_databox();
|
||||
$statusStructure = $databox->getStatusStructure();
|
||||
$recordsStatuses = [];
|
||||
foreach ($statusStructure as $status) {
|
||||
$bit = $status['bit'];
|
||||
if (!isset($recordsStatuses[$bit])) {
|
||||
$recordsStatuses[$bit] = $status;
|
||||
}
|
||||
$statusSet = \databox_status::bitIsSet(bindec($atribute->getValue()), $bit);
|
||||
if (!isset($recordsStatuses[$bit]['flag'])) {
|
||||
$recordsStatuses[$bit]['flag'] = (int) $statusSet;
|
||||
}
|
||||
}
|
||||
return $recordsStatuses;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -226,6 +226,8 @@ class LazaretManipulator
|
||||
$this->entityManager->remove($lazaretFile);
|
||||
$this->entityManager->flush();
|
||||
|
||||
$ret['result']['record_id'] = $record->getRecordId();
|
||||
|
||||
$ret['success'] = true;
|
||||
} catch (\Exception $e) {
|
||||
$ret['message'] = $this->app->trans('An error occured');
|
||||
|
@@ -232,7 +232,7 @@ class BasketRepository extends EntityRepository
|
||||
$dql = "SELECT b\n"
|
||||
. "FROM Phraseanet:Basket b\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 = [
|
||||
'usr_id' => $user->getId()
|
||||
];
|
||||
|
@@ -45,6 +45,9 @@ class TextNode extends AbstractTermNode implements ContextAbleInterface
|
||||
foreach ($context->localizeField($field) as $f) {
|
||||
$index_fields[] = $f;
|
||||
}
|
||||
foreach ($context->truncationField($field) as $f) {
|
||||
$index_fields[] = $f;
|
||||
}
|
||||
}
|
||||
if (!$index_fields) {
|
||||
return null;
|
||||
|
@@ -323,7 +323,7 @@ class ElasticsearchOptions
|
||||
"aggregated (2 values: fired = 0 or 1)" => -1,
|
||||
],
|
||||
'output_formatter' => function($value) {
|
||||
static $map = ['0'=>"No flash", '1'=>"Flash"];
|
||||
static $map = ["false"=>"No flash", "true"=>"Flash", '0'=>"No flash", '1'=>"Flash"];
|
||||
return array_key_exists($value, $map) ? $map[$value] : $value;
|
||||
},
|
||||
],
|
||||
|
@@ -90,6 +90,16 @@ class Index
|
||||
// TODO Maybe replace nfkc_normalizer + asciifolding with icu_folding
|
||||
'filter' => ['nfkc_normalizer', 'asciifolding']
|
||||
],
|
||||
'truncation_analyzer' => [
|
||||
'type' => 'custom',
|
||||
'tokenizer' => 'truncation_tokenizer',
|
||||
'filter' => ['lowercase', 'stop', 'kstem']
|
||||
],
|
||||
'truncation_analyzer#search' => [
|
||||
'type' => 'custom',
|
||||
'tokenizer' => 'truncation_tokenizer',
|
||||
'filter' => ['lowercase', 'stop', 'kstem']
|
||||
],
|
||||
// Lang specific
|
||||
'fr_full' => [
|
||||
'type' => 'custom',
|
||||
@@ -145,6 +155,12 @@ class Index
|
||||
]
|
||||
],
|
||||
'tokenizer' => [
|
||||
'truncation_tokenizer' => [
|
||||
"type" => "edgeNGram",
|
||||
"min_gram" => "2",
|
||||
"max_gram" => "15",
|
||||
"token_chars" => [ "letter", "digit", "punctuation", "symbol" ]
|
||||
],
|
||||
'thesaurus_path' => [
|
||||
'type' => 'path_hierarchy'
|
||||
]
|
||||
|
@@ -13,6 +13,7 @@ namespace Alchemy\Phrasea\SearchEngine\Elastic\Indexer\Record\Hydrator;
|
||||
|
||||
use Alchemy\Phrasea\SearchEngine\Elastic\Exception\Exception;
|
||||
use Alchemy\Phrasea\SearchEngine\Elastic\RecordHelper;
|
||||
use Alchemy\Phrasea\SearchEngine\Elastic\Structure\GlobalStructure;
|
||||
use Alchemy\Phrasea\SearchEngine\Elastic\Thesaurus;
|
||||
use Alchemy\Phrasea\SearchEngine\Elastic\Thesaurus\CandidateTerms;
|
||||
use Alchemy\Phrasea\SearchEngine\Elastic\Thesaurus\Concept;
|
||||
@@ -27,7 +28,7 @@ class ThesaurusHydrator implements HydratorInterface
|
||||
private $thesaurus;
|
||||
private $candidate_terms;
|
||||
|
||||
public function __construct(Structure $structure, Thesaurus $thesaurus, CandidateTerms $candidate_terms)
|
||||
public function __construct(GlobalStructure $structure, Thesaurus $thesaurus, CandidateTerms $candidate_terms)
|
||||
{
|
||||
$this->structure = $structure;
|
||||
$this->thesaurus = $thesaurus;
|
||||
@@ -42,7 +43,7 @@ class ThesaurusHydrator implements HydratorInterface
|
||||
$fields = [];
|
||||
$index_fields = [];
|
||||
foreach ($structure as $name => $field) {
|
||||
$fields[$name] = $field->getThesaurusRoots();
|
||||
$fields[$name] = $field; // ->getThesaurusRoots();
|
||||
$index_fields[$name] = $field->getIndexField();
|
||||
}
|
||||
// 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'])) {
|
||||
throw new Exception('Expected a record with the "databox_id" key set.');
|
||||
@@ -61,7 +68,14 @@ class ThesaurusHydrator implements HydratorInterface
|
||||
$terms = array();
|
||||
$filters = 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
|
||||
$field_values = \igorw\get_in($record, explode('.', $index_fields[$name]));
|
||||
if ($field_values !== null) {
|
||||
@@ -84,12 +98,13 @@ class ThesaurusHydrator implements HydratorInterface
|
||||
$bulk = $this->thesaurus->findConceptsBulk($terms, null, $filters, true);
|
||||
|
||||
foreach ($bulk as $offset => $item_concepts) {
|
||||
if ($item_concepts && is_array($item_concepts) && count($item_concepts)>0) {
|
||||
$name = $field_names[$offset];
|
||||
if ($item_concepts && is_array($item_concepts) && count($item_concepts)>0) {
|
||||
foreach ($item_concepts as $concept) {
|
||||
$record['concept_path'][$name][] = $concept->getPath();
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
$this->candidate_terms->insert($field_names[$offset], $values[$offset]);
|
||||
}
|
||||
}
|
||||
|
@@ -55,8 +55,13 @@ class StringFieldMapping extends ComplexFieldMapping
|
||||
{
|
||||
$child = new StringFieldMapping('light');
|
||||
$child->setAnalyzer('general_light');
|
||||
|
||||
$this->addChild($child);
|
||||
|
||||
$child = new StringFieldMapping('truncated');
|
||||
$child->setAnalyzer('truncation_analyzer', 'indexing');
|
||||
$child->setAnalyzer('truncation_analyzer#search', 'searching');
|
||||
$this->addChild($child);
|
||||
|
||||
$this->addLocalizedChildren($locales);
|
||||
|
||||
return $this;
|
||||
|
@@ -4,6 +4,11 @@ namespace Alchemy\Phrasea\SearchEngine\Elastic\Search;
|
||||
|
||||
class Escaper
|
||||
{
|
||||
public function quoteWord($value)
|
||||
{
|
||||
return '"' . $this->escapeRaw($value) . '"';
|
||||
}
|
||||
|
||||
public function escapeWord($value)
|
||||
{
|
||||
// Strip double quotes from values to prevent broken queries
|
||||
|
@@ -46,22 +46,23 @@ class FacetsResponse
|
||||
if (!isset($bucket['key']) || !isset($bucket['doc_count'])) {
|
||||
$this->throwAggregationResponseError();
|
||||
}
|
||||
$key = array_key_exists('key_as_string', $bucket) ? $bucket['key_as_string'] : $bucket['key'];
|
||||
if($tf) {
|
||||
// the field is one of the hardcoded tech fields
|
||||
$value = [
|
||||
'value' => $valueFormatter($bucket['key']),
|
||||
'raw_value' => $bucket['key'],
|
||||
'value' => $valueFormatter($key),
|
||||
'raw_value' => $key,
|
||||
'count' => $bucket['doc_count'],
|
||||
'query' => sprintf($tf['query'], $this->escaper->escapeWord($bucket['key']))
|
||||
'query' => sprintf($tf['query'], $this->escaper->escapeWord($key))
|
||||
];
|
||||
}
|
||||
else {
|
||||
// the field is a normal field
|
||||
$value = [
|
||||
'value' => $bucket['key'],
|
||||
'raw_value' => $bucket['key'],
|
||||
'value' => $key,
|
||||
'raw_value' => $key,
|
||||
'count' => $bucket['doc_count'],
|
||||
'query' => sprintf('field.%s:%s', $this->escaper->escapeWord($name), $this->escaper->escapeWord($bucket['key']))
|
||||
'query' => sprintf('field.%s=%s', $this->escaper->escapeWord($name), $this->escaper->quoteWord($key))
|
||||
];
|
||||
}
|
||||
|
||||
|
@@ -9,6 +9,7 @@ use Alchemy\Phrasea\SearchEngine\Elastic\Structure\Field;
|
||||
use Alchemy\Phrasea\SearchEngine\Elastic\AST\Field as ASTField;
|
||||
use Alchemy\Phrasea\SearchEngine\Elastic\AST\Flag;
|
||||
use Alchemy\Phrasea\SearchEngine\Elastic\Structure\Structure;
|
||||
use Alchemy\Phrasea\SearchEngine\SearchEngineOptions;
|
||||
|
||||
/**
|
||||
* @todo Check for private fields and only search on them if allowed
|
||||
@@ -23,13 +24,23 @@ class QueryContext
|
||||
private $queryLocale;
|
||||
/** @var array */
|
||||
private $fields;
|
||||
/** @var SearchEngineOptions */
|
||||
private $options;
|
||||
|
||||
public function __construct(Structure $structure, array $locales, $queryLocale, array $fields = null)
|
||||
/**
|
||||
* @param SearchEngineOptions|null $options
|
||||
* @param Structure $structure
|
||||
* @param array $locales
|
||||
* @param $queryLocale
|
||||
* @param array $fields
|
||||
*/
|
||||
public function __construct($options, Structure $structure, array $locales, $queryLocale, array $fields = null)
|
||||
{
|
||||
$this->structure = $structure;
|
||||
$this->locales = $locales;
|
||||
$this->queryLocale = $queryLocale;
|
||||
$this->fields = $fields;
|
||||
$this->options = $options;
|
||||
}
|
||||
|
||||
public function narrowToFields(array $fields)
|
||||
@@ -43,7 +54,7 @@ class QueryContext
|
||||
}
|
||||
}
|
||||
|
||||
return new static($this->structure, $this->locales, $this->queryLocale, $fields);
|
||||
return new static($this->options, $this->structure, $this->locales, $this->queryLocale, $fields);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -131,6 +142,16 @@ class QueryContext
|
||||
return $ret;
|
||||
}
|
||||
|
||||
public function truncationField(Field $field)
|
||||
{
|
||||
if($this->options && $this->options->useTruncation()) {
|
||||
return [sprintf('%s.truncated', $field->getIndexField())];
|
||||
}
|
||||
else {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
private function localizeFieldName($field)
|
||||
{
|
||||
$fields = array();
|
||||
|
@@ -23,7 +23,7 @@ class QueryContextFactory
|
||||
? $this->getLimitedStructure($options)
|
||||
: $this->structure;
|
||||
|
||||
$context = new QueryContext($structure, $this->locales, $this->current_locale);
|
||||
$context = new QueryContext($options, $structure, $this->locales, $this->current_locale);
|
||||
|
||||
if ($options) {
|
||||
$fields = $this->getSearchedFields($options);
|
||||
|
@@ -63,4 +63,26 @@ class StringUtils
|
||||
|
||||
return self::$transliterator->transliterate($string);
|
||||
}
|
||||
|
||||
/**
|
||||
* replace bad chars (ascii 0...31 except 9,10,13)
|
||||
*
|
||||
* @param $s
|
||||
* @param string $replace
|
||||
* @return mixed
|
||||
*/
|
||||
public static function substituteCtrlCharacters($s, $replace = '_')
|
||||
{
|
||||
static $bad_chars = null;
|
||||
if($bad_chars === null) {
|
||||
$bad_chars = [];
|
||||
for($i=0; $i<32; $i++) {
|
||||
if($i != 9 && $i != 10 && $i != 13) {
|
||||
$bad_chars[] = chr($i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return str_replace($bad_chars, $replace, $s);
|
||||
}
|
||||
}
|
||||
|
@@ -24,6 +24,11 @@ class Field implements Typed
|
||||
*/
|
||||
private $name;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $databox_id;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
@@ -43,6 +48,8 @@ class Field implements Typed
|
||||
|
||||
private $thesaurus_roots;
|
||||
|
||||
private $generate_cterms;
|
||||
|
||||
private $used_by_collections;
|
||||
|
||||
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, [
|
||||
'databox_id' => $databox->get_sbas_id(),
|
||||
'searchable' => $field->is_indexable(),
|
||||
'private' => $field->isBusiness(),
|
||||
'facet' => $facet,
|
||||
'thesaurus_roots' => $roots,
|
||||
'generate_cterms' => $field->get_generate_cterms(),
|
||||
'used_by_collections' => $databox->get_collection_unique_ids()
|
||||
]);
|
||||
}
|
||||
@@ -99,10 +108,12 @@ class Field implements Typed
|
||||
{
|
||||
$this->name = (string) $name;
|
||||
$this->type = $type;
|
||||
$this->databox_id = \igorw\get_in($options, ['databox_id'], 0);
|
||||
$this->is_searchable = \igorw\get_in($options, ['searchable'], true);
|
||||
$this->is_private = \igorw\get_in($options, ['private'], false);
|
||||
$this->facet = \igorw\get_in($options, ['facet']);
|
||||
$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'], []);
|
||||
|
||||
Assertion::boolean($this->is_searchable);
|
||||
@@ -122,10 +133,12 @@ class Field implements Typed
|
||||
public function withOptions(array $options)
|
||||
{
|
||||
return new self($this->name, $this->type, $options + [
|
||||
'databox_id' => $this->databox_id,
|
||||
'searchable' => $this->is_searchable,
|
||||
'private' => $this->is_private,
|
||||
'facet' => $this->facet,
|
||||
'thesaurus_roots' => $this->thesaurus_roots,
|
||||
'generate_cterms' => $this->generate_cterms,
|
||||
'used_by_collections' => $this->used_by_collections
|
||||
]);
|
||||
}
|
||||
@@ -150,6 +163,11 @@ class Field implements Typed
|
||||
return sprintf('concept_path.%s', $this->name);
|
||||
}
|
||||
|
||||
public function get_databox_id()
|
||||
{
|
||||
return $this->databox_id;
|
||||
}
|
||||
|
||||
public function getType()
|
||||
{
|
||||
return $this->type;
|
||||
@@ -190,6 +208,11 @@ class Field implements Typed
|
||||
return $this->thesaurus_roots;
|
||||
}
|
||||
|
||||
public function get_generate_cterms()
|
||||
{
|
||||
return $this->generate_cterms;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge with another field, returning the new instance
|
||||
*
|
||||
|
@@ -14,6 +14,12 @@ final class GlobalStructure implements Structure
|
||||
*/
|
||||
private $fields = array();
|
||||
|
||||
|
||||
/**
|
||||
* @var Field[][]
|
||||
*/
|
||||
private $fieldsByDatabox = [];
|
||||
|
||||
/**
|
||||
* @var Field[]
|
||||
* */
|
||||
@@ -119,6 +125,10 @@ final class GlobalStructure implements Structure
|
||||
|
||||
public function add(Field $field)
|
||||
{
|
||||
// store info for each field, not still merged by databox
|
||||
$this->fieldsByDatabox[$field->get_databox_id()][$field->getName()] = $field;
|
||||
|
||||
// store merged infos (same field name)
|
||||
$name = $field->getName();
|
||||
|
||||
if (isset($this->fields[$name])) {
|
||||
@@ -152,6 +162,11 @@ final class GlobalStructure implements Structure
|
||||
return $this->fields;
|
||||
}
|
||||
|
||||
public function getAllFieldsByDatabox($databox_id)
|
||||
{
|
||||
return $this->fieldsByDatabox[$databox_id];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Field[]
|
||||
*/
|
||||
|
@@ -32,6 +32,7 @@ class CandidateTerms
|
||||
|
||||
public function insert($field, $value)
|
||||
{
|
||||
$value = StringUtils::substituteCtrlCharacters($value, '');
|
||||
$this->ensureVisitorSetup();
|
||||
if (!$this->visitor->hasTerm($field, $value)) {
|
||||
$this->new_candidates[$value] = $field;
|
||||
|
@@ -71,6 +71,8 @@ class SearchEngineOptions
|
||||
protected $i18n;
|
||||
/** @var bool */
|
||||
protected $stemming = true;
|
||||
/** @var bool */
|
||||
protected $use_truncation = false;
|
||||
/** @var string */
|
||||
protected $sort_by;
|
||||
|
||||
@@ -105,7 +107,8 @@ class SearchEngineOptions
|
||||
'sort_ord',
|
||||
'business_fields',
|
||||
'max_results',
|
||||
'first_result'
|
||||
'first_result',
|
||||
'use_truncation',
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -217,6 +220,29 @@ class SearchEngineOptions
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells whether to use truncation or not
|
||||
*
|
||||
* @param boolean $boolean
|
||||
* @return $this
|
||||
*/
|
||||
public function setUseTruncation($boolean)
|
||||
{
|
||||
$this->use_truncation = !!$boolean;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return wheter the use of truncation is enabled or not
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function useTruncation()
|
||||
{
|
||||
return $this->use_truncation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return wheter the use of stemming is enabled or not
|
||||
*
|
||||
@@ -542,6 +568,8 @@ class SearchEngineOptions
|
||||
$options->setFields($databoxFields);
|
||||
$options->setDateFields($databoxDateFields);
|
||||
|
||||
$options->setUseTruncation((Boolean) $request->get('truncation'));
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
@@ -628,6 +656,7 @@ class SearchEngineOptions
|
||||
}
|
||||
},
|
||||
'stemming' => $optionSetter('setStemming'),
|
||||
'use_truncation' => $optionSetter('setUseTruncation'),
|
||||
'date_fields' => function ($value, SearchEngineOptions $options) use ($fieldNormalizer) {
|
||||
$options->setDateFields($fieldNormalizer($value));
|
||||
},
|
||||
|
@@ -48,6 +48,7 @@ class PhraseanetExtension extends \Twig_Extension
|
||||
new \Twig_SimpleFunction('border_checker_from_fqcn', array($this, 'getCheckerFromFQCN')),
|
||||
new \Twig_SimpleFunction('caption_field', array($this, 'getCaptionField')),
|
||||
new \Twig_SimpleFunction('caption_field_label', array($this, 'getCaptionFieldLabel')),
|
||||
new \Twig_SimpleFunction('caption_field_gui_visible', array($this, 'getCaptionFieldGuiVisible')),
|
||||
new \Twig_SimpleFunction('caption_field_order', array($this, 'getCaptionFieldOrder')),
|
||||
|
||||
new \Twig_SimpleFunction('flag_slugify', array(Flag::class, 'normalizeName')),
|
||||
@@ -77,6 +78,29 @@ class PhraseanetExtension extends \Twig_Extension
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* get localized field's gui_visible
|
||||
* @param RecordInterface $record
|
||||
* @param $fieldName
|
||||
* @return string - the name gui_visible
|
||||
*/
|
||||
public function getCaptionFieldGuiVisible(RecordInterface $record, $fieldName)
|
||||
{
|
||||
if ($record) {
|
||||
/** @var \appbox $appbox */
|
||||
$appbox = $this->app['phraseanet.appbox'];
|
||||
$databox = $appbox->get_databox($record->getDataboxId());
|
||||
foreach ($databox->get_meta_structure() as $meta) {
|
||||
/** @var \databox_field $meta */
|
||||
if ($meta->get_name() === $fieldName) {
|
||||
return $meta->get_gui_visible($this->app['locale']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
public function getCaptionField(RecordInterface $record, $field, $value)
|
||||
{
|
||||
if ($record instanceof ElasticsearchRecord) {
|
||||
|
@@ -80,7 +80,7 @@ class RedisSessionHandler implements \SessionHandlerInterface
|
||||
*/
|
||||
public function destroy($sessionId)
|
||||
{
|
||||
return 1 === $this->redis->delete($this->prefix.$sessionId);
|
||||
return 1 === $this->redis->del($this->prefix.$sessionId);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -462,6 +462,9 @@ class databox extends base implements ThumbnailedElement
|
||||
->set_aggregable((isset($field['aggregable']) ? (string) $field['aggregable'] : 0))
|
||||
->set_type($type)
|
||||
->set_tbranch(isset($field['tbranch']) ? (string) $field['tbranch'] : '')
|
||||
->set_generate_cterms((isset($field['generate_cterms']) && (string) $field['generate_cterms'] == 1))
|
||||
->set_gui_editable((!isset($field['gui_editable']) || (isset($field['gui_editable']) && (string) $field['gui_editable'] == 1)))
|
||||
->set_gui_visible((!isset($field['gui_visible']) || (isset($field['gui_visible']) && (string) $field['gui_visible'] == 1)))
|
||||
->set_thumbtitle(isset($field['thumbtitle']) ? (string) $field['thumbtitle'] : (isset($field['thumbTitle']) ? $field['thumbTitle'] : '0'))
|
||||
->set_report(isset($field['report']) ? (string) $field['report'] : '1')
|
||||
->save();
|
||||
@@ -1214,21 +1217,40 @@ class databox extends base implements ThumbnailedElement
|
||||
$domct = $this->get_dom_cterms();
|
||||
|
||||
if ($domct !== false) {
|
||||
$changed = false;
|
||||
$nodesToDel = [];
|
||||
// loop on first level : "fields"
|
||||
for($n = $domct->documentElement->firstChild; $n; $n = $n->nextSibling) {
|
||||
if($n->nodeType == XML_ELEMENT_NODE && !($n->getAttribute('delbranch'))){
|
||||
$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) {
|
||||
$n->parentNode->removeChild($n);
|
||||
$changed = true;
|
||||
}
|
||||
if(!empty($nodesToDel)) {
|
||||
if($changed) {
|
||||
$this->saveCterms($domct);
|
||||
}
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
// no-op
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -43,6 +43,9 @@ class databox_field implements cache_cacheableInterface
|
||||
protected $report;
|
||||
protected $type;
|
||||
protected $tbranch;
|
||||
protected $generate_cterms;
|
||||
protected $gui_editable;
|
||||
protected $gui_visible;
|
||||
protected $separator;
|
||||
protected $thumbtitle;
|
||||
|
||||
@@ -166,6 +169,9 @@ class databox_field implements cache_cacheableInterface
|
||||
$this->position = (int)$row['sorter'];
|
||||
$this->type = $row['type'] ?: self::TYPE_STRING;
|
||||
$this->tbranch = $row['tbranch'];
|
||||
$this->generate_cterms = (bool)$row['generate_cterms'];
|
||||
$this->gui_editable = (bool)$row['gui_editable'];
|
||||
$this->gui_visible = (bool)$row['gui_visible'];
|
||||
$this->VocabularyType = $row['VocabularyControlType'];
|
||||
$this->VocabularyRestriction = (bool)$row['RestrictToVocabularyControl'];
|
||||
|
||||
@@ -306,6 +312,9 @@ class databox_field implements cache_cacheableInterface
|
||||
`report` = :report,
|
||||
`type` = :type,
|
||||
`tbranch` = :tbranch,
|
||||
`generate_cterms` = :generate_cterms,
|
||||
`gui_editable` = :gui_editable,
|
||||
`gui_visible` = :gui_visible,
|
||||
`sorter` = :position,
|
||||
`thumbtitle` = :thumbtitle,
|
||||
`VocabularyControlType` = :VocabularyControlType,
|
||||
@@ -329,6 +338,9 @@ class databox_field implements cache_cacheableInterface
|
||||
':report' => $this->report ? '1' : '0',
|
||||
':type' => $this->type,
|
||||
':tbranch' => $this->tbranch,
|
||||
':generate_cterms' => $this->generate_cterms ? '1' : '0',
|
||||
':gui_editable' => $this->gui_editable ? '1' : '0',
|
||||
':gui_visible' => $this->gui_visible ? '1' : '0',
|
||||
':position' => $this->position,
|
||||
':thumbtitle' => $this->thumbtitle,
|
||||
':VocabularyControlType' => $this->getVocabularyControl() ? $this->getVocabularyControl()->getType() : null,
|
||||
@@ -380,6 +392,9 @@ class databox_field implements cache_cacheableInterface
|
||||
$meta->setAttribute('aggregable', $this->aggregable);
|
||||
$meta->setAttribute('type', $this->type);
|
||||
$meta->setAttribute('tbranch', $this->tbranch);
|
||||
$meta->setAttribute('generate_cterms', $this->generate_cterms ? '1' : '0');
|
||||
$meta->setAttribute('gui_editable', $this->gui_editable ? '1' : '0');
|
||||
$meta->setAttribute('gui_visible', $this->gui_visible ? '1' : '0');
|
||||
if ($this->multi) {
|
||||
$meta->setAttribute('separator', $this->separator);
|
||||
}
|
||||
@@ -711,6 +726,39 @@ class databox_field implements cache_cacheableInterface
|
||||
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
|
||||
@@ -795,6 +843,33 @@ class databox_field implements cache_cacheableInterface
|
||||
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
|
||||
*
|
||||
@@ -905,6 +980,9 @@ class databox_field implements cache_cacheableInterface
|
||||
'sorter' => $this->position,
|
||||
'thumbtitle' => $this->thumbtitle,
|
||||
'tbranch' => $this->tbranch,
|
||||
'generate_cterms' => $this->generate_cterms,
|
||||
'gui_editable' => $this->gui_editable,
|
||||
'gui_visible' => $this->gui_visible,
|
||||
'separator' => $this->separator,
|
||||
'required' => $this->required,
|
||||
'report' => $this->report,
|
||||
@@ -943,10 +1021,10 @@ class databox_field implements cache_cacheableInterface
|
||||
}
|
||||
|
||||
$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`,
|
||||
`report`, `sorter`, `separator`)
|
||||
VALUES (null, :name, '', 0, 0, 1, 'string', '',
|
||||
VALUES (null, :name, '', 0, 1, 1, 0, 1, 'string', '', 1,
|
||||
null, 0, 0, 0,
|
||||
1, :sorter, '')";
|
||||
|
||||
|
67
lib/classes/patch/410alpha17a.php
Normal file
67
lib/classes/patch/410alpha17a.php
Normal 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;
|
||||
}
|
||||
}
|
69
lib/classes/patch/410alpha19a.php
Normal file
69
lib/classes/patch/410alpha19a.php
Normal 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;
|
||||
}
|
||||
}
|
@@ -1685,6 +1685,43 @@ class record_adapter implements RecordInterface, cache_cacheableInterface
|
||||
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[]
|
||||
* @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[]
|
||||
* @throws Exception
|
||||
* @throws \Doctrine\DBAL\DBALException
|
||||
*/
|
||||
public function getChildren()
|
||||
public function getChildren($offset = 1, $max_items = null)
|
||||
{
|
||||
if (!$this->isStory()) {
|
||||
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);
|
||||
}
|
||||
|
@@ -2033,6 +2033,30 @@
|
||||
<default></default>
|
||||
<comment></comment>
|
||||
</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>
|
||||
<indexes>
|
||||
<index>
|
||||
|
@@ -79,7 +79,7 @@
|
||||
<size>748</size>
|
||||
<mediatype>video</mediatype>
|
||||
<writeDatas>yes</writeDatas>
|
||||
<acodec>libfaac</acodec>
|
||||
<acodec>libmp3lame</acodec>
|
||||
<vcodec>libx264</vcodec>
|
||||
<devices>screen</devices>
|
||||
<bitrate>1000</bitrate>
|
||||
|
@@ -79,7 +79,7 @@
|
||||
<size>748</size>
|
||||
<mediatype>video</mediatype>
|
||||
<writeDatas>yes</writeDatas>
|
||||
<acodec>libfaac</acodec>
|
||||
<acodec>libmp3lame</acodec>
|
||||
<vcodec>libx264</vcodec>
|
||||
<devices>screen</devices>
|
||||
<bitrate>1000</bitrate>
|
||||
|
@@ -79,7 +79,7 @@
|
||||
<size>748</size>
|
||||
<mediatype>video</mediatype>
|
||||
<writeDatas>yes</writeDatas>
|
||||
<acodec>libfaac</acodec>
|
||||
<acodec>libmp3lame</acodec>
|
||||
<vcodec>libx264</vcodec>
|
||||
<devices>screen</devices>
|
||||
<bitrate>1000</bitrate>
|
||||
|
@@ -65,7 +65,7 @@
|
||||
"normalize-css": "^2.1.0",
|
||||
"npm": "^6.0.0",
|
||||
"npm-modernizr": "^2.8.3",
|
||||
"phraseanet-production-client": "0.34.72-d",
|
||||
"phraseanet-production-client": "^0.34.86-d",
|
||||
"requirejs": "^2.3.5",
|
||||
"tinymce": "^4.0.28",
|
||||
"underscore": "^1.8.3",
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:jms="urn:jms:translation" version="1.2">
|
||||
<file date="2019-05-21T05:53:02Z" source-language="en" target-language="de" datatype="plaintext" original="not.available">
|
||||
<file date="2019-11-19T08:48:53Z" source-language="en" target-language="de" datatype="plaintext" original="not.available">
|
||||
<header>
|
||||
<tool tool-id="JMSTranslationBundle" tool-name="JMSTranslationBundle" tool-version="1.1.0-DEV"/>
|
||||
<note>The source node in most cases contains the sample message as written by the developer. If it looks like a dot-delimitted string such as "form.label.firstname", then the developer has not provided a default message.</note>
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:jms="urn:jms:translation" version="1.2">
|
||||
<file date="2019-05-21T05:54:16Z" source-language="en" target-language="en" datatype="plaintext" original="not.available">
|
||||
<file date="2019-11-19T08:49:17Z" source-language="en" target-language="en" datatype="plaintext" original="not.available">
|
||||
<header>
|
||||
<tool tool-id="JMSTranslationBundle" tool-name="JMSTranslationBundle" tool-version="1.1.0-DEV"/>
|
||||
<note>The source node in most cases contains the sample message as written by the developer. If it looks like a dot-delimitted string such as "form.label.firstname", then the developer has not provided a default message.</note>
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:jms="urn:jms:translation" version="1.2">
|
||||
<file date="2019-05-21T05:55:28Z" source-language="en" target-language="fr" datatype="plaintext" original="not.available">
|
||||
<file date="2019-11-15T08:03:23Z" source-language="en" target-language="fr" datatype="plaintext" original="not.available">
|
||||
<header>
|
||||
<tool tool-id="JMSTranslationBundle" tool-name="JMSTranslationBundle" tool-version="1.1.0-DEV"/>
|
||||
<note>The source node in most cases contains the sample message as written by the developer. If it looks like a dot-delimitted string such as "form.label.firstname", then the developer has not provided a default message.</note>
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:jms="urn:jms:translation" version="1.2">
|
||||
<file date="2019-05-21T05:56:47Z" source-language="en" target-language="nl" datatype="plaintext" original="not.available">
|
||||
<file date="2019-11-19T08:50:13Z" source-language="en" target-language="nl" datatype="plaintext" original="not.available">
|
||||
<header>
|
||||
<tool tool-id="JMSTranslationBundle" tool-name="JMSTranslationBundle" tool-version="1.1.0-DEV"/>
|
||||
<note>The source node in most cases contains the sample message as written by the developer. If it looks like a dot-delimitted string such as "form.label.firstname", then the developer has not provided a default message.</note>
|
||||
|
BIN
resources/www/common/images/icons/basket_feedback_read.png
Normal file
BIN
resources/www/common/images/icons/basket_feedback_read.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.3 KiB |
BIN
resources/www/common/images/icons/basket_feedback_unread.png
Normal file
BIN
resources/www/common/images/icons/basket_feedback_unread.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.5 KiB |
31
resources/www/common/images/icons/icon-right-arrow.svg
Normal file
31
resources/www/common/images/icons/icon-right-arrow.svg
Normal 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 |
1
resources/www/common/images/icons/to_be_denied.svg
Normal file
1
resources/www/common/images/icons/to_be_denied.svg
Normal 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 |
1
resources/www/common/images/icons/to_be_validated.svg
Normal file
1
resources/www/common/images/icons/to_be_validated.svg
Normal 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 |
@@ -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-"] {
|
||||
/* use !important to prevent issues with browser extensions that change fonts */
|
||||
|
@@ -50,7 +50,9 @@ $mainMenuLinkBackgroundHoverColor: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[class*=" icon-"].fa, [class^=icon-].fa, .fa {
|
||||
font-family: Fontawesome!important;
|
||||
}
|
||||
[class^="icon-"], [class*=" icon-"].icomoon {
|
||||
display: inline-block;
|
||||
width: inherit;
|
||||
|
@@ -1,4 +1,6 @@
|
||||
$iconsPath: '../../../assets/common/images/icons/';
|
||||
$feedbackColor : #8bc34a;
|
||||
$basketColor : #2196f3 ;
|
||||
ul.image_set {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
@@ -6,30 +8,66 @@ ul.image_set{
|
||||
width: 100%;
|
||||
border: none;
|
||||
list-style-type: none;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
li.image_box a img {
|
||||
position: relative;
|
||||
vertical-align:middle;
|
||||
border: none;
|
||||
width: auto !important;
|
||||
height: 100% !important;
|
||||
top: 0 !important;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
li.image_box a {
|
||||
padding: 0;
|
||||
text-shadow: 0;
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
li.image_box {
|
||||
width:80px;
|
||||
height:80px;
|
||||
width: 18%;
|
||||
position: relative;
|
||||
float:left;
|
||||
margin:0px 15px 15px 0px;
|
||||
padding:0px;
|
||||
margin: 0 2.5% 15px 0;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
background: #ededed;
|
||||
&:nth-child(5n) {
|
||||
margin-right: 0;
|
||||
}
|
||||
.thumb_wrapper {
|
||||
background: #ededed;
|
||||
width: 100% !important;
|
||||
height: 160px !important;
|
||||
@media screen and (max-width: 767px) {
|
||||
height: 130px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 767px) {
|
||||
li.image_box {
|
||||
width: 32%;
|
||||
margin: 0 2% 15px 0;
|
||||
&:nth-child(5n) {
|
||||
margin-right: 2%;
|
||||
}
|
||||
&:nth-child(3n) {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
a.no.active_choice {
|
||||
background-color: red;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
a.active_choice {
|
||||
background-color: #53b401;
|
||||
color: #fff;
|
||||
@@ -45,10 +83,13 @@ a.active_choice{
|
||||
}
|
||||
|
||||
.valid_choice.agree {
|
||||
background-image: url('#{$iconsPath}ok.png');
|
||||
background: #7ed321;
|
||||
border-top-left-radius: 100px;
|
||||
}
|
||||
|
||||
.valid_choice.disagree {
|
||||
background-image: url('#{$iconsPath}delete.png');
|
||||
background: #d0021b;
|
||||
border-top-left-radius: 100px;
|
||||
}
|
||||
|
||||
.thumb_wrapper {
|
||||
@@ -70,3 +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;
|
||||
}
|
||||
|
@@ -1,5 +1,28 @@
|
||||
@import './jquery-mobile/jquery-validator';
|
||||
|
||||
@font-face {
|
||||
font-family: 'icomoon';
|
||||
src: url("../../common/fonts/PhraseanetIcomoon/fonts/icomoon.eot?xt8hfo");
|
||||
src: url("../../common/fonts/PhraseanetIcomoon/fonts/icomoon.eot?xt8hfo#iefix") format("embedded-opentype"), url("../../common/fonts/PhraseanetIcomoon/fonts/icomoon.ttf?xt8hfo") format("truetype"), url("../../common/fonts/PhraseanetIcomoon/fonts/icomoon.woff?xt8hfo") format("woff"), url("../../common/fonts/PhraseanetIcomoon/fonts/icomoon.svg?xt8hfo#icomoon") format("svg");
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
[class^="icon-"], [class*=" icon-"] {
|
||||
/* use !important to prevent issues with browser extensions that change fonts */
|
||||
font-family: 'icomoon' !important;
|
||||
speak: none;
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
font-variant: normal;
|
||||
text-transform: none;
|
||||
line-height: 1;
|
||||
|
||||
/* Better Font Rendering =========== */
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.nav_button {
|
||||
width: 90%;
|
||||
margin: 0px auto;
|
||||
@@ -25,3 +48,676 @@
|
||||
#right-btn {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.report-modal, .ui-dialog .ui-dialog-content {
|
||||
background: linear-gradient(#3c3c3c, #111);
|
||||
}
|
||||
|
||||
.ui-bar-c, .ui-body-c, .ui-btn-down-c, .ui-btn-hover-c, .ui-btn-up-c, .ui-overlay-c {
|
||||
text-shadow: none;
|
||||
}
|
||||
|
||||
@import '../../_shared/styles/variables';
|
||||
$lightboxPath: '../images/';
|
||||
$imagesPath: '/assets/vendors/jquery-ui/images/dark-hive/';
|
||||
|
||||
$mainMenuBackgroundColor: #c7c7c7; //BFBFBF;
|
||||
$mainMenuBottomBorder: 1px solid #c7c7c7;
|
||||
$mainMenuLinkColor: #212121;
|
||||
$mainMenuLinkHoverColor: #000000;
|
||||
$mainMenuLinkActiveColor: #BFBFBF;
|
||||
$mainMenuLinkBackgroundHoverColor: transparent;
|
||||
|
||||
body {
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style-type: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
img {
|
||||
border: none;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.title15 {
|
||||
font-size: 15px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.record_display_box {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: block;
|
||||
table {
|
||||
vertical-align: middle;
|
||||
table-layout: fixed;
|
||||
}
|
||||
.title {
|
||||
margin: 0 10px;
|
||||
}
|
||||
.record {
|
||||
position: relative;
|
||||
max-width: none;
|
||||
max-height: none;
|
||||
}
|
||||
.header {
|
||||
color: #BFBFBF;
|
||||
height: 30px;
|
||||
bottom: auto;
|
||||
overflow: hidden;
|
||||
.title {
|
||||
overflow: hidden;
|
||||
line-height: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
}
|
||||
.lightbox_container {
|
||||
top: 30px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.display_id {
|
||||
top: 5px;
|
||||
margin: 0 0 0 5px;
|
||||
background: #bebebe;
|
||||
}
|
||||
}
|
||||
|
||||
#sc_wrapper {
|
||||
left: 20px;
|
||||
right: 20px;
|
||||
overflow-x: scroll;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
#sc_container {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.basket_element_wrapper {
|
||||
position: relative;
|
||||
float: left;
|
||||
padding: 0 5px;
|
||||
}
|
||||
|
||||
.basket_element {
|
||||
position: relative;
|
||||
float: left;
|
||||
width: 114px;
|
||||
height: 130px;
|
||||
border: 1px solid #212121;
|
||||
text-align: left;
|
||||
padding: 5px 8px;
|
||||
&.selected {
|
||||
background-color: #212121;
|
||||
}
|
||||
.display_id {
|
||||
top: 4px;
|
||||
left: 8px;
|
||||
}
|
||||
.agreement {
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
right: 8px;
|
||||
z-index: 99;
|
||||
}
|
||||
.image {
|
||||
position: relative;
|
||||
z-index: 90;
|
||||
}
|
||||
.previewTips {
|
||||
background-image: url('#{$iconsPath}zoom.gif');
|
||||
background-position: center center;
|
||||
background-repeat: no-repeat;
|
||||
cursor: help;
|
||||
position: absolute;
|
||||
bottom: 4px;
|
||||
right: 8px;
|
||||
height: 18px;
|
||||
width: 18px;
|
||||
z-index: 99;
|
||||
}
|
||||
}
|
||||
|
||||
#report .display_id {
|
||||
top: 0;
|
||||
left: 0;
|
||||
border-radius: 0;
|
||||
background-color: rgba(216, 216, 216, 0.7);
|
||||
font-weight: 700;
|
||||
z-index: 99;
|
||||
color: #333333;
|
||||
position: absolute;
|
||||
width: 22px;
|
||||
padding: 2px 0;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
#report .CHIM.diapo {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.display_id {
|
||||
background-color: #FFFFFF;
|
||||
padding: 3px 6px;
|
||||
font-weight: bold;
|
||||
z-index: 99;
|
||||
color: #212121;
|
||||
-moz-border-radius: 50%;
|
||||
-webkit-border-radius: 50%;
|
||||
border-radius: 50%;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.not_decided {
|
||||
opacity: 0.30;
|
||||
filter: alpha(opacity=30);
|
||||
}
|
||||
|
||||
#sc_wrapper .not_decided {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#right_scroller {
|
||||
width: 30px;
|
||||
right: -10px;
|
||||
left: auto;
|
||||
background-image: url('#{$lightboxPath}right_arrow.png');
|
||||
background-position: center center;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
#left_scroller {
|
||||
width: 30px;
|
||||
left: -10px;
|
||||
right: auto;
|
||||
background-image: url('#{$lightboxPath}left_arrow.png');
|
||||
background-position: center center;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
#basket_infos {
|
||||
overflow: hidden;
|
||||
background-color: #1F1E1B;
|
||||
color: #BFBFBF;
|
||||
}
|
||||
|
||||
#basket_options {
|
||||
height: 35px;
|
||||
top: auto;
|
||||
background-color: #1F1E1B;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#basket_options .confirm_report {
|
||||
margin: 5px auto;
|
||||
}
|
||||
|
||||
#basket_infos .user_infos {
|
||||
height: 120px;
|
||||
top: auto;
|
||||
}
|
||||
|
||||
#basket_infos {
|
||||
table {
|
||||
width: 100%;
|
||||
margin: 5px 0;
|
||||
.title {
|
||||
width: 100%;
|
||||
}
|
||||
.report_wrapper {
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#right_column {
|
||||
left: auto;
|
||||
right: 0;
|
||||
width: 240px;
|
||||
}
|
||||
|
||||
#right_column_validation_toggle {
|
||||
bottom: 45px;
|
||||
background-color: #1F1E1B;
|
||||
height: 30px;
|
||||
top: auto;
|
||||
text-align: center;
|
||||
display: none;
|
||||
line-height: 25px;
|
||||
color: #BFBFBF;
|
||||
}
|
||||
|
||||
#right_column .right_column_title {
|
||||
height: 30px;
|
||||
bottom: auto;
|
||||
}
|
||||
|
||||
#right_column .right_column_title img.expanded {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#right_column .right_column_title img.collapsed {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
#right_column .right_column_title.expanded img.expanded {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
#right_column .right_column_title.expanded img.collapsed {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#right_column .right_column_wrapper {
|
||||
top: 30px;
|
||||
bottom: 45px;
|
||||
}
|
||||
|
||||
#right_column .right_column_wrapper.caption {
|
||||
bottom: 85px;
|
||||
}
|
||||
|
||||
#record_infos {
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
#record_compare {
|
||||
visibility: hidden;
|
||||
top: auto;
|
||||
left: auto;
|
||||
}
|
||||
|
||||
#record_compare .header, #record_compare .lightbox_container {
|
||||
left: 5px;
|
||||
}
|
||||
|
||||
#record_wrapper.comparison #record_main .header, #record_wrapper.comparison #record_main .lightbox_container {
|
||||
right: 5px;
|
||||
}
|
||||
|
||||
.agreement_selector {
|
||||
position: absolute;
|
||||
bottom: 10px;
|
||||
right: 10px;
|
||||
}
|
||||
|
||||
.clickable {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.big_box.agree {
|
||||
border: 2px solid #35AC00;
|
||||
background-color: #35AC00;
|
||||
}
|
||||
|
||||
.big_box.disagree {
|
||||
border: 2px solid #DE1200;
|
||||
background-color: #DE1200;
|
||||
}
|
||||
|
||||
.big_box, .big_box.not_decided {
|
||||
width: 95px;
|
||||
margin: 0 auto;
|
||||
padding: 6px 10px;
|
||||
height: 30px;
|
||||
color: #1F1E1B;
|
||||
background-color: #353430;
|
||||
border: 2px solid #353430;
|
||||
text-align: center;
|
||||
opacity: 1;
|
||||
filter: alpha(opacity=100);
|
||||
}
|
||||
|
||||
.big_box span {
|
||||
margin: 0 10px;
|
||||
}
|
||||
|
||||
li.userchoice {
|
||||
margin: 5px 0 0px 20px;
|
||||
}
|
||||
|
||||
.userchoice.disagree {
|
||||
color: #DE1200;
|
||||
}
|
||||
|
||||
.userchoice.agree {
|
||||
color: #35AC00;
|
||||
}
|
||||
|
||||
.basket_report_user {
|
||||
padding: 7px 10px;
|
||||
}
|
||||
|
||||
.basket_report_user_wrapper {
|
||||
margin: 0;
|
||||
border-bottom: 1px solid #b2b2b2;
|
||||
border-radius: 0;
|
||||
display: inline-block;
|
||||
padding: 10px 15px;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
background: #e5e5e5;
|
||||
text-shadow: none;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.ui-button-text-icon-primary .ui-button-text, .ui-button-text-icons .ui-button-text {
|
||||
padding: 0.2em 1em 0.2em 2.1em;
|
||||
}
|
||||
|
||||
/*******
|
||||
*
|
||||
* Index
|
||||
*
|
||||
* ******/
|
||||
#main_index {
|
||||
position: relative;
|
||||
width: 600px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
#main_wrapper {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#main_wrapper h1 {
|
||||
font-weight: bold;
|
||||
font-size: 25px;
|
||||
}
|
||||
|
||||
table th {
|
||||
text-align: right;
|
||||
vertical-align: bottom;
|
||||
height: 60px;
|
||||
border-bottom: 1px solid #8F8F8F;
|
||||
}
|
||||
|
||||
table th.title {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
table th h1 {
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
table th i {
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
.ui-state-default.note_closer, .ui-state-default.note_saver {
|
||||
background-color: #353430;
|
||||
padding: 5px;
|
||||
margin: 0 10px;
|
||||
}
|
||||
|
||||
.ui-state-default.note_saver {
|
||||
background-color: #1F1E1B;
|
||||
}
|
||||
|
||||
.record_display_box form .buttons {
|
||||
margin: 5px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.record_display_box form {
|
||||
margin: 15px 0;
|
||||
width: 90%;
|
||||
}
|
||||
|
||||
.record_display_box form textarea {
|
||||
width: 100%;
|
||||
height: 75px;
|
||||
}
|
||||
|
||||
#navigation {
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
.basket_downloader {
|
||||
background-color: #1F1E1B;
|
||||
margin: 0 10px;
|
||||
padding: 2px;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
hr {
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
background-color: #212121;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.report {
|
||||
margin: 0 10px;
|
||||
vertical-align: bottom;
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
* Overrides JqueryUI
|
||||
*
|
||||
*/
|
||||
.ui-button {
|
||||
background: transparent;
|
||||
border: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.ui-button-text-only .ui-button-text {
|
||||
padding: 0.15em;
|
||||
}
|
||||
|
||||
.ui-dialog {
|
||||
.ui-dialog-titlebar {
|
||||
padding: 1em 1em;
|
||||
}
|
||||
}
|
||||
|
||||
.videoTips {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#basket_infos {
|
||||
margin-bottom: 20px;
|
||||
.mobile_aggreement_box {
|
||||
padding: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-button-icon.ui-icon.ui-icon-closethick {
|
||||
background-position: -73px 0px;
|
||||
background-color: rgba(0, 0, 0, .9);
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.btn-container {
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
button.confirm_report {
|
||||
background: #38c !important;
|
||||
color: #fff !important;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
padding: 6px;
|
||||
display: inline-block;
|
||||
border: 1px solid #38c;
|
||||
text-shadow: 0 1px 0 #111;
|
||||
border-radius: 16px;
|
||||
font-family: Roboto, sans-serif;
|
||||
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2) !important;
|
||||
}
|
||||
|
||||
.report_wrapper {
|
||||
position: relative;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.report_btn {
|
||||
position: absolute;
|
||||
top: -41px;
|
||||
right: 0;
|
||||
z-index: 1;
|
||||
background: #8bc34a;
|
||||
border: 1px solid #8bc34a;
|
||||
box-sizing: border-box;
|
||||
padding: 8px 13px;
|
||||
min-width: 110px;
|
||||
border-radius: 4px;
|
||||
color: #fff !important;
|
||||
text-decoration: none;
|
||||
text-align: center;
|
||||
transition: all 0.3s;
|
||||
&:hover, &:focus {
|
||||
background: darken(#8bc34a, 0.2);
|
||||
border-color: darken(#8bc34a, 0.3);
|
||||
}
|
||||
&.report_list {
|
||||
background: #f44336;
|
||||
border-color: #f44336;
|
||||
&:hover, &:focus {
|
||||
background: darken(#f44336, 0.2);
|
||||
border-color: darken(#f44336, 0.3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.report_list {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.report_summary_backup, .report_list_backup {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.chim-wrapper-block {
|
||||
margin-bottom: 15px;
|
||||
.chim-block {
|
||||
margin: 0;
|
||||
}
|
||||
.chim-inner {
|
||||
width: 100%;
|
||||
display: inline-block;
|
||||
min-width: 296px;
|
||||
}
|
||||
.chim-left {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
background: #ededed;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
.diapo {
|
||||
display: inline-block;
|
||||
div {
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.chim-right {
|
||||
width: 100%;
|
||||
.validate-icon {
|
||||
float: left;
|
||||
height: 25px;
|
||||
vertical-align: middle;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
}
|
||||
h3 {
|
||||
font-size: 16px;
|
||||
margin: 0;
|
||||
padding-top: 3px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.thumb_wrapper img.record_image {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
/*header lightbox*/
|
||||
.ui-footer .ui-title, .ui-header .ui-title {
|
||||
min-height: 26px;
|
||||
padding: 10px 0;
|
||||
}
|
||||
.ui-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;
|
||||
}
|
||||
|
||||
|
@@ -109,3 +109,48 @@ dd {
|
||||
float: left;
|
||||
}
|
||||
}
|
||||
|
||||
/*Help menu*/
|
||||
.contextMenu.helpcontextmenu {
|
||||
display: none;
|
||||
&.shown {
|
||||
display: block;
|
||||
position: absolute;
|
||||
z-index: 101;
|
||||
}
|
||||
}
|
||||
.help-trigger {
|
||||
display:inline-block;
|
||||
cursor:pointer;
|
||||
padding: 0 4px;
|
||||
border:none;
|
||||
margin-left: -12px;
|
||||
margin-right: 94px;
|
||||
.fa-caret-down:before {
|
||||
content: "\25BC";
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
.context-menu-theme-vista {
|
||||
background-image: none;
|
||||
background-color: #414141;
|
||||
border-bottom-left-radius: 2px;
|
||||
border-bottom-right-radius: 2px;
|
||||
|
||||
.context-menu-item {
|
||||
transition: 500ms;
|
||||
&:hover {
|
||||
background-image: none;
|
||||
background-color: #75abff;
|
||||
border: none;
|
||||
}
|
||||
.context-menu-item-inner {
|
||||
padding: 4px 12px;
|
||||
margin-left: 0;
|
||||
font-family: Helvetica;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
17
templates/mobile/common/menubar.html.twig
Normal file
17
templates/mobile/common/menubar.html.twig
Normal 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"></span>
|
||||
<span class="text">{{ 'Guest' | trans }}</span>
|
||||
{% else %}
|
||||
<a target="_blank" href="{{ path('account') }}"
|
||||
title="{{ 'login:: Mon compte' | trans }}">
|
||||
<span class="icomoon icon-agree"></span>
|
||||
<span class="text">{{ app.getAuthenticatedUser().getDisplayName() }}</span>
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
@@ -26,7 +26,7 @@
|
||||
|
||||
#content {
|
||||
position: fixed;
|
||||
top:50px;
|
||||
top:90px;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
@@ -40,7 +40,6 @@
|
||||
{% block content %}
|
||||
{% set record = basket_element.getRecord(app) %}
|
||||
|
||||
|
||||
<div data-role="page" id="page">
|
||||
<div data-role="header">
|
||||
<a href="{{ path('lightbox_validation', { 'basket' : basket_element.getBasket().getId() }) }}"
|
||||
@@ -49,8 +48,10 @@
|
||||
<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 class="lightbox-user-info">
|
||||
{% include 'common/menubar.html.twig' %}
|
||||
</div>
|
||||
<div id="content" data-role="content">
|
||||
{{ thumbnail.format100percent(record.get_preview()) }}
|
||||
<div class="nav_button">
|
||||
{% if prevId != NULL %}
|
||||
<a data-ajax="false" id="left-btn"
|
||||
@@ -64,6 +65,7 @@
|
||||
{% endif %}
|
||||
<div style="clear: both;"></div>
|
||||
</div>
|
||||
<div class="center-image">{{ thumbnail.format100percent(record.get_preview()) }}</div>
|
||||
{% if basket_element.getBasket().getValidation() %}
|
||||
{% if basket_element.getBasket().getValidation().getParticipant(app.getAuthenticatedUser()).getCanAgree() %}
|
||||
<fieldset data-role="controlgroup" data-type="horizontal" style="text-align:center;">
|
||||
@@ -75,13 +77,13 @@
|
||||
for="radio-view-no_{{ basket_element.getId() }}">{{ 'validation:: NON' | trans }}</label>
|
||||
</fieldset>
|
||||
{% endif %}
|
||||
<div style="text-align:center;margin:0 0 1em 0">
|
||||
<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">
|
||||
<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">
|
||||
{{ 'validation:: editer ma note' | trans }}
|
||||
</a>
|
||||
</div>
|
||||
<div>
|
||||
<ul data-role="listview" id="notes_{{basket_element.getId()}}">
|
||||
<ul data-role="listview" class="lightbox-list-view" id="notes_{{basket_element.getId()}}">
|
||||
{% include 'lightbox/sc_note.html.twig' %}
|
||||
</ul>
|
||||
</div>
|
||||
|
@@ -17,7 +17,7 @@
|
||||
|
||||
#content {
|
||||
position: fixed;
|
||||
top:50px;
|
||||
top:90px;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
@@ -35,6 +35,9 @@
|
||||
<a href="{{ path('lightbox_feed_entry', { 'entry_id' : feed_element.getEntry().getId() }) }}" data-rel="back" data-icon="arrow-l">Back</a>
|
||||
<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>
|
||||
</div>
|
||||
<div class="lightbox-user-info">
|
||||
{% include 'common/menubar.html.twig' %}
|
||||
</div>
|
||||
<div id="content" data-role="content">
|
||||
{{ thumbnail.format100percent(record.get_preview()) }}
|
||||
|
@@ -39,19 +39,22 @@
|
||||
<div data-role="header">
|
||||
<h1>{{module_name}}</h1>
|
||||
<a href="#about" class="ui-btn-right">{{ 'a propos' | trans }}</a>
|
||||
</div>
|
||||
<div class="lightbox-user-info">
|
||||
{% include 'common/menubar.html.twig' %}
|
||||
</div>
|
||||
<div data-role="content">
|
||||
<ul data-role="listview" style="width:100%;margin: 0;">
|
||||
<li>
|
||||
<ul id="lightbox-menu" data-role="listview" style="width:100%;margin: 0;">
|
||||
<li class="lightbox-item">
|
||||
<a href="#validation">{{ 'Validations' | trans }}</a>
|
||||
<span class="ui-li-count">{{_self.valid_baskets_length(baskets_collection)}}</span>
|
||||
</li>
|
||||
<li>
|
||||
<li class="lightbox-item">
|
||||
<a href="#baskets">{{ 'Paniers' | trans }}</a>
|
||||
<span class="ui-li-count">{{_self.baskets_length(baskets_collection)}}</span>
|
||||
</li>
|
||||
</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>
|
||||
</div>
|
||||
</div>
|
||||
@@ -64,6 +67,9 @@
|
||||
<div data-role="header">
|
||||
<a href="#home" data-rel="back" data-icon="arrow-l">{{ 'Back' | trans }}</a>
|
||||
<h1>{{ 'a propos' | trans }}</h1>
|
||||
</div>
|
||||
<div class="lightbox-user-info">
|
||||
{% include 'common/menubar.html.twig' %}
|
||||
</div>
|
||||
<div data-role="content">
|
||||
<p>Phraseanet Version {{ app['phraseanet.version'].getName() }} - {{ app['phraseanet.version'].getNumber() }}</p>
|
||||
@@ -83,28 +89,39 @@
|
||||
<div id="validation" data-role="page">
|
||||
<div data-role="header">
|
||||
<a rel="external" href="#home" data-icon="arrow-l">{{ 'Back' | trans }}</a>
|
||||
<h1>{{ 'Validations' | trans }}</h1>
|
||||
<h1>{{ module_name }}</h1>
|
||||
</div>
|
||||
<div class="lightbox-user-info">
|
||||
{% include 'common/menubar.html.twig' %}
|
||||
</div>
|
||||
<div data-role="content">
|
||||
<p>
|
||||
<p class="basket-title">
|
||||
{{ 'Validations' | trans }}
|
||||
<span>{{_self.valid_baskets_length(baskets_collection)}}</span>
|
||||
</p>
|
||||
<p class="lightbox-msg">
|
||||
{{ 'Voici vos validations en cours' | trans }}
|
||||
</p>
|
||||
<div class="feed-list">
|
||||
<ul data-role="listview" style="width:100%;margin: 0;">
|
||||
{% for basket in baskets_collection %}
|
||||
{% for basket in baskets_collection | sort | reverse%}
|
||||
{% if basket.getValidation() %}
|
||||
{% set basket_length = basket.getElements().count() %}
|
||||
<li>
|
||||
{% if basket.getElements().first() %}
|
||||
<div class="lightbox-img">
|
||||
<img src="{{ basket.getElements().first().getRecord(app).get_thumbnail.get_url()}}" />
|
||||
</div>
|
||||
{% endif %}
|
||||
<h3><a href="{{ path('lightbox_validation', { 'basket' : basket.getId() }) }}">{{basket.getName()}}</a></h3>
|
||||
<p>{{ basket.getDescription() }}</p>
|
||||
<span class="ui-li-count">{{ basket_length }}</span>
|
||||
{#<p>{{ basket.getDescription() }}</p>#}
|
||||
<span class="ui-li-count validation">{{ basket_length }}</span>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div data-role="footer">
|
||||
|
||||
</div>
|
||||
@@ -113,28 +130,39 @@
|
||||
<div id="baskets" data-role="page">
|
||||
<div data-role="header">
|
||||
<a rel="external" href="#home" data-icon="arrow-l">{{ 'Back' | trans }}</a>
|
||||
<h1>{{ 'Paniers' | trans }}</h1>
|
||||
<h1>{{module_name}}</h1>
|
||||
</div>
|
||||
<div class="lightbox-user-info">
|
||||
{% include 'common/menubar.html.twig' %}
|
||||
</div>
|
||||
<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 }}
|
||||
</p>
|
||||
<div class="feed-list">
|
||||
<ul data-role="listview" style="width:100%;margin: 0;">
|
||||
{% for basket in baskets_collection %}
|
||||
{% for basket in baskets_collection | sort | reverse %}
|
||||
{% if basket.getValidation() is empty %}
|
||||
{% set basket_length = basket.getElements().count() %}
|
||||
<li>
|
||||
{% if basket.getElements().first() %}
|
||||
<div class="lightbox-img">
|
||||
<img src="{{ basket.getElements().first().getRecord(app).get_thumbnail.get_url()}}" />
|
||||
</div>
|
||||
{% endif %}
|
||||
<h3><a href="{{ path('lightbox_validation', { 'basket' : basket.getId() }) }}">{{ basket.getName() }}</a></h3>
|
||||
<p>{{ basket.getDescription() }}</p>
|
||||
<span class="ui-li-count">{{basket_length}}</span>
|
||||
{#<p>{{ basket.getDescription() }}</p>#}
|
||||
<span class="ui-li-count baskets">{{basket_length}}</span>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div data-role="footer">
|
||||
|
||||
</div>
|
||||
|
@@ -1,17 +1,19 @@
|
||||
{% for validationDatas in basket_element.getValidationDatas() %}
|
||||
{% set is_mine = validationDatas.getParticipant().getUser().getId() == app.getAuthenticatedUser().getId() %}
|
||||
{% if validationDatas.getNote() != '' or (validationDatas.getAgreement() is not null and is_mine) %}
|
||||
{% if basket_element.getBasket().getValidation().getParticipant(app.getAuthenticatedUser()).getCanSeeOthers() or validationDatas.getParticipant().getUser() == app.getAuthenticatedUser() %}
|
||||
|
||||
<li>
|
||||
<h3 style="text-align:left;">
|
||||
{% if is_mine == false and validationDatas.getAgreement() is not null %}
|
||||
<img style="vertical-align:middle;"
|
||||
src="/assets/lightbox/images/{% if validationDatas.getAgreement() == true %}agree.png{% else %}disagree.png{% endif %}" />
|
||||
{% endif %}
|
||||
<div class="validate-icon">
|
||||
{% if validationDatas.getAgreement() == true %}<span class="icomoon icon-agree"></span>{% endif %}
|
||||
{% if validationDatas.getAgreement() == false and validationDatas.getAgreement() is not null %}<span class="icomoon icon-disagree"> {{ validationDatas.getAgreement() }}</span>{% endif %}
|
||||
</div>
|
||||
<div class="validate-info">
|
||||
<h3>
|
||||
{{ validationDatas.getParticipant().getUser().getDisplayName() }}
|
||||
</h3>
|
||||
{% if validationDatas.getNote() != '' %}
|
||||
<p style="text-align:left;">{{ 'validation:: note' | trans }} : {{ validationDatas.getNote()|nl2br }} </p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
@@ -31,6 +31,9 @@
|
||||
data-icon="arrow-l">{{ 'Back' | trans }}</a>
|
||||
<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>
|
||||
</div>
|
||||
<div class="lightbox-user-info">
|
||||
{% include 'common/menubar.html.twig' %}
|
||||
</div>
|
||||
<div data-role="content">
|
||||
<input type="hidden" id="basket_validation_id" value="{{ basket.getId() }}">
|
||||
@@ -38,22 +41,56 @@
|
||||
{% set basket_length = basket.getElements().count() %}
|
||||
{% trans with {'%basket_length%' : basket_length} %}%basket_length% documents{% endtrans %}
|
||||
</p>
|
||||
{% if basket.getValidation() %}
|
||||
<div class="report_wrapper">
|
||||
<a id="report_summary" class="report_btn report_summary" href="#" onclick="$.ajax({
|
||||
type: 'GET',
|
||||
url: '/lightbox/ajax/LOAD_REPORT/' + $('#navigation').val() + '/',
|
||||
dataType: 'html',
|
||||
success: function (data) {
|
||||
$('#report_list_backup').empty().append($('#report').html());
|
||||
$('#report').empty().append(data);
|
||||
$('#report .record_image').css('height','auto');
|
||||
return;
|
||||
}
|
||||
});
|
||||
$(this).hide();
|
||||
$('#report_list').show();">
|
||||
{{ 'lightbox::recaptitulatif' | trans }}
|
||||
</a>
|
||||
<a id="report_list" class="report_btn report_list" href="#" onclick=" $(this).hide();
|
||||
$('#report_summary').show();
|
||||
$('#report').empty().append($('#report_list_backup').html());">
|
||||
{{ 'lightbox::list' | trans }}
|
||||
</a>
|
||||
</div>
|
||||
<div class="report_list_backup" id="report_list_backup"> </div>
|
||||
<div class="report_summary_backup " id="report_summary_backup"> </div>
|
||||
{% endif %}
|
||||
<input type="hidden" id="navigation" value="{{ basket.getId() }}">
|
||||
<div id="report" class="summary_block">
|
||||
<ul class="image_set">
|
||||
{% for basket_element in basket.getElements() %}
|
||||
|
||||
<li class="image_box" id="sselcontid_{{basket_element.getId()}}">
|
||||
<div class="display_id">
|
||||
{{basket_element.getOrd()}}
|
||||
</div>
|
||||
{% if basket_element.getBasket().getValidation() and basket_element.getBasket().getValidation().getParticipant(app.getAuthenticatedUser()).getCanAgree() %}
|
||||
<div class="valid_choice valid_choice_{{basket_element.getId()}} {% if basket_element.getUserValidationDatas(app.getAuthenticatedUser()).getAgreement() == true %}agree{% elseif basket_element.getUserValidationDatas(app.getAuthenticatedUser()).getAgreement() == false and basket_element.getUserValidationDatas(app.getAuthenticatedUser()).getAgreement() is not null %}disagree{% endif %}">
|
||||
</div>
|
||||
{% endif %}
|
||||
<a href="{{ path('lightbox_ajax_load_basketelement', { 'sselcont_id' : basket_element.getId() }) }}">
|
||||
{{thumbnail.format(basket_element.getRecord(app).get_thumbnail(), 80, 80, '', true, false)}}
|
||||
{{thumbnail.format(basket_element.getRecord(app).get_thumbnail(), 300, 300, '', true, false)}}
|
||||
</a>
|
||||
<input type="hidden" class="display_id" name="display_id" value="{{basket_element.getOrd()}}" />
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
<div data-role="footer">
|
||||
</div>
|
||||
|
||||
<div class="btn-container">
|
||||
{% if basket.getValidation() and basket.getValidation().getParticipant(app.getAuthenticatedUser()).getCanAgree() %}
|
||||
<button class="confirm_report" style="width:100%;" title="{{ 'validation::envoyer mon rapport' | trans }}">
|
||||
{{ 'validation::envoyer mon rapport' | trans }}
|
||||
@@ -62,6 +99,8 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
|
@@ -189,6 +189,22 @@
|
||||
</label>
|
||||
</td>
|
||||
</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>
|
||||
<td colspan="2">
|
||||
<label for="business" class="checkbox">
|
||||
@@ -247,6 +263,10 @@
|
||||
<td><label for="tbranch">{% trans %}Thesaurus branch{% endtrans %}</label></td>
|
||||
<td><input id="tbranch" type="text" value="<%= field.tbranch %>"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for="generate_cterms" class="checkbox">{% trans %}Generate-cterms{% endtrans %}</label></td>
|
||||
<td><input id="generate_cterms" type="checkbox" <%= field.generate_cterms ? "checked='checked'" : "" %> /></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -1,8 +1,10 @@
|
||||
{% macro caption(record, can_see_business, display_exif, limitedWidth = false) %}
|
||||
<dl class="{% if limitedWidth %}{% else %}dl-horizontal{% endif %}">
|
||||
{% for name, value in record.getCaption(caption_field_order(record, can_see_business)) %}
|
||||
{% if caption_field_gui_visible(record, name) == 1 %}
|
||||
<dt>{{ caption_field_label(record, name) }}</dt>
|
||||
<dd>{{ caption_field(record, name, value)|e|highlight|linkify|parseColor }}</dd>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</dl>
|
||||
{% if display_exif|default(true) and app.getAuthenticator().user is not none and user_setting('technical_display') == 'group' %}
|
||||
|
@@ -302,8 +302,18 @@
|
||||
$(document).ready(function () {
|
||||
$('body').on('click', '#help-trigger', function (event) {
|
||||
$('#mainMenu .helpcontextmenu').toggleClass('shown');
|
||||
console.log('mety');
|
||||
|
||||
});
|
||||
|
||||
$(document).on('click', function (e) {
|
||||
if($('#mainMenu .helpcontextmenu').hasClass('shown')) {
|
||||
var $this = $(e.target);
|
||||
if ($this.closest('#mainMenu .helpcontextmenu.shown, .help-trigger').length == 0 && $this[0].id != "toggle") {
|
||||
$('#mainMenu .helpcontextmenu').removeClass('shown');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// var key = 'help';
|
||||
var configurationSettingLinks = {{ configuration|json_encode|raw }};
|
||||
//seperate array based on location of link
|
||||
|
@@ -4,48 +4,49 @@
|
||||
<div style="margin:0 7px;overflow:hidden;">
|
||||
{% for basket_element in basket.getElements() %}
|
||||
{% set record = basket_element.getRecord(app) %}
|
||||
<div class="chim-wrapper wrapCHIM_{{basket_element.getId()}} valid">
|
||||
<div style="margin:20px;">
|
||||
<table style="width: 100%; min-width: 330px;">
|
||||
<tr valign="top">
|
||||
<td style="width:165px;">
|
||||
<div id="CHIM_{{basket_element.getId()}}_{{record.get_serialize_key()}}"
|
||||
class="CHIM diapo">
|
||||
<div class="chim-wrapper-block chim-wrapper wrapCHIM_{{basket_element.getId()}} valid">
|
||||
<div class="chim-block">
|
||||
<div class="chim-inner">
|
||||
<div class="chim-left">
|
||||
<div id="CHIM_{{basket_element.getId()}}_{{record.get_serialize_key()}}" class="CHIM diapo">
|
||||
<div class="display_id">
|
||||
{{basket_element.getOrd()}}
|
||||
</div>
|
||||
{{thumbnail.format(record.get_thumbnail(),165, 125, '', true, false)}}
|
||||
</div>
|
||||
</td>
|
||||
<td style="width:10px;">
|
||||
</td>
|
||||
<td>
|
||||
</div>
|
||||
<div class="chim-right">
|
||||
{% for validationDatas in basket_element.getValidationDatas()%}
|
||||
{% if basket_element.getBasket().getValidation().getParticipant(app.getAuthenticatedUser()).getCanSeeOthers() or validationDatas.getParticipant().getUser() == app.getAuthenticatedUser() %}
|
||||
|
||||
<div class="basket_report_user_wrapper ui-corner-all">
|
||||
<div class="basket_report_user">
|
||||
{% if validationDatas.getAgreement() == true %}
|
||||
{% set imguser = '<img src="/assets/lightbox/images/agree.png" />' %}
|
||||
{% set imguser = '<span class="icomoon icon-agree"></span>' %}
|
||||
{% set styleuser = '' %}
|
||||
{% elseif validationDatas.getAgreement() is null %}
|
||||
{% set imguser = '' %}
|
||||
{% set imguser = ' ' %}
|
||||
{% set styleuser = 'margin-left:18px;' %}
|
||||
{% else %}
|
||||
{% set imguser = '<img src="/assets/lightbox/images/disagree.png" />' %}
|
||||
{% set imguser = '<span class="icomoon icon-disagree"></span>' %}
|
||||
{% set styleuser = '' %}
|
||||
{% endif %}
|
||||
<b style="{{styleuser}}">{{imguser|raw}} {{validationDatas.getParticipant().getUser().getDisplayName()}}</b>
|
||||
|
||||
<div class="validate-icon">
|
||||
{{imguser|raw}}
|
||||
</div>
|
||||
<div class="validate-info">
|
||||
<h3> {{validationDatas.getParticipant().getUser().getDisplayName()}}</h3>
|
||||
{% if validationDatas.getNote() != '' %}
|
||||
: {{validationDatas.getNote()|nl2br}}
|
||||
<p> {{validationDatas.getNote()|nl2br}}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div><hr/></div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
|
@@ -16,9 +16,10 @@
|
||||
<a class="workzone-menu-title" href="{{ path('prod_baskets_basket', { 'basket' : basket.getId() }) }}">
|
||||
<span>
|
||||
{% 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 %}
|
||||
<img src='/assets/common/images/icons/basket.png' title=''/>
|
||||
{{basket.getName()|e}}
|
||||
</span>
|
||||
</a>
|
||||
@@ -26,11 +27,9 @@
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
<img
|
||||
title="{{ basket.getDescription() }}"
|
||||
class="basketTips basket_title"
|
||||
src="/assets/common/images/icons/valid.png"
|
||||
/>
|
||||
<img title="{{ basket.getDescription()|e }}"
|
||||
class="basket_title"
|
||||
src="/assets/common/images/icons/valid.png" />
|
||||
</td>
|
||||
<td>
|
||||
<a class="contextMenuTrigger icon-display-grid" href="#"></a>
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user