Merge branch 'master' into PHRAS-2184-list-manager-email-domain-filter
@@ -15,7 +15,7 @@ jobs:
|
|||||||
CIRCLE_TEST_REPORTS: /tmp/circleci-test-results
|
CIRCLE_TEST_REPORTS: /tmp/circleci-test-results
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/build-image:ubuntu-14.04-XXL-upstart-1189-5614f37
|
- image: circleci/build-image:ubuntu-14.04-XXL-upstart-1189-5614f37
|
||||||
command: /sbin/init
|
- image: circleci/rabbitmq:3.7.7
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
- run: mkdir -p $CIRCLE_ARTIFACTS $CIRCLE_TEST_REPORTS
|
- run: mkdir -p $CIRCLE_ARTIFACTS $CIRCLE_TEST_REPORTS
|
||||||
@@ -24,10 +24,7 @@ jobs:
|
|||||||
command: nvm install v10.12.0 && nvm alias default v10.12.0
|
command: nvm install v10.12.0 && nvm alias default v10.12.0
|
||||||
- run:
|
- run:
|
||||||
working_directory: ~/alchemy-fr/Phraseanet
|
working_directory: ~/alchemy-fr/Phraseanet
|
||||||
command: 'sudo service memcached status || sudo service memcached start; sudo
|
command: 'sudo service mysql status || sudo service mysql start;'
|
||||||
redis-cli ping >/dev/null 2>&1 || sudo service redis-server start; sudo
|
|
||||||
service mysql status || sudo service mysql start; sudo service rabbitmq-server
|
|
||||||
status || sudo service rabbitmq-server start; '
|
|
||||||
# Dependencies
|
# Dependencies
|
||||||
# This would typically go in either a build or a build-and-test job when using workflows
|
# This would typically go in either a build or a build-and-test job when using workflows
|
||||||
# Restore the dependency cache
|
# Restore the dependency cache
|
||||||
@@ -124,7 +121,7 @@ workflows:
|
|||||||
context: "AWS London"
|
context: "AWS London"
|
||||||
create-repo: true
|
create-repo: true
|
||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
extra-build-args: "--target phraseanet"
|
extra-build-args: "--target phraseanet-fpm"
|
||||||
region: AWS_DEFAULT_REGION
|
region: AWS_DEFAULT_REGION
|
||||||
repo: "${AWS_RESOURCE_NAME_PREFIX}/phraseanet"
|
repo: "${AWS_RESOURCE_NAME_PREFIX}/phraseanet"
|
||||||
tag: "alpha-0.1"
|
tag: "alpha-0.1"
|
||||||
@@ -139,3 +136,14 @@ workflows:
|
|||||||
region: AWS_DEFAULT_REGION
|
region: AWS_DEFAULT_REGION
|
||||||
repo: "${AWS_RESOURCE_NAME_PREFIX}/phraseanet-nginx"
|
repo: "${AWS_RESOURCE_NAME_PREFIX}/phraseanet-nginx"
|
||||||
tag: "alpha-0.1"
|
tag: "alpha-0.1"
|
||||||
|
- aws-ecr/build_and_push_image:
|
||||||
|
account-url: AWS_ACCOUNT_URL
|
||||||
|
aws-access-key-id: AWS_ACCESS_KEY_ID
|
||||||
|
aws-secret-access-key: AWS_SECRET_ACCESS_KEY
|
||||||
|
context: "AWS London"
|
||||||
|
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"
|
||||||
|
|||||||
140
CHANGELOG.md
@@ -1,14 +1,140 @@
|
|||||||
# CHANGELOG
|
# CHANGELOG
|
||||||
|
|
||||||
|
## 4.0.9
|
||||||
|
|
||||||
## 4.0.0 (xxxx-xx-xx)
|
### Adds
|
||||||
|
|
||||||
- Convert Orders custom adapter to Doctrine entity.
|
- PHRAS-2535 - Back / Front - Unsubscription: It's now possible to request a validation by email to delete a Phraseanet user account.
|
||||||
- Convert Feeds custom adapter to Doctrine entity.
|
- PHRAS-2480 - Back / Front - It's now possible to add a user model as order manager on a collection:All users with this model applied can manage orders on this collection. This features fixes an issue when users is provided by SAML and the orders manager is lost when user logs in.
|
||||||
- Convert Users custom adapter to Doctrine entity.
|
- PHRAS-2474 - Back / front. - Searched terms are now found even if the searched terms are split in Business Field and regular Field.
|
||||||
- Convert Ftp Export custom adapter to Doctrine entity.
|
- PHRAS-2462 - Front - Share media on LinkedIn as you can do on Facebook, Twitter.
|
||||||
- Convert Ftp Export custom adapter to Doctrine entity.
|
- PHRAS-2417 - Front - Skin: grey and white, graphic enhancements.
|
||||||
- Session management is now part of Phraseanet configuration.
|
- PHRAS-2067 - Front - Introducing thumbnail & preview generic images for Fonts
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
|
||||||
|
* PHRAS-2491 - Front - Click on facets title (expand/collapse) launched a bad query, due to jquery error.
|
||||||
|
* PHRAS-2510 - Front - Facets values appear Truncated after 15th character.
|
||||||
|
* PHRAS-2153 - Front - No user search possible with the field "Company" and field "Country".
|
||||||
|
* PHRAS-2154 - Front - Bug on Chrome only - selected 1 document instead of all for the feedback.
|
||||||
|
* PHRAS-2538 - Back - Some MP4 files were not correctly detected by Phraseanet.
|
||||||
|
|
||||||
|
## 4.0.8
|
||||||
|
|
||||||
|
### Adds:
|
||||||
|
|
||||||
|
- Upload: Distant files can be added via their URL in GUI and by API. Phraseanet downloads the file before archiving it.
|
||||||
|
- Search optimisation when searching in full text, there was a problem when the query mixed different types of fields.
|
||||||
|
- Search optimisation, it’s now possible to search a partial date in full text.
|
||||||
|
- Populate optimisation, now populating time: 3 times faster.
|
||||||
|
- It is now possible to migrate from 3.1 3.0 version to 4.X, without an intermediate step in 3.8.Fix:
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
|
||||||
|
- Search filter were not taken into account due to a bug in JS.
|
||||||
|
- Overlay title: In this field, text was repeated twice if : one or several words were highlighted in the field, and if the title contained more than 102 characters.
|
||||||
|
- List Manager: it was impossible to add users in the list manager after page 3.
|
||||||
|
- List of fields was not refreshed in the exported fields section.
|
||||||
|
- Push and Feedback fix error when adding a user when Geonames was not set (null value in Geonames).
|
||||||
|
|
||||||
|
## 4.0.7
|
||||||
|
|
||||||
|
### Adds:
|
||||||
|
|
||||||
|
- Advanced search refacto
|
||||||
|
- Thesaurus search is now in strict mode
|
||||||
|
- Refactoring of report module
|
||||||
|
- Refactoring query storage and changing strategy for field search restriction
|
||||||
|
- It is now possible to search for terms in thesaurus and candidates in all languages, not only on the login language
|
||||||
|
- Enhancements on archive task
|
||||||
|
- Graphic enhancements for menu and icons
|
||||||
|
- Video file enhancement, support of MXF container
|
||||||
|
- Extraction of a video soundtrack (MP3, MP4, WAVE, etc.)
|
||||||
|
- For Office Documents, all generated subviews will be PDF assets by default. The flexpaper preview still exists but will be optional.
|
||||||
|
- In Prod Gui, there will be 5 facets but the possibility to view more.
|
||||||
|
|
||||||
|
### Fixes:
|
||||||
|
|
||||||
|
- Quarantine: Fix for the “Substitute” action: alert when selection is empty
|
||||||
|
- Quarantine: File name with a special character can’t be added
|
||||||
|
- Fix for the Adobe CC default token
|
||||||
|
- XSS vulnerabilities in Prod, Admin & Lightbox. Many thanks to Kris (@HV_hat_)
|
||||||
|
- PDF containing (XMP-xmp:PageImage) fails generating subview
|
||||||
|
- MIME types are trucated
|
||||||
|
-Vagrant dev environment fix
|
||||||
|
- Feedback: Sort assets “Order by best choice” has no effect
|
||||||
|
|
||||||
|
## 4.0.3
|
||||||
|
|
||||||
|
### Adds:
|
||||||
|
|
||||||
|
- Prod: For a record, show the current day in the statistics section of the detailed view.
|
||||||
|
- Prod: Store state (open or closed) of facet answer. eg: Database or collection, store in session.
|
||||||
|
- Admin: Access to scheduler and task local menu when parameter is set to false in .yml configuration.
|
||||||
|
- Prod: Database, collection and document type facets are fixed on top
|
||||||
|
- Prod: Better rendering for values of exposure, shutter speed and flash status in facets. eg for shutter speed: 1/30 instead of 0,0333333.
|
||||||
|
- Versions 4 are now compliant with the Phraseanet plugins for Adobe CC Suite.
|
||||||
|
- White list mode: extending autoregistration and adding wildcard access condition by mail domain. Automatically grant access to a user according to the email entered in the request.
|
||||||
|
- Find your documents from the colors in the facets (AI plugin)
|
||||||
|
- Generate a PDF from a Word document or a picture, it’s now possible to define a pdf subview type
|
||||||
|
- Specify a temporary work repository for building video subdefs, to accelerate video generation.
|
||||||
|
|
||||||
|
### Fixes:
|
||||||
|
|
||||||
|
- Prod: In Upload, correct status are not loaded
|
||||||
|
- Prod:Arrow keys navigation adds last selected facet as filter
|
||||||
|
- Admin:Subdef presets, sizes and bitrates (bits/s) not OK
|
||||||
|
- Admin: App error on loading in French due to a simple quote
|
||||||
|
- Prod: Deletion message is not fully readable when deleting a story
|
||||||
|
- Fixing highlight with Elasticsearch for full text only, not for the thesaurus
|
||||||
|
- 500 error at the first authentication for a user with the SAML Phraseanet pluginDev
|
||||||
|
- Dev: Fix API version returned in answer
|
||||||
|
- Dev: Fix vagrant provisioning for Windows
|
||||||
|
|
||||||
|
## 4.0.2
|
||||||
|
|
||||||
|
### Adds:
|
||||||
|
|
||||||
|
- Prod: Message Improv, when selected records are in Trash and another one.
|
||||||
|
- Prod: alt-click on active facets (filter) to invert it.
|
||||||
|
- Prod: do not erase facets in filter when returning 0 answers.
|
||||||
|
- Core: Add preference to authorize user connection without an email
|
||||||
|
- Core: Add preference to set default validity period of download link
|
||||||
|
|
||||||
|
### Fixes:
|
||||||
|
|
||||||
|
- Thesaurus: 0 character terms are blocked
|
||||||
|
- Admin: fix action create and drop index from elasticsearch
|
||||||
|
- Prod: Fix advanced sarch: no filters possible on fields using IE
|
||||||
|
- Prod: 500 error in publication reader when record is missing (deleted from db)Unit test: fix error in Json serialization for custom link
|
||||||
|
- Prod: fix field list in advanced search with Edge browser
|
||||||
|
- Upload: fix 500 error when missing collection
|
||||||
|
- Install wizard: fix error in graphical installer
|
||||||
|
|
||||||
|
## 4.0.0
|
||||||
|
|
||||||
|
### Adds:
|
||||||
|
|
||||||
|
#### Phraseanet gets a new search engine: Elasticsearch
|
||||||
|
- Faceted navigation enables to create a “mapping” of the response. Browse in a very intuitive way by creating several associations of filters. Facets can be used on the databases, collections, documentary fields and technical data.
|
||||||
|
- Speed of processing search and results display has been improved
|
||||||
|
- Possibility to use Kibana (open source visualization plugin for Elasticsearch)
|
||||||
|
|
||||||
|
#### API enhancement
|
||||||
|
- New API routes are available (orders, facets, quarantine)
|
||||||
|
- Enhancement of new, faster routes
|
||||||
|
|
||||||
|
#### Redesign of the Prod interface
|
||||||
|
- Enhanced, redesigned ergonomics: the detailed view windows; redesign of the workzone (baskets and stories, facets, webgalleries)
|
||||||
|
- New white and grey skins are now available
|
||||||
|
- New order manager
|
||||||
|
|
||||||
|
#### Other
|
||||||
|
- Permalinks sharing: activate/deactivate sharing links for the document and sub resolutions
|
||||||
|
- New: the applicative trash: you can now define a collection named _TRASH_. Then, all deleted records from collections (except from Trash) go to the Trash collection. Permalinks on subdefs are deactivated. When you delete a record from the Trash collection, it is permanently deleted. When you move a record from the Trash collection to another, the permalinks are reactivated.
|
||||||
|
- Rewriting of the task scheduler based on the web sockets
|
||||||
|
- Quarantine enhancement
|
||||||
|
- Drag and drop upload
|
||||||
|
|
||||||
## 3.8.8 (2015-12-02)
|
## 3.8.8 (2015-12-02)
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ RUN apt-get update \
|
|||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
RUN php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" \
|
RUN php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" \
|
||||||
&& php -r "if (hash_file('sha384', 'composer-setup.php') === '48e3236262b34d30969dca3c37281b3b4bbe3221bda826ac6a9a62d6444cdb0dcd0615698a5cbe587c3f0fe57a54d8f5') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;" \
|
&& 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 composer-setup.php --install-dir=/usr/local/bin --filename=composer \
|
||||||
&& php -r "unlink('composer-setup.php');"
|
&& php -r "unlink('composer-setup.php');"
|
||||||
|
|
||||||
@@ -88,7 +88,7 @@ COPY templates /var/alchemy/templates
|
|||||||
COPY tests /var/alchemy/tests
|
COPY tests /var/alchemy/tests
|
||||||
|
|
||||||
# Phraseanet
|
# Phraseanet
|
||||||
FROM php:7.0-fpm-stretch as phraseanet
|
FROM php:7.0-fpm-stretch as phraseanet-fpm
|
||||||
RUN apt-get update \
|
RUN apt-get update \
|
||||||
&& apt-get install -y \
|
&& apt-get install -y \
|
||||||
apt-transport-https \
|
apt-transport-https \
|
||||||
@@ -152,6 +152,10 @@ WORKDIR /var/alchemy/Phraseanet
|
|||||||
ENTRYPOINT ["/phraseanet-entrypoint.sh"]
|
ENTRYPOINT ["/phraseanet-entrypoint.sh"]
|
||||||
CMD ["/boot.sh"]
|
CMD ["/boot.sh"]
|
||||||
|
|
||||||
|
# phraseanet-worker
|
||||||
|
FROM phraseanet-fpm as phraseanet-worker
|
||||||
|
CMD ["/worker-boot.sh"]
|
||||||
|
|
||||||
# phraseanet-nginx
|
# phraseanet-nginx
|
||||||
FROM nginx:1.15 as phraseanet-nginx
|
FROM nginx:1.15 as phraseanet-nginx
|
||||||
RUN useradd -u 1000 app
|
RUN useradd -u 1000 app
|
||||||
|
|||||||
34
README.md
@@ -10,6 +10,10 @@ Phraseanet 4.1 - Digital Asset Management application
|
|||||||
- Elasticsearch search engine
|
- Elasticsearch search engine
|
||||||
- Multiple resolution assets generation
|
- Multiple resolution assets generation
|
||||||
|
|
||||||
|
# License :
|
||||||
|
|
||||||
|
Phraseanet is licensed under GPL-v3 license.
|
||||||
|
|
||||||
# Documentation :
|
# Documentation :
|
||||||
|
|
||||||
https://docs.phraseanet.com/
|
https://docs.phraseanet.com/
|
||||||
@@ -31,34 +35,42 @@ https://www.phraseanet.com/download/
|
|||||||
# Development :
|
# Development :
|
||||||
|
|
||||||
For development purpose Phraseanet is shipped with ready to use development environments using vagrant.
|
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.
|
||||||
|
|
||||||
- git clone
|
git clone
|
||||||
- vagrant up
|
vagrant up --provision
|
||||||
|
|
||||||
|
then, a prompt allow you to choose PHP version, and another one to choose a complete build or an Alchemy prebuilt boxes.
|
||||||
|
|
||||||
|
Ex:
|
||||||
|
- vagrant up --provision //// 5.6 ///// 1 >> Build an ubuntu/xenial box with php5.6
|
||||||
|
- vagrant up --provision //// 7.0 ///// 1 >> Build an ubuntu/xenial with php7.0
|
||||||
|
- vagrant up --provision //// 7.2 ///// 2 >> Build the alchemy/phraseanet-php-7.2 box
|
||||||
|
- 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
|
For development with Phraseanet API see https://docs.phraseanet.com/4.0/en/Devel/index.html
|
||||||
|
|
||||||
# License :
|
|
||||||
|
|
||||||
Phraseanet is licensed under GPL-v3 license.
|
|
||||||
|
|
||||||
|
|
||||||
# Docker build
|
# Docker build
|
||||||
|
|
||||||
WARNING : still in a work-in-progress status and can be used only for test purposes.
|
WARNING : still in a work-in-progress status and can be used only for test purposes.
|
||||||
|
|
||||||
The docker distribution come with 2 differents containers :
|
The docker distribution come with 3 differents containers :
|
||||||
* an nginx that act as the front http server.
|
* An nginx that act as the front http server.
|
||||||
* the php-fpm who serves the php files through nginx.
|
* The php-fpm who serves the php files through nginx.
|
||||||
|
* The worker who execute Phraseanet scheduler.
|
||||||
|
|
||||||
## How to build
|
## How to build
|
||||||
|
|
||||||
The two images can be built respectively with these two commands :
|
The three images can be built respectively with these commands :
|
||||||
|
|
||||||
# nginx server
|
# nginx server
|
||||||
docker build --target phraseanet-nginx -t local/phraseanet-nginx .
|
docker build --target phraseanet-nginx -t local/phraseanet-nginx .
|
||||||
|
|
||||||
# php-fpm application
|
# php-fpm application
|
||||||
docker build --target phraseanet -t local/phraseanet .
|
docker build --target phraseanet-fpm -t local/phraseanet-fpm .
|
||||||
|
|
||||||
|
# worker
|
||||||
|
docker build --target phraseanet-worker -t local/phraseanet-worker .
|
||||||
|
|
||||||
|
|||||||
110
Vagrantfile
vendored
@@ -1,16 +1,6 @@
|
|||||||
Vagrant.require_version ">= 1.5"
|
Vagrant.require_version ">= 1.5"
|
||||||
|
require 'json'
|
||||||
|
|
||||||
class MyCustomError < StandardError
|
|
||||||
attr_reader :code
|
|
||||||
|
|
||||||
def initialize(code)
|
|
||||||
@code = code
|
|
||||||
end
|
|
||||||
|
|
||||||
def to_s
|
|
||||||
"[#{code} #{super}]"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
# Check to determine whether we're on a windows or linux/os-x host,
|
# Check to determine whether we're on a windows or linux/os-x host,
|
||||||
# later on we use this to launch ansible in the supported way
|
# later on we use this to launch ansible in the supported way
|
||||||
# source: https://stackoverflow.com/questions/2108727/which-in-ruby-checking-if-program-exists-in-path-from-ruby
|
# source: https://stackoverflow.com/questions/2108727/which-in-ruby-checking-if-program-exists-in-path-from-ruby
|
||||||
@@ -34,15 +24,62 @@ else if which('ifconfig')
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
$php = [ "5.6", "7.0", "7.1", "7.2" ]
|
|
||||||
$phpVersion = ENV['phpversion'] ? ENV['phpversion'] : "7.0";
|
|
||||||
|
|
||||||
unless Vagrant.has_plugin?('vagrant-hostmanager')
|
unless Vagrant.has_plugin?('vagrant-hostmanager')
|
||||||
raise "vagrant-hostmanager is not installed! Please run\n vagrant plugin install vagrant-hostmanager\n\n"
|
raise "vagrant-hostmanager is not installed! Please run\n vagrant plugin install vagrant-hostmanager\n\n"
|
||||||
end
|
end
|
||||||
|
|
||||||
unless $php.include?($phpVersion)
|
# Check to determine if box_meta JSON is present
|
||||||
raise "You should specify php version before running vagrant\n\n (Available : 5.6, 7.0, 7.1, 7.2 | default => 5.6)\n\n Exemple: phpversion='7.0' vagrant up \n\n"
|
# if provisionned : pick name of box
|
||||||
|
if File.file?(".vagrant/machines/default/virtualbox/box_meta")
|
||||||
|
data = File.read(".vagrant/machines/default/virtualbox/box_meta")
|
||||||
|
parsed_json = JSON.parse(data)
|
||||||
|
$box = parsed_json["name"]
|
||||||
|
end
|
||||||
|
|
||||||
|
# if not : run prompt to configure provisioning
|
||||||
|
if !File.file?(".vagrant/machines/default/virtualbox/box_meta") && ARGV[0] == 'up'
|
||||||
|
print "\033[34m \nChoose a Build type :\n\n(1) Use prebuilt Phraseanet Box\n(2) Build Phraseanet from scratch (xenial)\n\033[00m"
|
||||||
|
type = STDIN.gets.chomp
|
||||||
|
print "\n"
|
||||||
|
# Switch between Phraseanet box and native trusty64
|
||||||
|
case (type)
|
||||||
|
when '1'
|
||||||
|
$box = "alchemy/Phraseanet-vagrant-dev_php"
|
||||||
|
$playbook = "resources/ansible/playbook-boxes.yml"
|
||||||
|
when '2'
|
||||||
|
$box = "ubuntu/xenial64"
|
||||||
|
$playbook = "resources/ansible/playbook.yml"
|
||||||
|
else
|
||||||
|
raise "\033[31mYou should specify Build type before running vagrant\n\n (Available : 1, 2)\n\n\033[00m"
|
||||||
|
end
|
||||||
|
print "\033[32m-----------------------------------------------\n"
|
||||||
|
print "Build with "+$box+" box\n"
|
||||||
|
print "-----------------------------------------------\n\n\033[00m"
|
||||||
|
|
||||||
|
print "\033[34mChoose a PHP version for your build (Available : 5.6, 7.0, 7.1, 7.2)\n\033[00m"
|
||||||
|
phpversion = STDIN.gets.chomp
|
||||||
|
print "\n"
|
||||||
|
# Php version selection
|
||||||
|
case (phpversion)
|
||||||
|
when "5.6", "7.0", "7.1", "7.2"
|
||||||
|
print "\033[32mSelected PHP version : "+phpversion+"\n\033[00m"
|
||||||
|
print "Continue ? (Y/n) \n"
|
||||||
|
continue = STDIN.gets.chomp
|
||||||
|
case continue
|
||||||
|
when 'n', 'no', 'N', 'NO'
|
||||||
|
raise "\033[31mBuild aborted\033[00m"
|
||||||
|
else
|
||||||
|
if (type == '1')
|
||||||
|
$box.concat(phpversion)
|
||||||
|
end
|
||||||
|
print "\033[32m-----------------------------------------------\n"
|
||||||
|
print "Build with PHP"+phpversion+"\n"
|
||||||
|
print "-----------------------------------------------\n\n\033[00m"
|
||||||
|
|
||||||
|
end
|
||||||
|
else
|
||||||
|
raise "\033[31mYou should specify php version before running vagrant\n\n (Available : 5.6, 7.0, 7.1, 7.2)\n\n\033[00m"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
$root = File.dirname(File.expand_path(__FILE__))
|
$root = File.dirname(File.expand_path(__FILE__))
|
||||||
@@ -95,8 +132,6 @@ else if $env == "linux"
|
|||||||
$hostIps = `ifconfig | sed -nE 's/[[:space:]]*inet ([0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3})(.*)$/\\1/p'`.split("\n");
|
$hostIps = `ifconfig | sed -nE 's/[[:space:]]*inet ([0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3})(.*)$/\\1/p'`.split("\n");
|
||||||
else
|
else
|
||||||
$hostIps = `resources/ansible/inventories/GetIpAdresses.cmd`;
|
$hostIps = `resources/ansible/inventories/GetIpAdresses.cmd`;
|
||||||
# raise MyCustomError.new($hostIps), "HOST IP"
|
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -119,33 +154,28 @@ Vagrant.configure("2") do |config|
|
|||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
# Switch between Phraseanet box and native trusty64
|
config.vm.box = $box
|
||||||
config.vm.box = "alchemy/Phraseanet-vagrant-dev"
|
|
||||||
#config.vm.box = "ubuntu/trusty64"
|
|
||||||
|
|
||||||
# In case, Phraseanet box, choose the php version
|
|
||||||
# For php 7.0 use box 0.0.1
|
|
||||||
# For php 7.1 use box 0.0.2
|
|
||||||
config.vm.box_version = "0.0.1"
|
|
||||||
|
|
||||||
config.ssh.forward_agent = true
|
config.ssh.forward_agent = true
|
||||||
config_net(config)
|
config_net(config)
|
||||||
|
|
||||||
# If ansible is in your path it will provision from your HOST machine
|
# If ansible is in your path it will provision from your HOST machine
|
||||||
# If ansible is not found in the path it will be instaled in the VM and provisioned from there
|
# If ansible is not found in the path it will be instaled in the VM and provisioned from there
|
||||||
if which('ansible-playbook')
|
if which('ansible-playbook')
|
||||||
config.vm.provision "ansible_local" do |ansible|
|
|
||||||
ansible.playbook = "resources/ansible/playbook.yml"
|
if $playbook
|
||||||
ansible.limit = 'all'
|
config.vm.provision "ansible_local" do |ansible|
|
||||||
ansible.verbose = 'vvv'
|
ansible.playbook = $playbook
|
||||||
ansible.extra_vars = {
|
ansible.limit = 'all'
|
||||||
hostname: $hostname,
|
ansible.verbose = 'vvv'
|
||||||
host_addresses: $hostIps,
|
ansible.extra_vars = {
|
||||||
phpversion: $phpVersion,
|
hostname: $hostname,
|
||||||
postfix: {
|
host_addresses: $hostIps,
|
||||||
postfix_domain: $hostname + ".vb"
|
phpversion: phpversion,
|
||||||
|
postfix: {
|
||||||
|
postfix_domain: $hostname + ".vb"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
config.vm.provision "ansible_local", run: "always" do |ansible|
|
config.vm.provision "ansible_local", run: "always" do |ansible|
|
||||||
@@ -158,10 +188,6 @@ Vagrant.configure("2") do |config|
|
|||||||
}
|
}
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
# raise MyCustomError.new([$hostname, $phpVersion, $hostIps]), "HOST IP"
|
|
||||||
# raise MyCustomError.new($hostIps), "HOST IP"
|
|
||||||
# raise MyCustomError.new($hostIps), "HOST IP"
|
|
||||||
|
|
||||||
config.vm.provision :shell, path: "resources/ansible/windows.sh", args: [$hostname, $phpVersion, $hostIps]
|
config.vm.provision :shell, path: "resources/ansible/windows.sh", args: [$hostname, $phpVersion, $hostIps]
|
||||||
# config.vm.provision :shell, run: "always", path: "resources/ansible/windows-always.sh", args: ["default"]
|
# config.vm.provision :shell, run: "always", path: "resources/ansible/windows-always.sh", args: ["default"]
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ use Alchemy\Phrasea\Command\Plugin\AddPlugin;
|
|||||||
use Alchemy\Phrasea\Command\Plugin\RemovePlugin;
|
use Alchemy\Phrasea\Command\Plugin\RemovePlugin;
|
||||||
use Alchemy\Phrasea\Command\Plugin\EnablePlugin;
|
use Alchemy\Phrasea\Command\Plugin\EnablePlugin;
|
||||||
use Alchemy\Phrasea\Command\Plugin\DisablePlugin;
|
use Alchemy\Phrasea\Command\Plugin\DisablePlugin;
|
||||||
|
use Alchemy\Phrasea\Command\Plugin\DownloadPlugin;
|
||||||
use Alchemy\Phrasea\CLI;
|
use Alchemy\Phrasea\CLI;
|
||||||
use Alchemy\Phrasea\Command\Setup\CheckEnvironment;
|
use Alchemy\Phrasea\Command\Setup\CheckEnvironment;
|
||||||
use Alchemy\Phrasea\Core\CLIProvider\DoctrineMigrationServiceProvider;
|
use Alchemy\Phrasea\Core\CLIProvider\DoctrineMigrationServiceProvider;
|
||||||
@@ -50,7 +51,7 @@ $app = new CLI("
|
|||||||
This program comes with ABSOLUTELY NO WARRANTY.
|
This program comes with ABSOLUTELY NO WARRANTY.
|
||||||
This is free software, and you are welcome to redistribute it
|
This is free software, and you are welcome to redistribute it
|
||||||
under certain conditions; type `about:license' for details.\n\n"
|
under certain conditions; type `about:license' for details.\n\n"
|
||||||
. ' SETUP', $version->getName() . ' ' . $version->getNumber());
|
. ' SETUP', $version->getName() . ' ' . $version->getNumber());
|
||||||
|
|
||||||
$app->register(new DoctrineMigrationServiceProvider());
|
$app->register(new DoctrineMigrationServiceProvider());
|
||||||
|
|
||||||
@@ -70,6 +71,7 @@ if ($configurationTester->isInstalled()) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$app->command(new AddPlugin());
|
$app->command(new AddPlugin());
|
||||||
|
$app->command(new DownloadPlugin());
|
||||||
$app->command(new ListPlugin());
|
$app->command(new ListPlugin());
|
||||||
$app->command(new RemovePlugin());
|
$app->command(new RemovePlugin());
|
||||||
$app->command(new PluginsReset());
|
$app->command(new PluginsReset());
|
||||||
|
|||||||
0
cache/.gitkeep
vendored
@@ -47,7 +47,7 @@
|
|||||||
"php": ">=5.5.9",
|
"php": ">=5.5.9",
|
||||||
"ext-intl": "*",
|
"ext-intl": "*",
|
||||||
"alchemy-fr/tcpdf-clone": "~6.0",
|
"alchemy-fr/tcpdf-clone": "~6.0",
|
||||||
"alchemy/embed-bundle": "^2.0.4",
|
"alchemy/embed-bundle": "^2.0.7",
|
||||||
"alchemy/geonames-api-consumer": "~0.1.0",
|
"alchemy/geonames-api-consumer": "~0.1.0",
|
||||||
"alchemy/mediavorus": "^0.4.4",
|
"alchemy/mediavorus": "^0.4.4",
|
||||||
"alchemy/oauth2php": "1.1.0",
|
"alchemy/oauth2php": "1.1.0",
|
||||||
@@ -120,7 +120,8 @@
|
|||||||
"google/recaptcha": "^1.1",
|
"google/recaptcha": "^1.1",
|
||||||
"facebook/graph-sdk": "^5.6",
|
"facebook/graph-sdk": "^5.6",
|
||||||
"box/spout": "^2.7",
|
"box/spout": "^2.7",
|
||||||
"paragonie/random-lib": "^2.0"
|
"paragonie/random-lib": "^2.0",
|
||||||
|
"czproject/git-php": "^3.17"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"mikey179/vfsstream": "~1.5",
|
"mikey179/vfsstream": "~1.5",
|
||||||
|
|||||||
66
composer.lock
generated
@@ -4,7 +4,7 @@
|
|||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "a40bfa0aa6310530dc0c92b141b21305",
|
"content-hash": "f3b1fc0a30bf14b05e57ce673550d9c0",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "alchemy-fr/tcpdf-clone",
|
"name": "alchemy-fr/tcpdf-clone",
|
||||||
@@ -131,16 +131,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "alchemy/embed-bundle",
|
"name": "alchemy/embed-bundle",
|
||||||
"version": "2.0.4",
|
"version": "2.0.7",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/alchemy-fr/embed-bundle.git",
|
"url": "https://github.com/alchemy-fr/embed-bundle.git",
|
||||||
"reference": "b510748686c05c0c1d59b7ad15e2c1098abafc5a"
|
"reference": "c585ccf18e53a9a6f2b696ddbbc39521732dfdde"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/alchemy-fr/embed-bundle/zipball/b510748686c05c0c1d59b7ad15e2c1098abafc5a",
|
"url": "https://api.github.com/repos/alchemy-fr/embed-bundle/zipball/c585ccf18e53a9a6f2b696ddbbc39521732dfdde",
|
||||||
"reference": "b510748686c05c0c1d59b7ad15e2c1098abafc5a",
|
"reference": "c585ccf18e53a9a6f2b696ddbbc39521732dfdde",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
@@ -178,10 +178,10 @@
|
|||||||
],
|
],
|
||||||
"description": "Embed resources bundle",
|
"description": "Embed resources bundle",
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/alchemy-fr/embed-bundle/tree/2.0.4",
|
"source": "https://github.com/alchemy-fr/embed-bundle/tree/2.0.7",
|
||||||
"issues": "https://github.com/alchemy-fr/embed-bundle/issues"
|
"issues": "https://github.com/alchemy-fr/embed-bundle/issues"
|
||||||
},
|
},
|
||||||
"time": "2019-06-03T13:35:50+00:00"
|
"time": "2019-09-02T12:28:19+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "alchemy/geonames-api-consumer",
|
"name": "alchemy/geonames-api-consumer",
|
||||||
@@ -383,16 +383,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "alchemy/phpexiftool",
|
"name": "alchemy/phpexiftool",
|
||||||
"version": "0.7.0",
|
"version": "0.7.2",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/alchemy-fr/PHPExiftool.git",
|
"url": "https://github.com/alchemy-fr/PHPExiftool.git",
|
||||||
"reference": "7372ca4e43473328bf06bca810558fbad7bb2f95"
|
"reference": "ba1cb51eceb6562d7996023478977a8739de188b"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/alchemy-fr/PHPExiftool/zipball/7372ca4e43473328bf06bca810558fbad7bb2f95",
|
"url": "https://api.github.com/repos/alchemy-fr/PHPExiftool/zipball/ba1cb51eceb6562d7996023478977a8739de188b",
|
||||||
"reference": "7372ca4e43473328bf06bca810558fbad7bb2f95",
|
"reference": "ba1cb51eceb6562d7996023478977a8739de188b",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -452,7 +452,7 @@
|
|||||||
"exiftool",
|
"exiftool",
|
||||||
"metadata"
|
"metadata"
|
||||||
],
|
],
|
||||||
"time": "2017-05-18T19:04:04+00:00"
|
"time": "2019-02-13T13:06:43+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "alchemy/queue-bundle",
|
"name": "alchemy/queue-bundle",
|
||||||
@@ -1156,6 +1156,48 @@
|
|||||||
],
|
],
|
||||||
"time": "2016-08-09T20:10:17+00:00"
|
"time": "2016-08-09T20:10:17+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "czproject/git-php",
|
||||||
|
"version": "v3.17.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/czproject/git-php.git",
|
||||||
|
"reference": "a7b911b81a2fe626f748a4ac8955353c5777bc6c"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/czproject/git-php/zipball/a7b911b81a2fe626f748a4ac8955353c5777bc6c",
|
||||||
|
"reference": "a7b911b81a2fe626f748a4ac8955353c5777bc6c",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">=5.4.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"nette/tester": "^1.1"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"classmap": [
|
||||||
|
"src/"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"BSD-3-Clause"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Jan Pecha",
|
||||||
|
"email": "janpecha@email.cz"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Library for work with Git repository in PHP.",
|
||||||
|
"keywords": [
|
||||||
|
"git"
|
||||||
|
],
|
||||||
|
"time": "2019-02-09T13:11:36+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "dailymotion/sdk",
|
"name": "dailymotion/sdk",
|
||||||
"version": "1.6.5",
|
"version": "1.6.5",
|
||||||
|
|||||||
@@ -231,17 +231,19 @@ embed_bundle:
|
|||||||
video:
|
video:
|
||||||
player: videojs
|
player: videojs
|
||||||
autoplay: false
|
autoplay: false
|
||||||
coverSubdef: previewx4
|
cover_subdef: thumbnail
|
||||||
available-speeds:
|
message_start: StartOfMessage
|
||||||
|
available_speeds:
|
||||||
- 1
|
- 1
|
||||||
- 1.5
|
- 1.5
|
||||||
- 3
|
- 3
|
||||||
audio:
|
audio:
|
||||||
player: videojs
|
player: videojs
|
||||||
autoplay: false
|
autoplay: false
|
||||||
|
cover_subdef: thumbnail
|
||||||
document:
|
document:
|
||||||
player: flexpaper
|
player: flexpaper
|
||||||
enable-pdfjs: true
|
enable_pdfjs: true
|
||||||
geocoding-providers:
|
geocoding-providers:
|
||||||
-
|
-
|
||||||
map-provider: mapboxWebGL
|
map-provider: mapboxWebGL
|
||||||
|
|||||||
3
docker/phraseanet/worker-boot.sh
Executable file
@@ -0,0 +1,3 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
runuser app -c 'php /var/alchemy/Phraseanet/bin/console task-manager:scheduler:run'
|
||||||
@@ -115,6 +115,7 @@ use Symfony\Component\Form\FormBuilderInterface;
|
|||||||
use Symfony\Component\Form\FormInterface;
|
use Symfony\Component\Form\FormInterface;
|
||||||
use Symfony\Component\Form\FormTypeInterface;
|
use Symfony\Component\Form\FormTypeInterface;
|
||||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||||
|
use Symfony\Component\Process\ExecutableFinder;
|
||||||
use Unoconv\UnoconvServiceProvider;
|
use Unoconv\UnoconvServiceProvider;
|
||||||
use XPDF\PdfToText;
|
use XPDF\PdfToText;
|
||||||
use XPDF\XPDFServiceProvider;
|
use XPDF\XPDFServiceProvider;
|
||||||
@@ -237,8 +238,19 @@ class Application extends SilexApplication
|
|||||||
|
|
||||||
$this->register(new UnicodeServiceProvider());
|
$this->register(new UnicodeServiceProvider());
|
||||||
$this->register(new ValidatorServiceProvider());
|
$this->register(new ValidatorServiceProvider());
|
||||||
$this->register(new XPDFServiceProvider());
|
|
||||||
$this->setupXpdf();
|
if ($this['configuration.store']->isSetup()) {
|
||||||
|
$binariesConfig = $this['conf']->get(['main', 'binaries']);
|
||||||
|
$executableFinder = new ExecutableFinder();
|
||||||
|
$this->register(new XPDFServiceProvider(), [
|
||||||
|
'xpdf.configuration' => [
|
||||||
|
'pdftotext.binaries' => isset($binariesConfig['pdftotext_binary']) ? $binariesConfig['pdftotext_binary'] : $executableFinder->find('pdftotext'),
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->setupXpdf();
|
||||||
|
}
|
||||||
|
|
||||||
$this->register(new FileServeServiceProvider());
|
$this->register(new FileServeServiceProvider());
|
||||||
$this->register(new ManipulatorServiceProvider());
|
$this->register(new ManipulatorServiceProvider());
|
||||||
$this->register(new PluginServiceProvider());
|
$this->register(new PluginServiceProvider());
|
||||||
@@ -653,7 +665,7 @@ class Application extends SilexApplication
|
|||||||
private function setupGeonames()
|
private function setupGeonames()
|
||||||
{
|
{
|
||||||
$this['geonames.server-uri'] = $this->share(function (Application $app) {
|
$this['geonames.server-uri'] = $this->share(function (Application $app) {
|
||||||
return $app['conf']->get(['registry', 'webservices', 'geonames-server'], 'http://geonames.alchemyasp.com/');
|
return $app['conf']->get(['registry', 'webservices', 'geonames-server'], 'https://geonames.alchemyasp.com/');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -51,4 +51,41 @@ abstract class AbstractPluginCommand extends Command
|
|||||||
$this->container['plugins.autoloader-generator']->write($manifests);
|
$this->container['plugins.autoloader-generator']->write($manifests);
|
||||||
$output->writeln(" <comment>OK</comment>");
|
$output->writeln(" <comment>OK</comment>");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
$output->writeln(" <comment>OK</comment> found <info>".$manifest->getName()."</info>");
|
||||||
|
|
||||||
|
$targetDir = $this->container['plugin.path'] . DIRECTORY_SEPARATOR . $manifest->getName();
|
||||||
|
|
||||||
|
$output->write("Setting up composer...");
|
||||||
|
$this->container['plugins.composer-installer']->install($temporaryDir);
|
||||||
|
$output->writeln(" <comment>OK</comment>");
|
||||||
|
|
||||||
|
$output->write("Installing plugin <info>".$manifest->getName()."</info>...");
|
||||||
|
$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>");
|
||||||
|
|
||||||
|
$output->write("Activating plugin...");
|
||||||
|
$this->container['conf']->set(['plugins', $manifest->getName(), 'enabled'], true);
|
||||||
|
$output->writeln(" <comment>OK</comment>");
|
||||||
|
|
||||||
|
$this->updateConfigFiles($input, $output);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ namespace Alchemy\Phrasea\Command\Plugin;
|
|||||||
use Symfony\Component\Console\Input\InputInterface;
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
use Symfony\Component\Console\Output\OutputInterface;
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
use Symfony\Component\Console\Input\InputArgument;
|
use Symfony\Component\Console\Input\InputArgument;
|
||||||
|
use Symfony\Component\Console\Input\ArrayInput;
|
||||||
|
|
||||||
class AddPlugin extends AbstractPluginCommand
|
class AddPlugin extends AbstractPluginCommand
|
||||||
{
|
{
|
||||||
@@ -29,41 +30,36 @@ class AddPlugin extends AbstractPluginCommand
|
|||||||
protected function doExecutePluginAction(InputInterface $input, OutputInterface $output)
|
protected function doExecutePluginAction(InputInterface $input, OutputInterface $output)
|
||||||
{
|
{
|
||||||
$source = $input->getArgument('source');
|
$source = $input->getArgument('source');
|
||||||
|
$shouldDownload = $this->shouldDownloadPlugin($source);
|
||||||
|
|
||||||
$temporaryDir = $this->container['temporary-filesystem']->createTemporaryDirectory();
|
if ($shouldDownload){
|
||||||
|
$command = $this->getApplication()->find('plugins:download');
|
||||||
|
$arguments = [
|
||||||
|
'command' => 'plugins:download',
|
||||||
|
'source' => $source,
|
||||||
|
'shouldInstallPlugin' => true
|
||||||
|
];
|
||||||
|
|
||||||
$output->write("Importing <info>$source</info>...");
|
$downloadInput = new ArrayInput($arguments);
|
||||||
$this->container['plugins.importer']->import($source, $temporaryDir);
|
$command->run($downloadInput, $output);
|
||||||
$output->writeln(" <comment>OK</comment>");
|
|
||||||
|
|
||||||
$output->write("Validating plugin...");
|
} else {
|
||||||
$manifest = $this->container['plugins.plugins-validator']->validatePlugin($temporaryDir);
|
|
||||||
$output->writeln(" <comment>OK</comment> found <info>".$manifest->getName()."</info>");
|
|
||||||
|
|
||||||
$targetDir = $this->container['plugin.path'] . DIRECTORY_SEPARATOR . $manifest->getName();
|
$this->doInstallPlugin($source, $input, $output);
|
||||||
|
}
|
||||||
$output->write("Setting up composer...");
|
|
||||||
$this->container['plugins.composer-installer']->install($temporaryDir);
|
|
||||||
$output->writeln(" <comment>OK</comment>");
|
|
||||||
|
|
||||||
$output->write("Installing plugin <info>".$manifest->getName()."</info>...");
|
|
||||||
$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>");
|
|
||||||
|
|
||||||
$output->write("Activating plugin...");
|
|
||||||
$this->container['conf']->set(['plugins', $manifest->getName(), 'enabled'], true);
|
|
||||||
$output->writeln(" <comment>OK</comment>");
|
|
||||||
|
|
||||||
$this->updateConfigFiles($input, $output);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function shouldDownloadPlugin($source)
|
||||||
|
{
|
||||||
|
$allowedScheme = array('https','ssh');
|
||||||
|
|
||||||
|
$scheme = parse_url($source, PHP_URL_SCHEME);
|
||||||
|
if (in_array($scheme, $allowedScheme)){
|
||||||
|
return true;
|
||||||
|
} else{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
157
lib/Alchemy/Phrasea/Command/Plugin/DownloadPlugin.php
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
<?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\Plugin;
|
||||||
|
|
||||||
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
|
use Symfony\Component\Console\Input\InputArgument;
|
||||||
|
use Symfony\Component\Console\Input\ArrayInput;
|
||||||
|
use Cz\Git\GitRepository as GitRepository;
|
||||||
|
|
||||||
|
class DownloadPlugin extends AbstractPluginCommand
|
||||||
|
{
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct('plugins:download');
|
||||||
|
|
||||||
|
$this
|
||||||
|
->setDescription('Downloads a plugin to Phraseanet')
|
||||||
|
->addArgument('source', InputArgument::REQUIRED, 'The source is a remote url (.zip or .git)')
|
||||||
|
->addArgument('destination', InputArgument::OPTIONAL, 'Download destination')
|
||||||
|
->addArgument('shouldInstallPlugin', InputArgument::OPTIONAL, 'True or false, determines if plugin should be installed after download');
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function doExecutePluginAction(InputInterface $input, OutputInterface $output)
|
||||||
|
{
|
||||||
|
$source = $input->getArgument('source');
|
||||||
|
$destination = $input->getArgument('destination');
|
||||||
|
$shouldInstallPlugin = false;
|
||||||
|
$shouldInstallPlugin = $input->getArgument('shouldInstallPlugin');
|
||||||
|
|
||||||
|
$destinationSubdir = '/plugin-'.md5($source);
|
||||||
|
|
||||||
|
if ($destination){
|
||||||
|
|
||||||
|
$destination = trim($destination);
|
||||||
|
$destination = rtrim($destination, '/');
|
||||||
|
|
||||||
|
$localDownloadPath = $destination;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
$localDownloadPath = '/tmp/plugin-download' . $destinationSubdir;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_dir($localDownloadPath)) {
|
||||||
|
mkdir($localDownloadPath, 0755, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
$extension = $this->getURIExtension($source);
|
||||||
|
|
||||||
|
if ($extension){
|
||||||
|
|
||||||
|
switch ($extension){
|
||||||
|
|
||||||
|
case 'zip':
|
||||||
|
|
||||||
|
$localUnpackPath = '/tmp/plugin-zip'. $destinationSubdir;
|
||||||
|
|
||||||
|
if (!is_dir($localUnpackPath)) {
|
||||||
|
mkdir($localUnpackPath, 0755, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
$localArchiveFile = $localUnpackPath . '/plugin-downloaded.zip';
|
||||||
|
|
||||||
|
// download
|
||||||
|
$output->writeln("Downloading <info>$source</info>...");
|
||||||
|
set_time_limit(0);
|
||||||
|
$fp = fopen ($localArchiveFile, 'w+');
|
||||||
|
$ch = curl_init($source);;
|
||||||
|
curl_setopt($ch, CURLOPT_FILE, $fp);
|
||||||
|
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
|
||||||
|
curl_exec($ch);
|
||||||
|
curl_close($ch);
|
||||||
|
fclose($fp);
|
||||||
|
|
||||||
|
// unpack
|
||||||
|
$output->writeln("Unpacking <info>$source</info>...");
|
||||||
|
$zip = new \ZipArchive();
|
||||||
|
$errorUnpack = false;
|
||||||
|
|
||||||
|
if ($zip->open($localArchiveFile)) {
|
||||||
|
for ($i = 0; $i < $zip->numFiles; $i++) {
|
||||||
|
if (!($zip->extractTo($localDownloadPath, array($zip->getNameIndex($i))))) {
|
||||||
|
$errorUnpack = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$zip->close();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($errorUnpack){
|
||||||
|
$output->writeln("Failed unzipping <info>$source</info>");
|
||||||
|
} else {
|
||||||
|
$output->writeln("Plugin downloaded to <info>$localDownloadPath</info>");
|
||||||
|
if ($shouldInstallPlugin) $this->doInstallPlugin($localDownloadPath, $input, $output);
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove zip archive
|
||||||
|
$this->delDirTree($localUnpackPath);
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'git':
|
||||||
|
$output->writeln("Downloading <info>$source</info>...");
|
||||||
|
$repo = GitRepository::cloneRepository($source, $localDownloadPath);
|
||||||
|
$output->writeln("Plugin downloaded to <info>$localDownloadPath</info>");
|
||||||
|
if ($shouldInstallPlugin) $this->doInstallPlugin($localDownloadPath, $input, $output);
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
$output->writeln("The source <info>$source</info> is not supported. Only .zip and .git are supported.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getURIExtension($source)
|
||||||
|
{
|
||||||
|
$validExtension = false;
|
||||||
|
$allowedExtension = array('zip','git');
|
||||||
|
|
||||||
|
$path = parse_url($source, PHP_URL_PATH);
|
||||||
|
if (strpos($path, '.') !== false) {
|
||||||
|
$pathParts = explode('.', $path);
|
||||||
|
$extension = $pathParts[1];
|
||||||
|
if (in_array($extension, $allowedExtension)){
|
||||||
|
$validExtension = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($validExtension){
|
||||||
|
return $extension;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static function delDirTree($dir) {
|
||||||
|
$files = array_diff(scandir($dir), array('.','..'));
|
||||||
|
foreach ($files as $file) {
|
||||||
|
(is_dir("$dir/$file")) ? self::delDirTree("$dir/$file") : unlink("$dir/$file");
|
||||||
|
}
|
||||||
|
return rmdir($dir);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -944,7 +944,7 @@ class V1Controller extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
$originalName = $pi['filename'] . '.' . $pi['extension'];
|
$originalName = $pi['filename'] . '.' . $pi['extension'];
|
||||||
$newPathname = $tempfile;
|
$uploadedFilename = $newPathname = $tempfile;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -956,8 +956,11 @@ class V1Controller extends Controller
|
|||||||
if (!$file->isValid()) {
|
if (!$file->isValid()) {
|
||||||
return $this->getBadRequestAction($request, 'Data corrupted, please try again');
|
return $this->getBadRequestAction($request, 'Data corrupted, please try again');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$uploadedFilename = $file->getPathname();
|
||||||
$originalName = $file->getClientOriginalName();
|
$originalName = $file->getClientOriginalName();
|
||||||
$newPathname = $file->getPathname() . '.' . $file->getClientOriginalExtension();
|
$newPathname = $file->getPathname() . '.' . $file->getClientOriginalExtension();
|
||||||
|
|
||||||
if (false === rename($file->getPathname(), $newPathname)) {
|
if (false === rename($file->getPathname(), $newPathname)) {
|
||||||
return Result::createError($request, 403, 'Error while renaming file')->createResponse();
|
return Result::createError($request, 403, 'Error while renaming file')->createResponse();
|
||||||
}
|
}
|
||||||
@@ -1010,6 +1013,11 @@ class V1Controller extends Controller
|
|||||||
$nosubdef = $request->get('nosubdefs') === '' || \p4field::isyes($request->get('nosubdefs'));
|
$nosubdef = $request->get('nosubdefs') === '' || \p4field::isyes($request->get('nosubdefs'));
|
||||||
$this->getBorderManager()->process($session, $Package, $callback, $behavior, $nosubdef);
|
$this->getBorderManager()->process($session, $Package, $callback, $behavior, $nosubdef);
|
||||||
|
|
||||||
|
// remove $newPathname on temporary directory
|
||||||
|
if ($newPathname !== $uploadedFilename) {
|
||||||
|
@rename($newPathname, $uploadedFilename);
|
||||||
|
}
|
||||||
|
|
||||||
$ret = ['entity' => null];
|
$ret = ['entity' => null];
|
||||||
|
|
||||||
if ($output instanceof \record_adapter) {
|
if ($output instanceof \record_adapter) {
|
||||||
@@ -1081,6 +1089,11 @@ class V1Controller extends Controller
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// remove $newPathname on temporary directory
|
||||||
|
if ($renamedFilename !== $uploadedFilename) {
|
||||||
|
@rename($renamedFilename, $uploadedFilename);
|
||||||
|
}
|
||||||
|
|
||||||
return Result::create($request, $ret)->createResponse();
|
return Result::create($request, $ret)->createResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1984,7 +1997,7 @@ class V1Controller extends Controller
|
|||||||
return $this->getBadRequestAction($request);
|
return $this->getBadRequestAction($request);
|
||||||
}
|
}
|
||||||
|
|
||||||
$datas = substr($datas, 0, ($n)) . $value . substr($datas, ($n + 2));
|
$datas = substr($datas, 0, ($n)) . $value . substr($datas, ($n + 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
$record->setStatus(strrev($datas));
|
$record->setStatus(strrev($datas));
|
||||||
@@ -2588,8 +2601,18 @@ class V1Controller extends Controller
|
|||||||
foreach ($recordsData as $data) {
|
foreach ($recordsData as $data) {
|
||||||
$records[] = $this->addOrDelStoryRecord($story, $data, $action);
|
$records[] = $this->addOrDelStoryRecord($story, $data, $action);
|
||||||
if($action === 'ADD' && !$cover_set && isset($data->{'use_as_cover'}) && $data->{'use_as_cover'} === true) {
|
if($action === 'ADD' && !$cover_set && isset($data->{'use_as_cover'}) && $data->{'use_as_cover'} === true) {
|
||||||
|
$coverSource = [];
|
||||||
|
|
||||||
|
if (isset($data->{'thumbnail_cover_source'})) {
|
||||||
|
$coverSource['thumbnail_cover_source'] = $data->{'thumbnail_cover_source'};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($data->{'preview_cover_source'})) {
|
||||||
|
$coverSource['preview_cover_source'] = $data->{'preview_cover_source'};
|
||||||
|
}
|
||||||
|
|
||||||
// because we can try many records as cover source, we let it fail
|
// because we can try many records as cover source, we let it fail
|
||||||
$cover_set = ($this->setStoryCover($story, $data->{'record_id'}, true) !== false);
|
$cover_set = ($this->setStoryCover($story, $data->{'record_id'}, true, $coverSource) !== false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2643,14 +2666,26 @@ class V1Controller extends Controller
|
|||||||
|
|
||||||
$story = new \record_adapter($this->app, $databox_id, $story_id);
|
$story = new \record_adapter($this->app, $databox_id, $story_id);
|
||||||
|
|
||||||
|
$coverSource = [];
|
||||||
|
|
||||||
|
if (isset($data->{'thumbnail_cover_source'})) {
|
||||||
|
$coverSource['thumbnail_cover_source'] = $data->{'thumbnail_cover_source'};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($data->{'preview_cover_source'})) {
|
||||||
|
$coverSource['preview_cover_source'] = $data->{'preview_cover_source'};
|
||||||
|
}
|
||||||
|
|
||||||
// we do NOT let "setStoryCover()" fail : pass false as last arg
|
// we do NOT let "setStoryCover()" fail : pass false as last arg
|
||||||
$record_key = $this->setStoryCover($story, $data->{'record_id'}, false);
|
$record_key = $this->setStoryCover($story, $data->{'record_id'}, false, $coverSource);
|
||||||
|
|
||||||
return Result::create($request, array($record_key))->createResponse();
|
return Result::create($request, array($record_key))->createResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function setStoryCover(\record_adapter $story, $record_id, $can_fail=false)
|
protected function setStoryCover(\record_adapter $story, $record_id, $can_fail=false, $coverSource = [])
|
||||||
{
|
{
|
||||||
|
$coverSource = array_merge(['thumbnail_cover_source' => 'thumbnail', 'preview_cover_source' => 'preview'], $coverSource);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$record = new \record_adapter($this->app, $story->getDataboxId(), $record_id);
|
$record = new \record_adapter($this->app, $story->getDataboxId(), $record_id);
|
||||||
} catch (\Exception_Record_AdapterNotFound $e) {
|
} catch (\Exception_Record_AdapterNotFound $e) {
|
||||||
@@ -2662,18 +2697,22 @@ class V1Controller extends Controller
|
|||||||
$this->app->abort(404, sprintf('Record identified by databox_id %s and record_id %s is not in the story', $story->getDataboxId(), $record_id));
|
$this->app->abort(404, sprintf('Record identified by databox_id %s and record_id %s is not in the story', $story->getDataboxId(), $record_id));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($record->getType() !== 'image' && $record->getType() !== 'video') {
|
// taking account all record type as a cover
|
||||||
// this can fail so we can loop on many records during story creation...
|
// if ($record->getType() !== 'image' && $record->getType() !== 'video') {
|
||||||
if($can_fail) {
|
// // this can fail so we can loop on many records during story creation...
|
||||||
return false;
|
// if($can_fail) {
|
||||||
}
|
// return false;
|
||||||
$this->app->abort(403, sprintf('Record identified by databox_id %s and record_id %s is not an image nor a video', $story->getDataboxId(), $record_id));
|
// }
|
||||||
}
|
// $this->app->abort(403, sprintf('Record identified by databox_id %s and record_id %s is not an image nor a video', $story->getDataboxId(), $record_id));
|
||||||
|
// }
|
||||||
|
|
||||||
foreach ($record->get_subdefs() as $name => $value) {
|
foreach ($record->get_subdefs() as $name => $value) {
|
||||||
if (!in_array($name, array('thumbnail', 'preview'))) {
|
if (!($key = array_search($name, $coverSource))) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$name = ($key == 'thumbnail_cover_source') ? 'thumbnail': 'preview';
|
||||||
|
|
||||||
$media = $this->app->getMediaFromUri($value->getRealPath());
|
$media = $this->app->getMediaFromUri($value->getRealPath());
|
||||||
$this->getSubdefSubstituer()->substituteSubdef($story, $name, $media); // name = thumbnail | preview
|
$this->getSubdefSubstituer()->substituteSubdef($story, $name, $media); // name = thumbnail | preview
|
||||||
$this->getDataboxLogger($story->getDatabox())->log(
|
$this->getDataboxLogger($story->getDatabox())->log(
|
||||||
|
|||||||
@@ -114,6 +114,7 @@ class MoveCollectionController extends Controller
|
|||||||
$trashCollectionsBySbasId = [];
|
$trashCollectionsBySbasId = [];
|
||||||
|
|
||||||
foreach ($records as $record) {
|
foreach ($records as $record) {
|
||||||
|
$oldCollectionId = $record->getCollection()->get_coll_id();
|
||||||
$record->move_to_collection($collection, $this->getApplicationBox());
|
$record->move_to_collection($collection, $this->getApplicationBox());
|
||||||
|
|
||||||
if ($request->request->get("chg_coll_son") == "1") {
|
if ($request->request->get("chg_coll_son") == "1") {
|
||||||
@@ -130,7 +131,7 @@ class MoveCollectionController extends Controller
|
|||||||
$trashCollectionsBySbasId[$sbasId] = $record->getDatabox()->getTrashCollection();
|
$trashCollectionsBySbasId[$sbasId] = $record->getDatabox()->getTrashCollection();
|
||||||
}
|
}
|
||||||
if ($trashCollectionsBySbasId[$sbasId] !== null) {
|
if ($trashCollectionsBySbasId[$sbasId] !== null) {
|
||||||
if ($record->getCollection()->get_coll_id() == $trashCollectionsBySbasId[$sbasId]->get_coll_id() && $collection->get_coll_id() !== $trashCollectionsBySbasId[$sbasId]->get_coll_id()) {
|
if ($oldCollectionId == $trashCollectionsBySbasId[$sbasId]->get_coll_id() && $collection->get_coll_id() !== $trashCollectionsBySbasId[$sbasId]->get_coll_id()) {
|
||||||
// record is already in trash so active it
|
// record is already in trash so active it
|
||||||
foreach ($record->get_subdefs() as $subdef) {
|
foreach ($record->get_subdefs() as $subdef) {
|
||||||
if (($pl = $subdef->get_permalink())) {
|
if (($pl = $subdef->get_permalink())) {
|
||||||
|
|||||||
@@ -463,6 +463,8 @@ class PushController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
$manager = $this->getEntityManager();
|
||||||
|
|
||||||
$password = $this->getRandomGenerator()->generateString(128);
|
$password = $this->getRandomGenerator()->generateString(128);
|
||||||
|
|
||||||
$user = $this->getUserManipulator()->createUser($email, $password, $email);
|
$user = $this->getUserManipulator()->createUser($email, $password, $email);
|
||||||
@@ -476,12 +478,15 @@ class PushController extends Controller
|
|||||||
$user->setCompany($request->request->get('company'));
|
$user->setCompany($request->request->get('company'));
|
||||||
}
|
}
|
||||||
if ($request->request->get('job')) {
|
if ($request->request->get('job')) {
|
||||||
$user->setCompany($request->request->get('job'));
|
$user->setJob($request->request->get('job'));
|
||||||
}
|
}
|
||||||
if ($request->request->get('form_geonameid')) {
|
if ($request->request->get('city')) {
|
||||||
$this->getUserManipulator()->setGeonameId($user, $request->request->get('form_geonameid'));
|
$this->getUserManipulator()->setGeonameId($user, $request->request->get('city'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$manager->persist($user);
|
||||||
|
$manager->flush();
|
||||||
|
|
||||||
$result['message'] = $this->app->trans('User successfully created');
|
$result['message'] = $this->app->trans('User successfully created');
|
||||||
$result['success'] = true;
|
$result['success'] = true;
|
||||||
$result['user'] = $this->formatUser($user);
|
$result['user'] = $this->formatUser($user);
|
||||||
|
|||||||
@@ -179,7 +179,6 @@ class QueryController extends Controller
|
|||||||
};
|
};
|
||||||
|
|
||||||
$userManipulator->setUserSetting($user, 'last_jsonquery', (string)$request->request->get('jsQuery'));
|
$userManipulator->setUserSetting($user, 'last_jsonquery', (string)$request->request->get('jsQuery'));
|
||||||
|
|
||||||
$jsQuery = @json_decode((string)$request->request->get('jsQuery'), true);
|
$jsQuery = @json_decode((string)$request->request->get('jsQuery'), true);
|
||||||
if(($ft = $findFulltext($jsQuery['query'])) !== null) {
|
if(($ft = $findFulltext($jsQuery['query'])) !== null) {
|
||||||
$userManipulator->setUserSetting($user, 'start_page_query', $ft);
|
$userManipulator->setUserSetting($user, 'start_page_query', $ft);
|
||||||
@@ -215,7 +214,7 @@ class QueryController extends Controller
|
|||||||
if (min($d2top, $d2bottom) < 4) {
|
if (min($d2top, $d2bottom) < 4) {
|
||||||
if ($d2bottom < 4) {
|
if ($d2bottom < 4) {
|
||||||
if($page != 1){
|
if($page != 1){
|
||||||
$string .= "<a id='PREV_PAGE' class='btn btn-primary btn-mini'></a>";
|
$string .= "<a id='PREV_PAGE' class='btn btn-primary btn-mini icon-baseline-chevron_left-24px'></a>";
|
||||||
}
|
}
|
||||||
for ($i = 1; ($i <= 4 && (($i <= $npages) === true)); $i++) {
|
for ($i = 1; ($i <= 4 && (($i <= $npages) === true)); $i++) {
|
||||||
if ($i == $page)
|
if ($i == $page)
|
||||||
@@ -224,13 +223,13 @@ class QueryController extends Controller
|
|||||||
$string .= '<a class="btn btn-primary btn-mini search-navigate-action" data-page="'.$i.'">' . $i . '</a>';
|
$string .= '<a class="btn btn-primary btn-mini search-navigate-action" data-page="'.$i.'">' . $i . '</a>';
|
||||||
}
|
}
|
||||||
if ($npages > 4)
|
if ($npages > 4)
|
||||||
$string .= "<a id='NEXT_PAGE' class='btn btn-primary btn-mini'></a>";
|
$string .= "<a id='NEXT_PAGE' class='btn btn-primary btn-mini icon icon-baseline-chevron_right-24px'></a>";
|
||||||
$string .= '<a href="#" class="btn btn-primary btn-mini search-navigate-action" data-page="' . $npages . '" id="last"></a>';
|
$string .= '<a href="#" class="btn btn-primary btn-mini search-navigate-action icon icon-double-arrows" data-page="' . $npages . '" id="last"></a>';
|
||||||
} else {
|
} else {
|
||||||
$start = $npages - 4;
|
$start = $npages - 4;
|
||||||
if (($start) > 0){
|
if (($start) > 0){
|
||||||
$string .= '<a class="btn btn-primary btn-mini search-navigate-action" data-page="1" id="first"></a>';
|
$string .= '<a class="btn btn-primary btn-mini search-navigate-action" data-page="1" id="first"><span class="icon icon-double-arrows icon-inverse"></span></a>';
|
||||||
$string .= '<a id="PREV_PAGE" class="btn btn-primary btn-mini"></a>';
|
$string .= '<a id="PREV_PAGE" class="btn btn-primary btn-mini icon icon-baseline-chevron_left-24px"></a>';
|
||||||
}else
|
}else
|
||||||
$start = 1;
|
$start = 1;
|
||||||
for ($i = ($start); $i <= $npages; $i++) {
|
for ($i = ($start); $i <= $npages; $i++) {
|
||||||
@@ -240,11 +239,11 @@ class QueryController extends Controller
|
|||||||
$string .= '<a class="btn btn-primary btn-mini search-navigate-action" data-page="'.$i.'">' . $i . '</a>';
|
$string .= '<a class="btn btn-primary btn-mini search-navigate-action" data-page="'.$i.'">' . $i . '</a>';
|
||||||
}
|
}
|
||||||
if($page < $npages){
|
if($page < $npages){
|
||||||
$string .= "<a id='NEXT_PAGE' class='btn btn-primary btn-mini'></a>";
|
$string .= "<a id='NEXT_PAGE' class='btn btn-primary btn-mini icon icon-baseline-chevron_right-24px'></a>";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$string .= '<a class="btn btn-primary btn-mini btn-mini search-navigate-action" data-page="1" id="first"></a>';
|
$string .= '<a class="btn btn-primary btn-mini search-navigate-action" data-page="1" id="first"><span class="icon icon-double-arrows icon-inverse"></span></a>';
|
||||||
|
|
||||||
for ($i = ($page - 2); $i <= ($page + 2); $i++) {
|
for ($i = ($page - 2); $i <= ($page + 2); $i++) {
|
||||||
if ($i == $page)
|
if ($i == $page)
|
||||||
@@ -253,10 +252,10 @@ class QueryController extends Controller
|
|||||||
$string .= '<a class="btn btn-primary btn-mini search-navigate-action" data-page="'.$i.'">' . $i . '</a>';
|
$string .= '<a class="btn btn-primary btn-mini search-navigate-action" data-page="'.$i.'">' . $i . '</a>';
|
||||||
}
|
}
|
||||||
|
|
||||||
$string .= '<a href="#" class="btn btn-primary btn-mini search-navigate-action" data-page="' . $npages . '" id="last"></a>';
|
$string .= '<a href="#" class="btn btn-primary btn-mini search-navigate-action icon icon-double-arrows" data-page="' . $npages . '" id="last"></a>';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$string .= '<div style="display:none;"><div id="NEXT_PAGE"></div><div id="PREV_PAGE"></div></div>';
|
$string .= '<div style="display:none;"><div id="NEXT_PAGE" class="icon icon-baseline-chevron_right-24px"></div><div id="PREV_PAGE" class="icon icon-baseline-chevron_left-24px"></div></div>';
|
||||||
|
|
||||||
$explain = $this->render(
|
$explain = $this->render(
|
||||||
"prod/results/infos.html.twig",
|
"prod/results/infos.html.twig",
|
||||||
@@ -317,7 +316,7 @@ class QueryController extends Controller
|
|||||||
</tfoot>
|
</tfoot>
|
||||||
</table></div></div>'
|
</table></div></div>'
|
||||||
. '</div><a href="#" class="search-display-info" data-infos="' . str_replace('"', '"', $explain) . '">'
|
. '</div><a href="#" class="search-display-info" data-infos="' . str_replace('"', '"', $explain) . '">'
|
||||||
. $this->app->trans('%total% reponses', ['%total%' => '<span>'.$result->getTotal().'</span>']) . '</a>';
|
. $this->app->trans('%total% reponses', ['%total%' => '<span>'.number_format($result->getTotal(),null, null, ' ').'</span>']) . '</a>';
|
||||||
|
|
||||||
$json['infos'] = $infoResult;
|
$json['infos'] = $infoResult;
|
||||||
$json['navigationTpl'] = $string;
|
$json['navigationTpl'] = $string;
|
||||||
@@ -471,7 +470,6 @@ class QueryController extends Controller
|
|||||||
$json['results'] = $this->render($template, ['results'=> $result]);
|
$json['results'] = $this->render($template, ['results'=> $result]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return $this->app->json($json);
|
return $this->app->json($json);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,12 +15,11 @@ use Alchemy\Phrasea\Core\Configuration\DisplaySettingService;
|
|||||||
use Alchemy\Phrasea\Exception\SessionNotFound;
|
use Alchemy\Phrasea\Exception\SessionNotFound;
|
||||||
use Alchemy\Phrasea\Feed\Aggregate;
|
use Alchemy\Phrasea\Feed\Aggregate;
|
||||||
use Alchemy\Phrasea\Helper;
|
use Alchemy\Phrasea\Helper;
|
||||||
use Alchemy\Phrasea\Model\Entities\UserSetting;
|
use Alchemy\Phrasea\Helper\WorkZone as WorkzoneHelper;
|
||||||
use Alchemy\Phrasea\Model\Repositories\FeedRepository;
|
use Alchemy\Phrasea\Model\Repositories\FeedRepository;
|
||||||
use Symfony\Component\Finder\Finder;
|
|
||||||
use Symfony\Component\Finder\SplFileInfo;
|
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
|
||||||
|
|
||||||
class RootController extends Controller
|
class RootController extends Controller
|
||||||
{
|
{
|
||||||
use Application\Helper\FirewallAware;
|
use Application\Helper\FirewallAware;
|
||||||
@@ -41,12 +40,11 @@ class RootController extends Controller
|
|||||||
public function indexAction(Request $request) {
|
public function indexAction(Request $request) {
|
||||||
try {
|
try {
|
||||||
\Session_Logger::updateClientInfos($this->app, 1);
|
\Session_Logger::updateClientInfos($this->app, 1);
|
||||||
} catch (SessionNotFound $e) {
|
}
|
||||||
|
catch (SessionNotFound $e) {
|
||||||
return $this->app->redirectPath('logout');
|
return $this->app->redirectPath('logout');
|
||||||
}
|
}
|
||||||
|
|
||||||
$css = [];
|
|
||||||
|
|
||||||
$user = $this->getAuthenticatedUser();
|
$user = $this->getAuthenticatedUser();
|
||||||
$cssfile = $this->getSettings()->getUserSetting($user, 'css');
|
$cssfile = $this->getSettings()->getUserSetting($user, 'css');
|
||||||
|
|
||||||
@@ -85,6 +83,22 @@ class RootController extends Controller
|
|||||||
/** @var \Closure $filter */
|
/** @var \Closure $filter */
|
||||||
$filter = $this->app['plugin.filter_by_authorization'];
|
$filter = $this->app['plugin.filter_by_authorization'];
|
||||||
|
|
||||||
|
/* prepare work to extend whole taskbar... later
|
||||||
|
$menus = [
|
||||||
|
'push' => ['native'=>true, 'n'=>0],
|
||||||
|
'tools' => ['native'=>true, 'n'=>0],
|
||||||
|
];
|
||||||
|
/ ** @var ActionBarPluginInterface $plugin * /
|
||||||
|
foreach($filter('actionbar') as $kplugin=>$plugin) {
|
||||||
|
foreach($plugin->getActionBar() as $kmenu=>$menu) {
|
||||||
|
if(!array_key_exists($kmenu, $menus)) {
|
||||||
|
$menus[$kmenu] = ['native'=>false, 'n'=>0];
|
||||||
|
}
|
||||||
|
$menus[$kmenu]['n']++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
$plugins = [
|
$plugins = [
|
||||||
'workzone' => $filter('workzone'),
|
'workzone' => $filter('workzone'),
|
||||||
'actionbar' => $filter('actionbar'),
|
'actionbar' => $filter('actionbar'),
|
||||||
@@ -92,7 +106,7 @@ class RootController extends Controller
|
|||||||
|
|
||||||
return $this->render('prod/index.html.twig', [
|
return $this->render('prod/index.html.twig', [
|
||||||
'module_name' => 'Production',
|
'module_name' => 'Production',
|
||||||
'WorkZone' => new Helper\WorkZone($this->app, $request),
|
'WorkZone' => new WorkzoneHelper($this->app, $request),
|
||||||
'module_prod' => $helper,
|
'module_prod' => $helper,
|
||||||
'search_datas' => $helper->get_search_datas(),
|
'search_datas' => $helper->get_search_datas(),
|
||||||
'cssfile' => $cssfile,
|
'cssfile' => $cssfile,
|
||||||
@@ -105,7 +119,7 @@ class RootController extends Controller
|
|||||||
'feeds' => $feeds,
|
'feeds' => $feeds,
|
||||||
'aggregate' => $aggregate,
|
'aggregate' => $aggregate,
|
||||||
'GV_google_api' => $conf->get(['registry', 'webservices', 'google-charts-enabled']),
|
'GV_google_api' => $conf->get(['registry', 'webservices', 'google-charts-enabled']),
|
||||||
'geocodingProviders' => $conf->get(['geocoding-providers']),
|
'geocodingProviders' => $conf->get(['geocoding-providers']),
|
||||||
'search_status' => \databox_status::getSearchStatus($this->app),
|
'search_status' => \databox_status::getSearchStatus($this->app),
|
||||||
'thesau_js_list' => $thjslist,
|
'thesau_js_list' => $thjslist,
|
||||||
'thesau_json_sbas' => json_encode($sbas),
|
'thesau_json_sbas' => json_encode($sbas),
|
||||||
|
|||||||
@@ -367,15 +367,17 @@ class UploadController extends Controller
|
|||||||
$postMaxSize = PHP_INT_MAX;
|
$postMaxSize = PHP_INT_MAX;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$r = 0;
|
||||||
switch (strtolower(substr($postMaxSize, -1))) {
|
switch (strtolower(substr($postMaxSize, -1))) {
|
||||||
/** @noinspection PhpMissingBreakStatementInspection */
|
/** @noinspection PhpMissingBreakStatementInspection */
|
||||||
case 'g':
|
case 'g':
|
||||||
$postMaxSize *= 1024;
|
$r += 10;
|
||||||
/** @noinspection PhpMissingBreakStatementInspection */
|
/** @noinspection PhpMissingBreakStatementInspection */
|
||||||
case 'm':
|
case 'm':
|
||||||
$postMaxSize *= 1024;
|
$r += 10;
|
||||||
case 'k':
|
case 'k':
|
||||||
$postMaxSize *= 1024;
|
$r += 10;
|
||||||
|
$postMaxSize = ((int)($postMaxSize))<<$r;
|
||||||
}
|
}
|
||||||
|
|
||||||
return min(UploadedFile::getMaxFilesize(), (int) $postMaxSize);
|
return min(UploadedFile::getMaxFilesize(), (int) $postMaxSize);
|
||||||
|
|||||||
@@ -522,27 +522,35 @@ class AccountController extends Controller
|
|||||||
|
|
||||||
$list = array_keys($this->app['repo.collections-registry']->getBaseIdMap());
|
$list = array_keys($this->app['repo.collections-registry']->getBaseIdMap());
|
||||||
|
|
||||||
$this->app->getAclForUser($user)->revoke_access_from_bases($list);
|
try {
|
||||||
|
$this->app->getAclForUser($user)->revoke_access_from_bases($list);
|
||||||
|
}
|
||||||
|
catch (\Exception $e) {
|
||||||
|
// one or more access could not be revoked ? the user will not be phantom
|
||||||
|
$this->app->addFlash('error', $this->app->trans('phraseanet::error: failed to revoke some user access'));
|
||||||
|
}
|
||||||
|
|
||||||
if ($this->app->getAclForUser($user)->is_phantom()) {
|
if ($this->app->getAclForUser($user)->is_phantom()) {
|
||||||
// send confirmation email: the account has been deleted
|
// send confirmation email: the account has been deleted
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$receiver = Receiver::fromUser($user);
|
$receiver = Receiver::fromUser($user);
|
||||||
} catch (InvalidArgumentException $e) {
|
$mail = MailSuccessAccountDelete::create($this->app, $receiver);
|
||||||
$this->app->addFlash('error', $this->app->trans('phraseanet::erreur: echec du serveur de mail'));
|
}
|
||||||
|
catch (InvalidArgumentException $e) {
|
||||||
|
$this->app->addFlash('error', $this->app->trans('phraseanet::erreur: echec du serveur de mail'));
|
||||||
|
$mail = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
$mail = MailSuccessAccountDelete::create($this->app, $receiver);
|
|
||||||
|
|
||||||
$this->app['manipulator.user']->delete($user);
|
$this->app['manipulator.user']->delete($user);
|
||||||
|
|
||||||
$this->deliver($mail);
|
if($mail) {
|
||||||
|
$this->deliver($mail);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->getAuthenticator()->closeAccount();
|
||||||
|
$this->app->addFlash('info', $this->app->trans('phraseanet::account The account has been deleted'));
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->getAuthenticator()->closeAccount();
|
|
||||||
$this->app->addFlash('info', $this->app->trans('phraseanet::account The account has been deleted'));
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -265,7 +265,7 @@ class LoginController extends Controller
|
|||||||
return $this->render('login/register-classic.html.twig', array_merge(
|
return $this->render('login/register-classic.html.twig', array_merge(
|
||||||
$this->getDefaultTemplateVariables($request),
|
$this->getDefaultTemplateVariables($request),
|
||||||
[
|
[
|
||||||
'geonames_server_uri' => str_replace(sprintf('%s:', parse_url($url, PHP_URL_SCHEME)), '', $url),
|
'geonames_server_uri' => $url,
|
||||||
'form' => $form->createView()
|
'form' => $form->createView()
|
||||||
]));
|
]));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -94,10 +94,9 @@ class SessionController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check session state
|
* @param Request $request
|
||||||
*
|
|
||||||
* @param Request $request
|
|
||||||
* @return JsonResponse
|
* @return JsonResponse
|
||||||
|
* @throws \Exception in case "new \DateTime()" fails ?
|
||||||
*/
|
*/
|
||||||
public function updateSession(Request $request)
|
public function updateSession(Request $request)
|
||||||
{
|
{
|
||||||
@@ -120,7 +119,8 @@ class SessionController extends Controller
|
|||||||
|
|
||||||
return $this->app->json($ret);
|
return $this->app->json($ret);
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
$ret['status'] = 'disconnected';
|
$ret['status'] = 'disconnected';
|
||||||
|
|
||||||
return $this->app->json($ret);
|
return $this->app->json($ret);
|
||||||
@@ -128,7 +128,8 @@ class SessionController extends Controller
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
$this->getApplicationBox()->get_connection();
|
$this->getApplicationBox()->get_connection();
|
||||||
} catch (\Exception $e) {
|
}
|
||||||
|
catch (\Exception $e) {
|
||||||
return $this->app->json($ret);
|
return $this->app->json($ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -148,8 +149,9 @@ class SessionController extends Controller
|
|||||||
$module->setModuleId($moduleId);
|
$module->setModuleId($moduleId);
|
||||||
$module->setSession($session);
|
$module->setSession($session);
|
||||||
$manager->persist($module);
|
$manager->persist($module);
|
||||||
} else {
|
}
|
||||||
$manager->persist($session->getModuleById($moduleId)->setUpdated(new \DateTime()));
|
else {
|
||||||
|
$manager->persist($session->getModuleById($moduleId)->setUpdated($now));
|
||||||
}
|
}
|
||||||
|
|
||||||
$manager->persist($session);
|
$manager->persist($session);
|
||||||
@@ -231,7 +233,10 @@ class SessionController extends Controller
|
|||||||
*/
|
*/
|
||||||
private function getBasketRepository()
|
private function getBasketRepository()
|
||||||
{
|
{
|
||||||
return $this->getEntityManager()->getRepository('Phraseanet:Basket');
|
/** @var BasketRepository $ret */
|
||||||
|
$ret = $this->getEntityManager()->getRepository('Phraseanet:Basket');
|
||||||
|
|
||||||
|
return $ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -146,7 +146,7 @@ class RegistryFormManipulator
|
|||||||
],
|
],
|
||||||
'webservices' => [
|
'webservices' => [
|
||||||
'google-charts-enabled' => true,
|
'google-charts-enabled' => true,
|
||||||
'geonames-server' => 'http://geonames.alchemyasp.com/',
|
'geonames-server' => 'https://geonames.alchemyasp.com/',
|
||||||
'captchas-enabled' => false,
|
'captchas-enabled' => false,
|
||||||
'recaptcha-public-key' => '',
|
'recaptcha-public-key' => '',
|
||||||
'recaptcha-private-key' => '',
|
'recaptcha-private-key' => '',
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ class Version
|
|||||||
/**
|
/**
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
private $number = '4.1.0-alpha.14a';
|
private $number = '4.1.0-alpha.15a';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var string
|
* @var string
|
||||||
|
|||||||
@@ -11,28 +11,29 @@
|
|||||||
|
|
||||||
namespace Alchemy\Phrasea\Helper;
|
namespace Alchemy\Phrasea\Helper;
|
||||||
|
|
||||||
use Doctrine\Common\Collections\ArrayCollection;
|
|
||||||
use Alchemy\Phrasea\Model\Entities\Basket as BasketEntity;
|
use Alchemy\Phrasea\Model\Entities\Basket as BasketEntity;
|
||||||
|
use Alchemy\Phrasea\Model\Repositories\BasketRepository;
|
||||||
|
use Alchemy\Phrasea\Model\Repositories\StoryWZRepository;
|
||||||
|
use Doctrine\Common\Collections\ArrayCollection;
|
||||||
|
|
||||||
class WorkZone extends Helper
|
class WorkZone extends Helper
|
||||||
{
|
{
|
||||||
const BASKETS = 'baskets';
|
const BASKETS = 'baskets';
|
||||||
const STORIES = 'stories';
|
const STORIES = 'stories';
|
||||||
const VALIDATIONS = 'validations';
|
const VALIDATIONS = 'validations';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* Returns an ArrayCollection containing three keys :
|
* Returns an ArrayCollection containing three keys :
|
||||||
* - self::BASKETS : an ArrayCollection of the actives baskets
|
* - self::BASKETS : an ArrayCollection of the actives baskets (Non Archived)
|
||||||
* (Non Archived)
|
|
||||||
* - self::STORIES : an ArrayCollection of working stories
|
* - self::STORIES : an ArrayCollection of working stories
|
||||||
* - self::VALIDATIONS : the validation people are waiting from me
|
* - self::VALIDATIONS : the validation people are waiting from me
|
||||||
*
|
*
|
||||||
* @return \Doctrine\Common\Collections\ArrayCollection
|
* @param null|string $sort "date"|"name"
|
||||||
|
* @return ArrayCollection
|
||||||
*/
|
*/
|
||||||
public function getContent($sort)
|
public function getContent($sort = null)
|
||||||
{
|
{
|
||||||
/* @var $repo_baskets Alchemy\Phrasea\Model\Repositories\BasketRepository */
|
/* @var $repo_baskets BasketRepository */
|
||||||
$repo_baskets = $this->app['repo.baskets'];
|
$repo_baskets = $this->app['repo.baskets'];
|
||||||
|
|
||||||
$sort = in_array($sort, ['date', 'name']) ? $sort : 'name';
|
$sort = in_array($sort, ['date', 'name']) ? $sort : 'name';
|
||||||
@@ -42,7 +43,7 @@ class WorkZone extends Helper
|
|||||||
$baskets = $repo_baskets->findActiveByUser($this->app->getAuthenticatedUser(), $sort);
|
$baskets = $repo_baskets->findActiveByUser($this->app->getAuthenticatedUser(), $sort);
|
||||||
|
|
||||||
// force creation of a default basket
|
// force creation of a default basket
|
||||||
if (0 === count($baskets)) {
|
if (count($baskets) === 0) {
|
||||||
$basket = new BasketEntity();
|
$basket = new BasketEntity();
|
||||||
|
|
||||||
$basket->setName($this->app->trans('Default basket'));
|
$basket->setName($this->app->trans('Default basket'));
|
||||||
@@ -55,7 +56,7 @@ class WorkZone extends Helper
|
|||||||
|
|
||||||
$validations = $repo_baskets->findActiveValidationByUser($this->app->getAuthenticatedUser(), $sort);
|
$validations = $repo_baskets->findActiveValidationByUser($this->app->getAuthenticatedUser(), $sort);
|
||||||
|
|
||||||
/* @var $repo_stories Alchemy\Phrasea\Model\Repositories\StoryWZRepository */
|
/* @var $repo_stories StoryWZRepository */
|
||||||
$repo_stories = $this->app['repo.story-wz'];
|
$repo_stories = $this->app['repo.story-wz'];
|
||||||
|
|
||||||
$stories = $repo_stories->findByUser($this->app, $this->app->getAuthenticatedUser(), $sort);
|
$stories = $repo_stories->findByUser($this->app, $this->app->getAuthenticatedUser(), $sort);
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ class SubdefGenerator
|
|||||||
|
|
||||||
public function generateSubdefs(\record_adapter $record, array $wanted_subdefs = null)
|
public function generateSubdefs(\record_adapter $record, array $wanted_subdefs = null)
|
||||||
{
|
{
|
||||||
if ($record->get_hd_file() !== null) {
|
if ($record->get_hd_file() !== null && $record->get_hd_file()->getMimeType() == "application/x-indesign") {
|
||||||
$mediaSource = $this->mediavorus->guess($record->get_hd_file()->getPathname());
|
$mediaSource = $this->mediavorus->guess($record->get_hd_file()->getPathname());
|
||||||
$metadatas = $mediaSource->getMetadatas();
|
$metadatas = $mediaSource->getMetadatas();
|
||||||
|
|
||||||
@@ -69,15 +69,27 @@ class SubdefGenerator
|
|||||||
if(!isset($this->tmpFilesystem)){
|
if(!isset($this->tmpFilesystem)){
|
||||||
$this->tmpFilesystem = Manager::create();
|
$this->tmpFilesystem = Manager::create();
|
||||||
}
|
}
|
||||||
$tmpDir = $this->tmpFilesystem->createTemporaryDirectory();
|
$tmpDir = $this->tmpFilesystem->createTemporaryDirectory(0777, 500);
|
||||||
|
|
||||||
try {
|
$files = $this->app['exiftool.preview-extractor']->extract($record->get_hd_file()->getPathname(), $tmpDir);
|
||||||
$this->app['filesystem']->dumpFile($tmpDir.'/file.jpg', $metadatas->get('XMP-xmp:PageImage')->getValue()->asString());
|
|
||||||
$this->tmpFilePath = $tmpDir.'/file.jpg';
|
$selected = null;
|
||||||
} catch (\Exception $e) {
|
$size = null;
|
||||||
$this->logger->error(sprintf('Unable to write temporary file : %s', $e->getMessage()));
|
|
||||||
|
foreach ($files as $file) {
|
||||||
|
if ($file->isDir() || $file->isDot()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_null($selected) || $file->getSize() > $size) {
|
||||||
|
$selected = $file->getPathname();
|
||||||
|
$size = $file->getSize();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($selected) {
|
||||||
|
$this->tmpFilePath = $selected;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -73,6 +73,9 @@ class SubdefSubstituer
|
|||||||
|
|
||||||
$this->createMediaSubdef($record, 'document', $media);
|
$this->createMediaSubdef($record, 'document', $media);
|
||||||
|
|
||||||
|
$record->setMimeType($media->getFile()->getMimeType());
|
||||||
|
$record->setType($media->getType());
|
||||||
|
|
||||||
$record->write_metas();
|
$record->write_metas();
|
||||||
|
|
||||||
if ($shouldSubdefsBeRebuilt) {
|
if ($shouldSubdefsBeRebuilt) {
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ namespace Alchemy\Phrasea\Metadata;
|
|||||||
use Alchemy\Phrasea\Border\File;
|
use Alchemy\Phrasea\Border\File;
|
||||||
use Alchemy\Phrasea\Databox\DataboxRepository;
|
use Alchemy\Phrasea\Databox\DataboxRepository;
|
||||||
use Alchemy\Phrasea\Metadata\Tag\NoSource;
|
use Alchemy\Phrasea\Metadata\Tag\NoSource;
|
||||||
|
use DateTime;
|
||||||
use PHPExiftool\Driver\Metadata\Metadata;
|
use PHPExiftool\Driver\Metadata\Metadata;
|
||||||
|
|
||||||
class PhraseanetMetadataSetter
|
class PhraseanetMetadataSetter
|
||||||
@@ -66,8 +67,16 @@ class PhraseanetMetadataSetter
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$data['value'] = $value;
|
if ($field->get_type() == 'date') {
|
||||||
|
try {
|
||||||
|
$dateTime = new DateTime($value);
|
||||||
|
$value = $dateTime->format('Y/m/d H:i:s');
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
// $value unchanged
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$data['value'] = $value;
|
||||||
$metadataInRecordFormat[] = $data;
|
$metadataInRecordFormat[] = $data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,21 +54,24 @@ class BasketRepository extends EntityRepository
|
|||||||
/**
|
/**
|
||||||
* Returns all basket for a given user that are not marked as archived
|
* Returns all basket for a given user that are not marked as archived
|
||||||
*
|
*
|
||||||
* @param User $user
|
* @param User $user
|
||||||
|
* @param null|string $sort
|
||||||
* @return Basket[]
|
* @return Basket[]
|
||||||
*/
|
*/
|
||||||
public function findActiveByUser(User $user, $sort = null)
|
public function findActiveByUser(User $user, $sort = null)
|
||||||
{
|
{
|
||||||
$dql = 'SELECT b
|
// checked : 4 usages, "b.elements" is useless
|
||||||
FROM Phraseanet:Basket b
|
$dql = "SELECT b\n"
|
||||||
LEFT JOIN b.elements e
|
. " FROM Phraseanet:Basket b\n"
|
||||||
WHERE b.user = :usr_id
|
// . " LEFT JOIN b.elements e\n" //
|
||||||
AND b.archived = false';
|
. " WHERE b.user = :usr_id\n"
|
||||||
|
. " AND b.archived = false";
|
||||||
|
|
||||||
if ($sort == 'date') {
|
if ($sort == 'date') {
|
||||||
$dql .= ' ORDER BY b.created DESC';
|
$dql .= "\n ORDER BY b.created DESC";
|
||||||
} elseif ($sort == 'name') {
|
}
|
||||||
$dql .= ' ORDER BY b.name ASC';
|
elseif ($sort == 'name') {
|
||||||
|
$dql .= "\n ORDER BY b.name ASC";
|
||||||
}
|
}
|
||||||
|
|
||||||
$query = $this->_em->createQuery($dql);
|
$query = $this->_em->createQuery($dql);
|
||||||
@@ -80,24 +83,27 @@ class BasketRepository extends EntityRepository
|
|||||||
/**
|
/**
|
||||||
* Returns all unread basket for a given user that are not marked as archived
|
* Returns all unread basket for a given user that are not marked as archived
|
||||||
*
|
*
|
||||||
* @param User $user
|
* @param User $user
|
||||||
* @return Basket[]
|
* @return Basket[]
|
||||||
*/
|
*/
|
||||||
public function findUnreadActiveByUser(User $user)
|
public function findUnreadActiveByUser(User $user)
|
||||||
{
|
{
|
||||||
$dql = 'SELECT b
|
// checked : 2 usages, "b.elements" is useless
|
||||||
FROM Phraseanet:Basket b
|
$dql = "SELECT b\n"
|
||||||
JOIN b.elements e
|
. " FROM Phraseanet:Basket b\n"
|
||||||
LEFT JOIN b.validation s
|
// . " JOIN b.elements e\n"
|
||||||
LEFT JOIN s.participants p
|
. " LEFT JOIN b.validation s\n"
|
||||||
WHERE b.archived = false
|
. " LEFT JOIN s.participants p\n"
|
||||||
AND (
|
. " WHERE b.archived = false\n"
|
||||||
(b.user = :usr_id_owner AND b.isRead = false)
|
. " AND (\n"
|
||||||
OR (b.user != :usr_id_ownertwo
|
. " (b.user = :usr_id_owner AND b.isRead = false)\n"
|
||||||
AND p.user = :usr_id_participant
|
. " OR \n"
|
||||||
AND p.is_aware = false)
|
. " (b.user != :usr_id_ownertwo\n"
|
||||||
)
|
. " AND p.user = :usr_id_participant\n"
|
||||||
AND (s.expires IS NULL OR s.expires > CURRENT_TIMESTAMP())';
|
. " AND p.is_aware = false\n"
|
||||||
|
. " AND s.expires > CURRENT_TIMESTAMP()\n"
|
||||||
|
. " )\n"
|
||||||
|
. " )";
|
||||||
|
|
||||||
$params = [
|
$params = [
|
||||||
'usr_id_owner' => $user->getId(),
|
'usr_id_owner' => $user->getId(),
|
||||||
@@ -115,11 +121,22 @@ class BasketRepository extends EntityRepository
|
|||||||
* Returns all baskets that are in validation session not expired and
|
* Returns all baskets that are in validation session not expired and
|
||||||
* where a specified user is participant (not owner)
|
* where a specified user is participant (not owner)
|
||||||
*
|
*
|
||||||
* @param User $user
|
* @param User $user
|
||||||
|
* @param null|string $sort
|
||||||
* @return Basket[]
|
* @return Basket[]
|
||||||
*/
|
*/
|
||||||
public function findActiveValidationByUser(User $user, $sort = null)
|
public function findActiveValidationByUser(User $user, $sort = null)
|
||||||
{
|
{
|
||||||
|
// checked : 2 usages, "b.elements" seems useless.
|
||||||
|
$dql = "SELECT b\n"
|
||||||
|
. "FROM Phraseanet:Basket b\n"
|
||||||
|
// . " JOIN b.elements e\n"
|
||||||
|
// . " JOIN e.validation_datas v\n"
|
||||||
|
. " JOIN b.validation s\n"
|
||||||
|
. " JOIN s.participants p\n"
|
||||||
|
. "WHERE b.user != ?1 AND p.user = ?2\n"
|
||||||
|
. " AND (s.expires IS NULL OR s.expires > CURRENT_TIMESTAMP())";
|
||||||
|
|
||||||
$dql = 'SELECT b
|
$dql = 'SELECT b
|
||||||
FROM Phraseanet:Basket b
|
FROM Phraseanet:Basket b
|
||||||
JOIN b.elements e
|
JOIN b.elements e
|
||||||
@@ -130,9 +147,9 @@ class BasketRepository extends EntityRepository
|
|||||||
AND (s.expires IS NULL OR s.expires > CURRENT_TIMESTAMP()) ';
|
AND (s.expires IS NULL OR s.expires > CURRENT_TIMESTAMP()) ';
|
||||||
|
|
||||||
if ($sort == 'date') {
|
if ($sort == 'date') {
|
||||||
$dql .= ' ORDER BY b.created DESC';
|
$dql .= "\nORDER BY b.created DESC";
|
||||||
} elseif ($sort == 'name') {
|
} elseif ($sort == 'name') {
|
||||||
$dql .= ' ORDER BY b.name ASC';
|
$dql .= "\nORDER BY b.name ASC";
|
||||||
}
|
}
|
||||||
|
|
||||||
$query = $this->_em->createQuery($dql);
|
$query = $this->_em->createQuery($dql);
|
||||||
@@ -152,10 +169,11 @@ class BasketRepository extends EntityRepository
|
|||||||
*/
|
*/
|
||||||
public function findUserBasket($basket_id, User $user, $requireOwner)
|
public function findUserBasket($basket_id, User $user, $requireOwner)
|
||||||
{
|
{
|
||||||
$dql = 'SELECT b
|
// checked : 3 usages, "b.elements e" seems useless
|
||||||
FROM Phraseanet:Basket b
|
$dql = "SELECT b\n"
|
||||||
LEFT JOIN b.elements e
|
. " FROM Phraseanet:Basket b\n"
|
||||||
WHERE b.id = :basket_id';
|
// . " LEFT JOIN b.elements e\n"
|
||||||
|
. " WHERE b.id = :basket_id";
|
||||||
|
|
||||||
$query = $this->_em->createQuery($dql);
|
$query = $this->_em->createQuery($dql);
|
||||||
$query->setParameters(['basket_id' => $basket_id]);
|
$query->setParameters(['basket_id' => $basket_id]);
|
||||||
@@ -188,7 +206,7 @@ class BasketRepository extends EntityRepository
|
|||||||
|
|
||||||
public function findContainingRecordForUser(\record_adapter $record, User $user)
|
public function findContainingRecordForUser(\record_adapter $record, User $user)
|
||||||
{
|
{
|
||||||
|
// todo : check "e.sbas_id = e.sbas_id" ???
|
||||||
$dql = 'SELECT b
|
$dql = 'SELECT b
|
||||||
FROM Phraseanet:Basket b
|
FROM Phraseanet:Basket b
|
||||||
JOIN b.elements e
|
JOIN b.elements e
|
||||||
@@ -210,30 +228,31 @@ class BasketRepository extends EntityRepository
|
|||||||
{
|
{
|
||||||
switch ($type) {
|
switch ($type) {
|
||||||
case self::RECEIVED:
|
case self::RECEIVED:
|
||||||
$dql = 'SELECT b
|
// todo : check when called, and if "LEFT JOIN b.elements e" is usefull
|
||||||
FROM Phraseanet:Basket b
|
$dql = "SELECT b\n"
|
||||||
JOIN b.elements e
|
. "FROM Phraseanet:Basket b\n"
|
||||||
WHERE b.user = :usr_id AND b.pusher_id IS NOT NULL';
|
. " JOIN b.elements e\n"
|
||||||
|
. "WHERE b.user = :usr_id AND b.pusher_id IS NOT NULL";
|
||||||
$params = [
|
$params = [
|
||||||
'usr_id' => $user->getId()
|
'usr_id' => $user->getId()
|
||||||
];
|
];
|
||||||
break;
|
break;
|
||||||
case self::VALIDATION_DONE:
|
case self::VALIDATION_DONE:
|
||||||
$dql = 'SELECT b
|
// todo : check when called, and if "LEFT JOIN b.elements e" is usefull
|
||||||
FROM Phraseanet:Basket b
|
$dql = "SELECT b\n"
|
||||||
JOIN b.elements e
|
. "FROM Phraseanet:Basket b\n"
|
||||||
JOIN b.validation s
|
. " JOIN b.elements e\n"
|
||||||
JOIN s.participants p
|
. " JOIN b.validation s\n"
|
||||||
WHERE b.user != ?1 AND p.user = ?2';
|
. " JOIN s.participants p\n"
|
||||||
|
. "WHERE b.user != ?1 AND p.user = ?2";
|
||||||
$params = [
|
$params = [
|
||||||
1 => $user->getId()
|
1 => $user->getId(),
|
||||||
, 2 => $user->getId()
|
2 => $user->getId()
|
||||||
];
|
];
|
||||||
break;
|
break;
|
||||||
case self::VALIDATION_SENT:
|
case self::VALIDATION_SENT:
|
||||||
$dql = 'SELECT b
|
$dql = 'SELECT b
|
||||||
FROM Phraseanet:Basket b
|
FROM Phraseanet:Basket b
|
||||||
JOIN b.elements e
|
|
||||||
JOIN b.validation v
|
JOIN b.validation v
|
||||||
WHERE b.user = :usr_id';
|
WHERE b.user = :usr_id';
|
||||||
$params = [
|
$params = [
|
||||||
@@ -243,7 +262,6 @@ class BasketRepository extends EntityRepository
|
|||||||
case self::MYBASKETS:
|
case self::MYBASKETS:
|
||||||
$dql = 'SELECT b
|
$dql = 'SELECT b
|
||||||
FROM Phraseanet:Basket b
|
FROM Phraseanet:Basket b
|
||||||
LEFT JOIN b.elements e
|
|
||||||
LEFT JOIN b.validation s
|
LEFT JOIN b.validation s
|
||||||
LEFT JOIN s.participants p
|
LEFT JOIN s.participants p
|
||||||
WHERE (b.user = :usr_id)';
|
WHERE (b.user = :usr_id)';
|
||||||
@@ -252,6 +270,7 @@ class BasketRepository extends EntityRepository
|
|||||||
];
|
];
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
// todo : check when called, and if "LEFT JOIN b.elements e" is usefull
|
||||||
$dql = 'SELECT b
|
$dql = 'SELECT b
|
||||||
FROM Phraseanet:Basket b
|
FROM Phraseanet:Basket b
|
||||||
LEFT JOIN b.elements e
|
LEFT JOIN b.elements e
|
||||||
@@ -297,6 +316,7 @@ class BasketRepository extends EntityRepository
|
|||||||
*/
|
*/
|
||||||
public function findActiveValidationAndBasketByUser(User $user, $sort = null)
|
public function findActiveValidationAndBasketByUser(User $user, $sort = null)
|
||||||
{
|
{
|
||||||
|
// todo : check caller and if "LEFT JOIN b.elements e" is usefull
|
||||||
$dql = 'SELECT b
|
$dql = 'SELECT b
|
||||||
FROM Phraseanet:Basket b
|
FROM Phraseanet:Basket b
|
||||||
LEFT JOIN b.elements e
|
LEFT JOIN b.elements e
|
||||||
|
|||||||
@@ -30,6 +30,11 @@ class FieldKey implements Key, QueryPostProcessor
|
|||||||
return $this->getField($context)->getIndexField($raw);
|
return $this->getField($context)->getIndexField($raw);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getFieldType(QueryContext $context)
|
||||||
|
{
|
||||||
|
return $this->getField($context)->getType();
|
||||||
|
}
|
||||||
|
|
||||||
public function isValueCompatible($value, QueryContext $context)
|
public function isValueCompatible($value, QueryContext $context)
|
||||||
{
|
{
|
||||||
return ValueChecker::isValueCompatible($this->getField($context), $value);
|
return ValueChecker::isValueCompatible($this->getField($context), $value);
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ use Alchemy\Phrasea\SearchEngine\Elastic\Search\QueryContext;
|
|||||||
|
|
||||||
interface Key
|
interface Key
|
||||||
{
|
{
|
||||||
|
public function getFieldType(QueryContext $context);
|
||||||
public function getIndexField(QueryContext $context, $raw = false);
|
public function getIndexField(QueryContext $context, $raw = false);
|
||||||
public function isValueCompatible($value, QueryContext $context);
|
public function isValueCompatible($value, QueryContext $context);
|
||||||
public function __toString();
|
public function __toString();
|
||||||
|
|||||||
@@ -23,6 +23,11 @@ class MetadataKey implements Key
|
|||||||
return $this->getTag($context)->getIndexField($raw);
|
return $this->getTag($context)->getIndexField($raw);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getFieldType(QueryContext $context)
|
||||||
|
{
|
||||||
|
return $this->getTag($context)->getType();
|
||||||
|
}
|
||||||
|
|
||||||
public function isValueCompatible($value, QueryContext $context)
|
public function isValueCompatible($value, QueryContext $context)
|
||||||
{
|
{
|
||||||
return ValueChecker::isValueCompatible($this->getTag($context), $value);
|
return ValueChecker::isValueCompatible($this->getTag($context), $value);
|
||||||
|
|||||||
@@ -52,6 +52,11 @@ class NativeKey implements Key
|
|||||||
$this->key = $key;
|
$this->key = $key;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getFieldType(QueryContext $context)
|
||||||
|
{
|
||||||
|
return $this->type;
|
||||||
|
}
|
||||||
|
|
||||||
public function getIndexField(QueryContext $context, $raw = false)
|
public function getIndexField(QueryContext $context, $raw = false)
|
||||||
{
|
{
|
||||||
return $this->key;
|
return $this->key;
|
||||||
|
|||||||
@@ -2,18 +2,20 @@
|
|||||||
|
|
||||||
namespace Alchemy\Phrasea\SearchEngine\Elastic\AST\KeyValue;
|
namespace Alchemy\Phrasea\SearchEngine\Elastic\AST\KeyValue;
|
||||||
|
|
||||||
|
use Alchemy\Phrasea\SearchEngine\Elastic\FieldMapping;
|
||||||
|
use Alchemy\Phrasea\SearchEngine\Elastic\RecordHelper;
|
||||||
|
use Alchemy\Phrasea\SearchEngine\Elastic\Structure\Field as StructureField;
|
||||||
use Assert\Assertion;
|
use Assert\Assertion;
|
||||||
use Alchemy\Phrasea\SearchEngine\Elastic\AST\KeyValue\FieldKey;
|
|
||||||
use Alchemy\Phrasea\SearchEngine\Elastic\AST\KeyValue\Key;
|
|
||||||
use Alchemy\Phrasea\SearchEngine\Elastic\AST\Node;
|
use Alchemy\Phrasea\SearchEngine\Elastic\AST\Node;
|
||||||
use Alchemy\Phrasea\SearchEngine\Elastic\Exception\QueryException;
|
use Alchemy\Phrasea\SearchEngine\Elastic\Exception\QueryException;
|
||||||
use Alchemy\Phrasea\SearchEngine\Elastic\Search\QueryContext;
|
use Alchemy\Phrasea\SearchEngine\Elastic\Search\QueryContext;
|
||||||
use Alchemy\Phrasea\SearchEngine\Elastic\Search\QueryHelper;
|
|
||||||
use Alchemy\Phrasea\SearchEngine\Elastic\Search\QueryPostProcessor;
|
use Alchemy\Phrasea\SearchEngine\Elastic\Search\QueryPostProcessor;
|
||||||
|
|
||||||
class RangeExpression extends Node
|
class RangeExpression extends Node
|
||||||
{
|
{
|
||||||
|
/** @var FieldKey */
|
||||||
private $key;
|
private $key;
|
||||||
|
|
||||||
private $lower_bound;
|
private $lower_bound;
|
||||||
private $lower_inclusive;
|
private $lower_inclusive;
|
||||||
private $higher_bound;
|
private $higher_bound;
|
||||||
@@ -55,20 +57,34 @@ class RangeExpression extends Node
|
|||||||
public function buildQuery(QueryContext $context)
|
public function buildQuery(QueryContext $context)
|
||||||
{
|
{
|
||||||
$params = array();
|
$params = array();
|
||||||
if ($this->lower_bound !== null) {
|
/** @var StructureField $field */
|
||||||
$this->assertValueCompatible($this->lower_bound, $context);
|
// $field = $this->key->getField($context);
|
||||||
if ($this->lower_inclusive) {
|
$lower_bound = $this->lower_bound;
|
||||||
$params['gte'] = $this->lower_bound;
|
$higher_bound = $this->higher_bound;
|
||||||
} else {
|
|
||||||
$params['gt'] = $this->lower_bound;
|
if($this->key->getFieldType($context) === FieldMapping::TYPE_DATE) {
|
||||||
|
if($lower_bound !== null) {
|
||||||
|
$lower_bound = RecordHelper::sanitizeDate($lower_bound);
|
||||||
|
}
|
||||||
|
if($higher_bound !== null) {
|
||||||
|
$higher_bound = RecordHelper::sanitizeDate($higher_bound);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ($this->higher_bound !== null) {
|
|
||||||
$this->assertValueCompatible($this->higher_bound, $context);
|
if ($lower_bound !== null) {
|
||||||
if ($this->higher_inclusive) {
|
$this->assertValueCompatible($lower_bound, $context);
|
||||||
$params['lte'] = $this->higher_bound;
|
if ($this->lower_inclusive) {
|
||||||
|
$params['gte'] = $lower_bound;
|
||||||
} else {
|
} else {
|
||||||
$params['lt'] = $this->higher_bound;
|
$params['gt'] = $lower_bound;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($higher_bound !== null) {
|
||||||
|
$this->assertValueCompatible($higher_bound, $context);
|
||||||
|
if ($this->higher_inclusive) {
|
||||||
|
$params['lte'] = $higher_bound;
|
||||||
|
} else {
|
||||||
|
$params['lt'] = $higher_bound;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -34,6 +34,11 @@ class TimestampKey implements Key, Typed
|
|||||||
return FieldMapping::TYPE_DATE;
|
return FieldMapping::TYPE_DATE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getFieldType(QueryContext $context)
|
||||||
|
{
|
||||||
|
return FieldMapping::TYPE_DATE;
|
||||||
|
}
|
||||||
|
|
||||||
public function getIndexField(QueryContext $context, $raw = false)
|
public function getIndexField(QueryContext $context, $raw = false)
|
||||||
{
|
{
|
||||||
return $this->index_field;
|
return $this->index_field;
|
||||||
|
|||||||
@@ -396,10 +396,10 @@ class ElasticSearchEngine implements SearchEngineInterface
|
|||||||
if ($options->getDateFields() && ($options->getMaxDate() || $options->getMinDate())) {
|
if ($options->getDateFields() && ($options->getMaxDate() || $options->getMinDate())) {
|
||||||
$range = [];
|
$range = [];
|
||||||
if ($options->getMaxDate()) {
|
if ($options->getMaxDate()) {
|
||||||
$range['lte'] = $options->getMaxDate()->format(FieldMapping::DATE_FORMAT_CAPTION_PHP);
|
$range['lte'] = $options->getMaxDate()->format('Y-m-d');
|
||||||
}
|
}
|
||||||
if ($options->getMinDate()) {
|
if ($options->getMinDate()) {
|
||||||
$range['gte'] = $options->getMinDate()->format(FieldMapping::DATE_FORMAT_CAPTION_PHP);
|
$range['gte'] = $options->getMinDate()->format('Y-m-d');
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($options->getDateFields() as $dateField) {
|
foreach ($options->getDateFields() as $dateField) {
|
||||||
|
|||||||
@@ -16,8 +16,7 @@ class FieldMapping
|
|||||||
|
|
||||||
const DATE_FORMAT_MYSQL = 'yyyy-MM-dd HH:mm:ss';
|
const DATE_FORMAT_MYSQL = 'yyyy-MM-dd HH:mm:ss';
|
||||||
const DATE_FORMAT_CAPTION = 'yyyy/MM/dd'; // ES format
|
const DATE_FORMAT_CAPTION = 'yyyy/MM/dd'; // ES format
|
||||||
const DATE_FORMAT_MYSQL_OR_CAPTION = 'yyyy-MM-dd HH:mm:ss||yyyy/MM/dd';
|
const DATE_FORMAT_MYSQL_OR_CAPTION = 'yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||yyyy-MM||yyyy';
|
||||||
const DATE_FORMAT_CAPTION_PHP = 'Y/m/d'; // PHP format
|
|
||||||
|
|
||||||
// Core types
|
// Core types
|
||||||
const TYPE_STRING = 'string';
|
const TYPE_STRING = 'string';
|
||||||
|
|||||||
@@ -155,15 +155,16 @@ class BulkOperation
|
|||||||
// nb: results (items) are returned IN THE SAME ORDER as commands were pushed in the stack
|
// nb: results (items) are returned IN THE SAME ORDER as commands were pushed in the stack
|
||||||
// so the items[X] match the operationIdentifiers[X]
|
// so the items[X] match the operationIdentifiers[X]
|
||||||
foreach ($response['items'] as $key => $item) {
|
foreach ($response['items'] as $key => $item) {
|
||||||
foreach($item as $command=>$result) { // command may be "index" or "delete"
|
foreach ($item as $command=>$result) { // command may be "index" or "delete"
|
||||||
if($response['errors'] && $result['status'] >= 400) { // 4xx or 5xx error
|
if ($response['errors'] && $result['status'] >= 400) { // 4xx or 5xx
|
||||||
throw new Exception(sprintf('%d: %s', $key, var_export($result, true)));
|
$err = array_key_exists('error', $result) ? var_export($result['error'], true) : ($command . " error " . $result['status']);
|
||||||
|
throw new Exception(sprintf('%d: %s', $key, $err));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$operationIdentifier = $this->operationIdentifiers[$key];
|
$operationIdentifier = $this->operationIdentifiers[$key];
|
||||||
|
|
||||||
if(is_string($operationIdentifier) || is_int($operationIdentifier)) { // dont include null keys
|
if (is_string($operationIdentifier) || is_int($operationIdentifier)) { // dont include null keys
|
||||||
$callbackData[$operationIdentifier] = $response['items'][$key];
|
$callbackData[$operationIdentifier] = $response['items'][$key];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,18 +39,13 @@ class MetadataHydrator implements HydratorInterface
|
|||||||
|
|
||||||
public function hydrateRecords(array &$records)
|
public function hydrateRecords(array &$records)
|
||||||
{
|
{
|
||||||
$sql = <<<SQL
|
$sql = "(SELECT record_id, ms.name AS `key`, m.value AS value, 'caption' AS type, ms.business AS private\n"
|
||||||
(SELECT record_id, ms.name AS `key`, m.value AS value, 'caption' AS type, ms.business AS private
|
. " FROM metadatas AS m INNER JOIN metadatas_structure AS ms ON (ms.id = m.meta_struct_id)\n"
|
||||||
FROM metadatas AS m
|
. " WHERE record_id IN (?))\n"
|
||||||
INNER JOIN metadatas_structure AS ms ON (ms.id = m.meta_struct_id)
|
. "UNION\n"
|
||||||
WHERE record_id IN (?))
|
. "(SELECT record_id, t.name AS `key`, t.value AS value, 'exif' AS type, 0 AS private\n"
|
||||||
|
. " FROM technical_datas AS t\n"
|
||||||
UNION
|
. " WHERE record_id IN (?))\n";
|
||||||
|
|
||||||
(SELECT record_id, t.name AS `key`, t.value AS value, 'exif' AS type, 0 AS private
|
|
||||||
FROM technical_datas AS t
|
|
||||||
WHERE record_id IN (?))
|
|
||||||
SQL;
|
|
||||||
|
|
||||||
$ids = array_keys($records);
|
$ids = array_keys($records);
|
||||||
$statement = $this->connection->executeQuery(
|
$statement = $this->connection->executeQuery(
|
||||||
@@ -62,7 +57,7 @@ SQL;
|
|||||||
while ($metadata = $statement->fetch()) {
|
while ($metadata = $statement->fetch()) {
|
||||||
// Store metadata value
|
// Store metadata value
|
||||||
$key = $metadata['key'];
|
$key = $metadata['key'];
|
||||||
$value = $metadata['value'];
|
$value = trim($metadata['value']);
|
||||||
|
|
||||||
// Do not keep empty values
|
// Do not keep empty values
|
||||||
if ($key === '' || $value === '') {
|
if ($key === '' || $value === '') {
|
||||||
@@ -80,7 +75,7 @@ SQL;
|
|||||||
case 'caption':
|
case 'caption':
|
||||||
// Sanitize fields
|
// Sanitize fields
|
||||||
$value = StringHelper::crlfNormalize($value);
|
$value = StringHelper::crlfNormalize($value);
|
||||||
$value = $this->sanitizeValue($value, $this->structure->typeOf($key));
|
$value = $this->helper->sanitizeValue($value, $this->structure->typeOf($key));
|
||||||
// Private caption fields are kept apart
|
// Private caption fields are kept apart
|
||||||
$type = $metadata['private'] ? 'private_caption' : 'caption';
|
$type = $metadata['private'] ? 'private_caption' : 'caption';
|
||||||
// Caption are multi-valued
|
// Caption are multi-valued
|
||||||
@@ -103,7 +98,7 @@ SQL;
|
|||||||
}
|
}
|
||||||
$tag = $this->structure->getMetadataTagByName($key);
|
$tag = $this->structure->getMetadataTagByName($key);
|
||||||
if ($tag) {
|
if ($tag) {
|
||||||
$value = $this->sanitizeValue($value, $tag->getType());
|
$value = $this->helper->sanitizeValue($value, $tag->getType());
|
||||||
}
|
}
|
||||||
// EXIF data is single-valued
|
// EXIF data is single-valued
|
||||||
$record['metadata_tags'][$key] = $value;
|
$record['metadata_tags'][$key] = $value;
|
||||||
@@ -118,33 +113,6 @@ SQL;
|
|||||||
$this->clearGpsPositionBuffer();
|
$this->clearGpsPositionBuffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
private function sanitizeValue($value, $type)
|
|
||||||
{
|
|
||||||
switch ($type) {
|
|
||||||
case FieldMapping::TYPE_STRING:
|
|
||||||
return str_replace("\0", "", $value);
|
|
||||||
|
|
||||||
case FieldMapping::TYPE_DATE:
|
|
||||||
return $this->helper->sanitizeDate($value);
|
|
||||||
|
|
||||||
case FieldMapping::TYPE_FLOAT:
|
|
||||||
case FieldMapping::TYPE_DOUBLE:
|
|
||||||
return (float) $value;
|
|
||||||
|
|
||||||
case FieldMapping::TYPE_INTEGER:
|
|
||||||
case FieldMapping::TYPE_LONG:
|
|
||||||
case FieldMapping::TYPE_SHORT:
|
|
||||||
case FieldMapping::TYPE_BYTE:
|
|
||||||
return (int) $value;
|
|
||||||
|
|
||||||
case FieldMapping::TYPE_BOOLEAN:
|
|
||||||
return (bool) $value;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return $value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function handleGpsPosition(&$records, $id, $tag_name, $value)
|
private function handleGpsPosition(&$records, $id, $tag_name, $value)
|
||||||
{
|
{
|
||||||
// Get position object
|
// Get position object
|
||||||
|
|||||||
@@ -11,6 +11,8 @@
|
|||||||
|
|
||||||
namespace Alchemy\Phrasea\SearchEngine\Elastic\Indexer\Record\Hydrator;
|
namespace Alchemy\Phrasea\SearchEngine\Elastic\Indexer\Record\Hydrator;
|
||||||
|
|
||||||
|
use Alchemy\Phrasea\SearchEngine\Elastic\FieldMapping;
|
||||||
|
use Alchemy\Phrasea\SearchEngine\Elastic\RecordHelper;
|
||||||
use Doctrine\DBAL\Connection;
|
use Doctrine\DBAL\Connection;
|
||||||
use Doctrine\DBAL\Driver\Connection as DriverConnection;
|
use Doctrine\DBAL\Driver\Connection as DriverConnection;
|
||||||
|
|
||||||
@@ -18,31 +20,34 @@ class TitleHydrator implements HydratorInterface
|
|||||||
{
|
{
|
||||||
private $connection;
|
private $connection;
|
||||||
|
|
||||||
public function __construct(DriverConnection $connection)
|
/** @var RecordHelper */
|
||||||
|
private $helper;
|
||||||
|
|
||||||
|
public function __construct(DriverConnection $connection, RecordHelper $helper)
|
||||||
{
|
{
|
||||||
$this->connection = $connection;
|
$this->connection = $connection;
|
||||||
|
$this->helper = $helper;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function hydrateRecords(array &$records)
|
public function hydrateRecords(array &$records)
|
||||||
{
|
{
|
||||||
$sql = <<<SQL
|
$sql = "SELECT\n"
|
||||||
SELECT
|
. "m.`record_id`,\n"
|
||||||
m.`record_id`,
|
. " CASE ms.`thumbtitle`\n"
|
||||||
CASE ms.`thumbtitle`
|
. " WHEN '1' THEN 'default'\n"
|
||||||
WHEN "1" THEN "default"
|
. " WHEN '0' THEN 'default'\n"
|
||||||
WHEN "0" THEN "default"
|
. " ELSE ms.`thumbtitle`\n"
|
||||||
ELSE ms.`thumbtitle`
|
. " END AS locale,\n"
|
||||||
END AS locale,
|
. " CASE ms.`thumbtitle`\n"
|
||||||
CASE ms.`thumbtitle`
|
. " WHEN '0' THEN r.`originalname`\n"
|
||||||
WHEN "0" THEN r.`originalname`
|
. " ELSE GROUP_CONCAT(m.`value` ORDER BY ms.`thumbtitle`, ms.`sorter` SEPARATOR ' - ')\n"
|
||||||
ELSE GROUP_CONCAT(m.`value` ORDER BY ms.`thumbtitle`, ms.`sorter` SEPARATOR " - ")
|
. " END AS title\n"
|
||||||
END AS title
|
. "FROM metadatas AS m FORCE INDEX(`record_id`)\n"
|
||||||
FROM metadatas AS m FORCE INDEX(`record_id`)
|
. "STRAIGHT_JOIN metadatas_structure AS ms ON (ms.`id` = m.`meta_struct_id`)\n"
|
||||||
STRAIGHT_JOIN metadatas_structure AS ms ON (ms.`id` = m.`meta_struct_id`)
|
. "STRAIGHT_JOIN record AS r ON (r.`record_id` = m.`record_id`)\n"
|
||||||
STRAIGHT_JOIN record AS r ON (r.`record_id` = m.`record_id`)
|
. "WHERE m.`record_id` IN (?)\n"
|
||||||
WHERE m.`record_id` IN (?)
|
. "GROUP BY m.`record_id`, ms.`thumbtitle`\n";
|
||||||
GROUP BY m.`record_id`, ms.`thumbtitle`
|
|
||||||
SQL;
|
|
||||||
$statement = $this->connection->executeQuery(
|
$statement = $this->connection->executeQuery(
|
||||||
$sql,
|
$sql,
|
||||||
array(array_keys($records)),
|
array(array_keys($records)),
|
||||||
@@ -50,7 +55,7 @@ SQL;
|
|||||||
);
|
);
|
||||||
|
|
||||||
while ($row = $statement->fetch()) {
|
while ($row = $statement->fetch()) {
|
||||||
$records[$row['record_id']]['title'][$row['locale']] = $row['title'];
|
$records[$row['record_id']]['title'][$row['locale']] = $this->helper->sanitizeValue($row['title'], FieldMapping::TYPE_STRING);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,6 +57,9 @@ class DateFieldMapping extends ComplexFieldMapping
|
|||||||
*/
|
*/
|
||||||
protected function getProperties()
|
protected function getProperties()
|
||||||
{
|
{
|
||||||
return array_merge([ 'format' => $this->format ], parent::getProperties());
|
return array_merge([
|
||||||
|
'format' => $this->format,
|
||||||
|
'ignore_malformed' => true
|
||||||
|
], parent::getProperties());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -89,31 +89,72 @@ class RecordHelper
|
|||||||
return $this->collectionMap;
|
return $this->collectionMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $date
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public static function validateDate($date)
|
|
||||||
{
|
|
||||||
$d = DateTime::createFromFormat(FieldMapping::DATE_FORMAT_CAPTION_PHP, $date);
|
|
||||||
|
|
||||||
return $d && $d->format(FieldMapping::DATE_FORMAT_CAPTION_PHP) == $date;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $value
|
* @param string $value
|
||||||
* @return null|string
|
* @return null|string
|
||||||
*/
|
*/
|
||||||
public static function sanitizeDate($value)
|
public static function sanitizeDate($value)
|
||||||
{
|
{
|
||||||
// introduced in https://github.com/alchemy-fr/Phraseanet/commit/775ce804e0257d3a06e4e068bd17330a79eb8370#diff-bee690ed259e0cf73a31dee5295d2edcR286
|
$v_fix = null;
|
||||||
// not sure if it's really needed
|
|
||||||
try {
|
try {
|
||||||
$date = new \DateTime($value);
|
$a = explode(';', preg_replace('/\D+/', ';', trim($value)));
|
||||||
|
switch (count($a)) {
|
||||||
return $date->format(FieldMapping::DATE_FORMAT_CAPTION_PHP);
|
case 1: // yyyy
|
||||||
|
$date = new \DateTime($a[0] . '-01-01'); // will throw if date is not valid
|
||||||
|
$v_fix = $date->format('Y');
|
||||||
|
break;
|
||||||
|
case 2: // yyyy;mm
|
||||||
|
$date = new \DateTime( $a[0] . '-' . $a[1] . '-01');
|
||||||
|
$v_fix = $date->format('Y-m');
|
||||||
|
break;
|
||||||
|
case 3: // yyyy;mm;dd
|
||||||
|
$date = new \DateTime($a[0] . '-' . $a[1] . '-' . $a[2]);
|
||||||
|
$v_fix = $date->format('Y-m-d');
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
$date = new \DateTime($a[0] . '-' . $a[1] . '-' . $a[2] . ' ' . $a[3] . ':00:00');
|
||||||
|
$v_fix = $date->format('Y-m-d H:i:s');
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
$date = new \DateTime($a[0] . '-' . $a[1] . '-' . $a[2] . ' ' . $a[3] . ':' . $a[4] . ':00');
|
||||||
|
$v_fix = $date->format('Y-m-d H:i:s');
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
$date = new \DateTime($a[0] . '-' . $a[1] . '-' . $a[2] . ' ' . $a[3] . ':' . $a[4] . ':' . $a[5]);
|
||||||
|
$v_fix = $date->format('Y-m-d H:i:s');
|
||||||
|
break;
|
||||||
|
}
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
return null;
|
// no-op, v_fix = null
|
||||||
|
}
|
||||||
|
|
||||||
|
return $v_fix;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function sanitizeValue($value, $type)
|
||||||
|
{
|
||||||
|
switch ($type) {
|
||||||
|
case FieldMapping::TYPE_DATE:
|
||||||
|
return self::sanitizeDate($value);
|
||||||
|
|
||||||
|
case FieldMapping::TYPE_FLOAT:
|
||||||
|
case FieldMapping::TYPE_DOUBLE:
|
||||||
|
return (float) $value;
|
||||||
|
|
||||||
|
case FieldMapping::TYPE_INTEGER:
|
||||||
|
case FieldMapping::TYPE_LONG:
|
||||||
|
case FieldMapping::TYPE_SHORT:
|
||||||
|
case FieldMapping::TYPE_BYTE:
|
||||||
|
return (int) $value;
|
||||||
|
|
||||||
|
case FieldMapping::TYPE_BOOLEAN:
|
||||||
|
return (bool) $value;
|
||||||
|
|
||||||
|
case FieldMapping::TYPE_STRING:
|
||||||
|
return str_replace("\0", '', $value);
|
||||||
|
|
||||||
|
default:
|
||||||
|
return $value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -110,41 +110,50 @@ class QueryHelper
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getRangeFromDateString($string)
|
public static function getRangeFromDateString($value)
|
||||||
{
|
{
|
||||||
$formats = ['Y/m/d', 'Y/m', 'Y'];
|
$date_from = null;
|
||||||
$deltas = ['+1 day', '+1 month', '+1 year'];
|
$date_to = null;
|
||||||
$to = null;
|
try {
|
||||||
while ($format = array_pop($formats)) {
|
$a = explode(';', preg_replace('/\D+/', ';', trim($value)));
|
||||||
$delta = array_pop($deltas);
|
switch (count($a)) {
|
||||||
$from = date_create_from_format($format, $string);
|
case 1: // yyyy
|
||||||
if ($from !== false) {
|
$date_to = clone($date_from = new \DateTime($a[0] . '-01-01 00:00:00')); // will throw if date is not valid
|
||||||
// Rewind to start of range
|
$date_to->add(new \DateInterval('P1Y'));
|
||||||
$month = 1;
|
break;
|
||||||
$day = 1;
|
case 2: // yyyy;mm
|
||||||
switch ($format) {
|
$date_to = clone($date_from = new \DateTime($a[0] . '-' . $a[1] . '-01 00:00:00')); // will throw if date is not valid
|
||||||
case 'Y/m/d':
|
$date_to->add(new \DateInterval('P1M'));
|
||||||
$day = (int) $from->format('d');
|
break;
|
||||||
case 'Y/m':
|
case 3: // yyyy;mm;dd
|
||||||
$month = (int) $from->format('m');
|
$date_to = clone($date_from = new \DateTime($a[0] . '-' . $a[1] . '-' . $a[2] . ' 00:00:00')); // will throw if date is not valid
|
||||||
case 'Y':
|
$date_to->add(new \DateInterval('P1D'));
|
||||||
$year = (int) $from->format('Y');
|
break;
|
||||||
}
|
case 4:
|
||||||
date_date_set($from, $year, $month, $day);
|
$date_to = clone($date_from = new \DateTime($a[0] . '-' . $a[1] . '-' . $a[2] . ' ' . $a[3] . ':00:00'));
|
||||||
date_time_set($from, 0, 0, 0);
|
$date_to->add(new \DateInterval('PT1H'));
|
||||||
// Create end of the the range
|
break;
|
||||||
$to = date_modify(clone $from, $delta);
|
case 5:
|
||||||
break;
|
$date_to = clone($date_from = new \DateTime($a[0] . '-' . $a[1] . '-' . $a[2] . ' ' . $a[3] . ':' . $a[4] . ':00'));
|
||||||
|
$date_to->add(new \DateInterval('PT1M'));
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
$date_to = clone($date_from = new \DateTime($a[0] . '-' . $a[1] . '-' . $a[2] . ' ' . $a[3] . ':' . $a[4] . ':' . $a[5]));
|
||||||
|
// $date_to->add(new \DateInterval('PT1S')); // no need since precision is 1 sec, a "equal" will be generated when from==to
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (\Exception $e) {
|
||||||
|
// no-op
|
||||||
|
}
|
||||||
|
|
||||||
if (!$from || !$to) {
|
if ($date_from === null || $date_to === null) {
|
||||||
throw new \InvalidArgumentException(sprintf('Invalid date "%s".', $string));
|
throw new \InvalidArgumentException(sprintf('Invalid date "%s".', $value));
|
||||||
}
|
}
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'from' => $from->format(FieldMapping::DATE_FORMAT_CAPTION_PHP),
|
'from' => $date_from->format('Y-m-d H:i:s'),
|
||||||
'to' => $to->format(FieldMapping::DATE_FORMAT_CAPTION_PHP)
|
'to' => $date_to->format('Y-m-d H:i:s')
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ namespace Alchemy\Phrasea\SearchEngine\Elastic\Search;
|
|||||||
use Alchemy\Phrasea\SearchEngine\Elastic\AST;
|
use Alchemy\Phrasea\SearchEngine\Elastic\AST;
|
||||||
use Alchemy\Phrasea\SearchEngine\Elastic\Exception\Exception;
|
use Alchemy\Phrasea\SearchEngine\Elastic\Exception\Exception;
|
||||||
use Alchemy\Phrasea\SearchEngine\Elastic\FieldMapping;
|
use Alchemy\Phrasea\SearchEngine\Elastic\FieldMapping;
|
||||||
use Alchemy\Phrasea\SearchEngine\Elastic\Mapping;
|
use Alchemy\Phrasea\SearchEngine\Elastic\RecordHelper;
|
||||||
use Alchemy\Phrasea\SearchEngine\Elastic\Structure\Structure;
|
use Alchemy\Phrasea\SearchEngine\Elastic\Structure\Structure;
|
||||||
use Hoa\Compiler\Llk\TreeNode;
|
use Hoa\Compiler\Llk\TreeNode;
|
||||||
use Hoa\Visitor\Element;
|
use Hoa\Visitor\Element;
|
||||||
@@ -166,6 +166,12 @@ class QueryVisitor implements Visit
|
|||||||
$key = $node->getChild(0)->accept($this);
|
$key = $node->getChild(0)->accept($this);
|
||||||
$boundary = $node->getChild(1)->accept($this);
|
$boundary = $node->getChild(1)->accept($this);
|
||||||
|
|
||||||
|
if ($this->isDateKey($key)) {
|
||||||
|
if(($v = RecordHelper::sanitizeDate($boundary)) !== null) {
|
||||||
|
$boundary = $v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
switch ($node->getId()) {
|
switch ($node->getId()) {
|
||||||
case NodeTypes::LT_EXPR:
|
case NodeTypes::LT_EXPR:
|
||||||
return AST\KeyValue\RangeExpression::lessThan($key, $boundary);
|
return AST\KeyValue\RangeExpression::lessThan($key, $boundary);
|
||||||
@@ -195,11 +201,15 @@ class QueryVisitor implements Visit
|
|||||||
try {
|
try {
|
||||||
// Try to create a range for incomplete dates
|
// Try to create a range for incomplete dates
|
||||||
$range = QueryHelper::getRangeFromDateString($right);
|
$range = QueryHelper::getRangeFromDateString($right);
|
||||||
return new AST\KeyValue\RangeExpression(
|
if ($range['from'] === $range['to']) {
|
||||||
$left,
|
return new AST\KeyValue\EqualExpression($left, $range['from']);
|
||||||
$range['from'], true,
|
} else {
|
||||||
$range['to'], false
|
return new AST\KeyValue\RangeExpression(
|
||||||
);
|
$left,
|
||||||
|
$range['from'], true,
|
||||||
|
$range['to'], false
|
||||||
|
);
|
||||||
|
}
|
||||||
} catch (\InvalidArgumentException $e) {
|
} catch (\InvalidArgumentException $e) {
|
||||||
// Fall back to equal expression
|
// Fall back to equal expression
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
namespace Alchemy\Phrasea\SearchEngine\Elastic\Structure;
|
namespace Alchemy\Phrasea\SearchEngine\Elastic\Structure;
|
||||||
|
|
||||||
use Alchemy\Phrasea\SearchEngine\Elastic\FieldMapping;
|
use Alchemy\Phrasea\SearchEngine\Elastic\FieldMapping;
|
||||||
use Alchemy\Phrasea\SearchEngine\Elastic\Mapping;
|
|
||||||
use Alchemy\Phrasea\SearchEngine\Elastic\RecordHelper;
|
use Alchemy\Phrasea\SearchEngine\Elastic\RecordHelper;
|
||||||
use Assert\Assertion;
|
use Assert\Assertion;
|
||||||
|
|
||||||
@@ -20,7 +19,7 @@ class ValueChecker
|
|||||||
{
|
{
|
||||||
Assertion::allIsInstanceOf($list, Typed::class);
|
Assertion::allIsInstanceOf($list, Typed::class);
|
||||||
$is_numeric = is_numeric($value);
|
$is_numeric = is_numeric($value);
|
||||||
$is_valid_date = RecordHelper::validateDate($value);
|
$is_valid_date = (RecordHelper::sanitizeDate($value) !== null);
|
||||||
$filtered = [];
|
$filtered = [];
|
||||||
foreach ($list as $item) {
|
foreach ($list as $item) {
|
||||||
switch ($item->getType()) {
|
switch ($item->getType()) {
|
||||||
|
|||||||
@@ -121,13 +121,16 @@ class WriteMetadataJob extends AbstractJob
|
|||||||
$fieldName = $fieldStructure->get_name();
|
$fieldName = $fieldStructure->get_name();
|
||||||
|
|
||||||
// skip fields with no src
|
// skip fields with no src
|
||||||
if($tagName == '') {
|
if($tagName == '' || $tagName == 'Phraseanet:no-source') {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check exiftool known tags to skip Phraseanet:tf-*
|
// check exiftool known tags to skip Phraseanet:tf-*
|
||||||
try {
|
try {
|
||||||
TagFactory::getFromRDFTagname($tagName);
|
$tag = TagFactory::getFromRDFTagname($tagName);
|
||||||
|
if(!$tag->isWritable()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
} catch (TagUnknown $e) {
|
} catch (TagUnknown $e) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -139,29 +142,42 @@ class WriteMetadataJob extends AbstractJob
|
|||||||
if ($fieldStructure->is_multi()) {
|
if ($fieldStructure->is_multi()) {
|
||||||
$values = array();
|
$values = array();
|
||||||
foreach ($fieldValues as $value) {
|
foreach ($fieldValues as $value) {
|
||||||
$values[] = $value->getValue();
|
$values[] = $this->removeNulChar($value->getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
$value = new Value\Multi($values);
|
$value = new Value\Multi($values);
|
||||||
} else {
|
} else {
|
||||||
$fieldValue = array_pop($fieldValues);
|
$fieldValue = array_pop($fieldValues);
|
||||||
$value = $fieldValue->getValue();
|
$value = $this->removeNulChar($fieldValue->getValue());
|
||||||
|
|
||||||
$value = new Value\Mono($value);
|
// fix the dates edited into phraseanet
|
||||||
|
if($fieldStructure->get_type() === $fieldStructure::TYPE_DATE) {
|
||||||
|
try {
|
||||||
|
$value = self::fixDate($value); // will return NULL if the date is not valid
|
||||||
|
}
|
||||||
|
catch (\Exception $e) {
|
||||||
|
$value = null; // do NOT write back to iptc
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if($value !== null) { // do not write invalid dates
|
||||||
|
$value = new Value\Mono($value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch(\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
// the field is not set in the record, erase it
|
// the field is not set in the record, erase it
|
||||||
if ($fieldStructure->is_multi()) {
|
if ($fieldStructure->is_multi()) {
|
||||||
$value = new Value\Multi(array(''));
|
$value = new Value\Multi(array(''));
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
$value = new Value\Mono('');
|
$value = new Value\Mono('');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$metadata->add(
|
if($value !== null) { // do not write invalid data
|
||||||
new Metadata\Metadata($fieldStructure->get_tag(), $value)
|
$metadata->add(
|
||||||
);
|
new Metadata\Metadata($fieldStructure->get_tag(), $value)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$writer = $this->getMetadataWriter($jobData->getApplication());
|
$writer = $this->getMetadataWriter($jobData->getApplication());
|
||||||
@@ -215,4 +231,39 @@ class WriteMetadataJob extends AbstractJob
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function removeNulChar($value)
|
||||||
|
{
|
||||||
|
return str_replace("\0", "", $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* re-format a phraseanet date for iptc writing
|
||||||
|
* return NULL if the date is not valid
|
||||||
|
*
|
||||||
|
* @param string $value
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
private static function fixDate($value)
|
||||||
|
{
|
||||||
|
$date = null;
|
||||||
|
try {
|
||||||
|
$a = explode(';', preg_replace('/\D+/', ';', trim($value)));
|
||||||
|
switch (count($a)) {
|
||||||
|
case 3: // yyyy;mm;dd
|
||||||
|
$date = new \DateTime($a[0] . '-' . $a[1] . '-' . $a[2]);
|
||||||
|
$date = $date->format('Y-m-d H:i:s');
|
||||||
|
break;
|
||||||
|
case 6: // yyyy;mm;dd;hh;mm;ss
|
||||||
|
$date = new \DateTime($a[0] . '-' . $a[1] . '-' . $a[2] . ' ' . $a[3] . ':' . $a[4] . ':' . $a[5]);
|
||||||
|
$date = $date->format('Y-m-d H:i:s');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (\Exception $e) {
|
||||||
|
$date = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $date;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1115,7 +1115,6 @@ class ACL implements cache_cacheableInterface
|
|||||||
/**
|
/**
|
||||||
* @param array $base_ids
|
* @param array $base_ids
|
||||||
* @return $this
|
* @return $this
|
||||||
* @throws DBALException
|
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public function revoke_access_from_bases(Array $base_ids)
|
public function revoke_access_from_bases(Array $base_ids)
|
||||||
@@ -1125,24 +1124,30 @@ class ACL implements cache_cacheableInterface
|
|||||||
|
|
||||||
$usr_id = $this->user->getId();
|
$usr_id = $this->user->getId();
|
||||||
|
|
||||||
|
$errors = 0;
|
||||||
foreach ($base_ids as $base_id) {
|
foreach ($base_ids as $base_id) {
|
||||||
if (!$stmt_del->execute([':base_id' => $base_id, ':usr_id' => $usr_id])) {
|
if ($stmt_del->execute([':base_id' => $base_id, ':usr_id' => $usr_id])) {
|
||||||
throw new Exception('Error while deleteing some rights');
|
$this->app['dispatcher']->dispatch(
|
||||||
}
|
AclEvents::ACCESS_TO_BASE_REVOKED,
|
||||||
|
new AccessToBaseRevokedEvent(
|
||||||
$this->app['dispatcher']->dispatch(
|
$this,
|
||||||
AclEvents::ACCESS_TO_BASE_REVOKED,
|
[
|
||||||
new AccessToBaseRevokedEvent(
|
'base_id' => $base_id
|
||||||
$this,
|
]
|
||||||
array(
|
|
||||||
'base_id'=>$base_id
|
|
||||||
)
|
)
|
||||||
)
|
);
|
||||||
);
|
}
|
||||||
|
else {
|
||||||
|
$errors++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
$stmt_del->closeCursor();
|
$stmt_del->closeCursor();
|
||||||
$this->delete_data_from_cache(self::CACHE_RIGHTS_BAS);
|
$this->delete_data_from_cache(self::CACHE_RIGHTS_BAS);
|
||||||
|
|
||||||
|
if($errors > 0) {
|
||||||
|
throw new Exception('Error while deleting some rights');
|
||||||
|
}
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
4
lib/classes/cache/databox.php
vendored
@@ -121,9 +121,9 @@ class cache_databox
|
|||||||
|
|
||||||
$conn = $app->getApplicationBox()->get_connection();
|
$conn = $app->getApplicationBox()->get_connection();
|
||||||
|
|
||||||
$sql = 'UPDATE sitepreff SET memcached_update = :date';
|
$sql = 'UPDATE sitepreff SET memcached_update = current_timestamp()';
|
||||||
$stmt = $conn->prepare($sql);
|
$stmt = $conn->prepare($sql);
|
||||||
$stmt->execute([':date' => $now]);
|
$stmt->execute();
|
||||||
$stmt->closeCursor();
|
$stmt->closeCursor();
|
||||||
|
|
||||||
self::$refreshing = false;
|
self::$refreshing = false;
|
||||||
|
|||||||
@@ -11,6 +11,9 @@
|
|||||||
|
|
||||||
use Alchemy\Phrasea\Application;
|
use Alchemy\Phrasea\Application;
|
||||||
use Alchemy\Phrasea\Model\Entities\User;
|
use Alchemy\Phrasea\Model\Entities\User;
|
||||||
|
use Alchemy\Phrasea\Model\Repositories\BasketRepository;
|
||||||
|
use Alchemy\Phrasea\Model\Repositories\UserRepository;
|
||||||
|
|
||||||
|
|
||||||
class eventsmanager_notify_orderdeliver extends eventsmanager_notifyAbstract
|
class eventsmanager_notify_orderdeliver extends eventsmanager_notifyAbstract
|
||||||
{
|
{
|
||||||
@@ -31,9 +34,9 @@ class eventsmanager_notify_orderdeliver extends eventsmanager_notifyAbstract
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param Array $datas
|
* @param string[] $data
|
||||||
* @param boolean $unread
|
* @param boolean $unread
|
||||||
* @return string
|
* @return array
|
||||||
*/
|
*/
|
||||||
public function datas(array $data, $unread)
|
public function datas(array $data, $unread)
|
||||||
{
|
{
|
||||||
@@ -41,24 +44,29 @@ class eventsmanager_notify_orderdeliver extends eventsmanager_notifyAbstract
|
|||||||
$ssel_id = $data['ssel_id'];
|
$ssel_id = $data['ssel_id'];
|
||||||
$n = $data['n'];
|
$n = $data['n'];
|
||||||
|
|
||||||
if (null === $user= $this->app['repo.users']->find(($from))) {
|
/** @var UserRepository $userRepo */
|
||||||
|
$userRepo = $this->app['repo.users'];
|
||||||
|
if( ($user= $userRepo->find(($from))) === null ) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
$sender = $user->getDisplayName();
|
$sender = $user->getDisplayName();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
/** @var BasketRepository $repository */
|
||||||
$repository = $this->app['repo.baskets'];
|
$repository = $this->app['repo.baskets'];
|
||||||
|
|
||||||
$basket = $repository->findUserBasket($ssel_id, $this->app->getAuthenticatedUser(), false);
|
$basket = $repository->findUserBasket($ssel_id, $this->app->getAuthenticatedUser(), false);
|
||||||
} catch (\Exception $e) {
|
}
|
||||||
|
catch (\Exception $e) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
$ret = [
|
$ret = [
|
||||||
'text' => $this->app->trans('%user% vous a delivre %quantity% document(s) pour votre commande %title%', ['%user%' => $sender, '%quantity%' => $n, '%title%' => '<a href="/lightbox/compare/'
|
'text' => $this->app->trans('%user% vous a delivre %quantity% document(s) pour votre commande %title%', ['%user%' => $sender, '%quantity%' => $n, '%title%' => '<a href="/lightbox/compare/'
|
||||||
. $ssel_id . '/" target="_blank">'
|
. $ssel_id . '/" target="_blank">'
|
||||||
. $basket->getName() . '</a>'])
|
. $basket->getName() . '</a>']),
|
||||||
, 'class' => ''
|
'class' => ''
|
||||||
];
|
];
|
||||||
|
|
||||||
return $ret;
|
return $ret;
|
||||||
|
|||||||
@@ -11,6 +11,9 @@
|
|||||||
|
|
||||||
use Alchemy\Phrasea\Application;
|
use Alchemy\Phrasea\Application;
|
||||||
use Alchemy\Phrasea\Model\Entities\User;
|
use Alchemy\Phrasea\Model\Entities\User;
|
||||||
|
use Alchemy\Phrasea\Model\Repositories\BasketRepository;
|
||||||
|
use Alchemy\Phrasea\Model\Repositories\UserRepository;
|
||||||
|
|
||||||
|
|
||||||
class eventsmanager_notify_validationdone extends eventsmanager_notifyAbstract
|
class eventsmanager_notify_validationdone extends eventsmanager_notifyAbstract
|
||||||
{
|
{
|
||||||
@@ -31,35 +34,38 @@ class eventsmanager_notify_validationdone extends eventsmanager_notifyAbstract
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param string $datas
|
* @param string[] $data
|
||||||
* @param boolean $unread
|
* @param boolean $unread
|
||||||
* @return Array
|
* @return array
|
||||||
*/
|
*/
|
||||||
public function datas(array $data, $unread)
|
public function datas(array $data, $unread)
|
||||||
{
|
{
|
||||||
$from = $data['from'];
|
$from = $data['from'];
|
||||||
$ssel_id = $data['ssel_id'];
|
$ssel_id = $data['ssel_id'];
|
||||||
|
|
||||||
if (null === $registered_user = $this->app['repo.users']->find($from)) {
|
/** @var UserRepository $userRepo */
|
||||||
|
$userRepo = $this->app['repo.users'];
|
||||||
|
if ( ($registered_user = $userRepo->find($from)) === null ) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
$sender = $registered_user->getDisplayName();
|
$sender = $registered_user->getDisplayName();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
/** @var BasketRepository $repository */
|
||||||
$repository = $this->app['repo.baskets'];
|
$repository = $this->app['repo.baskets'];
|
||||||
|
|
||||||
$basket = $repository->findUserBasket($ssel_id, $this->app->getAuthenticatedUser(), false);
|
$basket = $repository->findUserBasket($ssel_id, $this->app->getAuthenticatedUser(), false);
|
||||||
} catch (\Exception $e) {
|
}
|
||||||
|
catch (\Exception $e) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
$ret = [
|
$ret = [
|
||||||
'text' => $this->app->trans('%user% a envoye son rapport de validation de %title%', ['%user%' => $sender, '%title%' => '<a href="/lightbox/validate/'
|
'text' => $this->app->trans('%user% a envoye son rapport de validation de %title%', ['%user%' => $sender, '%title%' => '<a href="/lightbox/validate/'
|
||||||
. $ssel_id . '/" target="_blank">'
|
. $ssel_id . '/" target="_blank">'
|
||||||
. $basket->getName() . '</a>'
|
. $basket->getName() . '</a>']),
|
||||||
])
|
'class' => ''
|
||||||
, 'class' => ''
|
|
||||||
];
|
];
|
||||||
|
|
||||||
return $ret;
|
return $ret;
|
||||||
@@ -84,12 +90,18 @@ class eventsmanager_notify_validationdone extends eventsmanager_notifyAbstract
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param integer $usr_id The id of the user to check
|
* @param User $user The id of the user to check
|
||||||
*
|
*
|
||||||
* @return boolean
|
* @return boolean
|
||||||
*/
|
*/
|
||||||
public function is_available(User $user)
|
public function is_available(User $user)
|
||||||
{
|
{
|
||||||
return $this->app->getAclForUser($user)->has_right(\ACL::CANPUSH);
|
try {
|
||||||
|
return $this->app->getAclForUser($user)->has_right(\ACL::CANPUSH);
|
||||||
|
}
|
||||||
|
catch (\Exception $e) {
|
||||||
|
// has_right(unknow_right) ? will not happen !
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,13 +9,16 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
use Alchemy\Phrasea\Application;
|
use Alchemy\Phrasea\Application;
|
||||||
use Alchemy\Phrasea\Model\Serializer\CaptionSerializer;
|
|
||||||
use Alchemy\Phrasea\Model\Entities\Token;
|
use Alchemy\Phrasea\Model\Entities\Token;
|
||||||
use Alchemy\Phrasea\Model\Entities\User;
|
use Alchemy\Phrasea\Model\Entities\User;
|
||||||
|
use Alchemy\Phrasea\Model\Repositories\BasketRepository;
|
||||||
|
use Alchemy\Phrasea\Model\Repositories\StoryWZRepository;
|
||||||
|
use Alchemy\Phrasea\Model\Serializer\CaptionSerializer;
|
||||||
use Assert\Assertion;
|
use Assert\Assertion;
|
||||||
use Doctrine\DBAL\Connection;
|
use Doctrine\DBAL\Connection;
|
||||||
use Symfony\Component\Filesystem\Filesystem;
|
use Symfony\Component\Filesystem\Filesystem;
|
||||||
|
|
||||||
|
|
||||||
class set_export extends set_abstract
|
class set_export extends set_abstract
|
||||||
{
|
{
|
||||||
private static $maxFilenameLength = 256;
|
private static $maxFilenameLength = 256;
|
||||||
@@ -60,6 +63,7 @@ class set_export extends set_abstract
|
|||||||
$remain_hd = [];
|
$remain_hd = [];
|
||||||
|
|
||||||
if ($storyWZid) {
|
if ($storyWZid) {
|
||||||
|
/** @var StoryWZRepository $repository */
|
||||||
$repository = $app['repo.story-wz'];
|
$repository = $app['repo.story-wz'];
|
||||||
|
|
||||||
$storyWZ = $repository->findByUserAndId($this->app, $app->getAuthenticatedUser(), $storyWZid);
|
$storyWZ = $repository->findByUserAndId($this->app, $app->getAuthenticatedUser(), $storyWZid);
|
||||||
@@ -68,6 +72,7 @@ class set_export extends set_abstract
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($sstid != "") {
|
if ($sstid != "") {
|
||||||
|
/** @var BasketRepository $repository */
|
||||||
$repository = $app['repo.baskets'];
|
$repository = $app['repo.baskets'];
|
||||||
|
|
||||||
$Basket = $repository->findUserBasket($sstid, $app->getAuthenticatedUser(), false);
|
$Basket = $repository->findUserBasket($sstid, $app->getAuthenticatedUser(), false);
|
||||||
|
|||||||
@@ -1278,6 +1278,27 @@
|
|||||||
<field>id</field>
|
<field>id</field>
|
||||||
</fields>
|
</fields>
|
||||||
</index>
|
</index>
|
||||||
|
<index>
|
||||||
|
<name>usr_id</name>
|
||||||
|
<type>INDEX</type>
|
||||||
|
<fields>
|
||||||
|
<field>usr_id</field>
|
||||||
|
</fields>
|
||||||
|
</index>
|
||||||
|
<index>
|
||||||
|
<name>unread</name>
|
||||||
|
<type>INDEX</type>
|
||||||
|
<fields>
|
||||||
|
<field>unread</field>
|
||||||
|
</fields>
|
||||||
|
</index>
|
||||||
|
<index>
|
||||||
|
<name>created_on</name>
|
||||||
|
<type>INDEX</type>
|
||||||
|
<fields>
|
||||||
|
<field>created_on</field>
|
||||||
|
</fields>
|
||||||
|
</index>
|
||||||
</indexes>
|
</indexes>
|
||||||
<engine>InnoDB</engine>
|
<engine>InnoDB</engine>
|
||||||
</table>
|
</table>
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ main:
|
|||||||
doctype_aggregate_limit: 0
|
doctype_aggregate_limit: 0
|
||||||
camera_model_aggregate_limit: 0
|
camera_model_aggregate_limit: 0
|
||||||
iso_aggregate_limit: 0
|
iso_aggregate_limit: 0
|
||||||
aperture_aggregate_limit: 0
|
aperture_aggregate_limit: 0
|
||||||
shutterspeed_aggregate_limit: 0
|
shutterspeed_aggregate_limit: 0
|
||||||
flashfired_aggregate_limit: 0
|
flashfired_aggregate_limit: 0
|
||||||
framerate_aggregate_limit: 0
|
framerate_aggregate_limit: 0
|
||||||
@@ -234,6 +234,7 @@ embed_bundle:
|
|||||||
audio:
|
audio:
|
||||||
player: videojs
|
player: videojs
|
||||||
autoplay: false
|
autoplay: false
|
||||||
|
cover_subdef: thumbnail
|
||||||
document:
|
document:
|
||||||
#player: flexpaper
|
#player: flexpaper
|
||||||
enable_pdfjs: true
|
enable_pdfjs: true
|
||||||
@@ -297,6 +298,6 @@ rabbitmq:
|
|||||||
user: ''
|
user: ''
|
||||||
password: ''
|
password: ''
|
||||||
vhost: /
|
vhost: /
|
||||||
|
|
||||||
Console_logger_enabled_environments: [test]
|
Console_logger_enabled_environments: [test]
|
||||||
|
|
||||||
|
|||||||
@@ -65,7 +65,7 @@
|
|||||||
"normalize-css": "^2.1.0",
|
"normalize-css": "^2.1.0",
|
||||||
"npm": "^6.0.0",
|
"npm": "^6.0.0",
|
||||||
"npm-modernizr": "^2.8.3",
|
"npm-modernizr": "^2.8.3",
|
||||||
"phraseanet-production-client": "^0.34.16-d",
|
"phraseanet-production-client": "0.34.72-d",
|
||||||
"requirejs": "^2.3.5",
|
"requirejs": "^2.3.5",
|
||||||
"tinymce": "^4.0.28",
|
"tinymce": "^4.0.28",
|
||||||
"underscore": "^1.8.3",
|
"underscore": "^1.8.3",
|
||||||
|
|||||||
21
resources/ansible/playbook-boxes.yml
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
---
|
||||||
|
- hosts: all
|
||||||
|
sudo: true
|
||||||
|
vars_files:
|
||||||
|
- vars/all.yml
|
||||||
|
roles:
|
||||||
|
# - server
|
||||||
|
# - repositories
|
||||||
|
# - vagrant_local
|
||||||
|
- nginx
|
||||||
|
# - mariadb
|
||||||
|
# - elasticsearch
|
||||||
|
# - rabbitmq
|
||||||
|
# - php
|
||||||
|
- xdebug
|
||||||
|
# - composer
|
||||||
|
- mailcatcher
|
||||||
|
# - node
|
||||||
|
# - yarn
|
||||||
|
# - ffmpeg
|
||||||
|
- app
|
||||||
@@ -4,18 +4,18 @@
|
|||||||
vars_files:
|
vars_files:
|
||||||
- vars/all.yml
|
- vars/all.yml
|
||||||
roles:
|
roles:
|
||||||
# - server
|
- server
|
||||||
# - repositories
|
- repositories
|
||||||
# - vagrant_local
|
- vagrant_local
|
||||||
- nginx
|
- nginx
|
||||||
# - mariadb
|
- mariadb
|
||||||
# - elasticsearch
|
- elasticsearch
|
||||||
# - rabbitmq
|
- rabbitmq
|
||||||
# - php
|
- php
|
||||||
- xdebug
|
- xdebug
|
||||||
# - composer
|
- composer
|
||||||
# - mailcatcher
|
- mailcatcher
|
||||||
# - node
|
- node
|
||||||
# - yarn
|
- yarn
|
||||||
# - ffmpeg
|
- ffmpeg
|
||||||
- app
|
- app
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
changed_when: false
|
changed_when: false
|
||||||
|
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
apt: pkg=openjdk-7-jre state=latest
|
apt: pkg=openjdk-8-jre state=latest
|
||||||
|
|
||||||
- name: Remove temporary debian package
|
- name: Remove temporary debian package
|
||||||
shell: rm -f /tmp/elasticsearch-{{ elasticsearch.version }}.deb
|
shell: rm -f /tmp/elasticsearch-{{ elasticsearch.version }}.deb
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
|
|
||||||
- name: Install mailcatcher gem
|
- name: Install mailcatcher gem
|
||||||
# gem module is flaky, this is consistent
|
# gem module is flaky, this is consistent
|
||||||
command: gem install mailcatcher --conservative
|
command: gem install mailcatcher -v 0.6.4 --conservative
|
||||||
ignore_errors: yes
|
ignore_errors: yes
|
||||||
|
|
||||||
- name: Install mailcatcher supervisord conf
|
- name: Install mailcatcher supervisord conf
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
- name: Add Key for MariaDB Repository
|
- name: Add Key for MariaDB Repository
|
||||||
sudo: yes
|
sudo: yes
|
||||||
apt_key: url=http://keyserver.ubuntu.com/pks/lookup?op=get&search=0xcbcb082a1bb943db
|
apt_key: url=http://keyserver.ubuntu.com/pks/lookup?op=get&search=0xF1656F24C74CD1D8
|
||||||
|
|
||||||
# RabbitMQ
|
# RabbitMQ
|
||||||
- name: Add rabbitmq package repository
|
- name: Add rabbitmq package repository
|
||||||
|
|||||||
@@ -34,12 +34,14 @@ server:
|
|||||||
- fr_FR.UTF-8
|
- fr_FR.UTF-8
|
||||||
- de_DE.UTF-8
|
- de_DE.UTF-8
|
||||||
- nl_NL.UTF-8
|
- nl_NL.UTF-8
|
||||||
|
|
||||||
repositories:
|
repositories:
|
||||||
php: 'ppa:ondrej/php'
|
php: 'ppa:ondrej/php'
|
||||||
mariadb: 'deb http://mirror6.layerjet.com/mariadb/repo/10.1/ubuntu'
|
mariadb: 'deb [arch=amd64,arm64,i386,ppc64el] http://mirror.nodesdirect.com/mariadb/repo/10.3/ubuntu'
|
||||||
elasticsearch: 'ppa:webupd8team/java'
|
elasticsearch: 'ppa:webupd8team/java'
|
||||||
rabbitmq: 'deb http://www.rabbitmq.com/debian/ testing main'
|
rabbitmq: 'deb http://www.rabbitmq.com/debian/ testing main'
|
||||||
yarn: 'https://dl.yarnpkg.com/debian/'
|
yarn: 'https://dl.yarnpkg.com/debian/'
|
||||||
|
|
||||||
vagrant_local:
|
vagrant_local:
|
||||||
install: '1'
|
install: '1'
|
||||||
vm:
|
vm:
|
||||||
|
|||||||
@@ -148,6 +148,9 @@ $mainMenuLinkBackgroundHoverColor: transparent;
|
|||||||
}
|
}
|
||||||
#FNDR a {
|
#FNDR a {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
|
img {
|
||||||
|
width: 16px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#FNDR a:hover {
|
#FNDR a:hover {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
@@ -840,4 +843,4 @@ span.simplecolorpicker.picker {
|
|||||||
|
|
||||||
@import './databases';
|
@import './databases';
|
||||||
@import './fields';
|
@import './fields';
|
||||||
@import './tables';
|
@import './tables';
|
||||||
|
|||||||
BIN
resources/www/common/images/Thumbs.db
Normal file
BIN
resources/www/common/images/blank.gif
Normal file
|
After Width: | Height: | Size: 49 B |
BIN
resources/www/common/images/colorpicker_background.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
resources/www/common/images/colorpicker_hex.png
Normal file
|
After Width: | Height: | Size: 532 B |
BIN
resources/www/common/images/colorpicker_hsb_b.png
Normal file
|
After Width: | Height: | Size: 970 B |
BIN
resources/www/common/images/colorpicker_hsb_h.png
Normal file
|
After Width: | Height: | Size: 1012 B |
BIN
resources/www/common/images/colorpicker_hsb_s.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
resources/www/common/images/colorpicker_indic.gif
Normal file
|
After Width: | Height: | Size: 86 B |
BIN
resources/www/common/images/colorpicker_overlay.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
resources/www/common/images/colorpicker_rgb_b.png
Normal file
|
After Width: | Height: | Size: 970 B |
BIN
resources/www/common/images/colorpicker_rgb_g.png
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
resources/www/common/images/colorpicker_rgb_r.png
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
resources/www/common/images/colorpicker_select.gif
Normal file
|
After Width: | Height: | Size: 78 B |
BIN
resources/www/common/images/colorpicker_submit.png
Normal file
|
After Width: | Height: | Size: 984 B |
BIN
resources/www/common/images/custom_background.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
resources/www/common/images/custom_hex.png
Normal file
|
After Width: | Height: | Size: 562 B |
BIN
resources/www/common/images/custom_hsb_b.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
resources/www/common/images/custom_hsb_h.png
Normal file
|
After Width: | Height: | Size: 970 B |
BIN
resources/www/common/images/custom_hsb_s.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
resources/www/common/images/custom_indic.gif
Normal file
|
After Width: | Height: | Size: 86 B |
BIN
resources/www/common/images/custom_rgb_b.png
Normal file
|
After Width: | Height: | Size: 1008 B |
BIN
resources/www/common/images/custom_rgb_g.png
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
resources/www/common/images/custom_rgb_r.png
Normal file
|
After Width: | Height: | Size: 1018 B |
BIN
resources/www/common/images/custom_submit.png
Normal file
|
After Width: | Height: | Size: 997 B |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 1010 B After Width: | Height: | Size: 1.3 KiB |
BIN
resources/www/common/images/select.png
Normal file
|
After Width: | Height: | Size: 506 B |
BIN
resources/www/common/images/select2.png
Normal file
|
After Width: | Height: | Size: 518 B |
BIN
resources/www/common/images/slider.png
Normal file
|
After Width: | Height: | Size: 315 B |
@@ -31,17 +31,13 @@ var commonModule = (function ($, p4) {
|
|||||||
$(this).removeClass('context-menu-item-hover');
|
$(this).removeClass('context-menu-item-hover');
|
||||||
});
|
});
|
||||||
|
|
||||||
// $('#help-trigger').contextMenu('#mainMenu .helpcontextmenu', {openEvt: 'click', dropDown: true, theme: 'vista', dropDown: true,
|
|
||||||
// showTransition: 'slideDown',
|
|
||||||
// hideTransition: 'hide',
|
|
||||||
// shadow: false
|
|
||||||
// });
|
|
||||||
|
|
||||||
$('body').on('click', '.infoDialog', function (event) {
|
$('body').on('click', '.infoDialog', function (event) {
|
||||||
infoDialog($(this));
|
infoDialog($(this));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
function showOverlay(n, appendto, callback, zIndex) {
|
function showOverlay(n, appendto, callback, zIndex) {
|
||||||
|
|
||||||
var div = "OVERLAY";
|
var div = "OVERLAY";
|
||||||
|
|||||||
167
resources/www/common/styles/colorpicker.scss
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
$colorPickerImagesPath: '/assets/common/images/' !default;
|
||||||
|
|
||||||
|
.colorpicker {
|
||||||
|
width: 356px;
|
||||||
|
height: 176px;
|
||||||
|
overflow: hidden;
|
||||||
|
position: absolute;
|
||||||
|
background: url('#{$colorPickerImagesPath}colorpicker_background.png');
|
||||||
|
font-family: Arial, Helvetica, sans-serif;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.colorpicker_color {
|
||||||
|
width: 150px;
|
||||||
|
height: 150px;
|
||||||
|
left: 14px;
|
||||||
|
top: 13px;
|
||||||
|
position: absolute;
|
||||||
|
background: #f00;
|
||||||
|
overflow: hidden;
|
||||||
|
cursor: crosshair;
|
||||||
|
}
|
||||||
|
.colorpicker_color div {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 150px;
|
||||||
|
height: 150px;
|
||||||
|
background: url('#{$colorPickerImagesPath}colorpicker_overlay.png');
|
||||||
|
}
|
||||||
|
.colorpicker_color div div {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 11px;
|
||||||
|
height: 11px;
|
||||||
|
overflow: hidden;
|
||||||
|
background: url('#{$colorPickerImagesPath}colorpicker_select.gif');
|
||||||
|
margin: -5px 0 0 -5px;
|
||||||
|
}
|
||||||
|
.colorpicker_hue {
|
||||||
|
position: absolute;
|
||||||
|
top: 13px;
|
||||||
|
left: 171px;
|
||||||
|
width: 35px;
|
||||||
|
height: 150px;
|
||||||
|
cursor: n-resize;
|
||||||
|
}
|
||||||
|
.colorpicker_hue div {
|
||||||
|
position: absolute;
|
||||||
|
width: 35px;
|
||||||
|
height: 9px;
|
||||||
|
overflow: hidden;
|
||||||
|
background: url('#{$colorPickerImagesPath}colorpicker_indic.gif') left top;
|
||||||
|
margin: -4px 0 0 0;
|
||||||
|
left: 0px;
|
||||||
|
}
|
||||||
|
.colorpicker_new_color {
|
||||||
|
position: absolute;
|
||||||
|
width: 60px;
|
||||||
|
height: 30px;
|
||||||
|
left: 213px;
|
||||||
|
top: 13px;
|
||||||
|
background: #f00;
|
||||||
|
}
|
||||||
|
.colorpicker_current_color {
|
||||||
|
position: absolute;
|
||||||
|
width: 60px;
|
||||||
|
height: 30px;
|
||||||
|
left: 283px;
|
||||||
|
top: 13px;
|
||||||
|
background: #f00;
|
||||||
|
}
|
||||||
|
.colorpicker input {
|
||||||
|
background-color: transparent;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
position: absolute;
|
||||||
|
font-size: 10px;
|
||||||
|
font-family: Arial, Helvetica, sans-serif;
|
||||||
|
color: #898989;
|
||||||
|
top: 4px;
|
||||||
|
right: 11px;
|
||||||
|
text-align: right;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
height: 11px;
|
||||||
|
}
|
||||||
|
.colorpicker_hex {
|
||||||
|
position: absolute;
|
||||||
|
width: 72px;
|
||||||
|
height: 22px;
|
||||||
|
background: url('#{$colorPickerImagesPath}colorpicker_hex.png') top;
|
||||||
|
left: 212px;
|
||||||
|
top: 142px;
|
||||||
|
}
|
||||||
|
.colorpicker_hex input {
|
||||||
|
right: 6px;
|
||||||
|
}
|
||||||
|
.colorpicker_field {
|
||||||
|
height: 22px;
|
||||||
|
width: 62px;
|
||||||
|
background-position: top;
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
.colorpicker_field span {
|
||||||
|
position: absolute;
|
||||||
|
width: 12px;
|
||||||
|
height: 22px;
|
||||||
|
overflow: hidden;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
cursor: n-resize;
|
||||||
|
}
|
||||||
|
.colorpicker_rgb_r {
|
||||||
|
background-image: url('#{$colorPickerImagesPath}colorpicker_rgb_r.png');
|
||||||
|
top: 52px;
|
||||||
|
left: 212px;
|
||||||
|
}
|
||||||
|
.colorpicker_rgb_g {
|
||||||
|
background-image: url('#{$colorPickerImagesPath}colorpicker_rgb_g.png');
|
||||||
|
top: 82px;
|
||||||
|
left: 212px;
|
||||||
|
}
|
||||||
|
.colorpicker_rgb_b {
|
||||||
|
background-image: url('#{$colorPickerImagesPath}colorpicker_rgb_b.png');
|
||||||
|
top: 112px;
|
||||||
|
left: 212px;
|
||||||
|
}
|
||||||
|
.colorpicker_hsb_h {
|
||||||
|
background-image: url('#{$colorPickerImagesPath}colorpicker_hsb_h.png');
|
||||||
|
top: 52px;
|
||||||
|
left: 282px;
|
||||||
|
}
|
||||||
|
.colorpicker_hsb_s {
|
||||||
|
background-image: url('#{$colorPickerImagesPath}colorpicker_hsb_s.png');
|
||||||
|
top: 82px;
|
||||||
|
left: 282px;
|
||||||
|
}
|
||||||
|
.colorpicker_hsb_b {
|
||||||
|
background-image: url('#{$colorPickerImagesPath}colorpicker_hsb_b.png');
|
||||||
|
top: 112px;
|
||||||
|
left: 282px;
|
||||||
|
}
|
||||||
|
.colorpicker_submit {
|
||||||
|
position: absolute;
|
||||||
|
width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
background: url('#{$colorPickerImagesPath}colorpicker_submit.png') top;
|
||||||
|
left: 322px;
|
||||||
|
top: 142px;
|
||||||
|
overflow: hidden;
|
||||||
|
.submiter {
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.colorpicker_focus {
|
||||||
|
background-position: center;
|
||||||
|
}
|
||||||
|
.colorpicker_hex.colorpicker_focus {
|
||||||
|
background-position: bottom;
|
||||||
|
}
|
||||||
|
.colorpicker_submit.colorpicker_focus {
|
||||||
|
background-position: bottom;
|
||||||
|
}
|
||||||
|
.colorpicker_slider {
|
||||||
|
background-position: bottom;
|
||||||
|
}
|
||||||
|
|
||||||