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
PHRASEANET_DOCKER_REGISTRY=local
# Tag of the Docker images
PHRASEANET_DOCKER_TAG=4.1.3
# APPLICATION PORT
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_PASS=vdh4dpe5Wy3R
RABBITMQ_MANAGEMENT_PORT=10811
# Mysql configuration
MYSQL_ROOT_PASSWORD=root
SERVER_NAME=phraseanet-docker
# --------------- GATEWAY TIMEOUT -----------------------
# --------------- GATEWAY SETTING (nginx) -----------------------
GATEWAY_SEND_TIMEOUT=120
GATEWAY_PROXY_TIMEOUT=120
GATEWAY_FASTCGI_TIMEOUT=300
# --------------- PHP CONFIGURATION --------------------
# Max upload size
@@ -40,26 +59,44 @@ PHP_LOG_LEVEL=warning
# --------------- MYSQL CONFIGURATION --------------------
# Mysql configuration
MYSQL_ROOT_PASSWORD=root
SERVER_NAME=phraseanet-docker
# Mysql max allowed packet
MYSQL_MAX_ALLOWED_PACKET=16M
# --------------- PHRASEANET CONFIGURATION --------------------
# --------------- PHRASEANET CONFIGURATION -----------------
# 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 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_EMAIL=admin@alchemy.fr
PHRASEANET_ADMIN_ACCOUNT_PASSWORD=iJRqXU0MwbyJewQLBbra6IWHsWly
# Database parameters
# Mysql parameters
PHRASEANET_DB_HOST=db
PHRASEANET_DB_PORT=3306
PHRASEANET_DB_USER=root
PHRASEANET_DB_PASSWORD=root
# Installation parameters
INSTALL_DB_TEMPLATE=DublinCore
INSTALL_APPBOX=ab_master
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
PHRASEANET_FFMPEG_TIMEOUT=7200
PHRASEANET_FFPROBE_TIMEOUT=120
@@ -70,7 +107,6 @@ PHRASEANET_UNOCON_TIMEOUT=120
PHRASEANET_EXIFTOOL_TIMEOUT=120
# network : comma separated list of IP ou SUBNETS
PHRASEANET_TRUSTED_PROXIES=
# api
@@ -78,9 +114,16 @@ PHRASEANET_API_ENABLED=true
PHRASEANET_API_SSL=true
PHRASEANET_API_AUTH_TOKEN_HEADER_ONLY=false
# Mapbox setting Geolocalisation
PHRASEANET_MAPBOX_ACTIVATE=false
PHRASEANET_MAPBOX_TOKEN=
PHRASEANET_MAPBOX_KIND=
# Phraseanet mail configuration
PHRASEANET_EMITTER_EMAIL=phraseanet@example.com
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_HOST=mailhog
PHRASEANET_SMTP_PORT=1025
@@ -108,7 +151,6 @@ PHRASEANET_WORKER_webhook=1
PHRASEANET_WORKER_writeMetadatas=1
# Locale setting
LC_MESSAGES=C.UTF-8
LC_COLLATE=C.UTF-8
LC_IDENTIFICATION=C.UTF-8
@@ -118,31 +160,6 @@ LC_CTYPE=C.UTF-8
LC_TIME=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
PHRASEANET_CONFIG_DIR=./config
PHRASEANET_LOGS_DIR=./logs
@@ -159,6 +176,37 @@ PHRASEANET_LAZARET_DIR=./datas/lazaret
PHRASEANET_CAPTION_DIR=./tmp/caption
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
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)
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>)
`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

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:
type: string
meta_struct_id:
$ref: '#/ID'
$ref: 'common.yaml#/ID'
action:
type: string
enum:
@@ -15,6 +15,8 @@ RecordPatch_metadata:
value:
# todo : change to string, int, number, array
type: string
RecordPatch_status:
type: object
required:
@@ -39,254 +41,9 @@ RecordPatch:
type: array
items:
$ref: '#/RecordPatch_status'
ApiResponse_meta:
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'
# -------------------- searchraw ---------------
# -------------------- 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"]
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
- Generate dist ```npm run dist```
- ```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

View File

@@ -2,7 +2,7 @@ const _root = __dirname + '/../';
module.exports = {
// path helpers
// path helpers
_app: 'app',
minified: 'app.min.js',
dev: 'app.js',
@@ -11,5 +11,7 @@ module.exports = {
sourceDir: _root + 'src/',
testDir: _root + 'tests',
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: {
path: config.distDir,
filename: '[name].js',
chunkFilename: 'lazy-[name].js',
chunkFilename: 'lazy-[name].js?v=' + config.jsFileVersion,
libraryTarget: 'umd',
library: config._app,
publicPath: '/assets/production/'

View File

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

View File

@@ -96,7 +96,7 @@ return /******/ (function(modules) { // webpackBootstrap
/******/ if (__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);
/******/ script.onerror = script.onload = onScriptComplete;
/******/ function onScriptComplete() {

View File

@@ -96,7 +96,7 @@ return /******/ (function(modules) { // webpackBootstrap
/******/ if (__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);
/******/ script.onerror = script.onload = onScriptComplete;
/******/ function onScriptComplete() {

View File

@@ -91,7 +91,7 @@
/******/ if (__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);
/******/ script.onerror = script.onload = onScriptComplete;
/******/ function onScriptComplete() {

View File

@@ -91,7 +91,7 @@
/******/ if (__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);
/******/ script.onerror = script.onload = onScriptComplete;
/******/ function onScriptComplete() {

View File

@@ -65747,13 +65747,13 @@ var previewRecordService = function previewRecordService(services) {
var basketIcon = '';
if (data.containerType !== null) {
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') {
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') {
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 {
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 = '';
if (data.containerType !== null) {
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') {
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') {
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 {
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;
}
div#PREVIEWTITLEWRAPPER span {
line-height: 25px;
}
.PNB10 {
position: absolute;
top: 10px;

File diff suppressed because one or more lines are too long

View File

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

File diff suppressed because one or more lines are too long

View File

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

File diff suppressed because one or more lines are too long

View File

@@ -384,13 +384,13 @@ const previewRecordService = services => {
let basketIcon = '';
if (data.containerType !== null ) {
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') {
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') {
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 {
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 {
top: 10px;
span {
line-height: 25px;
}
}
.PNB10 {

233
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)
# Features :
# Main Features :
- Several GUI : Prod, Admin, Thesaurus, Lightbox ,Report,
- Metadata Management (include Thesaurus and DublinCore Mapping)
- RestFull APIS
- Elasticsearch search engine
- Multiple resolution assets generation
- Advanced Rights Management
- Rich ecosystem: Plugin for Wordpress, Drupal and Adobe Creative Suite.
# 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
# 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:
## Prerequisites
- docker-compose >=v1.25.4
- docker >=v18.01-ce
- docker-compose >=v1.27.4
- 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
## 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.
Use `export` to override these values.
Use `export` method to override these values.
i.e:
```bash
@@ -51,8 +136,10 @@ export INSTALL_ACCOUNT_EMAIL=foo@bar.com
export INSTALL_ACCOUNT_PASSWORD=$3cr3t!
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.
@@ -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
Why this option `-f docker-compose.yml`?
The development and integration concerns are separated using a `docker-compose.override.yml`. By default, `docker-compose` will include this files if it exists.
If you don't work on phraseanet development, avoiding this `-f docker-compose.yml` parameters will throw errors. So you have to add this options on every `docker-compose` commands to avoid this inclusion.
> You can also delete the `docker-compose.override.yml` to get free from this behavior.
#### Running workers
```bash
docker-compose -f docker-compose.yml run --rm worker <command>
```
Where `<command>` can be:
- `bin/console 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
#### Use the prebuild image from dockerhub, see DockerHub section bellow for more information.
set env var
```PHRASEANET_DOCKER_REGISTRY```
i.e:
```bash
# Registry from where you pull Docker images
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
@@ -132,14 +222,21 @@ Pull images before launch docker-compose
```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
## 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:
@@ -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
> 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!
### Developer shell
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)
```
#### 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)
You can also download a testing pre installed Virtual Machine in OVA format here :
https://www.phraseanet.com/download/
# With Vagrant (deprecated)
## 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
depends_on:
- db
networks:
- internal
gateway:
volumes:
- ../:/var/alchemy
- .:/var/alchemy/Phraseanet
- ./docker/nginx/root/entrypoint.sh:/entrypoint.sh
networks:
- internal
builder:
build:
context: .
@@ -37,6 +40,8 @@ services:
- dev_vol:/home/app
environment:
- PHRASEANET_PROJECT_NAME
networks:
- internal
phraseanet:
environment:
@@ -46,27 +51,34 @@ services:
volumes:
- ../:/var/alchemy
- .:/var/alchemy/Phraseanet
networks:
- internal
worker:
volumes:
- ../:/var/alchemy
- .:/var/alchemy/Phraseanet
networks:
- internal
rabbitmq:
ports:
- ${RABBITMQ_MANAGEMENT_PORT}:15672
networks:
- internal
db:
volumes:
- ${PHRASEANET_DB_DIR}:/var/lib/mysql:rw
networks:
- internal
elasticsearch:
ports:
- 9200:9200
volumes:
- ${PHRASEANET_ELASTICSEARCH_DIR}:/usr/share/elasticsearch/data:rw
networks:
- internal
kibana:
image: kibana:4.6.6
ports:
@@ -75,6 +87,8 @@ services:
- elasticsearch
depends_on:
- elasticsearch
networks:
- internal
logstash:
image: logstash:7.6.2
@@ -86,6 +100,8 @@ services:
depends_on:
- elasticsearch
restart: on-failure
networks:
- internal
filebeat:
hostname: filebeat
@@ -95,6 +111,8 @@ services:
- ${PHRASEANET_LOGS_DIR}:/var/alchemy/Phraseanet/logs:ro
command: filebeat run -e --strict.perms=false
restart: on-failure
networks:
- internal
ftpd-server:
image: stilliard/pure-ftpd:hardened
@@ -105,9 +123,5 @@ services:
volumes:
- ${PHRASEANET_FTP_DIR}:/home/dev
restart: on-failure
networks:
default:
ipam:
config:
- subnet: $PHRASEANET_SUBNET_IPS
networks:
- internal

View File

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

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
if [ -f "$FILE" ]; then
echo "$FILE exists, skip setup."
if [[ $PHRASEANET_PROJECT_NAME ]]; then
if [[ ! -f "$FILE" && $PHRASEANET_INSTALL = 1 ]];then
echo `date +"%Y-%m-%d %H:%M:%S"` " - Phraseanet $FILE doesn't exist, Play Phraseanet installation..."
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
fi
echo `date +"%Y-%m-%d %H:%M:%S"` " - Phraseanet Setting available language in GUI and search"
counter=0
if [[ -n $PHRASEANET_AVAILABLE_LANGUAGE ]]; then
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
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
bin/setup system:config add trusted-proxies $i
done
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.ffprobe_timeout $PHRASEANET_FFPROBE_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.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-require-ssl $PHRASEANET_API_SSL
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
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
@@ -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.prefix $PHRASEANET_MAIL_OBJECT_PREFIX
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
bin/console user:password --user_id=$PHRASEANET_ADMIN_ACCOUNT_ID --password $PHRASEANET_ADMIN_ACCOUNT_PASSWORD -y
fi
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
if [ ${XDEBUG_ENABLED} == "1" ]; then
@@ -72,7 +116,7 @@ fi
./docker/phraseanet/plugins/console init
rm -Rf cache/*
chmod 600 config/configuration.yml
chown -R app:app \
cache \
@@ -86,7 +130,7 @@ if [ -d "plugins/" ];then
chown -R app:app plugins;
fi
chown -R app:app datas && echo `date +"%Y-%m-%d %H:%M:%S"` " - Finished chown on datas by entreypoint" &
echo `date +"%Y-%m-%d %H:%M:%S"` " - Finished runnning Phraseanet entrypoint.sh"
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"` " - End of Phraseanet entrypoint.sh"
bash -e docker-php-entrypoint $@

View File

@@ -31,15 +31,15 @@ class CollectionRequestMapper
$demands = array();
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");
}
foreach ($data['registrations']['by-type']['rejected'] as $collectionId => $waiting) {
foreach ($data['registrations-by-type']['rejected'] as $collectionId => $waiting) {
$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");
}
}

View File

@@ -289,7 +289,7 @@ class RegistrationService
* @param array $selectedCollections
* @return \collection[]
*/
private function getAuthorizedCollections(array $selectedCollections = null)
private function getAuthorizedCollections($selectedCollections)
{
$inscriptions = $this->registrationManager->getRegistrationSummary();
$authorizedCollections = [];
@@ -300,7 +300,7 @@ class RegistrationService
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;
}
}

View File

@@ -63,142 +63,108 @@ class RegistrationManager
}
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' => [
'by-type' => [
'inactive' => [],
'accepted' => [],
'in-time' => [],
'out-dated' => [],
'pending' => [],
'rejected' => [],
]
'registrations-by-type' => [
'active' => [],
'inactive' => [],
'accepted' => [],
'in-time' => [],
'out-dated' => [],
'pending' => [],
'rejected' => [],
],
// Registration configuration on databox and collections that belong to the databox
'config' => [
'db-name' => $databox->get_dbname(),
'cgu' => $databox->get_cgus(),
'can-register' => $databox->isRegistrationEnabled(),
// Configuration on collection
'collections' => [],
]
'db-name' => $databox->get_dbname(),
'cgu' => $databox->get_cgus(),
'can-register' => $databox->isRegistrationEnabled(),
// Configuration on collection
'collections' => [],
'display' => false, // set to true if there is at least one collection to display
];
foreach ($databox->get_collections() as $collection) {
$base_id = $collection->get_base_id();
$userRegistration = igorw\get_in($userData, [$sbas_id, $base_id]);
// 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
if (null !== $registration = $this->getUserCollectionRegistration($collection, $userData)) {
$data[$databox->get_sbas_id()]['registrations']['by-type'][$registration['type']][] = $registration;
if (!is_null($userRegistration)) { // && !is_null($userRegistration['active'])) {
$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;
}
/**
* 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,
'force-push-authentication' => false,
'enable-feed-notification' => true,
'download-link-validity' => 24,
],
'ftp' => [
'ftp-enabled' => false,

View File

@@ -57,7 +57,8 @@ class ManipulatorServiceProvider implements ServiceProviderInterface
$app['orm.em'],
$app['random.medium'],
$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', [
'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()

View File

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

View File

@@ -11,6 +11,7 @@
namespace Alchemy\Phrasea\Model\Manipulator;
use Alchemy\Phrasea\Core\Configuration\PropertyAccess;
use Alchemy\Phrasea\Model\Entities\Basket;
use Alchemy\Phrasea\Model\Entities\FeedEntry;
use Alchemy\Phrasea\Model\Entities\Token;
@@ -41,6 +42,7 @@ class TokenManipulator implements ManipulatorInterface
private $om;
private $random;
private $repository;
private $conf;
private $temporaryDownloadPath;
@@ -48,12 +50,14 @@ class TokenManipulator implements ManipulatorInterface
ObjectManager $om,
Generator $random,
TokenRepository $repository,
$temporaryDownloadPath)
$temporaryDownloadPath,
PropertyAccess $configuration)
{
$this->om = $om;
$this->random = $random;
$this->repository = $repository;
$this->temporaryDownloadPath = $temporaryDownloadPath;
$this->conf = $configuration;
}
/**
@@ -168,7 +172,9 @@ class TokenManipulator implements ManipulatorInterface
*/
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)
{
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 = [];
$rsm = $this->createResultSetMappingBuilder('d');
$rsm->addScalarResult('sbas_id','sbas_id');
$rsm->addScalarResult('base_id','base_id');
$rsm->addScalarResult('bas_id','bas_id');
$rsm->addScalarResult('dbname','dbname');
$rsm->addScalarResult('time_limited', 'time_limited');
$rsm->addScalarResult('limited_from', 'limited_from');
$rsm->addScalarResult('limited_to', 'limited_to');
$rsm->addScalarResult('actif', 'actif');
$sql = "
SELECT dbname, sbas.sbas_id, time_limited,
UNIX_TIMESTAMP( limited_from ) AS limited_from,
UNIX_TIMESTAMP( limited_to ) AS limited_to,
bas.server_coll_id, Users.id, basusr.actif,
bas.base_id, " . $rsm->generateSelectClause(['d' => 'd',]) . "
FROM (Users, bas, sbas)
LEFT JOIN basusr ON ( Users.id = basusr.usr_id AND bas.base_id = basusr.base_id )
LEFT JOIN Registrations d ON ( d.user_id = Users.id AND bas.base_id = d.base_id )
WHERE basusr.actif = 1 AND bas.sbas_id = sbas.sbas_id
AND Users.id = ?";
// nb: UNIX_TIMESTAMP will return null if date is 0000-00-00 00:00:00
$sql = "SELECT dbname, sbas.sbas_id, time_limited,\n"
. " UNIX_TIMESTAMP( limited_from ) AS limited_from,\n"
. " UNIX_TIMESTAMP( limited_to ) AS limited_to,\n"
. " bas.server_coll_id, Users.id, basusr.actif,\n"
. " bas.base_id AS bas_id, " . $rsm->generateSelectClause(['d' => 'd',]) . "\n"
. "FROM (Users, bas, sbas)\n"
. " 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 )\n"
. "WHERE bas.active = 1 AND bas.sbas_id = sbas.sbas_id\n"
. " AND Users.id = ?\n"
. " AND ISNULL(model_of)";
$query = $this->_em->createNativeQuery($sql, $rsm);
$query->setParameter(1, $user->getId());
foreach ($query->getResult() as $row) {
$registrationEntity = $row[0];
$data[$row['sbas_id']][$row['base_id']] = [
'base-id' => $row['base_id'],
$in_time = null;
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'],
'active' => (Boolean) $row['actif'],
'time-limited' => (Boolean) $row['time_limited'],
'in-time' => $row['time_limited'] && ! ($row['limited_from'] >= time() && $row['limited_to'] <= time()),
'active' => self::nullOrBoolean($row['actif']),
'time-limited' => self::nullOrBoolean($row['time_limited']),
'in-time' => $in_time,
'registration' => $registrationEntity
];
}
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 [
// 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);
}
/**
* 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)
{
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);
break;
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;
case "REG":
$this->title = '<span style="color:#27bbe2;">';
$this->title .= $this->name;
if ($this->getNumber() != 0) {
$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;

View File

@@ -14,6 +14,7 @@ use Alchemy\Phrasea\Model\Entities\User;
use Alchemy\Phrasea\Model\Repositories\BasketRepository;
use Alchemy\Phrasea\Model\Repositories\StoryWZRepository;
use Alchemy\Phrasea\Model\Serializer\CaptionSerializer;
use Alchemy\Phrasea\Out\Module\PDFCgu;
use Assert\Assertion;
use Doctrine\DBAL\Connection;
use Symfony\Component\Filesystem\Filesystem;
@@ -418,6 +419,7 @@ class set_export extends set_abstract
$files[$id] = [
'base_id' => $download_element->getBaseId(),
'databox_id' => $download_element->getDataboxId(),
'record_id' => $download_element->getRecordId(),
'original_name' => '',
'export_name' => '',
@@ -691,6 +693,13 @@ class set_export extends set_abstract
$toRemove = [];
$archiveFiles = [];
$databoxIds = [];
$recordIdsPerDatabox = [];
// group recordId per databoxId
foreach ($files as $file) {
$recordIdsPerDatabox[$file['databox_id']][] = $file['record_id'];
}
foreach ($files as $record) {
if (isset($record["subdefs"])) {
@@ -709,6 +718,28 @@ class set_export extends set_abstract
}
$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"?>
<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>
<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>
@@ -9,9 +9,9 @@
<trans-unit id="96f0767cb7ea65a7f86c8c9432e80d16cf9d8680" resname="Please provide the same passwords." approved="yes">
<source>Please provide the same passwords.</source>
<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="36">Form/Login/PhraseaRenewPasswordForm.php</jms:reference-file>
<jms:reference-file line="49">Form/Login/PhraseaRegisterForm.php</jms:reference-file>
</trans-unit>
<trans-unit id="90b8c9717bb7ed061dbf20fe1986c8b8593d43d4" resname="The token provided is not valid anymore" approved="yes">
<source>The token provided is not valid anymore</source>

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:jms="urn:jms:translation" version="1.2">
<file date="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>
<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>
@@ -9,9 +9,9 @@
<trans-unit id="96f0767cb7ea65a7f86c8c9432e80d16cf9d8680" resname="Please provide the same passwords." approved="yes">
<source>Please provide the same passwords.</source>
<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="36">Form/Login/PhraseaRenewPasswordForm.php</jms:reference-file>
<jms:reference-file line="49">Form/Login/PhraseaRegisterForm.php</jms:reference-file>
</trans-unit>
<trans-unit id="90b8c9717bb7ed061dbf20fe1986c8b8593d43d4" resname="The token provided is not valid anymore" approved="yes">
<source>The token provided is not valid anymore</source>

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:jms="urn:jms:translation" version="1.2">
<file date="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>
<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>
@@ -9,9 +9,9 @@
<trans-unit id="96f0767cb7ea65a7f86c8c9432e80d16cf9d8680" resname="Please provide the same passwords." approved="yes">
<source>Please provide the same passwords.</source>
<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="36">Form/Login/PhraseaRenewPasswordForm.php</jms:reference-file>
<jms:reference-file line="49">Form/Login/PhraseaRegisterForm.php</jms:reference-file>
</trans-unit>
<trans-unit id="90b8c9717bb7ed061dbf20fe1986c8b8593d43d4" resname="The token provided is not valid anymore" approved="yes">
<source>The token provided is not valid anymore</source>

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:jms="urn:jms:translation" version="1.2">
<file date="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>
<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>
@@ -9,9 +9,9 @@
<trans-unit id="96f0767cb7ea65a7f86c8c9432e80d16cf9d8680" resname="Please provide the same passwords.">
<source>Please provide the same passwords.</source>
<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="36">Form/Login/PhraseaRenewPasswordForm.php</jms:reference-file>
<jms:reference-file line="49">Form/Login/PhraseaRegisterForm.php</jms:reference-file>
</trans-unit>
<trans-unit id="90b8c9717bb7ed061dbf20fe1986c8b8593d43d4" resname="The token provided is not valid anymore">
<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 {
float: right;
position: absolute;
right: 10px;
}
form[name=registerForm] .multiselect-group {
@@ -1157,7 +1157,7 @@ h1, h2, h3, h4 {
border-radius:10px;
display:inline-block;
position: relative;
top: 0.35rem;
top: -15px;
}
#updatingDemand .icon.active {

View File

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

View File

@@ -916,9 +916,9 @@
</div>
<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 style="padding-top: 0px; padding-left: 5px;">
<div style="padding-top: 6px; padding-left: 5px;">
<span id="SPANTITLE"> </span>
<br/>
<span id="PREVIEWTITLE_COLLNAME"> </span>
@@ -928,7 +928,7 @@
</div>
</div>
<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="PREVIEWCURRENT" class="ui-corner-bottom PNB10" style="top:11px;height:116px;bottom:auto;">
<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>
</button>
</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>
</div>

View File

@@ -88,6 +88,7 @@ class DoDownloadTest extends \PhraseanetAuthenticatedWebTestCase
'files' => [
[
'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(),
'export_name' => 'my_downloads',
'original_name' => '0470',
@@ -133,6 +134,7 @@ class DoDownloadTest extends \PhraseanetAuthenticatedWebTestCase
'files' => [
[
'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(),
'export_name' => 'my_downloads',
'original_name' => '0470',
@@ -151,6 +153,7 @@ class DoDownloadTest extends \PhraseanetAuthenticatedWebTestCase
],
[
'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(),
'export_name' => 'my_downloads2',
'original_name' => '0471',
@@ -297,6 +300,7 @@ class DoDownloadTest extends \PhraseanetAuthenticatedWebTestCase
'files' => [
[
'base_id' => 1,
'databox_id' => 1,
'record_id' => 1,
'export_name' => 'my_downloads',
'original_name' => '0470',
@@ -315,6 +319,7 @@ class DoDownloadTest extends \PhraseanetAuthenticatedWebTestCase
],
[
'base_id' => 1,
'databox_id' => 1,
'record_id' => 1,
'export_name' => 'my_downloads2',
'original_name' => '0471',

View File

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

View File

@@ -19,7 +19,7 @@ class TokenManipulatorTest extends \PhraseanetTestCase
{
$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);
$this->assertSame($user, $token->getUser());
@@ -42,7 +42,7 @@ class TokenManipulatorTest extends \PhraseanetTestCase
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');
$token = $manipulator->createBasketValidationToken(self::$DI['basket_4'], self::$DI['user_1'], $expire);
@@ -54,7 +54,7 @@ class TokenManipulatorTest extends \PhraseanetTestCase
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);
$this->assertSame(self::$DI['basket_4']->getId(), $token->getData());
@@ -65,14 +65,14 @@ class TokenManipulatorTest extends \PhraseanetTestCase
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.');
$manipulator->createBasketValidationToken(self::$DI['basket_1'], self::$DI['user_1'], null);
}
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']);
$this->assertSame(self::$DI['basket_4']->getId(), $token->getData());
@@ -83,7 +83,7 @@ class TokenManipulatorTest extends \PhraseanetTestCase
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']);
$this->assertSame(self::$DI['feed_public_entry']->getId(), $token->getData());
@@ -95,19 +95,19 @@ class TokenManipulatorTest extends \PhraseanetTestCase
public function testCreateDownloadToken()
{
$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);
$this->assertSame($data, $token->getData());
$this->assertSame(self::$DI['user'], $token->getUser());
$this->assertSame(TokenManipulator::TYPE_DOWNLOAD, $token->getType());
$this->assertDateNear('+3 hours', $token->getExpiration());
$this->assertDateNear('+24 hours', $token->getExpiration());
}
public function testCreateEmailExportToken()
{
$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);
$this->assertSame($data, $token->getData());
@@ -118,7 +118,7 @@ class TokenManipulatorTest extends \PhraseanetTestCase
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');
$this->assertSame('newemail@phraseanet.com', $token->getData());
@@ -129,7 +129,7 @@ class TokenManipulatorTest extends \PhraseanetTestCase
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']);
$this->assertNull($token->getData());
@@ -140,7 +140,7 @@ class TokenManipulatorTest extends \PhraseanetTestCase
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']);
$this->assertNull($token->getData());
@@ -160,7 +160,7 @@ class TokenManipulatorTest extends \PhraseanetTestCase
$em->expects($this->once())
->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);
}
@@ -175,7 +175,7 @@ class TokenManipulatorTest extends \PhraseanetTestCase
$em->expects($this->once())
->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);
}
@@ -183,7 +183,7 @@ class TokenManipulatorTest extends \PhraseanetTestCase
{
$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();
$this->assertCount(3, self::$DI['app']['repo.tokens']->findAll());