Merge branch 'master' into PHRAS-3358-renaming-patch

This commit is contained in:
Nicolas Maillat
2021-02-22 19:06:35 +01:00
committed by GitHub
65 changed files with 7150 additions and 2949 deletions

125
.env
View File

@@ -1,23 +1,42 @@
PHRASEANET_PROJECT_NAME=Phraseanet
PHRASEANET_SERVER_NAME=localhost # For dev purpose COMPOSE_FILE=docker-compose.yml:docker-compose.db.yml:docker-compose.mailhog.yml:docker-compose.override.yml
COMPOSE_FILE=docker-compose.yml:docker-compose.db.yml:docker-compose.mailhog.yml
# Registry from where you pull Docker images # Registry from where you pull Docker images
PHRASEANET_DOCKER_REGISTRY=local PHRASEANET_DOCKER_REGISTRY=local
# Tag of the Docker images # Tag of the Docker images
PHRASEANET_DOCKER_TAG=4.1.3 PHRASEANET_DOCKER_TAG=4.1.3
# APPLICATION PORT # APPLICATION PORT
PHRASEANET_APP_PORT=8082 PHRASEANET_APP_PORT=8082
# RabbitMQ configuration
# Phraseanet entrypoint do installation, setup and play upgrade, just essential parameters are define by this
# Phraseanet entrypoint do an installation if it not installed (0/1)
PHRASEANET_INSTALL=1
# Phraseanet entrypoint apply they env variables values to config/configuration.yml file (0/1)
PHRASEANET_SETUP=1
# Phraseanet entrypoint play an upgrade (0/1)
PHRASEANET_UPGRADE=0
# --------------- RabbitMQ SETTING ----------------------
RABBITMQ_DEFAULT_USER=alchemy RABBITMQ_DEFAULT_USER=alchemy
RABBITMQ_DEFAULT_PASS=vdh4dpe5Wy3R RABBITMQ_DEFAULT_PASS=vdh4dpe5Wy3R
RABBITMQ_MANAGEMENT_PORT=10811 RABBITMQ_MANAGEMENT_PORT=10811
# Mysql configuration
MYSQL_ROOT_PASSWORD=root
SERVER_NAME=phraseanet-docker
# --------------- GATEWAY TIMEOUT -----------------------
# --------------- GATEWAY SETTING (nginx) -----------------------
GATEWAY_SEND_TIMEOUT=120 GATEWAY_SEND_TIMEOUT=120
GATEWAY_PROXY_TIMEOUT=120 GATEWAY_PROXY_TIMEOUT=120
GATEWAY_FASTCGI_TIMEOUT=300 GATEWAY_FASTCGI_TIMEOUT=300
# --------------- PHP CONFIGURATION -------------------- # --------------- PHP CONFIGURATION --------------------
# Max upload size # Max upload size
@@ -40,26 +59,44 @@ PHP_LOG_LEVEL=warning
# --------------- MYSQL CONFIGURATION -------------------- # --------------- MYSQL CONFIGURATION --------------------
# Mysql configuration
MYSQL_ROOT_PASSWORD=root
SERVER_NAME=phraseanet-docker
# Mysql max allowed packet # Mysql max allowed packet
MYSQL_MAX_ALLOWED_PACKET=16M MYSQL_MAX_ALLOWED_PACKET=16M
# --------------- PHRASEANET CONFIGURATION -------------------- # --------------- PHRASEANET CONFIGURATION -----------------
# These variables are used in the configuration.yml . # These variables are used in the configuration.yml .
# use PHRASEANET_PROJECT_NAME env for setting Application title display on Phraseanet home page (0 keep value define configuration.yml / 1 use PHRASEANET_PROJECT_NAME)
ENV_SET_PHRASEANET_PROJET_NAME=1
PHRASEANET_PROJECT_NAME=Phraseanet
PHRASEANET_SERVER_NAME=localhost
# set here the first user / email couple # set here the first user / email couple
#set to id of Phraseanet root account, if you want activate a sync for Phraseanet root account password provide by PHRASEANET_ADMIN_ACCOUNT_PASSWORD env value. #set to id of Phraseanet root account, if you want activate a sync for Phraseanet root account password provide by PHRASEANET_ADMIN_ACCOUNT_PASSWORD env value.
PHRASEANET_ADMIN_ACCOUNT_ID= PHRASEANET_ADMIN_ACCOUNT_ID=
PHRASEANET_ADMIN_ACCOUNT_EMAIL=admin@alchemy.fr PHRASEANET_ADMIN_ACCOUNT_EMAIL=admin@alchemy.fr
PHRASEANET_ADMIN_ACCOUNT_PASSWORD=iJRqXU0MwbyJewQLBbra6IWHsWly PHRASEANET_ADMIN_ACCOUNT_PASSWORD=iJRqXU0MwbyJewQLBbra6IWHsWly
# Database parameters
# Mysql parameters
PHRASEANET_DB_HOST=db PHRASEANET_DB_HOST=db
PHRASEANET_DB_PORT=3306 PHRASEANET_DB_PORT=3306
PHRASEANET_DB_USER=root PHRASEANET_DB_USER=root
PHRASEANET_DB_PASSWORD=root PHRASEANET_DB_PASSWORD=root
# Installation parameters
INSTALL_DB_TEMPLATE=DublinCore INSTALL_DB_TEMPLATE=DublinCore
INSTALL_APPBOX=ab_master INSTALL_APPBOX=ab_master
INSTALL_DATABOX=db_databox1 INSTALL_DATABOX=db_databox1
# language : comma separated list of language code and the default language
PHRASEANET_AVAILABLE_LANGUAGE=fr,en,de,du
PHRASEANET_DEFAULT_LANGUAGE=en
# binaries execution timeouts # binaries execution timeouts
PHRASEANET_FFMPEG_TIMEOUT=7200 PHRASEANET_FFMPEG_TIMEOUT=7200
PHRASEANET_FFPROBE_TIMEOUT=120 PHRASEANET_FFPROBE_TIMEOUT=120
@@ -70,7 +107,6 @@ PHRASEANET_UNOCON_TIMEOUT=120
PHRASEANET_EXIFTOOL_TIMEOUT=120 PHRASEANET_EXIFTOOL_TIMEOUT=120
# network : comma separated list of IP ou SUBNETS # network : comma separated list of IP ou SUBNETS
PHRASEANET_TRUSTED_PROXIES= PHRASEANET_TRUSTED_PROXIES=
# api # api
@@ -78,9 +114,16 @@ PHRASEANET_API_ENABLED=true
PHRASEANET_API_SSL=true PHRASEANET_API_SSL=true
PHRASEANET_API_AUTH_TOKEN_HEADER_ONLY=false PHRASEANET_API_AUTH_TOKEN_HEADER_ONLY=false
# Mapbox setting Geolocalisation
PHRASEANET_MAPBOX_ACTIVATE=false
PHRASEANET_MAPBOX_TOKEN=
PHRASEANET_MAPBOX_KIND=
# Phraseanet mail configuration # Phraseanet mail configuration
PHRASEANET_EMITTER_EMAIL=phraseanet@example.com PHRASEANET_EMITTER_EMAIL=phraseanet@example.com
PHRASEANET_MAIL_OBJECT_PREFIX="phraseanet" PHRASEANET_MAIL_OBJECT_PREFIX="phraseanet"
# If set to true the SMTP parameters are set from .env file / to false SMTP parameters should be set in GUI
PHRASEANET_SMTP_ENABLED=true PHRASEANET_SMTP_ENABLED=true
PHRASEANET_SMTP_HOST=mailhog PHRASEANET_SMTP_HOST=mailhog
PHRASEANET_SMTP_PORT=1025 PHRASEANET_SMTP_PORT=1025
@@ -108,7 +151,6 @@ PHRASEANET_WORKER_webhook=1
PHRASEANET_WORKER_writeMetadatas=1 PHRASEANET_WORKER_writeMetadatas=1
# Locale setting # Locale setting
LC_MESSAGES=C.UTF-8 LC_MESSAGES=C.UTF-8
LC_COLLATE=C.UTF-8 LC_COLLATE=C.UTF-8
LC_IDENTIFICATION=C.UTF-8 LC_IDENTIFICATION=C.UTF-8
@@ -118,31 +160,6 @@ LC_CTYPE=C.UTF-8
LC_TIME=C.UTF-8 LC_TIME=C.UTF-8
LC_NAME=C.UTF-8 LC_NAME=C.UTF-8
# --- EXTERNAL BINARIES SETTING ----
# ImageMagick default policy override
IMAGEMAGICK_POLICY_VERSION=6
IMAGEMAGICK_POLICY_WIDTH=16KP
IMAGEMAGICK_POLICY_HEIGHT=16KP
IMAGEMAGICK_POLICY_MAP=512MiB
IMAGEMAGICK_POLICY_MEMORY=256MiB
IMAGEMAGICK_POLICY_AREA=128MB
IMAGEMAGICK_POLICY_DISK=1GiB
IMAGEMAGICK_POLICY_TEMPORARY_PATH=/tmp
# --- DEV purpose ---
# PhpMyAdmin port
PHRASEANET_PHPMYADMIN_PORT=8089
# Xdebug
XDEBUG_ENABLED=1
XDEBUG_PROFILER_ENABLED=0
IDE_KEY=PHPSTORM
PHRASEANET_SUBNET_IPS=172.32.0.0/16
XDEBUG_REMOTE_HOST=172.32.0.1
PHP_IDE_CONFIG=serverName=docker-server-phraseanet
# Volumes location # Volumes location
PHRASEANET_CONFIG_DIR=./config PHRASEANET_CONFIG_DIR=./config
PHRASEANET_LOGS_DIR=./logs PHRASEANET_LOGS_DIR=./logs
@@ -159,6 +176,37 @@ PHRASEANET_LAZARET_DIR=./datas/lazaret
PHRASEANET_CAPTION_DIR=./tmp/caption PHRASEANET_CAPTION_DIR=./tmp/caption
PHRASEANET_WORKER_TMP=./tmp/worker PHRASEANET_WORKER_TMP=./tmp/worker
# Plugin support
PHRASEANET_PLUGINS=
PHRASEANET_SSH_PRIVATE_KEY=
# --------------- EXTERNAL BINARIES SETTING -----------------
# ImageMagick default policy override
IMAGEMAGICK_POLICY_VERSION=6
IMAGEMAGICK_POLICY_WIDTH=16KP
IMAGEMAGICK_POLICY_HEIGHT=16KP
IMAGEMAGICK_POLICY_MAP=512MiB
IMAGEMAGICK_POLICY_MEMORY=256MiB
IMAGEMAGICK_POLICY_AREA=128MB
IMAGEMAGICK_POLICY_DISK=1GiB
IMAGEMAGICK_POLICY_TEMPORARY_PATH=/tmp
# --------------- DEV purpose -----------------
# PhpMyAdmin port
PHRASEANET_PHPMYADMIN_PORT=8089
# Xdebug
XDEBUG_ENABLED=1
XDEBUG_PROFILER_ENABLED=0
IDE_KEY=PHPSTORM
PHRASEANET_SUBNET_IPS=172.32.0.0/16
XDEBUG_REMOTE_HOST=172.32.0.1
PHP_IDE_CONFIG=serverName=docker-server-phraseanet
# for dev export ftp # for dev export ftp
PHRASEANET_FTP_DIR=./datas/ftp PHRASEANET_FTP_DIR=./datas/ftp
@@ -166,8 +214,3 @@ PHRASEANET_FTP_DIR=./datas/ftp
# For dev who don't have SSH_AUTH_SOCK (avoid an empty volume name) # For dev who don't have SSH_AUTH_SOCK (avoid an empty volume name)
SSH_AUTH_SOCK=/dev/null SSH_AUTH_SOCK=/dev/null
# Plugin support
PHRASEANET_PLUGINS=
PHRASEANET_SSH_PRIVATE_KEY=

View File

@@ -13,7 +13,7 @@ to update in swaggerhub (single file) :
- compile sources in a single file for swaggerhub (run from <phraseanet-dir>) - compile sources in a single file for swaggerhub (run from <phraseanet-dir>)
`swagger-cli bundle API_documentation/v3/api.yaml -o API_documentation/v3/_compiled.yaml -t yaml` `swagger-cli bundle API_documentation/v3/api.yaml -r -o API_documentation/v3/_compiled.yaml -t yaml`
- copy/paste the generated content from `_compiled.yaml` to - copy/paste the generated content from `_compiled.yaml` to

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,113 @@
error_response:
description: Any other error
ApiResponse_meta:
type: object
properties:
api_version:
type: string
example:
'3.0.0'
request:
type: string
example:
'GET|POST|... /api/v3/....'
response_time:
type: string
format: date-time
example:
'2021-02-11T14:18:02+00:00'
http_code:
type: integer
format: int32
example:
200
error_type:
type: string
example:
null
error_message:
type: string
example:
null
error_details:
type: string
example:
null
charset:
type: string
example:
'UTF-8'
Facet:
type: object
properties:
name:
type: string
description: 'Unique internal name (=key) for the facet'
example: '_base'
field:
type: string
description: 'Source field (from db structure). Can be also virtual field like "database" '
example: 'database'
values:
type: array
items:
type: object
properties:
value:
type: string
description: 'Human readable value for the value'
example: 'Demo'
raw_value:
type: string
description: 'Real value, to be used to query'
example: 'db_demo'
count:
type: integer
description: 'Number of items matching this value'
query:
type: string
description: 'Formulated query to search items matching this facet'
example: 'database:db_demo'
PermalinkObject:
type: object
properties:
created_on:
type: string
format: date-time
id:
$ref: '#/ID'
is_activated:
type: boolean
label:
type: string
updated_on:
type: string
format: date-time
page_url:
type: string
download_url:
type: string
url:
type: string
FacetsArray:
type: array
items:
$ref: '#/Facet'
ID:
type: integer
Metadata_value:
type: object
properties:
meta_id:
type: integer
example: 1771
value:
type: string
example: value_of_the_field

View File

@@ -0,0 +1,175 @@
ESRecord:
description: 'Raw response from es search on "record" index/mapping'
type: object
properties:
_index:
type: string
description: 'ES index'
example:
'phraseanet_dxmpcw3y8td68f+h_20201124161735.910647'
_type:
type: string
description: 'Data type'
example:
'record'
_id:
type: string
description: 'unique id of document (sbas_id + "_" + record_id)'
example:
'1_555'
_version:
type: integer
description: 'auto-increment at each indexation of the document'
example:
1
_score:
type: number
description: 'score of the document related to a whole resultset'
example:
1
_source:
$ref: '#/ESRecordSource'
ESRecordSource:
type: object
properties:
record_id:
$ref: 'common.yaml#/ID'
collection_id:
$ref: 'common.yaml#/ID'
uuid:
type: string
example:
'dcee40ea-ee26-4d8b-b0c2-d61305b03bc0'
flags_bitfield:
type: integer
sha256:
type: string
example:
'7fad283de349b903c850548cda65cf2d86d24c4e3856cdc2b97e47430494b8c8'
original_name:
type: string
example:
'1134340545.jpg'
mime:
type: string
example:
'image/jpeg'
type:
type: string
example:
'image'
created_on:
type: string
format: date-time
example:
'2020-12-07 09:48:01'
updated_on:
type: string
format: date-time
example:
'2021-01-01 15:30:00'
coll_id:
$ref: 'common.yaml#/ID'
collection_name:
type: string
example:
'collection de test'
witdh:
type: integer
example:
5616
height:
type: integer
example:
3744
size:
type: integer
example:
5618218
base_id:
$ref: 'common.yaml#/ID'
databox_id:
$ref: 'common.yaml#/ID'
databox_name:
type: string
example:
'db_databox1'
record_type:
type: string
enum: ['record','story']
title:
type: object
description: 'key->value list, where (key) is the lng, and (value) the title in this lng.'
additionalProperties: true
example:
fr: 'titre en Français'
en: 'title in english'
'': 'undefined-lng title ? To be fixed'
metadata_tags:
type: object
description: 'key->value list, where (key) is the name of the metadata, and (value) the value.'
additionalProperties: true
example:
Channels: 3
ColorDepth: 8
ColorSpace: 0
FileSize: 5618218
Height: 3744
MimeType: 'image/jpeg'
Width: 5616
caption:
type: object
description: >
'key->value list, where (key) is the field name, and (value) the value(s).'
'nb: mono-valued field value is a 1 element array.'
additionalProperties:
type: array
items:
type: string
example:
Artist: ['Bob']
Title: ['Cropped Hand Of Person Holding Computer Mouse']
Keywords: ['Hand', 'Mouse (computer)']
caption_all:
type: array
items:
type: string
description: >
'all fields values in a single array'
example: ['Bob','Cropped Hand Of Person Holding Computer Mouse','Hand','Mouse (computer)']
flags:
$ref: '#/Flags'
subdefs:
type: object
description: 'key->value list, where (key) is the name of the subdef, and (value) is the subdef object.'
additionalProperties:
type: object
example:
document:
type: object
properties:
width: 5616
height: 3744
size: 5618218
mime: 'image/jpeg'
permalink: 'http://localhost/permalink/v1/2/34/document/1134340545.jpg?token=xrdMnK6peB...'
thumbnail:
type: object
properties:
width: 1024
height: 683
size: 20011
mime: 'image/jpeg'
permalink: 'http://localhost/permalink/v1/2/34/preview/1134340545.jpg?token=E5aSbXQTmAz...'
Flags:
type: object
description: 'key->value list, where (key) is the name of the flag (=status bit), and (value) is the boolean value.'
additionalProperties:
type: boolean
example:
public: true
color_checked: true
embargo: false

View File

@@ -0,0 +1,293 @@
_Record_:
type: object
properties:
databox_id:
type: integer
example:
2
record_id:
type: integer
example:
34
updated_on:
type: string
format: date-time
created_on:
type: string
format: date-time
collection_id:
type: integer
example:
5
base_id:
type: integer
example:
14
thumbnail:
$ref: '#/Thumbnail'
uuid:
type: string
example:
'5b079f33-0851-4aec-a978-b7f8d7204e5a'
_record_extension_:
type: object
properties:
subdefs:
type: array
items:
$ref: '#/Subdef'
status:
type: array
items:
type: object
properties:
bit:
type: integer
example: 4
state:
type: boolean
example: false
metadata:
type: array
items:
type: object
properties:
meta_structure_id:
type: integer
name:
type: string
labels:
type: object
additionalProperties:
type: string
example:
fr: label_du_champ_en_français
en: field_label_in_english
value:
oneOf:
- $ref: 'common.yaml#/Metadata_value'
- type: array
items:
$ref: 'common.yaml#/Metadata_value'
dces:
type: array
items:
type: string
example:
'_not_documented_TODO_'
Record:
allOf: # Combines the basic _Record_ and the records-only properties
- $ref: '#/_Record_'
- type: object
properties:
mime_type:
type: string
example:
'image/jpeg'
title:
type: string
example:
'Sleepy cat'
original_name:
type: string
example:
'DSC_12345.jpg'
technical_informations:
type: array
items:
$ref: '#/TechnicalInformation'
sha256:
type: string
example:
'6f330ac0ae2...'
phrasea_type:
type: string
enum:
- image
- video
example:
'image'
is_story:
type: boolean
default: false
Record_extended:
allOf:
- $ref: '#/Record'
- $ref: '#/_record_extension_'
Story:
allOf: # Combines the basic _Record_ and the stories-only properties
- $ref: '#/_Record_'
- type: object
properties:
mime_type:
type: string
default: null
title:
type: string
example:
'sans-titre'
original_name:
type: string
default: null
is_story:
type: boolean
default: true
children_offset:
type: integer
description: 'Children pagination offset ; Always 0'
example: 0
children_limit:
type: integer
description: 'Children pagination limit ; Equal to "story_children_limit" passed in request'
example: 10
children_count:
type: integer
description: 'Number of children in "children" array ; [0...limit]'
example: 5
children_total:
type: integer
description: 'Total number of __visibles for user__ children in this story'
example: 5
children:
type: array
items:
type: object
items:
$ref: '#/Record'
Story_extended:
allOf:
- $ref: '#/Story'
- $ref: '#/_record_extension_'
TechnicalInformation:
type: object
additionalProperties:
type: string
example:
Aperture: 6.3
CameraModel: 'Canon EOS 5D Mark II'
Channels: 3
ColorSpace: RGB
FileSize: 5618218
Subdef:
type: object
properties:
name:
type: string
permalink:
$ref: 'common.yaml#/PermalinkObject'
height:
type: integer
width:
type: integer
filesize:
type: integer
devices:
type: array
items:
type: string
enum:
- screen
- printer ?
player_type:
type: string
enum:
- IMAGE
mime_type:
type: string
substituted:
type: boolean
example: false
created_on:
type: string
format: date-time
updated_on:
type: string
format: date-time
url:
type: string
url_ttl:
type: integer
example: 7200
Thumbnail:
allOf:
- type: object
properties:
name:
example: 'thumbnail'
mime_type:
example: 'image/jpeg'
height:
example: 160
width:
example: 240
filesize:
example: 2375
url:
example: 'http://phraseanet.demo.fr/medias/eyJ0eXAiOiJKV1Q...'
- $ref: '#/Subdef'
ApiResponse_record:
type: object
properties:
meta:
$ref: 'common.yaml#/ApiResponse_meta'
response:
$ref: '#/Record'
ApiResponse_record_extended:
type: object
properties:
meta:
$ref: 'common.yaml#/ApiResponse_meta'
response:
$ref: '#/Record_extended'
ApiResponse_story:
type: object
properties:
meta:
$ref: 'common.yaml#/ApiResponse_meta'
response:
$ref: '#/Story'
ApiResponse_story_extended:
type: object
properties:
meta:
$ref: 'common.yaml#/ApiResponse_meta'
response:
$ref: '#/Story_extended'
# for "/stories/{sbas_id}/{record_id}/children"
RecordUri:
type: string
example: '/api/v3/records/1/48'
RecordsUriArray:
type: array
items:
$ref: '#/RecordUri'
example:
- '/api/v3/records/1/48'
- '/api/v3/records/1/49'
- '/api/v3/records/1/50'
ApiResponse_RecordsUriArray:
type: object
properties:
meta:
$ref: 'common.yaml#/ApiResponse_meta'
response:
$ref: '#/RecordsUriArray'

View File

@@ -1,15 +0,0 @@
record_response:
type: object
properties:
200:
description: successful operation
content:
application/json:
schema:
$ref: schemas.yaml#/ApiResponse_record
400:
description: Any other error
404:
description: Record not found
error_response:
description: Any (other) error

View File

@@ -4,7 +4,7 @@ RecordPatch_metadata:
field_name: field_name:
type: string type: string
meta_struct_id: meta_struct_id:
$ref: '#/ID' $ref: 'common.yaml#/ID'
action: action:
type: string type: string
enum: enum:
@@ -15,6 +15,8 @@ RecordPatch_metadata:
value: value:
# todo : change to string, int, number, array # todo : change to string, int, number, array
type: string type: string
RecordPatch_status: RecordPatch_status:
type: object type: object
required: required:
@@ -39,254 +41,9 @@ RecordPatch:
type: array type: array
items: items:
$ref: '#/RecordPatch_status' $ref: '#/RecordPatch_status'
ApiResponse_meta: # -------------------- searchraw ---------------
type: object
properties:
api_version:
type: string
request:
type: string
response_time:
type: string
format: date-time
http_code:
type: integer
format: int32
error_type:
type: string
error_message:
type: string
ApiResponse_record:
type: object
properties:
meta:
$ref: '#/ApiResponse_meta'
response:
$ref: '#/Record'
# -------------------- search --------------- # -------------------- search ---------------
ApiResponse_search:
type: object
properties:
meta:
$ref: '#/ApiResponse_meta'
response:
$ref: '#/ApiResponse_search_response'
ApiResponse_search_response:
type: object
properties:
offset:
type: integer
description: 'The pagination offset as passed (or computed from "page/per_page") in request'
limit:
type: integer
description: 'The pagination limit as passed in request'
count:
type: integer
description: 'The number of results in this page [0...limit]'
total:
type: integer
description: 'The total number of results'
minimum: 1
results:
type: object
properties:
stories:
type: array
items:
$ref: '#/Story'
records:
type: array
items:
$ref: '#/Record'
facets:
type: array
items:
type: object
properties:
name:
type: string
description: 'The unique internal name (=key) for the facet'
example: '_base'
field:
type: string
description: 'Source field (from db structure). Can be also virtual field like "database" '
example: 'database'
values:
type: array
items:
type: object
properties:
value:
type: string
description: 'Human readable value for the value'
example: 'Demo'
raw_value:
type: string
description: 'Real value, to be used to query'
example: 'db_demo'
count:
type: integer
description: 'Number of items matching this value'
query:
type: string
description: 'Formulated query to search items matching this facet'
example: 'database:db_demo'
ID:
type: integer
Permalink:
type: object
properties:
created_on:
type: string
format: date-time
id:
$ref: '#/ID'
is_activated:
type: boolean
label:
type: string
updated_on:
type: string
format: date-time
page_url:
type: string
download_url:
type: string
url:
type: string
Subdef:
type: object
properties:
name:
type: string
permalink:
$ref: '#/Permalink'
height:
type: integer
width:
type: integer
filesize:
type: integer
devices:
type: array
items:
type: string
enum:
- screen
- printer ?
player_type:
type: string
enum:
- IMAGE
mime_type:
type: string
substituted:
type: boolean
created_on:
type: string
format: date-time
updated_on:
type: string
format: date-time
url:
type: string
url_ttl:
type: integer
TechnicalInformation:
type: object
properties:
name:
type: string
value:
type: string
_Record_:
type: object
properties:
databox_id:
$ref: '#/ID'
record_id:
$ref: '#/ID'
title:
type: string
original_name:
type: string
updated_on:
type: string
format: date-time
created_on:
type: string
format: date-time
collection_id:
$ref: '#/ID'
base_id:
$ref: '#/ID'
thumbnail:
$ref: '#/Subdef'
uuid:
type: string
Record:
allOf: # Combines the basic _Record_ and the records-only properties
- $ref: '#/_Record_'
- type: object
properties:
mime_type:
type: string
technical_informations:
type: array
items:
$ref: '#/TechnicalInformation'
sha256:
type: string
phrasea_type:
type: string
enum:
- image
- video
Story:
allOf: # Combines the basic _Record_ and the stories-only properties
- $ref: '#/_Record_'
- type: object
properties:
children_offset:
type: integer
description: 'The children pagination offset ; Always 0'
children_limit:
type: integer
description: 'The children pagination limit ; Equal to "story_children_limit" passed in request'
children_count:
type: integer
description: 'The number of children in "children" array ; [0...limit]'
children_total:
type: integer
description: 'The total number of -visibles for user- children in this story'
children:
type: array
items:
type: object
items:
$ref: '#/Record'
RecordUri:
type: string
example:
'/api/v3/records/1/48'
RecordsUriArray:
type: array
items:
$ref: '#/RecordUri'
ApiResponse_RecordsUriArray:
type: object
properties:
meta:
$ref: '#/ApiResponse_meta'
response:
$ref: '#/RecordsUriArray'

View File

@@ -0,0 +1,39 @@
ApiResponse_search:
type: object
properties:
meta:
$ref: 'common.yaml#/ApiResponse_meta'
response:
$ref: '#/ApiResponse_search_response'
ApiResponse_search_response:
type: object
properties:
offset:
type: integer
description: 'Pagination offset as passed (or computed from "page/per_page") in request'
limit:
type: integer
description: 'Pagination limit as passed in request'
count:
type: integer
description: 'Number of results in this page [0...limit]'
total:
type: integer
description: 'Total number of results'
minimum: 1
results:
type: object
properties:
stories:
type: array
items:
$ref: 'record.yaml#/Story'
records:
type: array
items:
$ref: 'record.yaml#/Record'
facets:
$ref: 'common.yaml#/FacetsArray'

View File

@@ -0,0 +1,38 @@
ApiResponse_searchraw:
type: object
properties:
meta:
$ref: 'common.yaml#/ApiResponse_meta'
response:
$ref: '#/ApiResponse_searchraw_response'
ApiResponse_searchraw_response:
type: object
properties:
results:
type: array
items:
$ref: 'es.yaml#/ESRecord'
took:
type: integer
description: 'Search duration in msec'
example:
12
# offset:
# type: integer
# description: 'Pagination offset as passed (or computed from "page/per_page") in request'
# limit:
# type: integer
# description: 'Pagination limit as passed in request'
count:
type: integer
description: 'Number of results in this page [0...limit]'
example:
1
total:
type: integer
description: 'Total number of results'
example:
1
facets:
$ref: 'common.yaml#/FacetsArray'

View File

@@ -229,3 +229,5 @@ COPY --from=builder /var/alchemy/Phraseanet/www /var/alchemy/Phraseanet/www
ENTRYPOINT ["/entrypoint.sh"] ENTRYPOINT ["/entrypoint.sh"]
CMD ["nginx", "-g", "daemon off;"] CMD ["nginx", "-g", "daemon off;"]
HEALTHCHECK CMD wget --spider http://127.0.0.1/login || nginx -s reload || exit 1

View File

@@ -16,7 +16,7 @@ Node `^5.0.0`.
- make your modification - make your modification
- Generate dist ```npm run dist``` - Generate dist ```npm run dist```
- ```make install_asset``` to copy assets in www/assets folder - ```make install_asset``` to copy assets in www/assets folder
- If features is finished ```dist``` folder is to be commited and increment `jsFileVersion` in `lib/Alchemy/Phrasea/Twig/PhraseanetExtension.php` - If features is finished ```dist``` folder is to be commited and increment `jsFileVersion` in `lib/Alchemy/Phrasea/Twig/PhraseanetExtension.php` and in `Phraseanet-production-client/config/config.js`
## Available commands ## Available commands

View File

@@ -11,5 +11,7 @@ module.exports = {
sourceDir: _root + 'src/', sourceDir: _root + 'src/',
testDir: _root + 'tests', testDir: _root + 'tests',
setupDir: _root + 'tests/setup/node.js', setupDir: _root + 'tests/setup/node.js',
karmaConf: _root + 'config/karma.conf.js' karmaConf: _root + 'config/karma.conf.js',
// change this version when you change JS file for lazy loading
jsFileVersion: 6
}; };

View File

@@ -40,7 +40,7 @@ module.exports = {
output: { output: {
path: config.distDir, path: config.distDir,
filename: '[name].js', filename: '[name].js',
chunkFilename: 'lazy-[name].js', chunkFilename: 'lazy-[name].js?v=' + config.jsFileVersion,
libraryTarget: 'umd', libraryTarget: 'umd',
library: config._app, library: config._app,
publicPath: '/assets/production/' publicPath: '/assets/production/'

View File

@@ -20,7 +20,7 @@ module.exports = Object.assign({}, webpackConfig, {
output: { output: {
path: config.distDir, path: config.distDir,
filename: '[name].min.js', filename: '[name].min.js',
chunkFilename: 'lazy-[name].min.js', chunkFilename: 'lazy-[name].min.js?v=' + config.jsFileVersion,
libraryTarget: 'umd', libraryTarget: 'umd',
library: config._app, library: config._app,
publicPath: '/assets/production/' publicPath: '/assets/production/'

View File

@@ -96,7 +96,7 @@ return /******/ (function(modules) { // webpackBootstrap
/******/ if (__webpack_require__.nc) { /******/ if (__webpack_require__.nc) {
/******/ script.setAttribute("nonce", __webpack_require__.nc); /******/ script.setAttribute("nonce", __webpack_require__.nc);
/******/ } /******/ }
/******/ script.src = __webpack_require__.p + "lazy-" + ({}[chunkId]||chunkId) + ".js"; /******/ script.src = __webpack_require__.p + "lazy-" + ({}[chunkId]||chunkId) + ".js?v=6";
/******/ var timeout = setTimeout(onScriptComplete, 120000); /******/ var timeout = setTimeout(onScriptComplete, 120000);
/******/ script.onerror = script.onload = onScriptComplete; /******/ script.onerror = script.onload = onScriptComplete;
/******/ function onScriptComplete() { /******/ function onScriptComplete() {

View File

@@ -96,7 +96,7 @@ return /******/ (function(modules) { // webpackBootstrap
/******/ if (__webpack_require__.nc) { /******/ if (__webpack_require__.nc) {
/******/ script.setAttribute("nonce", __webpack_require__.nc); /******/ script.setAttribute("nonce", __webpack_require__.nc);
/******/ } /******/ }
/******/ script.src = __webpack_require__.p + "lazy-" + ({}[chunkId]||chunkId) + ".min.js"; /******/ script.src = __webpack_require__.p + "lazy-" + ({}[chunkId]||chunkId) + ".min.js?v=6";
/******/ var timeout = setTimeout(onScriptComplete, 120000); /******/ var timeout = setTimeout(onScriptComplete, 120000);
/******/ script.onerror = script.onload = onScriptComplete; /******/ script.onerror = script.onload = onScriptComplete;
/******/ function onScriptComplete() { /******/ function onScriptComplete() {

View File

@@ -91,7 +91,7 @@
/******/ if (__webpack_require__.nc) { /******/ if (__webpack_require__.nc) {
/******/ script.setAttribute("nonce", __webpack_require__.nc); /******/ script.setAttribute("nonce", __webpack_require__.nc);
/******/ } /******/ }
/******/ script.src = __webpack_require__.p + "lazy-" + ({}[chunkId]||chunkId) + ".js"; /******/ script.src = __webpack_require__.p + "lazy-" + ({}[chunkId]||chunkId) + ".js?v=6";
/******/ var timeout = setTimeout(onScriptComplete, 120000); /******/ var timeout = setTimeout(onScriptComplete, 120000);
/******/ script.onerror = script.onload = onScriptComplete; /******/ script.onerror = script.onload = onScriptComplete;
/******/ function onScriptComplete() { /******/ function onScriptComplete() {

View File

@@ -91,7 +91,7 @@
/******/ if (__webpack_require__.nc) { /******/ if (__webpack_require__.nc) {
/******/ script.setAttribute("nonce", __webpack_require__.nc); /******/ script.setAttribute("nonce", __webpack_require__.nc);
/******/ } /******/ }
/******/ script.src = __webpack_require__.p + "lazy-" + ({}[chunkId]||chunkId) + ".min.js"; /******/ script.src = __webpack_require__.p + "lazy-" + ({}[chunkId]||chunkId) + ".min.js?v=6";
/******/ var timeout = setTimeout(onScriptComplete, 120000); /******/ var timeout = setTimeout(onScriptComplete, 120000);
/******/ script.onerror = script.onload = onScriptComplete; /******/ script.onerror = script.onload = onScriptComplete;
/******/ function onScriptComplete() { /******/ function onScriptComplete() {

View File

@@ -65747,13 +65747,13 @@ var previewRecordService = function previewRecordService(services) {
var basketIcon = ''; var basketIcon = '';
if (data.containerType !== null) { if (data.containerType !== null) {
if (data.containerType === 'feedback') { if (data.containerType === 'feedback') {
basketIcon = "<img src='/assets/common/images/icons/basket_validation.png' title='' width='20' class='btn-image' style='width:20px;height: 20px;'/>"; basketIcon = "<img src='/assets/common/images/icons/basket_validation.png' title='' width='24' class='btn-image' style='width:24px;height: 24px;'/>";
} else if (data.containerType === 'push') { } else if (data.containerType === 'push') {
basketIcon = "<img src='/assets/common/images/icons/basket_push.png' title='' width='20' class='btn-image' style='width:20px;height: 20px;'/>"; basketIcon = "<img src='/assets/common/images/icons/basket_push.png' title='' width='24' class='btn-image' style='width:24px;height: 24px;'/>";
} else if (data.containerType === 'regroup') { } else if (data.containerType === 'regroup') {
basketIcon = "<img src='/assets/common/images/icons/story.png' title='' width='20' class='btn-image' style='width:20px;height: 20px;'/>"; basketIcon = "<img src='/assets/common/images/icons/story.png' title='' width='24' class='btn-image' style='width:24px;height: 24px;'/>";
} else { } else {
basketIcon = "<img src='/assets/common/images/icons/basket.png' title='' width='20' class='btn-image' style='width:20px;height: 20px;'/>"; basketIcon = "<img src='/assets/common/images/icons/basket.png' title='' width='24' class='btn-image' style='width:24px;height: 24px;'/>";
} }
} }

View File

@@ -65747,13 +65747,13 @@ var previewRecordService = function previewRecordService(services) {
var basketIcon = ''; var basketIcon = '';
if (data.containerType !== null) { if (data.containerType !== null) {
if (data.containerType === 'feedback') { if (data.containerType === 'feedback') {
basketIcon = "<img src='/assets/common/images/icons/basket_validation.png' title='' width='20' class='btn-image' style='width:20px;height: 20px;'/>"; basketIcon = "<img src='/assets/common/images/icons/basket_validation.png' title='' width='24' class='btn-image' style='width:24px;height: 24px;'/>";
} else if (data.containerType === 'push') { } else if (data.containerType === 'push') {
basketIcon = "<img src='/assets/common/images/icons/basket_push.png' title='' width='20' class='btn-image' style='width:20px;height: 20px;'/>"; basketIcon = "<img src='/assets/common/images/icons/basket_push.png' title='' width='24' class='btn-image' style='width:24px;height: 24px;'/>";
} else if (data.containerType === 'regroup') { } else if (data.containerType === 'regroup') {
basketIcon = "<img src='/assets/common/images/icons/story.png' title='' width='20' class='btn-image' style='width:20px;height: 20px;'/>"; basketIcon = "<img src='/assets/common/images/icons/story.png' title='' width='24' class='btn-image' style='width:24px;height: 24px;'/>";
} else { } else {
basketIcon = "<img src='/assets/common/images/icons/basket.png' title='' width='20' class='btn-image' style='width:20px;height: 20px;'/>"; basketIcon = "<img src='/assets/common/images/icons/basket.png' title='' width='24' class='btn-image' style='width:24px;height: 24px;'/>";
} }
} }

View File

@@ -2048,6 +2048,10 @@ div#PREVIEWTITLEWRAPPER {
top: 10px; top: 10px;
} }
div#PREVIEWTITLEWRAPPER span {
line-height: 25px;
}
.PNB10 { .PNB10 {
position: absolute; position: absolute;
top: 10px; top: 10px;

File diff suppressed because one or more lines are too long

View File

@@ -2063,6 +2063,10 @@ div#PREVIEWTITLEWRAPPER {
top: 10px; top: 10px;
} }
div#PREVIEWTITLEWRAPPER span {
line-height: 25px;
}
.PNB10 { .PNB10 {
position: absolute; position: absolute;
top: 10px; top: 10px;

File diff suppressed because one or more lines are too long

View File

@@ -2065,6 +2065,10 @@ div#PREVIEWTITLEWRAPPER {
top: 10px; top: 10px;
} }
div#PREVIEWTITLEWRAPPER span {
line-height: 25px;
}
.PNB10 { .PNB10 {
position: absolute; position: absolute;
top: 10px; top: 10px;

File diff suppressed because one or more lines are too long

View File

@@ -384,13 +384,13 @@ const previewRecordService = services => {
let basketIcon = ''; let basketIcon = '';
if (data.containerType !== null ) { if (data.containerType !== null ) {
if (data.containerType === 'feedback') { if (data.containerType === 'feedback') {
basketIcon = "<img src='/assets/common/images/icons/basket_validation.png' title='' width='20' class='btn-image' style='width:20px;height: 20px;'/>"; basketIcon = "<img src='/assets/common/images/icons/basket_validation.png' title='' width='24' class='btn-image' style='width:24px;height: 24px;'/>";
} else if (data.containerType === 'push') { } else if (data.containerType === 'push') {
basketIcon = "<img src='/assets/common/images/icons/basket_push.png' title='' width='20' class='btn-image' style='width:20px;height: 20px;'/>"; basketIcon = "<img src='/assets/common/images/icons/basket_push.png' title='' width='24' class='btn-image' style='width:24px;height: 24px;'/>";
} else if (data.containerType === 'regroup') { } else if (data.containerType === 'regroup') {
basketIcon = "<img src='/assets/common/images/icons/story.png' title='' width='20' class='btn-image' style='width:20px;height: 20px;'/>"; basketIcon = "<img src='/assets/common/images/icons/story.png' title='' width='24' class='btn-image' style='width:24px;height: 24px;'/>";
} else { } else {
basketIcon = "<img src='/assets/common/images/icons/basket.png' title='' width='20' class='btn-image' style='width:20px;height: 20px;'/>"; basketIcon = "<img src='/assets/common/images/icons/basket.png' title='' width='24' class='btn-image' style='width:24px;height: 24px;'/>";
} }
} }

View File

@@ -164,6 +164,9 @@ EM {
div#PREVIEWTITLEWRAPPER { div#PREVIEWTITLEWRAPPER {
top: 10px; top: 10px;
span {
line-height: 25px;
}
} }
.PNB10 { .PNB10 {

231
README.md
View File

@@ -3,12 +3,15 @@ Phraseanet 4.1 - Digital Asset Management application
[![CircleCI](https://circleci.com/gh/alchemy-fr/Phraseanet/tree/master.svg?style=shield)](https://circleci.com/gh/alchemy-fr/Phraseanet/tree/master) [![CircleCI](https://circleci.com/gh/alchemy-fr/Phraseanet/tree/master.svg?style=shield)](https://circleci.com/gh/alchemy-fr/Phraseanet/tree/master)
# Features : # Main Features :
- Several GUI : Prod, Admin, Thesaurus, Lightbox ,Report,
- Metadata Management (include Thesaurus and DublinCore Mapping) - Metadata Management (include Thesaurus and DublinCore Mapping)
- RestFull APIS - RestFull APIS
- Elasticsearch search engine - Elasticsearch search engine
- Multiple resolution assets generation - Multiple resolution assets generation
- Advanced Rights Management
- Rich ecosystem: Plugin for Wordpress, Drupal and Adobe Creative Suite.
# License : # License :
@@ -20,29 +23,111 @@ https://docs.phraseanet.com/
For development with Phraseanet API see https://docs.phraseanet.com/4.1/en/Devel/index.html For development with Phraseanet API see https://docs.phraseanet.com/4.1/en/Devel/index.html
# Installation :
You can download a packaged version here: : # Installation and Quick Launch:
https://www.phraseanet.com/download/
And follow the install steps described at https://docs.phraseanet.com/4.1/en/Admin/Install.html You can download a packaged version here:
https://www.phraseanet.com/en/download/
You can also ```git clone``` this repository for dev and/or test.
In each case, Phraseanet includes Dockerfile for building images and Docker-compose deployment.
See below for more information about Prerequisites and how to personalize the stack deployed.
But in fact if you have Docker Prerequisites, Phraseanet can be deployed and installed with these 2 simple commands.
In a terminal from the Phraseanet repositorie launch
docker-compose build
docker-compose up -d
After installation processus, The default parameters allow you to reach the app on : `http://localhost:8082`
Default see phraseanet credential define in .env file.
PHRASEANET_ADMIN_ACCOUNT_EMAIL
PHRASEANET_ADMIN_ACCOUNT_PASSWORD
> Note : This install will be made with default password for all services.
> Except for a test, This is VERY important to customise .env file and define your own password.
For installation on your own mono-tenant or multi-tenant infrastructure (mysql, elasticsearch, redis, etc) stack,
follow the install steps described at
https://docs.phraseanet.com/4.1/en/Admin/Install.html
We do not recommend using this method anymore.
# Phraseanet with Docker: # Phraseanet with Docker:
## Prerequisites ## Prerequisites
- docker-compose >=v1.25.4 - docker-compose >=v1.27.4
- docker >=v18.01-ce - docker >=v19.03.13
Note about elasticsearch container, check this link
Note about elasticsearch container
Check this link
https://www.elastic.co/guide/en/elasticsearch/reference/current/docker.html#docker-prod-prerequisites https://www.elastic.co/guide/en/elasticsearch/reference/current/docker.html#docker-prod-prerequisites
## Get started
### What is docker? Read this:
https://www.docker.com/get-started
### Host requirement:
Linux : https://hub.docker.com/search?q=&type=edition&offering=community&operating_system=linux
Macintosh : https://hub.docker.com/editions/community/docker-ce-desktop-mac
Windows : https://hub.docker.com/editions/community/docker-ce-desktop-windows
> Note: All our images are Linux based, so with Macintosh and Windows hosts, the containers run in vm provided by Docker.
> For optimal performances, prefer a Linux host.
## Stack description and customization
We provide a Dockerfile docker-compose deployment
Use ```COMPOSE_FILE``` env variables for composing this deployment.
By default COMPOSE_FILE is set for deploying a test stack including containers.
phraseanet_db_1
phraseanet_elasticsearch_1
phraseanet_gateway_1
phraseanet_mailhog_1
phraseanet_phraseanet_1
phraseanet_rabbitmq_1
phraseanet_redis_1
phraseanet_worker_1
At first launch of the stack, Phraseanet container plays install.
it will restart until it can do this installation: waiting for readiness of all other containers
You should review the default env variables defined in `.env` file. You should review the default env variables defined in `.env` file.
Use `export` to override these values. Use `export` method to override these values.
i.e: i.e:
```bash ```bash
@@ -51,8 +136,10 @@ export INSTALL_ACCOUNT_EMAIL=foo@bar.com
export INSTALL_ACCOUNT_PASSWORD=$3cr3t! export INSTALL_ACCOUNT_PASSWORD=$3cr3t!
export PHRASEANET_APP_PORT=8082 export PHRASEANET_APP_PORT=8082
``` ```
If you are not interested in the development of Phraseanet, you can ignore everything in `.env` after the `DEV Purpose` part.
### Using a env.local (custom .env)
### Using a env.local method for custom .env values
It may be easier to deal with a local file to manage our env variables. It may be easier to deal with a local file to manage our env variables.
@@ -69,61 +156,64 @@ function dc() {
} }
``` ```
### Running the application ### Phraseanet Docker Images
If you are not interested in the development of Phraseanet, you can ignore everything in `.env` after the `DEV Purpose` part. You have two choices
docker-compose -f docker-compose.yml up -d #### Use the prebuild image from dockerhub, see DockerHub section bellow for more information.
Why this option `-f docker-compose.yml`? set env var
The development and integration concerns are separated using a `docker-compose.override.yml`. By default, `docker-compose` will include this files if it exists. ```PHRASEANET_DOCKER_REGISTRY```
If you don't work on phraseanet development, avoiding this `-f docker-compose.yml` parameters will throw errors. So you have to add this options on every `docker-compose` commands to avoid this inclusion.
> You can also delete the `docker-compose.override.yml` to get free from this behavior.
#### Running workers
```bash
docker-compose -f docker-compose.yml run --rm worker <command>
```
Where `<command>` can be:
- `bin/console worker:execute -m 2` (default)
- `bin/console task-manager:scheduler:run`
- ...
The default parameters allow you to reach the app with : `http://localhost:8082`
### Use Phraseanet images from docker hub
Retrieve on Docker hub prebuilt images for Phraseanet.
https://hub.docker.com/r/alchemyfr/phraseanet-fpm
https://hub.docker.com/r/alchemyfr/phraseanet-worker
https://hub.docker.com/r/alchemyfr/phraseanet-nginx
https://hub.docker.com/repository/docker/alchemyfr/phraseanet-db
https://hub.docker.com/repository/docker/alchemyfr/phraseanet-elasticsearch
To use them and not build the images locally, we advise to override the properties in file: env.local
i.e:
```bash ```bash
# Registry from where you pull Docker images # Registry from where you pull Docker images
PHRASEANET_DOCKER_REGISTRY=alchemyfr PHRASEANET_DOCKER_REGISTRY=alchemyfr
# Tag of the Docker images
PHRASEANET_DOCKER_TAG=
``` ```
or and launch
Pull images before launch docker-compose ```docker-compose pull```
#### Tag organisation on docker hub > Pulling images from Docker Hub takes ~ 3 minutes, depending on your bandwith
#### Build local images
launch
```docker-compose build```
> The first build takes ~ 30 minutes on host without any Docker building cache, depending on your bandwith and the host capacity.
### Running the application
docker-compose up -d
The default parameters allow you to reach the app with : `http://localhost:8082`
### Phraseanet images from Docker Hub
Retrieve on Docker Hub prebuilt images for Phraseanet.
https://hub.docker.com/r/alchemyfr/phraseanet-fpm
https://hub.docker.com/r/alchemyfr/phraseanet-worker
https://hub.docker.com/r/alchemyfr/phraseanet-nginx
https://hub.docker.com/repository/docker/alchemyfr/phraseanet-db
https://hub.docker.com/repository/docker/alchemyfr/phraseanet-elasticsearch
#### Tag organization on Docker Hub
```latest``` : latest stable version ```latest``` : latest stable version
@@ -132,14 +222,21 @@ Pull images before launch docker-compose
```4.1.1``` : Phraseanet version 4.1.1 ```4.1.1``` : Phraseanet version 4.1.1
Etc.. Etc
```nightly``` : Development version, the latest version with successful automated tests. Built and published every night ```nightly``` : Development version, the latest version with successful automated tests. Built and published every night
## Development mode ## Development mode
The development mode uses the `docker-compose-override.yml` file.
The development mode uses the `docker-compose-override.yml` file, so you need to set ```COMPOSE_FILE``` env
```COMPOSE_FILE=docker-compose.yml:docker-compose.db.yml:docker-compose.mailhog.yml:docker-compose.override.yml```
You can run it with: You can run it with:
@@ -151,9 +248,10 @@ This can be made easily from the builder container:
docker-compose run --rm -u app builder make install install_composer_dev docker-compose run --rm -u app builder make install install_composer_dev
> Please note that the phraseanet image does not contain nor `composer` neither `node` tools. This allow the final image to be slim. > Please note that the phraseanet image does not contain nor `composer` neither `node` tools. This allows the final image to be light.
> If you need to use dev tools, ensure you are running the `builder` image! > If you need to use dev tools, ensure you are running the `builder` image!
### Developer shell ### Developer shell
You can also obtain a shell access in builder container: You can also obtain a shell access in builder container:
@@ -220,12 +318,25 @@ export PHRASEANET_SSH_PRIVATE_KEY=$(cat ~/.ssh/id_rsa)
export PHRASEANET_SSH_PRIVATE_KEY=$(openssl rsa -in ~/.ssh/id_rsa -out /tmp/id_rsa_raw && cat /tmp/id_rsa_raw && rm /tmp/id_rsa_raw) export PHRASEANET_SSH_PRIVATE_KEY=$(openssl rsa -in ~/.ssh/id_rsa -out /tmp/id_rsa_raw && cat /tmp/id_rsa_raw && rm /tmp/id_rsa_raw)
``` ```
#### Running workers
```bash
docker-compose -f docker-compose.yml run --rm worker <command>
```
Where `<command>` can be:
- `bin/console worker:execute -m 2` (default)
- `bin/console task-manager:scheduler:run`
- ...
# Try Phraseanet with Pre installed VM (deprecated) # Try Phraseanet with Pre installed VM (deprecated)
You can also download a testing pre installed Virtual Machine in OVA format here : You can also download a testing pre installed Virtual Machine in OVA format here :
https://www.phraseanet.com/download/ https://www.phraseanet.com/download/
# With Vagrant (deprecated) # With Vagrant (deprecated)
## Development : ## Development :

14
docker-compose.db.yml Normal file
View File

@@ -0,0 +1,14 @@
version: "3.4"
services:
db:
image: $PHRASEANET_DOCKER_REGISTRY/phraseanet-db:$PHRASEANET_DOCKER_TAG
build: ./docker/db
restart: on-failure
environment:
- MYSQL_ROOT_PASSWORD
- MYSQL_MAX_ALLOWED_PACKET
volumes:
- ${PHRASEANET_DB_DIR}:/var/lib/mysql
networks:
- internal

View File

@@ -0,0 +1,10 @@
version: "3.4"
services:
mailhog:
image: mailhog/mailhog
ports:
- 1025:1025
- 8025:8025
networks:
- internal

View File

@@ -7,13 +7,16 @@ services:
- ${PHRASEANET_PHPMYADMIN_PORT}:80 - ${PHRASEANET_PHPMYADMIN_PORT}:80
depends_on: depends_on:
- db - db
networks:
- internal
gateway: gateway:
volumes: volumes:
- ../:/var/alchemy - ../:/var/alchemy
- .:/var/alchemy/Phraseanet - .:/var/alchemy/Phraseanet
- ./docker/nginx/root/entrypoint.sh:/entrypoint.sh - ./docker/nginx/root/entrypoint.sh:/entrypoint.sh
networks:
- internal
builder: builder:
build: build:
context: . context: .
@@ -37,6 +40,8 @@ services:
- dev_vol:/home/app - dev_vol:/home/app
environment: environment:
- PHRASEANET_PROJECT_NAME - PHRASEANET_PROJECT_NAME
networks:
- internal
phraseanet: phraseanet:
environment: environment:
@@ -46,27 +51,34 @@ services:
volumes: volumes:
- ../:/var/alchemy - ../:/var/alchemy
- .:/var/alchemy/Phraseanet - .:/var/alchemy/Phraseanet
networks:
- internal
worker: worker:
volumes: volumes:
- ../:/var/alchemy - ../:/var/alchemy
- .:/var/alchemy/Phraseanet - .:/var/alchemy/Phraseanet
networks:
- internal
rabbitmq: rabbitmq:
ports: ports:
- ${RABBITMQ_MANAGEMENT_PORT}:15672 - ${RABBITMQ_MANAGEMENT_PORT}:15672
networks:
- internal
db: db:
volumes: volumes:
- ${PHRASEANET_DB_DIR}:/var/lib/mysql:rw - ${PHRASEANET_DB_DIR}:/var/lib/mysql:rw
networks:
- internal
elasticsearch: elasticsearch:
ports: ports:
- 9200:9200 - 9200:9200
volumes: volumes:
- ${PHRASEANET_ELASTICSEARCH_DIR}:/usr/share/elasticsearch/data:rw - ${PHRASEANET_ELASTICSEARCH_DIR}:/usr/share/elasticsearch/data:rw
networks:
- internal
kibana: kibana:
image: kibana:4.6.6 image: kibana:4.6.6
ports: ports:
@@ -75,6 +87,8 @@ services:
- elasticsearch - elasticsearch
depends_on: depends_on:
- elasticsearch - elasticsearch
networks:
- internal
logstash: logstash:
image: logstash:7.6.2 image: logstash:7.6.2
@@ -86,6 +100,8 @@ services:
depends_on: depends_on:
- elasticsearch - elasticsearch
restart: on-failure restart: on-failure
networks:
- internal
filebeat: filebeat:
hostname: filebeat hostname: filebeat
@@ -95,6 +111,8 @@ services:
- ${PHRASEANET_LOGS_DIR}:/var/alchemy/Phraseanet/logs:ro - ${PHRASEANET_LOGS_DIR}:/var/alchemy/Phraseanet/logs:ro
command: filebeat run -e --strict.perms=false command: filebeat run -e --strict.perms=false
restart: on-failure restart: on-failure
networks:
- internal
ftpd-server: ftpd-server:
image: stilliard/pure-ftpd:hardened image: stilliard/pure-ftpd:hardened
@@ -105,9 +123,5 @@ services:
volumes: volumes:
- ${PHRASEANET_FTP_DIR}:/home/dev - ${PHRASEANET_FTP_DIR}:/home/dev
restart: on-failure restart: on-failure
networks:
networks: - internal
default:
ipam:
config:
- subnet: $PHRASEANET_SUBNET_IPS

View File

@@ -1,5 +1,11 @@
version: "3.4" version: "3.4"
networks:
internal:
ipam:
config:
- subnet: $PHRASEANET_SUBNET_IPS
services: services:
gateway: gateway:
build: build:
@@ -24,6 +30,8 @@ services:
- GATEWAY_FASTCGI_TIMEOUT - GATEWAY_FASTCGI_TIMEOUT
ports: ports:
- ${PHRASEANET_APP_PORT}:80 - ${PHRASEANET_APP_PORT}:80
networks:
- internal
phraseanet: phraseanet:
build: build:
@@ -35,11 +43,13 @@ services:
image: $PHRASEANET_DOCKER_REGISTRY/phraseanet-fpm:$PHRASEANET_DOCKER_TAG image: $PHRASEANET_DOCKER_REGISTRY/phraseanet-fpm:$PHRASEANET_DOCKER_TAG
restart: on-failure restart: on-failure
depends_on: depends_on:
- db
- redis - redis
- rabbitmq - rabbitmq
- elasticsearch - elasticsearch
environment: environment:
- PHRASEANET_INSTALL
- PHRASEANET_SETUP
- PHRASEANET_UPGRADE
- PHRASEANET_PROJECT_NAME - PHRASEANET_PROJECT_NAME
- PHRASEANET_TRUSTED_PROXIES - PHRASEANET_TRUSTED_PROXIES
- MAX_BODY_SIZE - MAX_BODY_SIZE
@@ -61,6 +71,8 @@ services:
- INSTALL_APPBOX - INSTALL_APPBOX
- INSTALL_DATABOX - INSTALL_DATABOX
- PHRASEANET_SERVER_NAME - PHRASEANET_SERVER_NAME
- PHRASEANET_AVAILABLE_LANGUAGE
- PHRASEANET_DEFAULT_LANGUAGE
- PHRASEANET_RABBITMQ_USER=$RABBITMQ_DEFAULT_USER - PHRASEANET_RABBITMQ_USER=$RABBITMQ_DEFAULT_USER
- PHRASEANET_RABBITMQ_PASSWORD=$RABBITMQ_DEFAULT_PASS - PHRASEANET_RABBITMQ_PASSWORD=$RABBITMQ_DEFAULT_PASS
- PHRASEANET_EMITTER_EMAIL - PHRASEANET_EMITTER_EMAIL
@@ -86,6 +98,7 @@ services:
- PHRASEANET_API_ENABLED - PHRASEANET_API_ENABLED
- PHRASEANET_API_SSL - PHRASEANET_API_SSL
- PHRASEANET_API_AUTH_TOKEN_HEADER_ONLY - PHRASEANET_API_AUTH_TOKEN_HEADER_ONLY
- ENV_SET_PHRASEANET_PROJET_NAME
- LC_MESSAGES=C.UTF-8 - LC_MESSAGES=C.UTF-8
- LC_COLLATE=C.UTF-8 - LC_COLLATE=C.UTF-8
- LC_IDENTIFICATION=C.UTF-8 - LC_IDENTIFICATION=C.UTF-8
@@ -94,7 +107,6 @@ services:
- LC_CTYPE=C.UTF-8 - LC_CTYPE=C.UTF-8
- LC_TIME=C.UTF-8 - LC_TIME=C.UTF-8
- LC_NAME=C.UTF-8 - LC_NAME=C.UTF-8
volumes: volumes:
- ${PHRASEANET_CONFIG_DIR}:/var/alchemy/Phraseanet/config:rw - ${PHRASEANET_CONFIG_DIR}:/var/alchemy/Phraseanet/config:rw
- ${PHRASEANET_LOGS_DIR}:/var/alchemy/Phraseanet/logs:rw - ${PHRASEANET_LOGS_DIR}:/var/alchemy/Phraseanet/logs:rw
@@ -104,6 +116,8 @@ services:
- ${PHRASEANET_PLUGINS_DIR}:/var/alchemy/Phraseanet/www/plugins:rw - ${PHRASEANET_PLUGINS_DIR}:/var/alchemy/Phraseanet/www/plugins:rw
- ${PHRASEANET_CACHE_DIR}:/var/alchemy/Phraseanet/cache:rw - ${PHRASEANET_CACHE_DIR}:/var/alchemy/Phraseanet/cache:rw
- ${PHRASEANET_TMP_DIR}:/var/alchemy/Phraseanet/tmp:rw - ${PHRASEANET_TMP_DIR}:/var/alchemy/Phraseanet/tmp:rw
networks:
- internal
worker: worker:
build: build:
@@ -115,16 +129,11 @@ services:
image: $PHRASEANET_DOCKER_REGISTRY/phraseanet-worker:$PHRASEANET_DOCKER_TAG image: $PHRASEANET_DOCKER_REGISTRY/phraseanet-worker:$PHRASEANET_DOCKER_TAG
restart: on-failure restart: on-failure
depends_on: depends_on:
- db
- redis - redis
- rabbitmq - rabbitmq
- elasticsearch - elasticsearch
- phraseanet - phraseanet
environment: environment:
- PHRASEANET_PROJECT_NAME
- PHRASEANET_TRUSTED_PROXIES
- MAX_BODY_SIZE
- MAX_INPUT_VARS
- OPCACHE_ENABLED - OPCACHE_ENABLED
- SESSION_CACHE_LIMITER - SESSION_CACHE_LIMITER
- PHP_LOG_LEVEL - PHP_LOG_LEVEL
@@ -168,16 +177,8 @@ services:
- ${PHRASEANET_CUSTOM_DIR}:/var/alchemy/Phraseanet/www/custom:rw - ${PHRASEANET_CUSTOM_DIR}:/var/alchemy/Phraseanet/www/custom:rw
- ${PHRASEANET_CACHE_DIR}:/var/alchemy/Phraseanet/cache:rw - ${PHRASEANET_CACHE_DIR}:/var/alchemy/Phraseanet/cache:rw
- ${PHRASEANET_TMP_DIR}:/var/alchemy/Phraseanet/tmp:rw - ${PHRASEANET_TMP_DIR}:/var/alchemy/Phraseanet/tmp:rw
networks:
db: - internal
image: $PHRASEANET_DOCKER_REGISTRY/phraseanet-db:$PHRASEANET_DOCKER_TAG
build: ./docker/db
restart: on-failure
environment:
- MYSQL_ROOT_PASSWORD
- MYSQL_MAX_ALLOWED_PACKET
volumes:
- ${PHRASEANET_DB_DIR}:/var/lib/mysql
rabbitmq: rabbitmq:
image: rabbitmq:3-management image: rabbitmq:3-management
@@ -185,10 +186,14 @@ services:
environment: environment:
- RABBITMQ_DEFAULT_USER - RABBITMQ_DEFAULT_USER
- RABBITMQ_DEFAULT_PASS - RABBITMQ_DEFAULT_PASS
networks:
- internal
redis: redis:
image: redis image: redis
restart: on-failure restart: on-failure
networks:
- internal
elasticsearch: elasticsearch:
image: $PHRASEANET_DOCKER_REGISTRY/phraseanet-elasticsearch:$PHRASEANET_DOCKER_TAG image: $PHRASEANET_DOCKER_REGISTRY/phraseanet-elasticsearch:$PHRASEANET_DOCKER_TAG
@@ -196,12 +201,8 @@ services:
restart: on-failure restart: on-failure
volumes: volumes:
- ${PHRASEANET_ELASTICSEARCH_DIR}:/usr/share/elasticsearch/data - ${PHRASEANET_ELASTICSEARCH_DIR}:/usr/share/elasticsearch/data
networks:
mailhog: - internal
image: mailhog/mailhog
ports:
- 1025:1025
- 8025:8025
volumes: volumes:
config_vol: config_vol:

View File

@@ -39,7 +39,6 @@ bin/setup system:config set workers.queue.worker-queue.vhost /
/var/alchemy/Phraseanet/bin/setup system:config set main.search-engine.options.host elasticsearch /var/alchemy/Phraseanet/bin/setup system:config set main.search-engine.options.host elasticsearch
/var/alchemy/Phraseanet/bin/setup system:config set main.search-engine.options.minScore 2 /var/alchemy/Phraseanet/bin/setup system:config set main.search-engine.options.minScore 2
/var/alchemy/Phraseanet/bin/setup system:config set main.search-engine.options.minScore 2
/var/alchemy/Phraseanet/bin/setup system:config set main.search-engine.options.facets._base.limit 10 /var/alchemy/Phraseanet/bin/setup system:config set main.search-engine.options.facets._base.limit 10
/var/alchemy/Phraseanet/bin/setup system:config set main.search-engine.options.facets._collection.limit 10 /var/alchemy/Phraseanet/bin/setup system:config set main.search-engine.options.facets._collection.limit 10
/var/alchemy/Phraseanet/bin/setup system:config set main.search-engine.options.facets._doctype.limit 10 /var/alchemy/Phraseanet/bin/setup system:config set main.search-engine.options.facets._doctype.limit 10
@@ -50,21 +49,6 @@ bin/setup system:config set workers.queue.worker-queue.vhost /
/var/alchemy/Phraseanet/bin/setup system:config set main.cache.options.namespace $PHRASEANET_SERVER_NAME /var/alchemy/Phraseanet/bin/setup system:config set main.cache.options.namespace $PHRASEANET_SERVER_NAME
/var/alchemy/Phraseanet/bin/setup system:config set main.cache.type redis /var/alchemy/Phraseanet/bin/setup system:config set main.cache.type redis
## enable API and disable ssl on it
/var/alchemy/Phraseanet/bin/setup system:config set registry.api-clients.api-enabled $PHRASEANET_API_ENABLED
/var/alchemy/Phraseanet/bin/setup system:config set registry.api-clients.api-require-ssl $PHRASEANET_API_SSL
/var/alchemy/Phraseanet/bin/setup system:config set registry.api-clients.api-auth-token-header-only $PHRASEANET_API_AUTH_TOKEN_HEADER_ONLY
## Trusted proxie setting
if [[ -n $PHRASEANET_TRUSTED_PROXIES ]]; then
bin/setup system:config add trusted-proxies $PHRASEANET_TRUSTED_PROXIES
fi
## set instance title
bin/setup system:config set registry.general.title $PHRASEANET_PROJECT_NAME
/var/alchemy/Phraseanet/bin/console compile:configuration /var/alchemy/Phraseanet/bin/console compile:configuration

View File

@@ -10,18 +10,67 @@ cat docker/phraseanet/root/usr/local/etc/php-fpm.d/zz-docker.conf | sed "s/\$RE
FILE=config/configuration.yml FILE=config/configuration.yml
if [ -f "$FILE" ]; then if [[ ! -f "$FILE" && $PHRASEANET_INSTALL = 1 ]];then
echo "$FILE exists, skip setup." echo `date +"%Y-%m-%d %H:%M:%S"` " - Phraseanet $FILE doesn't exist, Play Phraseanet installation..."
if [[ $PHRASEANET_PROJECT_NAME ]]; then chown app:app \
cache \
config \
tmp \
logs \
www \
datas
runuser app -c docker/phraseanet/auto-install.sh
chmod 600 config/configuration.yml
echo `date +"%Y-%m-%d %H:%M:%S"` " - End of Phraseanet Installation"
fi
if [[ -f "$FILE" && $PHRASEANET_UPGRADE = 1 ]];then
echo `date +"%Y-%m-%d %H:%M:%S"` " - Start Phraseanet upgrade datas"
# TODO check before if an upgrade is require
bin/setup system:upgrade -y
echo `date +"%Y-%m-%d %H:%M:%S"` " - End Phraseanet upgrade datas"
fi
if [[ -f "$FILE" && $PHRASEANET_SETUP = 1 ]]; then
echo `date +"%Y-%m-%d %H:%M:%S"` " - $FILE exists, start setup ."
if [[ $PHRASEANET_PROJECT_NAME && $ENV_SET_PHRASEANET_PROJET_NAME == 1 ]]; then
bin/setup system:config set registry.general.title $PHRASEANET_PROJECT_NAME bin/setup system:config set registry.general.title $PHRASEANET_PROJECT_NAME
fi fi
if [[ -n $PHRASEANET_TRUSTED_PROXIES ]]; then
for i in $(echo $PHRASEANET_TRUSTED_PROXIES | sed "s/,/ /g") echo `date +"%Y-%m-%d %H:%M:%S"` " - Phraseanet Setting available language in GUI and search"
do counter=0
bin/setup system:config add trusted-proxies $i if [[ -n $PHRASEANET_AVAILABLE_LANGUAGE ]]; then
done for i in $(echo $PHRASEANET_AVAILABLE_LANGUAGE | sed "s/,/ /g")
do
counter=$(( counter+1 ))
if [[ $counter -eq 1 ]] ; then
bin/setup system:config set languages.available $i
else
bin/setup system:config add languages.available $i
fi
done
fi fi
bin/setup system:config set languages.default $PHRASEANET_DEFAULT_LANGUAGE
echo `date +"%Y-%m-%d %H:%M:%S"` " - Phraseanet setting for Trusted Proxies"
counter=0
if [[ -n $PHRASEANET_TRUSTED_PROXIES ]]; then
for i in $(echo $PHRASEANET_TRUSTED_PROXIES | sed "s/,/ /g")
do
counter=$(( counter+1 ))
if [[ $counter -eq 1 ]] ; then
bin/setup system:config set trusted-proxies $i
else
bin/setup system:config add trusted-proxies $i
fi
done
fi
echo `date +"%Y-%m-%d %H:%M:%S"` " - Phraseanet setting external Binaries timeout "
bin/setup system:config set main.binaries.ffmpeg_timeout $PHRASEANET_FFMPEG_TIMEOUT bin/setup system:config set main.binaries.ffmpeg_timeout $PHRASEANET_FFMPEG_TIMEOUT
bin/setup system:config set main.binaries.ffprobe_timeout $PHRASEANET_FFPROBE_TIMEOUT bin/setup system:config set main.binaries.ffprobe_timeout $PHRASEANET_FFPROBE_TIMEOUT
bin/setup system:config set main.binaries.gs_timeout $PHRASEANET_GS_TIMEOUT bin/setup system:config set main.binaries.gs_timeout $PHRASEANET_GS_TIMEOUT
@@ -30,11 +79,12 @@ if [ -f "$FILE" ]; then
bin/setup system:config set main.binaries.unoconv_timeout $PHRASEANET_UNOCON_TIMEOUT bin/setup system:config set main.binaries.unoconv_timeout $PHRASEANET_UNOCON_TIMEOUT
bin/setup system:config set main.binaries.exiftool_timeout $PHRASEANET_EXIFTOOL_TIMEOUT bin/setup system:config set main.binaries.exiftool_timeout $PHRASEANET_EXIFTOOL_TIMEOUT
echo `date +"%Y-%m-%d %H:%M:%S"` " - Phraseanet setting API "
bin/setup system:config set registry.api-clients.api-enabled $PHRASEANET_API_ENABLED bin/setup system:config set registry.api-clients.api-enabled $PHRASEANET_API_ENABLED
bin/setup system:config set registry.api-clients.api-require-ssl $PHRASEANET_API_SSL bin/setup system:config set registry.api-clients.api-require-ssl $PHRASEANET_API_SSL
bin/setup system:config set registry.api-clients.api-auth-token-header-only $PHRASEANET_API_AUTH_TOKEN_HEADER_ONLY bin/setup system:config set registry.api-clients.api-auth-token-header-only $PHRASEANET_API_AUTH_TOKEN_HEADER_ONLY
echo `date +"%Y-%m-%d %H:%M:%S"` " - Phraseanet setting SMTP "
if [[ $PHRASEANET_SMTP_ENABLED && $PHRASEANET_SMTP_ENABLED = true ]]; then if [[ $PHRASEANET_SMTP_ENABLED && $PHRASEANET_SMTP_ENABLED = true ]]; then
bin/setup system:config set registry.email.smtp-enabled $PHRASEANET_SMTP_ENABLED bin/setup system:config set registry.email.smtp-enabled $PHRASEANET_SMTP_ENABLED
bin/setup system:config set registry.email.smtp-auth-enabled $PHRASEANET_SMTP_AUTH_ENABLED bin/setup system:config set registry.email.smtp-auth-enabled $PHRASEANET_SMTP_AUTH_ENABLED
@@ -46,23 +96,17 @@ if [ -f "$FILE" ]; then
bin/setup system:config set registry.email.emitter-email $PHRASEANET_EMITTER_EMAIL bin/setup system:config set registry.email.emitter-email $PHRASEANET_EMITTER_EMAIL
bin/setup system:config set registry.email.prefix $PHRASEANET_MAIL_OBJECT_PREFIX bin/setup system:config set registry.email.prefix $PHRASEANET_MAIL_OBJECT_PREFIX
fi fi
# TODO define mapbox setting
# echo `date +"%Y-%m-%d %H:%M:%S"` " - Phraseanet setting Mapbox "
echo `date +"%Y-%m-%d %H:%M:%S"` " - Phraseanet root account Password sync"
if [[ -n ${PHRASEANET_ADMIN_ACCOUNT_ID} && $PHRASEANET_ADMIN_ACCOUNT_ID =~ ^[0-9]+$ ]]; then if [[ -n ${PHRASEANET_ADMIN_ACCOUNT_ID} && $PHRASEANET_ADMIN_ACCOUNT_ID =~ ^[0-9]+$ ]]; then
bin/console user:password --user_id=$PHRASEANET_ADMIN_ACCOUNT_ID --password $PHRASEANET_ADMIN_ACCOUNT_PASSWORD -y bin/console user:password --user_id=$PHRASEANET_ADMIN_ACCOUNT_ID --password $PHRASEANET_ADMIN_ACCOUNT_PASSWORD -y
fi fi
echo `date +"%Y-%m-%d %H:%M:%S"` " - config/configuration.yml update by Phraseanet entrypoint.sh Finished !" echo `date +"%Y-%m-%d %H:%M:%S"` " - config/configuration.yml update by Phraseanet entrypoint.sh Finished !"
else
echo "$FILE doesn't exist, entering setup..."
chown app:app \
cache \
config \
tmp \
logs \
www \
datas
runuser app -c docker/phraseanet/auto-install.sh
echo `date +"%Y-%m-%d %H:%M:%S"` " - End of Phraseanet Installation"
fi fi
if [ ${XDEBUG_ENABLED} == "1" ]; then if [ ${XDEBUG_ENABLED} == "1" ]; then
@@ -72,7 +116,7 @@ fi
./docker/phraseanet/plugins/console init ./docker/phraseanet/plugins/console init
rm -Rf cache/* rm -Rf cache/*
chmod 600 config/configuration.yml
chown -R app:app \ chown -R app:app \
cache \ cache \
@@ -86,7 +130,7 @@ if [ -d "plugins/" ];then
chown -R app:app plugins; chown -R app:app plugins;
fi fi
chown -R app:app datas && echo `date +"%Y-%m-%d %H:%M:%S"` " - Finished chown on datas by entreypoint" & chown -R app:app datas && echo `date +"%Y-%m-%d %H:%M:%S"` " - Finished chown on datas by entrypoint" &
echo `date +"%Y-%m-%d %H:%M:%S"` " - Finished runnning Phraseanet entrypoint.sh" echo `date +"%Y-%m-%d %H:%M:%S"` " - End of Phraseanet entrypoint.sh"
bash -e docker-php-entrypoint $@ bash -e docker-php-entrypoint $@

View File

@@ -31,15 +31,15 @@ class CollectionRequestMapper
$demands = array(); $demands = array();
foreach ($databoxStatuses as $databoxId => $data) { foreach ($databoxStatuses as $databoxId => $data) {
foreach ($data['registrations']['by-type']['pending'] as $collectionId => $waiting) { foreach (['registrations-by-type']['pending'] as $collectionId => $waiting) {
$demands[] = $this->mapCollectionStatus($databoxId, $collectionId, "pending"); $demands[] = $this->mapCollectionStatus($databoxId, $collectionId, "pending");
} }
foreach ($data['registrations']['by-type']['rejected'] as $collectionId => $waiting) { foreach ($data['registrations-by-type']['rejected'] as $collectionId => $waiting) {
$demands[] = $this->mapCollectionStatus($databoxId, $collectionId, "rejected"); $demands[] = $this->mapCollectionStatus($databoxId, $collectionId, "rejected");
} }
foreach ($data['registrations']['by-type']['accepted'] as $collectionId => $waiting) { foreach ($data['registrations-by-type']['accepted'] as $collectionId => $waiting) {
$demands[] = $this->mapCollectionStatus($databoxId, $collectionId, "accepted"); $demands[] = $this->mapCollectionStatus($databoxId, $collectionId, "accepted");
} }
} }

View File

@@ -289,7 +289,7 @@ class RegistrationService
* @param array $selectedCollections * @param array $selectedCollections
* @return \collection[] * @return \collection[]
*/ */
private function getAuthorizedCollections(array $selectedCollections = null) private function getAuthorizedCollections($selectedCollections)
{ {
$inscriptions = $this->registrationManager->getRegistrationSummary(); $inscriptions = $this->registrationManager->getRegistrationSummary();
$authorizedCollections = []; $authorizedCollections = [];
@@ -300,7 +300,7 @@ class RegistrationService
continue; continue;
} }
if (\igorw\get_in($inscriptions, [$databox->get_sbas_id(), 'config', 'collections', $collection->get_base_id(), 'can-register'])) { if (\igorw\get_in($inscriptions, [$databox->get_sbas_id(), 'collections', $collection->get_base_id(), 'can-register'])) {
$authorizedCollections[$collection->get_base_id()] = $collection; $authorizedCollections[$collection->get_base_id()] = $collection;
} }
} }

View File

@@ -63,142 +63,108 @@ class RegistrationManager
} }
foreach ($this->appbox->get_databoxes() as $databox) { foreach ($this->appbox->get_databoxes() as $databox) {
$data[$databox->get_sbas_id()] = [ $sbas_id = $databox->get_sbas_id();
$data[$sbas_id] = [
// Registrations on databox by type // Registrations on databox by type
'registrations' => [ 'registrations-by-type' => [
'by-type' => [ 'active' => [],
'inactive' => [], 'inactive' => [],
'accepted' => [], 'accepted' => [],
'in-time' => [], 'in-time' => [],
'out-dated' => [], 'out-dated' => [],
'pending' => [], 'pending' => [],
'rejected' => [], 'rejected' => [],
]
], ],
// Registration configuration on databox and collections that belong to the databox // Registration configuration on databox and collections that belong to the databox
'config' => [ 'db-name' => $databox->get_dbname(),
'db-name' => $databox->get_dbname(), 'cgu' => $databox->get_cgus(),
'cgu' => $databox->get_cgus(), 'can-register' => $databox->isRegistrationEnabled(),
'can-register' => $databox->isRegistrationEnabled(), // Configuration on collection
// Configuration on collection 'collections' => [],
'collections' => [], 'display' => false, // set to true if there is at least one collection to display
]
]; ];
foreach ($databox->get_collections() as $collection) { foreach ($databox->get_collections() as $collection) {
$base_id = $collection->get_base_id();
$userRegistration = igorw\get_in($userData, [$sbas_id, $base_id]);
// Sets collection info // Sets collection info
$data[$databox->get_sbas_id()]['config']['collections'][$collection->get_base_id()] = $this->getCollectionSummary($collection, $userData); $data[$sbas_id]['collections'][$base_id] = [
'coll-name' => $collection->get_label($this->locale),
// gets collection registration or fallback to databox configuration
'can-register' => $collection->isRegistrationEnabled(),
// boolean to tell whether user has already requested an access to the collection
'registration' => !is_null($userRegistration) && !is_null($userRegistration['active']),
'type' => null
];
// Sets registration by type // Sets registration by type
if (null !== $registration = $this->getUserCollectionRegistration($collection, $userData)) { if (!is_null($userRegistration)) { // && !is_null($userRegistration['active'])) {
$data[$databox->get_sbas_id()]['registrations']['by-type'][$registration['type']][] = $registration;
$userRegistration['coll-name'] = $collection->get_label($this->locale);
$userRegistration['can-register'] = $collection->isRegistrationEnabled();
// sets default type
$type = 'inactive';
// gets registration entity
$registration = $userRegistration['registration'];
if(!is_null($userRegistration['active'])) {
// rights are set in basusr, we don't really care about registration
$isTimeLimited = (Boolean) $userRegistration['time-limited'];
if($isTimeLimited) {
// any time limit overrides (=automates) the 'active' value
$isOnTime = (Boolean) $userRegistration['in-time'];
$type = $isOnTime ? 'in-time' : 'out-dated';
}
else {
// no time limit, use the 'active' value - but be nice if this is the result of registration
$isPending = !is_null($registration) && $registration->isPending();
$isRejected = !is_null($registration) && !$isPending && $registration->isRejected();
$isAccepted = !is_null($registration) && !$isPending && !$isRejected;
if ($userRegistration['active'] === false) {
// no access
$type = $isRejected ? 'rejected' : 'inactive';
}
else {
// access
$type = $isAccepted ? 'accepted' : 'active';
}
}
}
else {
// nothing in basusr, use only registration
if(is_null($registration)) {
// no registration
$type = 'inactive';
}
else {
// something in registration
$isPending = $registration->isPending();
$isRejected = !$isPending && $registration->isRejected();
if($isPending) {
$type = 'pending';
}
else {
$type = $isRejected ? 'rejected' : 'accepted';
}
}
}
// the twig template will not display an inactive collection, unless it is registrable
if($type !== 'inactive' || $collection->isRegistrationEnabled()) {
// at least one collection is displayed so the dbox must be displayed
$data[$sbas_id]['display'] = true;
}
$userRegistration['type'] = $type;
$data[$sbas_id]['collections'][$base_id]['type'] = $type;
$data[$sbas_id]['registrations-by-type'][$type][] = $userRegistration;
} }
} }
} }
return $data; return $data;
} }
/**
* Tells whether user has ever requested a registration on collection or not.
*
* @param \collection $collection
* @param $userData
*
* @return boolean
*/
private function userHasRequestedARegistrationOnCollection(\collection $collection, $userData)
{
if (null === $userRegistration = igorw\get_in($userData, [$collection->get_sbas_id(), $collection->get_base_id()])) {
return false;
}
return !is_null($userRegistration['active']);
}
/**
* Returns a user registration for given collection or null if no registration were requested.
*
* @param \collection $collection
* @param $userData
*
* @return null|array
*/
private function getUserCollectionRegistration(\collection $collection, $userData)
{
if (false === $this->userHasRequestedARegistrationOnCollection($collection, $userData)) {
return null;
}
$userRegistration = igorw\get_in($userData, [$collection->get_sbas_id(), $collection->get_base_id()]);
// sets collection name
$userRegistration['coll-name'] = $collection->get_label($this->locale);
// sets default type
$userRegistration['type'] = 'active';
// gets registration entity
$registration = $userRegistration['registration'];
// set registration type & return user registration
$registrationStillExists = !is_null($registration);
$registrationNoMoreExists = !$registrationStillExists;
$isPending = $registrationStillExists && $registration->isPending() && !$registration->isRejected();
$isRejected = $registrationStillExists && $registration->isRejected();
$isDone = ($registrationNoMoreExists) || (!$isPending && !$isRejected);
$isActive = (Boolean) $userRegistration['active'];
$isTimeLimited = (Boolean) $userRegistration['time-limited'];
$isNotTimeLimited = !$isTimeLimited;
$isOnTime = (Boolean) $userRegistration['in-time'];
$isOutDated = !$isOnTime;
if (!$isActive) {
$userRegistration['type'] = 'inactive';
return $userRegistration;
}
if ($isDone) {
$userRegistration['type'] = 'accepted';
return $userRegistration;
}
if ($isRejected) {
$userRegistration['type'] = 'rejected';
return $userRegistration;
}
if ($isTimeLimited && $isOnTime && $isPending) {
$userRegistration['type'] = 'in-time';
return $userRegistration;
}
if ($isTimeLimited && $isOutDated && $isPending) {
$userRegistration['type'] = 'out-dated';
return $userRegistration;
}
if ($isNotTimeLimited && $isPending) {
$userRegistration['type'] = 'pending';
return $userRegistration;
}
return $userRegistration;
}
private function getCollectionSummary(\collection $collection, $userData)
{
return [
'coll-name' => $collection->get_label($this->locale),
// gets collection registration or fallback to databox configuration
'can-register' => $collection->isRegistrationEnabled(),
'cgu' => $collection->getTermsOfUse(),
// boolean to tell whether user has already requested an access to the collection
'registration' => $this->userHasRequestedARegistrationOnCollection($collection, $userData)
];
}
} }

View File

@@ -127,6 +127,7 @@ class RegistryFormManipulator
'enable-push-authentication' => false, 'enable-push-authentication' => false,
'force-push-authentication' => false, 'force-push-authentication' => false,
'enable-feed-notification' => true, 'enable-feed-notification' => true,
'download-link-validity' => 24,
], ],
'ftp' => [ 'ftp' => [
'ftp-enabled' => false, 'ftp-enabled' => false,

View File

@@ -57,7 +57,8 @@ class ManipulatorServiceProvider implements ServiceProviderInterface
$app['orm.em'], $app['orm.em'],
$app['random.medium'], $app['random.medium'],
$app['repo.tokens'], $app['repo.tokens'],
$app['tmp.download.path'] $app['tmp.download.path'],
$app['conf']
); );
}); });

View File

@@ -57,6 +57,9 @@ class ActionsFormType extends AbstractType
$builder->add('enable-feed-notification', 'checkbox', [ $builder->add('enable-feed-notification', 'checkbox', [
'label' => 'Enable possibility to notify users when publishing a new feed entry', 'label' => 'Enable possibility to notify users when publishing a new feed entry',
]); ]);
$builder->add('download-link-validity', 'integer', [
'label' => 'Validity period of the download links',
]);
} }
public function getName() public function getName()

View File

@@ -73,8 +73,8 @@ class PhraseaRegisterForm extends AbstractType
$choices = $baseIds = []; $choices = $baseIds = [];
foreach ($this->app['registration.manager']->getRegistrationSummary() as $baseInfo) { foreach ($this->app['registration.manager']->getRegistrationSummary() as $baseInfo) {
$dbName = $baseInfo['config']['db-name']; $dbName = $baseInfo['db-name'];
foreach ($baseInfo['config']['collections'] as $baseId => $collInfo) { foreach ($baseInfo['collections'] as $baseId => $collInfo) {
if (false === $collInfo['can-register']) { if (false === $collInfo['can-register']) {
continue; continue;
} }

View File

@@ -11,6 +11,7 @@
namespace Alchemy\Phrasea\Model\Manipulator; namespace Alchemy\Phrasea\Model\Manipulator;
use Alchemy\Phrasea\Core\Configuration\PropertyAccess;
use Alchemy\Phrasea\Model\Entities\Basket; use Alchemy\Phrasea\Model\Entities\Basket;
use Alchemy\Phrasea\Model\Entities\FeedEntry; use Alchemy\Phrasea\Model\Entities\FeedEntry;
use Alchemy\Phrasea\Model\Entities\Token; use Alchemy\Phrasea\Model\Entities\Token;
@@ -41,6 +42,7 @@ class TokenManipulator implements ManipulatorInterface
private $om; private $om;
private $random; private $random;
private $repository; private $repository;
private $conf;
private $temporaryDownloadPath; private $temporaryDownloadPath;
@@ -48,12 +50,14 @@ class TokenManipulator implements ManipulatorInterface
ObjectManager $om, ObjectManager $om,
Generator $random, Generator $random,
TokenRepository $repository, TokenRepository $repository,
$temporaryDownloadPath) $temporaryDownloadPath,
PropertyAccess $configuration)
{ {
$this->om = $om; $this->om = $om;
$this->random = $random; $this->random = $random;
$this->repository = $repository; $this->repository = $repository;
$this->temporaryDownloadPath = $temporaryDownloadPath; $this->temporaryDownloadPath = $temporaryDownloadPath;
$this->conf = $configuration;
} }
/** /**
@@ -168,7 +172,9 @@ class TokenManipulator implements ManipulatorInterface
*/ */
public function createDownloadToken(User $user, $data) public function createDownloadToken(User $user, $data)
{ {
return $this->create($user, self::TYPE_DOWNLOAD, new DateTime('+3 hours'), $data); $downloadLinkValidity = (int) $this->conf->get(['registry', 'actions', 'download-link-validity'], 24);
return $this->create($user, self::TYPE_DOWNLOAD, new DateTime("+{$downloadLinkValidity} hours"), $data);
} }
/** /**
@@ -178,7 +184,9 @@ class TokenManipulator implements ManipulatorInterface
*/ */
public function createEmailExportToken($data) public function createEmailExportToken($data)
{ {
return $this->create(null, self::TYPE_EMAIL, new DateTime('+1 day'), $data); $downloadLinkValidity = (int) $this->conf->get(['registry', 'actions', 'download-link-validity'], 24);
return $this->create(null, self::TYPE_EMAIL, new DateTime("+{$downloadLinkValidity} hours"), $data);
} }
/** /**

View File

@@ -84,40 +84,59 @@ class RegistrationRepository extends EntityRepository
$data = []; $data = [];
$rsm = $this->createResultSetMappingBuilder('d'); $rsm = $this->createResultSetMappingBuilder('d');
$rsm->addScalarResult('sbas_id','sbas_id'); $rsm->addScalarResult('sbas_id','sbas_id');
$rsm->addScalarResult('base_id','base_id'); $rsm->addScalarResult('bas_id','bas_id');
$rsm->addScalarResult('dbname','dbname'); $rsm->addScalarResult('dbname','dbname');
$rsm->addScalarResult('time_limited', 'time_limited'); $rsm->addScalarResult('time_limited', 'time_limited');
$rsm->addScalarResult('limited_from', 'limited_from'); $rsm->addScalarResult('limited_from', 'limited_from');
$rsm->addScalarResult('limited_to', 'limited_to'); $rsm->addScalarResult('limited_to', 'limited_to');
$rsm->addScalarResult('actif', 'actif'); $rsm->addScalarResult('actif', 'actif');
$sql = " // nb: UNIX_TIMESTAMP will return null if date is 0000-00-00 00:00:00
SELECT dbname, sbas.sbas_id, time_limited, $sql = "SELECT dbname, sbas.sbas_id, time_limited,\n"
UNIX_TIMESTAMP( limited_from ) AS limited_from, . " UNIX_TIMESTAMP( limited_from ) AS limited_from,\n"
UNIX_TIMESTAMP( limited_to ) AS limited_to, . " UNIX_TIMESTAMP( limited_to ) AS limited_to,\n"
bas.server_coll_id, Users.id, basusr.actif, . " bas.server_coll_id, Users.id, basusr.actif,\n"
bas.base_id, " . $rsm->generateSelectClause(['d' => 'd',]) . " . " bas.base_id AS bas_id, " . $rsm->generateSelectClause(['d' => 'd',]) . "\n"
FROM (Users, bas, sbas) . "FROM (Users, bas, sbas)\n"
LEFT JOIN basusr ON ( Users.id = basusr.usr_id AND bas.base_id = basusr.base_id ) . " LEFT JOIN basusr ON ( Users.id = basusr.usr_id AND bas.base_id = basusr.base_id )\n"
LEFT JOIN Registrations d ON ( d.user_id = Users.id AND bas.base_id = d.base_id ) . " LEFT JOIN Registrations d ON ( d.user_id = Users.id AND bas.base_id = d.base_id )\n"
WHERE basusr.actif = 1 AND bas.sbas_id = sbas.sbas_id . "WHERE bas.active = 1 AND bas.sbas_id = sbas.sbas_id\n"
AND Users.id = ?"; . " AND Users.id = ?\n"
. " AND ISNULL(model_of)";
$query = $this->_em->createNativeQuery($sql, $rsm); $query = $this->_em->createNativeQuery($sql, $rsm);
$query->setParameter(1, $user->getId()); $query->setParameter(1, $user->getId());
foreach ($query->getResult() as $row) { foreach ($query->getResult() as $row) {
$registrationEntity = $row[0]; $registrationEntity = $row[0];
$data[$row['sbas_id']][$row['base_id']] = [ $in_time = null;
'base-id' => $row['base_id'], if(($row['time_limited'] !== null) && ($row['limited_from'] !== null || $row['limited_to'] !== null)) {
$in_time = true;
if($row['limited_from'] !== null && time() < $row['limited_from']) {
$in_time = false;
}
elseif($row['limited_to'] !== null && time() > $row['limited_to']) {
$in_time = false;
}
}
$data[$row['sbas_id']][$row['bas_id']] = [
'base-id' => $row['bas_id'],
'db-name' => $row['dbname'], 'db-name' => $row['dbname'],
'active' => (Boolean) $row['actif'], 'active' => self::nullOrBoolean($row['actif']),
'time-limited' => (Boolean) $row['time_limited'], 'time-limited' => self::nullOrBoolean($row['time_limited']),
'in-time' => $row['time_limited'] && ! ($row['limited_from'] >= time() && $row['limited_to'] <= time()), 'in-time' => $in_time,
'registration' => $registrationEntity 'registration' => $registrationEntity
]; ];
} }
return $data; return $data;
} }
public static function nullOrBoolean($v)
{
if(!is_null($v)) {
$v = (boolean)$v;
}
return $v;
}
} }

View File

@@ -0,0 +1,249 @@
<?php
namespace Alchemy\Phrasea\Out\Module;
use Alchemy\Phrasea\Application;
use Alchemy\Phrasea\Out\Tool\PhraseaPDF;
use \IntlDateFormatter as DateFormatter;
class PDFCgu extends PDF
{
private $databoxId;
private $htmlContent = '';
private $recordIds;
public function __construct(Application $app, $databoxId, array $recordIds)
{
parent::__construct($app);
$this->app = $app;
$this->databoxId = $databoxId;
$this->recordIds = $recordIds;
$this->printCgu();
}
public function save()
{
if (!$this->isContentEmpty()) {
$this->pdf->Close();
$pathName = self::getDataboxCguPath($this->app, $this->databoxId);
$this->pdf->Output($pathName, 'F');
}
}
public static function getDataboxCguPath(Application $app, $databoxId)
{
return \p4string::addEndSlash($app['tmp.download.path']). self::getDataboxCguPdfName($app, $databoxId);
}
public static function getDataboxCguPdfName(Application $app, $databoxId)
{
$databox = $app->findDataboxById($databoxId);
return 'cgu_' . $databox->get_viewname() . '.pdf';
}
public static function isDataboxCguEmpty(Application $app, $databoxId)
{
$databox = $app->findDataboxById($databoxId);
$CGUs = $databox->get_cgus();
foreach ($CGUs as $locale => $tou) {
if (trim($tou['value']) !== '') {
return false;
}
}
return true;
}
private function printCgu()
{
$databox = $this->app->findDataboxById($this->databoxId);
$databox->get_dbname();
$CGUs = $databox->get_cgus();
$printedDate = new \DateTime();
foreach ($CGUs as $locale => $tou) {
if (trim($tou['value']) !== '') {
$this->htmlContent .= '<h2> ' . $this->app->trans('Terms Of Use', [], 'messages', $locale) . '</h2>';
$infoDate = $this->app->trans('CGU::PDF CGU generated on %updated_on% and printed on %printed_on%', [
'%updated_on%' => $this->formatDate(new \DateTime($tou['updated_on']), $locale),
'%printed_on%' => $this->formatDate($printedDate, $locale)
], 'messages', $locale);
$this->htmlContent .= '<p><strong style="color:#737275;">'. $infoDate . '</strong></p>';
$this->htmlContent .= $tou['value'];
}
}
if (!$this->isContentEmpty()) {
$this->pdf->AddPage();
$this->pdf->writeHTML($this->htmlContent);
// add thumbnail in cgu
$this->print_thumbnail_list();
}
}
private function print_thumbnail_list()
{
$this->pdf->AddPage();
$oldMargins = $this->pdf->getMargins();
$lmargin = $oldMargins['left'];
$rmargin = $oldMargins['right'];
$this->pdf->SetLeftMargin($lmargin + 55);
$ndoc = 0;
foreach ($this->recordIds as $recordId) {
/* @var \record_adapter $rec */
$rec = new \record_adapter($this->app, $this->databoxId, $recordId);
$subdef = $rec->get_subdef('thumbnail');
$fimg = $subdef->getRealPath();
$wimg = $himg = 50;
// 1px = 3.77952 mm
$finalWidth = round($subdef->get_width() / 3.779528, 2);
$finalHeight = round($subdef->get_height() / 3.779528, 2);
$aspectH = $finalWidth/$finalHeight;
$aspectW = $finalHeight/$finalWidth;
if ($finalWidth > 0 && $finalHeight > 0) {
if ($finalWidth > $finalHeight && $finalWidth > $wimg) {
$finalWidth = $wimg;
$finalHeight = $wimg * $aspectW;
} else if ($finalHeight > $finalWidth && $finalHeight > $himg) {
$finalHeight = $himg;
$finalWidth = $himg * $aspectH;
} else if ($finalHeight == $finalWidth & $finalWidth > $wimg) {
$finalHeight = $wimg;
$finalWidth = $himg;
}
}
if ($this->pdf->GetY() > $this->pdf->getPageHeight() - (6 + $finalHeight + 20))
$this->pdf->AddPage();
$title = "record : " . $rec->get_title();
$y = $this->pdf->GetY();
$t = \phrasea::bas_labels($rec->getBaseId(), $this->app);
$this->pdf->SetFont(PhraseaPDF::FONT, '', 10);
$this->pdf->SetFillColor(220, 220, 220);
$this->pdf->SetLeftMargin($lmargin);
$this->pdf->SetRightMargin($rmargin);
$this->pdf->SetX($lmargin);
$this->pdf->SetY($y);
$this->pdf->out = false;
$this->pdf->MultiCell(140, 4, $title, "LTR", "L", 1);
$y2 = $this->pdf->GetY();
$h = $y2 - $y;
$this->pdf->out = true;
$this->pdf->SetX($lmargin);
$this->pdf->SetY($y);
$this->pdf->Cell(0, $h, "", "LTR", 1, "R", 1);
$this->pdf->SetX($lmargin);
$this->pdf->SetY($y);
$this->pdf->Cell(0, 4, $t, "", 1, "R");
$this->pdf->SetX($lmargin);
$this->pdf->SetY($y);
$this->pdf->MultiCell(140, 4, $title, "", "L");
$this->pdf->SetX($lmargin);
$this->pdf->SetY($y = $y2);
$this->pdf->SetLeftMargin($lmargin + 55);
$this->pdf->SetY($y + 2);
if ($fimg) {
$y = $this->pdf->GetY();
$this->pdf->Image($fimg, $lmargin, $y, $finalWidth, $finalHeight);
$this->pdf->SetY($y + 3);
}
$nf = 0;
$this->pdf->SetX($lmargin + 55);
$p0 = $this->pdf->PageNo();
$y0 = $this->pdf->GetY();
foreach ($rec->get_caption()->get_fields() as $field) {
/* @var $field \caption_field */
$this->pdf->SetFont(PhraseaPDF::FONT, 'B', 12);
$this->pdf->Write(5, $field->get_name() . " : ");
$this->pdf->SetFont(PhraseaPDF::FONT, '', 12);
$this->pdf->Write(5, $field->get_serialized_values());
$this->pdf->Write(6, "\n");
$nf++;
}
if ($this->pdf->PageNo() == $p0 && ($this->pdf->GetY() - $y0) < $finalHeight)
$this->pdf->SetY($y0 + $finalHeight);
$ndoc++;
}
$this->pdf->SetLeftMargin($lmargin);
}
private function isContentEmpty()
{
return (trim($this->htmlContent) === '') ? true : false;
}
private function formatDate(\DateTime $date, $locale)
{
switch ($locale) {
case 'fr':
$fmt = new DateFormatter(
'fr_FR',
DateFormatter::LONG,
DateFormatter::NONE
);
$date_formated = $fmt->format($date);
break;
case 'en':
$fmt = new DateFormatter(
'en_EN',
DateFormatter::LONG,
DateFormatter::NONE
);
$date_formated = $fmt->format($date);
break;
case 'de':
$fmt = new DateFormatter(
'de_DE',
DateFormatter::LONG,
DateFormatter::NONE
);
$date_formated = $fmt->format($date);
break;
default:
$fmt = new DateFormatter(
'en_EN',
DateFormatter::LONG,
DateFormatter::NONE ,
null,
null,
'yyyy/mm/dd'
);
$date_formated = $fmt->format($date);
break;
}
return $date_formated;
}
}

View File

@@ -59,7 +59,7 @@ class PhraseanetExtension extends \Twig_Extension
{ {
return [ return [
// change this version when you change JS file to force the navigation to reload js file // change this version when you change JS file to force the navigation to reload js file
'jsFileVersion' => 4 'jsFileVersion' => 6
]; ];
} }

View File

@@ -821,22 +821,6 @@ class collection implements ThumbnailedElement, cache_cacheableInterface
return $this->get_databox()->getAutoregisterModel($email); return $this->get_databox()->getAutoregisterModel($email);
} }
/**
* Gets terms of use.
*
* @return null|string
*/
public function getTermsOfUse()
{
if (false === $xml = simplexml_load_string($this->get_prefs())) {
return null;
}
foreach ($xml->xpath('/baseprefs/cgu') as $sbpcgu) {
return $sbpcgu->saveXML();
}
}
public function get_cache_key($option = null) public function get_cache_key($option = null)
{ {
return 'collection_' . $this->collectionVO->getCollectionId() . ($option ? '_' . $option : ''); return 'collection_' . $this->collectionVO->getCollectionId() . ($option ? '_' . $option : '');

View File

@@ -273,15 +273,20 @@ class record_preview extends record_adapter
$this->title .= ' : </span> ' . parent::get_title($options); $this->title .= ' : </span> ' . parent::get_title($options);
break; break;
case "BASK": case "BASK":
$this->title .= $this->name . ' (' . $this->getNumber() . '/' . $this->total . ') - ' . parent::get_title($options); $this->title = '<span style="color:#27bbe2;">';
$this->title .= $this->name . ' (' . $this->getNumber() . '/' . $this->total . ') : </span>' . parent::get_title($options);
break; break;
case "REG": case "REG":
$this->title = '<span style="color:#27bbe2;">';
$this->title .= $this->name; $this->title .= $this->name;
if ($this->getNumber() != 0) { if ($this->getNumber() != 0) {
$this->title .= sprintf( $this->title .= sprintf(
' (%s) - %s',$this->getNumber() . '/' . $this->total, parent::get_title($options) ' (%s) : </span> %s',$this->getNumber() . '/' . $this->total, parent::get_title($options)
); );
} else {
$this->title .= '</span>';
} }
break; break;

View File

@@ -14,6 +14,7 @@ use Alchemy\Phrasea\Model\Entities\User;
use Alchemy\Phrasea\Model\Repositories\BasketRepository; use Alchemy\Phrasea\Model\Repositories\BasketRepository;
use Alchemy\Phrasea\Model\Repositories\StoryWZRepository; use Alchemy\Phrasea\Model\Repositories\StoryWZRepository;
use Alchemy\Phrasea\Model\Serializer\CaptionSerializer; use Alchemy\Phrasea\Model\Serializer\CaptionSerializer;
use Alchemy\Phrasea\Out\Module\PDFCgu;
use Assert\Assertion; use Assert\Assertion;
use Doctrine\DBAL\Connection; use Doctrine\DBAL\Connection;
use Symfony\Component\Filesystem\Filesystem; use Symfony\Component\Filesystem\Filesystem;
@@ -418,6 +419,7 @@ class set_export extends set_abstract
$files[$id] = [ $files[$id] = [
'base_id' => $download_element->getBaseId(), 'base_id' => $download_element->getBaseId(),
'databox_id' => $download_element->getDataboxId(),
'record_id' => $download_element->getRecordId(), 'record_id' => $download_element->getRecordId(),
'original_name' => '', 'original_name' => '',
'export_name' => '', 'export_name' => '',
@@ -691,6 +693,13 @@ class set_export extends set_abstract
$toRemove = []; $toRemove = [];
$archiveFiles = []; $archiveFiles = [];
$databoxIds = [];
$recordIdsPerDatabox = [];
// group recordId per databoxId
foreach ($files as $file) {
$recordIdsPerDatabox[$file['databox_id']][] = $file['record_id'];
}
foreach ($files as $record) { foreach ($files as $record) {
if (isset($record["subdefs"])) { if (isset($record["subdefs"])) {
@@ -709,6 +718,28 @@ class set_export extends set_abstract
} }
$toRemove[] = $path; $toRemove[] = $path;
} }
if (!in_array($record['databox_id'], $databoxIds)) {
// add also the databox cgu in the zip
$databoxIds[] = $record['databox_id'];
// if cgu content empty, do not add pdf in zip
if (!PDFCgu::isDataboxCguEmpty($app, $record['databox_id'])) {
try {
$pdfCgu = new PDFCgu($app, $record['databox_id'], $recordIdsPerDatabox[$record['databox_id']]);
$pdfCgu->save();
$databoxCguPath = PDFCgu::getDataboxCguPath($app, $record['databox_id']);
} catch (\Exception $e) {
$app['logger']->error("Exception occurred when generating cgu pdf : " . $e->getMessage());
continue;
}
$archiveFiles[$app['unicode']->remove_diacritics($obj["folder"].PDFCgu::getDataboxCguPdfName($app, $record['databox_id']))] = $databoxCguPath;
$toRemove[] = $databoxCguPath;
}
}
} }
} }
} }

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:jms="urn:jms:translation" version="1.2"> <xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:jms="urn:jms:translation" version="1.2">
<file date="2021-02-09T15:07:53Z" source-language="en" target-language="de" datatype="plaintext" original="not.available"> <file date="2021-02-19T06:46:05Z" source-language="en" target-language="de" datatype="plaintext" original="not.available">
<header> <header>
<tool tool-id="JMSTranslationBundle" tool-name="JMSTranslationBundle" tool-version="1.1.0-DEV"/> <tool tool-id="JMSTranslationBundle" tool-name="JMSTranslationBundle" tool-version="1.1.0-DEV"/>
<note>The source node in most cases contains the sample message as written by the developer. If it looks like a dot-delimitted string such as "form.label.firstname", then the developer has not provided a default message.</note> <note>The source node in most cases contains the sample message as written by the developer. If it looks like a dot-delimitted string such as "form.label.firstname", then the developer has not provided a default message.</note>
@@ -9,9 +9,9 @@
<trans-unit id="96f0767cb7ea65a7f86c8c9432e80d16cf9d8680" resname="Please provide the same passwords." approved="yes"> <trans-unit id="96f0767cb7ea65a7f86c8c9432e80d16cf9d8680" resname="Please provide the same passwords." approved="yes">
<source>Please provide the same passwords.</source> <source>Please provide the same passwords.</source>
<target state="translated">Bitte geben Sie diesselbe Passwörter ein.</target> <target state="translated">Bitte geben Sie diesselbe Passwörter ein.</target>
<jms:reference-file line="49">Form/Login/PhraseaRegisterForm.php</jms:reference-file>
<jms:reference-file line="44">Form/Login/PhraseaRecoverPasswordForm.php</jms:reference-file> <jms:reference-file line="44">Form/Login/PhraseaRecoverPasswordForm.php</jms:reference-file>
<jms:reference-file line="36">Form/Login/PhraseaRenewPasswordForm.php</jms:reference-file> <jms:reference-file line="36">Form/Login/PhraseaRenewPasswordForm.php</jms:reference-file>
<jms:reference-file line="49">Form/Login/PhraseaRegisterForm.php</jms:reference-file>
</trans-unit> </trans-unit>
<trans-unit id="90b8c9717bb7ed061dbf20fe1986c8b8593d43d4" resname="The token provided is not valid anymore" approved="yes"> <trans-unit id="90b8c9717bb7ed061dbf20fe1986c8b8593d43d4" resname="The token provided is not valid anymore" approved="yes">
<source>The token provided is not valid anymore</source> <source>The token provided is not valid anymore</source>

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:jms="urn:jms:translation" version="1.2"> <xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:jms="urn:jms:translation" version="1.2">
<file date="2021-02-09T15:08:37Z" source-language="en" target-language="en" datatype="plaintext" original="not.available"> <file date="2021-02-19T06:46:44Z" source-language="en" target-language="en" datatype="plaintext" original="not.available">
<header> <header>
<tool tool-id="JMSTranslationBundle" tool-name="JMSTranslationBundle" tool-version="1.1.0-DEV"/> <tool tool-id="JMSTranslationBundle" tool-name="JMSTranslationBundle" tool-version="1.1.0-DEV"/>
<note>The source node in most cases contains the sample message as written by the developer. If it looks like a dot-delimitted string such as "form.label.firstname", then the developer has not provided a default message.</note> <note>The source node in most cases contains the sample message as written by the developer. If it looks like a dot-delimitted string such as "form.label.firstname", then the developer has not provided a default message.</note>
@@ -9,9 +9,9 @@
<trans-unit id="96f0767cb7ea65a7f86c8c9432e80d16cf9d8680" resname="Please provide the same passwords." approved="yes"> <trans-unit id="96f0767cb7ea65a7f86c8c9432e80d16cf9d8680" resname="Please provide the same passwords." approved="yes">
<source>Please provide the same passwords.</source> <source>Please provide the same passwords.</source>
<target state="translated">Please provide the same passwords.</target> <target state="translated">Please provide the same passwords.</target>
<jms:reference-file line="49">Form/Login/PhraseaRegisterForm.php</jms:reference-file>
<jms:reference-file line="44">Form/Login/PhraseaRecoverPasswordForm.php</jms:reference-file> <jms:reference-file line="44">Form/Login/PhraseaRecoverPasswordForm.php</jms:reference-file>
<jms:reference-file line="36">Form/Login/PhraseaRenewPasswordForm.php</jms:reference-file> <jms:reference-file line="36">Form/Login/PhraseaRenewPasswordForm.php</jms:reference-file>
<jms:reference-file line="49">Form/Login/PhraseaRegisterForm.php</jms:reference-file>
</trans-unit> </trans-unit>
<trans-unit id="90b8c9717bb7ed061dbf20fe1986c8b8593d43d4" resname="The token provided is not valid anymore" approved="yes"> <trans-unit id="90b8c9717bb7ed061dbf20fe1986c8b8593d43d4" resname="The token provided is not valid anymore" approved="yes">
<source>The token provided is not valid anymore</source> <source>The token provided is not valid anymore</source>

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:jms="urn:jms:translation" version="1.2"> <xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:jms="urn:jms:translation" version="1.2">
<file date="2021-02-09T15:09:25Z" source-language="en" target-language="fr" datatype="plaintext" original="not.available"> <file date="2021-02-19T06:47:23Z" source-language="en" target-language="fr" datatype="plaintext" original="not.available">
<header> <header>
<tool tool-id="JMSTranslationBundle" tool-name="JMSTranslationBundle" tool-version="1.1.0-DEV"/> <tool tool-id="JMSTranslationBundle" tool-name="JMSTranslationBundle" tool-version="1.1.0-DEV"/>
<note>The source node in most cases contains the sample message as written by the developer. If it looks like a dot-delimitted string such as "form.label.firstname", then the developer has not provided a default message.</note> <note>The source node in most cases contains the sample message as written by the developer. If it looks like a dot-delimitted string such as "form.label.firstname", then the developer has not provided a default message.</note>
@@ -9,9 +9,9 @@
<trans-unit id="96f0767cb7ea65a7f86c8c9432e80d16cf9d8680" resname="Please provide the same passwords." approved="yes"> <trans-unit id="96f0767cb7ea65a7f86c8c9432e80d16cf9d8680" resname="Please provide the same passwords." approved="yes">
<source>Please provide the same passwords.</source> <source>Please provide the same passwords.</source>
<target state="translated">Veuillez indiquer des mots de passe identiques.</target> <target state="translated">Veuillez indiquer des mots de passe identiques.</target>
<jms:reference-file line="49">Form/Login/PhraseaRegisterForm.php</jms:reference-file>
<jms:reference-file line="44">Form/Login/PhraseaRecoverPasswordForm.php</jms:reference-file> <jms:reference-file line="44">Form/Login/PhraseaRecoverPasswordForm.php</jms:reference-file>
<jms:reference-file line="36">Form/Login/PhraseaRenewPasswordForm.php</jms:reference-file> <jms:reference-file line="36">Form/Login/PhraseaRenewPasswordForm.php</jms:reference-file>
<jms:reference-file line="49">Form/Login/PhraseaRegisterForm.php</jms:reference-file>
</trans-unit> </trans-unit>
<trans-unit id="90b8c9717bb7ed061dbf20fe1986c8b8593d43d4" resname="The token provided is not valid anymore" approved="yes"> <trans-unit id="90b8c9717bb7ed061dbf20fe1986c8b8593d43d4" resname="The token provided is not valid anymore" approved="yes">
<source>The token provided is not valid anymore</source> <source>The token provided is not valid anymore</source>

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:jms="urn:jms:translation" version="1.2"> <xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:jms="urn:jms:translation" version="1.2">
<file date="2021-02-09T15:10:15Z" source-language="en" target-language="nl" datatype="plaintext" original="not.available"> <file date="2021-02-19T06:48:05Z" source-language="en" target-language="nl" datatype="plaintext" original="not.available">
<header> <header>
<tool tool-id="JMSTranslationBundle" tool-name="JMSTranslationBundle" tool-version="1.1.0-DEV"/> <tool tool-id="JMSTranslationBundle" tool-name="JMSTranslationBundle" tool-version="1.1.0-DEV"/>
<note>The source node in most cases contains the sample message as written by the developer. If it looks like a dot-delimitted string such as "form.label.firstname", then the developer has not provided a default message.</note> <note>The source node in most cases contains the sample message as written by the developer. If it looks like a dot-delimitted string such as "form.label.firstname", then the developer has not provided a default message.</note>
@@ -9,9 +9,9 @@
<trans-unit id="96f0767cb7ea65a7f86c8c9432e80d16cf9d8680" resname="Please provide the same passwords."> <trans-unit id="96f0767cb7ea65a7f86c8c9432e80d16cf9d8680" resname="Please provide the same passwords.">
<source>Please provide the same passwords.</source> <source>Please provide the same passwords.</source>
<target state="new">Please provide the same passwords.</target> <target state="new">Please provide the same passwords.</target>
<jms:reference-file line="49">Form/Login/PhraseaRegisterForm.php</jms:reference-file>
<jms:reference-file line="44">Form/Login/PhraseaRecoverPasswordForm.php</jms:reference-file> <jms:reference-file line="44">Form/Login/PhraseaRecoverPasswordForm.php</jms:reference-file>
<jms:reference-file line="36">Form/Login/PhraseaRenewPasswordForm.php</jms:reference-file> <jms:reference-file line="36">Form/Login/PhraseaRenewPasswordForm.php</jms:reference-file>
<jms:reference-file line="49">Form/Login/PhraseaRegisterForm.php</jms:reference-file>
</trans-unit> </trans-unit>
<trans-unit id="90b8c9717bb7ed061dbf20fe1986c8b8593d43d4" resname="The token provided is not valid anymore"> <trans-unit id="90b8c9717bb7ed061dbf20fe1986c8b8593d43d4" resname="The token provided is not valid anymore">
<source>The token provided is not valid anymore</source> <source>The token provided is not valid anymore</source>

View File

@@ -818,8 +818,8 @@ form[name=registerForm] .multiselect-container li.active label {
} }
form[name=registerForm] .multiselect b.caret { form[name=registerForm] .multiselect b.caret {
float: right; position: absolute;
right: 10px;
} }
form[name=registerForm] .multiselect-group { form[name=registerForm] .multiselect-group {
@@ -1157,7 +1157,7 @@ h1, h2, h3, h4 {
border-radius:10px; border-radius:10px;
display:inline-block; display:inline-block;
position: relative; position: relative;
top: 0.35rem; top: -15px;
} }
#updatingDemand .icon.active { #updatingDemand .icon.active {

View File

@@ -35,9 +35,9 @@
{% if show == true %} {% if show == true %}
<div class="registration"> <div class="registration">
<div class="coll-name"> <div class="coll-name">
<span> <span>
{{ collInfo["coll-name"] }} {{ collInfo["coll-name"] }}
</span> </span>
</div> </div>
<div class="icon-box"> <div class="icon-box">
{% if type == 'registrable' %} {% if type == 'registrable' %}
@@ -47,23 +47,23 @@
{% endif %} {% endif %}
</div> </div>
<div class="status-box"> <div class="status-box">
<span class="status {{ type }}" > <span class="status {{ type }}" >
{% if type == 'active' %} {% if type == 'active' %}
{{ "registration:collection.active" | trans }} {{ "registration:collection.active" | trans }}
{% elseif type == 'registrable' %} {% elseif type == 'registrable' %}
{{ "registration:collection.registrable" | trans }} {{ "registration:collection.registrable" | trans }}
{% elseif type == 'in-time' %} {% elseif type == 'in-time' %}
{{ "registration:collection.in-time" | trans }} {{ "registration:collection.in-time" | trans }}
{% elseif type == 'out-dated' %} {% elseif type == 'out-dated' %}
{{ "registration:collection.out-dated" | trans }} {{ "registration:collection.out-dated" | trans }}
{% elseif type == 'pending' %} {% elseif type == 'pending' %}
{{ "registration:collection.pending" | trans }} {{ "registration:collection.pending" | trans }}
{% elseif type == 'rejected' %} {% elseif type == 'rejected' %}
{{ "registration:collection.rejected" | trans }} {{ "registration:collection.rejected" | trans }}
{% elseif type == 'accepted' %} {% elseif type == 'accepted' %}
{{ "registration:collection.accepted" | trans }} {{ "registration:collection.accepted" | trans }}
{% endif %} {% endif %}
</span> </span>
</div> </div>
</div> </div>
{% endif %} {% endif %}

View File

@@ -916,9 +916,9 @@
</div> </div>
<div id="PREVIEWBOX" class="PNB10 ui-dialog " style="overflow:hidden;"> <div id="PREVIEWBOX" class="PNB10 ui-dialog " style="overflow:hidden;">
<div id="PREVIEWTITLE" style="height:58px;bottom:auto;" class="PNB"> <div id="PREVIEWTITLE" style="height:70px;bottom:auto;" class="PNB">
<div class="PNB10 ui-corner-top" id='PREVIEWTITLEWRAPPER'> <div class="PNB10 ui-corner-top" id='PREVIEWTITLEWRAPPER'>
<div style="padding-top: 0px; padding-left: 5px;"> <div style="padding-top: 6px; padding-left: 5px;">
<span id="SPANTITLE"> </span> <span id="SPANTITLE"> </span>
<br/> <br/>
<span id="PREVIEWTITLE_COLLNAME"> </span> <span id="PREVIEWTITLE_COLLNAME"> </span>
@@ -928,7 +928,7 @@
</div> </div>
</div> </div>
<div class="PNB" style="right:180px;"> <div class="PNB" style="right:180px;">
<div class="PNB" style="top:58px;"> <div class="PNB" style="top:70px;">
<div id="PREVIEWLEFT" class="preview_col PNB" style="width:60%;right:auto;overflow:hidden;"> <div id="PREVIEWLEFT" class="preview_col PNB" style="width:60%;right:auto;overflow:hidden;">
<div id="PREVIEWCURRENT" class="ui-corner-bottom PNB10" style="top:11px;height:116px;bottom:auto;"> <div id="PREVIEWCURRENT" class="ui-corner-bottom PNB10" style="top:11px;height:116px;bottom:auto;">
<div id="PREVIEWCURRENTGLOB" style=""></div> <div id="PREVIEWCURRENTGLOB" style=""></div>
@@ -977,7 +977,7 @@
<span class="ui-button-icon-primary ui-icon ui-icon-closethick"></span><span class="ui-button-text">close</span> <span class="ui-button-icon-primary ui-icon ui-icon-closethick"></span><span class="ui-button-text">close</span>
</button> </button>
</div> </div>
<div id="PREVIEWOTHERS" style="top:97px;" class="PNB10 ui-corner-all"> <div id="PREVIEWOTHERS" style="top:109px;" class="PNB10 ui-corner-all">
<div id="PREVIEWOTHERSINNER" style=""></div> <div id="PREVIEWOTHERSINNER" style=""></div>
</div> </div>
</div> </div>

View File

@@ -88,6 +88,7 @@ class DoDownloadTest extends \PhraseanetAuthenticatedWebTestCase
'files' => [ 'files' => [
[ [
'base_id' => self::$DI['record_1']->get_base_id(), 'base_id' => self::$DI['record_1']->get_base_id(),
'databox_id' => self::$DI['record_1']->get_sbas_id(),
'record_id' => self::$DI['record_1']->get_record_id(), 'record_id' => self::$DI['record_1']->get_record_id(),
'export_name' => 'my_downloads', 'export_name' => 'my_downloads',
'original_name' => '0470', 'original_name' => '0470',
@@ -133,6 +134,7 @@ class DoDownloadTest extends \PhraseanetAuthenticatedWebTestCase
'files' => [ 'files' => [
[ [
'base_id' => self::$DI['record_1']->get_base_id(), 'base_id' => self::$DI['record_1']->get_base_id(),
'databox_id' => self::$DI['record_1']->get_sbas_id(),
'record_id' => self::$DI['record_1']->get_record_id(), 'record_id' => self::$DI['record_1']->get_record_id(),
'export_name' => 'my_downloads', 'export_name' => 'my_downloads',
'original_name' => '0470', 'original_name' => '0470',
@@ -151,6 +153,7 @@ class DoDownloadTest extends \PhraseanetAuthenticatedWebTestCase
], ],
[ [
'base_id' => self::$DI['record_2']->get_base_id(), 'base_id' => self::$DI['record_2']->get_base_id(),
'databox_id' => self::$DI['record_1']->get_sbas_id(),
'record_id' => self::$DI['record_2']->get_record_id(), 'record_id' => self::$DI['record_2']->get_record_id(),
'export_name' => 'my_downloads2', 'export_name' => 'my_downloads2',
'original_name' => '0471', 'original_name' => '0471',
@@ -297,6 +300,7 @@ class DoDownloadTest extends \PhraseanetAuthenticatedWebTestCase
'files' => [ 'files' => [
[ [
'base_id' => 1, 'base_id' => 1,
'databox_id' => 1,
'record_id' => 1, 'record_id' => 1,
'export_name' => 'my_downloads', 'export_name' => 'my_downloads',
'original_name' => '0470', 'original_name' => '0470',
@@ -315,6 +319,7 @@ class DoDownloadTest extends \PhraseanetAuthenticatedWebTestCase
], ],
[ [
'base_id' => 1, 'base_id' => 1,
'databox_id' => 1,
'record_id' => 1, 'record_id' => 1,
'export_name' => 'my_downloads2', 'export_name' => 'my_downloads2',
'original_name' => '0471', 'original_name' => '0471',

View File

@@ -67,7 +67,7 @@ class RegistrationManagerTest extends \PhraseanetTestCase
$databox = current(self::$DI['app']->getDataboxes()); $databox = current(self::$DI['app']->getDataboxes());
$collection = current($databox->get_collections()); $collection = current($databox->get_collections());
$this->assertEquals($value, count($rs[$databox->get_sbas_id()]['registrations']['by-type'][$type])); $this->assertEquals($value, count($rs[$databox->get_sbas_id()]['registrations-by-type'][$type]));
} }
public function userDataProvider() public function userDataProvider()
@@ -81,103 +81,121 @@ class RegistrationManagerTest extends \PhraseanetTestCase
$rejectedRegistration = new Registration(); $rejectedRegistration = new Registration();
$rejectedRegistration->setBaseId(1); $rejectedRegistration->setBaseId(1);
$rejectedRegistration->setUser(new User()); $rejectedRegistration->setUser(new User());
$rejectedRegistration->setPending(true); $rejectedRegistration->setPending(false);
$rejectedRegistration->setRejected(true); $rejectedRegistration->setRejected(true);
$acceptedRegistration = new Registration();
$acceptedRegistration->setBaseId(1);
$acceptedRegistration->setUser(new User());
$acceptedRegistration->setPending(false);
$acceptedRegistration->setRejected(false);
$registrations = [
'pending' => $pendingRegistration,
'accepted' => $acceptedRegistration,
'rejected' => $rejectedRegistration,
'inactive' => null
];
$databox = current((new \appbox(new Application(Application::ENV_TEST)))->get_databoxes()); $databox = current((new \appbox(new Application(Application::ENV_TEST)))->get_databoxes());
$collection = current($databox->get_collections()); $collection = current($databox->get_collections());
$noLimitedPendingRegistration = [ $tests = [];
[
$databox->get_sbas_id() => [
$collection->get_base_id() => [
'base-id' => $collection->get_base_id(),
'db-name' => 'toto',
'active' => true,
'time-limited' => false,
'in-time' => null,
'registration' => $pendingRegistration
]
]
],
'pending',
1
];
$rejectedRegistration = [ // ====== no access in basusr : result comes only from "registration" ======
[ foreach($registrations as $k=>$registration) {
$databox->get_sbas_id() => [ // pending, accepted, rejected, inactive
$collection->get_base_id() => [ $tests[] = [
'base-id' => $collection->get_base_id(), [
'db-name' => 'titi', $databox->get_sbas_id() => [
'active' => true, $collection->get_base_id() => [
'time-limited' => false, 'base-id' => $collection->get_base_id(),
'in-time' => null, 'db-name' => 'toto',
'registration' => $rejectedRegistration 'active' => null,
'time-limited' => null,
'in-time' => null,
'registration' => $registration
]
] ]
] ],
], $k,
'rejected', 1
1 ];
]; }
$noActiveRegistration = [ // ======= rights with time limit : registration does not matter =======
[ foreach($registrations as $registration) {
$databox->get_sbas_id() => [ $tests[] = [
$collection->get_base_id() => [ [
'base-id' => 1, $databox->get_sbas_id() => [
'db-name' => 'tutu', $collection->get_base_id() => [
'active' => false, 'base-id' => $collection->get_base_id(),
'time-limited' => false, 'db-name' => 'toto',
'in-time' => null, 'active' => true,
'registration' => $pendingRegistration 'time-limited' => true,
'in-time' => true,
'registration' => $registration
]
] ]
] ],
], 'in-time',
'inactive', 1
1 ];
]; $tests[] = [
[
$limitedActiveIntimePendingRegistration = [ $databox->get_sbas_id() => [
[ $collection->get_base_id() => [
$databox->get_sbas_id() => [ 'base-id' => $collection->get_base_id(),
$collection->get_base_id() => [ 'db-name' => 'toto',
'base-id' => $collection->get_base_id(), 'active' => true,
'db-name' => 'tata', 'time-limited' => true,
'active' => true, 'in-time' => false,
'time-limited' => true, 'registration' => $registration
'in-time' => true, ]
'registration' => $pendingRegistration
] ]
] ],
], 'out-dated',
'in-time', 1
1 ];
]; }
$limitedActiveOutdatedPendingRegistration = [ // ======= rights, no time limit : registration may matter =======
[ foreach($registrations as $k=>$registration) {
$databox->get_sbas_id() => [ // pending, accepted, rejected, inactive
$collection->get_base_id() => [ $tests[] = [
'base-id' => $collection->get_base_id(), [
'db-name' => 'toutou', $databox->get_sbas_id() => [
'active' => true, $collection->get_base_id() => [
'time-limited' => true, 'base-id' => $collection->get_base_id(),
'in-time' => false, 'db-name' => 'toto',
'registration' => $pendingRegistration 'active' => true,
'time-limited' => false,
'in-time' => null,
'registration' => $registration
]
] ]
] ],
], $k=='accepted' ? 'accepted' : 'active',
'out-dated', 1
1 ];
]; $tests[] = [
[
$databox->get_sbas_id() => [
$collection->get_base_id() => [
'base-id' => $collection->get_base_id(),
'db-name' => 'toto',
'active' => false,
'time-limited' => false,
'in-time' => null,
'registration' => $registration
]
]
],
$k=='rejected' ? 'rejected' : 'inactive',
1
];
}
return [ return $tests;
$noLimitedPendingRegistration,
$noActiveRegistration,
$limitedActiveIntimePendingRegistration,
$limitedActiveOutdatedPendingRegistration,
$rejectedRegistration
];
} }
} }

View File

@@ -19,7 +19,7 @@ class TokenManipulatorTest extends \PhraseanetTestCase
{ {
$user = $user ? self::$DI['user'] : null; $user = $user ? self::$DI['user'] : null;
$manipulator = new TokenManipulator(self::$DI['app']['orm.em'], self::$DI['app']['random.low'], self::$DI['app']['repo.tokens'], self::$DI['app']['tmp.download.path']); $manipulator = new TokenManipulator(self::$DI['app']['orm.em'], self::$DI['app']['random.low'], self::$DI['app']['repo.tokens'], self::$DI['app']['tmp.download.path'], self::$DI['app']['conf']);
$token = $manipulator->create($user, $type, $expiration, $data); $token = $manipulator->create($user, $type, $expiration, $data);
$this->assertSame($user, $token->getUser()); $this->assertSame($user, $token->getUser());
@@ -42,7 +42,7 @@ class TokenManipulatorTest extends \PhraseanetTestCase
public function testCreateBasketValidationToken() public function testCreateBasketValidationToken()
{ {
$manipulator = new TokenManipulator(self::$DI['app']['orm.em'], self::$DI['app']['random.low'], self::$DI['app']['repo.tokens'], self::$DI['app']['tmp.download.path']); $manipulator = new TokenManipulator(self::$DI['app']['orm.em'], self::$DI['app']['random.low'], self::$DI['app']['repo.tokens'], self::$DI['app']['tmp.download.path'], self::$DI['app']['conf']);
$expire = new DateTime('+10 days'); $expire = new DateTime('+10 days');
$token = $manipulator->createBasketValidationToken(self::$DI['basket_4'], self::$DI['user_1'], $expire); $token = $manipulator->createBasketValidationToken(self::$DI['basket_4'], self::$DI['user_1'], $expire);
@@ -54,7 +54,7 @@ class TokenManipulatorTest extends \PhraseanetTestCase
public function testCreateBasketValidationTokenWithoutExpiration() public function testCreateBasketValidationTokenWithoutExpiration()
{ {
$manipulator = new TokenManipulator(self::$DI['app']['orm.em'], self::$DI['app']['random.low'], self::$DI['app']['repo.tokens'], self::$DI['app']['tmp.download.path']); $manipulator = new TokenManipulator(self::$DI['app']['orm.em'], self::$DI['app']['random.low'], self::$DI['app']['repo.tokens'], self::$DI['app']['tmp.download.path'], self::$DI['app']['conf']);
$token = $manipulator->createBasketValidationToken(self::$DI['basket_4'], self::$DI['user_1'], null); $token = $manipulator->createBasketValidationToken(self::$DI['basket_4'], self::$DI['user_1'], null);
$this->assertSame(self::$DI['basket_4']->getId(), $token->getData()); $this->assertSame(self::$DI['basket_4']->getId(), $token->getData());
@@ -65,14 +65,14 @@ class TokenManipulatorTest extends \PhraseanetTestCase
public function testCreateBasketValidationTokenWithInvalidBasket() public function testCreateBasketValidationTokenWithInvalidBasket()
{ {
$manipulator = new TokenManipulator(self::$DI['app']['orm.em'], self::$DI['app']['random.low'], self::$DI['app']['repo.tokens'], self::$DI['app']['tmp.download.path']); $manipulator = new TokenManipulator(self::$DI['app']['orm.em'], self::$DI['app']['random.low'], self::$DI['app']['repo.tokens'], self::$DI['app']['tmp.download.path'], self::$DI['app']['conf']);
$this->setExpectedException('InvalidArgumentException', 'A validation token requires a validation basket.'); $this->setExpectedException('InvalidArgumentException', 'A validation token requires a validation basket.');
$manipulator->createBasketValidationToken(self::$DI['basket_1'], self::$DI['user_1'], null); $manipulator->createBasketValidationToken(self::$DI['basket_1'], self::$DI['user_1'], null);
} }
public function testCreateBasketAccessToken() public function testCreateBasketAccessToken()
{ {
$manipulator = new TokenManipulator(self::$DI['app']['orm.em'], self::$DI['app']['random.low'], self::$DI['app']['repo.tokens'], self::$DI['app']['tmp.download.path']); $manipulator = new TokenManipulator(self::$DI['app']['orm.em'], self::$DI['app']['random.low'], self::$DI['app']['repo.tokens'], self::$DI['app']['tmp.download.path'], self::$DI['app']['conf']);
$token = $manipulator->createBasketAccessToken(self::$DI['basket_4'], self::$DI['user']); $token = $manipulator->createBasketAccessToken(self::$DI['basket_4'], self::$DI['user']);
$this->assertSame(self::$DI['basket_4']->getId(), $token->getData()); $this->assertSame(self::$DI['basket_4']->getId(), $token->getData());
@@ -83,7 +83,7 @@ class TokenManipulatorTest extends \PhraseanetTestCase
public function testCreateFeedEntryToken() public function testCreateFeedEntryToken()
{ {
$manipulator = new TokenManipulator(self::$DI['app']['orm.em'], self::$DI['app']['random.low'], self::$DI['app']['repo.tokens'], self::$DI['app']['tmp.download.path']); $manipulator = new TokenManipulator(self::$DI['app']['orm.em'], self::$DI['app']['random.low'], self::$DI['app']['repo.tokens'], self::$DI['app']['tmp.download.path'], self::$DI['app']['conf']);
$token = $manipulator->createFeedEntryToken(self::$DI['user'], self::$DI['feed_public_entry']); $token = $manipulator->createFeedEntryToken(self::$DI['user'], self::$DI['feed_public_entry']);
$this->assertSame(self::$DI['feed_public_entry']->getId(), $token->getData()); $this->assertSame(self::$DI['feed_public_entry']->getId(), $token->getData());
@@ -95,19 +95,19 @@ class TokenManipulatorTest extends \PhraseanetTestCase
public function testCreateDownloadToken() public function testCreateDownloadToken()
{ {
$data = serialize(['some' => 'data']); $data = serialize(['some' => 'data']);
$manipulator = new TokenManipulator(self::$DI['app']['orm.em'], self::$DI['app']['random.low'], self::$DI['app']['repo.tokens'], self::$DI['app']['tmp.download.path']); $manipulator = new TokenManipulator(self::$DI['app']['orm.em'], self::$DI['app']['random.low'], self::$DI['app']['repo.tokens'], self::$DI['app']['tmp.download.path'], self::$DI['app']['conf']);
$token = $manipulator->createDownloadToken(self::$DI['user'], $data); $token = $manipulator->createDownloadToken(self::$DI['user'], $data);
$this->assertSame($data, $token->getData()); $this->assertSame($data, $token->getData());
$this->assertSame(self::$DI['user'], $token->getUser()); $this->assertSame(self::$DI['user'], $token->getUser());
$this->assertSame(TokenManipulator::TYPE_DOWNLOAD, $token->getType()); $this->assertSame(TokenManipulator::TYPE_DOWNLOAD, $token->getType());
$this->assertDateNear('+3 hours', $token->getExpiration()); $this->assertDateNear('+24 hours', $token->getExpiration());
} }
public function testCreateEmailExportToken() public function testCreateEmailExportToken()
{ {
$data = serialize(['some' => 'data']); $data = serialize(['some' => 'data']);
$manipulator = new TokenManipulator(self::$DI['app']['orm.em'], self::$DI['app']['random.low'], self::$DI['app']['repo.tokens'], self::$DI['app']['tmp.download.path']); $manipulator = new TokenManipulator(self::$DI['app']['orm.em'], self::$DI['app']['random.low'], self::$DI['app']['repo.tokens'], self::$DI['app']['tmp.download.path'], self::$DI['app']['conf']);
$token = $manipulator->createEmailExportToken($data); $token = $manipulator->createEmailExportToken($data);
$this->assertSame($data, $token->getData()); $this->assertSame($data, $token->getData());
@@ -118,7 +118,7 @@ class TokenManipulatorTest extends \PhraseanetTestCase
public function testCreateResetEmailToken() public function testCreateResetEmailToken()
{ {
$manipulator = new TokenManipulator(self::$DI['app']['orm.em'], self::$DI['app']['random.low'], self::$DI['app']['repo.tokens'], self::$DI['app']['tmp.download.path']); $manipulator = new TokenManipulator(self::$DI['app']['orm.em'], self::$DI['app']['random.low'], self::$DI['app']['repo.tokens'], self::$DI['app']['tmp.download.path'], self::$DI['app']['conf']);
$token = $manipulator->createResetEmailToken(self::$DI['user'], 'newemail@phraseanet.com'); $token = $manipulator->createResetEmailToken(self::$DI['user'], 'newemail@phraseanet.com');
$this->assertSame('newemail@phraseanet.com', $token->getData()); $this->assertSame('newemail@phraseanet.com', $token->getData());
@@ -129,7 +129,7 @@ class TokenManipulatorTest extends \PhraseanetTestCase
public function testCreateAccountUnlockToken() public function testCreateAccountUnlockToken()
{ {
$manipulator = new TokenManipulator(self::$DI['app']['orm.em'], self::$DI['app']['random.low'], self::$DI['app']['repo.tokens'], self::$DI['app']['tmp.download.path']); $manipulator = new TokenManipulator(self::$DI['app']['orm.em'], self::$DI['app']['random.low'], self::$DI['app']['repo.tokens'], self::$DI['app']['tmp.download.path'], self::$DI['app']['conf']);
$token = $manipulator->createAccountUnlockToken(self::$DI['user']); $token = $manipulator->createAccountUnlockToken(self::$DI['user']);
$this->assertNull($token->getData()); $this->assertNull($token->getData());
@@ -140,7 +140,7 @@ class TokenManipulatorTest extends \PhraseanetTestCase
public function testCreateResetPasswordToken() public function testCreateResetPasswordToken()
{ {
$manipulator = new TokenManipulator(self::$DI['app']['orm.em'], self::$DI['app']['random.low'], self::$DI['app']['repo.tokens'], self::$DI['app']['tmp.download.path']); $manipulator = new TokenManipulator(self::$DI['app']['orm.em'], self::$DI['app']['random.low'], self::$DI['app']['repo.tokens'], self::$DI['app']['tmp.download.path'], self::$DI['app']['conf']);
$token = $manipulator->createResetPasswordToken(self::$DI['user']); $token = $manipulator->createResetPasswordToken(self::$DI['user']);
$this->assertNull($token->getData()); $this->assertNull($token->getData());
@@ -160,7 +160,7 @@ class TokenManipulatorTest extends \PhraseanetTestCase
$em->expects($this->once()) $em->expects($this->once())
->method('flush'); ->method('flush');
$manipulator = new TokenManipulator($em, self::$DI['app']['random.low'], self::$DI['app']['repo.tokens'], self::$DI['app']['tmp.download.path']); $manipulator = new TokenManipulator($em, self::$DI['app']['random.low'], self::$DI['app']['repo.tokens'], self::$DI['app']['tmp.download.path'], self::$DI['app']['conf']);
$manipulator->update($token); $manipulator->update($token);
} }
@@ -175,7 +175,7 @@ class TokenManipulatorTest extends \PhraseanetTestCase
$em->expects($this->once()) $em->expects($this->once())
->method('flush'); ->method('flush');
$manipulator = new TokenManipulator($em, self::$DI['app']['random.low'], self::$DI['app']['repo.tokens'], self::$DI['app']['tmp.download.path']); $manipulator = new TokenManipulator($em, self::$DI['app']['random.low'], self::$DI['app']['repo.tokens'], self::$DI['app']['tmp.download.path'], self::$DI['app']['conf']);
$manipulator->delete($token); $manipulator->delete($token);
} }
@@ -183,7 +183,7 @@ class TokenManipulatorTest extends \PhraseanetTestCase
{ {
$this->assertCount(4, self::$DI['app']['repo.tokens']->findAll()); $this->assertCount(4, self::$DI['app']['repo.tokens']->findAll());
$manipulator = new TokenManipulator(self::$DI['app']['orm.em'], self::$DI['app']['random.low'], self::$DI['app']['repo.tokens'], self::$DI['app']['tmp.download.path'], self::$DI['app']['tmp.download.path']); $manipulator = new TokenManipulator(self::$DI['app']['orm.em'], self::$DI['app']['random.low'], self::$DI['app']['repo.tokens'], self::$DI['app']['tmp.download.path'], self::$DI['app']['conf']);
$manipulator->removeExpiredTokens(); $manipulator->removeExpiredTokens();
$this->assertCount(3, self::$DI['app']['repo.tokens']->findAll()); $this->assertCount(3, self::$DI['app']['repo.tokens']->findAll());