mirror of
https://github.com/alchemy-fr/Phraseanet.git
synced 2025-10-23 09:53:15 +00:00
Merge branch 'master' into PHRAS-2184-list-manager-email-domain-filter
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"
|
||||
|
36
.dockerignore
Normal file
36
.dockerignore
Normal file
@@ -0,0 +1,36 @@
|
||||
.idea
|
||||
.settings
|
||||
/*.md
|
||||
/.circleci
|
||||
/.dockerignore
|
||||
/.env
|
||||
/.env.*
|
||||
/env.*
|
||||
/.git
|
||||
/.gitignore
|
||||
/.travis.yml
|
||||
/AUTHORS
|
||||
/CONTRIBUTORS
|
||||
/Dockerfile
|
||||
/LICENSE
|
||||
/bin
|
||||
!/bin/console
|
||||
!/bin/developer
|
||||
!/bin/setup
|
||||
/cache
|
||||
/config/configuration.yml
|
||||
/config/configuration-compiled.php
|
||||
!/config/configuration.sample.yml
|
||||
/datas
|
||||
/docker-compose.*
|
||||
/logs
|
||||
/node_modules
|
||||
/plugins
|
||||
/tmp
|
||||
/vendor
|
||||
/volumes
|
||||
/www/assets
|
||||
/www/custom
|
||||
/www/plugins
|
||||
/www/thumbnails
|
||||
npm-debug.log
|
83
.env
Normal file
83
.env
Normal file
@@ -0,0 +1,83 @@
|
||||
PHRASEANET_PROJECT_NAME=Phraseanet
|
||||
# Registry from where you pull Docker images
|
||||
PHRASEANET_DOCKER_REGISTRY=local
|
||||
# Tag of the Docker images
|
||||
PHRASEANET_DOCKER_TAG=latest
|
||||
# APPLICATION PORT
|
||||
PHRASEANET_APP_PORT=8082
|
||||
# RabbitMQ configuration
|
||||
RABBITMQ_DEFAULT_USER=alchemy
|
||||
RABBITMQ_DEFAULT_PASS=vdh4dpe5Wy3R
|
||||
RABBITMQ_MANAGEMENT_PORT=10811
|
||||
# Mysql configuration
|
||||
MYSQL_ROOT_PASSWORD=root
|
||||
SERVER_NAME=phraseanet-docker
|
||||
|
||||
|
||||
# --------------- APPLICATION CONFIGURATION --------------------
|
||||
|
||||
# Max upload size
|
||||
MAX_BODY_SIZE=2G
|
||||
# Max input var
|
||||
MAX_INPUT_VARS=12000
|
||||
|
||||
# Enable opcache ? (0/1)
|
||||
OPCACHE_ENABLED=1
|
||||
# session cache limiter (off/on)
|
||||
SESSION_CACHE_LIMITER=off
|
||||
|
||||
# PHP LOG LEVEL : Possible Values: alert, error, warning, notice, debug
|
||||
PHP_LOG_LEVEL=warning
|
||||
|
||||
# --------------- APPLICATION STARTUP CONFIGURATION --------------------
|
||||
|
||||
# These variables are only used if the configuration.yml file is not present, in order to automate the installation procedure
|
||||
|
||||
# set here the first user / email couple
|
||||
INSTALL_ACCOUNT_EMAIL=admin@alchemy.fr
|
||||
INSTALL_ACCOUNT_PASSWORD=iJRqXU0MwbyJewQLBbra6IWHsWly
|
||||
# Database parameters
|
||||
INSTALL_DB_HOST=db
|
||||
INSTALL_DB_PORT=3306
|
||||
INSTALL_DB_USER=root
|
||||
INSTALL_DB_PASSWORD=root
|
||||
INSTALL_DB_TEMPLATE=en-simple
|
||||
INSTALL_APPBOX=ab_master
|
||||
INSTALL_DATABOX=db_databox1
|
||||
INSTALL_SERVER_NAME=localhost
|
||||
|
||||
# Mysql max allowed packet
|
||||
MYSQL_MAX_ALLOWED_PACKET=16M
|
||||
|
||||
|
||||
# --- DEV purpose ---
|
||||
|
||||
# PhpMyAdmin port
|
||||
PHRASEANET_PHPMYADMIN_PORT=8089
|
||||
|
||||
# Xdebug
|
||||
XDEBUG_ENABLED=1
|
||||
XDEBUG_PROFILER_ENABLED=0
|
||||
IDE_KEY=PHPSTORM
|
||||
PHRASEANET_SUBNET_IPS=172.32.0.0/16
|
||||
XDEBUG_REMOTE_HOST=172.32.0.1
|
||||
PHP_IDE_CONFIG=serverName=docker-server-phraseanet
|
||||
|
||||
# Volumes location for dev
|
||||
PHRASEANET_CONFIG_DIR=./config
|
||||
PHRASEANET_LOGS_DIR=./logs
|
||||
PHRASEANET_DATA_DIR=./datas
|
||||
PHRASEANET_DB_DIR=./volumes/db
|
||||
PHRASEANET_ELASTICSEARCH_DIR=./volumes/elasticsearch
|
||||
PHRASEANET_THUMBNAILS_DIR=./www/thumbnails
|
||||
PHRASEANET_CUSTOM_DIR=./www/custom
|
||||
PHRASEANET_TMP_DIR=./tmp
|
||||
PHRASEANET_CACHE_DIR=./cache
|
||||
|
||||
|
||||
# For dev who don't have SSH_AUTH_SOCK (avoid an empty volume name)
|
||||
SSH_AUTH_SOCK=/dev/null
|
||||
|
||||
# Plugin support
|
||||
PHRASEANET_PLUGINS=
|
||||
PHRASEANET_SSH_PRIVATE_KEY=
|
4
.gitignore
vendored
4
.gitignore
vendored
@@ -71,3 +71,7 @@ playbook.retry
|
||||
npm-debug.log
|
||||
|
||||
/Phrasea_datas
|
||||
|
||||
.env.*
|
||||
env.local
|
||||
/volumes
|
||||
|
287
Dockerfile
287
Dockerfile
@@ -1,12 +1,21 @@
|
||||
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)
|
||||
#########################################################################
|
||||
|
||||
RUN apt-get update \
|
||||
FROM php:7.0-fpm-stretch as phraseanet-system
|
||||
|
||||
ENV FFMPEG_VERSION=4.2.2
|
||||
|
||||
RUN echo "deb http://deb.debian.org/debian stretch main non-free" > /etc/apt/sources.list \
|
||||
&& 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 \
|
||||
&& apt-get install -y --no-install-recommends \
|
||||
zlib1g-dev \
|
||||
git \
|
||||
ghostscript \
|
||||
gpac \
|
||||
@@ -23,142 +32,194 @@ RUN apt-get update \
|
||||
libxslt-dev \
|
||||
libzmq3-dev \
|
||||
locales \
|
||||
gettext \
|
||||
mcrypt \
|
||||
swftools \
|
||||
unoconv \
|
||||
unzip \
|
||||
xpdf \
|
||||
libreoffice-base-core \
|
||||
libreoffice-impress \
|
||||
libreoffice-calc \
|
||||
libreoffice-math \
|
||||
libreoffice-writer \
|
||||
libreoffice-pdfimport \
|
||||
# FFmpeg
|
||||
yasm \
|
||||
libvorbis-dev \
|
||||
texi2html \
|
||||
nasm \
|
||||
zlib1g-dev \
|
||||
libx264-dev \
|
||||
libfdk-aac-dev \
|
||||
libopus-dev \
|
||||
libvpx-dev \
|
||||
libmp3lame-dev \
|
||||
libogg-dev \
|
||||
libopencore-amrnb-dev \
|
||||
libopencore-amrwb-dev \
|
||||
libdc1394-22-dev \
|
||||
libx11-dev \
|
||||
libswscale-dev \
|
||||
libpostproc-dev \
|
||||
libxvidcore-dev \
|
||||
libtheora-dev \
|
||||
libgsm1-dev \
|
||||
libfreetype6-dev \
|
||||
# End FFmpeg
|
||||
&& 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 \
|
||||
&& pecl install \
|
||||
redis \
|
||||
amqp-1.9.3 \
|
||||
zmq-beta \
|
||||
imagick-beta \
|
||||
xdebug-2.6.1 \
|
||||
&& docker-php-ext-enable redis amqp zmq imagick \
|
||||
&& pecl clear-cache \
|
||||
&& docker-php-source delete \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
&& mkdir /tmp/ffmpeg \
|
||||
&& curl -s https://ffmpeg.org/releases/ffmpeg-${FFMPEG_VERSION}.tar.bz2 | tar jxf - -C /tmp/ffmpeg \
|
||||
&& ( \
|
||||
cd /tmp/ffmpeg/ffmpeg-${FFMPEG_VERSION} \
|
||||
&& ./configure \
|
||||
--enable-gpl \
|
||||
--enable-nonfree \
|
||||
--enable-libfdk-aac \
|
||||
--enable-libfdk_aac \
|
||||
--enable-libgsm \
|
||||
--enable-libmp3lame \
|
||||
--enable-libtheora \
|
||||
--enable-libvorbis \
|
||||
--enable-libvpx \
|
||||
--enable-libfreetype \
|
||||
--enable-libopus \
|
||||
--enable-libx264 \
|
||||
--enable-libxvid \
|
||||
--enable-zlib \
|
||||
--enable-postproc \
|
||||
--enable-swscale \
|
||||
--enable-pthreads \
|
||||
--enable-libdc1394 \
|
||||
--enable-version3 \
|
||||
--enable-libopencore-amrnb \
|
||||
--enable-libopencore-amrwb \
|
||||
&& make \
|
||||
&& make install \
|
||||
&& make distclean \
|
||||
) \
|
||||
&& rm -rf /tmp/ffmpeg \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists \
|
||||
&& mkdir /entrypoint /var/alchemy \
|
||||
&& useradd -u 1000 app \
|
||||
&& mkdir -p /home/app/.composer \
|
||||
&& chown -R app: /home/app /var/alchemy
|
||||
|
||||
RUN php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" \
|
||||
&& php -r "if (hash_file('sha384', 'composer-setup.php') === 'a5c698ffe4b8e849a443b120cd5ba38043260d5c4023dbf93e1558871f1f07f58274fc6f4c93bcfd858c6bd0775cd8d1') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;" \
|
||||
&& php composer-setup.php --install-dir=/usr/local/bin --filename=composer \
|
||||
&& php -r "unlink('composer-setup.php');"
|
||||
ENV XDEBUG_ENABLED=0
|
||||
|
||||
#########################################################################
|
||||
# This image is used to build the apps
|
||||
#########################################################################
|
||||
|
||||
FROM phraseanet-system as builder
|
||||
|
||||
COPY --from=composer:1.9.1 /usr/bin/composer /usr/bin/composer
|
||||
|
||||
# Node Installation (node + yarn)
|
||||
# Reference :
|
||||
# https://linuxize.com/post/how-to-install-node-js-on-ubuntu-18.04/
|
||||
# https://yarnpkg.com/lang/en/docs/install/#debian-stable
|
||||
RUN curl -sL https://deb.nodesource.com/setup_10.x | bash - \
|
||||
&& apt install -y nodejs \
|
||||
&& curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \
|
||||
&& echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list \
|
||||
&& apt-get update && apt-get install -y --no-install-recommends yarn \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists/
|
||||
|
||||
RUN mkdir /entrypoint /var/alchemy \
|
||||
&& useradd -u 1000 app \
|
||||
&& mkdir -p /home/app/.composer \
|
||||
&& chown -R app: /home/app /var/alchemy
|
||||
|
||||
WORKDIR /var/alchemy/
|
||||
|
||||
COPY gulpfile.js /var/alchemy/
|
||||
COPY Makefile /var/alchemy/
|
||||
COPY package.json /var/alchemy/
|
||||
COPY phpunit.xml.dist /var/alchemy/
|
||||
COPY yarn.lock /var/alchemy/
|
||||
COPY bin /var/alchemy/bin
|
||||
COPY composer.json /var/alchemy/
|
||||
COPY composer.lock /var/alchemy/
|
||||
RUN make install_composer
|
||||
COPY resources /var/alchemy/resources
|
||||
COPY www /var/alchemy/www
|
||||
RUN make clean_assets
|
||||
RUN make install_asset_dependencies
|
||||
RUN make install_assets
|
||||
|
||||
ADD ./docker/phraseanet/ /
|
||||
COPY lib /var/alchemy/lib
|
||||
COPY tmp /var/alchemy/tmp
|
||||
COPY config /var/alchemy/config
|
||||
COPY grammar /var/alchemy/grammar
|
||||
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/*
|
||||
&& apt-get install -y --no-install-recommends \
|
||||
nodejs \
|
||||
yarn \
|
||||
nano \
|
||||
vim \
|
||||
iputils-ping \
|
||||
zsh \
|
||||
ssh \
|
||||
telnet \
|
||||
autoconf \
|
||||
libtool \
|
||||
python \
|
||||
pkg-config \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists \
|
||||
&& git clone https://github.com/robbyrussell/oh-my-zsh.git /bootstrap/.oh-my-zsh \
|
||||
&& mkdir -p /var/alchemy/Phraseanet \
|
||||
&& chown -R app:app /var/alchemy
|
||||
|
||||
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 \
|
||||
&& chmod -R 777 /var/alchemy/Phraseanet/cache \
|
||||
&& mkdir -p /var/alchemy/Phraseanet/datas \
|
||||
&& chmod -R 777 /var/alchemy/Phraseanet/datas \
|
||||
&& mkdir -p /var/alchemy/Phraseanet/tmp \
|
||||
&& chmod -R 777 /var/alchemy/Phraseanet/tmp \
|
||||
&& mkdir -p /var/alchemy/Phraseanet/www/custom \
|
||||
&& chmod -R 777 /var/alchemy/Phraseanet/www/custom \
|
||||
&& mkdir -p /var/alchemy/Phraseanet/config \
|
||||
&& chmod -R 777 /var/alchemy/Phraseanet/config
|
||||
WORKDIR /var/alchemy/Phraseanet
|
||||
ENTRYPOINT ["/phraseanet-entrypoint.sh"]
|
||||
CMD ["/boot.sh"]
|
||||
|
||||
# phraseanet-worker
|
||||
USER app
|
||||
|
||||
# Warm up composer cache for faster builds
|
||||
COPY docker/caching/composer.* ./
|
||||
RUN composer install --prefer-dist --no-dev --no-progress --no-suggest --classmap-authoritative --no-interaction --no-scripts \
|
||||
&& rm -rf vendor composer.*
|
||||
# End warm up
|
||||
|
||||
COPY --chown=app . .
|
||||
|
||||
RUN make install
|
||||
|
||||
ADD ./docker/builder/root /
|
||||
|
||||
# SSH Private repo
|
||||
ARG SSH_PRIVATE_KEY
|
||||
ARG PHRASEANET_PLUGINS
|
||||
|
||||
RUN ( \
|
||||
test ! -z "${SSH_PRIVATE_KEY}" \
|
||||
&& mkdir -p ~/.ssh \
|
||||
&& echo "${SSH_PRIVATE_KEY}" > ~/.ssh/id_rsa \
|
||||
# make sure github domain.com is accepted
|
||||
&& ssh-keyscan -H github.com >> ~/.ssh/known_hosts \
|
||||
&& chmod 600 ~/.ssh/id_rsa \
|
||||
) || echo "Skip SSH key"
|
||||
|
||||
RUN ./docker/phraseanet/plugins/console install
|
||||
|
||||
ENTRYPOINT ["/bootstrap/entrypoint.sh"]
|
||||
|
||||
CMD []
|
||||
|
||||
#########################################################################
|
||||
# Phraseanet web application image
|
||||
#########################################################################
|
||||
|
||||
FROM phraseanet-system as phraseanet-fpm
|
||||
|
||||
COPY --from=builder --chown=app /var/alchemy/Phraseanet /var/alchemy/Phraseanet
|
||||
ADD ./docker/phraseanet/root /
|
||||
WORKDIR /var/alchemy/Phraseanet
|
||||
ENTRYPOINT ["docker/phraseanet/entrypoint.sh"]
|
||||
CMD ["php-fpm", "-F"]
|
||||
|
||||
#########################################################################
|
||||
# Phraseanet worker application image
|
||||
#########################################################################
|
||||
|
||||
FROM phraseanet-fpm as phraseanet-worker
|
||||
CMD ["/worker-boot.sh"]
|
||||
ENTRYPOINT ["docker/phraseanet/worker/entrypoint.sh"]
|
||||
CMD ["bin/console", "task-manager:scheduler:run"]
|
||||
|
||||
#########################################################################
|
||||
# phraseanet-nginx
|
||||
FROM nginx:1.15 as phraseanet-nginx
|
||||
RUN useradd -u 1000 app
|
||||
ADD ./docker/nginx/ /
|
||||
COPY --from=builder /var/alchemy/www /var/alchemy/Phraseanet/www
|
||||
CMD ["/boot.sh"]
|
||||
#########################################################################
|
||||
|
||||
FROM nginx:1.17.8-alpine as phraseanet-nginx
|
||||
RUN adduser --uid 1000 --disabled-password app
|
||||
ADD ./docker/nginx/root /
|
||||
COPY --from=builder /var/alchemy/Phraseanet/www /var/alchemy/Phraseanet/www
|
||||
|
||||
ENTRYPOINT ["/entrypoint.sh"]
|
||||
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
|
@@ -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"]
|
5
Makefile
5
Makefile
@@ -5,7 +5,10 @@ install:
|
||||
make install_assets
|
||||
|
||||
install_composer:
|
||||
composer install --ignore-platform-reqs
|
||||
composer install --ignore-platform-reqs --no-dev --no-suggest --classmap-authoritative --no-interaction
|
||||
|
||||
install_composer_dev:
|
||||
composer install
|
||||
|
||||
install_asset_dependencies:
|
||||
yarn install
|
||||
|
192
README.md
192
README.md
@@ -18,6 +18,8 @@ Phraseanet is licensed under GPL-v3 license.
|
||||
|
||||
https://docs.phraseanet.com/
|
||||
|
||||
For development with Phraseanet API see https://docs.phraseanet.com/4.0/en/Devel/index.html
|
||||
|
||||
# Installation :
|
||||
|
||||
You **must** not download the source from GitHub, but download a packaged version here :
|
||||
@@ -26,13 +28,176 @@ https://www.phraseanet.com/download/
|
||||
|
||||
And follow the install steps described at https://docs.phraseanet.com/4.0/en/Admin/Install.html
|
||||
|
||||
# Try Phraseanet :
|
||||
# Phraseanet with Docker:
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- docker-compose
|
||||
- docker >=v18.01-ce
|
||||
|
||||
## Get started
|
||||
|
||||
You should review the default env variables defined in `.env` file.
|
||||
Use `export` to override these values.
|
||||
|
||||
i.e:
|
||||
```bash
|
||||
export PHRASEANET_DOCKER_TAG=latest
|
||||
export INSTALL_ACCOUNT_EMAIL=foo@bar.com
|
||||
export INSTALL_ACCOUNT_PASSWORD=$3cr3t!
|
||||
export PHRASEANET_APP_PORT=8082
|
||||
```
|
||||
|
||||
### Using a env.local (custom .env)
|
||||
|
||||
It may be easier to deal with a local file to manage our env variables.
|
||||
|
||||
You can add your `env.local` at the root of this project and define a command function in your `~/.bashrc`:
|
||||
|
||||
```bash
|
||||
# ~/.bashrc or ~/.zshrc
|
||||
function dc() {
|
||||
if [ -f env.local ]; then
|
||||
env $(cat env.local | grep -v '#' | tr '\n' ' ') docker-compose $@
|
||||
else
|
||||
docker-compose $@
|
||||
fi
|
||||
}
|
||||
```
|
||||
|
||||
### Running the application
|
||||
|
||||
If you are not interested in the development of Phraseanet, you can ignore everything in `.env` after the `DEV Purpose` part.
|
||||
|
||||
docker-compose -f docker-compose.yml up -d
|
||||
|
||||
Why this option `-f docker-compose.yml`?
|
||||
The development and integration concerns are separated using a `docker-compose.override.yml`. By default, `docker-compose` will include this files if it exists.
|
||||
If you don't work on phraseanet development, avoiding this `-f docker-compose.yml` parameters will throw errors. So you have to add this options on every `docker-compose` commands to avoid this inclusion.
|
||||
|
||||
> You can also delete the `docker-compose.override.yml` to get free from this behavior.
|
||||
|
||||
#### Running workers
|
||||
|
||||
```bash
|
||||
docker-compose -f docker-compose.yml run --rm worker <command>
|
||||
```
|
||||
|
||||
Where `<command>` can be:
|
||||
- `bin/console task-manager:scheduler:run` (default)
|
||||
- `bin/console worker:execute -m 2`
|
||||
- ...
|
||||
|
||||
The default parameters allow you to reach the app with : `http://localhost:8082`
|
||||
|
||||
### Use Phraseanet images from docker hub
|
||||
|
||||
Retrieve on Docker hub prebuilt images for Phraseanet.
|
||||
|
||||
https://hub.docker.com/r/alchemyfr/phraseanet-fpm
|
||||
|
||||
https://hub.docker.com/r/alchemyfr/phraseanet-worker
|
||||
|
||||
https://hub.docker.com/r/alchemyfr/phraseanet-nginx
|
||||
|
||||
To use them and not build the images locally, we advise to override the properties in file: env.local
|
||||
|
||||
```bash
|
||||
# Registry from where you pull Docker images
|
||||
PHRASEANET_DOCKER_REGISTRY=alchemyfr
|
||||
# Tag of the Docker images
|
||||
PHRASEANET_DOCKER_TAG=
|
||||
```
|
||||
|
||||
## Development mode
|
||||
|
||||
The development mode uses the `docker-compose-override.yml` file.
|
||||
|
||||
You can run it with:
|
||||
|
||||
docker-compose up -d
|
||||
|
||||
The environment is not ready yet: you have to fetch all dependencies.
|
||||
|
||||
This can be made easily from the builder container:
|
||||
|
||||
docker-compose run --rm -u app builder make install install_composer_dev
|
||||
|
||||
> Please note that the phraseanet image does not contain nor `composer` neither `node` tools. This allow the final image to be slim.
|
||||
> If you need to use dev tools, ensure you are running the `builder` image!
|
||||
|
||||
### Developer shell
|
||||
|
||||
You can also obtain a shell access in builder container:
|
||||
|
||||
```bash
|
||||
docker-compose run --rm builder /bin/bash
|
||||
# or
|
||||
docker-compose run --rm builder /bin/zsh
|
||||
```
|
||||
|
||||
In this container you will have the same libraries (PHP, Node, composer, ...) that are used to build images.
|
||||
Also you have utils for development like telnet, ping, ssh, git, ...
|
||||
Your $HOME/.ssh directory is also mounted to builder's home with your ssh agent.
|
||||
|
||||
### Using Xdebug
|
||||
|
||||
Xdebug is enabled by default with the `docker-compose.override.yml`
|
||||
You can disable it by setting:
|
||||
|
||||
```bash
|
||||
export XDEBUG_ENABLED=0
|
||||
```
|
||||
|
||||
Remote host is fixed because of the subnet network from compose.
|
||||
|
||||
You need to configure file mapping in your IDE.
|
||||
For PhpStorm, you can follow this example:
|
||||
|
||||

|
||||
|
||||
> Configure the `Absolute path on the server` to `/var/alchemy/Phraseanet` at the project root path (i.e. `~/projects/Phraseanet`).
|
||||
|
||||
#### Xdebug on MacOS
|
||||
|
||||
You have to set the following env:
|
||||
```bash
|
||||
XDEBUG_REMOTE_HOST=host.docker.internal
|
||||
```
|
||||
|
||||
> Don't forget to recreate your container (`docker-compose up -d phraseanet`)
|
||||
|
||||
### Build images with plugins
|
||||
|
||||
Plugins can be installed during build if you set the `PHRASEANET_PLUGINS` env var as follows:
|
||||
|
||||
```bash
|
||||
PHRASEANET_PLUGINS="git@github.com:alchemy-fr/Phraseanet-plugin-webgallery.git"
|
||||
|
||||
# You can optionally precise the branch to install
|
||||
# If not precised, the main branch will be pulled
|
||||
PHRASEANET_PLUGINS="git@github.com:alchemy-fr/Phraseanet-plugin-webgallery.git(custom-branch)"
|
||||
|
||||
# Plugins are separated by spaces
|
||||
PHRASEANET_PLUGINS="git@github.com:foo/bar.git(branch-1) git@github.com:baz/42.git"
|
||||
```
|
||||
|
||||
If you install private plugins, make sure you export your SSH private key content in order to allow docker build to access the GIT repository:
|
||||
```bash
|
||||
export PHRASEANET_SSH_PRIVATE_KEY=$(cat ~/.ssh/id_rsa)
|
||||
# or if your private key is protected by a passphrase:
|
||||
export PHRASEANET_SSH_PRIVATE_KEY=$(openssl rsa -in ~/.ssh/id_rsa -out /tmp/id_rsa_raw && cat /tmp/id_rsa_raw && rm /tmp/id_rsa_raw)
|
||||
```
|
||||
|
||||
# Try Phraseanet with Pre installed VM (deprecated)
|
||||
|
||||
You can also download a testing pre installed Virtual Machine in OVA format here :
|
||||
|
||||
https://www.phraseanet.com/download/
|
||||
|
||||
# Development :
|
||||
# With Vagrant (deprecated)
|
||||
|
||||
## Development :
|
||||
|
||||
For development purpose Phraseanet is shipped with ready to use development environments using vagrant.
|
||||
You can easily choose betweeen a complete build or a prebuild box, with a specific PHP version.
|
||||
@@ -49,28 +214,5 @@ Ex:
|
||||
- vagrant up --provision //// 5.6 ///// 1 >> Build the alchemy/phraseanet-php-5.6 box
|
||||
|
||||
|
||||
For development with Phraseanet API see https://docs.phraseanet.com/4.0/en/Devel/index.html
|
||||
|
||||
|
||||
# Docker build
|
||||
|
||||
WARNING : still in a work-in-progress status and can be used only for test purposes.
|
||||
|
||||
The docker distribution come with 3 differents containers :
|
||||
* An nginx that act as the front http server.
|
||||
* The php-fpm who serves the php files through nginx.
|
||||
* The worker who execute Phraseanet scheduler.
|
||||
|
||||
## How to build
|
||||
|
||||
The three images can be built respectively with these commands :
|
||||
|
||||
# nginx server
|
||||
docker build --target phraseanet-nginx -t local/phraseanet-nginx .
|
||||
|
||||
# php-fpm application
|
||||
docker build --target phraseanet-fpm -t local/phraseanet-fpm .
|
||||
|
||||
# worker
|
||||
docker build --target phraseanet-worker -t local/phraseanet-worker .
|
||||
|
||||
|
35
bin/console
35
bin/console
@@ -23,12 +23,19 @@ use Alchemy\Phrasea\Command\SearchEngine\IndexPopulateCommand;
|
||||
use Alchemy\Phrasea\Command\Thesaurus\FindConceptsCommand;
|
||||
use Alchemy\Phrasea\Core\Version;
|
||||
use Alchemy\Phrasea\Command\CreateCollection;
|
||||
use Alchemy\Phrasea\Command\Collection\UnPublishCollectionCommand;
|
||||
use Alchemy\Phrasea\Command\Collection\PublishCollectionCommand;
|
||||
use Alchemy\Phrasea\Command\Collection\ListCollectionCommand;
|
||||
use Alchemy\Phrasea\Command\Databox\CreateDataboxCommand;
|
||||
use Alchemy\Phrasea\Command\Databox\UnMountDataboxCommand;
|
||||
use Alchemy\Phrasea\Command\Databox\MountDataboxCommand;
|
||||
use Alchemy\Phrasea\Command\Databox\ListDataboxCommand;
|
||||
use Alchemy\Phrasea\Command\MailTest;
|
||||
use Alchemy\Phrasea\Command\Compile\Configuration;
|
||||
use Alchemy\Phrasea\Command\RecordAdd;
|
||||
use Alchemy\Phrasea\Command\RescanTechnicalDatas;
|
||||
use Alchemy\Phrasea\CLI;
|
||||
use Alchemy\Phrasea\Command\User\UserApplicationsCommand;
|
||||
use Alchemy\Phrasea\Command\Plugin\AddPlugin;
|
||||
use Alchemy\Phrasea\Command\Plugin\RemovePlugin;
|
||||
use Alchemy\Phrasea\Command\CheckConfig;
|
||||
@@ -46,7 +53,11 @@ use Alchemy\Phrasea\Command\Task\TaskRun;
|
||||
use Alchemy\Phrasea\Command\Task\TaskStart;
|
||||
use Alchemy\Phrasea\Command\Task\TaskState;
|
||||
use Alchemy\Phrasea\Command\Task\TaskStop;
|
||||
use Alchemy\Phrasea\Command\User\UserCreateCommand;
|
||||
use Alchemy\Phrasea\Command\User\UserPasswordCommand;
|
||||
use Alchemy\Phrasea\Command\User\UserListCommand;
|
||||
use Alchemy\Phrasea\Command\UpgradeDBDatas;
|
||||
use Alchemy\Phrasea\Command\ApplyRightsCommand;
|
||||
|
||||
require_once __DIR__ . '/../lib/autoload.php';
|
||||
|
||||
@@ -83,6 +94,7 @@ $cli->command(new \module_console_aboutLicense('about:license'));
|
||||
$cli->command(new CheckConfig('check:config'));
|
||||
|
||||
$cli->command(new UpgradeDBDatas('system:upgrade-datas'));
|
||||
$cli->command(new ApplyRightsCommand('system:apply-rights'));
|
||||
|
||||
$cli->command(new \module_console_systemMailCheck('system:mail-check'));
|
||||
$cli->command(new \module_console_systemBackupDB('system:backup-db'));
|
||||
@@ -107,8 +119,25 @@ $cli->command(new \module_console_fieldsDelete('fields:delete'));
|
||||
$cli->command(new \module_console_fieldsRename('fields:rename'));
|
||||
$cli->command(new \module_console_fieldsMerge('fields:merge'));
|
||||
|
||||
$cli->command(new UserApplicationsCommand('user:applications'));
|
||||
|
||||
$cli->command(new CreateCollection('collection:create'));
|
||||
$cli->command(new UnPublishCollectionCommand('collection:unpublish'));
|
||||
$cli->command(new PublishCollectionCommand('collection:publish'));
|
||||
$cli->command(new ListCollectionCommand('collection:list'));
|
||||
|
||||
$cli->command(new ListDataboxCommand('databox:list'));
|
||||
$cli->command(new CreateDataboxCommand('databox:create'));
|
||||
$cli->command(new UnMountDataboxCommand('databox:unmount'));
|
||||
$cli->command(new MountDataboxCommand('databox:mount'));
|
||||
|
||||
|
||||
$cli->command(new UserCreateCommand('user:create'));
|
||||
|
||||
$cli->command(new UserPasswordCommand('user:password'));
|
||||
|
||||
$cli->command(new UserListCommand('user:list'));
|
||||
|
||||
|
||||
$cli->command(new RecordAdd('records:add'));
|
||||
$cli->command(new RescanTechnicalDatas('records:rescan-technical-datas'));
|
||||
@@ -133,9 +162,9 @@ $cli->command(new QueryParseCommand());
|
||||
$cli->command(new QuerySampleCommand());
|
||||
$cli->command(new FindConceptsCommand());
|
||||
|
||||
$cli->command($cli['alchemy_worker.commands.run_dispatcher_command']);
|
||||
$cli->command($cli['alchemy_worker.commands.run_worker_command']);
|
||||
$cli->command($cli['alchemy_worker.commands.show_configuration']);
|
||||
//$cli->command($cli['alchemy_worker.commands.run_dispatcher_command']);
|
||||
//$cli->command($cli['alchemy_worker.commands.run_worker_command']);
|
||||
//$cli->command($cli['alchemy_worker.commands.show_configuration']);
|
||||
|
||||
$cli->loadPlugins();
|
||||
|
||||
|
@@ -18,6 +18,18 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "package",
|
||||
"package": {
|
||||
"name": "exiftool/exiftool",
|
||||
"version": "11",
|
||||
"source": {
|
||||
"url": "https://github.com/exiftool/exiftool.git",
|
||||
"type": "git",
|
||||
"reference": "11.84"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "vcs",
|
||||
"url": "https://github.com/alchemy-fr/tcpdf-clone"
|
||||
@@ -84,7 +96,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.6",
|
||||
"monolog/monolog": "~1.3",
|
||||
"mrclay/minify": "~2.1.6",
|
||||
"neutron/process-manager": "2.0.x-dev@dev",
|
||||
@@ -93,9 +105,9 @@
|
||||
"neutron/silex-imagine-provider": "~0.1.0",
|
||||
"neutron/temporary-filesystem": "~2.1",
|
||||
"pagerfanta/pagerfanta": "^1.0",
|
||||
"php-ffmpeg/php-ffmpeg": "~0.5.0",
|
||||
"php-ffmpeg/php-ffmpeg": "^v0.15",
|
||||
"php-xpdf/php-xpdf": "~0.2.1",
|
||||
"phpexiftool/exiftool": "10.10",
|
||||
"exiftool/exiftool": "^11",
|
||||
"ramsey/uuid": "^3.0",
|
||||
"roave/security-advisories": "dev-master",
|
||||
"silex/silex": "^1.3.0",
|
||||
|
267
composer.lock
generated
267
composer.lock
generated
@@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "f3b1fc0a30bf14b05e57ce673550d9c0",
|
||||
"content-hash": "008ff0b5d3d13b4f0ce5d34348ded83a",
|
||||
"packages": [
|
||||
{
|
||||
"name": "alchemy-fr/tcpdf-clone",
|
||||
@@ -131,16 +131,16 @@
|
||||
},
|
||||
{
|
||||
"name": "alchemy/embed-bundle",
|
||||
"version": "2.0.7",
|
||||
"version": "2.0.9",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/alchemy-fr/embed-bundle.git",
|
||||
"reference": "c585ccf18e53a9a6f2b696ddbbc39521732dfdde"
|
||||
"reference": "8cdb9612a9e3edd998b68f0803eacca8e0f50775"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/alchemy-fr/embed-bundle/zipball/c585ccf18e53a9a6f2b696ddbbc39521732dfdde",
|
||||
"reference": "c585ccf18e53a9a6f2b696ddbbc39521732dfdde",
|
||||
"url": "https://api.github.com/repos/alchemy-fr/embed-bundle/zipball/8cdb9612a9e3edd998b68f0803eacca8e0f50775",
|
||||
"reference": "8cdb9612a9e3edd998b68f0803eacca8e0f50775",
|
||||
"shasum": ""
|
||||
},
|
||||
"require-dev": {
|
||||
@@ -178,10 +178,10 @@
|
||||
],
|
||||
"description": "Embed resources bundle",
|
||||
"support": {
|
||||
"source": "https://github.com/alchemy-fr/embed-bundle/tree/2.0.7",
|
||||
"source": "https://github.com/alchemy-fr/embed-bundle/tree/2.0.9",
|
||||
"issues": "https://github.com/alchemy-fr/embed-bundle/issues"
|
||||
},
|
||||
"time": "2019-09-02T12:28:19+00:00"
|
||||
"time": "2020-02-04T14:53:00+00:00"
|
||||
},
|
||||
{
|
||||
"name": "alchemy/geonames-api-consumer",
|
||||
@@ -275,16 +275,16 @@
|
||||
},
|
||||
{
|
||||
"name": "alchemy/mediavorus",
|
||||
"version": "0.4.9",
|
||||
"version": "0.4.10",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/alchemy-fr/MediaVorus.git",
|
||||
"reference": "1a96dc4142ff8474c11285cab9eab11df9683255"
|
||||
"reference": "3e235eb1efb528aea2973c946f4bf47630b98985"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/alchemy-fr/MediaVorus/zipball/1a96dc4142ff8474c11285cab9eab11df9683255",
|
||||
"reference": "1a96dc4142ff8474c11285cab9eab11df9683255",
|
||||
"url": "https://api.github.com/repos/alchemy-fr/MediaVorus/zipball/3e235eb1efb528aea2973c946f4bf47630b98985",
|
||||
"reference": "3e235eb1efb528aea2973c946f4bf47630b98985",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -300,6 +300,7 @@
|
||||
},
|
||||
"require-dev": {
|
||||
"jms/serializer": "~0.12.0",
|
||||
"phpunit/phpunit": "^4.0|^5.0",
|
||||
"silex/silex": "~1.0",
|
||||
"symfony/yaml": "~2.0"
|
||||
},
|
||||
@@ -333,7 +334,7 @@
|
||||
"keywords": [
|
||||
"metadata"
|
||||
],
|
||||
"time": "2019-01-22T11:23:34+00:00"
|
||||
"time": "2020-02-18T13:37:45+00:00"
|
||||
},
|
||||
{
|
||||
"name": "alchemy/oauth2php",
|
||||
@@ -383,30 +384,27 @@
|
||||
},
|
||||
{
|
||||
"name": "alchemy/phpexiftool",
|
||||
"version": "0.7.2",
|
||||
"version": "0.7.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/alchemy-fr/PHPExiftool.git",
|
||||
"reference": "ba1cb51eceb6562d7996023478977a8739de188b"
|
||||
"reference": "0b22e7d7cc40f2a6b9c85c0cfbd968a39a31dab2"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/alchemy-fr/PHPExiftool/zipball/ba1cb51eceb6562d7996023478977a8739de188b",
|
||||
"reference": "ba1cb51eceb6562d7996023478977a8739de188b",
|
||||
"url": "https://api.github.com/repos/alchemy-fr/PHPExiftool/zipball/0b22e7d7cc40f2a6b9c85c0cfbd968a39a31dab2",
|
||||
"reference": "0b22e7d7cc40f2a6b9c85c0cfbd968a39a31dab2",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"doctrine/cache": "^1.0",
|
||||
"doctrine/collections": "^1.0",
|
||||
"exiftool/exiftool": "^11",
|
||||
"monolog/monolog": "^1.3",
|
||||
"php": ">=5.5.9",
|
||||
"phpexiftool/exiftool": "10.10",
|
||||
"symfony/console": "^2.1|^3.0",
|
||||
"symfony/process": "^2.1|^3.0"
|
||||
},
|
||||
"replace": {
|
||||
"phpexiftool/phpexiftool": "<0.5.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"jms/serializer": "~0.10|^1.0",
|
||||
"phpunit/phpunit": "^4.0|^5.0",
|
||||
@@ -452,7 +450,7 @@
|
||||
"exiftool",
|
||||
"metadata"
|
||||
],
|
||||
"time": "2019-02-13T13:06:43+00:00"
|
||||
"time": "2020-01-17T14:28:33+00:00"
|
||||
},
|
||||
{
|
||||
"name": "alchemy/queue-bundle",
|
||||
@@ -2105,6 +2103,16 @@
|
||||
],
|
||||
"time": "2012-05-30T15:01:08+00:00"
|
||||
},
|
||||
{
|
||||
"name": "exiftool/exiftool",
|
||||
"version": "11",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/exiftool/exiftool.git",
|
||||
"reference": "11.84"
|
||||
},
|
||||
"type": "library"
|
||||
},
|
||||
{
|
||||
"name": "facebook/graph-sdk",
|
||||
"version": "5.6.1",
|
||||
@@ -2853,6 +2861,7 @@
|
||||
}
|
||||
],
|
||||
"description": "Provides a simple API and specification that abstracts away the details of HTTP into a single PHP function.",
|
||||
"abandoned": true,
|
||||
"time": "2015-05-20T03:37:09+00:00"
|
||||
},
|
||||
{
|
||||
@@ -2903,6 +2912,7 @@
|
||||
"Guzzle",
|
||||
"stream"
|
||||
],
|
||||
"abandoned": true,
|
||||
"time": "2014-10-12T19:18:40+00:00"
|
||||
},
|
||||
{
|
||||
@@ -4321,16 +4331,16 @@
|
||||
},
|
||||
{
|
||||
"name": "media-alchemyst/media-alchemyst",
|
||||
"version": "0.5.2",
|
||||
"version": "0.5.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/alchemy-fr/Media-Alchemyst.git",
|
||||
"reference": "5d2fe6dd95215804202ecf0466fd9cfaeedd0140"
|
||||
"reference": "2b9f7697997f7863bbc3d08344c559a1cba519c2"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/alchemy-fr/Media-Alchemyst/zipball/5d2fe6dd95215804202ecf0466fd9cfaeedd0140",
|
||||
"reference": "5d2fe6dd95215804202ecf0466fd9cfaeedd0140",
|
||||
"url": "https://api.github.com/repos/alchemy-fr/Media-Alchemyst/zipball/2b9f7697997f7863bbc3d08344c559a1cba519c2",
|
||||
"reference": "2b9f7697997f7863bbc3d08344c559a1cba519c2",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -4340,9 +4350,9 @@
|
||||
"monolog/monolog": "~1.0",
|
||||
"neutron/temporary-filesystem": "^2.1.1",
|
||||
"php": ">=5.3.3",
|
||||
"php-ffmpeg/php-ffmpeg": ">=0.4.2,<0.6",
|
||||
"php-ffmpeg/php-ffmpeg": "^v0.15",
|
||||
"php-mp4box/php-mp4box": "~0.3.0",
|
||||
"php-unoconv/php-unoconv": "~0.3.0",
|
||||
"php-unoconv/php-unoconv": "~0.3.1",
|
||||
"pimple/pimple": "~1.0",
|
||||
"swftools/swftools": "~0.3.0",
|
||||
"symfony/console": "^2.1|^3.0",
|
||||
@@ -4391,7 +4401,7 @@
|
||||
"video",
|
||||
"video processing"
|
||||
],
|
||||
"time": "2019-01-25T12:09:11+00:00"
|
||||
"time": "2020-04-01T08:51:55+00:00"
|
||||
},
|
||||
{
|
||||
"name": "monolog/monolog",
|
||||
@@ -5137,29 +5147,29 @@
|
||||
},
|
||||
{
|
||||
"name": "php-ffmpeg/php-ffmpeg",
|
||||
"version": "0.5.1",
|
||||
"version": "v0.15",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/PHP-FFMpeg/PHP-FFMpeg.git",
|
||||
"reference": "c8949fe3df89edd7692368cc110a51a27971f28a"
|
||||
"reference": "984dbd046b6d8c285f9e7419fc7645f197513bfa"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/PHP-FFMpeg/PHP-FFMpeg/zipball/c8949fe3df89edd7692368cc110a51a27971f28a",
|
||||
"reference": "c8949fe3df89edd7692368cc110a51a27971f28a",
|
||||
"url": "https://api.github.com/repos/PHP-FFMpeg/PHP-FFMpeg/zipball/984dbd046b6d8c285f9e7419fc7645f197513bfa",
|
||||
"reference": "984dbd046b6d8c285f9e7419fc7645f197513bfa",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"alchemy/binary-driver": "~1.5",
|
||||
"doctrine/cache": "~1.0",
|
||||
"evenement/evenement": "~1.0",
|
||||
"neutron/temporary-filesystem": "~2.1, >=2.1.1",
|
||||
"php": ">=5.3.3"
|
||||
"alchemy/binary-driver": "^1.5 || ~2.0.0 || ^5.0",
|
||||
"doctrine/cache": "^1.0",
|
||||
"evenement/evenement": "^3.0 || ^2.0 || ^1.0",
|
||||
"neutron/temporary-filesystem": "^2.1.1",
|
||||
"php": "^5.3.9 || ^7.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "~3.7",
|
||||
"sami/sami": "~1.0",
|
||||
"silex/silex": "~1.0"
|
||||
"silex/silex": "~1.0",
|
||||
"symfony/phpunit-bridge": "^5.0.4"
|
||||
},
|
||||
"suggest": {
|
||||
"php-ffmpeg/extras": "A compilation of common audio & video drivers for PHP-FFMpeg"
|
||||
@@ -5167,7 +5177,7 @@
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "0.5-dev"
|
||||
"dev-master": "0.7-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
@@ -5189,6 +5199,21 @@
|
||||
"name": "Phraseanet Team",
|
||||
"email": "info@alchemy.fr",
|
||||
"homepage": "http://www.phraseanet.com/"
|
||||
},
|
||||
{
|
||||
"name": "Patrik Karisch",
|
||||
"email": "patrik@karisch.guru",
|
||||
"homepage": "http://www.karisch.guru"
|
||||
},
|
||||
{
|
||||
"name": "Romain Biard",
|
||||
"email": "romain.biard@gmail.com",
|
||||
"homepage": "https://www.strime.io/"
|
||||
},
|
||||
{
|
||||
"name": "Jens Hausdorf",
|
||||
"email": "hello@jens-hausdorf.de",
|
||||
"homepage": "https://jens-hausdorf.de"
|
||||
}
|
||||
],
|
||||
"description": "FFMpeg PHP, an Object Oriented library to communicate with AVconv / ffmpeg",
|
||||
@@ -5202,7 +5227,7 @@
|
||||
"video",
|
||||
"video processing"
|
||||
],
|
||||
"time": "2014-08-26T08:46:56+00:00"
|
||||
"time": "2020-03-23T09:32:09+00:00"
|
||||
},
|
||||
{
|
||||
"name": "php-mp4box/php-mp4box",
|
||||
@@ -5257,16 +5282,16 @@
|
||||
},
|
||||
{
|
||||
"name": "php-unoconv/php-unoconv",
|
||||
"version": "0.3.0",
|
||||
"version": "0.3.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/alchemy-fr/PHP-Unoconv.git",
|
||||
"reference": "6d1e14a7467b5d637741396549529dc4d5f9f355"
|
||||
"reference": "8fa666972f6c13fe9703dfe894cd311a61f89f33"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/alchemy-fr/PHP-Unoconv/zipball/6d1e14a7467b5d637741396549529dc4d5f9f355",
|
||||
"reference": "6d1e14a7467b5d637741396549529dc4d5f9f355",
|
||||
"url": "https://api.github.com/repos/alchemy-fr/PHP-Unoconv/zipball/8fa666972f6c13fe9703dfe894cd311a61f89f33",
|
||||
"reference": "8fa666972f6c13fe9703dfe894cd311a61f89f33",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -5303,7 +5328,7 @@
|
||||
"keywords": [
|
||||
"unoconv"
|
||||
],
|
||||
"time": "2013-06-25T10:09:59+00:00"
|
||||
"time": "2019-09-16T09:54:30+00:00"
|
||||
},
|
||||
{
|
||||
"name": "php-xpdf/php-xpdf",
|
||||
@@ -5405,39 +5430,6 @@
|
||||
],
|
||||
"time": "2015-05-17T12:39:23+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpexiftool/exiftool",
|
||||
"version": "10.10",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/alchemy-fr/exiftool.git",
|
||||
"reference": "0833cab894c890353192a83011428525a318bedf"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/alchemy-fr/exiftool/zipball/0833cab894c890353192a83011428525a318bedf",
|
||||
"reference": "0833cab894c890353192a83011428525a318bedf",
|
||||
"shasum": ""
|
||||
},
|
||||
"type": "library",
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"Perl Licensing"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Phil Harvey",
|
||||
"email": "phil@owl.phy.queensu.ca",
|
||||
"homepage": "http://www.sno.phy.queensu.ca/~phil/exiftool/"
|
||||
}
|
||||
],
|
||||
"description": "Exiftool is a library for reading, writing and editing meta information. This package is not PHP, but required for the main PHP driver : PHP Exiftool",
|
||||
"keywords": [
|
||||
"exiftool",
|
||||
"metadatas"
|
||||
],
|
||||
"time": "2016-01-25T11:10:14+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpoption/phpoption",
|
||||
"version": "1.5.0",
|
||||
@@ -5902,6 +5894,12 @@
|
||||
"url": "https://github.com/Roave/SecurityAdvisories.git",
|
||||
"reference": "0698207bf8a9bed212fdde2d8c7cdc77085660c4"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/0698207bf8a9bed212fdde2d8c7cdc77085660c4",
|
||||
"reference": "0698207bf8a9bed212fdde2d8c7cdc77085660c4",
|
||||
"shasum": ""
|
||||
},
|
||||
"conflict": {
|
||||
"adodb/adodb-php": "<5.20.6",
|
||||
"amphp/artax": ">=2,<2.0.4|>0.7.1,<1.0.4",
|
||||
@@ -6186,7 +6184,7 @@
|
||||
],
|
||||
"description": "A WebProfiler for Silex",
|
||||
"homepage": "http://silex.sensiolabs.org/",
|
||||
"abandoned": true,
|
||||
"abandoned": "symfony/web-profiler-bundle",
|
||||
"time": "2016-01-10T11:39:13+00:00"
|
||||
},
|
||||
{
|
||||
@@ -6436,6 +6434,7 @@
|
||||
"profiler",
|
||||
"silex"
|
||||
],
|
||||
"abandoned": true,
|
||||
"time": "2016-10-26T11:08:02+00:00"
|
||||
},
|
||||
{
|
||||
@@ -6478,6 +6477,7 @@
|
||||
"plugin",
|
||||
"silex"
|
||||
],
|
||||
"abandoned": true,
|
||||
"time": "2015-11-11T07:16:28+00:00"
|
||||
},
|
||||
{
|
||||
@@ -7726,6 +7726,7 @@
|
||||
"code",
|
||||
"zf2"
|
||||
],
|
||||
"abandoned": "laminas/laminas-code",
|
||||
"time": "2016-04-20T17:26:42+00:00"
|
||||
},
|
||||
{
|
||||
@@ -7780,12 +7781,13 @@
|
||||
"events",
|
||||
"zf2"
|
||||
],
|
||||
"abandoned": "laminas/laminas-eventmanager",
|
||||
"time": "2016-02-18T20:53:00+00:00"
|
||||
}
|
||||
],
|
||||
"packages-dev": [
|
||||
{
|
||||
"name": "mikey179/vfsStream",
|
||||
"name": "mikey179/vfsstream",
|
||||
"version": "v1.6.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
@@ -7830,76 +7832,6 @@
|
||||
"homepage": "http://vfs.bovigo.org/",
|
||||
"time": "2016-07-18T14:02:57+00:00"
|
||||
},
|
||||
{
|
||||
"name": "php-amqplib/php-amqplib",
|
||||
"version": "v2.6.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-amqplib/php-amqplib.git",
|
||||
"reference": "fa2f0d4410a11008cb36b379177291be7ee9e4f6"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-amqplib/php-amqplib/zipball/fa2f0d4410a11008cb36b379177291be7ee9e4f6",
|
||||
"reference": "fa2f0d4410a11008cb36b379177291be7ee9e4f6",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-bcmath": "*",
|
||||
"ext-mbstring": "*",
|
||||
"php": ">=5.3.0"
|
||||
},
|
||||
"replace": {
|
||||
"videlalvaro/php-amqplib": "self.version"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^4.8",
|
||||
"scrutinizer/ocular": "^1.1",
|
||||
"squizlabs/php_codesniffer": "^2.5"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-sockets": "Use AMQPSocketConnection"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.7-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"PhpAmqpLib\\": "PhpAmqpLib/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"LGPL-2.1"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Alvaro Videla",
|
||||
"role": "Original Maintainer"
|
||||
},
|
||||
{
|
||||
"name": "John Kelly",
|
||||
"email": "johnmkelly86@gmail.com",
|
||||
"role": "Maintainer"
|
||||
},
|
||||
{
|
||||
"name": "Raúl Araya",
|
||||
"email": "nubeiro@gmail.com",
|
||||
"role": "Maintainer"
|
||||
}
|
||||
],
|
||||
"description": "Formerly videlalvaro/php-amqplib. This library is a pure PHP implementation of the AMQP protocol. It's been tested against RabbitMQ.",
|
||||
"homepage": "https://github.com/php-amqplib/php-amqplib/",
|
||||
"keywords": [
|
||||
"message",
|
||||
"queue",
|
||||
"rabbitmq"
|
||||
],
|
||||
"time": "2016-04-11T14:30:01+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpdocumentor/reflection-common",
|
||||
"version": "1.0",
|
||||
@@ -8046,6 +7978,39 @@
|
||||
],
|
||||
"time": "2016-11-25T06:54:22+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpexiftool/exiftool",
|
||||
"version": "10.10",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/alchemy-fr/exiftool.git",
|
||||
"reference": "0833cab894c890353192a83011428525a318bedf"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/alchemy-fr/exiftool/zipball/0833cab894c890353192a83011428525a318bedf",
|
||||
"reference": "0833cab894c890353192a83011428525a318bedf",
|
||||
"shasum": ""
|
||||
},
|
||||
"type": "library",
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"Perl Licensing"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Phil Harvey",
|
||||
"email": "phil@owl.phy.queensu.ca",
|
||||
"homepage": "http://www.sno.phy.queensu.ca/~phil/exiftool/"
|
||||
}
|
||||
],
|
||||
"description": "Exiftool is a library for reading, writing and editing meta information. This package is not PHP, but required for the main PHP driver : PHP Exiftool",
|
||||
"keywords": [
|
||||
"exiftool",
|
||||
"metadatas"
|
||||
],
|
||||
"time": "2016-01-25T11:10:14+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpspec/prophecy",
|
||||
"version": "v1.6.2",
|
||||
|
@@ -22,13 +22,54 @@ main:
|
||||
path: '/tmp/db.sqlite'
|
||||
charset: UTF8
|
||||
cache:
|
||||
type: MemcacheCache
|
||||
type: redis
|
||||
options:
|
||||
host: localhost
|
||||
port: 11211
|
||||
port: 6379
|
||||
search-engine:
|
||||
type: phrasea
|
||||
options: []
|
||||
type: elasticsearch
|
||||
options:
|
||||
host: elasticsearch
|
||||
port: 9200
|
||||
index: ''
|
||||
shards: 3
|
||||
replicas: 0
|
||||
minScore: 2
|
||||
highlight: true
|
||||
populate_order: RECORD_ID
|
||||
populate_direction: DESC
|
||||
activeTab: ''
|
||||
facets:
|
||||
_base:
|
||||
limit: 10
|
||||
_collection:
|
||||
limit: 10
|
||||
_doctype:
|
||||
limit: 10
|
||||
_camera_model:
|
||||
limit: 0
|
||||
_iso:
|
||||
limit: 0
|
||||
_aperture:
|
||||
limit: 0
|
||||
_shutterspeed:
|
||||
limit: 0
|
||||
_flashfired:
|
||||
limit: 0
|
||||
_framerate:
|
||||
limit: 0
|
||||
_audiosamplerate:
|
||||
limit: 0
|
||||
_videocodec:
|
||||
limit: 0
|
||||
_audiocodec:
|
||||
limit: 0
|
||||
_orientation:
|
||||
limit: 0
|
||||
_colorspace:
|
||||
limit: 0
|
||||
_mimetype:
|
||||
limit: 0
|
||||
task-manager:
|
||||
status: started
|
||||
enabled: true
|
||||
@@ -62,12 +103,7 @@ main:
|
||||
mp4box_timeout: 60
|
||||
swftools_timeout: 60
|
||||
unoconv_timeout: 60
|
||||
task-manager:
|
||||
status: started
|
||||
listener:
|
||||
protocol: tcp
|
||||
host: 127.0.0.1
|
||||
port: 6700
|
||||
exiftool_timeout: 60
|
||||
storage:
|
||||
subdefs: null
|
||||
cache: null
|
||||
@@ -75,20 +111,7 @@ main:
|
||||
download: null
|
||||
lazaret: null
|
||||
caption: null
|
||||
bridge:
|
||||
youtube:
|
||||
enabled: false
|
||||
client_id: null
|
||||
client_secret: null
|
||||
developer_key: null
|
||||
flickr:
|
||||
enabled: false
|
||||
client_id: null
|
||||
client_secret: null
|
||||
dailymotion:
|
||||
enabled: false
|
||||
client_id: null
|
||||
client_secret: null
|
||||
worker_tmp_files: null
|
||||
border-manager:
|
||||
enabled: true
|
||||
extension-mapping:
|
||||
@@ -98,12 +121,17 @@ border-manager:
|
||||
-
|
||||
type: Checker\Sha256
|
||||
enabled: true
|
||||
collections: []
|
||||
compare-ignore-collections: []
|
||||
-
|
||||
type: Checker\UUID
|
||||
enabled: true
|
||||
collections: []
|
||||
compare-ignore-collections: []
|
||||
-
|
||||
type: Checker\Colorspace
|
||||
enabled: false
|
||||
collections: []
|
||||
options:
|
||||
colorspaces: [cmyk, grayscale, rgb]
|
||||
media_types: [Image]
|
||||
@@ -123,6 +151,8 @@ border-manager:
|
||||
enabled: false
|
||||
options:
|
||||
sensitive: true
|
||||
collections: []
|
||||
compare-ignore-collections: []
|
||||
-
|
||||
type: Checker\MediaType
|
||||
enabled: false
|
||||
@@ -244,6 +274,14 @@ embed_bundle:
|
||||
document:
|
||||
player: flexpaper
|
||||
enable_pdfjs: true
|
||||
video-editor:
|
||||
ChapterVttFieldName: VideoTextTrackChapters
|
||||
seekBackwardStep: 500 # in ms
|
||||
seekForwardStep: 500 # in ms
|
||||
playbackRates:
|
||||
- 1
|
||||
- '1.5'
|
||||
- 3
|
||||
geocoding-providers:
|
||||
-
|
||||
map-provider: mapboxWebGL
|
||||
|
103
docker-compose.override.yml
Normal file
103
docker-compose.override.yml
Normal file
@@ -0,0 +1,103 @@
|
||||
version: "3.4"
|
||||
services:
|
||||
phpmyadmin:
|
||||
image: phpmyadmin/phpmyadmin
|
||||
restart: on-failure
|
||||
ports:
|
||||
- ${PHRASEANET_PHPMYADMIN_PORT}:80
|
||||
depends_on:
|
||||
- db
|
||||
|
||||
gateway:
|
||||
volumes:
|
||||
- ../:/var/alchemy
|
||||
- .:/var/alchemy/Phraseanet
|
||||
- ./docker/nginx/root/entrypoint.sh:/entrypoint.sh
|
||||
- ${PHRASEANET_DATA_DIR}:/var/alchemy/Phraseanet/datas:rw
|
||||
- ${PHRASEANET_THUMBNAILS_DIR}:/var/alchemy/Phraseanet/www/thumbnails:rw
|
||||
- ${PHRASEANET_TMP_DIR}:/var/alchemy/Phraseanet/tmp:rw
|
||||
- ${PHRASEANET_CUSTOM_DIR}:/var/alchemy/Phraseanet/www/custom:rw
|
||||
|
||||
builder:
|
||||
build:
|
||||
context: .
|
||||
target: builder
|
||||
args:
|
||||
- SSH_PRIVATE_KEY=${PHRASEANET_SSH_PRIVATE_KEY}
|
||||
- PHRASEANET_PLUGINS=${PHRASEANET_PLUGINS}
|
||||
stdin_open: true
|
||||
tty: true
|
||||
volumes:
|
||||
- ../:/var/alchemy
|
||||
- .:/var/alchemy/Phraseanet
|
||||
- ${PHRASEANET_CONFIG_DIR}:/var/alchemy/Phraseanet/config:rw
|
||||
- ${PHRASEANET_LOGS_DIR}:/var/alchemy/Phraseanet/logs:rw
|
||||
- ${PHRASEANET_DATA_DIR}:/var/alchemy/Phraseanet/datas:rw
|
||||
- ${PHRASEANET_THUMBNAILS_DIR}:/var/alchemy/Phraseanet/www/thumbnails:rw
|
||||
- ${PHRASEANET_CUSTOM_DIR}:/var/alchemy/Phraseanet/www/custom:rw
|
||||
- ${SSH_AUTH_SOCK}:/ssh-auth-sock
|
||||
- ${HOME}/.ssh:/home/app/.ssh
|
||||
- dev_vol:/home/app
|
||||
environment:
|
||||
- PHRASEANET_PROJECT_NAME
|
||||
|
||||
phraseanet:
|
||||
environment:
|
||||
- XDEBUG_ENABLED
|
||||
- XDEBUG_CONFIG=remote_host=${XDEBUG_REMOTE_HOST} idekey=${IDE_KEY} remote_enable=1 profiler_enable=${XDEBUG_PROFILER_ENABLED} profiler_output_dir=/var/alchemy/Phraseanet/cache/profiler
|
||||
- PHP_IDE_CONFIG
|
||||
volumes:
|
||||
- ../:/var/alchemy
|
||||
- .:/var/alchemy/Phraseanet
|
||||
- ${PHRASEANET_CONFIG_DIR}:/var/alchemy/Phraseanet/config:rw
|
||||
- ${PHRASEANET_LOGS_DIR}:/var/alchemy/Phraseanet/logs:rw
|
||||
- ${PHRASEANET_DATA_DIR}:/var/alchemy/Phraseanet/datas:rw
|
||||
- ${PHRASEANET_THUMBNAILS_DIR}:/var/alchemy/Phraseanet/www/thumbnails:rw
|
||||
- ${PHRASEANET_CUSTOM_DIR}:/var/alchemy/Phraseanet/www/custom:rw
|
||||
- ${PHRASEANET_TMP_DIR}:/var/alchemy/Phraseanet/tmp:rw
|
||||
|
||||
worker:
|
||||
volumes:
|
||||
- ../:/var/alchemy
|
||||
- .:/var/alchemy/Phraseanet
|
||||
- ${PHRASEANET_CONFIG_DIR}:/var/alchemy/Phraseanet/config:rw
|
||||
- ${PHRASEANET_LOGS_DIR}:/var/alchemy/Phraseanet/logs:rw
|
||||
- ${PHRASEANET_DATA_DIR}:/var/alchemy/Phraseanet/datas:rw
|
||||
- ${PHRASEANET_THUMBNAILS_DIR}:/var/alchemy/Phraseanet/www/thumbnails:rw
|
||||
- ${PHRASEANET_CUSTOM_DIR}:/var/alchemy/Phraseanet/www/custom:rw
|
||||
- ${PHRASEANET_TMP_DIR}:/var/alchemy/Phraseanet/tmp:rw
|
||||
|
||||
rabbitmq:
|
||||
ports:
|
||||
- ${RABBITMQ_MANAGEMENT_PORT}:15672
|
||||
|
||||
db:
|
||||
volumes:
|
||||
- ${PHRASEANET_DB_DIR}:/var/lib/mysql:rw
|
||||
|
||||
mailhog:
|
||||
image: mailhog/mailhog
|
||||
ports:
|
||||
- 1025:1025
|
||||
- 8025:8025
|
||||
|
||||
elasticsearch:
|
||||
ports:
|
||||
- 9200:9200
|
||||
volumes:
|
||||
- ${PHRASEANET_ELASTICSEARCH_DIR}:/usr/share/elasticsearch/data:rw
|
||||
|
||||
kibana:
|
||||
image: kibana:4.6.6
|
||||
ports:
|
||||
- 5601:5601
|
||||
|
||||
networks:
|
||||
default:
|
||||
ipam:
|
||||
config:
|
||||
- subnet: $PHRASEANET_SUBNET_IPS
|
||||
|
||||
volumes:
|
||||
dev_vol:
|
||||
driver: local
|
143
docker-compose.yml
Normal file
143
docker-compose.yml
Normal file
@@ -0,0 +1,143 @@
|
||||
version: "3.4"
|
||||
|
||||
services:
|
||||
gateway:
|
||||
build:
|
||||
context: .
|
||||
target: phraseanet-nginx
|
||||
args:
|
||||
- SSH_PRIVATE_KEY=${PHRASEANET_SSH_PRIVATE_KEY}
|
||||
- PHRASEANET_PLUGINS=${PHRASEANET_PLUGINS}
|
||||
image: $PHRASEANET_DOCKER_REGISTRY/phraseanet-nginx:$PHRASEANET_DOCKER_TAG
|
||||
restart: on-failure
|
||||
volumes:
|
||||
- data_vol:/var/alchemy/Phraseanet/datas:rw
|
||||
- thumbnails_vol:/var/alchemy/Phraseanet/www/thumbnails:rw
|
||||
- custom_vol:/var/alchemy/Phraseanet/www/custom:rw
|
||||
depends_on:
|
||||
- phraseanet
|
||||
environment:
|
||||
- MAX_BODY_SIZE
|
||||
ports:
|
||||
- ${PHRASEANET_APP_PORT}:80
|
||||
|
||||
phraseanet:
|
||||
build:
|
||||
context: .
|
||||
target: phraseanet-fpm
|
||||
args:
|
||||
- SSH_PRIVATE_KEY=${PHRASEANET_SSH_PRIVATE_KEY}
|
||||
- PHRASEANET_PLUGINS=${PHRASEANET_PLUGINS}
|
||||
image: $PHRASEANET_DOCKER_REGISTRY/phraseanet-fpm:$PHRASEANET_DOCKER_TAG
|
||||
restart: on-failure
|
||||
depends_on:
|
||||
- db
|
||||
- redis
|
||||
- rabbitmq
|
||||
- elasticsearch
|
||||
environment:
|
||||
- PHRASEANET_PROJECT_NAME
|
||||
- MAX_BODY_SIZE
|
||||
- MAX_INPUT_VARS
|
||||
- OPCACHE_ENABLED
|
||||
- SESSION_CACHE_LIMITER
|
||||
- PHP_LOG_LEVEL
|
||||
- INSTALL_ACCOUNT_EMAIL
|
||||
- INSTALL_ACCOUNT_PASSWORD
|
||||
- INSTALL_DB_HOST
|
||||
- INSTALL_DB_PORT
|
||||
- INSTALL_DB_USER
|
||||
- INSTALL_DB_PASSWORD
|
||||
- INSTALL_DB_TEMPLATE
|
||||
- INSTALL_APPBOX
|
||||
- INSTALL_DATABOX
|
||||
- INSTALL_SERVER_NAME
|
||||
- INSTALL_RABBITMQ_USER=$RABBITMQ_DEFAULT_USER
|
||||
- INSTALL_RABBITMQ_PASSWORD=$RABBITMQ_DEFAULT_PASS
|
||||
volumes:
|
||||
- config_vol:/var/alchemy/Phraseanet/config:rw
|
||||
- data_vol:/var/alchemy/Phraseanet/datas:rw
|
||||
- tmp_vol:/var/alchemy/Phraseanet/tmp:rw
|
||||
- logs_vol:/var/alchemy/Phraseanet/logs:rw
|
||||
- thumbnails_vol:/var/alchemy/Phraseanet/www/thumbnails:rw
|
||||
- custom_vol:/var/alchemy/Phraseanet/www/custom:rw
|
||||
- cache_vol:/var/alchemy/Phraseanet/cache:rw
|
||||
|
||||
worker:
|
||||
build:
|
||||
context: .
|
||||
target: phraseanet-worker
|
||||
args:
|
||||
- SSH_PRIVATE_KEY=${PHRASEANET_SSH_PRIVATE_KEY}
|
||||
- PHRASEANET_PLUGINS=${PHRASEANET_PLUGINS}
|
||||
image: $PHRASEANET_DOCKER_REGISTRY/phraseanet-worker:$PHRASEANET_DOCKER_TAG
|
||||
restart: on-failure
|
||||
depends_on:
|
||||
- db
|
||||
- redis
|
||||
- rabbitmq
|
||||
- elasticsearch
|
||||
environment:
|
||||
- PHRASEANET_PROJECT_NAME
|
||||
- MAX_BODY_SIZE
|
||||
- MAX_INPUT_VARS
|
||||
- OPCACHE_ENABLED
|
||||
- SESSION_CACHE_LIMITER
|
||||
- PHP_LOG_LEVEL
|
||||
volumes:
|
||||
- config_vol:/var/alchemy/Phraseanet/config:rw
|
||||
- data_vol:/var/alchemy/Phraseanet/datas:rw
|
||||
- tmp_vol:/var/alchemy/Phraseanet/tmp:rw
|
||||
- logs_vol:/var/alchemy/Phraseanet/logs:rw
|
||||
- thumbnails_vol:/var/alchemy/Phraseanet/www/thumbnails:rw
|
||||
- custom_vol:/var/alchemy/Phraseanet/www/custom:rw
|
||||
- cache_vol:/var/alchemy/Phraseanet/cache:rw
|
||||
|
||||
db:
|
||||
image: $PHRASEANET_DOCKER_REGISTRY/phraseanet-db:$PHRASEANET_DOCKER_TAG
|
||||
build: ./docker/db
|
||||
restart: on-failure
|
||||
environment:
|
||||
- MYSQL_ROOT_PASSWORD
|
||||
- MYSQL_MAX_ALLOWED_PACKET
|
||||
volumes:
|
||||
- db_vol:/var/lib/mysql
|
||||
|
||||
rabbitmq:
|
||||
image: rabbitmq:3-management
|
||||
restart: on-failure
|
||||
environment:
|
||||
- RABBITMQ_DEFAULT_USER
|
||||
- RABBITMQ_DEFAULT_PASS
|
||||
|
||||
redis:
|
||||
image: redis
|
||||
restart: on-failure
|
||||
|
||||
elasticsearch:
|
||||
image: $PHRASEANET_DOCKER_REGISTRY/phraseanet-elasticsearch:$PHRASEANET_DOCKER_TAG
|
||||
build: ./docker/elasticsearch
|
||||
restart: on-failure
|
||||
volumes:
|
||||
- elasticsearch_vol:/usr/share/elasticsearch/data
|
||||
|
||||
volumes:
|
||||
config_vol:
|
||||
driver: local
|
||||
data_vol:
|
||||
driver: local
|
||||
tmp_vol:
|
||||
driver: local
|
||||
db_vol:
|
||||
driver: local
|
||||
elasticsearch_vol:
|
||||
driver: local
|
||||
thumbnails_vol:
|
||||
driver: local
|
||||
custom_vol:
|
||||
driver: local
|
||||
cache_vol:
|
||||
driver: local
|
||||
# to be replacer by stdout/stderr
|
||||
logs_vol:
|
||||
driver: local
|
@@ -0,0 +1,7 @@
|
||||
local ret_status="%(?:%{$fg_bold[green]%}➜ :%{$fg_bold[red]%}➜ %s)"
|
||||
PROMPT='%* %{$fg_bold[green]%}%n%{$fg[grey]%}@%m%{$fg_bold[green]%}%u ${ret_status}%{$fg_bold[green]%}%p %{$fg[cyan]%}%c %{$fg_bold[blue]%}$(git_prompt_info)%{$fg_bold[blue]%} % %{$reset_color%}'
|
||||
|
||||
ZSH_THEME_GIT_PROMPT_PREFIX="[%{$fg[red]%}"
|
||||
ZSH_THEME_GIT_PROMPT_SUFFIX="%{$reset_color%}"
|
||||
ZSH_THEME_GIT_PROMPT_DIRTY="%{$fg[blue]%}] %{$fg[yellow]%}✗%{$reset_color%}"
|
||||
ZSH_THEME_GIT_PROMPT_CLEAN="%{$fg[blue]%}]"
|
56
docker/builder/root/bootstrap/.zshrc
Normal file
56
docker/builder/root/bootstrap/.zshrc
Normal file
@@ -0,0 +1,56 @@
|
||||
export LC_ALL=en_US.UTF-8
|
||||
export LANG=en_US.UTF-8
|
||||
|
||||
export ZSH=$HOME/.oh-my-zsh
|
||||
|
||||
ZSH_THEME="alchemy"
|
||||
|
||||
# Uncomment the following line to use case-sensitive completion.
|
||||
# CASE_SENSITIVE="true"
|
||||
|
||||
# Uncomment the following line to use hyphen-insensitive completion. Case
|
||||
# sensitive completion must be off. _ and - will be interchangeable.
|
||||
# HYPHEN_INSENSITIVE="true"
|
||||
|
||||
# Uncomment the following line to disable bi-weekly auto-update checks.
|
||||
# DISABLE_AUTO_UPDATE="true"
|
||||
|
||||
# Uncomment the following line to change how often to auto-update (in days).
|
||||
# export UPDATE_ZSH_DAYS=13
|
||||
|
||||
# Uncomment the following line to disable colors in ls.
|
||||
# DISABLE_LS_COLORS="true"
|
||||
|
||||
# Uncomment the following line to disable auto-setting terminal title.
|
||||
# DISABLE_AUTO_TITLE="true"
|
||||
|
||||
# Uncomment the following line to enable command auto-correction.
|
||||
# ENABLE_CORRECTION="true"
|
||||
|
||||
# Uncomment the following line to display red dots whilst waiting for completion.
|
||||
# COMPLETION_WAITING_DOTS="true"
|
||||
|
||||
# Uncomment the following line if you want to disable marking untracked files
|
||||
# under VCS as dirty. This makes repository status check for large repositories
|
||||
# much, much faster.
|
||||
# DISABLE_UNTRACKED_FILES_DIRTY="true"
|
||||
|
||||
# Uncomment the following line if you want to change the command execution time
|
||||
# stamp shown in the history command output.
|
||||
# The optional three formats: "mm/dd/yyyy"|"dd.mm.yyyy"|"yyyy-mm-dd"
|
||||
# HIST_STAMPS="mm/dd/yyyy"
|
||||
|
||||
# Would you like to use another custom folder than $ZSH/custom?
|
||||
# ZSH_CUSTOM=/path/to/new-custom-folder
|
||||
|
||||
# Which plugins would you like to load? (plugins can be found in ~/.oh-my-zsh/plugins/*)
|
||||
# Custom plugins may be added to ~/.oh-my-zsh/custom/plugins/
|
||||
# Example format: plugins=(rails git textmate ruby lighthouse)
|
||||
# Add wisely, as too many plugins slow down shell startup.
|
||||
plugins=(git symfony2)
|
||||
|
||||
# User configuration
|
||||
|
||||
source $ZSH/oh-my-zsh.sh
|
||||
|
||||
alias ll='ls -alFh'
|
5
docker/builder/root/bootstrap/entrypoint.d/ohmyzsh.sh
Normal file
5
docker/builder/root/bootstrap/entrypoint.d/ohmyzsh.sh
Normal file
@@ -0,0 +1,5 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [ ! -d "$HOME/.oh-my-zsh" ]; then
|
||||
cp -r "/bootstrap/.oh-my-zsh" "$HOME/.oh-my-zsh"
|
||||
fi
|
7
docker/builder/root/bootstrap/entrypoint.d/zshrc.sh
Normal file
7
docker/builder/root/bootstrap/entrypoint.d/zshrc.sh
Normal file
@@ -0,0 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
ZSH_FILE="$HOME/.zshrc"
|
||||
|
||||
if [ ! -f "$HOME/.zshrc" ]; then
|
||||
cp "/bootstrap/.zshrc" "$HOME/.zshrc"
|
||||
fi
|
17
docker/builder/root/bootstrap/entrypoint.sh
Executable file
17
docker/builder/root/bootstrap/entrypoint.sh
Executable file
@@ -0,0 +1,17 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [ -d /bootstrap/entrypoint.d ]; then
|
||||
for i in /bootstrap/entrypoint.d/*.sh; do
|
||||
if [ -r $i ]; then
|
||||
. $i
|
||||
fi
|
||||
done
|
||||
unset i
|
||||
fi
|
||||
|
||||
if [ ! -t 1 ] ; then
|
||||
echo "No tty available."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
exec "$@"
|
154
docker/caching/composer.json
Normal file
154
docker/caching/composer.json
Normal file
@@ -0,0 +1,154 @@
|
||||
{
|
||||
"name": "phraseanet/phraseanet",
|
||||
"description": "Phraseanet",
|
||||
"license": "GPL-3.0",
|
||||
"config": {
|
||||
"bin-dir": "bin/"
|
||||
},
|
||||
"repositories": [
|
||||
{
|
||||
"type": "package",
|
||||
"package": {
|
||||
"name": "facebook/php-sdk",
|
||||
"version": "3.2.3",
|
||||
"source": {
|
||||
"url": "https://github.com/facebookarchive/facebook-php-sdk.git",
|
||||
"type": "git",
|
||||
"reference": "3.2.3"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "package",
|
||||
"package": {
|
||||
"name": "exiftool/exiftool",
|
||||
"version": "11",
|
||||
"source": {
|
||||
"url": "https://github.com/exiftool/exiftool.git",
|
||||
"type": "git",
|
||||
"reference": "11.84"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "vcs",
|
||||
"url": "https://github.com/alchemy-fr/tcpdf-clone"
|
||||
},
|
||||
{
|
||||
"type": "git",
|
||||
"url": "https://github.com/romainneutron/ProcessManager.git"
|
||||
},
|
||||
{
|
||||
"type": "vcs",
|
||||
"url": "https://github.com/alchemy-fr/imagine"
|
||||
},
|
||||
{
|
||||
"type": "vcs",
|
||||
"url": "https://github.com/alchemy-fr/JMSTranslationBundle"
|
||||
},
|
||||
{
|
||||
"type": "vcs",
|
||||
"url": "https://github.com/alchemy-fr/embed-bundle.git"
|
||||
},
|
||||
{
|
||||
"type": "git",
|
||||
"url": "https://github.com/alchemy-fr/fractal.git"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=5.5.9",
|
||||
"ext-intl": "*",
|
||||
"alchemy-fr/tcpdf-clone": "~6.0",
|
||||
"alchemy/embed-bundle": "^2.0.7",
|
||||
"alchemy/geonames-api-consumer": "~0.1.0",
|
||||
"alchemy/mediavorus": "^0.4.4",
|
||||
"alchemy/oauth2php": "1.1.0",
|
||||
"alchemy/phlickr": "0.2.9",
|
||||
"alchemy/phpexiftool": "^0.7.0",
|
||||
"alchemy/rest-bundle": "^0.0.5",
|
||||
"alchemy/symfony-cors": "^0.1.0",
|
||||
"alchemy/task-manager": "2.0.x-dev@dev",
|
||||
"alchemy/zippy": "^0.3.0",
|
||||
"beberlei/assert": "^2.3",
|
||||
"cocur/slugify": "^2.0",
|
||||
"dailymotion/sdk": "~1.5",
|
||||
"data-uri/data-uri": "~0.1.0",
|
||||
"dflydev/doctrine-orm-service-provider": "~1.0",
|
||||
"doctrine/cache": "1.6.x-dev",
|
||||
"doctrine/dbal": "^2.4.0",
|
||||
"doctrine/migrations": "^1.0.0",
|
||||
"doctrine/orm": "^2.4.0",
|
||||
"elasticsearch/elasticsearch": "~2.0",
|
||||
"firebase/php-jwt": "^3.0.0",
|
||||
"gedmo/doctrine-extensions": "~2.3.0",
|
||||
"goodby/csv": "^1.3.0",
|
||||
"google/apiclient": "^2.0",
|
||||
"guzzle/guzzle": "~3.0",
|
||||
"hoa/compiler": "~2.0",
|
||||
"hoa/console": "~2.0",
|
||||
"hoa/dispatcher": "~0.0",
|
||||
"hoa/router": "~2.0",
|
||||
"igorw/get-in": "~1.0",
|
||||
"imagine/imagine": "0.6.x-dev",
|
||||
"jms/serializer": "~0.10",
|
||||
"jms/translation-bundle": "dev-rebase-2015-10-20",
|
||||
"justinrainbow/json-schema": "2.0.3 as 1.6.1",
|
||||
"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.5",
|
||||
"monolog/monolog": "~1.3",
|
||||
"mrclay/minify": "~2.1.6",
|
||||
"neutron/process-manager": "2.0.x-dev@dev",
|
||||
"neutron/recaptcha": "~0.1.0",
|
||||
"neutron/silex-filesystem-provider": "~1.0",
|
||||
"neutron/silex-imagine-provider": "~0.1.0",
|
||||
"neutron/temporary-filesystem": "~2.1",
|
||||
"pagerfanta/pagerfanta": "^1.0",
|
||||
"php-ffmpeg/php-ffmpeg": "~0.5.0",
|
||||
"php-xpdf/php-xpdf": "~0.2.1",
|
||||
"exiftool/exiftool": "^11",
|
||||
"ramsey/uuid": "^3.0",
|
||||
"roave/security-advisories": "dev-master",
|
||||
"silex/silex": "^1.3.0",
|
||||
"silex/web-profiler": "~1.0",
|
||||
"simple-bus/doctrine-orm-bridge": "^4.0",
|
||||
"simple-bus/jms-serializer-bridge": "^1.0",
|
||||
"simple-bus/message-bus": "^2.1",
|
||||
"simple-bus/serialization": "^2.0",
|
||||
"sorien/silex-dbal-profiler": "^1.1",
|
||||
"sorien/silex-pimple-dumper": "^1.0",
|
||||
"swiftmailer/swiftmailer": "~5.4.5",
|
||||
"symfony/symfony": "~2.7.10|~2.8.3",
|
||||
"themattharris/tmhoauth": "~0.7",
|
||||
"twig/extensions": "^1.2.0",
|
||||
"twig/twig": "~1.14, >=1.14.2",
|
||||
"vierbergenlars/php-semver": "~2.1",
|
||||
"webmozart/json": "^1.1",
|
||||
"willdurand/negotiation": "^2.0.0-alpha1",
|
||||
"zend/gdata": "~1.12.1",
|
||||
"alchemy/worker-bundle": "^0.1.6",
|
||||
"alchemy/queue-bundle": "^0.1.5",
|
||||
"google/recaptcha": "^1.1",
|
||||
"facebook/graph-sdk": "^5.6",
|
||||
"box/spout": "^2.7",
|
||||
"paragonie/random-lib": "^2.0",
|
||||
"czproject/git-php": "^3.17"
|
||||
},
|
||||
"require-dev": {
|
||||
"mikey179/vfsstream": "~1.5",
|
||||
"phpunit/phpunit": "^4.8|^5.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-0": {
|
||||
"Alchemy\\": "lib",
|
||||
"": "lib/classes"
|
||||
}
|
||||
},
|
||||
"include-path": ["vendor/zend/gdata/library"],
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "4.1.x-dev"
|
||||
}
|
||||
}
|
||||
}
|
8839
docker/caching/composer.lock
generated
Normal file
8839
docker/caching/composer.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
12
docker/db/Dockerfile
Normal file
12
docker/db/Dockerfile
Normal file
@@ -0,0 +1,12 @@
|
||||
FROM mariadb:10.4.5
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y \
|
||||
gettext \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists/
|
||||
|
||||
ADD ./docker/ /
|
||||
|
||||
ENTRYPOINT ["/phraseanet-entrypoint.sh"]
|
||||
CMD ["mysqld","--sql_mode="]
|
3
docker/db/docker/custom.cnf
Normal file
3
docker/db/docker/custom.cnf
Normal file
@@ -0,0 +1,3 @@
|
||||
[mysqld]
|
||||
|
||||
max_allowed_packet=$MYSQL_MAX_ALLOWED_PACKET
|
@@ -0,0 +1,5 @@
|
||||
CREATE DATABASE ab_master CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
CREATE DATABASE db_databox1 CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
CREATE DATABASE db_unitTest CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
CREATE DATABASE db_dataset1 CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
CREATE DATABASE db_dataset2 CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
7
docker/db/docker/phraseanet-entrypoint.sh
Executable file
7
docker/db/docker/phraseanet-entrypoint.sh
Executable file
@@ -0,0 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -eo pipefail
|
||||
shopt -s nullglob
|
||||
|
||||
envsubst < /custom.cnf > /etc/mysql/conf.d/custom.cnf
|
||||
docker-entrypoint.sh "$@"
|
3
docker/elasticsearch/Dockerfile
Normal file
3
docker/elasticsearch/Dockerfile
Normal file
@@ -0,0 +1,3 @@
|
||||
FROM elasticsearch:2.4
|
||||
|
||||
RUN /usr/share/elasticsearch/bin/plugin install analysis-icu
|
@@ -1,4 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
cat nginx.conf.sample | sed "s/\$MAX_BODY_SIZE/$MAX_BODY_SIZE/g" > /etc/nginx/nginx.conf
|
||||
nginx -g "daemon off;"
|
@@ -1,85 +0,0 @@
|
||||
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;
|
||||
client_max_body_size $MAX_BODY_SIZE;
|
||||
|
||||
location /api {
|
||||
rewrite ^(.*)$ /api.php/$1 last;
|
||||
}
|
||||
|
||||
location / {
|
||||
# First attempt to serve request as file, then
|
||||
# as directory, then fall back to index.html
|
||||
try_files $uri $uri/ @rewriteapp;
|
||||
}
|
||||
|
||||
location @rewriteapp {
|
||||
rewrite ^(.*)$ /index.php/$1 last;
|
||||
}
|
||||
|
||||
# PHP scripts -> PHP-FPM server listening on 127.0.0.1:9000
|
||||
location ~ ^/(index|index_dev|api)\.php(/|$) {
|
||||
fastcgi_pass backend;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
include fastcgi_params;
|
||||
}
|
||||
|
||||
location ~ ^/(status|ping)$ {
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
fastcgi_index index.php;
|
||||
include fastcgi_params;
|
||||
fastcgi_pass backend;
|
||||
}
|
||||
}
|
||||
}
|
7
docker/nginx/root/entrypoint.sh
Executable file
7
docker/nginx/root/entrypoint.sh
Executable file
@@ -0,0 +1,7 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -xe
|
||||
|
||||
cat /nginx.conf.sample | sed "s/\$MAX_BODY_SIZE/$MAX_BODY_SIZE/g" > /etc/nginx/conf.d/default.conf
|
||||
|
||||
exec "$@"
|
31
docker/nginx/root/etc/nginx/nginx.conf
Executable file
31
docker/nginx/root/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;
|
||||
|
||||
}
|
39
docker/nginx/root/nginx.conf.sample
Normal file
39
docker/nginx/root/nginx.conf.sample
Normal file
@@ -0,0 +1,39 @@
|
||||
upstream backend {
|
||||
server phraseanet:9000;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
root /var/alchemy/Phraseanet/www;
|
||||
|
||||
index index.php;
|
||||
client_max_body_size $MAX_BODY_SIZE;
|
||||
|
||||
location /api {
|
||||
rewrite ^(.*)$ /api.php/$1 last;
|
||||
}
|
||||
|
||||
location / {
|
||||
# First attempt to serve request as file, then
|
||||
# as directory, then fall back to index.html
|
||||
try_files $uri $uri/ @rewriteapp;
|
||||
}
|
||||
|
||||
location @rewriteapp {
|
||||
rewrite ^(.*)$ /index.php/$1 last;
|
||||
}
|
||||
|
||||
# PHP scripts -> PHP-FPM server listening on 127.0.0.1:9000
|
||||
location ~ ^/(index|index_dev|api)\.php(/|$) {
|
||||
fastcgi_pass backend;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
include fastcgi_params;
|
||||
}
|
||||
|
||||
location ~ ^/(status|ping)$ {
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
fastcgi_index index.php;
|
||||
include fastcgi_params;
|
||||
fastcgi_pass backend;
|
||||
}
|
||||
}
|
@@ -1,9 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
if [ ! -z ${DOCKER_XDEBUG_ENABLED} ]; then
|
||||
. usr-bin/docker-xdebug-enable
|
||||
fi
|
||||
|
||||
exec "$@"
|
@@ -1,4 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
/sbin/ip route|awk '/default/ { print $3 }'
|
||||
# TODO support MacOS & Windows host IP
|
@@ -1,5 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
unset HOST_IP
|
||||
unset XDEBUG_CONFIG
|
||||
unset XDEBUG_REMOTE_HOST
|
@@ -1,9 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
HOST_IP=$(docker-get-host-ip);
|
||||
|
||||
export HOST_IP
|
||||
export XDEBUG_CONFIG="remote_enable=1 remote_host=${HOST_IP} idekey=PHPSTORM";
|
||||
export XDEBUG_REMOTE_HOST="${HOST_IP}";
|
@@ -2,6 +2,16 @@
|
||||
|
||||
set -xe
|
||||
|
||||
if [ -z "$INSTALL_ACCOUNT_EMAIL" ]; then
|
||||
echo "INSTALL_ACCOUNT_EMAIL var is not set."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$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 \
|
||||
@@ -17,28 +27,33 @@ set -xe
|
||||
|
||||
/var/alchemy/Phraseanet/bin/setup system:config set main.search-engine.options.host elasticsearch
|
||||
/var/alchemy/Phraseanet/bin/setup system:config set main.search-engine.options.minScore 2
|
||||
/var/alchemy/Phraseanet/bin/setup system:config set main.search-engine.options.base_aggregate_limit 10
|
||||
/var/alchemy/Phraseanet/bin/setup system:config set main.search-engine.options.collection_aggregate_limit 10
|
||||
/var/alchemy/Phraseanet/bin/setup system:config set main.search-engine.options.doctype_aggregate_limit 10
|
||||
/var/alchemy/Phraseanet/bin/setup system:config set main.search-engine.options.minScore 2
|
||||
/var/alchemy/Phraseanet/bin/setup system:config set main.search-engine.options.facets._base.limit 10
|
||||
/var/alchemy/Phraseanet/bin/setup system:config set main.search-engine.options.facets._collection.limit 10
|
||||
/var/alchemy/Phraseanet/bin/setup system:config set main.search-engine.options.facets._doctype.limit 10
|
||||
|
||||
## Redis
|
||||
/var/alchemy/Phraseanet/bin/setup system:config set main.cache.options.host redis
|
||||
/var/alchemy/Phraseanet/bin/setup system:config set main.cache.options.port 6379
|
||||
/var/alchemy/Phraseanet/bin/setup system:config set main.cache.options.domain $INSTALL_SERVER_NAME
|
||||
/var/alchemy/Phraseanet/bin/setup system:config set main.cache.options.namespace $INSTALL_SERVER_NAME
|
||||
/var/alchemy/Phraseanet/bin/setup system:config set main.cache.type redis
|
||||
|
||||
# RabbitMQ
|
||||
|
||||
bin/setup system:config set rabbitmq.server.host rabbitmq
|
||||
bin/setup system:config set rabbitmq.server.port 5672
|
||||
bin/setup system:config set rabbitmq.server.user $INSTALL_RABBITMQ_USER
|
||||
bin/setup system:config set rabbitmq.server.password $INSTALL_RABBITMQ_PASSWORD
|
||||
bin/setup system:config set rabbitmq.server.vhost /
|
||||
|
||||
# Bus configuration for scheduler & worker
|
||||
bin/setup system:config set workers.queue.worker-queue.registry alchemy_worker.queue_registry
|
||||
bin/setup system:config set workers.queue.worker-queue.host rabbitmq
|
||||
bin/setup system:config set workers.queue.worker-queue.port 5672
|
||||
bin/setup system:config set workers.queue.worker-queue.user $INSTALL_RABBITMQ_USER
|
||||
bin/setup system:config set workers.queue.worker-queue.password $INSTALL_RABBITMQ_PASSWORD
|
||||
bin/setup system:config set workers.queue.worker-queue.vhost /
|
||||
|
||||
# Create elasticsearch index
|
||||
/var/alchemy/Phraseanet/bin/console searchengine:index -c
|
||||
|
||||
## enable API and disable ssl on it
|
||||
/var/alchemy/Phraseanet/bin/setup system:config set registry.api-clients.api-enabled true
|
||||
/var/alchemy/Phraseanet/bin/setup system:config set main.api_require_ssl false
|
||||
|
||||
# set instance title
|
||||
bin/setup system:config set registry.general.title $PHRASEANET_PROJECT_NAME
|
||||
|
||||
/var/alchemy/Phraseanet/bin/console compile:configuration
|
||||
|
@@ -1,11 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
FILE=/var/alchemy/Phraseanet/config/configuration.yml
|
||||
if [ -f "$FILE" ]; then
|
||||
echo "$FILE exist, skip setup."
|
||||
else
|
||||
echo "$FILE doesn't exist, entering setup..."
|
||||
runuser app -c '/auto-install.sh'
|
||||
fi
|
||||
|
||||
php-fpm
|
35
docker/phraseanet/entrypoint.sh
Executable file
35
docker/phraseanet/entrypoint.sh
Executable file
@@ -0,0 +1,35 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
envsubst < "docker/phraseanet/php.ini.sample" > /usr/local/etc/php/php.ini
|
||||
envsubst < "docker/phraseanet/php-fpm.conf.sample" > /usr/local/etc/php-fpm.conf
|
||||
envsubst < "docker/phraseanet/root/usr/local/etc/php-fpm.d/zz-docker.conf" > /usr/local/etc/php-fpm.d/zz-docker.conf
|
||||
|
||||
chown -R app:app \
|
||||
cache \
|
||||
config \
|
||||
datas \
|
||||
tmp \
|
||||
logs \
|
||||
www/thumbnails \
|
||||
www/custom
|
||||
|
||||
FILE=config/configuration.yml
|
||||
|
||||
if [ -f "$FILE" ]; then
|
||||
bin/setup system:config set registry.general.title $PHRASEANET_PROJECT_NAME
|
||||
echo "$FILE exists, skip setup."
|
||||
else
|
||||
echo "$FILE doesn't exist, entering setup..."
|
||||
runuser app -c docker/phraseanet/auto-install.sh
|
||||
fi
|
||||
|
||||
if [ ${XDEBUG_ENABLED} == "1" ]; then
|
||||
echo "XDEBUG is enabled. YOU MAY KEEP THIS FEATURE DISABLED IN PRODUCTION."
|
||||
docker-php-ext-enable xdebug
|
||||
fi
|
||||
|
||||
./docker/phraseanet/plugins/console init
|
||||
|
||||
bash -e docker-php-entrypoint $@
|
@@ -935,7 +935,7 @@ cli_server.color = On
|
||||
[Date]
|
||||
; Defines the default timezone used by the date functions
|
||||
; http://php.net/date.timezone
|
||||
date.timezone = Europe/Paris
|
||||
date.timezone = UTC
|
||||
|
||||
; http://php.net/date.default-latitude
|
||||
;date.default_latitude = 31.7667
|
||||
|
@@ -1,8 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
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 $@
|
29
docker/phraseanet/plugins/InitCommand.php
Normal file
29
docker/phraseanet/plugins/InitCommand.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace Alchemy\Docker\Plugins\Command;
|
||||
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class InitCommand extends Command
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName('init')
|
||||
->setDescription('Initialize plugins');
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
foreach (glob('./plugins/*') as $dir) {
|
||||
if (is_dir($dir)) {
|
||||
$output->writeln(sprintf('Init <info>%s</info> plugin', basename($dir)));
|
||||
SubCommand::run(sprintf('bin/setup plugin:add %s', $dir));
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
64
docker/phraseanet/plugins/InstallCommand.php
Normal file
64
docker/phraseanet/plugins/InstallCommand.php
Normal file
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
namespace Alchemy\Docker\Plugins\Command;
|
||||
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class InstallCommand extends Command
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName('install')
|
||||
->setDescription('Install plugins');
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$plugins = trim(getenv('PHRASEANET_PLUGINS'));
|
||||
if (empty($plugins)) {
|
||||
$output->writeln('<comment>No plugin to install... SKIP</comment>');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
$pluginsDir = 'plugins';
|
||||
if (!is_dir($pluginsDir)) {
|
||||
mkdir($pluginsDir);
|
||||
}
|
||||
|
||||
foreach (explode(' ', $plugins) as $key => $plugin) {
|
||||
$plugin = trim($plugin);
|
||||
$repo = $plugin;
|
||||
$branch = 'master';
|
||||
if (1 === preg_match('#^(.+)\(([^)]+)\)$#', $plugin, $matches)) {
|
||||
$repo = $matches[1];
|
||||
$branch = $matches[2];
|
||||
}
|
||||
|
||||
$pluginPath = './plugin' . $key;
|
||||
if (is_dir($pluginPath)) {
|
||||
SubCommand::run(sprintf('rm -rf %s', $pluginPath));
|
||||
}
|
||||
|
||||
$output->writeln(sprintf('Installing <info>%s</info> (branch: <info>%s</info>)', $repo, $branch));
|
||||
SubCommand::run(sprintf('git clone --single-branch --branch %s %s %s', $branch, $repo, $pluginPath));
|
||||
|
||||
$manifestSrc = $pluginPath.'/manifest.json';
|
||||
if (!file_exists($manifestSrc)) {
|
||||
throw new \Exception(sprintf('Cannot install plugin %s: no manifest.json file found', $plugin));
|
||||
}
|
||||
$pluginDestName = json_decode(file_get_contents($manifestSrc), true)['name'];
|
||||
rename($pluginPath, $pluginsDir.'/'.$pluginDestName);
|
||||
$pluginPath = $pluginsDir.'/'.$pluginDestName;
|
||||
|
||||
if (file_exists($pluginPath.'/composer.json')) {
|
||||
SubCommand::run(sprintf('cd %s && composer install --no-dev', $pluginPath));
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
26
docker/phraseanet/plugins/SubCommand.php
Normal file
26
docker/phraseanet/plugins/SubCommand.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace Alchemy\Docker\Plugins\Command;
|
||||
|
||||
function setupStreaming()
|
||||
{
|
||||
ini_set('output_buffering', 'off');
|
||||
ini_set('zlib.output_compression', false);
|
||||
if (function_exists('apache_setenv')) {
|
||||
apache_setenv('no-gzip', '1');
|
||||
apache_setenv('dont-vary', '1');
|
||||
}
|
||||
}
|
||||
|
||||
setupStreaming();
|
||||
|
||||
abstract class SubCommand
|
||||
{
|
||||
static public function run($cmd)
|
||||
{
|
||||
system($cmd, $return);
|
||||
if (0 !== $return) {
|
||||
throw new \Exception(sprintf('Error %d: %s', $return, $cmd));
|
||||
}
|
||||
}
|
||||
}
|
17
docker/phraseanet/plugins/console
Executable file
17
docker/phraseanet/plugins/console
Executable file
@@ -0,0 +1,17 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
namespace Alchemy\Docker\Plugins\Command;
|
||||
|
||||
require __DIR__.'/../../../vendor/autoload.php';
|
||||
require __DIR__.'/SubCommand.php';
|
||||
require __DIR__.'/InstallCommand.php';
|
||||
require __DIR__.'/InitCommand.php';
|
||||
|
||||
use Symfony\Component\Console\Application;
|
||||
|
||||
$application = new Application();
|
||||
|
||||
$application->add(new InstallCommand());
|
||||
$application->add(new InitCommand());
|
||||
|
||||
$application->run();
|
@@ -1,6 +1,5 @@
|
||||
[global]
|
||||
daemonize = no
|
||||
error_log = /var/log/fpm-php.www.log
|
||||
process.max = 128
|
||||
|
||||
[www]
|
@@ -1,3 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
runuser app -c 'php /var/alchemy/Phraseanet/bin/console task-manager:scheduler:run'
|
18
docker/phraseanet/worker/entrypoint.sh
Executable file
18
docker/phraseanet/worker/entrypoint.sh
Executable file
@@ -0,0 +1,18 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
DOCKER_DIR="./docker/phraseanet"
|
||||
|
||||
mkdir -p "${APP_DIR}/tmp/locks" \
|
||||
&& chown -R app:app "${APP_DIR}/tmp"
|
||||
|
||||
envsubst < "${DOCKER_DIR}/php.ini.sample" > /usr/local/etc/php/php.ini
|
||||
envsubst < "${DOCKER_DIR}/php-fpm.conf.sample" > /usr/local/etc/php-fpm.conf
|
||||
|
||||
if [ ${XDEBUG_ENABLED} == "1" ]; then
|
||||
echo "XDEBUG is enabled. YOU MAY KEEP THIS FEATURE DISABLED IN PRODUCTION."
|
||||
docker-php-ext-enable xdebug
|
||||
fi
|
||||
|
||||
runuser -u app -- $@
|
@@ -128,7 +128,7 @@ key:
|
||||
| quoted_string()
|
||||
|
||||
group:
|
||||
::space::? ::parenthese_:: primary() ::_parenthese:: ::space::?
|
||||
::space::? ::parenthese_:: ::space::? primary() ::space::? ::_parenthese:: ::space::?
|
||||
|
||||
|
||||
// Thesaurus terms
|
||||
|
@@ -167,11 +167,11 @@ class AccountService
|
||||
* @param string $login
|
||||
* @throws AccountException
|
||||
*/
|
||||
public function deleteAccount($login = null)
|
||||
public function deleteAccount($login = null, array $grantedBaseIdList = array())
|
||||
{
|
||||
$user = $this->getUserOrCurrentUser($login);
|
||||
|
||||
$this->userManipulator->delete($user);
|
||||
$this->userManipulator->delete($user, $grantedBaseIdList);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -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());
|
||||
|
@@ -328,11 +328,9 @@ class RegistrationService
|
||||
|
||||
|
||||
$autoReg = $acl->get_granted_base();
|
||||
$granted = [];
|
||||
foreach ($autoReg as $baseId => $collection) {
|
||||
$granted[$baseId] = $collection->get_label($this->app['locale']);
|
||||
}
|
||||
if(count($granted) > 0) {
|
||||
|
||||
$this->app['manipulator.webhook-event']->create(
|
||||
WebhookEvent::USER_REGISTRATION_GRANTED,
|
||||
WebhookEvent::USER_REGISTRATION_TYPE,
|
||||
@@ -340,8 +338,11 @@ class RegistrationService
|
||||
'user_id' => $user->getId(),
|
||||
'granted' => $granted,
|
||||
'rejected' => []
|
||||
]
|
||||
],
|
||||
[$baseId]
|
||||
);
|
||||
|
||||
unset($granted);
|
||||
}
|
||||
|
||||
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -14,6 +14,8 @@ namespace Alchemy\Phrasea\Border;
|
||||
use Alchemy\Phrasea\Application;
|
||||
use Alchemy\Phrasea\Border\Checker\CheckerInterface;
|
||||
use Alchemy\Phrasea\Border\Attribute\AttributeInterface;
|
||||
use Alchemy\Phrasea\Core\Event\Record\RecordEvents;
|
||||
use Alchemy\Phrasea\Core\Event\Record\SubdefinitionCreateEvent;
|
||||
use Alchemy\Phrasea\Exception\RuntimeException;
|
||||
use Alchemy\Phrasea\Metadata\Tag\TfArchivedate;
|
||||
use Alchemy\Phrasea\Metadata\Tag\TfQuarantine;
|
||||
@@ -333,7 +335,7 @@ class Manager
|
||||
$this->app['phraseanet.metadata-setter']->replaceMetadata($newMetadata, $element);
|
||||
|
||||
if(!$nosubdef) {
|
||||
$element->rebuild_subdefs();
|
||||
$this->app['dispatcher']->dispatch(RecordEvents::SUBDEFINITION_CREATE, new SubdefinitionCreateEvent($element, true));
|
||||
}
|
||||
|
||||
return $element;
|
||||
|
@@ -73,7 +73,7 @@ class RedisCache extends CacheProvider implements Cache
|
||||
*/
|
||||
protected function doDelete($id)
|
||||
{
|
||||
return $this->_redis->delete($id);
|
||||
return $this->_redis->del($id);
|
||||
}
|
||||
|
||||
/**
|
||||
|
89
lib/Alchemy/Phrasea/Command/ApplyRightsCommand.php
Normal file
89
lib/Alchemy/Phrasea/Command/ApplyRightsCommand.php
Normal file
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of Phraseanet
|
||||
*
|
||||
* (c) 2005-2020 Alchemy
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Alchemy\Phrasea\Command;
|
||||
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Alchemy\Phrasea\Model\Entities\User;
|
||||
|
||||
class ApplyRightsCommand extends Command
|
||||
{
|
||||
public function __construct($name = null)
|
||||
{
|
||||
parent::__construct($name);
|
||||
|
||||
$this->setDescription('Apply right on databox, inject appbox:basusr to dboxes:collusr')
|
||||
->addOption('user_id', null, InputOption::VALUE_REQUIRED, 'the user ID to apply rights')
|
||||
;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function doExecute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$userId = $input->getOption('user_id');
|
||||
$userRepository = $this->container['repo.users'];
|
||||
|
||||
if ($userId) {
|
||||
if (($user = $userRepository->find($userId)) === null) {
|
||||
$output->writeln('user not found!');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
$this->injectRightsSbas($user);
|
||||
} else {
|
||||
foreach ($userRepository->findAll() as $user) {
|
||||
$this->injectRightsSbas($user);
|
||||
}
|
||||
}
|
||||
|
||||
$output->writeln('Apply right on databox finished!');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private function injectRightsSbas(User $user)
|
||||
{
|
||||
$userAcl = $this->container->getAclForUser($user);
|
||||
|
||||
foreach ($userAcl->get_granted_sbas() as $databox) {
|
||||
|
||||
$userAcl->delete_injected_rights_sbas($databox);
|
||||
|
||||
$sql = "INSERT INTO collusr
|
||||
(site, usr_id, coll_id, mask_and, mask_xor, ord)
|
||||
VALUES (:site_id, :usr_id, :coll_id, :mask_and, :mask_xor, :ord)";
|
||||
$stmt = $databox->get_connection()->prepare($sql);
|
||||
$iord = 0;
|
||||
|
||||
// fix collusr if user has right on collection
|
||||
foreach ($userAcl->get_granted_base([], [$databox->get_sbas_id()]) as $collection) {
|
||||
try {
|
||||
$stmt->execute([
|
||||
':site_id' => $this->container['conf']->get(['main', 'key']),
|
||||
':usr_id' => $user->getId(),
|
||||
':coll_id' => $collection->get_coll_id(),
|
||||
':mask_and' => $userAcl->get_mask_and($collection->get_base_id()),
|
||||
':mask_xor' => $userAcl->get_mask_xor($collection->get_base_id()),
|
||||
':ord' => $iord++
|
||||
]);
|
||||
} catch (DBALException $e) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
$stmt->closeCursor();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,86 @@
|
||||
<?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.
|
||||
*/
|
||||
namespace Alchemy\Phrasea\Command\Collection;
|
||||
|
||||
use Alchemy\Phrasea\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class ListCollectionCommand extends Command
|
||||
{
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct($name = null)
|
||||
{
|
||||
parent::__construct('collection:list');
|
||||
$this->setDescription('List all collection in Phraseanet')
|
||||
->addOption('databox_id', 'd', InputOption::VALUE_REQUIRED, 'The id of the databox to list collection')
|
||||
->addOption('jsonformat', null, InputOption::VALUE_NONE, 'Output in json format')
|
||||
->setHelp('');
|
||||
return $this;
|
||||
}
|
||||
protected function doExecute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
try {
|
||||
$jsonformat = $input->getOption('jsonformat');
|
||||
$databox = $this->container->findDataboxById($input->getOption('databox_id'));
|
||||
$collections = $this->listDataboxCollections($databox);
|
||||
|
||||
if ($jsonformat) {
|
||||
foreach ($collections as $collection) {
|
||||
$collectionList[] = array_combine(['id local for API', 'id distant', 'name','label','status','total records'], $collection);
|
||||
}
|
||||
echo json_encode($collectionList);
|
||||
} else {
|
||||
$table = $this->getHelperSet()->get('table');
|
||||
$table
|
||||
->setHeaders(['id local for API', 'id distant', 'name','label','status','total records'])
|
||||
->setRows($collections)
|
||||
->render($output);
|
||||
}
|
||||
|
||||
} catch (\Exception $e) {
|
||||
$output->writeln("<error>{$e->getMessage()}</error>");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private function listDataboxCollections(\databox $databox)
|
||||
{
|
||||
return array_map(function (\collection $collection) {
|
||||
return $this->listCollection($collection);
|
||||
}, array_merge($databox->get_collections(),$this->getUnabledCollection($databox->get_activable_colls())));
|
||||
}
|
||||
|
||||
private function getUnabledCollection($collections)
|
||||
{
|
||||
return array_map(function ($colId){
|
||||
return \collection::getByBaseId($this->container, $colId);
|
||||
},$collections);
|
||||
|
||||
}
|
||||
|
||||
private function listCollection(\collection $collection)
|
||||
{
|
||||
return [
|
||||
$collection->get_base_id(),
|
||||
$collection->get_coll_id(),
|
||||
$collection->get_name(),
|
||||
'en: ' . $collection->get_label('en') .
|
||||
', de: ' . $collection->get_label('de') .
|
||||
', fr: ' . $collection->get_label('fr') .
|
||||
', nl: ' . $collection->get_label('nl'),
|
||||
($collection->is_active()) ? 'enabled' : 'disabled',
|
||||
$collection->get_record_amount()
|
||||
];
|
||||
}
|
||||
}
|
@@ -0,0 +1,61 @@
|
||||
<?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.
|
||||
*/
|
||||
|
||||
namespace Alchemy\Phrasea\Command\Collection;
|
||||
|
||||
use Alchemy\Phrasea\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
|
||||
class PublishCollectionCommand extends Command
|
||||
{
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct($name = null)
|
||||
{
|
||||
parent::__construct('collection:publish');
|
||||
|
||||
$this->setDescription('Publish collection in Phraseanet')
|
||||
->addOption('collection_id', null, InputOption::VALUE_REQUIRED, 'The base_id of the collection to publish but keep with existing right into present in application box.')
|
||||
->setHelp('');
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function doExecute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
try {
|
||||
|
||||
$collection = \collection::getByBaseId($this->container,(int)$input->getOption('collection_id'));
|
||||
$dialog = $this->getHelperSet()->get('dialog');
|
||||
|
||||
do {
|
||||
$continue = mb_strtolower($dialog->ask($output, '<question> Do you want really publish this collection? (y/N)</question>', 'N'));
|
||||
} while ( ! in_array($continue, ['y', 'n']));
|
||||
|
||||
if ($continue !== 'y') {
|
||||
$output->writeln('Aborting !');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$collection->enable($this->container->getApplicationBox());
|
||||
$output->writeln('<info>Publish collection successful</info>');
|
||||
} catch (\Exception $e) {
|
||||
$output->writeln('<error>Publish collection failed : '.$e->getMessage().'</error>');
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,61 @@
|
||||
<?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.
|
||||
*/
|
||||
|
||||
namespace Alchemy\Phrasea\Command\Collection;
|
||||
|
||||
use Alchemy\Phrasea\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
|
||||
class UnPublishCollectionCommand extends Command
|
||||
{
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct($name = null)
|
||||
{
|
||||
parent::__construct('collection:unpublish');
|
||||
|
||||
$this->setDescription('Unpublish collection in Phraseanet')
|
||||
->addOption('collection_id', null, InputOption::VALUE_REQUIRED, 'The base_id of the collection to unpublish, the base_id is the same id used in API.')
|
||||
->setHelp('');
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function doExecute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
try {
|
||||
|
||||
$collection = \collection::getByBaseId($this->container,(int)$input->getOption('collection_id'));
|
||||
$dialog = $this->getHelperSet()->get('dialog');
|
||||
|
||||
do {
|
||||
$continue = mb_strtolower($dialog->ask($output, sprintf("<question> Do you want really unpublish the collection %s? (y/N)</question>", $collection->get_name()), 'N'));
|
||||
} while ( ! in_array($continue, ['y', 'n']));
|
||||
|
||||
if ($continue !== 'y') {
|
||||
$output->writeln('<info>Aborting !</>');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$collection->disable($this->container->getApplicationBox());
|
||||
$output->writeln('<info>Unpublish collection successful</info>');
|
||||
} catch (\Exception $e) {
|
||||
$output->writeln('<error>Unpublish collection failed : '.$e->getMessage().'</error>');
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
72
lib/Alchemy/Phrasea/Command/Databox/ListDataboxCommand.php
Normal file
72
lib/Alchemy/Phrasea/Command/Databox/ListDataboxCommand.php
Normal file
@@ -0,0 +1,72 @@
|
||||
<?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.
|
||||
*/
|
||||
|
||||
namespace Alchemy\Phrasea\Command\Databox;
|
||||
|
||||
use Alchemy\Phrasea\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class ListDataboxCommand extends Command
|
||||
{
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct($name = null)
|
||||
{
|
||||
parent::__construct('databox:list');
|
||||
|
||||
$this->setDescription('List all databox in Phraseanet')
|
||||
->addOption('jsonformat', null, InputOption::VALUE_NONE, 'Output in json format')
|
||||
->setHelp('');
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function doExecute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
try {
|
||||
$jsonformat = $input->getOption('jsonformat');
|
||||
$databoxes = array_map(function (\databox $databox) {
|
||||
return $this->listDatabox($databox);
|
||||
}, $this->container->getApplicationBox()->get_databoxes());
|
||||
|
||||
if ($jsonformat) {
|
||||
foreach ($databoxes as $databox) {
|
||||
$databoxList[] = array_combine(['id', 'name', 'alias'], $databox);
|
||||
}
|
||||
echo json_encode($databoxList);
|
||||
} else {
|
||||
$table = $this->getHelperSet()->get('table');
|
||||
$table
|
||||
->setHeaders(['id', 'name', 'alias'])
|
||||
->setRows($databoxes)
|
||||
->render($output);
|
||||
}
|
||||
|
||||
} catch (\Exception $e) {
|
||||
$output->writeln('<error>Listing databox failed : '.$e->getMessage().'</error>');
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private function listDatabox(\databox $databox)
|
||||
{
|
||||
return [
|
||||
$databox->get_sbas_id(),
|
||||
$databox->get_dbname(),
|
||||
$databox->get_viewname()
|
||||
];
|
||||
}
|
||||
|
||||
}
|
@@ -1,7 +1,16 @@
|
||||
<?php
|
||||
/*
|
||||
* This file is part of Phraseanet
|
||||
*
|
||||
* (c) 2005-2020 Alchemy
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Alchemy\Phrasea\Command\Databox;
|
||||
|
||||
|
||||
use Alchemy\Phrasea\Command\Command;
|
||||
use Alchemy\Phrasea\Databox\DataboxConnectionSettings;
|
||||
use Alchemy\Phrasea\Databox\DataboxService;
|
||||
@@ -13,42 +22,84 @@ use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class MountDataboxCommand extends Command
|
||||
{
|
||||
|
||||
protected function configure()
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct($name = null)
|
||||
{
|
||||
$this->setName('databox:mount')
|
||||
->addArgument('databox', InputArgument::REQUIRED, 'Database name for the databox', null)
|
||||
->addArgument('owner', InputArgument::REQUIRED, 'Email of the databox admin user', null)
|
||||
->addOption('connection', 'c', InputOption::VALUE_NONE, 'Flag to set new database settings')
|
||||
->addOption('db-host', null, InputOption::VALUE_OPTIONAL, 'MySQL server host', 'localhost')
|
||||
->addOption('db-port', null, InputOption::VALUE_OPTIONAL, 'MySQL server port', 3306)
|
||||
->addOption('db-user', null, InputOption::VALUE_OPTIONAL, 'MySQL server user', 'phrasea')
|
||||
->addOption('db-password', null, InputOption::VALUE_OPTIONAL, 'MySQL server password', null);
|
||||
parent::__construct('databox:mount');
|
||||
|
||||
$this->setDescription('Mount databox')
|
||||
->addArgument('databox', InputArgument::REQUIRED, 'Database name in Mysql', null)
|
||||
->addArgument('user_id', InputArgument::REQUIRED, 'The Id of user owner (this account became full admin on this databox)', null)
|
||||
->addOption('db-host', null, InputOption::VALUE_OPTIONAL, 'MySQL server host')
|
||||
->addOption('db-port', null, InputOption::VALUE_OPTIONAL, 'MySQL server port')
|
||||
->addOption('db-user', null, InputOption::VALUE_OPTIONAL, 'MySQL server user')
|
||||
->addOption('db-password', null, InputOption::VALUE_OPTIONAL, 'MySQL server password')
|
||||
;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function doExecute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$databoxName = $input->getArgument('databox');
|
||||
$connectionSettings = $input->getOption('connection') == false ? null : new DataboxConnectionSettings(
|
||||
$input->getOption('db-host'),
|
||||
$input->getOption('db-port'),
|
||||
$input->getOption('db-user'),
|
||||
$input->getOption('db-password')
|
||||
);
|
||||
try {
|
||||
|
||||
/** @var UserRepository $userRepository */
|
||||
$userRepository = $this->container['repo.users'];
|
||||
/** @var DataboxService $databoxService */
|
||||
$databoxService = $this->container['databox.service'];
|
||||
/** @var UserRepository $userRepository */
|
||||
$userRepository = $this->container['repo.users'];
|
||||
|
||||
$owner = $userRepository->findByEmail($input->getArgument('owner'));
|
||||
$owner = $userRepository->find($input->getArgument('user_id'));
|
||||
|
||||
$databoxService->mountDatabox(
|
||||
$databoxName,
|
||||
$owner,
|
||||
$connectionSettings
|
||||
);
|
||||
if (empty($owner)) {
|
||||
$output->writeln('<error>User not found ! </error>');
|
||||
|
||||
$output->writeln('Databox mounted');
|
||||
return;
|
||||
}
|
||||
|
||||
if ($owner->isGuest() || !$this->container->getAclForUser($owner)->is_admin()) {
|
||||
$output->writeln('<error>Admin role is required for the owner ! </error>');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$databoxName = $input->getArgument('databox');
|
||||
$dialog = $this->getHelperSet()->get('dialog');
|
||||
|
||||
$connectionSettings = new DataboxConnectionSettings(
|
||||
$input->getOption('db-host')?:$this->container['conf']->get(['main', 'database', 'host']),
|
||||
$input->getOption('db-port')?:$this->container['conf']->get(['main', 'database', 'port']),
|
||||
$input->getOption('db-user')?:$this->container['conf']->get(['main', 'database', 'user']),
|
||||
$input->getOption('db-password')?:$this->container['conf']->get(['main', 'database', 'password'])
|
||||
);
|
||||
|
||||
do {
|
||||
$continue = mb_strtolower($dialog->ask($output, '<question> Do you want really mount this databox? (y/N)</question>', 'N'));
|
||||
}
|
||||
while ( ! in_array($continue, ['y', 'n']));
|
||||
|
||||
if ($continue !== 'y') {
|
||||
$output->writeln('Aborting !');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var DataboxService $databoxService */
|
||||
$databoxService = $this->container['databox.service'];
|
||||
|
||||
\phrasea::clear_sbas_params($this->container);
|
||||
|
||||
$databox = $databoxService->mountDatabox(
|
||||
$databoxName,
|
||||
$owner,
|
||||
$connectionSettings
|
||||
);
|
||||
|
||||
$output->writeln("\n\t<info>Data-Box ID ".$databox->get_sbas_id()." mounted successful !</info>\n");
|
||||
} catch (\Exception $e) {
|
||||
$output->writeln('<error>Mount databox failed :'.$e->getMessage().'</error>');
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -0,0 +1,60 @@
|
||||
<?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.
|
||||
*/
|
||||
|
||||
namespace Alchemy\Phrasea\Command\Databox;
|
||||
|
||||
use Alchemy\Phrasea\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class UnMountDataboxCommand extends Command
|
||||
{
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct($name = null)
|
||||
{
|
||||
parent::__construct('databox:unmount');
|
||||
|
||||
$this->setDescription('Unmount databox')
|
||||
->addArgument('databox_id', InputArgument::REQUIRED, 'The id of the databox to unmount', null)
|
||||
;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function doExecute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
try {
|
||||
$databox = $this->container->findDataboxById($input->getArgument('databox_id'));
|
||||
$dialog = $this->getHelperSet()->get('dialog');
|
||||
|
||||
do {
|
||||
$continue = mb_strtolower($dialog->ask($output, '<question> Do you want really unmount this databox? (y/N)</question>', 'N'));
|
||||
} while ( ! in_array($continue, ['y', 'n']));
|
||||
|
||||
if ($continue !== 'y') {
|
||||
$output->writeln('Aborting !');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$databox->unmount_databox();
|
||||
$output->writeln('<info>Unmount databox successful</info>');
|
||||
} catch (\Exception $e) {
|
||||
$output->writeln('<error>Unmount databox failed : '.$e->getMessage().'</error>');
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
@@ -15,6 +15,7 @@ use Alchemy\Phrasea\Application;
|
||||
use Alchemy\Phrasea\Command\Command;
|
||||
use Alchemy\Phrasea\Exception\RuntimeException;
|
||||
use Alchemy\Phrasea\Model\Entities\User;
|
||||
use Alchemy\Phrasea\Plugin\Plugin;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\HttpKernel\Client;
|
||||
@@ -30,6 +31,15 @@ class JsFixtures extends Command
|
||||
|
||||
protected function doExecute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
/** @var Plugin $plugin */
|
||||
$msg = [];
|
||||
foreach($this->container['plugins.manager']->listPlugins() as $plugin) {
|
||||
$msg[] = sprintf(" bin/setup plugins:remove \"%s\"", $plugin->getName());
|
||||
}
|
||||
if(count($msg) !== 0) {
|
||||
throw new RuntimeException("You must remove plugins first:\n" . join("\n", $msg));
|
||||
}
|
||||
|
||||
if (!file_exists($this->container['db.fixture.info']['path'])) {
|
||||
throw new RuntimeException('You must generate sqlite db first, run "bin/developer phraseanet:regenerate-sqlite" command.');
|
||||
}
|
||||
@@ -104,6 +114,7 @@ class JsFixtures extends Command
|
||||
private function writeResponse(OutputInterface $output, $method, $path, $to, $authenticateUser = false)
|
||||
{
|
||||
$environment = Application::ENV_TEST;
|
||||
/** @var Application $app */
|
||||
$app = require __DIR__ . '/../../Application/Root.php';
|
||||
$app['orm.em'] = $app->extend('orm.em', function($em, $app) {
|
||||
return $app['orm.ems'][$app['db.fixture.hash.key']];
|
||||
|
@@ -15,6 +15,21 @@ use Alchemy\Phrasea\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
function normalizePath($path) {
|
||||
return array_reduce(explode('/', $path), function ($a, $b) {
|
||||
if($a === 0)
|
||||
$a = '/';
|
||||
|
||||
if($b === '' || $b === '.')
|
||||
return $a;
|
||||
|
||||
if($b === '..')
|
||||
return dirname($a);
|
||||
|
||||
return preg_replace('/\/+/', '/', "$a/$b");
|
||||
}, 0);
|
||||
}
|
||||
|
||||
abstract class AbstractPluginCommand extends Command
|
||||
{
|
||||
protected function validatePlugins(InputInterface $input, OutputInterface $output)
|
||||
@@ -54,33 +69,42 @@ abstract class AbstractPluginCommand extends Command
|
||||
|
||||
protected function doInstallPlugin($source, InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$temporaryDir = $this->container['temporary-filesystem']->createTemporaryDirectory();
|
||||
|
||||
$output->write("Importing <info>$source</info>...");
|
||||
$this->container['plugins.importer']->import($source, $temporaryDir);
|
||||
$output->writeln(" <comment>OK</comment>");
|
||||
|
||||
$output->write("Validating plugin...");
|
||||
$manifest = $this->container['plugins.plugins-validator']->validatePlugin($temporaryDir);
|
||||
$manifest = $this->container['plugins.plugins-validator']->validatePlugin($source);
|
||||
$output->writeln(" <comment>OK</comment> found <info>".$manifest->getName()."</info>");
|
||||
|
||||
$targetDir = $this->container['plugin.path'] . DIRECTORY_SEPARATOR . $manifest->getName();
|
||||
if (normalizePath($targetDir) !== normalizePath($source)) {
|
||||
$temporaryDir = $this->container['temporary-filesystem']->createTemporaryDirectory();
|
||||
$output->write("Importing <info>$source</info>...");
|
||||
$this->container['plugins.importer']->import($source, $temporaryDir);
|
||||
$output->writeln(" <comment>OK</comment>");
|
||||
$workingDir = $temporaryDir;
|
||||
} else {
|
||||
$workingDir = $targetDir;
|
||||
}
|
||||
|
||||
$output->write("Setting up composer...");
|
||||
$this->container['plugins.composer-installer']->install($temporaryDir);
|
||||
$output->writeln(" <comment>OK</comment>");
|
||||
if (!is_dir($workingDir.'/vendor')) {
|
||||
$output->write("Setting up composer...");
|
||||
$this->container['plugins.composer-installer']->install($workingDir);
|
||||
$output->writeln(" <comment>OK</comment>");
|
||||
}
|
||||
|
||||
$output->write("Installing plugin <info>".$manifest->getName()."</info>...");
|
||||
$this->container['filesystem']->mirror($temporaryDir, $targetDir);
|
||||
if (isset($temporaryDir)) {
|
||||
$this->container['filesystem']->mirror($temporaryDir, $targetDir);
|
||||
}
|
||||
$output->writeln(" <comment>OK</comment>");
|
||||
|
||||
$output->write("Copying public files <info>".$manifest->getName()."</info>...");
|
||||
$this->container['plugins.assets-manager']->update($manifest);
|
||||
$output->writeln(" <comment>OK</comment>");
|
||||
|
||||
$output->write("Removing temporary directory...");
|
||||
$this->container['filesystem']->remove($temporaryDir);
|
||||
$output->writeln(" <comment>OK</comment>");
|
||||
if (isset($temporaryDir)) {
|
||||
$output->write("Removing temporary directory...");
|
||||
$this->container['filesystem']->remove($temporaryDir);
|
||||
$output->writeln(" <comment>OK</comment>");
|
||||
}
|
||||
|
||||
$output->write("Activating plugin...");
|
||||
$this->container['conf']->set(['plugins', $manifest->getName(), 'enabled'], true);
|
||||
|
@@ -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,8 +52,17 @@ 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('yes', 'y', InputOption::VALUE_NONE, 'Answer yes to all questions');
|
||||
->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('download-path', null, InputOption::VALUE_OPTIONAL, 'Path to download repository', __DIR__ . '/../../../../../tmp/download')
|
||||
->addOption('lazaret-path', null, InputOption::VALUE_OPTIONAL, 'Path to lazaret repository', __DIR__ . '/../../../../../tmp/lazaret')
|
||||
->addOption('caption-path', null, InputOption::VALUE_OPTIONAL, 'Path to caption repository', __DIR__ . '/../../../../../tmp/caption')
|
||||
->addOption('scheduler-locks-path', null, InputOption::VALUE_OPTIONAL, 'Path to scheduler-locks repository', __DIR__ . '/../../../../../tmp/locks')
|
||||
->addOption('worker-tmp-files', null, InputOption::VALUE_OPTIONAL, 'Path to worker-tmp-files repository', __DIR__ . '/../../../../../tmp')
|
||||
->addOption('yes', 'y', InputOption::VALUE_NONE, 'Answer yes to all questions')
|
||||
->setHelp("Phraseanet can only be installed on 64 bits PHP.");
|
||||
;
|
||||
|
||||
return $this;
|
||||
}
|
||||
@@ -72,6 +82,14 @@ class Install extends Command
|
||||
*/
|
||||
protected function doExecute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
if(PHP_INT_SIZE !== 8) {
|
||||
$output->writeln(sprintf(
|
||||
"<error>Phraseanet can only be installed on 64 bits PHP, your version is %d bits (PHP_INT_SIZE=%d).</error>",
|
||||
PHP_INT_SIZE<<3,PHP_INT_SIZE
|
||||
));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/** @var DialogHelper $dialog */
|
||||
$dialog = $this->getHelperSet()->get('dialog');
|
||||
|
||||
@@ -121,6 +139,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);
|
||||
|
||||
@@ -131,7 +164,10 @@ class Install extends Command
|
||||
}
|
||||
}
|
||||
|
||||
$this->container['phraseanet.installer']->install($email, $password, $abConn, $serverName, $dataPath, $dbConn, $templateName, $this->detectBinaries());
|
||||
$storagePaths = $this->getStoragePaths($input, $dataPath);
|
||||
|
||||
$this->container['phraseanet.installer']->install($email, $password, $abConn, $serverName, $storagePaths, $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 +375,56 @@ 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 getStoragePaths(InputInterface $input, $dataPath)
|
||||
{
|
||||
$schedulerLocksPath = $input->getOption('scheduler-locks-path');
|
||||
|
||||
if (!is_dir($schedulerLocksPath)) {
|
||||
mkdir($schedulerLocksPath, 0755, true);
|
||||
}
|
||||
|
||||
if (($schedulerLocksPath = realpath($schedulerLocksPath)) === FALSE) {
|
||||
throw new \InvalidArgumentException(sprintf('Path %s does not exist.', $schedulerLocksPath));
|
||||
}
|
||||
|
||||
return [
|
||||
'subdefs' => $dataPath,
|
||||
'download' => $input->getOption('download-path'),
|
||||
'lazaret' => $input->getOption('lazaret-path'),
|
||||
'caption' => $input->getOption('caption-path'),
|
||||
'worker_tmp_files' => $input->getOption('worker-tmp-files')
|
||||
];
|
||||
}
|
||||
|
||||
private function detectBinaries()
|
||||
{
|
||||
return [
|
||||
|
329
lib/Alchemy/Phrasea/Command/User/UserApplicationsCommand.php
Normal file
329
lib/Alchemy/Phrasea/Command/User/UserApplicationsCommand.php
Normal file
@@ -0,0 +1,329 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Phraseanet
|
||||
*
|
||||
* (c) 2005-2020 Alchemy
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Alchemy\Phrasea\Command\User;
|
||||
|
||||
use Alchemy\Phrasea\Command\Command;
|
||||
use Symfony\Component\Console\Helper\DialogHelper;
|
||||
use Alchemy\Phrasea\ControllerProvider\Api\V2;
|
||||
use Alchemy\Phrasea\Core\LazyLocator;
|
||||
use Alchemy\Phrasea\Model\Entities\ApiApplication;
|
||||
use Alchemy\Phrasea\Model\Entities\ApiAccount;
|
||||
use Alchemy\Phrasea\Model\Entities\User;
|
||||
use Alchemy\Phrasea\Notification\Mail\MailRequestPasswordSetup;
|
||||
use Alchemy\Phrasea\Notification\Mail\MailRequestEmailConfirmation;
|
||||
use Alchemy\Phrasea\Notification\Receiver;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
|
||||
class UserApplicationsCommand extends Command
|
||||
{
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct($name = null)
|
||||
{
|
||||
parent::__construct('user:applications');
|
||||
|
||||
$this->setDescription('List, Create, Edit, Delete application in Phraseanet <comment>(experimental)</>')
|
||||
->addOption('list', null, InputOption::VALUE_NONE, 'List all applications or user applications if --user_id is set')
|
||||
->addOption('create', null, InputOption::VALUE_NONE, 'Create application for user in Phraseanet')
|
||||
->addOption('edit', null, InputOption::VALUE_NONE, 'Edit application in Phraseanet work only if app_id is set')
|
||||
->addOption('delete', null, InputOption::VALUE_NONE, 'Delete application in Phraseanet, require an app_id')
|
||||
->addOption('user_id', 'u', InputOption::VALUE_REQUIRED, 'The Id of user owner of application (user_id), required to Create, Edit and Delete.')
|
||||
->addOption('app_id', 'a', InputOption::VALUE_REQUIRED, 'The application ID, required for Edit and Delete')
|
||||
->addOption('name', null, InputOption::VALUE_REQUIRED, 'The desired name for application, required for Create and Edit.')
|
||||
->addOption('type', 't', InputOption::VALUE_OPTIONAL, 'The kind of application, Desktop or Web.',ApiApplication::WEB_TYPE)
|
||||
->addOption('description', 'd', InputOption::VALUE_REQUIRED, 'The desired description for application.')
|
||||
->addOption('website', 'w', InputOption::VALUE_OPTIONAL, 'The desired url, eg: -w "https://www.alchemy.fr".')
|
||||
->addOption('callback', 'c', InputOption::VALUE_OPTIONAL, 'The desired endpoint for callback, required for web kind eg: -c "https://www.alchemy.fr/callback"')
|
||||
->addOption('webhook_url', null, InputOption::VALUE_REQUIRED, 'The webhook url')
|
||||
->addOption('active', null, InputOption::VALUE_OPTIONAL, 'Activate or deactivate the app, values true or false', 'true')
|
||||
->addOption('generate_token', null, InputOption::VALUE_NONE, 'Generate or regenerate the access token')
|
||||
->addOption('password_oauth2_gt', null, InputOption::VALUE_OPTIONAL, 'Activate or deactivate password OAuth2 grant type , values true or false', 'false')
|
||||
->addOption('jsonformat', null, InputOption::VALUE_NONE, 'Output in json format')
|
||||
|
||||
->setHelp('');
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function doExecute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$userId = $input->getOption('user_id');
|
||||
$appId = $input->getOption('app_id');
|
||||
$name = $input->getOption('name');
|
||||
$type = $input->getOption('type');
|
||||
$description = $input->getOption('description');
|
||||
$website = $input->getOption('website');
|
||||
$urlCallback = $input->getOption('callback');
|
||||
$webhookUrl = $input->getOption('webhook_url');
|
||||
$active = $input->getOption('active');
|
||||
$generateToken = $input->getOption('generate_token');
|
||||
$passwordOauth2Gt = $input->getOption('password_oauth2_gt');
|
||||
$create = $input->getOption('create');
|
||||
$edit = $input->getOption('edit');
|
||||
$delete = $input->getOption('delete');
|
||||
$list = $input->getOption('list');
|
||||
$jsonformat = $input->getOption('jsonformat');
|
||||
|
||||
$applicationManipulator = $this->container['manipulator.api-application'];
|
||||
$apiOauthTokenManipulator = $this->container['manipulator.api-oauth-token'];
|
||||
$accountRepository = $this->container['repo.api-accounts'];
|
||||
$apiApllicationConverter = $this->container['converter.api-application'];
|
||||
$userRepository = $this->container['repo.users'];
|
||||
$apiOauthRepository = $this->container['repo.api-oauth-tokens'];
|
||||
|
||||
if ($create) {
|
||||
if (null === $user = $userRepository->find($userId)) {
|
||||
$output->writeln('<error>User not found</error>');
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!$name) {
|
||||
$output->writeln('<error>Name of application must be provide with option --name.</error>');
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!$description) {
|
||||
$output->writeln('<error>Desciption of application must be provide.</error>');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
try {
|
||||
$application = $applicationManipulator
|
||||
->create(
|
||||
$name,
|
||||
$type,
|
||||
$description,
|
||||
$website,
|
||||
$user,
|
||||
$urlCallback
|
||||
);
|
||||
|
||||
$apiAccountManipulator = $this->container['manipulator.api-account'];
|
||||
$apiAccountManipulator->create($application, $user, V2::VERSION);
|
||||
|
||||
$account = $accountRepository->findByUserAndApplication($user, $application);
|
||||
|
||||
if ($generateToken) {
|
||||
$apiOauthTokenManipulator->create($account);
|
||||
}
|
||||
|
||||
if ($passwordOauth2Gt) {
|
||||
if (in_array($passwordOauth2Gt, ['true', 'false'])) {
|
||||
$application->setGrantPassword(($passwordOauth2Gt == 'true') ? true : false);
|
||||
$applicationManipulator->update($application);
|
||||
} else {
|
||||
$output->writeln('<error> Value of option --password_oauth2_gt should be "true" or "false"</error>');
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if ($webhookUrl) {
|
||||
$applicationManipulator->setWebhookUrl($application, $webhookUrl);
|
||||
$applicationManipulator->update($application);
|
||||
}
|
||||
|
||||
if ($active) {
|
||||
if (in_array($active, ['true', 'false'])) {
|
||||
$application->setActivated(($active == 'true') ? true : false);
|
||||
$applicationManipulator->update($application);
|
||||
} else {
|
||||
$output->writeln('<error>Value of option --active should be "true" or "false"</error>');
|
||||
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
$application->setActivated(true);
|
||||
$applicationManipulator->update($application);
|
||||
}
|
||||
|
||||
$this->showApllicationInformation($apiOauthRepository, $account, $application, $jsonformat, $output);
|
||||
} catch (\Exception $e) {
|
||||
$output->writeln('<error>Create an application for user failed : '.$e->getMessage().'</error>');
|
||||
}
|
||||
} elseif ($edit) {
|
||||
if (!$appId) {
|
||||
$output->writeln('<error>ID of the application must be provided with option --app_id to edit the application.</error>');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
$application = $apiApllicationConverter->convert($appId);
|
||||
$account = $accountRepository->findByUserAndApplication($application->getCreator(), $application);
|
||||
|
||||
if (!$account) {
|
||||
$output->writeln('<error>ApiAccount not found!</error>');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ($name) {
|
||||
$application->setName($name);
|
||||
}
|
||||
if ($type) {
|
||||
$applicationManipulator->setType($application, $type);
|
||||
if ($type == ApiApplication::DESKTOP_TYPE) {
|
||||
$applicationManipulator->setRedirectUri($application, ApiApplication::NATIVE_APP_REDIRECT_URI);
|
||||
}
|
||||
}
|
||||
if ($description) {
|
||||
$application->setDescription($description);
|
||||
}
|
||||
if ($website) {
|
||||
$applicationManipulator->setWebsiteUrl($application, $website);
|
||||
}
|
||||
if ($urlCallback) {
|
||||
$applicationManipulator->setRedirectUri($application, $urlCallback);
|
||||
}
|
||||
if ($generateToken) {
|
||||
if (null !== $devToken = $apiOauthRepository->findDeveloperToken($account)) {
|
||||
$apiOauthTokenManipulator->renew($devToken);
|
||||
} else {
|
||||
$apiOauthTokenManipulator->create($account);
|
||||
}
|
||||
}
|
||||
if ($passwordOauth2Gt) {
|
||||
if (in_array($passwordOauth2Gt, ['true', 'false'])) {
|
||||
$application->setGrantPassword(($passwordOauth2Gt == 'true') ? true : false);
|
||||
} else {
|
||||
$output->writeln('<error> Value of option --password_oauth2_gt should be "true" or "false"</error>');
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if ($webhookUrl) {
|
||||
$applicationManipulator->setWebhookUrl($application, $webhookUrl);
|
||||
}
|
||||
|
||||
if ($active) {
|
||||
if (in_array($active, ['true', 'false'])) {
|
||||
$application->setActivated(($active == 'true') ? true : false);
|
||||
} else {
|
||||
$output->writeln('<error>Value of option --active should be "true" or "false"</error>');
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
$applicationManipulator->update($application);
|
||||
|
||||
$this->showApllicationInformation($apiOauthRepository, $account, $application, $jsonformat, $output);
|
||||
} elseif ($list) {
|
||||
if ($userId) {
|
||||
if (null === $user = $userRepository->find($userId)) {
|
||||
$output->writeln('<error>User not found</error>');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
$accounts = $accountRepository->findByUser($user);
|
||||
} else {
|
||||
$accounts = $accountRepository->findAll();
|
||||
}
|
||||
|
||||
$applicationList = [];
|
||||
|
||||
foreach ($accounts as $account) {
|
||||
$application = $account->getApplication();
|
||||
$token = $apiOauthRepository->findDeveloperToken($account);
|
||||
|
||||
$applicationList[] = [
|
||||
$application->getId(),
|
||||
$account->getUser()->getId(),
|
||||
$application->getName(),
|
||||
$application->getClientId(),
|
||||
$application->getClientSecret(),
|
||||
$application->getRedirectUri(),
|
||||
($token) ? $token->getOauthToken() : '-',
|
||||
$application->isPasswordGranted() ? "true": "false"
|
||||
];
|
||||
}
|
||||
|
||||
$applicationTable = $this->getHelperSet()->get('table');
|
||||
$headers = ['app_id', 'user_id', 'name', 'client_id', 'client_secret', 'callback_url', 'generated token', 'grant_password status'];
|
||||
|
||||
if ($jsonformat ) {
|
||||
foreach ($applicationList as $appList) {
|
||||
$appInfo[] = array_combine($headers, $appList);
|
||||
}
|
||||
|
||||
echo json_encode($appInfo);
|
||||
} else {
|
||||
$applicationTable = $this->getHelperSet()->get('table');
|
||||
$applicationTable
|
||||
->setHeaders($headers)
|
||||
->setRows($applicationList)
|
||||
->render($output)
|
||||
;
|
||||
}
|
||||
} elseif ($delete) {
|
||||
if (!$appId) {
|
||||
$output->writeln('<error>ID of the application must be provided with option --app_id to delete the app.</error>');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
$application = $apiApllicationConverter->convert($appId);
|
||||
|
||||
if (is_null($application->getCreator())) {
|
||||
/** @var DialogHelper $dialog */
|
||||
$dialog = $this->getHelperSet()->get('dialog');
|
||||
|
||||
$continue = $dialog->askConfirmation($output, "<question>It's a special phraseanet application, do you want really to delete it? (N/y)</>", false);
|
||||
|
||||
if (!$continue) {
|
||||
$output->writeln("<info>See you later !</>");
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
$applicationManipulator->delete($application);
|
||||
|
||||
$output->writeln("<info>Application ID $appId deleted successfully !</info>");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private function showApllicationInformation($apiOauthRepository, ApiAccount $account, ApiApplication $application, $jsonformat, $output)
|
||||
{
|
||||
$token = $account ? $apiOauthRepository->findDeveloperToken($account) : null;
|
||||
|
||||
$applicationCreated = [
|
||||
$application->getClientSecret(),
|
||||
$application->getClientId(),
|
||||
$this->container["conf"]->get("servername") . "api/oauthv2/authorize",
|
||||
$this->container["conf"]->get("servername") . "api/oauthv2/token",
|
||||
($token) ? $token->getOauthToken() : '-',
|
||||
$application->isPasswordGranted() ? "true": "false"
|
||||
];
|
||||
|
||||
$headers = ['client_secret', 'client_id', 'Authorize endpoint url', 'Access endpoint', 'generated token', 'grant_password status'];
|
||||
if ($jsonformat ) {
|
||||
$createdAppInfo = array_combine($headers, $applicationCreated);
|
||||
echo json_encode($createdAppInfo);
|
||||
} else {
|
||||
$table = $this->getHelperSet()->get('table');
|
||||
$table
|
||||
->setHeaders($headers)
|
||||
->setRows([$applicationCreated])
|
||||
->render($output)
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
211
lib/Alchemy/Phrasea/Command/User/UserCreateCommand.php
Normal file
211
lib/Alchemy/Phrasea/Command/User/UserCreateCommand.php
Normal file
@@ -0,0 +1,211 @@
|
||||
<?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.
|
||||
*/
|
||||
|
||||
namespace Alchemy\Phrasea\Command\User;
|
||||
|
||||
use Alchemy\Phrasea\Application\Helper\NotifierAware;
|
||||
use Alchemy\Phrasea\Command\Command;
|
||||
use Alchemy\Phrasea\Core\LazyLocator;
|
||||
use Alchemy\Phrasea\Model\Entities\User;
|
||||
use Alchemy\Phrasea\Notification\Mail\MailRequestPasswordSetup;
|
||||
use Alchemy\Phrasea\Notification\Mail\MailRequestEmailConfirmation;
|
||||
use Alchemy\Phrasea\Notification\Receiver;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
|
||||
|
||||
class UserCreateCommand extends Command
|
||||
{
|
||||
use NotifierAware;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct($name = null)
|
||||
{
|
||||
parent::__construct('user:create');
|
||||
|
||||
$this->setDescription('Create user in Phraseanet')
|
||||
->addOption('user_login', null, InputOption::VALUE_REQUIRED, 'The desired login for created user.')
|
||||
->addOption('user_mail', null, InputOption::VALUE_OPTIONAL, 'The desired mail for created user.')
|
||||
->addOption('user_password', null, InputOption::VALUE_OPTIONAL, 'The desired password')
|
||||
->addOption('send_mail_confirm', null, InputOption::VALUE_NONE, 'Send an email to user, for validate email.')
|
||||
->addOption('send_mail_password', null, InputOption::VALUE_NONE, 'Send an email to user, for password definition, work only if user_password is not define')
|
||||
->addOption('model_number', null, InputOption::VALUE_OPTIONAL, 'Id of model')
|
||||
->addOption('user_gender', null, InputOption::VALUE_OPTIONAL, 'The gender for created user.')
|
||||
->addOption('user_firstname', null, InputOption::VALUE_OPTIONAL, 'The first name for created user.')
|
||||
->addOption('user_lastname', null, InputOption::VALUE_OPTIONAL, 'The last name for created user.')
|
||||
->addOption('user_compagny', null, InputOption::VALUE_OPTIONAL, 'The compagny for created user.')
|
||||
->addOption('user_job', null, InputOption::VALUE_OPTIONAL, 'The job for created user.')
|
||||
->addOption('user_activitie', null, InputOption::VALUE_OPTIONAL, 'The activitie for created user.')
|
||||
->addOption('user_phone', null, InputOption::VALUE_OPTIONAL, 'The phone number for created user.')
|
||||
->setHelp('');
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function doExecute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
|
||||
$userLogin = $input->getOption('user_login');
|
||||
$userMail = $input->getOption('user_mail');
|
||||
$userPassword = $input->getOption('user_password');
|
||||
$sendMailConfirm = $input->getOption('send_mail_confirm');
|
||||
$sendMailPassword = $input->getOption('send_mail_password');
|
||||
$modelNumber = $input->getOption('model_number');
|
||||
$userGender = $input->getOption('user_gender');
|
||||
$userFirstName = $input->getOption('user_firstname');
|
||||
$userLastName = $input->getOption('user_lastname');
|
||||
$userCompagny = $input->getOption('user_compagny');
|
||||
$userJob = $input->getOption('user_job');
|
||||
$userActivity = $input->getOption('user_activitie');
|
||||
$userPhone = $input->getOption('user_phone');
|
||||
|
||||
$userRepository = $this->container['repo.users'];
|
||||
|
||||
if ($userMail) {
|
||||
if (!\Swift_Validate::email($userMail)) {
|
||||
$output->writeln('<error>Invalid mail address</error>');
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (null !== $userRepository->findByEmail($userMail)) {
|
||||
$output->writeln('<error>An user exist with this email.</error>');
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$password = (!is_null($userPassword)) ? $userPassword : $this->container['random.medium']->generateString(128);
|
||||
$userManipulator = $this->container['manipulator.user'];
|
||||
$user = $userManipulator->createUser($userLogin, $password, $userMail);
|
||||
|
||||
if ($userGender) {
|
||||
if (null === $gender = $this->verifyGender($userGender)) {
|
||||
$output->writeln('<bg=yellow;options=bold>Gender '.$userGender.' not exists.</>');
|
||||
}
|
||||
$user->setGender($gender);
|
||||
}
|
||||
|
||||
if($userFirstName) $user->setFirstName($userFirstName);
|
||||
if($userLastName) $user->setLastName($userLastName);
|
||||
if($userCompagny) $user->setCompany($userCompagny);
|
||||
if($userJob) $user->setJob($userJob);
|
||||
if($userActivity) $user->setActivity($userActivity);
|
||||
if($userPhone) $user->setPhone($userPhone);
|
||||
|
||||
if ($sendMailPassword and $userMail and is_null($userPassword)) {
|
||||
$this->sendPasswordSetupMail($user);
|
||||
}
|
||||
|
||||
if ($sendMailConfirm and $userMail) {
|
||||
$user->setMailLocked(true);
|
||||
$this->sendAccountUnlockEmail($user);
|
||||
}
|
||||
|
||||
if ($modelNumber) {
|
||||
$template = $userRepository->find($modelNumber);
|
||||
if (!$template) {
|
||||
$output->writeln('<bg=yellow;options=bold>Model '.$modelNumber.' not found.</>');
|
||||
} else {
|
||||
$base_ids = [];
|
||||
foreach ($this->container->getApplicationBox()->get_databoxes() as $databox) {
|
||||
foreach ($databox->get_collections() as $collection) {
|
||||
$base_ids[] = $collection->get_base_id();
|
||||
}
|
||||
}
|
||||
$this->container->getAclForUser($user)->apply_model($template, $base_ids);
|
||||
}
|
||||
}
|
||||
|
||||
$this->container['orm.em']->flush();
|
||||
|
||||
$output->writeln("<info>Create new user successful !</info>");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get gender for user
|
||||
* @param $type
|
||||
* @return int|null
|
||||
*/
|
||||
private function verifyGender($type)
|
||||
{
|
||||
switch (strtolower($type)) {
|
||||
case "mlle.":
|
||||
case "mlle":
|
||||
case "miss":
|
||||
case "mademoiselle":
|
||||
case "0":
|
||||
$gender = User::GENDER_MISS;
|
||||
break;
|
||||
case "mme":
|
||||
case "madame":
|
||||
case "ms":
|
||||
case "ms.":
|
||||
case "1":
|
||||
$gender = User::GENDER_MRS;
|
||||
break;
|
||||
case "m":
|
||||
case "m.":
|
||||
case "mr":
|
||||
case "mr.":
|
||||
case "monsieur":
|
||||
case "mister":
|
||||
case "2":
|
||||
$gender = User::GENDER_MR;
|
||||
break;
|
||||
default:
|
||||
$gender = null;
|
||||
}
|
||||
return $gender;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send mail for renew password
|
||||
* @param User $user
|
||||
*/
|
||||
public function sendPasswordSetupMail(User $user)
|
||||
{
|
||||
$this->setDelivererLocator(new LazyLocator($this->container, 'notification.deliverer'));
|
||||
$receiver = Receiver::fromUser($user);
|
||||
|
||||
$token = $this->container['manipulator.token']->createResetPasswordToken($user);
|
||||
|
||||
$mail = MailRequestPasswordSetup::create($this->container, $receiver);
|
||||
$servername = $this->container['conf']->get('servername');
|
||||
$mail->setButtonUrl('http://'.$servername.'/login/renew-password/?token='.$token->getValue());
|
||||
$mail->setLogin($user->getLogin());
|
||||
|
||||
$this->deliver($mail);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param User $user
|
||||
*/
|
||||
public function sendAccountUnlockEmail(User $user)
|
||||
{
|
||||
$this->setDelivererLocator(new LazyLocator($this->container, 'notification.deliverer'));
|
||||
$receiver = Receiver::fromUser($user);
|
||||
|
||||
$token = $this->container['manipulator.token']->createAccountUnlockToken($user);
|
||||
|
||||
$mail = MailRequestEmailConfirmation::create($this->container, $receiver);
|
||||
$servername = $this->container['conf']->get('servername');
|
||||
$mail->setButtonUrl('http://'.$servername.'/login/register-confirm/?code='.$token->getValue());
|
||||
$mail->setExpiration($token->getExpiration());
|
||||
|
||||
$this->deliver($mail);
|
||||
}
|
||||
|
||||
}
|
236
lib/Alchemy/Phrasea/Command/User/UserListCommand.php
Normal file
236
lib/Alchemy/Phrasea/Command/User/UserListCommand.php
Normal file
@@ -0,0 +1,236 @@
|
||||
<?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.
|
||||
*/
|
||||
|
||||
namespace Alchemy\Phrasea\Command\User;
|
||||
|
||||
use Alchemy\Phrasea\Command\Command;
|
||||
use Alchemy\Phrasea\Model\Entities\User;
|
||||
use Symfony\Component\Console\Helper\TableCell;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Alchemy\Phrasea\Utilities\NullableDateTime;
|
||||
|
||||
|
||||
class UserListCommand extends Command
|
||||
{
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct($name = null)
|
||||
{
|
||||
parent::__construct('user:list');
|
||||
|
||||
$this->setDescription('List of all user <comment>(experimental)</>')
|
||||
->addOption('user_id', null, InputOption::VALUE_OPTIONAL, 'The id of user export only info this user ')
|
||||
->addOption('user_email', null, InputOption::VALUE_OPTIONAL, 'The mail of user export only info this user .')
|
||||
->addOption('database_id', null, InputOption::VALUE_OPTIONAL, 'Id of database.')
|
||||
->addOption('collection_id', null, InputOption::VALUE_OPTIONAL, 'Id of the collection.')
|
||||
->addOption('mail_lock_status', null, InputOption::VALUE_NONE, 'Status by mail locked')
|
||||
->addOption('guest', null, InputOption::VALUE_NONE, 'Only guest user')
|
||||
->addOption('created', null, InputOption::VALUE_OPTIONAL, 'Created at with operator,aaaa-mm-jj hh:mm:ss.')
|
||||
->addOption('updated', null, InputOption::VALUE_OPTIONAL, 'Update at with operator,aaaa-mm-jj hh:mm:ss.')
|
||||
->addOption('right', null, InputOption::VALUE_NONE, 'Show right information')
|
||||
->addOption('adress', null, InputOption::VALUE_NONE, 'Show adress information')
|
||||
->addOption('models', null, InputOption::VALUE_NONE, "Show only defined models, if --user_id is set with --models it's the template owner")
|
||||
->addOption('jsonformat', null, InputOption::VALUE_NONE, 'Output in json format')
|
||||
->setHelp('');
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function doExecute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
|
||||
$userId = $input->getOption('user_id');
|
||||
$userEmail = $input->getOption('user_email');
|
||||
$databaseId = $input->getOption('database_id');
|
||||
$collectionId = $input->getOption('collection_id');
|
||||
$lockStatus = $input->getOption('mail_lock_status');
|
||||
$guest = $input->getOption('guest');
|
||||
$withAdress = $input->getOption('adress');
|
||||
$created = $input->getOption('created');
|
||||
$updated = $input->getOption('updated');
|
||||
$withRight = $input->getOption('right');
|
||||
$models = $input->getOption('models');
|
||||
$jsonformat = $input->getOption('jsonformat');
|
||||
|
||||
$query = $this->container['phraseanet.user-query'];
|
||||
|
||||
if($databaseId) $query->on_base_ids([$databaseId]);
|
||||
if($collectionId) $query->on_sbas_ids([$collectionId]);
|
||||
if($created) $this->addFilterDate($created,'created',$query);
|
||||
if($updated) $this->addFilterDate($updated,'updated',$query);
|
||||
if($userId && !$models) $query->addSqlFilter('Users.id = ?' ,[$userId]);
|
||||
if($userEmail && !$models) $query->addSqlFilter('Users.email = ?' ,[$userEmail]);
|
||||
if($lockStatus && !$models) $query->addSqlFilter('Users.mail_locked = 1');
|
||||
if($guest && !$models) $query->include_invite(true)->addSqlFilter('Users.guest = 1');
|
||||
|
||||
/** @var UserRepository $userRepository */
|
||||
$userRepository = $this->container['repo.users'];
|
||||
|
||||
if ($models && $userId) {
|
||||
$users = $userRepository->findBy(['templateOwner' => $userId]);
|
||||
} elseif ($models) {
|
||||
$users = $userRepository->findTemplate();
|
||||
} else {
|
||||
$users = $query->execute()->get_results();
|
||||
}
|
||||
|
||||
$userList = [];
|
||||
foreach ($users as $key => $user) {
|
||||
$userList[] = $this->listUser($user, $withAdress, $withRight);
|
||||
|
||||
$userListRaw[] = array_combine($this->headerTable($withAdress, $withRight), $this->listUser($user, $withAdress, $withRight));
|
||||
}
|
||||
|
||||
if ($jsonformat) {
|
||||
echo json_encode($userListRaw);
|
||||
} else {
|
||||
$table = $this->getHelperSet()->get('table');
|
||||
$table
|
||||
->setHeaders($this->headerTable($withAdress, $withRight))
|
||||
->setRows($userList)
|
||||
->render($output);
|
||||
;
|
||||
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $withAdress
|
||||
* @param $withRight
|
||||
* @return array
|
||||
*/
|
||||
private function headerTable($withAdress,$withRight)
|
||||
{
|
||||
$defaultHeader = ['id', 'login', 'email','last_model','first_name','last_name','gender','created','updated','status','locale'];
|
||||
$adressHeader = [ 'address', 'zip_code', 'city', 'country', 'phone', 'fax', 'job','position', 'company', 'geoname_id'];
|
||||
$rightHeader = [ 'admin', 'guest', 'mail_notification', 'ldap_created', 'mail_locked'];
|
||||
|
||||
return $this->createInformation($withAdress,$withRight,$defaultHeader,['adress' => $adressHeader,'right' =>$rightHeader]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param User $user
|
||||
* @param $withAdress
|
||||
* @param $withRight
|
||||
* @return array
|
||||
*/
|
||||
private function listUser(User $user,$withAdress,$withRight)
|
||||
{
|
||||
switch ($user->getGender()) {
|
||||
case User::GENDER_MRS:
|
||||
$gender = 'Mrs';
|
||||
break;
|
||||
case User::GENDER_MISS:
|
||||
$gender = 'Miss';
|
||||
break;
|
||||
case User::GENDER_MR:
|
||||
default:
|
||||
$gender = 'Mr';
|
||||
}
|
||||
|
||||
$defaultInfo = [
|
||||
$user->getId(),
|
||||
$user->getLogin() ?: '-',
|
||||
$user->getEmail() ?: '-',
|
||||
$user->getLastAppliedTemplate() ? $user->getLastAppliedTemplate()->getLogin() : '-',
|
||||
$user->getFirstName() ?: '-',
|
||||
$user->getLastName() ?: '-',
|
||||
$gender,
|
||||
NullableDateTime::format($user->getCreated(),'Y-m-d H:i:s'),
|
||||
NullableDateTime::format($user->getUpdated(),'Y-m-d H:i:s'),
|
||||
'status',
|
||||
$user->getLocale() ?: '-',
|
||||
];
|
||||
|
||||
return $this->createInformation($withAdress,$withRight,$defaultInfo,['adress' => $this->userAdress($user),'right' => $this->userRight($user)]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param User $user
|
||||
* @return array
|
||||
*/
|
||||
private function userAdress(User $user)
|
||||
{
|
||||
return [
|
||||
$user->getAddress() ?: '-',
|
||||
$user->getZipCode() ?: '-',
|
||||
$user->getCity() ?: '-',
|
||||
$user->getCountry() ?: '-',
|
||||
$user->getPhone() ?: '-',
|
||||
$user->getFax() ?: '-',
|
||||
$user->getJob() ?: '-',
|
||||
$user->getActivity() ?: '-',
|
||||
$user->getCompany() ?: '-',
|
||||
$user->getGeonameId() ?: '-',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param User $user
|
||||
* @return array
|
||||
*/
|
||||
private function userRight(User $user)
|
||||
{
|
||||
return [
|
||||
$user->isAdmin() ?: false,
|
||||
$user->isGuest() ?: false,
|
||||
$user->hasMailNotificationsActivated() ?: false,
|
||||
$user->hasLdapCreated() ?: false,
|
||||
$user->isMailLocked() ?: false,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $withAdress
|
||||
* @param $withRight
|
||||
* @param $default
|
||||
* @param $infoToMerge
|
||||
* @return array
|
||||
*/
|
||||
private function createInformation($withAdress,$withRight,$default,$infoToMerge)
|
||||
{
|
||||
if ($withAdress && $withRight) {
|
||||
$information = array_merge($default, $infoToMerge['adress'],$infoToMerge['right']);
|
||||
} elseif ($withAdress && !$withRight) {
|
||||
$information = array_merge($default, $infoToMerge['adress']);
|
||||
} elseif(!$withAdress && $withRight) {
|
||||
$information = array_merge($default, $infoToMerge['right']);
|
||||
} else {
|
||||
$information = $default;
|
||||
}
|
||||
|
||||
return $information;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $date
|
||||
* @param $type
|
||||
* @param $query
|
||||
*/
|
||||
private function addFilterDate($date,$type,$query){
|
||||
|
||||
list($operator,$dateAt) = explode(',', $date);
|
||||
|
||||
if (!in_array($operator,['=','>=','<=','>','<'])) {
|
||||
throw new \InvalidArgumentException(" '=' or '<=' or '>=' or '>' or '<'");
|
||||
}
|
||||
|
||||
$query->addSqlFilter($type.$operator.' ?' ,[$dateAt]);
|
||||
}
|
||||
|
||||
}
|
178
lib/Alchemy/Phrasea/Command/User/UserPasswordCommand.php
Normal file
178
lib/Alchemy/Phrasea/Command/User/UserPasswordCommand.php
Normal file
@@ -0,0 +1,178 @@
|
||||
<?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.
|
||||
*/
|
||||
|
||||
namespace Alchemy\Phrasea\Command\User;
|
||||
|
||||
use Alchemy\Phrasea\Application\Helper\NotifierAware;
|
||||
use Alchemy\Phrasea\Command\Command;
|
||||
use Alchemy\Phrasea\Core\LazyLocator;
|
||||
use Alchemy\Phrasea\Model\Entities\User;
|
||||
use Alchemy\Phrasea\Notification\Receiver;
|
||||
use Alchemy\Phrasea\Notification\Mail\MailRequestPasswordUpdate;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
|
||||
class UserPasswordCommand extends Command
|
||||
{
|
||||
use NotifierAware;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct($name = null)
|
||||
{
|
||||
parent::__construct('user:password');
|
||||
|
||||
$this->setDescription('Set user password in Phraseanet <comment>(experimental)</>')
|
||||
->addOption('user_id', null, InputOption::VALUE_REQUIRED, 'The id of user.')
|
||||
->addOption('generate', null, InputOption::VALUE_NONE, 'Generate and set with a random value')
|
||||
->addOption('password', null, InputOption::VALUE_OPTIONAL, 'Set the user password to the input value')
|
||||
->addOption('send_renewal_email', null, InputOption::VALUE_NONE, 'Send email link to user for password renewing, work only if --password or --generate are not define')
|
||||
->addOption('password_hash', null, InputOption::VALUE_OPTIONAL, 'Define a password hashed, work only with password_nonce')
|
||||
->addOption('password_nonce', null, InputOption::VALUE_OPTIONAL, 'Define a password nonce, work only with password_hash')
|
||||
->addOption('dump', null, InputOption::VALUE_NONE, 'Return the password hashed and nonce')
|
||||
->addOption('jsonformat', null, InputOption::VALUE_NONE, 'Output in json format')
|
||||
->addOption('yes', 'y', InputOption::VALUE_NONE, 'Answer yes to all questions')
|
||||
|
||||
->setHelp('');
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function doExecute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$dialog = $this->getHelperSet()->get('dialog');
|
||||
$userRepository = $this->container['repo.users'];
|
||||
$userManipulator = $this->container['manipulator.user'];
|
||||
|
||||
$user = $userRepository->find($input->getOption('user_id'));
|
||||
$password = $input->getOption('password');
|
||||
$generate = $input->getOption('generate');
|
||||
$sendRenewalEmail = $input->getOption('send_renewal_email');
|
||||
$dump = $input->getOption('dump');
|
||||
$passwordHash = $input->getOption('password_hash');
|
||||
$passwordNonce = $input->getOption('password_nonce');
|
||||
$jsonformat = $input->getOption('jsonformat');
|
||||
$yes = $input->getOption('yes');
|
||||
|
||||
|
||||
if ($user === null) {
|
||||
$output->writeln('<info>Not found User.</info>');
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ($passwordHash && $passwordNonce) {
|
||||
$user->setNonce($passwordNonce);
|
||||
$user->setPassword($passwordHash);
|
||||
$userManipulator->updateUser($user);
|
||||
|
||||
$output->writeln('<info>password set with hashed pass</info>');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ($dump) {
|
||||
$oldHash = $user->getPassword();
|
||||
$oldNonce = $user->getNonce();
|
||||
}
|
||||
|
||||
if ($generate) {
|
||||
$oldHash = $user->getPassword();
|
||||
$oldNonce = $user->getNonce();
|
||||
|
||||
$password = $this->container['random.medium']->generateString(64);
|
||||
} else {
|
||||
if (!$password && $sendRenewalEmail) {
|
||||
$this->sendPasswordSetupMail($user);
|
||||
$output->writeln('<info>email link sended for password renewing!</info>');
|
||||
|
||||
return 0;
|
||||
} elseif (!$password && !$sendRenewalEmail && ! $dump) {
|
||||
$output->writeln('<error>choose one option to set a password!</error>');
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if ($password) {
|
||||
if (!$yes) {
|
||||
do {
|
||||
$continue = mb_strtolower($dialog->ask($output, '<question>Do you want really set password to this user? (y/N)</question>', 'N'));
|
||||
} while (!in_array($continue, ['y', 'n']));
|
||||
|
||||
if ($continue !== 'y') {
|
||||
$output->writeln('Aborting !');
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
$oldHash = $user->getPassword();
|
||||
$oldNonce = $user->getNonce();
|
||||
|
||||
$userManipulator->setPassword($user,$password);
|
||||
}
|
||||
|
||||
if ($dump) {
|
||||
if ($jsonformat) {
|
||||
$hash['password_hash'] = $oldHash;
|
||||
$hash['nonce'] = $oldNonce;
|
||||
|
||||
echo json_encode($hash);
|
||||
|
||||
return 0;
|
||||
} else {
|
||||
$output->writeln('<info>password_hash :</info>' . $oldHash);
|
||||
$output->writeln('<info>nonce :</info>' . $oldNonce);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (($password || $generate)) {
|
||||
if ($jsonformat) {
|
||||
$hash['new_password'] = $password;
|
||||
$hash['previous_password_hash'] = $oldHash;
|
||||
$hash['previous_nonce'] = $oldNonce;
|
||||
|
||||
echo json_encode($hash);
|
||||
} else {
|
||||
$output->writeln('<info>new_password :</info>' . $password);
|
||||
$output->writeln('<info>previous_password_hash :</info>' . $oldHash);
|
||||
$output->writeln('<info>previous_nonce :</info>' . $oldNonce);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send mail for renew password
|
||||
* @param User $user
|
||||
*/
|
||||
private function sendPasswordSetupMail(User $user)
|
||||
{
|
||||
$this->setDelivererLocator(new LazyLocator($this->container, 'notification.deliverer'));
|
||||
$receiver = Receiver::fromUser($user);
|
||||
|
||||
$token = $this->container['manipulator.token']->createResetPasswordToken($user);
|
||||
|
||||
$url = $this->container['url_generator']->generate('login_renew_password', [ 'token' => $token->getValue() ], true);
|
||||
$mail = MailRequestPasswordUpdate::create($this->container, $receiver);
|
||||
$servername = $this->container['conf']->get('servername');
|
||||
$mail->setButtonUrl($url);
|
||||
$mail->setLogin($user->getLogin());
|
||||
$mail->setExpiration(new \DateTime('+1 day'));
|
||||
|
||||
$this->deliver($mail);
|
||||
}
|
||||
|
||||
}
|
@@ -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'
|
||||
];
|
||||
}
|
||||
|
@@ -13,9 +13,11 @@ namespace Alchemy\Phrasea\Controller\Admin;
|
||||
use Alchemy\Phrasea\Controller\Controller;
|
||||
use Alchemy\Phrasea\SearchEngine\Elastic\ElasticsearchSettingsFormType;
|
||||
use Alchemy\Phrasea\SearchEngine\Elastic\ElasticsearchOptions;
|
||||
use Alchemy\Phrasea\SearchEngine\Elastic\Structure\GlobalStructure;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use databox_descriptionStructure;
|
||||
|
||||
class SearchEngineController extends Controller
|
||||
{
|
||||
@@ -31,7 +33,19 @@ class SearchEngineController extends Controller
|
||||
$form->handleRequest($request);
|
||||
|
||||
if ($form->isValid()) {
|
||||
$this->saveElasticSearchOptions($form->getData());
|
||||
/** @var ElasticsearchOptions $data */
|
||||
$data = $form->getData();
|
||||
// $q = $request->request->get('elasticsearch_settings');
|
||||
$facetNames = []; // rebuild the data "_customValues/facets" list following the form order
|
||||
foreach($request->request->get('elasticsearch_settings') as $name=>$value) {
|
||||
$matches = null;
|
||||
if(preg_match('/^facets:(.+):limit$/', $name, $matches) === 1) {
|
||||
$facetNames[] = $matches[1];
|
||||
}
|
||||
}
|
||||
$data->reorderAggregableFields($facetNames);
|
||||
|
||||
$this->saveElasticSearchOptions($data);
|
||||
|
||||
return $this->app->redirectPath('admin_searchengine_form');
|
||||
}
|
||||
@@ -76,6 +90,16 @@ class SearchEngineController extends Controller
|
||||
*/
|
||||
private function saveElasticSearchOptions(ElasticsearchOptions $configuration)
|
||||
{
|
||||
// save to databoxes fields for backward compatibility (useless ?)
|
||||
foreach($configuration->getAggregableFields() as $fname=>$aggregableField) {
|
||||
foreach ($this->app->getDataboxes() as $databox) {
|
||||
if(!is_null($f = $databox->get_meta_structure()->get_element_by_name($fname, databox_descriptionStructure::STRICT_COMPARE))) {
|
||||
$f->set_aggregable($aggregableField['limit'])->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// save to conf
|
||||
$this->getConf()->set(['main', 'search-engine', 'options'], $configuration->toArray());
|
||||
}
|
||||
|
||||
@@ -85,7 +109,10 @@ class SearchEngineController extends Controller
|
||||
*/
|
||||
private function getConfigurationForm(ElasticsearchOptions $options)
|
||||
{
|
||||
return $this->app->form(new ElasticsearchSettingsFormType(), $options, [
|
||||
/** @var GlobalStructure $g */
|
||||
$g = $this->app['search_engine.structure'];
|
||||
|
||||
return $this->app->form(new ElasticsearchSettingsFormType($g, $options), $options, [
|
||||
'action' => $this->app->url('admin_searchengine_form'),
|
||||
]);
|
||||
}
|
||||
|
@@ -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,21 +373,21 @@ 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",
|
||||
Video::OPTION_GOPSIZE => "25",
|
||||
Video::OPTION_SIZE => "480",
|
||||
Video::OPTION_FRAMERATE => "25",
|
||||
Video::OPTION_VCODEC => "libtheora",
|
||||
Video::OPTION_ACODEC => "libfaac",
|
||||
Video::OPTION_VCODEC => "libx264",
|
||||
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 => "libx264",
|
||||
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"]
|
||||
],
|
||||
],
|
||||
|
@@ -515,9 +515,9 @@ class UserController extends Controller
|
||||
$denyColl[] = $label;
|
||||
$hookData['rejected'][$bas] = $label;
|
||||
}
|
||||
}
|
||||
|
||||
$this->app['manipulator.webhook-event']->create($hookName, $hookType, $hookData);
|
||||
$this->app['manipulator.webhook-event']->create($hookName, $hookType, $hookData, [$bas]);
|
||||
}
|
||||
|
||||
if ($user->hasMailNotificationsActivated() && (0 !== count($acceptColl) || 0 !== count($denyColl))) {
|
||||
$message = '';
|
||||
|
@@ -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;
|
||||
@@ -268,11 +269,13 @@ class Result
|
||||
public function getVersion()
|
||||
{
|
||||
if (null === $this->version) {
|
||||
if($this->request->attributes->get('api_version')){
|
||||
if ($this->request->attributes->get('api_version')) {
|
||||
$this->version = $this->request->attributes->get('api_version');
|
||||
}elseif(mb_strpos($this->request->getPathInfo(), '/api/v1') !== FALSE){
|
||||
} elseif (mb_strpos($this->request->getPathInfo(), '/api/v1') !== FALSE) {
|
||||
$this->version = V1::VERSION;
|
||||
}else{
|
||||
} 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(),
|
||||
@@ -1570,9 +1573,9 @@ class V1Controller extends Controller
|
||||
$options->setFirstResult((int)($request->get('offset_start') ?: 0));
|
||||
$options->setMaxResults((int)$request->get('per_page') ?: 10);
|
||||
|
||||
$this->getSearchEngine()->resetCache();
|
||||
$searchEngine = $this->getSearchEngine();
|
||||
|
||||
$search_result = $this->getSearchEngine()->query((string)$request->get('query'), $options);
|
||||
$search_result = $searchEngine->query((string)$request->get('query'), $options);
|
||||
|
||||
$this->getUserManipulator()->logQuery($this->getAuthenticatedUser(), $search_result->getQueryText());
|
||||
|
||||
@@ -1580,12 +1583,12 @@ class V1Controller extends Controller
|
||||
$collectionsReferencesByDatabox = $options->getCollectionsReferencesByDatabox();
|
||||
foreach ($collectionsReferencesByDatabox as $sbid => $references) {
|
||||
$databox = $this->findDataboxById($sbid);
|
||||
$collectionsIds = array_map(function(CollectionReference $ref){return $ref->getCollectionId();}, $references);
|
||||
$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;
|
||||
}
|
||||
|
||||
|
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);
|
||||
}
|
||||
}
|
||||
}
|
@@ -450,6 +450,51 @@ class LightboxController extends Controller
|
||||
return $this->app->json($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Basket $basket
|
||||
* @return Response
|
||||
*/
|
||||
public function ajaxGetElementsAction(Basket $basket)
|
||||
{
|
||||
$ret = [
|
||||
'error' => false,
|
||||
'datas' => [
|
||||
'counts' => [
|
||||
'yes' => 0,
|
||||
'no' => 0,
|
||||
'nul' => 0,
|
||||
'total' => 0
|
||||
]
|
||||
]
|
||||
];
|
||||
try {
|
||||
if (!$basket->getValidation()) {
|
||||
throw new Exception('There is no validation session attached to this basket');
|
||||
}
|
||||
foreach ($basket->getElements() as $element) {
|
||||
$vd = $element->getUserValidationDatas($this->getAuthenticatedUser());
|
||||
if($vd->getAgreement() === true) {
|
||||
$ret['datas']['counts']['yes']++;
|
||||
}
|
||||
elseif($vd->getAgreement() === false) {
|
||||
$ret['datas']['counts']['no']++;
|
||||
}
|
||||
elseif($vd->getAgreement() === null) {
|
||||
$ret['datas']['counts']['nul']++;
|
||||
}
|
||||
$ret['datas']['counts']['total']++;
|
||||
}
|
||||
}
|
||||
catch (Exception $e) {
|
||||
$ret = [
|
||||
'error' => true,
|
||||
'datas' => $e->getMessage()
|
||||
];
|
||||
}
|
||||
|
||||
return $this->app->json($ret);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Basket $basket
|
||||
* @throws Exception
|
||||
|
@@ -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()
|
||||
|
@@ -15,6 +15,7 @@ use Alchemy\Phrasea\Application\Helper\FilesystemAware;
|
||||
use Alchemy\Phrasea\Application\Helper\NotifierAware;
|
||||
use Alchemy\Phrasea\Controller\Controller;
|
||||
use Alchemy\Phrasea\Core\Event\ExportFailureEvent;
|
||||
use Alchemy\Phrasea\Core\Event\ExportMailEvent;
|
||||
use Alchemy\Phrasea\Core\PhraseaEvents;
|
||||
use Alchemy\Phrasea\Exception\InvalidArgumentException;
|
||||
use Alchemy\Phrasea\Model\Manipulator\TokenManipulator;
|
||||
@@ -193,42 +194,26 @@ class ExportController extends Controller
|
||||
$token = $this->getTokenManipulator()->createEmailExportToken(serialize($list));
|
||||
|
||||
if (count($destMails) > 0) {
|
||||
//zip documents
|
||||
\set_export::build_zip(
|
||||
$this->app,
|
||||
$token,
|
||||
$list,
|
||||
$this->app['tmp.download.path'].'/'. $token->getValue() . '.zip'
|
||||
);
|
||||
$emitterId = $this->getAuthenticatedUser()->getId();
|
||||
|
||||
$remaingEmails = $destMails;
|
||||
$tokenValue = $token->getValue();
|
||||
|
||||
$url = $this->app->url('prepare_download', ['token' => $token->getValue(), 'anonymous' => false, 'type' => \Session_Logger::EVENT_EXPORTMAIL]);
|
||||
|
||||
$user = $this->getAuthenticatedUser();
|
||||
$emitter = new Emitter($user->getDisplayName(), $user->getEmail());
|
||||
$params = [
|
||||
'url' => $url,
|
||||
'textmail' => $request->request->get('textmail'),
|
||||
'reading_confirm' => !!$request->request->get('reading_confirm', false),
|
||||
'ssttid' => $ssttid = $request->request->get('ssttid', ''),
|
||||
'lst' => $lst = $request->request->get('lst', ''),
|
||||
];
|
||||
|
||||
foreach ($destMails as $key => $mail) {
|
||||
try {
|
||||
$receiver = new Receiver(null, trim($mail));
|
||||
} catch (InvalidArgumentException $e) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$mail = MailRecordsExport::create($this->app, $receiver, $emitter, $request->request->get('textmail'));
|
||||
$mail->setButtonUrl($url);
|
||||
$mail->setExpiration($token->getExpiration());
|
||||
|
||||
$this->deliver($mail, !!$request->request->get('reading_confirm', false));
|
||||
unset($remaingEmails[$key]);
|
||||
}
|
||||
|
||||
//some mails failed
|
||||
if (count($remaingEmails) > 0) {
|
||||
foreach ($remaingEmails as $mail) {
|
||||
$this->dispatch(PhraseaEvents::EXPORT_MAIL_FAILURE, new ExportFailureEvent($this->getAuthenticatedUser(), $ssttid, $lst, \eventsmanager_notify_downloadmailfail::MAIL_FAIL, $mail));
|
||||
}
|
||||
}
|
||||
$this->dispatch(PhraseaEvents::EXPORT_MAIL_CREATE, new ExportMailEvent(
|
||||
$emitterId,
|
||||
$tokenValue,
|
||||
$destMails,
|
||||
$params
|
||||
));
|
||||
}
|
||||
|
||||
return $this->app->json([
|
||||
|
@@ -19,8 +19,10 @@ use Alchemy\Phrasea\Core\PhraseaEvents;
|
||||
use Alchemy\Phrasea\Feed\Aggregate;
|
||||
use Alchemy\Phrasea\Feed\Link\AggregateLinkGenerator;
|
||||
use Alchemy\Phrasea\Feed\Link\FeedLinkGenerator;
|
||||
use Alchemy\Phrasea\Model\Entities\Feed;
|
||||
use Alchemy\Phrasea\Model\Entities\FeedEntry;
|
||||
use Alchemy\Phrasea\Model\Entities\FeedItem;
|
||||
use Alchemy\Phrasea\Model\Entities\FeedPublisher;
|
||||
use Alchemy\Phrasea\Model\Repositories\FeedEntryRepository;
|
||||
use Alchemy\Phrasea\Model\Repositories\FeedItemRepository;
|
||||
use Alchemy\Phrasea\Model\Repositories\FeedPublisherRepository;
|
||||
@@ -46,6 +48,7 @@ class FeedController extends Controller
|
||||
}
|
||||
|
||||
public function createFeedEntryAction(Request $request) {
|
||||
/** @var Feed $feed */
|
||||
$feed = $this->getFeedRepository()->find($request->request->get('feed_id'));
|
||||
|
||||
if (null === $feed) {
|
||||
@@ -53,6 +56,8 @@ class FeedController extends Controller
|
||||
}
|
||||
|
||||
$user = $this->getAuthenticatedUser();
|
||||
|
||||
/** @var FeedPublisher $publisher */
|
||||
$publisher = $this->getFeedPublisherRepository()->findOneBy([
|
||||
'feed' => $feed,
|
||||
'user' => $user,
|
||||
|
@@ -136,6 +136,7 @@ class LanguageController
|
||||
'or' => $translator->trans('or'),
|
||||
'Suppr' => $translator->trans('Suppr'),
|
||||
'Add new range' => $translator->trans('Add new range'),
|
||||
'Save as VTT' => $translator->trans('Save as VTT'),
|
||||
'Export ranges' => $translator->trans('Export ranges'),
|
||||
'Start Range' => $translator->trans('Start Range'),
|
||||
'End Range' => $translator->trans('End Range'),
|
||||
|
@@ -70,7 +70,7 @@ class LazaretController extends Controller
|
||||
public function getElement($file_id)
|
||||
{
|
||||
$ret = ['success' => false, 'message' => '', 'result' => []];
|
||||
|
||||
|
||||
/* @var LazaretFile $lazaretFile */
|
||||
$lazaretFile = $this->getLazaretFileRepository()->find($file_id);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
@@ -12,7 +12,7 @@ namespace Alchemy\Phrasea\Controller\Prod;
|
||||
use Alchemy\Phrasea\Application\Helper\DataboxLoggerAware;
|
||||
use Alchemy\Phrasea\Controller\Controller;
|
||||
use Alchemy\Phrasea\Helper\Record as RecordHelper;
|
||||
use Alchemy\Phrasea\Out\Module\PDF as PDFExport;
|
||||
use Alchemy\Phrasea\Out\Module\PDFRecords;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
@@ -24,19 +24,27 @@ class PrinterController extends Controller
|
||||
{
|
||||
$printer = new RecordHelper\Printer($this->app, $request);
|
||||
|
||||
return $this->render('prod/actions/printer_default.html.twig', ['printer' => $printer, 'message' => '']);
|
||||
$basketFeedbackId = null;
|
||||
if($printer->is_basket() && ($basket = $printer->get_original_basket()) && ($validation = $basket->getValidation())) {
|
||||
if($validation->getInitiator()->getId() === $this->app->getAuthenticatedUser()->getId()) {
|
||||
$basketFeedbackId = $basket->getId();
|
||||
}
|
||||
}
|
||||
|
||||
return $this->render('prod/actions/printer_default.html.twig', ['printer' => $printer, 'message' => '', 'basketFeedbackId' => $basketFeedbackId]);
|
||||
}
|
||||
|
||||
public function printAction(Request $request)
|
||||
{
|
||||
$printer = new RecordHelper\Printer($this->app, $request);
|
||||
$b = $printer->get_original_basket();
|
||||
|
||||
$layout = $request->request->get('lay');
|
||||
|
||||
foreach ($printer->get_elements() as $record) {
|
||||
$this->getDataboxLogger($record->getDatabox())->log($record, \Session_Logger::EVENT_PRINT, $layout, '');
|
||||
}
|
||||
$PDF = new PDFExport($this->app, $printer->get_elements(), $layout);
|
||||
$PDF = new PDFRecords($this->app, $printer, $layout);
|
||||
|
||||
$response = new Response($PDF->render(), 200, array('Content-Type' => 'application/pdf'));
|
||||
$response->headers->set('Pragma', 'public', true);
|
||||
@@ -44,4 +52,5 @@ class PrinterController extends Controller
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -98,7 +98,7 @@ class PushController extends Controller
|
||||
$Basket->setUser($user_receiver);
|
||||
$Basket->setPusher($this->getAuthenticatedUser());
|
||||
$Basket->markUnread();
|
||||
|
||||
|
||||
$manager->persist($Basket);
|
||||
|
||||
foreach ($pusher->get_elements() as $element) {
|
||||
@@ -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');
|
||||
|
||||
@@ -604,6 +604,38 @@ class PushController extends Controller
|
||||
);
|
||||
}
|
||||
|
||||
public function updateExpirationAction(Request $request)
|
||||
{
|
||||
$ret = [
|
||||
'success' => false,
|
||||
'message' => $this->app->trans('Unable to save the expiration date')
|
||||
];
|
||||
if (is_null($request->request->get('date'))) {
|
||||
$ret['message'] = $this->app->trans('The provided date is null!');
|
||||
return $this->app->json($ret);
|
||||
}
|
||||
$repository = $this->app['repo.baskets'];
|
||||
$manager = $this->getEntityManager();
|
||||
$manager->beginTransaction();
|
||||
try {
|
||||
$basket = $repository->findUserBasket($request->request->get('basket_id'), $this->app->getAuthenticatedUser(), true);
|
||||
$date = new \DateTime($request->request->get('date') . " 23:59:59");
|
||||
$validation = $basket->getValidation();
|
||||
if (is_null($validation)) {
|
||||
return $this->app->json($ret);
|
||||
}
|
||||
$validation->setExpires($date);
|
||||
$manager->persist($validation);
|
||||
$manager->flush();
|
||||
$manager->commit();
|
||||
$ret['message'] = $this->app->trans('Expiration date successfully updated!');
|
||||
} catch (\Exception $e) {
|
||||
$ret['message'] = $e->getMessage();
|
||||
$manager->rollback();
|
||||
}
|
||||
return $this->app->json($ret);
|
||||
}
|
||||
|
||||
private function formatUser(User $user)
|
||||
{
|
||||
$subtitle = array_filter([$user->getJob(), $user->getCompany()]);
|
||||
@@ -738,4 +770,5 @@ class PushController extends Controller
|
||||
{
|
||||
return $this->app['random.medium'];
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -269,7 +269,7 @@ class QueryController extends Controller
|
||||
|
||||
$infoResult = '<div id="docInfo">'
|
||||
. $this->app->trans('%number% documents<br/>selectionnes', ['%number%' => '<span id="nbrecsel"></span>'])
|
||||
. '<div class="detailed_info_holder"><img src="/assets/common/images/icons/dots.png" class="image-normal"><img src="/assets/common/images/icons/dots-darkgreen-hover.png" class="image-hover">'
|
||||
. '<div class="detailed_info_holder"><img src="/assets/common/images/icons/dots.png" class="image-normal hidden"><img src="/assets/common/images/icons/dots-darkgreen-hover.png" class="image-hover">'
|
||||
. '<div class="detailed_info">
|
||||
<table>
|
||||
<thead>
|
||||
@@ -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']);
|
||||
@@ -424,24 +433,15 @@ class QueryController extends Controller
|
||||
|
||||
// populates facets (aggregates)
|
||||
$facets = [];
|
||||
// $facetClauses = [];
|
||||
foreach ($result->getFacets() as $facet) {
|
||||
$facetName = $facet['name'];
|
||||
|
||||
if(array_key_exists($facetName, $fieldsInfosByName)) {
|
||||
|
||||
$f = $fieldsInfosByName[$facetName];
|
||||
|
||||
$facet['label'] = $f['trans_label'];
|
||||
$facet['labels'] = $f['labels'];
|
||||
$facet['type'] = strtoupper($f['type']) . "-AGGREGATE";
|
||||
$facets[] = $facet;
|
||||
|
||||
// $facetClauses[] = [
|
||||
// 'type' => strtoupper($f['type']) . "-AGGREGATE",
|
||||
// 'field' => $f['field'],
|
||||
// 'facet' => $facet
|
||||
// ];
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -7,6 +7,7 @@
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Alchemy\Phrasea\Controller\Prod;
|
||||
|
||||
use Alchemy\Phrasea\Application;
|
||||
@@ -14,6 +15,8 @@ use Alchemy\Phrasea\Application\Helper\EntityManagerAware;
|
||||
use Alchemy\Phrasea\Application\Helper\SearchEngineAware;
|
||||
use Alchemy\Phrasea\Controller\Controller;
|
||||
use Alchemy\Phrasea\Controller\RecordsRequest;
|
||||
use Alchemy\Phrasea\Core\Event\Record\DeleteEvent;
|
||||
use Alchemy\Phrasea\Core\Event\Record\RecordEvents;
|
||||
use Alchemy\Phrasea\Core\Event\RecordEdit;
|
||||
use Alchemy\Phrasea\Core\PhraseaEvents;
|
||||
use Alchemy\Phrasea\Model\Entities\BasketElement;
|
||||
@@ -22,6 +25,7 @@ use Alchemy\Phrasea\Model\Repositories\StoryWZRepository;
|
||||
use Alchemy\Phrasea\SearchEngine\SearchEngineOptions;
|
||||
use Alchemy\Phrasea\Twig\Fit;
|
||||
use Alchemy\Phrasea\Twig\PhraseanetExtension;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
@@ -34,7 +38,7 @@ class RecordController extends Controller
|
||||
*
|
||||
* @param Request $request
|
||||
*
|
||||
* @return Response
|
||||
* @return \Symfony\Component\HttpFoundation\JsonResponse
|
||||
*/
|
||||
public function getRecord(Request $request)
|
||||
{
|
||||
@@ -192,7 +196,8 @@ class RecordController extends Controller
|
||||
$flatten = (bool)($request->request->get('del_children')) ? RecordsRequest::FLATTEN_YES_PRESERVE_STORIES : RecordsRequest::FLATTEN_NO;
|
||||
$records = RecordsRequest::fromRequest(
|
||||
$this->app,
|
||||
$request,$flatten,
|
||||
$request,
|
||||
$flatten,
|
||||
[\ACL::CANDELETERECORD]
|
||||
);
|
||||
|
||||
@@ -222,19 +227,19 @@ class RecordController extends Controller
|
||||
$manager->remove($attachedStory);
|
||||
}
|
||||
|
||||
foreach($record->get_grouping_parents() as $story) {
|
||||
foreach ($record->get_grouping_parents() as $story) {
|
||||
$this->getEventDispatcher()->dispatch(PhraseaEvents::RECORD_EDIT, new RecordEdit($story));
|
||||
}
|
||||
|
||||
$sbasId = $record->getDatabox()->get_sbas_id();
|
||||
if(!array_key_exists($sbasId, $trashCollectionsBySbasId)) {
|
||||
if (!array_key_exists($sbasId, $trashCollectionsBySbasId)) {
|
||||
$trashCollectionsBySbasId[$sbasId] = $record->getDatabox()->getTrashCollection();
|
||||
}
|
||||
$deleted[] = $record->getId();
|
||||
if($trashCollectionsBySbasId[$sbasId] !== null) {
|
||||
if ($trashCollectionsBySbasId[$sbasId] !== null) {
|
||||
if($record->getCollection()->get_coll_id() == $trashCollectionsBySbasId[$sbasId]->get_coll_id()) {
|
||||
// record is already in trash so delete it
|
||||
$record->delete();
|
||||
$this->getEventDispatcher()->dispatch(RecordEvents::DELETE, new DeleteEvent($record));
|
||||
} else {
|
||||
// move to trash collection
|
||||
$record->move_to_collection($trashCollectionsBySbasId[$sbasId], $this->getApplicationBox());
|
||||
@@ -247,7 +252,7 @@ class RecordController extends Controller
|
||||
}
|
||||
} else {
|
||||
// no trash collection, delete
|
||||
$record->delete();
|
||||
$this->getEventDispatcher()->dispatch(RecordEvents::DELETE, new DeleteEvent($record));
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
}
|
||||
@@ -278,35 +283,69 @@ class RecordController extends Controller
|
||||
* Delete a record or a list of records
|
||||
*
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @return string html
|
||||
*/
|
||||
public function whatCanIDelete(Request $request)
|
||||
{
|
||||
$viewParms = [];
|
||||
|
||||
// pre-count records that would be trashed/deleted when the "deleted children" will be un-checked
|
||||
|
||||
$records = RecordsRequest::fromRequest(
|
||||
$this->app,
|
||||
$request,
|
||||
!!$request->request->get('del_children'),
|
||||
RecordsRequest::FLATTEN_NO,
|
||||
[\ACL::CANDELETERECORD]
|
||||
);
|
||||
|
||||
$filteredRecord = $this->filterRecordToDelete($records);
|
||||
$filteredRecords = $this->filterRecordToDelete($records);
|
||||
|
||||
return $this->app->json([
|
||||
'renderView' => $this->render('prod/actions/delete_records_confirm.html.twig', [
|
||||
'records' => $records,
|
||||
'filteredRecord' => $filteredRecord
|
||||
]),
|
||||
'filteredRecord' => $filteredRecord
|
||||
]);
|
||||
$viewParms['parents_only'] = [
|
||||
'records' => $records,
|
||||
'trashableCount' => count($filteredRecords['trash']),
|
||||
'deletableCount' => count($filteredRecords['delete'])
|
||||
];
|
||||
|
||||
// pre-count records that would be trashed/deleted when the "deleted children" will be checked
|
||||
//
|
||||
$records = RecordsRequest::fromRequest(
|
||||
$this->app,
|
||||
$request,
|
||||
RecordsRequest::FLATTEN_YES_PRESERVE_STORIES,
|
||||
[\ACL::CANDELETERECORD]
|
||||
);
|
||||
$filteredRecords = $this->filterRecordToDelete($records);
|
||||
$viewParms['with_children'] = [
|
||||
'records' => $records,
|
||||
'trashableCount' => count($filteredRecords['trash']),
|
||||
'deletableCount' => count($filteredRecords['delete'])
|
||||
];
|
||||
|
||||
return $this->render(
|
||||
'prod/actions/delete_records_confirm.html.twig',
|
||||
$viewParms
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* classifies records in two groups (does NOT delete anything)
|
||||
* - 'trash' : the record can go to trash because the db has a "_TRASH_" coll, and the record is not already into it
|
||||
* - 'delete' : the record would be deleted because the db has no trash, or the record is already trashed
|
||||
*
|
||||
* @param RecordsRequest $records
|
||||
* @return array
|
||||
*/
|
||||
private function filterRecordToDelete(RecordsRequest $records)
|
||||
{
|
||||
$ret = [
|
||||
'trash' => [],
|
||||
'delete' => []
|
||||
];
|
||||
|
||||
$trashCollectionsBySbasId = [];
|
||||
$goingToTrash = [];
|
||||
$delete = [];
|
||||
foreach ($records as $record) {
|
||||
/** @var \record_adapter $record */
|
||||
$sbasId = $record->getDatabox()->get_sbas_id();
|
||||
if (!array_key_exists($sbasId, $trashCollectionsBySbasId)) {
|
||||
$trashCollectionsBySbasId[$sbasId] = $record->getDatabox()->getTrashCollection();
|
||||
@@ -314,21 +353,20 @@ class RecordController extends Controller
|
||||
if ($trashCollectionsBySbasId[$sbasId] !== null) {
|
||||
if ($record->getCollection()->get_coll_id() == $trashCollectionsBySbasId[$sbasId]->get_coll_id()) {
|
||||
// record is already in trash
|
||||
$delete[] = $record;
|
||||
$ret['delete'][] = $record;
|
||||
}
|
||||
else {
|
||||
// will be moved to trash
|
||||
$goingToTrash[] = $record;
|
||||
$ret['trash'][] = $record;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// trash does not exist
|
||||
$delete[] = $record;
|
||||
$ret['delete'][] = $record;
|
||||
}
|
||||
}
|
||||
//check if all values in array are true
|
||||
//return (!in_array(false, $goingToTrash, true));
|
||||
return ['trash' => $goingToTrash, 'delete' => $delete];
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -336,7 +374,8 @@ class RecordController extends Controller
|
||||
*
|
||||
* @param Request $request
|
||||
*
|
||||
* @return Response
|
||||
* @return \Symfony\Component\HttpFoundation\JsonResponse
|
||||
* @throws \Alchemy\Phrasea\Cache\Exception
|
||||
*/
|
||||
public function renewUrl(Request $request)
|
||||
{
|
||||
|
@@ -14,6 +14,8 @@ use Alchemy\Phrasea\Application\Helper\EntityManagerAware;
|
||||
use Alchemy\Phrasea\Controller\Controller;
|
||||
use Alchemy\Phrasea\Controller\RecordsRequest;
|
||||
use Alchemy\Phrasea\Controller\Exception as ControllerException;
|
||||
use Alchemy\Phrasea\Core\Event\Record\RecordEvents;
|
||||
use Alchemy\Phrasea\Core\Event\Record\SubdefinitionCreateEvent;
|
||||
use Alchemy\Phrasea\Core\Event\RecordEdit;
|
||||
use Alchemy\Phrasea\Core\PhraseaEvents;
|
||||
use Alchemy\Phrasea\Model\Entities\StoryWZ;
|
||||
@@ -68,7 +70,9 @@ class StoryController extends Controller
|
||||
break;
|
||||
}
|
||||
|
||||
$story->set_metadatas($metadatas)->rebuild_subdefs();
|
||||
$recordAdapter = $story->set_metadatas($metadatas);
|
||||
// tell phraseanet to rebuild subdef
|
||||
$this->dispatch(RecordEvents::SUBDEFINITION_CREATE, new SubdefinitionCreateEvent($recordAdapter));
|
||||
|
||||
$storyWZ = new StoryWZ();
|
||||
$storyWZ->setUser($this->getAuthenticatedUser());
|
||||
|
@@ -16,6 +16,7 @@ use Alchemy\Phrasea\Application\Helper\SubDefinitionSubstituerAware;
|
||||
use Alchemy\Phrasea\Controller\Controller;
|
||||
use Alchemy\Phrasea\Controller\RecordsRequest;
|
||||
use Alchemy\Phrasea\Core\Event\Record\RecordEvents;
|
||||
use Alchemy\Phrasea\Core\Event\Record\SubdefinitionCreateEvent;
|
||||
use Alchemy\Phrasea\Exception\RuntimeException;
|
||||
use Alchemy\Phrasea\Metadata\PhraseanetMetadataReader;
|
||||
use Alchemy\Phrasea\Metadata\PhraseanetMetadataSetter;
|
||||
@@ -156,7 +157,7 @@ class ToolsController extends Controller
|
||||
}
|
||||
|
||||
if (!$substituted || $force) {
|
||||
$record->rebuild_subdefs();
|
||||
$this->dispatch(RecordEvents::SUBDEFINITION_CREATE, new SubdefinitionCreateEvent($record));
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -181,8 +181,8 @@ class UploadController extends Controller
|
||||
$uploadedFilename = $file->getRealPath();
|
||||
$renamedFilename = null;
|
||||
|
||||
if(!empty($this->app['conf']->get(['main', 'storage', 'tmp_files']))) {
|
||||
$tmpStorage = \p4string::addEndSlash($this->app['conf']->get(['main', 'storage', 'tmp_files'])).'upload/';
|
||||
if(!empty($this->app['conf']->get(['main', 'storage', 'worker_tmp_files']))) {
|
||||
$tmpStorage = \p4string::addEndSlash($this->app['conf']->get(['main', 'storage', 'worker_tmp_files'])).'upload/';
|
||||
|
||||
if(!is_dir($tmpStorage)){
|
||||
$this->getFilesystem()->mkdir($tmpStorage);
|
||||
|
@@ -11,6 +11,7 @@
|
||||
|
||||
namespace Alchemy\Phrasea\Controller;
|
||||
|
||||
use Alchemy\Phrasea\Model\Converter\BasketConverter;
|
||||
use Alchemy\Phrasea\Model\Entities\Basket;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Alchemy\Phrasea\Application;
|
||||
@@ -21,6 +22,7 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
class RecordsRequest extends ArrayCollection
|
||||
{
|
||||
protected $isSingleStory = false;
|
||||
protected $rejected;
|
||||
protected $received;
|
||||
protected $basket;
|
||||
protected $databoxes;
|
||||
@@ -31,30 +33,40 @@ class RecordsRequest extends ArrayCollection
|
||||
const FLATTEN_YES_PRESERVE_STORIES = 'preserve';
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* RecordsRequest Constructor
|
||||
*
|
||||
* @param array $elements
|
||||
* @param ArrayCollection $rejected
|
||||
* @param ArrayCollection $received
|
||||
* @param Basket $basket
|
||||
* @param Basket|null $basket
|
||||
* @param Boolean $flatten
|
||||
*/
|
||||
public function __construct(array $elements, ArrayCollection $received, Basket $basket = null, $flatten = self::FLATTEN_NO)
|
||||
public function __construct(array $elements, ArrayCollection $rejected, ArrayCollection $received, Basket $basket = null, $flatten = self::FLATTEN_NO)
|
||||
{
|
||||
parent::__construct($elements);
|
||||
$this->received = $received;
|
||||
$this->rejected = $rejected;
|
||||
$this->basket = $basket;
|
||||
$this->isSingleStory = ($flatten !== self::FLATTEN_YES && 1 === count($this) && $this->first()->isStory());
|
||||
// since stories are already flattened by "fromRequest" (to apply rights on children),
|
||||
// flagging "isSingleStory" is a bit more difficult than checking the first item...
|
||||
// $this->isSingleStory = ($flatten !== self::FLATTEN_YES && count($this) === 1 && $this->first()->isStory());
|
||||
|
||||
if (self::FLATTEN_NO !== $flatten) {
|
||||
if ($flatten !== self::FLATTEN_NO) {
|
||||
$to_remove = [];
|
||||
/** @var record_adapter $record */
|
||||
foreach ($this as $key => $record) {
|
||||
if ($record->isStory()) {
|
||||
if (self::FLATTEN_YES === $flatten) {
|
||||
if ($flatten === self::FLATTEN_YES) {
|
||||
// simple flatten : remove the story
|
||||
$to_remove[] = $key;
|
||||
}
|
||||
foreach ($record->getChildren() as $child) {
|
||||
$this->set($child->getId(), $child);
|
||||
|
||||
try {
|
||||
foreach ($record->getChildren() as $child) {
|
||||
$this->set($child->getId(), $child);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
// getChildren will no fail since record IS a story
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -64,11 +76,39 @@ class RecordsRequest extends ArrayCollection
|
||||
}
|
||||
}
|
||||
|
||||
// We check that the list contains only one story, and that every other items (records) are children of this story
|
||||
// Too bad : there is no "isChildOf" method :(
|
||||
$rec = [];
|
||||
$children = [];
|
||||
$this->isSingleStory = false;
|
||||
|
||||
$i = 0;
|
||||
$records = $this->toArray();
|
||||
array_walk($records, function (\record_adapter $record) use (&$i) {
|
||||
foreach ($this as $key => $record) {
|
||||
if($record->isStory()) {
|
||||
if($this->isSingleStory) {
|
||||
// we already found a story, we cannot have 2, game over
|
||||
$this->isSingleStory = false;
|
||||
break;
|
||||
}
|
||||
$this->isSingleStory = true;
|
||||
foreach ($record->getChildren() as $child) {
|
||||
$children[$child->getId()] = 1; // to later find by key
|
||||
}
|
||||
}
|
||||
else {
|
||||
$rec[] = $record->getId();
|
||||
}
|
||||
$record->setNumber($i++);
|
||||
});
|
||||
}
|
||||
if($this->isSingleStory) {
|
||||
foreach ($rec as $rid) {
|
||||
if(!array_key_exists($rid, $children)) {
|
||||
// one record is not a child, game over
|
||||
$this->isSingleStory = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -106,7 +146,7 @@ class RecordsRequest extends ArrayCollection
|
||||
/** @var \record_adapter $record */
|
||||
foreach ($this as $record) {
|
||||
if (! isset($this->collections[$record->getBaseId()])) {
|
||||
$this->collections[$record->getBaseId()] = $record->get_collection();
|
||||
$this->collections[$record->getBaseId()] = $record->getCollection();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,6 +166,16 @@ class RecordsRequest extends ArrayCollection
|
||||
return $this->received;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all rejected records
|
||||
*
|
||||
* @return \record_adapter[]|ArrayCollection
|
||||
*/
|
||||
public function rejected()
|
||||
{
|
||||
return $this->rejected;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return basket entity if provided, null otherwise
|
||||
*
|
||||
@@ -201,15 +251,18 @@ class RecordsRequest extends ArrayCollection
|
||||
* @param boolean $flattenStories
|
||||
* @param array $rightsColl
|
||||
* @param array $rightsDatabox
|
||||
* @return RecordsRequest|\record_adapter[]
|
||||
* @return RecordsRequest
|
||||
* @throws \Alchemy\Phrasea\Cache\Exception
|
||||
*/
|
||||
public static function fromRequest(Application $app, Request $request, $flattenStories = self::FLATTEN_NO, array $rightsColl = [], array $rightsDatabox = [])
|
||||
{
|
||||
$elements = $received = [];
|
||||
$received = [];
|
||||
$basket = null;
|
||||
|
||||
if ($request->get('ssel')) {
|
||||
$basket = $app['converter.basket']->convert($request->get('ssel'));
|
||||
/** @var BasketConverter $basketConverter */
|
||||
$basketConverter = $app['converter.basket'];
|
||||
$basket = $basketConverter->convert($request->get('ssel'));
|
||||
$app['acl.basket']->hasAccess($basket, $app->getAuthenticatedUser());
|
||||
|
||||
foreach ($basket->getElements() as $basket_element) {
|
||||
@@ -240,35 +293,56 @@ class RecordsRequest extends ArrayCollection
|
||||
}
|
||||
}
|
||||
|
||||
// fill an array with records from flattened stories
|
||||
$elements = $received;
|
||||
|
||||
$to_remove = [];
|
||||
|
||||
foreach ($elements as $id => $record) {
|
||||
if (!$app->getAclForUser($app->getAuthenticatedUser())->has_access_to_record($record)) {
|
||||
$to_remove[] = $id;
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($rightsColl as $right) {
|
||||
if (!$app->getAclForUser($app->getAuthenticatedUser())->has_right_on_base($record->get_base_id(), $right)) {
|
||||
$to_remove[] = $id;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($rightsDatabox as $right) {
|
||||
if (!$app->getAclForUser($app->getAuthenticatedUser())->has_right_on_sbas($record->get_sbas_id(), $right)) {
|
||||
$to_remove[] = $id;
|
||||
continue;
|
||||
if ($flattenStories !== self::FLATTEN_NO) {
|
||||
/** @var record_adapter $record */
|
||||
foreach ($received as $key => $record) {
|
||||
if ($record->isStory()) {
|
||||
if ($flattenStories === self::FLATTEN_YES) {
|
||||
// simple flatten : remove the story from elements
|
||||
unset($elements[$key]);
|
||||
}
|
||||
foreach ($record->getChildren() as $child) {
|
||||
$elements[$child->getId()] = $child;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($to_remove as $id) {
|
||||
unset($elements[$id]);
|
||||
// apply rights filter, remove from elements if no rights
|
||||
$rejected = [];
|
||||
$acl = $app->getAclForUser($app->getAuthenticatedUser());
|
||||
foreach ($elements as $key => $record) {
|
||||
// any false or unknown right will throw exception and the record will be rejected
|
||||
try {
|
||||
if (!$acl->has_access_to_record($record)) {
|
||||
throw new \Exception();
|
||||
}
|
||||
|
||||
foreach ($rightsColl as $right) {
|
||||
if (!$acl->has_right_on_base($record->getBaseId(), $right)) {
|
||||
throw new \Exception();
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($rightsDatabox as $right) {
|
||||
if (!$acl->has_right_on_sbas($record->getDataboxId(), $right)) {
|
||||
throw new \Exception();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
$rejected[$key] = $record;
|
||||
}
|
||||
}
|
||||
// remove rejected from elements
|
||||
foreach ($rejected as $key => $record) {
|
||||
unset($elements[$key]);
|
||||
}
|
||||
|
||||
return new static($elements, new ArrayCollection($received), $basket, $flattenStories);
|
||||
// flattening is already done
|
||||
return new static($elements, new ArrayCollection($rejected), new ArrayCollection($received), $basket, self::FLATTEN_NO);
|
||||
}
|
||||
}
|
||||
|
@@ -458,9 +458,9 @@ class AccountController extends Controller
|
||||
->setZipCode($request->request->get("form_zip"))
|
||||
->setPhone($request->request->get("form_phone"))
|
||||
->setFax($request->request->get("form_fax"))
|
||||
->setJob($request->request->get("form_activity"))
|
||||
->setJob($request->request->get("form_function"))
|
||||
->setCompany($request->request->get("form_company"))
|
||||
->setPosition($request->request->get("form_function"))
|
||||
->setPosition($request->request->get("form_activity"))
|
||||
->setNotifications((Boolean) $request->request->get("mail_notifications"));
|
||||
|
||||
$service->updateAccount($command);
|
||||
@@ -518,7 +518,9 @@ class AccountController extends Controller
|
||||
$this->getApiApplicationManipulator()->deleteApiApplications($applications);
|
||||
|
||||
|
||||
// revoke access and delete phraseanet user account
|
||||
// get list of old granted base_id then revoke access and delete phraseanet user account
|
||||
|
||||
$oldGrantedBaseIds = array_keys($this->app->getAclForUser($user)->get_granted_base());
|
||||
|
||||
$list = array_keys($this->app['repo.collections-registry']->getBaseIdMap());
|
||||
|
||||
@@ -542,8 +544,9 @@ class AccountController extends Controller
|
||||
$mail = null;
|
||||
}
|
||||
|
||||
$this->app['manipulator.user']->delete($user);
|
||||
$mail = MailSuccessAccountDelete::create($this->app, $receiver);
|
||||
|
||||
$this->app['manipulator.user']->delete($user, [$user->getId() => $oldGrantedBaseIds]);
|
||||
if($mail) {
|
||||
$this->deliver($mail);
|
||||
}
|
||||
|
@@ -174,7 +174,9 @@ class SetupController extends Controller
|
||||
$email = $request->request->get('email');
|
||||
$password = $request->request->get('password');
|
||||
$template = $request->request->get('db_template');
|
||||
$dataPath = $request->request->get('datapath_noweb');
|
||||
$storagePath = [
|
||||
'subdefs' => $request->request->get('datapath_noweb')
|
||||
];
|
||||
|
||||
try {
|
||||
$installer = $this->app['phraseanet.installer'];
|
||||
@@ -193,7 +195,7 @@ class SetupController extends Controller
|
||||
$binaryData[$key] = $path;
|
||||
}
|
||||
|
||||
$user = $installer->install($email, $password, $abConn, $servername, $dataPath, $dbConn, $template, $binaryData);
|
||||
$user = $installer->install($email, $password, $abConn, $servername, $storagePath, $dbConn, $template, $binaryData);
|
||||
|
||||
$this->app->getAuthenticator()->openAccount($user);
|
||||
|
||||
|
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;
|
||||
}
|
||||
}
|
@@ -105,6 +105,11 @@ class Lightbox implements ControllerProviderInterface, ServiceProviderInterface
|
||||
->assert('basket', '\d+')
|
||||
;
|
||||
|
||||
$controllers->get('/ajax/GET_ELEMENTS/{basket}/', 'controller.lightbox:ajaxGetElementsAction')
|
||||
->bind('lightbox_ajax_get_elements')
|
||||
->assert('basket', '\d+')
|
||||
;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@@ -59,6 +59,9 @@ class Push implements ControllerProviderInterface, ServiceProviderInterface
|
||||
$controllers->post('/validate/', 'controller.prod.push:validateAction')
|
||||
->bind('prod_push_validate');
|
||||
|
||||
$controllers->post('/update-expiration/', 'controller.prod.push:updateExpirationAction')
|
||||
->bind('prod_push_do_update_expiration');
|
||||
|
||||
$controllers->get('/user/{usr_id}/', 'controller.prod.push:getUserAction')
|
||||
->assert('usr_id', '\d+');
|
||||
|
||||
|
@@ -20,6 +20,7 @@ class DisplaySettingService
|
||||
const ORDER_BY_ADMIN = "ORDER_BY_ADMIN";
|
||||
const ORDER_BY_BCT = "ORDER_BY_BCT";
|
||||
const ORDER_BY_HITS = "ORDER_BY_HITS";
|
||||
const ORDER_BY_HITS_ASC = "ORDER_BY_HITS_ASC";
|
||||
|
||||
/**
|
||||
* The default user settings.
|
||||
@@ -31,7 +32,7 @@ class DisplaySettingService
|
||||
'images_per_page' => '20',
|
||||
'images_size' => '120',
|
||||
'editing_images_size' => '134',
|
||||
'editing_top_box' => '180px',
|
||||
'editing_top_box' => '120px',
|
||||
'editing_right_box' => '400px',
|
||||
'editing_left_box' => '710px',
|
||||
'basket_sort_field' => 'name',
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user