Compare commits

...
Sign in to create a new pull request.

138 commits

Author SHA1 Message Date
Noah Lancien
5a15a5713f Merge branch 'trunk' 2025-12-01 09:12:22 +01:00
Noah Lancien
324841b778 Merge branch 'develop' into trunk 2025-12-01 09:11:52 +01:00
noah.lancien
42faaf51bb Merge branch '28-code-article' into 'develop'
fix: adding an article code to a media was broken #28

See merge request ldc-web/projets/dam!10
2025-12-01 08:09:29 +00:00
Noah Lancien
6f5a7dae69 fix: remove useless parts from previous commit 2025-11-28 17:31:54 +01:00
Noah Lancien
0f74b496ab fix: adding an article code to a media was broken #28 2025-11-28 17:20:03 +01:00
Vincent BRIDIER
286d3adc6b Merge branch 'trunk' 2025-11-19 15:07:56 +01:00
Vincent BRIDIER
748597277e Merge branch 'develop' into trunk 2025-11-19 15:07:48 +01:00
Vincent BRIDIER
20e5a325ff fix: some fixes 2025-11-19 15:07:39 +01:00
Vincent BRIDIER
ea692f98ac Merge branch 'trunk' 2025-11-19 15:03:03 +01:00
Vincent BRIDIER
931c53d76a Merge branch 'develop' into trunk 2025-11-19 15:02:55 +01:00
Vincent BRIDIER
a73bcc2b4b fix: some fixes 2025-11-19 15:02:45 +01:00
Vincent BRIDIER
05e1d60159 Merge branch 'trunk' 2025-11-19 14:28:31 +01:00
Vincent BRIDIER
9c2a3fb82e Merge branch 'develop' into trunk 2025-11-19 14:28:22 +01:00
Vincent BRIDIER
6c90845c4c fix: some fixes 2025-11-19 14:27:59 +01:00
Vincent BRIDIER
4976f1cac9 Merge branch 'block-arcadia-link-for-drafts' into 'develop'
feat: block arcadia link request when the media is in draft status

See merge request ldc-web/projets/dam!9
2025-11-19 13:20:20 +00:00
Noah Lancien
116f465849 feat: block arcadia link request when the media is in draft status 2025-11-19 11:20:01 +01:00
Vincent BRIDIER
840c3ea475
fix: Fix media link on ArcadiaLink 2025-11-17 17:31:56 +01:00
Vincent BRIDIER
7a9c8f4da3
fix: Fix xml generation on ArcadiaLink 2025-11-17 17:06:38 +01:00
Vincent BRIDIER
dbb92dd6ed
fix: Fix variante field - Close #27 2025-11-17 16:00:34 +01:00
Vincent BRIDIER
23147b209e Merge branch '24-alphabetical-order-for-user-list' into 'develop'
feat: order by alphabetical ASC the user list in delete page

See merge request ldc-web/projets/dam!7
2025-10-28 12:06:40 +00:00
Noah Lancien
763ace7667 feat: order by alphabetical ASC the user list in delete page 2025-10-28 09:49:12 +01:00
Vincent BRIDIER
241adfeb50
Merge branch 'trunk' 2025-10-27 16:08:03 +01:00
Vincent BRIDIER
9f3b536bf7
Merge branch 'develop' into trunk 2025-10-27 16:07:54 +01:00
Vincent BRIDIER
b6338d4574
fix: Fix folders regressions in controller 2025-10-27 16:07:44 +01:00
Vincent BRIDIER
485e66c137
Merge branch 'trunk' 2025-10-24 12:25:26 +02:00
Vincent BRIDIER
ac9dbc3aad
Merge branch 'develop' into trunk 2025-10-24 12:25:07 +02:00
Vincent BRIDIER
3d1e08c664
Merge branch 'develop' of lab.s-web.fr:ldc-web/projets/dam into develop 2025-10-24 12:22:53 +02:00
Vincent BRIDIER
1a64f7b0d2
feat: Remove ldc web status checker deps + composer update 2025-10-24 12:15:28 +02:00
Vincent BRIDIER
f3c97443a6
feat: Add auth bypass feature for local dev 2025-10-24 12:15:01 +02:00
Vincent BRIDIER
5a25b0ba21
fix: Fix Media model event triggering 2025-10-24 12:06:58 +02:00
Vincent BRIDIER
b7392ec487
fix: Fix arcadia:import command 2025-10-24 11:19:49 +02:00
Noah Lancien
43ae1cb818 Merge branch 'trunk' 2025-06-04 15:18:47 +02:00
Noah Lancien
3b8cb4c5ae Merge branch 'develop' into trunk 2025-05-07 14:42:28 +02:00
noah.lancien
82368e2dd3 Merge branch 're7-feedback' into 'develop'
fix: folder filter now reset when the subject is updated

See merge request ldc-web/projets/dam!6
2025-05-07 12:41:56 +00:00
Noah Lancien
925dc2a495 fix: folder filter now reset when the subject is updated 2025-05-07 12:40:45 +02:00
Noah Lancien
b1763e5db3 Merge branch 'develop' into trunk 2025-05-06 12:08:43 +02:00
noah.lancien
e0870d362e Merge branch 'multiple-fixes' into 'develop'
Issues 6,7,8,9,10,12,13

See merge request ldc-web/projets/dam!4
2025-05-06 10:07:55 +00:00
Noah Lancien
0984fa00b7 fix: many fixes from develop branch + new re7 feedbacks 2025-05-06 12:05:18 +02:00
Jérémy Munsch
ad00dd2246 Reset Media route resolv using uid (besoin métier) vu avec Vincent 2025-05-06 11:10:16 +02:00
Jérémy Munsch
3dce58d9a0 Fixes #12 Fixed MediaController::esFilters arcadia_id fetch from filters issue 2025-05-06 11:10:16 +02:00
Jérémy Munsch
0194d8e0c1 Fix Media duplicate
- put in transaction
- set arcadia_id to null
2025-05-06 11:10:13 +02:00
Jérémy Munsch
14aeec5e2a Fixes #10
- Remove explicition resolve for media in RouteServicePRovider
- Set usage of media id on media routes everywhere
2025-05-06 11:02:02 +02:00
Jérémy Munsch
eb212215a8 Fixes #9
- add FK on arcadia_links to media.id and remove orphans
- Refactored ArcadiaLinkController
- Added comments on ArcadiaLink model
- refactored arcadialinks.index
2025-05-06 11:02:02 +02:00
Jérémy Munsch
c5d8ea542f Add View service provider (prepare to bump slowly) 2025-05-06 11:02:02 +02:00
Jérémy Munsch
50832db21b Fixes #8+
Enums arent using BaseEnum trait and cannot since tool low php level, need to manually implement behaviors
2025-05-06 11:02:02 +02:00
Jérémy Munsch
c6eaf2f12b MediaController refactor getMediaOtherInfos 2025-05-06 11:02:02 +02:00
Jérémy Munsch
8651a5cc2a Fixes #7,#13 REfactor BasektController + Flush cache before querying basket media list 2025-05-06 11:02:00 +02:00
Jérémy Munsch
bc4a8f64ae Fixes #6 2025-05-06 11:01:28 +02:00
Jérémy Munsch
d62b1d513f UploadController fix Media query 2025-05-06 11:01:26 +02:00
Jérémy Munsch
f6dc577255 Add TODO in config/roles 2025-05-06 11:00:15 +02:00
Jérémy Munsch
80615fe61a Add Model User comments 2025-05-06 11:00:15 +02:00
Jérémy Munsch
92d837586e MediaController::loadMedia refactor 2025-05-06 11:00:14 +02:00
Jérémy Munsch
e18918fa61 Dossier model refactor getMediacountAttribute 2025-05-06 10:59:23 +02:00
Jérémy Munsch
156c4a6317 DossierController refactor with typehint 2025-05-06 10:59:23 +02:00
Jérémy Munsch
20c1d2da6f .gitignore update 2025-05-06 10:59:23 +02:00
Jérémy Munsch
d0778093d5 npm update 2025-05-06 10:59:23 +02:00
Jérémy Munsch
39a28c5e3f composer update 2025-05-06 10:59:23 +02:00
Jérémy Munsch
d41acba6c5 add name to package json 2025-05-06 10:59:23 +02:00
Jérémy Munsch
c9be7205be update client api + add .gitmodules 2025-05-06 10:59:23 +02:00
Noah Lancien
d34a9a4624 Merge branch 'develop' into trunk 2025-03-25 16:18:55 +01:00
noah.lancien
29f8dfd724 Merge branch 'issue-20-re7-feedback' into 'develop'
fix: many bug fixes - #20

See merge request ldc-web/projets/dam!5
2025-03-25 15:17:09 +00:00
Noah Lancien
58287ae7bc Merge branch 'develop' into trunk 2025-03-25 16:17:00 +01:00
Noah Lancien
98cbbe5b93 fix: review feedbacks 2025-03-25 15:23:44 +01:00
Noah Lancien
e39f4e8146 feat: added a link to media manager in the home page 2025-03-25 11:24:59 +01:00
Noah Lancien
caa7189cc3 fix: many bug fixes - #20 2025-03-25 11:00:14 +01:00
Vincent BRIDIER
764044384a Fix issue templates dir name 2023-11-24 11:05:14 +00:00
Vincent BRIDIER
daafb0c314 Add issue templates 2023-11-24 11:02:28 +00:00
Vincent BRIDIER
42f28abe33
Merge branch 'develop' into trunk 2023-11-24 09:42:12 +01:00
Vincent BRIDIER
6cce9a4449 Merge into 'develop'
Full rewrite DAM + API DAM + CLIENT

See merge request ldc-web/projets/dam!2
2023-11-24 08:27:41 +00:00
Vincent BRIDIER
2a898c3816
Remove sanctum 2023-11-10 16:31:11 +01:00
Vincent BRIDIER
a96fa57b85
NPM audit fix 2023-11-10 11:22:27 +01:00
Vincent BRIDIER
347a83d502
Composer update 2023-11-10 11:20:43 +01:00
Vincent BRIDIER
84e05664c5
Merge branch 'develop' into fckApi 2023-11-10 11:13:33 +01:00
Sebastien Hattry
1d8e8994bb Change dest mail 2023-06-29 07:32:26 +00:00
Jérémy Munsch
e6bcdcb8af
Merge branch 'hotfix' into develop 2022-10-26 13:00:29 +02:00
Jérémy Munsch
d343d87486
Merge branch 'hotfix' 2022-10-26 12:59:57 +02:00
Jérémy Munsch
5926c23a04
Allow contact list to be an array 2022-10-26 12:59:45 +02:00
Jérémy Munsch
9dfc4bf7e1
Merge branch 'hotfix' into develop 2022-10-26 12:47:17 +02:00
Jérémy Munsch
592bd9bdaa
Merge branch 'hotfix' 2022-10-26 12:45:49 +02:00
Jérémy Munsch
4e19c1c408
mail config MAIL_LIST_INVALID_XML 2022-10-26 12:45:23 +02:00
Jérémy Munsch
694eab00d0
add client as submodule 2022-08-01 17:46:36 +02:00
Jérémy Munsch
6bcfc66a9d
Add missing thumbs to api media list 2022-08-01 17:38:13 +02:00
Jérémy Munsch
d5edbebbdf
redis caching for used models on api 2022-08-01 16:27:22 +02:00
Jérémy Munsch
b06642b1bf
Token regen 2022-08-01 10:59:56 +02:00
Jérémy Munsch
f63220ff2a
Client generartion 2022-07-29 15:19:30 +02:00
Jérémy Munsch
5e14218ad4
Api + unit tests 2022-07-29 13:12:34 +02:00
Jérémy Munsch
aef61a6c8f
Media : Fix thumbnail generation 2022-07-28 12:21:06 +02:00
Jérémy Munsch
d2236fe041
Fixed upload system 2022-07-28 12:21:02 +02:00
Jérémy Munsch
ba2b9dcde2
Fix media sync with arcadia on update only if linked, check before link that is not already linked. 2022-07-27 16:57:05 +02:00
Jérémy Munsch
e7f5a7e6cd
Fix update media request missing articles + enum validate 2022-07-27 16:35:18 +02:00
Jérémy Munsch
14ff7b9413
Fix isLocked to be boolean 2022-07-27 16:30:03 +02:00
Jérémy Munsch
1f988cab3a
Change Media statut into Enum 2022-07-26 13:20:21 +02:00
Jérémy Munsch
f657e39a2e
Fix update media articles 2022-07-26 11:57:16 +02:00
Jérémy Munsch
7166d9a097
Token Crud on console + Mail on create and destroy 2022-07-26 10:39:55 +02:00
Jérémy Munsch
28f95934ed
Fix user marques relation 2022-07-25 13:09:46 +02:00
Jérémy Munsch
34a589307e
fir selector list freaking JS 2022-07-25 13:06:09 +02:00
Jérémy Munsch
3e4484ab71
npm bump deps 2022-07-25 12:57:53 +02:00
Jérémy Munsch
916c49012a
update composer deps 2022-07-25 12:49:18 +02:00
Jérémy Munsch
58a848524f
simplify more code 2022-07-25 12:21:18 +02:00
Jérémy Munsch
e107cfd337
simplify esFolderSearch 2022-07-25 12:11:35 +02:00
Jérémy Munsch
50a5aa5604
Changed event history to type 2022-07-25 12:08:16 +02:00
Jérémy Munsch
01cef75e0c
Remove TODO Supprimer old fichier dans le cas d'un update alrready done 2022-07-25 12:08:15 +02:00
Jérémy Munsch
ae30f926bb
Done TODO: delete all subject file type for a media on delete 2022-07-25 12:08:15 +02:00
Jérémy Munsch
914ab4c5f9
Fix: download media public 2022-07-25 12:08:15 +02:00
Jérémy Munsch
7085ec1aaa
Reorder route things + bind models for some routes 2022-07-25 12:08:11 +02:00
Jérémy Munsch
9767e62c72
Update NPM deps and webpack.mix.js
composer require ldc/web-status-checker-laravel remove Old static status checker

Fix ImportMedia

WIP commands OK

many fixes

Fix and try to optimize folder component

Fix front icons + actions based on icons

Favico

front fix alert

Fix Media upload on depot

fix folder component buttons

Full front fix with perf due tu vuejs

Download medias

Navigate within medias superfix

Move media + loader

isLocked boolean and remove media fix

Folder scroll fix

arcadia link color red

Fix panier show external

Tons of fixes back as front

REwrited all
2022-07-22 17:00:48 +02:00
Vincent BRIDIER
bb38b9e400 Merge bump into 'develop' 2022-06-20 12:07:52 +00:00
Vincent BRIDIER
44a6f6b7ed
Merge branch 'trunk' 2022-04-20 17:34:32 +02:00
Vincent BRIDIER
f6e0e96e62
Merge branch 'develop' into trunk 2022-04-20 17:34:19 +02:00
Vincent BRIDIER
e2b70f3e6f
Merge branch 'trunk' 2022-04-20 16:54:04 +02:00
Vincent BRIDIER
1fd1ee1317
Merge branch 'develop' into trunk 2022-04-20 16:53:46 +02:00
Vincent BRIDIER
9631a9eb2f
Merge branch 'trunk' 2022-04-20 16:06:56 +02:00
Vincent BRIDIER
b9814687c0
Merge branch 'develop' into trunk 2022-04-20 16:06:37 +02:00
Vincent BRIDIER
43fcd9e09f
Merge branch 'trunk' 2022-04-20 15:20:21 +02:00
Vincent BRIDIER
8fde9fb434
Merge branch 'develop' into trunk 2022-04-20 15:19:56 +02:00
Vincent BRIDIER
d91f4f4d3c
Merge branch 'trunk' 2022-04-07 09:16:44 +02:00
Vincent BRIDIER
5aaf5c642b
merge develop 2022-04-07 09:16:14 +02:00
Vincent BRIDIER
8b5ea3735c
Merge branch 'develop' into trunk 2022-04-01 12:16:05 +02:00
Vincent BRIDIER
2467e01e0e
Merge branch 'develop' into trunk 2022-03-31 12:43:03 +02:00
Vincent BRIDIER
f3423f4c89
Merge branch 'develop' into trunk 2022-03-31 11:42:21 +02:00
Vincent BRIDIER
9a35d7a5b6
Merge branch 'develop' into trunk 2022-03-28 12:38:05 +02:00
Vincent BRIDIER
4f7de039e8
Merge branch 'develop' into trunk 2022-03-28 11:40:19 +02:00
Vincent BRIDIER
99df59ce23
Merge branch 'develop' into trunk 2022-03-28 10:54:30 +02:00
Vincent BRIDIER
8bcf95011a Merge branch 'develop' into trunk 2022-02-15 10:25:48 +01:00
Olivier Legat
ef798a07ae Merge branch 'develop' into trunk 2022-01-24 17:07:44 +01:00
Vincent BRIDIER
1b5f49d979 Merge branch 'trunk' 2021-11-09 16:42:33 +01:00
Vincent BRIDIER
4b3f3ea948 Merge branch 'develop' into trunk 2021-11-09 16:42:17 +01:00
Vincent BRIDIER
6039963868 Merge branch 'trunk' 2021-09-28 10:02:40 +02:00
Vincent BRIDIER
5cf9e286eb Merge branch 'develop' into trunk 2021-09-28 10:01:43 +02:00
Olivier Legat
3b20915c4f Merge branch 'trunk' 2021-09-01 11:57:24 +02:00
Olivier Legat
82267c1b9d Merge branch 'develop' into trunk 2021-09-01 11:56:41 +02:00
Olivier Legat
d7469ef5c6 Merge branch 'trunk' 2021-07-19 09:05:24 +02:00
Olivier Legat
b46841a024 Merge branch 'develop' into trunk 2021-07-19 09:05:10 +02:00
Olivier Legat
4ae3f4f70f Merge branch 'trunk' 2021-07-13 14:40:17 +02:00
Olivier Legat
8691c45231 Merge branch 'trunk' 2021-07-09 13:19:36 +02:00
Olivier Legat
f8ea161878 Merge branch 'trunk' 2021-07-09 10:35:07 +02:00
Olivier Legat
50f1da22e6 Merge branch 'trunk' 2021-06-17 16:43:58 +02:00
Olivier Legat
89973ff2a0 Merge branch 'trunk' 2021-06-17 16:35:42 +02:00
301 changed files with 24033 additions and 27985 deletions

View file

@ -60,4 +60,4 @@ MAIL_LIST_SERVICEWEB=
PROJECT_UUID=
## MAIL ADDRESSES
MAIL_LIST_SERVICEWEB=equipe.web@ldc.fr
MAIL_LIST_SERVICEWEB=digital@ldc.fr

60
.env.testing Normal file
View file

@ -0,0 +1,60 @@
APP_NAME=Dam
APP_ENV=local
APP_KEY=base64:2fKdUeMKBwyKzJeOcIaRXC0yujSMKkQJ98XLhC2uqf0=
APP_DEBUG=true
APP_URL=https://dam.s-web.test
PROJECT_UUID=
LOG_CHANNEL=stack
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=debug
DB_CONNECTION=mysql
DB_HOST=dev-mariadb
DB_PORT=3306
DB_DATABASE=testing_dam
DB_USERNAME=root
DB_PASSWORD=secret
ELASTICSEARCH_HOSTS=dev-elasticsearch:9200
BROADCAST_DRIVER=log
CACHE_DRIVER=file
FILESYSTEM_DISK=local
QUEUE_CONNECTION=sync
SESSION_DRIVER=file
SESSION_LIFETIME=120
MEMCACHED_HOST=127.0.0.1
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
MAIL_MAILER=smtp
MAIL_HOST=dev-mailhog
MAIL_PORT=1025
MAIL_USERNAME=
MAIL_PASSWORD=
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS=no-reply@dam.lesidc.com
MAIL_FROM_NAME="${APP_NAME}"
MAIL_ADMIN=digital@ldc.fr
LDAP_ACCOUNT_PREFIX="cn="
LDAP_ACCOUNT_SUFFIX=",ou=users,dc=example,dc=org"
LDAP_HOSTS=dev-openldap
LDAP_PORT=1389
LDAP_BASE_DN="dc=example,dc=org"
LDAP_USERNAME="cn=admin,dc=example,dc=org"
LDAP_PASSWORD=adminpassword
LDAP_USE_SSL=false
PROJECT_UUID=
## MAIL ADDRESSES
MAIL_LIST_SERVICEWEB=digital@ldc.fr
LDC_STATUS_CHECKER_API_KEY=CE8S52Qq7stf5k8frF4T
LDC_STATUS_CHECKER_UUID=4d8b2c20-107a-4682-b591-dc6c685126df

60
.env.testing.example Normal file
View file

@ -0,0 +1,60 @@
APP_NAME=Dam
APP_ENV=local
APP_KEY=base64:2fKdUeMKBwyKzJeOcIaRXC0yujSMKkQJ98XLhC2uqf0=
APP_DEBUG=true
APP_URL=https://dam.s-web.test
PROJECT_UUID=
LOG_CHANNEL=stack
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=debug
DB_CONNECTION=mysql
DB_HOST=dev-mariadb
DB_PORT=3306
DB_DATABASE=testing_dam
DB_USERNAME=root
DB_PASSWORD=
ELASTICSEARCH_HOSTS=dev-elasticsearch:9200
BROADCAST_DRIVER=log
CACHE_DRIVER=file
FILESYSTEM_DISK=local
QUEUE_CONNECTION=sync
SESSION_DRIVER=file
SESSION_LIFETIME=120
MEMCACHED_HOST=127.0.0.1
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
MAIL_MAILER=smtp
MAIL_HOST=dev-mailhog
MAIL_PORT=1025
MAIL_USERNAME=
MAIL_PASSWORD=
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS=no-reply@dam.lesidc.com
MAIL_FROM_NAME="${APP_NAME}"
MAIL_ADMIN=digital@ldc.fr
LDAP_ACCOUNT_PREFIX=
LDAP_ACCOUNT_SUFFIX=
LDAP_HOSTS=
LDAP_PORT=
LDAP_BASE_DN=
LDAP_USERNAME=
LDAP_PASSWORD=
LDAP_USE_SSL=
PROJECT_UUID=
## MAIL ADDRESSES
MAIL_LIST_SERVICEWEB=digital@ldc.fr
LDC_STATUS_CHECKER_API_KEY=
LDC_STATUS_CHECKER_UUID=

3
.gitignore vendored
View file

@ -25,7 +25,7 @@ public/uploads/*
/storage/*.key
/vendor
.env
.env*
.phpunit.result.cache
Homestead.json
Homestead.yaml
@ -34,3 +34,4 @@ yarn-error.log
/.idea
/.vscode
*.sqlite*
auth.json

View file

@ -0,0 +1,11 @@
## Comportement constaté
## Comportement attendu
## Solution envisagée
## Étapes pour la reproduction du bug
*

View file

@ -0,0 +1,7 @@
## Présentation du besoin
## Implémentation
## Maquettes, documentations...

3
.gitmodules vendored Normal file
View file

@ -0,0 +1,3 @@
[submodule "clients/Api"]
path = clients/Api
url = ../dam-client.git

View file

@ -16,7 +16,8 @@
"titleBar.activeBackground": "#514403",
"titleBar.activeForeground": "#e7e7e7",
"titleBar.inactiveBackground": "#51440399",
"titleBar.inactiveForeground": "#e7e7e799"
"titleBar.inactiveForeground": "#e7e7e799",
"commandCenter.border": "#e7e7e799"
},
"peacock.remoteColor": "#514403",
"intelephense.environment.phpVersion": "8.1"

View file

@ -1,4 +1,10 @@
# DAM
# TODO:
- Remplace par VUE 3 + Runtime Only
- Typescript
- Faut-il exporter les stats Api avant de supprimer un ApiToken ?
## Installation
### - Default
```
@ -7,20 +13,10 @@ composer install
cp .env.example .env
php artisan key:generate
```
### - Stack en attendant le bump Laravel
### - Stack Laravel
```
npm install && npm run prod
composer install
```
Dans vendor/laravel/framework/src/Illuminate/Foundation/PackageManifest.php, ligne 116
```
//$packages = json_decode($this->files->get($path), true);
$installed = json_decode($this->files->get($path), true);
$packages = $installed['packages'] ?? $installed;
```
Puis
```
composer install
cp .env.example .env
php artisan key:generate
```
@ -29,9 +25,12 @@ php artisan key:generate
.env => remplacer MAIL_DRIVER par MAIL_MAILER
.env => remplacer FILESYSTEM_DRIVER par FILESYSTEM_DISK
.env rajouter PROJECT_UUID=
.env rajouter MAIL_LIST_SERVICEWEB=equipe.web@ldc.fr
.env rajouter MAIL_LIST_SERVICEWEB=digital@ldc.fr
# SUPERVISOR
jobs Api stats sur queue `database`
## Dans les LABs
```
MAIL_VERIFY_PEER=false
```
```

View file

@ -0,0 +1,225 @@
<?php
namespace App\Console\Commands\Api;
use App\Enums\Legacy\ApiError;
use App\Enums\Legacy\ApiStatus;
use App\Enums\MediaExtension;
use App\Exceptions\Api\ApiErrorException;
use App\Exceptions\Api\DamException;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Route;
use Symfony\Component\Process\Process;
/**
* @codeCoverageIgnore
*/
class GenerateApiClient extends Command
{
private const TARGET = 'clients/Api/src';
private const CLIENTNAMESPACE = 'LDC\\DAM\\Client';
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'api:generate-client';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Generate the dam client in clients/Api folder';
/**
* Execute the console command.
*
* @return integer
*/
public function handle()
{
$this->copyEnums();
$this->copyExceptions();
$this->copyRoutesList();
$this->copyApiConfigInfo();
return 0;
}
/**
* Copy config files
*
* @return void
*/
private function copyApiConfigInfo()
{
$this->info('Copy Api Config');
$rootTarget = app_path('../' . self::TARGET);
if (File::exists("$rootTarget/../apiConfig.readonly.json")) {
File::delete("$rootTarget/../apiConfig.readonly.json");
}
File::put(
"$rootTarget/../apiConfig.readonly.json",
\json_encode(config('api'), JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)
);
$this->phpcbfFile("$rootTarget/../apiConfig.readonly.json");
}
/**
* Copy routes into a json file
*
* @return void
*/
private function copyRoutesList()
{
$this->info('Copy Routes');
$routes = [];
/** @var \Illuminate\Routing\Route */
foreach (Route::getRoutes() as $route) {
if (\strpos($route->getPrefix(), 'api') !== false) {
$routes[$route->getName()] = [
'uri' => $route->uri(),
'methods' => $route->methods()
];
}
}
$rootTarget = app_path('../' . self::TARGET);
if (File::exists("$rootTarget/routes.json")) {
File::delete("$rootTarget/routes.json");
}
File::put(
"$rootTarget/routes.json",
\json_encode($routes, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)
);
$this->phpcbfFile("$rootTarget/routes.json");
}
/**
* Copy Exeption classes
*
* @return void
*/
private function copyExceptions(): void
{
$this->info('Copy Exceptions');
$rootTarget = app_path('../' . self::TARGET);
$this->copyToFolder([
DamException::class,
ApiErrorException::class
], 'Exceptions');
$this->replaceInPhpFile(
"$rootTarget/Exceptions/ApiErrorException.php",
ApiError::class,
self::CLIENTNAMESPACE . '\\Enums\\ApiError'
);
$this->replaceInPhpFile(
"$rootTarget/Exceptions/DamException.php",
DamException::class,
self::CLIENTNAMESPACE . '\\Exceptions\\DamException'
);
$this->replaceInPhpFile(
"$rootTarget/Exceptions/DamException.php",
ApiErrorException::class,
self::CLIENTNAMESPACE . '\\Exceptions\\ApiErrorException'
);
$this->phpcbfFile("$rootTarget/Exceptions/DamException.php");
$this->phpcbfFile("$rootTarget/Exceptions/ApiErrorException.php");
}
/**
* Copy enums classes
*
* @return void
*/
private function copyEnums(): void
{
$this->info('Copy Enums');
$classes = [
ApiError::class,
ApiStatus::class
];
$this->copyToFolder($classes, 'Enums');
$rootTarget = app_path('../' . self::TARGET);
foreach ($classes as $class) {
$classBasename = class_basename($class);
$dest = "$rootTarget/Enums/$classBasename.php";
$this->replaceInPhpFile($dest, 'BaseEnumRoutable', 'BaseEnum');
$this->replaceInPhpFile($dest, '\\Legacy', '');
$this->phpcbfFile($dest);
}
$mediaExtensionEnum = "<?php\n\nnamespace LDC\DAM\Client\Enums;\n\nuse Kwaadpepper\Enum\BaseEnum;\n\n/**\n";
foreach (MediaExtension::cases() as $strType) {
$mediaExtensionEnum .= " * @method static self {$strType->name}()\n";
}
// phpcs:ignore Generic.Files.LineLength.TooLong
$mediaExtensionEnum .= " *\n * @package DAMclient\n * @subpackage enums\n */\nclass MediaExtension extends BaseEnum\n{\n}\n";
File::put("$rootTarget/Enums/MediaExtension.php", $mediaExtensionEnum);
$this->phpcbfFile("$rootTarget/Enums/MediaExtensio.php");
}
/**
* Copy Classes to specified folder
*
* @param array $classes
* @param string $folder
* @return void
*/
private function copyToFolder(array $classes, string $folder): void
{
$rootTarget = app_path('../' . self::TARGET);
foreach ($classes as $class) {
$classFileName = \str_replace("App\\$folder\\", '', sprintf('%s.php', $class));
$classFileName = \str_replace('\\', '/', $classFileName);
$classBasename = class_basename($class);
$source = \app_path("$folder/$classFileName");
$dest = "$rootTarget/$folder/$classBasename.php";
if (File::exists($dest)) {
File::delete($dest);
}
File::copy($source, $dest);
$this->replaceInPhpFile($dest, 'namespace App', 'namespace ' . self::CLIENTNAMESPACE);
}
}
/**
* Replaces a specific string with another on a file
*
* @param string $source
* @param string $search
* @param string $replace
* @return void
*/
private function replaceInPhpFile(string $source, string $search, string $replace): void
{
$fContent = File::get($source);
// Remove trailing \Api .
$regex = '/namespace ([a-zA-Z0-9\\\\]+)\\\\Api;/m';
$fContent = preg_replace($regex, 'namespace $1;', $fContent);
// Replace namespace top level.
$pos = strpos($fContent, $search);
if ($pos !== false) {
File::put($source, \str_replace($search, $replace, $fContent));
}
}
/**
* Lint the file
*
* @param string $path
* @return void
* @throws \Symfony\Component\Process\Exception\RuntimeException If anything goes wrong.
* @throws \Symfony\Component\Process\Exception\ProcessTimedOutException If anything goes wrong.
* @throws \Symfony\Component\Process\Exception\ProcessSignaledException If anything goes wrong.
* @throws \Symfony\Component\Process\Exception\LogicException If anything goes wrong.
*/
private function phpcbfFile(string $path): void
{
$process = new Process(['./vendor/bin/phpcbf', $path]);
$process->run();
}
}

View file

@ -0,0 +1,184 @@
<?php
namespace App\Console\Commands\Api;
use App\Mail\Api\ApiTokenCreated;
use App\Mail\Api\ApiTokenRegenerated;
use App\Mail\Api\ApiTokenRemoved;
use App\Models\Api\ApiToken;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Mail;
class TokenCrud extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'api:token
{action? : Can be list, add, remove, regen, default is list}
{api_token? : Only if action is remove, pass the ID to remove}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Manage api tokens';
/**
* Execute the console command.
*
* @return inteager
*/
public function handle(): int
{
switch ($this->argument('action')) {
case null:
case 'list':
$this->table(['id', 'name', 'token_date'], ApiToken::all()->map(function (ApiToken $apiToken) {
return [
'id' => $apiToken->id,
'name' => $apiToken->name,
'token_date' => $apiToken->token_date,
];
}));
return 0;
case 'add':
return $this->createApiToken();
case 'remove':
return $this->removeApiToken();
case 'regen':
return $this->regenApiToken();
default:
$this->error(\sprintf(
'action \'%s\' is not availaible, use --help to see usage.',
$this->argument('action')
));
return -1;
} //end switch
}
/**
* Create api Token
*
* @return integer
*/
private function createApiToken(): int
{
return DB::transaction(function () {
$apiToken = new ApiToken();
do {
if (!($apiToken->name = $this->ask('Give your token a name'))) {
$this->warn('Please provide a valid name.');
}
} while (!$apiToken->name);
$publicToken = $apiToken->generatePublicToken();
$privateToken = $apiToken->generatePrivateToken();
if (
!$this->confirm(
"Are you should you want to create the following '{$apiToken->name}'"
)
) {
$this->info('Nothing was created.');
return 0;
}
$apiToken->saveOrFail();
Mail::send(new ApiTokenCreated($apiToken));
$this->alert("
Your new api Token for '{$apiToken->id}' named '{$apiToken->name}'
public: {$publicToken}
private: {$privateToken}
Please take note as it wont be available to display anymore !
");
return 0;
});
}
/**
* Remove Api token
*
* @return integer
*/
private function removeApiToken(): int
{
return DB::transaction(function () {
if (!\is_numeric($apiTokenId = $this->argument('api_token'))) {
$this->error('remove command needs an id, use `list` to see all.');
return -1;
}
/** @var \App\Models\Api\ApiToken|null */
if (!($apiToken = ApiToken::where('id', $apiTokenId)->first())) {
$this->error("api_token '{$apiTokenId}' was not found");
return -1;
}
if (
!$this->confirm(
"Are you sur you want to remove apiToken '{$apiToken->id}' '{$apiToken->name}' ?"
)
) {
$this->info('Nothing was deleted.');
return 0;
}
if (!$apiToken->deleteOrFail()) {
$this->error('removing apiToken failed');
}
Mail::send(new ApiTokenRemoved($apiToken));
$this->alert("apiToken '{$apiToken->id}' '{$apiToken->name}' has been removed !");
return 0;
});
}
/**
* Regenerate Api token
*
* @return integer
*/
private function regenApiToken(): int
{
return DB::transaction(function () {
if (!\is_numeric($apiTokenId = $this->argument('api_token'))) {
$this->error('remove command needs an id, use `list` to see all.');
return -1;
}
/** @var \App\Models\Api\ApiToken|null */
if (!($apiToken = ApiToken::where('id', $apiTokenId)->first())) {
$this->error("api_token '{$apiTokenId}' was not found");
return -1;
}
$publicToken = $apiToken->generatePublicToken();
$privateToken = $apiToken->generatePrivateToken();
if (
!$this->confirm(
"Are you sur you want to regen apiToken '{$apiToken->id}' '{$apiToken->name}' ?"
)
) {
$this->info('Nothing was done.');
return 0;
}
$apiToken->saveOrFail();
Mail::send(new ApiTokenRegenerated($apiToken));
$this->alert("
Your new api Token for '{$apiToken->id}' named '{$apiToken->name}'
public: {$publicToken}
private: {$privateToken}
Please take note as it wont be available to display anymore !
");
return 0;
});
}
}

View file

@ -19,7 +19,7 @@ class ArcadiaLinkCommand extends Command
*
* @var string
*/
protected $description = 'Lecture du flux Arcadia entrant (medias). Run the XmlArcadiaMediasIn manually';
protected $description = 'Lecture du flux Arcadia entrant (media). Run the XmlArcadiaMediasIn manually';
/**
* Execute the console command.
@ -34,7 +34,7 @@ class ArcadiaLinkCommand extends Command
$job->console = $this;
$job->handle();
$this->line("");
$this->newLine();
$this->info('Fin du traitement');
}
}

View file

@ -5,8 +5,6 @@ namespace App\Console\Commands;
use App\Models\Media;
use App\Models\Sujet;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
class DeleteMediaArchive extends Command
{
@ -15,7 +13,7 @@ class DeleteMediaArchive extends Command
*
* @var string
*/
protected $signature = 'medias:deletearchive';
protected $signature = 'media:deletearchive';
/**
* The console command description.
@ -35,75 +33,59 @@ class DeleteMediaArchive extends Command
//phpcs:ignore Generic.Files.LineLength.TooLong
$this->info('Vous allez supprimer définitivement tous les médias (et leurs fichiers) qui ont été supprimés (en soft delete).');
$choice = $this->choice('Etes-vous sûr de vouloir lancer ce traitement ?', ['y', 'n'], 'y');
if ($choice != "y") {
$this->error('Choix incorrect');
die();
if (!$this->confirm('Etes-vous sûr de vouloir lancer ce traitement ?')) {
return 0;
}
$sujets = Sujet::where('sujet_dam', 1)->get();
/** @var \Illuminate\Database\Eloquent\Collection<\App\Models\Sujet> */
$subjects = Sujet::where('sujet_dam', true)->get();
$this->line("");
$this->line("Liste des sujets disponibles");
$this->line("----------------------------");
foreach ($sujets as $key => $sujet) {
$this->line("[" . $sujet->codeSujet . "] " . $sujet->libelle);
$this->newLine();
$this->line('Liste des sujets disponibles');
$this->line('----------------------------');
$this->line('[0] Tous les sujets');
foreach ($subjects as $subject) {
$this->line("[{$subject->codeSujet}] {$subject->libelle}");
}
$this->line("[0] Tous les sujets");
$sujetId = $this->ask('Pour quel sujet de média souhaitez vous executer le traitement ?');
$sujetId = $this->askWithCompletion(
'Pour quel sujet de média souhaitez vous executer le traitement ?',
$subjects->pluck('codeSujet')->toArray(),
0
);
if ($sujetId == "") {
$this->newLine();
if ($sujetId != 0 and !$subjects->pluck('codeSujet')->contains(\intval($sujetId))) {
$this->error('Id de sujet incorrect');
die();
return -1;
}
if ($sujetId == 0) {
$medias = Media::onlyTrashed()->with('Sujet.formats')->get();
} else {
$medias = Media::onlyTrashed()->where('sujet_id', $sujetId)->with('Sujet.formats')->get();
}
$sujetId = intval($sujetId);
if ($medias->count() == 0) {
$mediaCollection = Media::onlyTrashed()->with('sujet.formats');
if ($sujetId !== 0) {
$mediaCollection->where('sujet_id', $sujetId);
}
/** @var \Illuminate\Database\Eloquent\Collection<\App\Models\Media> */
$mediaCollection = $mediaCollection->get();
if (!$mediaCollection->count()) {
$this->error('Aucun média a supprimer');
die();
return -1;
}
$this->info($medias->count() . ' média(s) trouvé(s) - Début du traitement');
$bar = $this->output->createProgressBar(count($medias));
$this->info($mediaCollection->count() . ' média(s) trouvé(s) - Début du traitement');
$bar = $this->output->createProgressBar($mediaCollection->count());
$bar->start();
foreach ($medias as $media) {
// * supprime les fichiers médias stockés sur disque
$files = Storage::files($media->folder . "/");
$filepath = preg_quote($media->folder . $media->uid, '/');
$matchingFiles = preg_grep('/^' . $filepath . '/', $files);
if (\is_array($matchingFiles)) {
$this->deleteFiles($matchingFiles);
}
// * supprime le media en bdd
$media->forceDelete();
foreach ($mediaCollection as $media) {
$media->prune();
$bar->advance();
}
$bar->finish();
$this->line("");
$this->newLine();
$this->info('Fin du traitement');
}
/**
* Delete files
*
* @param array $files
* @return void
*/
private function deleteFiles(array $files)
{
collect($files)->map(function (string $filename) {
if (!Storage::delete($filename)) {
Log::warning("Could not delete $filename from storage.");
}
});
}
}

View file

@ -2,23 +2,23 @@
namespace App\Console\Commands;
use App\Http\Traits\MediaTools;
use App\Traits\Http\HandlesMedia;
use App\Models\Article;
use App\Models\Marque;
use App\Models\Site;
use App\Models\Societe;
use App\Models\Sujet;
use App\Models\Typemedia;
use App\Models\Typevue;
use App\Models\TypeMedia;
use App\Models\TypeVue;
use App\Models\Variante;
use Illuminate\Console\Command;
use Illuminate\Contracts\Filesystem\FileNotFoundException;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use League\Flysystem\FileNotFoundException;
class ImportArcadiaFiles extends Command
class ImportArcadiaModelFiles extends Command
{
use MediaTools;
use HandlesMedia;
/**
* The name and signature of the console command.
@ -32,7 +32,7 @@ class ImportArcadiaFiles extends Command
*
* @var string
*/
protected $description = 'Import des fichiers xml Arcadia';
protected $description = 'Import des modèles xml Arcadia';
/**
* Execute the console command.
@ -44,17 +44,24 @@ class ImportArcadiaFiles extends Command
*/
public function handle()
{
/** @var \Illuminate\Support\Collection<string> */
$files = collect(Storage::disk('arcadia')->files())
->filter(function (string $fileName) {
return !\in_array($fileName, ['.gitignore']);
});
if (!$files->count()) {
$this->info('Pas de nouveau fichiers');
return 0;
}
$this->info('Démarrage du traitement');
$files = collect(Storage::disk('arcadia')->files());
$bar = $this->output->createProgressBar(count($files) - 1);
$bar = $this->output->createProgressBar($files->count() - 1);
$bar->start();
// ---- Pour chaque fichier
foreach ($files as $filename) {
if ($filename == ".gitignore") {
continue;
}
// ---- Récupération du contenu
if (!($fileContent = Storage::disk('arcadia')->get($filename))) {
throw new FileNotFoundException($filename);
@ -104,7 +111,7 @@ class ImportArcadiaFiles extends Command
} //end foreach
$bar->finish();
$this->line("");
$this->newLine();
$this->info('Fin du traitement');
}
@ -120,34 +127,21 @@ class ImportArcadiaFiles extends Command
/** @var \SimpleXMLElement $row */
foreach ($fileContent->dam as $row) {
/** @var \App\Models\Article|null $record */
$record = Article::where('noArt', $row->noArt)->first();
if ($record) {
// * Update record
$record->libCcial = $row->libCcial;
$record->codMarq = $row->codMarq;
$record->libMarq = $row->libMarq;
$record->codFamVte = $row->codFamVte;
$record->libFamVte = $row->libFamVte;
$record->codGencodAdm = $row->codGencodAdm;
$record->codGencodSymb = $row->codGencodSymb;
$record->codGencodPds = $row->codGencodPds;
$record->utilisableGroupe = $row->utilisableGroupe;
$record->saveOrFail();
} else {
// * Insert new record
$record = new Article();
$record->noArt = $row->noArt;
$record->libCcial = $row->libCcial;
$record->codMarq = $row->codMarq;
$record->libMarq = $row->libMarq;
$record->codFamVte = $row->codFamVte;
$record->libFamVte = $row->libFamVte;
$record->codGencodAdm = $row->codGencodAdm;
$record->codGencodSymb = $row->codGencodSymb;
$record->codGencodPds = $row->codGencodPds;
$record->utilisableGroupe = $row->utilisableGroupe;
$record->saveOrFail();
} //end if
$record = Article::where('noArt', (int)$row->noArt)->first();
if (!$record) {
$record = new Article();
$record->noArt = (int)$row->noArt;
}
$record->libCcial = (string)$row->libCcial;
$record->codMarq = (int)$row->codMarq;
$record->libMarq = (string)$row->libMarq;
$record->codFamVte = (int)$row->codFamVte;
$record->libFamVte = (string)$row->libFamVte;
$record->codGencodAdm = (string)$row->codGencodAdm;
$record->codGencodSymb = (string)$row->codGencodSymb;
$record->codGencodPds = (string)$row->codGencodPds;
$record->utilisableGroupe = (int)$row->utilisableGroupe;
$record->saveOrFail();
} //end foreach
}
@ -163,18 +157,13 @@ class ImportArcadiaFiles extends Command
/** @var \SimpleXMLElement $row */
foreach ($fileContent->dam as $row) {
/** @var \App\Models\Marque|null $record */
$record = Marque::where('codMarq', $row->codMarq)->first();
if ($record) {
// * Update record
$record->libMarq = $row->libMarq;
$record->saveOrFail();
} else {
// * Insert new record
$record = Marque::where('codMarq', (int)$row->codMarq)->first();
if (!$record) {
$record = new Marque();
$record->codMarq = $row->codMarq;
$record->libMarq = $row->libMarq;
$record->saveOrFail();
$record->codMarq = (int)$row->codMarq;
}
$record->libMarq = (string)$row->libMarq;
$record->saveOrFail();
}
}
@ -190,18 +179,13 @@ class ImportArcadiaFiles extends Command
/** @var \SimpleXMLElement $row */
foreach ($fileContent->mediaSujet as $row) {
/** @var \App\Models\Sujet|null $record */
$record = Sujet::where('codeSujet', $row->code)->first();
if ($record) {
// * Update record
$record->libelle = $row->libelle;
$record->saveOrFail();
} else {
// * Insert new record
$record = Sujet::where('codeSujet', (int)$row->code)->first();
if (!$record) {
$record = new Sujet();
$record->codeSujet = $row->code;
$record->libelle = $row->libelle;
$record->saveOrFail();
$record->codeSujet = (int)$row->code;
}
$record->libelle = (string)$row->libelle;
$record->saveOrFail();
}
}
@ -216,19 +200,14 @@ class ImportArcadiaFiles extends Command
{
/** @var \SimpleXMLElement $row */
foreach ($fileContent->mediatypemedia as $row) {
/** @var \App\Models\Typemedia|null $record */
$record = Typemedia::where('codeTypemedia', $row->code)->first();
if ($record) {
// * Update record
$record->libelle = $row->libelle;
$record->saveOrFail();
} else {
// * Insert new record
$record = new Typemedia();
$record->codeTypemedia = $row->code;
$record->libelle = $row->libelle;
$record->saveOrFail();
/** @var \App\Models\TypeMedia|null $record */
$record = TypeMedia::where('codeTypemedia', (int)$row->code)->first();
if (!$record) {
$record = new TypeMedia();
$record->codeTypemedia = (int)$row->code;
}
$record->libelle = (string)$row->libelle;
$record->saveOrFail();
}
}
@ -243,21 +222,15 @@ class ImportArcadiaFiles extends Command
{
/** @var \SimpleXMLElement $row */
foreach ($fileContent->mediatypesvue as $row) {
/** @var \App\Models\Typevue|null $record */
$record = Typevue::where('codeTypevue', $row->code)->first();
if ($record) {
// * Update record
$record->libelle = $row->libelle;
$record->sujet_id = $row->sujet_id->mediasujet->code;
$record->saveOrFail();
} else {
// * Insert new record
$record = new Typevue();
$record->codeTypevue = $row->code;
$record->libelle = $row->libelle;
$record->sujet_id = $row->sujet_id->mediasujet->code;
$record->saveOrFail();
/** @var \App\Models\TypeVue|null $record */
$record = TypeVue::where('codeTypevue', (int)$row->code)->first();
if (!$record) {
$record = new TypeVue();
$record->codeTypevue = (int)$row->code;
}
$record->libelle = (string)$row->libelle;
$record->sujet_id = (int)$row->sujet_id->mediasujet->code;
$record->saveOrFail();
}
}
@ -272,23 +245,16 @@ class ImportArcadiaFiles extends Command
{
/** @var \SimpleXMLElement $row */
foreach ($fileContent->mediavariante as $row) {
/** @var \App\Models\Typevue|null $record */
$record = Variante::where('codeVariante', $row->code)->first();
if ($record) {
// * Update record
$record->libelle = $row->libelle;
$record->sujet_id = $row->sujet_id->children()[0]->code;
$record->typevue_id = $row->CodeTypeVue->mediatypesvue->code;
$record->saveOrFail();
} else {
// * Insert new record
/** @var \App\Models\TypeVue|null $record */
$record = Variante::where('codeVariante', (int)$row->code)->first();
if (!$record) {
$record = new Variante();
$record->codeVariante = $row->code;
$record->libelle = $row->libelle;
$record->sujet_id = $row->sujet_id->children()[0]->code;
$record->typevue_id = $row->CodeTypeVue->mediatypesvue->code;
$record->saveOrFail();
$record->codeVariante = (int)$row->code;
}
$record->libelle = (string)$row->libelle;
$record->sujet_id = (int)$row->sujet_id->children()[0]->code;
$record->type_vue_id = (int)$row->CodeTypeVue->mediatypesvue->code;
$record->saveOrFail();
}
}
@ -303,19 +269,14 @@ class ImportArcadiaFiles extends Command
{
/** @var \SimpleXMLElement $row */
foreach ($fileContent->SITE_PROD_GRP as $row) {
/** @var \App\Models\Typevue|null $record */
$record = Site::where('id', $row->code)->first();
if ($record) {
// * Update record
$record->libelle = $row->libelle;
$record->saveOrFail();
} else {
// * Insert new record
$record = new Site();
$record->id = $row->code;
$record->libelle = $row->libelle;
$record->saveOrFail();
/** @var \App\Models\TypeVue|null $record */
$record = Site::where('id', (int)$row->code)->first();
if (!$record) {
$record = new Site();
$record->id = (int)$row->code;
}
$record->libelle = (string)$row->libelle;
$record->saveOrFail();
}
}
@ -330,19 +291,14 @@ class ImportArcadiaFiles extends Command
{
/** @var \SimpleXMLElement $row */
foreach ($fileContent->dam as $row) {
/** @var \App\Models\Typevue|null $record */
$record = Societe::where('id', $row->codSociete)->first();
if ($record) {
// * Update record
$record->libelle = $row->nomSociete;
$record->saveOrFail();
} else {
// * Insert new record
$record = new Societe();
$record->id = $row->codSociete;
$record->libelle = $row->nomSociete;
$record->saveOrFail();
/** @var \App\Models\TypeVue|null $record */
$record = Societe::where('id', (int)$row->codSociete)->first();
if (!$record) {
$record = new Societe();
$record->id = (int)$row->codSociete;
}
$record->libelle = (string)$row->nomSociete;
$record->saveOrFail();
}
}

View file

@ -1,393 +0,0 @@
<?php
namespace App\Console\Commands;
use App\Http\Traits\MediaTools;
use App\Models\Article;
use App\Models\Dossier;
use App\Models\Marque;
use App\Models\Media;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use League\Flysystem\FileNotFoundException;
class ImportMedia extends Command
{
use MediaTools;
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'medias:import';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Import des fichiers xml Média Visuel Arcadia';
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$this->info('Démarrage du traitement');
$files = File::glob(storage_path('export_arcadia') . '/DAM_MEDIA_VISUEL_*.*');
$this->line("-> Import du fichier DAM_MEDIA_VISUEL");
$this->xmlVisuels($files);
$this->associateMediasArticles($files);
$this->associateMediasMarques($files);
$this->associateMediasSocietes($files);
$this->line("");
$this->info('Fin du traitement');
}
/**
* Traitement de l'association media <-> societe
*
* @param array $files
* @return void
* @throws \ErrorException If a xml file could not be parsed.
* @throws \League\Flysystem\FileNotFoundException If a file could not be read.
*/
private function associateMediasSocietes(array $files): void
{
$files = File::glob(storage_path('export_arcadia') . "/DAM_ASSO_SOCIETE_*.*");
$this->line("-> Import du fichier DAM_ASSO_SOCIETE");
foreach ($files as $filename) {
$path = explode("/", $filename);
$filename = $path[count($path) - 1];
// ---- Récupération du contenu
if (!($oneFile = Storage::disk('arcadia')->get($filename))) {
throw new FileNotFoundException($filename);
}
if (!($fileContent = simplexml_load_string($oneFile))) {
throw new \ErrorException("Could not parse $filename from arcadia storage.");
}
// ---- traitement du fichier
$bar = $this->output->createProgressBar(count($fileContent->mediaassosociete) - 1);
$bar->start();
/** @var \SimpleXMLElement $row */
foreach ($fileContent->mediaassosociete as $row) {
/** @var \App\Models\Media|null */
$record = Media::where('arcadia_id', $row->codeVisuel)->first();
if ($record) {
// * attache la societe au média
$record->societes()->attach($row->codSociete);
} else {
$this->line("\nDAM_ASSO_SOCIETE : le media n°$row->codeVisuel n'existe pas");
}
$bar->advance();
}
$bar->finish();
$this->deleteFile($filename);
} //end foreach
}
/**
* Traitement de l'association media <-> marque
*
* @param array $files
* @return void
* @throws \ErrorException If a xml file could not be parsed.
* @throws \League\Flysystem\FileNotFoundException If a file could not be read.
*/
private function associateMediasMarques(array $files): void
{
$files = File::glob(storage_path('export_arcadia') . "/DAM_ASSO_MARQUE_*.*");
$this->line("-> Import du fichier DAM_ASSO_MARQUE");
foreach ($files as $filename) {
$path = explode("/", $filename);
$filename = $path[count($path) - 1];
// ---- Récupération du contenu
if (!($oneFile = Storage::disk('arcadia')->get($filename))) {
throw new FileNotFoundException($filename);
}
if (!($fileContent = simplexml_load_string($oneFile))) {
throw new \ErrorException("Could not parse $filename from arcadia storage.");
}
// ---- traitement du fichier
$bar = $this->output->createProgressBar(count($fileContent->mediaassomarque) - 1);
$bar->start();
/** @var \SimpleXMLElement $row */
foreach ($fileContent->mediaassomarque as $row) {
/** @var \App\Models\Media|null */
$record = Media::where('arcadia_id', $row->codeVisuel)->first();
if ($record) {
if ($record->marques->contains($row->codMarq)) {
$this->line("\nDAM_ASSO_MARQUE : le media n°$row->codeVisuel est déjà associé à cette marque.");
} else {
// * attache la marque au média
$record->marques()->attach($row->codMarq);
}
} else {
$this->line("\nDAM_ASSO_MARQUE : le media n°$row->codeVisuel n'existe pas");
}
$bar->advance();
}
$bar->finish();
$this->deleteFile($filename);
} //end foreach
}
/**
* Traitement de l'association media <-> article
*
* @param array $files
* @return void
* @throws \ErrorException If a xml file could not be parsed.
* @throws \Illuminate\Database\Eloquent\ModelNotFoundException If a model cant be found on transaction.
* @throws \League\Flysystem\FileNotFoundException If a file could not be read.
* @throws \Throwable If saving fails.
* @phpcs:disable Generic.Metrics.NestingLevel.TooHigh
* @phpcs:disable Squiz.Commenting.FunctionCommentThrowTag.WrongNumber
*/
private function associateMediasArticles(array $files): void
{
$files = File::glob(storage_path('export_arcadia') . "/DAM_ASSO_ARTICLE_*.*");
$this->line("-> Import du fichier DAM_ASSO_ARTICLE");
/** @var string $filename */
foreach ($files as $filename) {
$path = explode("/", $filename);
$filename = $path[count($path) - 1];
// ---- Récupération du contenu
if (!($oneFile = Storage::disk('arcadia')->get($filename))) {
throw new FileNotFoundException($filename);
}
if (!($fileContent = simplexml_load_string($oneFile))) {
throw new \ErrorException("Could not parse $filename from arcadia storage.");
}
// ---- traitement du fichier
$bar = $this->output->createProgressBar(count($fileContent->mediaassoarticle) - 1);
$bar->start();
/** @var \SimpleXMLElement $row */
foreach ($fileContent->mediaassoarticle as $row) {
/** @var \App\Models\Media|null */
$record = Media::where('arcadia_id', $row->codeVisuel)->first();
if ($record) {
/** @var \App\Models\Article|null */
$article = Article::where('noArt', $row->noArt)->first();
if ($article) {
// * attache l'article au média
$record->articles()->syncWithoutDetaching($article->id);
// * recherche un dossier lié à la marque de l'article
$marque_id = $article->codMarq;
/** @var \App\Models\Dossier|null */
$dossier = Dossier::whereHas("marques", function ($query) use ($marque_id) {
$query->where("codMarq", $marque_id);
})->first();
if ($dossier) {
// * attache le dossier marque au Média
$record->dossier_id = $dossier->id;
$record->save();
$this->line("Liason media n°$row->codeVisuel à la marque n°$article->libMarq");
} else {
DB::transaction(function () use ($record, $marque_id) {
/** @var \App\Models\Marque */
$marque = Marque::where('codMarq', '=', $marque_id)->firstOrFail();
// * On cree le dossier
$dossier = new Dossier();
$dossier->libelle = $marque->libMarq;
$dossier->sujet_id = 1;
$dossier->system = false;
$dossier->saveOrFail();
$dossier->marques()->syncWithoutDetaching($marque_id);
$dossier->saveOrFail();
$record->dossier_id = $dossier->id;
$record->saveOrFail();
});
} //end if
} else {
// phpcs:ignore Generic.Files.LineLength.TooLong
$this->line("\nDAM_ASSO_ARTICLE : le media n°$row->codeVisuel ne peut être lié à l'article n°$row->noArt");
} //end if
} else {
$this->line("\nDAM_ASSO_ARTICLE : le media n°$row->codeVisuel n'existe pas");
} //end if
$bar->advance();
} //end foreach
$bar->finish();
$this->deleteFile($filename);
} //end foreach
}
/**
* Traitement de l'import des XML Visuel
*
* @param array $files
* @return void
* @throws \League\Flysystem\FileNotFoundException If a file could not be read.
* @throws \ErrorException If a xml file could not be parsed.
*/
private function xmlVisuels(array $files): void
{
foreach ($files as $filename) {
$path = explode("/", $filename);
$filename = $path[count($path) - 1];
// ---- Récupération du contenu
if (!($oneFile = Storage::disk('arcadia')->get($filename))) {
throw new FileNotFoundException($filename);
}
if (!($fileContent = simplexml_load_string($oneFile))) {
throw new \ErrorException("Could not parse $filename from arcadia storage.");
}
// ---- traitement du fichier
$bar = $this->output->createProgressBar(count($fileContent->MEDIA_VISUEL) - 1);
$bar->start();
/** @var \SimpleXMLElement $row */
foreach ($fileContent->MEDIA_VISUEL as $row) {
/** @var \App\Models\Media|null */
$record = Media::where('arcadia_id', $row->code)->first();
if (!$record) {
// * Insert new record
$record = new Media();
$record->arcadia_id = $row->code;
if ($row->extension != "") {
$record->extension = $row->extension->typeFichier->libExtension;
}
$record->libelle = $row->libelle;
$record->largeur = $row->nbPxLargeur;
$record->largeur_mm = $row->largeurMm;
$record->hauteur = $row->nbPxHauteur;
$record->hauteur_mm = $row->hauteurMm;
$record->resolution = $row->pppLargeur;
if ($row->version != "") {
$record->version = $row->version;
} else {
$record->version = 1;
}
if ($row->codeTypeMedia != "") {
$record->typemedia_id = $row->codeTypeMedia;
} else {
$record->typemedia_id = 1;
}
if (
$row->codeTypeVue != ""
) {
$record->typevue_id = $row->codeTypeVue;
}
$record->commentaire = $row->commentaire;
$record->sujet_id = $row->codeSujet;
if ($row->langue != "") {
$record->langue_id = $row->langue;
}
if ($row->copyright != "") {
$record->copyright_id = $row->copyright;
} else {
$record->copyright_id = 3;
}
if ($row->codeVariante != "") {
$record->variante_id = $row->codeVariante;
}
if ($row->dateCreation != "") {
$row->dateCreation = substr($row->dateCreation, 0, 10);
$date = explode("/", $row->dateCreation);
$record->created_at = $date[2] . '-' . $date[1] . '-' . $date[0];
}
if ($row->dateModification != "") {
$row->dateModification = substr($row->dateModification, 0, 10);
$date = explode("/", $row->dateModification);
$record->updated_at = $date[2] . '-' . $date[1] . '-' . $date[0];
}
if ($row->datePublication != "") {
$row->datePublication = substr($row->datePublication, 0, 10);
$date = explode("/", $row->datePublication);
$record->published_at = $date[2] . '-' . $date[1] . '-' . $date[0];
}
$record->statut = config('services.statut.public_etendu');
// Affectation d'un dossier et d'un uid.
$destinationPath = $this->getMediaFolder() . "/";
$record->folder = $destinationPath;
$record->uid = Str::uuid();
$mediaFile = "/images/{$record->arcadia_id}.{$record->extension}";
if (Storage::disk('arcadia')->exists($mediaFile)) {
// * déplacement du média et renommage du fichier
if (!($fileContent = Storage::disk('arcadia')->get($mediaFile))) {
throw new \ErrorException("Could not read $mediaFile from arcadia storage.");
}
// * According to config, should throw exception on failure.
Storage::disk('local')->put(
$fileContent,
"{$destinationPath}{$record->uid}.$record->extension"
);
// * calcul de la taille du média
$record->taille = $this->formatBytes(Storage::disk('local')
->size("{$destinationPath}{$record->uid}.$record->extension"));
} else {
// * on applique une image par défaut
$record->extension = "jpg";
Storage::disk('local')->copy(
"assets/images/default.jpg",
"{$destinationPath}{$record->uid}.{$record->extension}"
);
// * calcul de la taille du média
$record->taille = $this->formatBytes(Storage::disk('local')
->size("{$destinationPath}{$record->uid}.{$record->extension}"));
} //end if
$record->saveOrFail();
// ---- Creation des vignettes de base
$this->createSysThumb($record);
// ---- Creation des vignettes en lien avec le sujet du média
$this->createThumb($record);
} else {
$this->line("\nDAM_MEDIA_VISUEL : le media n°$row->code existe déjà en base");
} //end if
$bar->advance();
} //end foreach
$bar->finish();
$this->deleteFile($filename);
} //end foreach
}
/**
* Delete file
*
* @param string $filename
* @return void
*/
private function deleteFile(string $filename): void
{
if (!Storage::disk('arcadia')->delete($filename)) {
Log::warning("Could not delete $filename from arcadia storage.");
}
}
}

View file

@ -1,78 +0,0 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
/**
* Command Name: LDC Web Status Checker
* Description: Envoi des infos Serveur/Laravel vers LDC Web
* Version: 1.1
* Author: Vincent BRIDIER
* Author URI: vincent.bridier@ldc.fr
*
* @codeCoverageIgnore
*/
class LdcWebStatusChecker extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'ldcweb:statuschecker';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Envoi des infos Serveur/Laravel vers LDC Web';
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$data = [];
$data['uuid'] = config('ldcweb.project_uuid');
// Sitemap.xml ?
if (file_exists(public_path() . '/sitemap.xml')) {
$data['sitemap'] = 'true';
} else {
$data['sitemap'] = 'false';
}
// Robots.txt ?
if (file_exists(public_path() . '/robots.txt')) {
$data['robots'] = 'true';
} else {
$data['robots'] = 'false';
}
// Check logs.
$data['logs'] = 0;
$patherror = storage_path('/logs');
$files = glob($patherror . '/*.log');
if (count($files) > 0) {
foreach ($files as $file) {
$data['logs'] = $data['logs'] + filesize($file);
}
}
$data['code']['version'] = app()->version();
// Send by cURL.
$url = 'https://ldc-web.ldc.fr/api/status-checker';
$ch = curl_init($url);
$payload = ['token' => 'CE8S52Qq7stf5k8frF4T', 'datas' => json_encode($data)];
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_exec($ch);
curl_close($ch);
return 0;
}
}

View file

@ -2,21 +2,18 @@
namespace App\Console\Commands;
use App\Http\Traits\MediaTools;
use App\Models\Media;
use App\Models\Sujet;
use Illuminate\Console\Command;
class RegenerateThumbnails extends Command
{
use MediaTools;
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'medias:regenerate';
protected $signature = 'media:regenerate';
/**
* The console command description.
@ -34,49 +31,61 @@ class RegenerateThumbnails extends Command
{
$this->info('Vous allez regénérer toutes les miniatures des médias.');
$sujets = Sujet::where('sujet_dam', 1)->get();
/** @var \Illuminate\Database\Eloquent\Collection<\App\Models\Sujet> */
$subjects = Sujet::where('sujet_dam', true)->get();
$this->line("");
$this->line("Liste des sujets disponibles");
$this->line("----------------------------");
foreach ($sujets as $key => $sujet) {
$this->line("[" . $sujet->codeSujet . "] " . $sujet->libelle);
$this->newLine();
$this->line('Liste des sujets disponibles');
$this->line('----------------------------');
$this->line('[0] Tous les sujets');
foreach ($subjects as $subject) {
$this->line("[{$subject->codeSujet}] {$subject->libelle}");
}
$this->line("[0] Tous les sujets");
$sujetId = $this->ask('Pour quel sujet de média souhaitez vous executer le traitement ?');
$sujetId = $this->askWithCompletion(
'Pour quel sujet de média souhaitez vous executer le traitement ?',
$subjects->pluck('codeSujet')->toArray(),
0
);
if ($sujetId == "") {
if ($sujetId != 0 and !$subjects->pluck('codeSujet')->contains(\intval($sujetId))) {
$this->error('Id de sujet incorrect');
die();
return -1;
}
if ($sujetId == 0) {
$medias = Media::where('typemedia_id', 1)->with('Sujet.formats')->get();
} else {
$medias = Media::where('sujet_id', $sujetId)->where('typemedia_id', 1)->with('Sujet.formats')->get();
}
$sujetId = intval($sujetId);
if ($medias->count() == 0) {
$mediaCollection = Media::onlyTrashed()->with('sujet.formats');
if ($sujetId !== 0) {
$mediaCollection->where('sujet_id', $sujetId);
}
/** @var \Illuminate\Database\Eloquent\Collection<\App\Models\Media> */
$mediaCollection = $mediaCollection->get();
if (!$mediaCollection->count()) {
$this->error('Aucun média n\'a été trouvé');
die();
return -1;
}
$this->info($medias->count() . ' média(s) trouvé(s) - Début du traitement');
$bar = $this->output->createProgressBar(count($medias));
$this->info($mediaCollection->count() . ' média(s) trouvé(s) - Début du traitement');
$bar = $this->output->createProgressBar($mediaCollection->count());
$bar->start();
/** @var \App\Models\Media $media */
foreach ($medias as $media) {
// ---- Creation des vignettes de base
$this->createSysThumb($media);
// ---- Creation des vignettes en lien avec le sujet du média
$this->createThumb($media);
foreach ($mediaCollection as $media) {
try {
// ---- Creation des vignettes de base
$media->createThumbnail();
// ---- Creation des vignettes en lien avec le sujet du média
$media->createSubjectThumbnails();
} catch (\ImagickException | \RuntimeException $e) {
report($e);
}
$bar->advance();
}
$bar->finish();
$this->line("");
$this->newLine();
$this->info('Fin du traitement');
}
}

View file

@ -12,7 +12,7 @@ class ReindexElasticMedia extends Command
*
* @var string
*/
protected $signature = 'medias:reindex';
protected $signature = 'media:reindex';
/**
* The console command description.
@ -29,39 +29,48 @@ class ReindexElasticMedia extends Command
public function handle()
{
$this->info('Reindexation de la base médias dans Elasticsearch...');
$this->info('Test Présence...');
$this->info('ES: Test présence index...');
if (Media::indexExists()) {
$this->info('Suppression index...');
$this->info('ES: Suppression index...');
Media::deleteIndex();
}
$this->info('Création...');
$this->info('ES: Création index...');
Media::createIndex();
$pageNumber = 0;
$perPage = 200;
/** @var \Illuminate\Pagination\LengthAwarePaginator $medias */
$medias = Media::paginate($perPage);
$bar = $this->output->createProgressBar(count($medias));
/** @var \Illuminate\Pagination\LengthAwarePaginator $mediaCollection */
$mediaCollection = Media::paginate($perPage);
$bar->start($medias->total());
if (!$mediaCollection->total()) {
$this->info('Pas de medias');
}
$bar = $this->output->createProgressBar(count($mediaCollection));
$bar->start($mediaCollection->total());
// * LOOP
do {
if ($pageNumber > 0) {
/** @var \Illuminate\Pagination\LengthAwarePaginator $medias */
$medias = Media::paginate($perPage, ['*'], 'page', $pageNumber);
/** @var \Illuminate\Pagination\LengthAwarePaginator $mediaCollection */
$mediaCollection = Media::paginate($perPage, ['*'], 'page', $pageNumber);
}
foreach ($medias as $media) {
$media->addToIndex();
/** @var \App\Models\Media $media */
foreach ($mediaCollection->getCollection() as $media) {
/** @throws \RuntimeException */
$media->addToElasticSearch();
$bar->advance();
}
++$pageNumber;
} while ($medias->hasMorePages());
} while ($mediaCollection->hasMorePages());
$bar->finish();
$this->line('');
$this->newLine();
$this->info('Fin du traitement');
}
}

View file

@ -12,7 +12,7 @@ class ReindexElasticOneMedia extends Command
*
* @var string
*/
protected $signature = 'medias:reindexone {media_id}';
protected $signature = 'media:reindexone {media_id}';
/**
* The console command description.
@ -28,12 +28,14 @@ class ReindexElasticOneMedia extends Command
*/
public function handle()
{
$this->info('Reindexation du media ' . $this->argument('media_id') . ' dans Elasticsearch...');
$media = Media::withTrashed()->find($this->argument('media_id'));
$esExist = Media::searchByQuery(array('match' => array('arcadia_id' => "$media->arcadia_id")));
if ($esExist->totalHits() > 0) {
$media->removeFromIndex();
$mediaID = \strval($this->argument('media_id'));
$this->info("Reindexation du media $mediaID dans Elasticsearch...");
/** @var \App\Models\Media */
if (!($media = Media::withTrashed()->find($this->argument('media_id')))) {
$this->info("Pas de media $mediaID");
}
$media->addToIndex();
/** @throws \RuntimeException */
$media->addToElasticSearch();
$this->info("Media ID '$mediaID' Ajouté");
}
}

View file

@ -0,0 +1,54 @@
<?php
namespace App\Console\Commands;
use App\Lib\ArcadiaLinkUtilities;
use App\Models\ArcadiaLink;
use App\Models\Media;
use Illuminate\Console\Command;
class ResendArcadiaLinkMedias extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'medias:resend-aracadia-links';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Resend files to Arcadia after Arcadia Link fix';
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
// FIXME : Variabiliser la date
//$al = ArcadiaLink::where('created_at', '>=', '2025-10-23')->get();
foreach ($al as $error)
{
$media = Media::withTrashed()->find($error->media_id);
if ($error->arcadia_id === 0) {
ArcadiaLinkUtilities::create($media);
} else {
if (is_null($media->deleted_at)) {
ArcadiaLinkUtilities::update($media);
} else {
ArcadiaLinkUtilities::delete($media);
}
}
}
return Command::SUCCESS;
}
}

View file

@ -12,7 +12,7 @@ class SendMediasModificationsReport extends Command
*
* @var string
*/
protected $signature = 'medias:sendreport';
protected $signature = 'media:sendreport';
/**
* The console command description.

View file

@ -16,8 +16,10 @@ class Kernel extends ConsoleKernel
*/
protected function schedule(Schedule $schedule)
{
$schedule->job(XmlArcadiaMediasIn::class)->name('arcadia_medias_in')
$schedule->job(XmlArcadiaMediasIn::class)->name('Import Arcadia Media Files')
->onOneServer()->cron('*/2 * * * *');
$schedule->command('arcadia:import')->name('Import Arcadia XML Models')
->onOneServer()->cron('*/30 * * * *');
}
/**

23
app/Enums/ContactType.php Normal file
View file

@ -0,0 +1,23 @@
<?php
namespace App\Enums;
enum ContactType: int
{
case bug = 1;
case evolution = 2;
/**
* Get num label
*
* @return string
*/
public function label(): string
{
return match ($this) {
static::bug => trans('Signaler un bug'),
static::evolution => trans('Proposer une amélioration'),
default => trans('MISSING LABEL')
};
}
}

View file

@ -0,0 +1,11 @@
<?php
namespace App\Enums;
enum EdiMouvementType: string
{
case create = 'C';
case update = 'M';
case delete = 'S';
case reactivate = 'R';
}

View file

@ -0,0 +1,29 @@
<?php
namespace App\Enums;
enum EventHistoryType: int
{
case created = 0;
case moved = 1;
case changed = 2;
case destroyed = 3;
case labelled = 4;
/**
* Get num label
*
* @return string
*/
public function label(): string
{
return match ($this) {
static::created => trans('Création'),
static::moved => trans('Déplacement'),
static::changed => trans('Modification'),
static::destroyed => trans('Suppression'),
static::labelled => trans('Etiquette'),
default => trans('MISSING LABEL')
};
}
}

View file

@ -0,0 +1,93 @@
<?php
namespace App\Enums\Legacy;
use Kwaadpepper\Enum\BaseEnumRoutable;
/**
* This describes an API error.
*
* @method static self modelNotFound()
* The request model was not found (typically the customer)
*
* @method static self validationFailed()
* This means some data could not be validated,
* you may have to look into message to get the details
*
* @method static self apiAuthFailed()
* The authentification to the api failed. You may have to
* check into config or .env file for your service credentials
*
* @method static self authFailed()
* The authentification for this customer failed because of wrong credentials
*
* @method static self methodNotAllowed()
* This http method is not allowed
*
* @method static self wrongTimeStamp()
* The timestamp given is wrong. This would happen in case if
* a replay attack scenario. Timestamps are provided in headers
* and in the http body
*
* @method static self missingParameter()
* The request is missing some parameters
*
* @method static self throttleRequestsException()
* Too many requests have been attempted. The is a throttle security.
* Normally you should not trigger this.
* If this happens fill a ticket on https://supportweb.ldc.fr
* with some technical details
*
* @method static self authorizationException()
* You are maybe trying to access data that is out of your range.
* This should not happen normally, it is here to allow you to handle this case.
* If this happens fill a ticket on https://supportweb.ldc.fr
* with some technical details
*
* @package DAMclient
* @subpackage enums
*
* TODO: pass this as php8 enum, when support of 7 will be dropped.
*/
class ApiError extends BaseEnumRoutable
{
/**
* Valeurs des possibilités de cet énumération (value)
*
* @return array
*/
public static function values(): array
{
return [
'modelNotFound' => 101,
'validationFailed' => 102,
'authFailed' => 117,
'apiAuthFailed' => 119,
'methodNotAllowed' => 150,
'wrongTimeStamp' => 151,
'missingParameter' => 400,
'authorizationException' => 403,
'throttleRequestsException' => 429
];
}
/**
* Libellés des possibilités de cet énumération (label)
*
* @return array
*/
public static function labels(): array
{
return [
'modelNotFound' => '%s introuvable.',
'validationFailed' => 'Impossible de valider les données',
'authFailed' => 'Echec de l\'authentification du compte',
'apiAuthFailed' => 'Echec de l\'authentification client à l\'Api DAM',
'methodNotAllowed' => 'This method is not allowed for this request',
'wrongTimeStamp' => 'Your request is malformed, please check timestamp header and parameter',
'missingParameter' => 'The request is missing some parameters',
'authorizationException' => 'You are not autorized to access this',
'throttleRequestsException' => 'Throttle: too many request have been attempted, please retry later'
];
}
}

View file

@ -0,0 +1,18 @@
<?php
namespace App\Enums\Legacy;
use Kwaadpepper\Enum\BaseEnumRoutable;
/**
* @method static self OK()
* @method static self KO()
*
* @package DAMclient
* @subpackage enums
*
* TODO: pass this as php8 enum, when support of 7 will be dropped.
*/
class ApiStatus extends BaseEnumRoutable
{
}

View file

@ -1,10 +1,5 @@
<?php
/**
* En attente phpcs merge
* https://github.com/squizlabs/PHP_CodeSniffer/pull/3478
*/
namespace App\Enums;
enum LogType: int

View file

@ -0,0 +1,17 @@
<?php
namespace App\Enums;
enum MediaExtension: string
{
case ai = 'ai';
case gif = 'gif';
case jpeg = 'jpeg';
case jpg = 'jpg';
case mp4 = 'mp4';
case mp3 = 'mp3';
case pdf = 'pdf';
case png = 'png';
case psd = 'psd';
case svg = 'svg';
}

10
app/Enums/MediaStatut.php Normal file
View file

@ -0,0 +1,10 @@
<?php
namespace App\Enums;
enum MediaStatut: int
{
case brouillon = 0;
case public = 1;
case public_etendu = 2;
}

View file

@ -0,0 +1,103 @@
<?php
namespace App\Events;
use App\Models\Etiquette;
use App\Models\Media;
use App\Models\User;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class MediaChangedLabel extends MediaCreated
{
use Dispatchable;
use SerializesModels;
/**
* The user who created the media
*
* @var \App\Models\User
*/
private $user;
/**
* The media
*
* @var \App\Models\Media
*/
private $media;
/**
* The oldLabel
*
* @var \App\Models\Etiquette|null
*/
private $oldLabel;
/**
* The newLabel
*
* @var \App\Models\Etiquette|null
*/
private $newLabel;
/**
* Create a new event instance.
*
* @param \App\Models\User $user
* @param \App\Models\Media $media
* @param \App\Models\Etiquette|null $oldLabel
* @param \App\Models\Etiquette|null $newLabel
*/
public function __construct(
User $user,
Media $media,
Etiquette $oldLabel,
Etiquette $newLabel,
) {
$this->user = $user;
$this->media = $media;
$this->oldLabel = $oldLabel;
$this->newLabel = $newLabel;
}
/**
* Get User who triggered the event
*
* @return \App\Models\User
*/
public function getUser(): User
{
return $this->user;
}
/**
* Get the media
*
* @return \App\Models\Media
*/
public function getMedia(): Media
{
return $this->media;
}
/**
* Get the oldLabel
*
* @return \App\Models\Etiquette|null
*/
public function getOldLabel(): Etiquette|null
{
return $this->oldLabel;
}
/**
* Get the newLabel
*
* @return \App\Models\Etiquette|null
*/
public function getNewLabel(): Etiquette|null
{
return $this->newLabel;
}
}

View file

@ -0,0 +1,60 @@
<?php
namespace App\Events;
use App\Models\Media;
use App\Models\User;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class MediaCreated
{
use Dispatchable;
use SerializesModels;
/**
* The user who created the media
*
* @var \App\Models\User
*/
private $user;
/**
* The media
*
* @var \App\Models\Media
*/
private $media;
/**
* Create a new event instance.
*
* @param \App\Models\User $user
* @param \App\Models\Media $media
*/
public function __construct(User $user, Media $media)
{
$this->user = $user;
$this->media = $media;
}
/**
* Get User who triggered the event
*
* @return \App\Models\User
*/
public function getUser(): User
{
return $this->user;
}
/**
* Get the media
*
* @return \App\Models\Media
*/
public function getMedia(): Media
{
return $this->media;
}
}

View file

@ -0,0 +1,7 @@
<?php
namespace App\Events;
class MediaDestroyed extends MediaCreated
{
}

View file

@ -0,0 +1,60 @@
<?php
namespace App\Events;
use App\Models\Media;
use App\Models\User;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class MediaLinkWithArcadia
{
use Dispatchable;
use SerializesModels;
/**
* The user who created the media
*
* @var \App\Models\User
*/
private $user;
/**
* The media
*
* @var \App\Models\Media
*/
private $media;
/**
* Create a new event instance.
*
* @param \App\Models\User $user
* @param \App\Models\Media $media
*/
public function __construct(User $user, Media $media)
{
$this->user = $user;
$this->media = $media;
}
/**
* Get User who triggered the event
*
* @return \App\Models\User
*/
public function getUser(): User
{
return $this->user;
}
/**
* Get the media
*
* @return \App\Models\Media
*/
public function getMedia(): Media
{
return $this->media;
}
}

View file

@ -0,0 +1,7 @@
<?php
namespace App\Events;
class MediaMoved extends MediaCreated
{
}

View file

@ -0,0 +1,122 @@
<?php
namespace App\Events;
use App\Models\Sujet;
use App\Models\User;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class MediaSearched
{
use Dispatchable;
use SerializesModels;
/**
* The user who created the media
*
* @var \App\Models\User
*/
private $user;
/**
* The sujet
*
* @var \App\Models\Sujet
*/
private $sujet;
/**
* The searchString
*
* @var string
*/
private $searchString;
/**
* The filters
*
* @var object
*/
private $filters;
/**
* The dossier_id
*
* @var integer
*/
private $dossier_id;
/**
* Create a new event instance.
*
* @param \App\Models\User $user
* @param \App\Models\Sujet $sujet
* @param string $searchString
* @param object $filters
* @param integer $dossier_id
*/
public function __construct(
User $user,
Sujet $sujet,
string $searchString,
object $filters,
int $dossier_id
) {
$this->user = $user;
$this->sujet = $sujet;
$this->searchString = $searchString;
$this->filters = $filters;
$this->dossier_id = $dossier_id;
}
/**
* Get User who triggered the event
*
* @return \App\Models\User
*/
public function getUser(): User
{
return $this->user;
}
/**
* Get the sujet
*
* @return \App\Models\Sujet
*/
public function getSujet(): Sujet
{
return $this->sujet;
}
/**
* Get the searchString
*
* @return string
*/
public function getSearchString(): string
{
return $this->searchString;
}
/**
* Get the filters
*
* @return string
*/
public function getFilters(): object
{
return $this->filters;
}
/**
* Get the dossier_id
*
* @return integer
*/
public function getDossierId(): int
{
return $this->dossier_id;
}
}

View file

@ -0,0 +1,7 @@
<?php
namespace App\Events;
class MediaUpdated extends MediaCreated
{
}

View file

@ -2,6 +2,6 @@
namespace App\Exceptions;
class AbortXmlImportException extends \ErrorException
class AbortXmlImportException extends \RuntimeException
{
}

View file

@ -0,0 +1,102 @@
<?php
namespace App\Exceptions\Api;
use App\Enums\Legacy\ApiError;
use Illuminate\Support\Facades\Log;
/**
* This would be thrown if any of the case
* referenced in \App\Enums\ApiError would
* occur within the dam
*
* On the client an ApiErrorException would
* also be throw with the ApiError corresponding to
* the situation
*
* @package DAMclient
* @subpackage exceptions
*/
class ApiErrorException extends \RuntimeException
{
/**
* Api Error
* This value makes the error more explicit.
* Handling these errors are normal as inputs or
* request may be undesired or refused by the dam.
*
* @var \App\Enums\Legacy\ApiError
*/
protected $error;
/**
* This is an explicit error message,
* ou may print this to the front or not depending
* on $error value
*
* @var string
*/
protected $message;
/**
* This is the http code returned by the dam.
* This value could be used to handle errors.
* But please take in consideration $error first
*
* @var integer
*/
protected $httpCode;
/**
* Create a new exception base on ApiError enum.
*
* @param \App\Enums\Legacy\ApiError $error
* @param integer $httpCode
* @param string $message
* @param \Throwable|null $previous
*/
public function __construct(ApiError $error, int $httpCode = 400, string $message = '', \Throwable $previous = null)
{
$this->error = $error;
$this->message = $message;
$this->httpCode = $httpCode;
parent::__construct(
strlen($this->message) ? $this->message : $error->label,
$error->value,
$previous
);
}
/**
* Get the Api Error
*
* @return \App\Enums\Legacy\ApiError
*/
public function getApiError(): ApiError
{
return $this->error;
}
/**
* Get the http code
*
* @return integer
*/
public function getStatusCode(): int
{
return $this->httpCode;
}
/**
* This is used by laravel only.
* It uses Log class to print the error.
*
* @return void
*/
public function report()
{
// @codeCoverageIgnoreStart
Log::debug(sprintf('DAM API: %s', $this->getMessage()));
// @codeCoverageIgnoreEnd
}
}

View file

@ -0,0 +1,10 @@
<?php
namespace App\Exceptions\Api;
/**
* Unhandled error that occus within DAM.
*/
class DamException extends \RuntimeException
{
}

View file

@ -2,6 +2,6 @@
namespace App\Exceptions;
class ElasticSearchException extends \ErrorException
class ElasticSearchException extends \RuntimeException
{
}

View file

@ -2,7 +2,20 @@
namespace App\Exceptions;
use App\Enums\Legacy\ApiError;
use App\Enums\Legacy\ApiStatus;
use App\Exceptions\Api\ApiErrorException;
use App\Http\Resources\Api\ApiResponse as ApiResponseResource;
use App\Lib\Helper;
use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Auth\AuthenticationException;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Http\Exceptions\ThrottleRequestsException;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Validation\ValidationException;
use Kwaadpepper\ExceptionHandler\Exceptions\ExceptionHandler;
use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException;
use Throwable;
class Handler extends ExceptionHandler
@ -12,14 +25,18 @@ class Handler extends ExceptionHandler
*
* @var array<class-string<\Throwable>, \Psr\Log\LogLevel::*>
*/
protected $levels = [];
protected $levels = [
ApiErrorException::class => \Psr\Log\LogLevel::WARNING
];
/**
* A list of the exception types that are not reported.
*
* @var array<int, class-string<\Throwable>>
*/
protected $dontReport = [];
protected $dontReport = [
ApiErrorException::class,
];
/**
* A list of the inputs that are never flashed for validation exceptions.
@ -42,4 +59,126 @@ class Handler extends ExceptionHandler
$this->reportable(function (Throwable $e) {
});
}
/**
* Render an exception into an HTTP response.
*
* @param mixed $request
* @param \Throwable $e
* @return \Symfony\Component\HttpFoundation\Response
*
* @throws \Throwable If needed.
*/
public function render(mixed $request, Throwable $e)
{
if (Helper::isRequestForApi($request) and $request->isJson()) {
return $this->handleApiException($request, $e);
} //end if
// @codeCoverageIgnoreStart
return parent::render($request, $e);
// @codeCoverageIgnoreEnd
}
/**
* Generate a json response for any api error case
*
* @param \Illuminate\Http\Request $request
* @param \Throwable $e
* @return \Illuminate\Http\JsonResponse
*/
private function handleApiException(Request $request, \Throwable $e): JsonResponse
{
switch (get_class($e)) {
case AuthenticationException::class:
/** @var AuthenticationException $e */
return $this->getReponse(
$request,
401,
ApiError::apiAuthFailed()->value,
sprintf('%s, %s', ApiError::apiAuthFailed()->label, $e->getMessage())
);
case MethodNotAllowedHttpException::class:
/** @var MethodNotAllowedHttpException $e */
return $this->getReponse(
$request,
$e->getStatusCode(),
ApiError::methodNotAllowed()->value,
sprintf('%s, %s', ApiError::methodNotAllowed()->label, $e->getMessage())
);
case ValidationException::class:
/** @var ValidationException $e */
return $this->getReponse(
$request,
$e->status,
ApiError::validationFailed()->value,
sprintf(ApiError::validationFailed()->label, $e->getMessage()),
['errors' => $e->errors()]
);
case ModelNotFoundException::class:
/** @var ModelNotFoundException $e */
return $this->getReponse(
$request,
404,
ApiError::modelNotFound()->value,
sprintf(
ApiError::modelNotFound()->label,
\array_slice(explode('\\', (string)$e->getModel()), -1)[0]
)
);
case ApiErrorException::class:
/** @var ApiErrorException $e */
return $this->getReponse(
$request,
$e->getStatusCode(),
$e->getCode(),
$e->getMessage()
);
case ThrottleRequestsException::class:
/** @var ThrottleRequestsException $e */
return $this->getReponse(
$request,
429,
ApiError::throttleRequestsException()->value,
ApiError::throttleRequestsException()->label
);
case AuthorizationException::class:
/** @var AuthorizationException $e */
return $this->getReponse(
$request,
403,
ApiError::authorizationException()->value,
ApiError::authorizationException()->label
);
} //end switch
// @codeCoverageIgnoreStart
if (config('app.env') === 'testing') {
// phpcs:ignore Squiz.PHP.DiscouragedFunctions.Found
print_r((string)$e);
}
return $this->getReponse($request, 500, intval($e->getCode()), $e->getMessage());
// @codeCoverageIgnoreEnd
}
/**
* Generate an Api json response
*
* @param \Illuminate\Http\Request $request
* @param integer $httpCode
* @param integer $apiCode
* @param string $message
* @param array $data
* @return \Illuminate\Http\JsonResponse
*/
private static function getReponse(
Request $request,
int $httpCode,
int $apiCode,
string $message,
array $data = []
): JsonResponse {
return (new ApiResponseResource(
(object)array_merge($data, ['message' => $message, 'code' => $apiCode]),
ApiStatus::KO()
))->toResponse($request)->setStatusCode($httpCode);
}
}

View file

@ -0,0 +1,19 @@
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use Illuminate\Http\JsonResponse;
class ApiController extends Controller
{
/**
* Just returns HTTP 200 response
*
* @return JsonResponse
*/
public function ping(): JsonResponse
{
return response()->json();
}
}

View file

@ -0,0 +1,86 @@
<?php
namespace App\Http\Controllers\Api;
use App\Enums\MediaStatut;
use App\Http\Controllers\Controller;
use App\Http\Resources\Api\MediaListResource;
use App\Http\Resources\Api\MediaPictureResource;
use App\Http\Resources\Api\MediaResource;
use App\Models\Article;
use App\Models\Media;
use Illuminate\Http\Resources\Json\JsonResource;
use Illuminate\Http\Resources\Json\ResourceCollection as JsonResourceCollection;
use Illuminate\Support\Facades\DB;
class MediaController extends Controller
{
/**
* Show specific media
*
* @param \App\Models\Media $media
* @return \Illuminate\Http\Resources\Json\JsonResource
*/
public function show(Media $media): JsonResource
{
return new MediaResource($media);
}
/**
* Get Media list of the latest version for each vue type and variant
* of media that is linked with an article.
*
* Working example
* SELECT `mediaList`.`id`,`mediaList`.`arcadia_id`,`mediaList`.`libelle`,`mediaList`.`uid`,
* `mediaList`.`version`,`mediaList`.`type_vue_id`,`mediaList`.`variante_id`,`mediaList`.`created_at`,`mediaList`.`updated_at`
* FROM (
* SELECT `media`.*
* from `media`
* JOIN `media_article` ON `media_article`.`media_id` =`media`.`id`
* JOIN `articles` ON `articles`.`id` =`media_article`.`article_id` AND `articles`.`noArt` = 2842
* WHERE
* `media`.type_media_id IN (1,5) AND
* `media`.isLocked = 0 AND
* `media`.statut != 0
* ORDER BY `version` DESC
* LIMIT 18446744073709551615
* ) AS mediaList
* GROUP BY `type_vue_id`,`variante_id`
*
* @param \App\Models\Article $article
* @return \Illuminate\Http\Resources\Json\ResourceCollection
*/
public function getMediaListByArticle(Article $article): JsonResourceCollection
{
// ! Hack to allow group by (may have side effects)
DB::statement("SET SQL_MODE=''");
/** @var \Illuminate\Database\Eloquent\Collection<\App\Models\Media> */
$mediaList = Media::cacheForever()->withTrashed()->fromSub(
Media::query()->whereHas('articles', function ($q) use ($article) {
$q->where('noArt', $article->noArt);
})->whereHas('typeMedia', function ($q) {
$q->whereIn('libelle', ['Photo', 'jpeg']);
})->where('isLocked', false)
->where('statut', '!=', MediaStatut::brouillon)
->orderBy('version', 'DESC')
// ! Maria Hack: use limit to preserve order.
->limit(\PHP_INT_MAX),
'mediaList'
)->groupBy(['mediaList.type_vue_id', 'mediaList.variante_id'])
->paginate();
return new MediaListResource($mediaList, true);
}
/**
* Get Media picture resource
*
* @param \App\Models\Media $media
* @return \Illuminate\Http\Resources\Json\JsonResource
*/
public function getMediaPicture(Media $media): JsonResource
{
return new MediaPictureResource($media);
}
}

View file

@ -3,25 +3,46 @@
namespace App\Http\Controllers;
use App\Models\ArcadiaLink;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Contracts\Database\Eloquent\Builder;
class ArcadiaLinkController extends Controller
{
public function index(Request $request)
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
Auth::user()->authorizeRoles(config('roles.const.ADMIN'));
$dt = Carbon::now('Europe/Paris')->subMinutes(15)->toDateTimeString();
$ano = ArcadiaLink::where('created_at', '<', $dt)->orderBy('id', 'DESC')->get();
return view('arcadialink.index', ['ano' => $ano]);
/** @var \App\Models\User */
$user = auth()->user();
$user->authorizeRoles(config('roles.const.ADMIN'));
// * ArcadiaLink->Media is nullable
$arcadiaLinkList = ArcadiaLink::query()
->with('media', fn (Builder $q) => $q->withTrashed())
->where('created_at', '<', now()->subMinutes(15))
->orderBy('id', 'DESC')->get();
return view('arcadialinks.index', compact('arcadiaLinkList'));
}
public function delete(Request $request)
/**
* Remove the specified resource from storage.
*
* @param \App\Models\ArcadiaLink $arcadia_link
* @return \Illuminate\Http\Response
*/
public function delete(ArcadiaLink $arcadia_link)
{
$id = $request->id;
$a = ArcadiaLink::find($id);
$a->delete();
return redirect()->route('arcadialink.index');
if ($arcadia_link->deleteOrFail()) {
return redirect()
->route('arcadialinks.index')
->with('warning', trans(
':model a bien été supprimé',
['model' => trans('ArcadiaLink')]
));
}
return back();
}
}

View file

@ -0,0 +1,22 @@
<?php
namespace App\Http\Controllers;
use App\Http\Requests\Bo\Ajax\GetArticleRequest;
use App\Models\Article;
use Illuminate\Http\JsonResponse;
class ArticleController
{
/**
* Renvoie un article par ID Arcadia
* @param \App\Http\Requests\Bo\Ajax\GetArticleRequest $request
* @return \Illuminate\Http\JsonResponse
*/
public function getArticle(GetArticleRequest $request): JsonResponse
{
return \response()->json([
'article' => Article::where('noArt', $request->arcadia_id)->firstOrFail()
]);
}
}

View file

@ -0,0 +1,85 @@
<?php
namespace App\Http\Controllers;
use App\Enums\ContactType;
use App\Http\Requests\Bo\Ajax\SendContactMessageRequest;
use App\Mail\ContactMail;
use App\Models\Dossier;
use App\Models\Format;
use App\Models\Recherche;
use App\Models\Sujet;
use Illuminate\Contracts\Database\Eloquent\Builder;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Mail;
class CommonController extends Controller
{
/**
* Show main interface
*
* @return \Illuminate\Contracts\Support\Renderable
*/
public function showInterface()
{
/** @var \App\Models\User */
$user = auth()->user();
/** @var \Illuminate\Database\Eloquent\Collection<\App\Models\Sujet> */
$sujets = $user->sujets;
/** @var \Illuminate\Database\Eloquent\Collection<\App\Models\Recherche> */
$recherches = Recherche::query()->whereHas('user', function (Builder $q) use ($user) {
$q->where('id', $user->id);
})->with('sujet')
->limit(10)
->orderBy('created_at', 'desc')
->get();
/** @var \Illuminate\Database\Eloquent\Collection<\App\Models\Dossier> */
$dossiers = Dossier::whereHas('users', function ($q) use ($user) {
$q->where('user_id', $user->id);
})->with('sujet')->get();
return view('home')->with([
'sujets' => $sujets,
'recherches' => $recherches,
'dossiers' => $dossiers
]);
}
/**
* Envoi d'un message
*
* @param \App\Http\Requests\Bo\Ajax\SendContactMessageRequest $request
* @return \Illuminate\Http\JsonResponse
*/
public function sendMessage(SendContactMessageRequest $request): JsonResponse
{
/** @var \App\Models\User $user */
$user = auth()->user();
foreach (config('mail.contactsList.admin') as $mail) {
Mail::to($mail)
->send(new ContactMail(ContactType::from($request->type), $request->url, $request->message, $user));
}
return response()->json(['status' => 'OK']);
}
/**
* Get data by sujet
*
* @param \App\Models\Sujet $sujet
* @return \Illuminate\Http\JsonResponse
*/
public function getDatasBySujet(Sujet $sujet): JsonResponse
{
/** @var \Illuminate\Database\Eloquent\Collection<\App\Models\Format> */
$formats = Format::withoutGlobalScope('App\Scopes\SystemScope')
->where('system', 1)->get();
$formats = $formats->merge($sujet->formats);
return response()->json([
'typesvue' => $sujet->typeVues,
'typesvariantes' => $sujet->variantes,
'formats' => $formats
]);
}
}

View file

@ -1,70 +0,0 @@
<?php
namespace App\Http\Controllers;
use App\Models\Format;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Mail;
use App\Models\Sujet;
use App\Models\Recherche;
use App\Models\Dossier;
use App\Mail\Contact;
use Illuminate\Http\Request;
class CommunController extends Controller
{
/**
* page d'accueil
*
* @return \Illuminate\Contracts\Support\Renderable
*/
public function index()
{
$user = Auth::user();
$sujets = $user->sujets;
$paniers = Auth::user()->paniersFavoris;
$recherches = Recherche::where('user_id', $user->id)
->with("sujet")
->limit(10)
->orderBy("created_at", "desc")
->get();
$dossiers = Dossier::whereHas("users", function ($query) use ($user) {
$query->where('user_id', $user->id);
})
->with("sujet")
->get();
return view('home')->with(['sujets' => $sujets, 'recherches' => $recherches, 'dossiers' => $dossiers, 'paniers' => $paniers]);
}
/**
* Envoi d'un message
*
*/
public function sendMessage(Request $request)
{
$type = $request->input('type');
$url = $request->input('url');
$message = $request->input('message');
if (Auth::check()) {
$user = auth()->user();
Mail::to(env('MAIL_ADMIN_ADDRESS', "informatique.internet@ldc.fr"))
->send(new Contact($type, $url, $message, $user));
}
return json_encode(array('status' => "OK"));
}
public function getDatasBySujet(Request $request)
{
$sujet_id = $request->sujet_id;
$sujet = Sujet::findOrFail($sujet_id);
$response['typesvue'] = $sujet->typeVues;
$response['typesvariantes'] = $sujet->variantes;
$response['formats'] = array_merge(Format::withoutGlobalScope('App\Scopes\SystemScope')->where('system', 1)->get()->toArray(), $sujet->formats->toArray());
return response()->json($response);
}
}

View file

@ -2,17 +2,141 @@
namespace App\Http\Controllers;
use App\Enums\MediaStatut;
use App\Http\Requests\Bo\Ajax\FetchFoldersRequest;
use App\Http\Requests\Bo\Ajax\LoadFoldersRequest;
use App\Http\Requests\Bo\Folders\StoreDepotFolderRequest;
use App\Http\Requests\Bo\Folders\StoreFolderRequest;
use App\Http\Requests\Bo\Folders\UpdateFolderRequest;
use App\Http\Resources\NestedDossiersListResource;
use App\Models\Dossier;
use App\Models\Sujet;
use App\Models\Marque;
use App\Models\Media;
use App\Models\Variante;
use App\Models\Format;
use Illuminate\Contracts\Database\Eloquent\Builder;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
class DossierController extends Controller
{
/**
* Ajax Load folders
*
* @param \App\Http\Requests\Bo\Ajax\LoadFoldersRequest $request
* @return \Illuminate\Http\JsonResponse
*/
public function loadFolders(LoadFoldersRequest $request): JsonResponse
{
/** @var \App\Models\User */
$user = Auth::user();
$folders = [];
$select = ['id', 'libelle', 'system'];
/** @var \Illuminate\Database\Eloquent\Collection<\App\Models\Dossier> Dossiers publiques */
$publicFolders = Dossier::query()
->whereHas('sujet', fn (Builder $q) => $q->where('codeSujet', $request->input('sujet_id')))
->where('is_private', false)
->whereNull('parent_id')
->select($select)->get();
// * On récupère les dossiers privés.
/** @var \Illuminate\Database\Eloquent\Builder */
$privateQuery = Dossier::query()
->whereHas('sujet', fn (Builder $q) => $q->where('codeSujet', $request->input('sujet_id')))
->where('is_private', true)
->select($select);
// ? Is admin ?
if (!$user->hasRole(config('roles.const.ADMIN'))) {
// * If not admin, get using brands.
/** @var \Illuminate\Database\Eloquent\Collection<integer> Codes marques */
$marques = $user->marques()->pluck('codMarq');
$privateQuery->whereHas('marques', fn (Builder $q) => $q->whereIn('codMarq', $marques));
}
/** @var \Illuminate\Database\Eloquent\Collection<\App\Models\Dossier> Dossiers privés */
$privateFolders = $privateQuery->get();
$foldersCollection = $publicFolders->merge($privateFolders);
$foldersCollection = $foldersCollection
->sortBy('libelle', SORT_NATURAL | SORT_FLAG_CASE)
->sortBy('favoris')->values();
// ? Is admin ?
if ($user->hasRole(config('roles.const.ADMIN'))) {
// * Ajout du dépôt de fichier pour les admin.
/** @var \Illuminate\Database\Eloquent\Collection<\App\Models\Dossier> Dossiers depots */
$depotQuery = Dossier::query()
->whereNull('parent_id')
->where('system', true)
->orderBy('libelle', 'ASC')
->get();
$foldersCollection = $foldersCollection->merge($depotQuery);
}
Arr::set($folders, 'data', new NestedDossiersListResource($foldersCollection));
// * Racine Count
$racineCountQuery = Media::query()
->whereHas('sujet', fn (Builder $q) => $q->where('codeSujet', $request->input('sujet_id')))
->whereDoesntHave('dossier');
// ? Is not admin ?
if (!$user->hasRole(config('roles.const.ADMIN'))) {
$racineCountQuery->where(
fn (Builder $q) =>
$q->where('statut', MediaStatut::public_etendu)
->orWhere('statut', MediaStatut::public)
->orWhere('created_by', $user->id)
);
}
Arr::set($folders, 'racineCount', $racineCountQuery->count());
return response()->json($folders);
}
/**
* Chargement des dossiers
*
* @param \App\Http\Requests\Bo\Ajax\FetchFoldersRequest $request
* @return \Illuminate\Http\JsonResponse
*/
public function loadFolder(FetchFoldersRequest $request): JsonResponse
{
/** @var \App\Models\User */
$user = auth()->user();
$response = [];
$response['variantes'] = Variante::query()
->whereHas('sujet', fn (Builder $q) => $q->where('codeSujet', $request->input('sujetId')))
->get();
/** @var \Illuminate\Database\Eloquent\Collection<\App\Models\Format> */
$FormatsSujet = Format::query()
->whereHas('sujets', fn (Builder $q) => $q->where('sujet_id', $request->input('sujetId')))
->get();
/** @var \Illuminate\Database\Eloquent\Collection<\App\Models\Format> */
$FormatsSystem = Format::query()
->withoutGlobalScope('App\Scopes\SystemScope')
->where('system', true)->get();
$response['formats'] = $FormatsSujet->merge($FormatsSystem);
$response['draggable'] = $user->hasRole([config('roles.const.ADMIN'), config('roles.const.REDACTEUR')]);
// Envoi du role .
$response['role'] = $user->role;
return \response()->json($response);
}
// * CRUD
/**
* Display a listing of the resource.
*
@ -20,11 +144,20 @@ class DossierController extends Controller
*/
public function index()
{
Auth::user()->authorizeRoles(config('roles.const.ADMIN'));
$dossiers = Dossier::whereNull('parent_id')->where('system', false)->orderBy('libelle', 'asc')->get();
$sujets = Sujet::where('sujet_dam', 1)->get();
/** @var \App\Models\User */
$user = auth()->user();
$user->authorizeRoles(config('roles.const.ADMIN'));
return view('dossiers.index')->with([
/** @var \Illuminate\Database\Eloquent\Collection<\App\Models\Dossier> */
$dossiers = Dossier::query()
->whereNull('parent_id')
->where('system', false)
->orderBy('libelle', 'asc')
->get();
/** @var \Illuminate\Database\Eloquent\Collection<\App\Models\Sujet> */
$sujets = Sujet::query()->where('sujet_dam', true)->get();
return view('dossiers.index', [
'dossiers' => $dossiers,
'sujets' => $sujets,
]);
@ -33,458 +166,164 @@ class DossierController extends Controller
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory
*/
public function create()
public function create(Request $request)
{
Auth::user()->authorizeRoles(config('roles.const.ADMIN'));
$marques = Marque::all();
$sujets = Sujet::where('sujet_dam', 1)->get();
return view('dossiers.create')->with(['marques' => $marques, 'sujets' => $sujets]);
}
if ($request->sub) {
/** @var \Illuminate\Database\Eloquent\Collection<\App\Models\Dossier> */
$dossiers = Dossier::query()
->whereNull('parent_id')
->where('system', false)
->orderBy('libelle', 'asc')->get();
return view('dossiers.create-sub', [
'dossiers' => $dossiers
]);
}
public function createSub()
{
$dossiers = Dossier::whereNull('parent_id')->where('system', false)->orderBy('libelle', 'asc')->get();
return view('dossiers.create-sub')->with(['dossiers' => $dossiers]);
/** @var \App\Models\User */
$user = auth()->user();
$user->authorizeRoles(config('roles.const.ADMIN'));
$marques = Marque::all();
/** @var \Illuminate\Database\Eloquent\Collection<\App\Models\Sujet> */
$sujets = Sujet::query()->where('sujet_dam', true)->get();
return view('dossiers.create')->with([
'marques' => $marques,
'sujets' => $sujets
]);
}
/**
* Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param \App\Http\Requests\Bo\Folders\StoreFolderRequest $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
public function store(StoreFolderRequest $request)
{
Auth::user()->authorizeRoles(config('roles.const.ADMIN'));
$validatedData = $request->validate([
'libelle' => 'required',
'sujet' => 'required',
'status' => 'required'
]);
DB::transaction(function () use ($request) {
/** @var \App\Models\User */
$user = auth()->user();
$user->authorizeRoles(config('roles.const.ADMIN'));
$dossierExist = Dossier::where('libelle', $request->libelle)->where('sujet_id', $request->sujet)->first();
if ($dossierExist) {
session()->flash('error', "Ce nom de dossier est déjà présent dans l'application pour ce sujet. Ajout impossible.");
return redirect()->route('dossier.create');
} else {
// ---- Le dossier
$dossier = new Dossier();
$dossier->libelle = $request->libelle;
$dossier->description = $request->description;
$dossier->is_private = $request->status;
$dossier->sujet()->associate($request->sujet);
$dossier = new Dossier();
$dossier->fill($request->validated());
if ($dossier->save()) {
if ($request->sub) {
/** @var \App\Models\Dossier */
$parent_dossier = Dossier::query()->findOrFail($request->input('parent_id'));
$dossier->sujet()->associate($parent_dossier->sujet);
} else {
$dossier->sujet()->associate($request->input('sujet'));
}
if ($dossier->saveOrFail()) {
// ---- Les marques
$sMarques = $request->marques;
if ($sMarques != "") {
$marques = explode(",", $sMarques);
foreach ($marques as $marqueId) {
$dossier->marques()->attach($marqueId);
}
if ($request->input('marques')) {
$dossier->marques()->sync($request->input('marques'));
}
session()->flash('message', "Le dossier a été enregistré");
return redirect()->route('dossier.edit', ['dossier' => $dossier]);
} else {
session()->flash('error', "Une erreur s'est produite, ajout impossible");
return redirect()->route('dossier.create');
return redirect()->route('dossier.edit', ['dossier' => $dossier])
->with('success', 'Le dossier a été enregistré');
}
}//end if
}
public function storeSub(Request $request)
{
Auth::user()->authorizeRoles(config('roles.const.ADMIN'));
$validatedData = $request->validate([
'libelle' => 'required',
'parent_id' => 'required',
]);
$dossierExist = Dossier::where('libelle', $request->libelle)->first();
$parent_dossier = Dossier::findOrFail($request->parent_id);
if ($dossierExist) {
session()->flash('error', "Ce nom de dossier est déjà présent dans l'application pour ce sujet. Ajout impossible.");
return redirect()->route('dossier.create');
} else {
// ---- Le dossier
$dossier = new Dossier();
$dossier->libelle = $request->libelle;
$dossier->description = $request->description;
$dossier->is_private = 0;
$dossier->parent_id = $request->parent_id;
$dossier->sujet_id = $parent_dossier->sujet_id;
if ($dossier->save()) {
session()->flash('message', "Le dossier a été enregistré");
return redirect()->route('dossier.edit', ['dossier' => $dossier]);
} else {
session()->flash('error', "Une erreur s'est produite, ajout impossible");
return redirect()->route('dossier.create');
}
}
}
/**
* Display the specified resource.
*
* @param \App\Models\Dossier $dossier
* @return \Illuminate\Http\Response
*/
public function show(Dossier $dossier)
{
});
return back();
}
/**
* Show the form for editing the specified resource.
*
* @param \App\Models\Dossier $dossier
* @return \Illuminate\Http\Response
* @return \Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory
*/
public function edit(Dossier $dossier)
{
Auth::user()->authorizeRoles(config('roles.const.ADMIN'));
/** @var \App\Models\User */
$user = auth()->user();
$user->authorizeRoles(config('roles.const.ADMIN'));
$marques = Marque::all();
/** @var \Illuminate\Database\Eloquent\Collection<\App\Models\Marque> */
$dossierMarques = $dossier->marques;
$marques = Marque::all();
$dossierSujets = $dossier->sujets;
$sujets = Sujet::where('sujet_dam', 1)->get();
return view('dossiers.edit')->with(['dossier' => $dossier, 'dossierMarques' => $dossierMarques, 'marques' => $marques, 'sujets' => $sujets]);
/** @var \Illuminate\Database\Eloquent\Collection<\App\Models\Sujet> */
$dossierSujets = $dossier->sujets;
/** @var \Illuminate\Database\Eloquent\Collection<\App\Models\Sujet> */
$sujets = Sujet::query()->where('sujet_dam', true)->get();
return view('dossiers.edit', [
'dossier' => $dossier,
'dossierMarques' => $dossierMarques,
'marques' => $marques,
'sujets' => $sujets
]);
}
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param \App\Models\Dossier $dossier
* @return \Illuminate\Http\Response
* @param \App\Http\Requests\Bo\Folders\UpdateFolderRequest $request
* @param \App\Models\Dossier $dossier
* @return \Illuminate\Http\RedirectResponse
*/
public function update(Request $request, Dossier $dossier)
public function update(UpdateFolderRequest $request, Dossier $dossier): RedirectResponse
{
Auth::user()->authorizeRoles(config('roles.const.ADMIN'));
DB::transaction(function () use ($request, $dossier) {
/** @var \App\Models\User */
$user = auth()->user();
$user->authorizeRoles(config('roles.const.ADMIN'));
if ($dossier->parent_id === null) {
$validatedData = $request->validate([
'libelle' => 'required',
'sujet' => 'required',
'status' => 'required'
]);
// ---- Le dossier
$dossier->fill($request->validated());
$dossierExist = Dossier::where('libelle', $request->libelle)->where('sujet_id', $request->sujet)->where('id', '<>', $dossier->id)->first();
if ($dossierExist) {
session()->flash('error', "Ce nom de dossier est déjà présent dans l'application pour ce sujet. Ajout impossible.");
return redirect()->route('dossier.edit', ['dossier' => $dossier]);
} else {
// ---- Le dossier
$dossier->libelle = $request->libelle;
$dossier->description = $request->description;
$dossier->is_private = $request->status;
$dossier->sujet()->dissociate();
$dossier->sujet()->associate($request->sujet);
if ($dossier->save()) {
// ---- Les marques
$dossier->marques()->detach();
$sMarques = $request->marques;
if ($sMarques != "") {
$marques = explode(",", $sMarques);
foreach ($marques as $marqueId) {
$dossier->marques()->attach($marqueId);
}
}
session()->flash('message', "Le dossier a été mis à jour");
return redirect()->route('dossier.edit', ['dossier' => $dossier]);
} else {
session()->flash('error', "Une erreur s'est produite, enregistrement impossible");
return redirect()->route('dossier.edit');
}
}//end if
} else {
$validatedData = $request->validate([
'libelle' => 'required',
]);
$dossierExist = Dossier::where('libelle', $request->libelle)->where('sujet_id', $request->sujet)->where('id', '<>', $dossier->id)->first();
if ($dossierExist) {
session()->flash('error', "Ce nom de dossier est déjà présent dans l'application pour ce sujet. Ajout impossible.");
return redirect()->route('dossier.edit', ['dossier' => $dossier]);
} else {
// ---- Le dossier
$dossier->libelle = $request->libelle;
$dossier->description = $request->description;
if ($dossier->save()) {
session()->flash('message', "Le dossier a été mis à jour");
return redirect()->route('dossier.edit', ['dossier' => $dossier]);
} else {
session()->flash('error', "Une erreur s'est produite, enregistrement impossible");
return redirect()->route('dossier.edit');
if ($dossier->saveOrFail()) {
// ---- Les marques
if ($request->marques) {
$dossier->marques()->sync($request->marques);
}
return redirect()->route('dossier.edit', ['dossier' => $dossier])
->with('success', trans('Le dossier a été mis à jour'));
}
}//end if
});
return back();
}
/**
* Remove the specified resource from storage.
*
* @param \App\Models\Dossier $dossier
* @return \Illuminate\Http\Response
* @return \Illuminate\Http\RedirectResponse
*/
public function destroy(Dossier $dossier)
public function destroy(Dossier $dossier): RedirectResponse
{
Auth::user()->authorizeRoles(config('roles.const.ADMIN'));
if ($dossier->delete()) {
return redirect(route('dossier.index'));
} else {
session()->flash('error', 'Erreur lors de la suppression');
return redirect()->route('dossier.edit', ['user' => $dossier]);
/** @var \App\Models\User */
$user = auth()->user();
$user->authorizeRoles(config('roles.const.ADMIN'));
if ($dossier->deleteOrFail()) {
return \response()->redirectToRoute('dossier.index')
->with('warning', trans('Le dossier à été supprimé'));
}
}
/**
* Chargement des dossiers
*
*/
public function loadFolder(Request $request)
{
$response = array();
$sujetId = $request->input('sujetId');
$response['variantes'] = Variante::where('sujet_id', $sujetId)->get();
$FormatsSujet = Format::whereHas('sujets', function ($query) use ($sujetId) {
$query->where('sujet_id', $sujetId);
})->get();
$FormatsSystem = Format::withoutGlobalScope('App\Scopes\SystemScope')->where('system', true)->get();
$response['formats'] = $FormatsSujet->merge($FormatsSystem);
if (Auth::user()->hasRole([config('roles.const.ADMIN'), config('roles.const.REDACTEUR')])) {
$response['draggable'] = true;
} else {
$response['draggable'] = false;
}
// Envoi du role
$response['role'] = Auth::user()->role;
return json_encode($response);
}
/**
* Ajouter un dossier
*
*/
public function addFolder(Request $request)
{
$response = array();
Auth::user()->authorizeRoles([config('roles.const.ADMIN'), config('roles.const.REDACTEUR')]);
$parent_id = $request->input('parent_id');
$libelle = $request->input('libelle');
$description = $request->input('description');
// ---- Le dossier
$dossier = new Dossier();
$dossier->libelle = htmlspecialchars($libelle);
$dossier->description = $description;
$dossier->parent_id = $parent_id;
$dossierParent = Dossier::where('id', $parent_id)->first();
if ($parent_id == 1 || $dossierParent->system) {
// dossier de dépôt
$dossier->system = true;
} else {
$dossier->system = false;
}
$dossier->sujet_id = $dossierParent->sujet_id;
$dossier->save();
$response['folder_id'] = $dossier->id;
$response['system'] = $dossier->system ? 1 : 0;
return json_encode($response);
return back();
}
/**
* Ajouter un dossier de dépôt
*
* @param \App\Http\Requests\Bo\Folders\StoreDepotFolderRequest $request
* @return \Illuminate\Http\JsonResponse
*/
public function addDepotFolder(Request $request)
public function addDepotFolder(StoreDepotFolderRequest $request): JsonResponse
{
$response = array();
$parent_id = $request->input('parent_id');
$libelle = $request->input('libelle');
$description = $request->input('description');
// ---- Le dossier
$dossier = new Dossier();
$dossier->libelle = htmlspecialchars($libelle);
$dossier->description = $description;
$dossier->parent_id = 1;
$dossier->system = true;
$dossier->save();
$response['folder_id'] = $dossier->id;
return json_encode($response);
}
/**
* supprimer un dossier
*
*/
public function removeFolder(Request $request)
{
Auth::user()->authorizeRoles([config('roles.const.ADMIN'), config('roles.const.REDACTEUR')]);
$dossier_id = $request->input('dossier_id');
// ---- Le dossier
$dossier = Dossier::where('id', $dossier_id)->first();
if ($dossier && !is_null($dossier->parent_id)) {
if ($dossier->delete()) {
return json_encode($dossier->parent_id);
}
}
return json_encode(false);
}
/**
* déplacer un dossier
*
*/
public function moveFolder(Request $request)
{
Auth::user()->authorizeRoles([config('roles.const.ADMIN'), config('roles.const.REDACTEUR')]);
$dossier_id = $request->input('dossier_id');
$parent_id = $request->input('parent_id');
$sujet_id = $request->input('sujet_id');
// ---- Le dossier
$dossier = Dossier::where('id', $dossier_id)->first();
if ($dossier && !is_null($dossier->parent_id)) {
$dossier->parent_id = $parent_id;
if ($dossier->save()) {
Media::where('dossier_id', $dossier_id)->update(['sujet_id' => $sujet_id]);
return json_encode(true);
}
}
return json_encode(false);
}
/**
* renommer un dossier
*
*/
public function renameFolder(Request $request)
{
Auth::user()->authorizeRoles([config('roles.const.ADMIN'), config('roles.const.REDACTEUR')]);
$dossier_id = $request->input('dossier_id');
$libelle = $request->input('libelle');
// ---- Le dossier
$dossier = Dossier::where('id', $dossier_id)->first();
if ($dossier && !is_null($dossier->parent_id)) {
$dossier->libelle = $libelle;
if ($dossier->save()) {
return json_encode(true);
}
}
return json_encode(false);
}
/**
* get propriétés d'un dossier
*
*/
public function getFolderProperty(Request $request)
{
$response = array();
$dossier_id = $request->input('dossier_id');
// ---- Le dossier
$dossier = Dossier::where('id', $dossier_id)->first();
if ($dossier) {
$response["data"] = $dossier;
}
return json_encode($response);
}
/**
* enregistre les propriétés d'un dossier
*
*/
public function saveFolderProperty(Request $request)
{
$dossier_id = $request->input('dossier_id');
$libelle = $request->input('libelle');
$description = $request->input('description');
// ---- Le dossier
$dossier = Dossier::where('id', $dossier_id)->first();
if ($dossier && !is_null($dossier->parent_id) && strlen($libelle) > 3) {
$dossier->libelle = $libelle;
$dossier->description = $description;
$dossier->save();
}
return json_encode(true);
}
public function loadFolders(Request $request)
{
$folders = array();
$user = Auth::user();
$sujet_id = $request->sujet_id;
$dossierQuery = Dossier::where([
['sujet_id', $sujet_id],
['is_private', 0]
])
->whereNull('parent_id');
$dossierQuery = $dossierQuery->get();
// On récupère les dossiers privés
if ($user->hasRole(config('roles.const.ADMIN'))) {
$privateQuery = Dossier::where([
['is_private', '=', 1],
['sujet_id', '=', $sujet_id],
])->get();
} else {
$marques = $user->marques()->pluck("marque_id");
$privateQuery = Dossier::where([
['is_private', '=', 1],
['sujet_id', '=', $sujet_id],
])->whereHas("marques", function ($query) use ($marques) {
$query->whereIn("marque_id", $marques);
})->get();
}
$dossierQuery = $dossierQuery->merge($privateQuery);
$dossierQuery = $dossierQuery->sortBy('libelle', SORT_NATURAL | SORT_FLAG_CASE)->values();
// Ajout du dépôt de fichier pour les admin
if ($user->hasRole(config('roles.const.ADMIN'))) {
$depotQuery = Dossier::whereNull('parent_id')
->where('system', true)
->orderBy('libelle', 'ASC')
->get();
$folders['data'] = $dossierQuery->merge($depotQuery);
} else {
$folders['data'] = $dossierQuery;
}
$folders['racineCount'] = Media::where('sujet_id', $sujet_id)
->whereNull('dossier_id')
->where(function ($query) use ($user) {
$query->where('statut', config('services.statut.public_etendu'))
->orWhere('statut', config('services.statut.public'))
->orWhere('created_by', $user->id);
})
->count();
return response()->json($folders);
$dossier = new Dossier($request->validated());
$dossier->parent_id = 1;
$dossier->system = true;
$dossier->saveOrFail();
return \response()->json([
'folder_id' => $dossier->id
]);
}
}

File diff suppressed because it is too large Load diff

View file

@ -2,15 +2,17 @@
namespace App\Http\Controllers;
use App\Http\Requests\Bo\AjaxAddMediasBasketRequest;
use App\Http\Requests\Bo\AjaxCreateBasketRequest;
use App\Http\Requests\Bo\AjaxDestroyBasketRequest;
use App\Http\Requests\Bo\AjaxRemoveMediasBasketRequest;
use App\Http\Requests\Bo\AjaxSaveBasketPropsRequest;
use App\Http\Requests\Bo\Ajax\AddMediasBasketRequest;
use App\Http\Requests\Bo\Ajax\CreateBasketRequest;
use App\Http\Requests\Bo\Ajax\DestroyBasketRequest;
use App\Http\Requests\Bo\Ajax\RemoveMediasBasketRequest;
use App\Http\Requests\Bo\Ajax\SaveBasketPropsRequest;
use Illuminate\Support\Str;
use App\Models\Panier;
use App\Models\Format;
use App\Models\Media;
use Illuminate\Contracts\View\View;
use Illuminate\Http\JsonResponse;
class PanierController extends Controller
{
@ -19,15 +21,16 @@ class PanierController extends Controller
/**
* Page principale des paniers
*
* @return \Illuminate\View\View|\Illuminate\Contracts\View\Factory
* @return \Illuminate\Contracts\View\View
*/
public function index()
public function index(): View
{
/** @var \App\Models\User $user */
$user = \auth()->user();
/** @var \Illuminate\Database\Eloquent\Collection $paniers */
// ! Has to flush Media cache before querying basket with media list
Media::flushQueryCache();
$paniers = $user->paniersOwner;
return view('panier.index')->with([
'paniers' => $paniers,
]);
@ -39,12 +42,12 @@ class PanierController extends Controller
*
* @return \Illuminate\Http\JsonResponse
*/
public function getBaskets()
public function getBaskets(): JsonResponse
{
/** @var \App\Models\User $user */
$user = \auth()->user();
/** @var \Illuminate\Database\Eloquent\Collection $paniers */
// ! Has to flush Media cache before querying basket with media list
Media::flushQueryCache();
$paniers = $user->paniersOwner;
return response()->json($paniers);
@ -53,41 +56,48 @@ class PanierController extends Controller
/**
* Ajout de média à un panier
*
* @param AjaxAddMediasBasketRequest $request
* @param \App\Http\Requests\Bo\Ajax\AddMediasBasketRequest $request
* @return \Illuminate\Http\JsonResponse
*/
public function addMediasBasket(AjaxAddMediasBasketRequest $request)
public function addMediasBasket(AddMediasBasketRequest $request): JsonResponse
{
/** @var \App\Models\Panier $panier */
$panier = Panier::whereId($request->panier_id)
->with('medias')->firstOrFail();
$panier = Panier::query()
->where('id', $request->input('panier_id'))
->with('mediaList')
->firstOrFail();
// * Sync every media to basket
$panier->medias()->sync(array_merge(
$panier->medias()->pluck('id')->all(),
$request->medias
$panier->mediaList()->sync(array_merge(
$panier->mediaList()->pluck('id')->all(),
$request->input('mediaList')
));
Media::flushQueryCache();
return response()->json();
}
/**
* Suppression de média à un panier
*
* @param AjaxRemoveMediasBasketRequest $request
* @param \App\Http\Requests\Bo\Ajax\RemoveMediasBasketRequest $request
* @return \Illuminate\Http\JsonResponse
*/
public function removeMediasBasket(AjaxRemoveMediasBasketRequest $request)
public function removeMediasBasket(RemoveMediasBasketRequest $request): JsonResponse
{
/** @var \App\Models\Panier $panier */
$panier = Panier::whereId($request->panier_id)
->with('medias')->firstOrFail();
$panier = Panier::query()
->where('id', $request->input('panier_id'))
->with('mediaList')
->firstOrFail();
/** @var \App\Models\Media $media */
$media = Media::whereId($request->media_id)->firstOrFail();
$media = Media::query()
->where('id', $request->input('media_id'))
->firstOrFail();
$panier->medias()->detach($media);
$panier->mediaList()->detach($media);
return response()->json(true);
}
@ -95,16 +105,16 @@ class PanierController extends Controller
/**
* Ajoute un panier
*
* @param AjaxCreateBasketRequest $request
* @param \App\Http\Requests\Bo\Ajax\CreateBasketRequest $request
* @return \Illuminate\Http\JsonResponse
*/
public function createBasket(AjaxCreateBasketRequest $request)
public function createBasket(CreateBasketRequest $request): JsonResponse
{
/** @var \App\Models\User $user */
$user = auth()->user();
$panier = new Panier();
$panier->libelle = $request->title;
$panier->libelle = $request->input('title');
$panier->uid = Str::uuid();
$panier->owner()->associate($user);
$panier->saveOrFail();
@ -115,13 +125,15 @@ class PanierController extends Controller
/**
* Supprimer un panier
*
* @param AjaxDestroyBasketRequest $request
* @param \App\Http\Requests\Bo\Ajax\DestroyBasketRequest $request
* @return \Illuminate\Http\JsonResponse
*/
public function destroyBasket(AjaxDestroyBasketRequest $request)
public function destroyBasket(DestroyBasketRequest $request): JsonResponse
{
/** @var \App\Models\Panier $basket */
$basket = Panier::whereId($request->basket_id)->firstOrFail();
$basket = Panier::query()
->where('id', $request->input('basket_id'))
->firstOrFail();
$basket->deleteOrFail();
@ -133,10 +145,10 @@ class PanierController extends Controller
/**
* Page publique d'un panier
*
* @param Panier $panier
* @return \Illuminate\View\View|\Illuminate\Contracts\View\Factory
* @param \App\Models\Panier $panier
* @return \Illuminate\Contracts\View\View
*/
public function showBasket(Panier $panier)
public function showBasket(Panier $panier): View
{
/** @var \App\Models\User|null $user */
$user = auth()->user();
@ -149,10 +161,13 @@ class PanierController extends Controller
}
}
/** @var \Illuminate\Database\Eloquent\Collection $formats */
$formats = Format::withoutGlobalScope('App\Scopes\SystemScope')
->where('system', true)->get();
return view('panier.show')->with([
/** @var \Illuminate\Database\Eloquent\Collection<\App\Models\Format> $formats */
$formats = Format::query()
->withoutGlobalScope('App\Scopes\SystemScope')
->where('system', true)
->get();
return view('panier.show', [
'panier' => $panier,
'formats' => $formats,
]);
@ -161,13 +176,15 @@ class PanierController extends Controller
/**
* Enregistre les propriétés d'un panier
*
* @param AjaxSaveBasketPropsRequest $request
* @param \App\Http\Requests\Bo\Ajax\SaveBasketPropsRequest $request
* @return \Illuminate\Http\JsonResponse
*/
public function saveBasketProperty(AjaxSaveBasketPropsRequest $request)
public function saveBasketProperty(SaveBasketPropsRequest $request): JsonResponse
{
/** @var \App\Models\Panier $panier */
$panier = Panier::whereId($request->basket_id)->firstOrFail();
$panier = Panier::query()
->where('id', $request->input('basket_id'))
->firstOrFail();
$panier->fill($request->validated());
$panier->saveOrFail();

View file

@ -4,25 +4,33 @@ namespace App\Http\Controllers;
use App\Models\Media;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class RecoveryController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
Auth::user()->authorizeRoles(config('roles.const.ADMIN'));
/** @var \App\Models\User */
$user = auth()->user();
$user->authorizeRoles(config('roles.const.ADMIN'));
return view('recovery.index');
}
/**
* Check if ArcadiaId exists.
*
* @param Request $request
* @return \Illuminate\Http\JsonResponse
*/
public function checkArcadiaId(Request $request)
{
$response = false;
$arcadia_id = $request->id;
$media = Media::onlyTrashed()->where('arcadia_id', '=', $arcadia_id)->first();
if ($media) {
$response = true;
}
return json_encode($response);
return \response()->json(
Media::onlyTrashed()->where('arcadia_id', '=', \intval($request->id))->exists()
);
}
}

View file

@ -2,548 +2,349 @@
namespace App\Http\Controllers;
use App\Http\Traits\MediaTools;
use App\Models\Historique;
use App\Enums\MediaExtension;
use App\Http\Requests\Bo\Ajax\UploadMediaFileRequest;
use App\Lib\ToolboxHelper;
use App\Models\Copyright;
use App\Models\Dossier;
use App\Models\Etiquette;
use App\Models\Langue;
use App\Models\Media;
use App\Models\Site;
use App\Models\Sujet;
use App\Models\TypeMedia;
use App\Models\User;
use App\Traits\Http\HandlesMedia;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Str;
use Illuminate\Validation\ValidationException;
use Pion\Laravel\ChunkUpload\Exceptions\UploadMissingFileException;
use Pion\Laravel\ChunkUpload\Receiver\FileReceiver;
use Symfony\Component\Mime\MimeTypes;
class UploadController extends Controller
{
use MediaTools;
use HandlesMedia;
public function index(Request $request)
/**
* Show upload form
*
* @param Request $request
* @return \Illuminate\Contracts\Validation\Validator|\Illuminate\Contracts\Validation\Factory
*/
public function showUploadForm(Request $request)
{
// ---- Vérification du token de sécurité
// TODO: Quand le DAM sera ouvert au public, Enforce security with dynamic tokens.
// ---- Vérification du token de sécurité .
$token = $request->get('token');
if ($token != env('TOKEN_DEPOT', "c8c4613a-236c-4b06-95f6-a9c9c6c18861")) {
return abort(404);
}
$sites = Site::orderBy('libelle')->get();
return view('upload.show')->with(
[
return view('upload.show')->with([
'sites' => $sites,
]
);
}
public function storeMedia(Request $request)
{
$uid = $request->input('uid');
$sujet_id = $request->input('sujet_id');
$dossier_id = $request->input('dossier_id');
$replaceMediaId = $request->input('replaceMediaId');
if ($dossier_id == 0) {
$dossier_id = null;
}
$headers = $request->headers->all();
$acceptFileTypes = array('gif','jpeg', 'jpg','png','mp4','mp3','psd','ai','svg','pdf');
if ($request->hasFile('files')) {
$filesa = [];
/** @var array<\Illuminate\Http\UploadedFile> */
$files = $request->file('files');
foreach ($files as $file) {
$is_last_chunk = false;
$is_chunked_upload = !empty($headers['content-range']);
if ($is_chunked_upload) {
if (preg_match('|(\d+)/(\d+)|', $headers['content-range'][0], $range)) {
if ($range[1] == $range[2] - 1) {
$is_last_chunk = true;
}
}
}
$extension = strtolower($file->getClientOriginalExtension());
$originalName = str_replace("." . $extension, "", $file->getClientOriginalName());
if (!in_array($extension, $acceptFileTypes)) {
return array("false");
}
if ($is_chunked_upload) {
$filename = $uid . "_" . $range[1] . "." . $extension;
} else {
$filename = $uid . '.' . $extension;
}
$uploadPath = public_path() . '/uploads/';
$file->move($uploadPath, $filename);
// * si c'est le dernier morceux d'un fichier
if ($is_last_chunk) {
if ($dossier = opendir($uploadPath)) {
$dirFiles = array();
// * on compile tous les chunk du fichier
while (false !== ($fichier = readdir($dossier))) {
if ($fichier != '.' && $fichier != '..') {
if (strpos($fichier, $uid) !== false) {
$dirFiles[$fichier] = str_replace(
".{$extension}",
"",
str_replace("{$uid}_", "", $fichier)
);
}
}
}
closedir($dossier);
// * on les trie pour les assembler dans le bon ordre
asort($dirFiles, SORT_NUMERIC);
// * on les assemble dans un fichier final
$filename = $uid . '.' . $extension;
foreach ($dirFiles as $key => $fichier) {
$file = fopen($uploadPath . $key, 'rb');
$buff = fread($file, filesize($uploadPath . $key));
fclose($file);
$final = fopen($uploadPath . $filename, 'a');
$write = fwrite($final, $buff);
fclose($final);
unlink($uploadPath . $key);
}
}//end if
}//end if
// * si c'est le dernier morceau d'un fichier ou que c'est un envoi simple
if ($is_last_chunk || !$is_chunked_upload) {
$destinationPath = $this->getMediaFolder() . "/";
// * si on upload un média pour remplacer un fichier par un autre
if ($replaceMediaId != 0) {
// * on récupère le média à modifier
/** @var \App\Models\Media|null */
$media = Media::where('id', $replaceMediaId)->first();
// * On déplace le fichier dans le répertoire des médias
if (!Storage::move("uploads/{$filename}", $destinationPath . $filename)) {
throw new \ErrorException(
"Could not move uploaded file $filename into {$destinationPath}{$filename}"
);
}
// * supprime les fichiers médias stockés sur disque
$filesfF = Storage::files($media->folder . "/");
$filepath = preg_quote($media->folder . $media->uid, '/');
$matchingFiles = preg_grep('/^' . $filepath . '/', $filesfF);
Storage::delete($matchingFiles);
$media->isDefault = 0;
$media->save();
} else {
// * On déplace le fichier dans le répertoire des médias
if (!Storage::move("uploads/{$filename}", $destinationPath . $filename)) {
throw new \ErrorException(
"Could not move uploaded file $filename into {$destinationPath}{$filename}"
);
}
// * on créé un média en bdd
$media = new Media();
$media->dossier_id = $dossier_id;
$media->sujet_id = $sujet_id;
// * Par défaut FR
$media->langue_id = 1;
// * Par défaut propriété de la marque
$media->copyright_id = 3;
}//end if
$media->extension = $extension;
$media->folder = $destinationPath;
if ($replaceMediaId == 0) {
$media->libelle = mb_substr($originalName, 0, 50, "UTF-8");
$media->created_by = Auth::user()->id;
}
$media->uid = $uid;
$media->taille = $this->formatBytes(Storage::size($destinationPath . $filename));
switch ($extension) {
// * Photo (1)
case 'gif':
case 'jpeg':
case 'jpg':
case 'png':
case 'ai':
case 'pdf':
case 'psd':
$media->typemedia_id = 1;
$im = new \Imagick($destinationPath . $filename);
if (in_array($extension, array("pdf", "ai", "psd", "gif"))) {
$im->setIteratorIndex(0);
}
$resolutions = $im->getImageResolution();
$media->resolution = round($resolutions['x'] * 2.54);
$imageprops = $im->getImageGeometry();
$media->largeur = $imageprops['width'];
$media->hauteur = $imageprops['height'];
if ($media->resolution != 0) {
$media->largeur_mm = ceil(($media->largeur * 2.54 / $media->resolution) * 10);
$media->hauteur_mm = ceil(($media->hauteur * 2.54 / $media->resolution) * 10);
} else {
$media->largeur_mm = "";
$media->hauteur_mm = "";
}
$media->save();
$im->destroy();
// ---- Creation des vignettes de base
$this->createSysThumb($media);
// ---- Creation des vignettes en lien avec le sujet du média
if (isset($sujet_id)) {
$this->createThumb($media);
}
break;
// * Illustration/Vectorielle (2)
case 'svg':
$media->typemedia_id = 2;
$media->save();
break;
// * Vidéo (3)
case 'mp4':
$media->typemedia_id = 3;
$getID3 = new \getID3();
if ($fileInfo = $getID3->analyze($destinationPath . $filename)) {
$media->largeur = $fileInfo['video']['resolution_x'];
$media->hauteur = $fileInfo['video']['resolution_y'];
$media->duree = $fileInfo['playtime_string'];
}
$media->save();
break;
// * Audio (4)
case 'mp3':
$media->typemedia_id = 4;
$getID3 = new \getID3();
if ($fileInfo = $getID3->analyze($destinationPath . $filename)) {
$media->duree = $fileInfo['playtime_string'];
}
$media->save();
break;
}//end switch
$id = $media->id;
$media = Media::find($id);
// ---- Index dans Elasticsearch
$media = Media::where('id', $media->id)->first();
$media->addToIndex();
// ---- Historique des actions
$historique = new Historique();
if ($user = Auth::user()) {
$historique->user_id = $user->id;
}
$historique->media_id = $media->id;
if ($replaceMediaId == 0) {
$historique->type = "Création";
$historique->description = "Média ajouté";
} else {
$historique->type = "Modification";
$historique->description = "Le média a été remplacé";
}
$historique->save();
$filest = array();
$filest['name'] = $filename;
$filest['uid'] = $uid;
$filest['libelle'] = $media->libelle;
$filest['id'] = $media->id;
$filest['folder'] = $media->folder;
$filest['extension'] = $media->extension;
$filest['typemedia_id'] = $media->typemedia_id;
$filesa['files'][] = $filest;
}//end if
}//end foreach
return $filesa;
}//end if
}
public function storeMediaV2(Request $request)
{
$uid = $request->input('uid');
$sujet_id = $request->input('sujet_id');
$dossier_id = $request->input('dossier_id');
$replaceMediaId = $request->input('replaceMediaId');
if ($dossier_id == 0) {
$dossier_id = null;
}
$filesa = [];
$acceptFileTypes = array('gif','jpeg', 'jpg','png','mp4','mp3','psd','ai','svg','pdf');
if (!$uid) {
$uid = Str::uuid();
}
$file = $request->file('files');
if (!empty($file)) {
$extension = strtolower($file->getClientOriginalExtension());
$originalName = str_replace("." . $extension, "", $file->getClientOriginalName());
if (!in_array($extension, $acceptFileTypes)) {
return array("false");
}
if (!in_array($extension, $acceptFileTypes)) {
return array("error" => "Ce type de fichier n'est pas accepté.");
}
// * check the error status
if ($file->getError() != 0) {
Log::error('error ' . $file->getError() . ' in file ' . $request->resumableFilename);
}
$temp_dir = 'uploads';
/**
* init the destination file (format <filename.ext>.part<#chunk>
* the file is stored in a temporary directory
*/
if (isset($request->resumableIdentifier) && trim($request->resumableIdentifier) != '') {
$temp_dir .= "/{$request->resumableIdentifier}";
}
$dest_file = "{$temp_dir}/{$request->resumableFilename}.part{$request->resumableChunkNumber}";
// * create the temporary directory
if (!is_dir($temp_dir)) {
mkdir($temp_dir, 0777, true);
}
// * move the temporary file
if (!move_uploaded_file($file->getPathname(), $dest_file)) {
Log::error(
//phpcs:ignore Generic.Files.LineLength.TooLong
"Error saving (move_uploaded_file) chunk $request->resumableChunkNumber for file $request->resumableFilename"
);
} else {
$filename = $uid . '.' . $extension;
// * check if all the parts present, and create the final destination file
$isLast = $this->createFileFromChunks(
$temp_dir,
$request->resumableFilename,
$request->resumableChunkSize,
$request->resumableTotalSize,
$request->resumableTotalChunks,
$filename
);
if ($isLast) {
$destinationPath = $this->getMediaFolder() . "/";
// * si on upload un média pour remplacer un fichier par un autre
if ($replaceMediaId != 0) {
// * on récupère le média à modifier
/** @var \App\Models\Media $media */
$media = Media::where('id', $replaceMediaId)->firstOrFail();
// * On déplace le fichier dans le répertoire des médias
Storage::move("uploads/" . $filename, $destinationPath . $filename);
// * supprime les fichiers médias stockés sur disque
$filesfF = Storage::files($media->folder . "/");
$filepath = preg_quote($media->folder . $media->uid, '/');
$matchingFiles = preg_grep('/^' . $filepath . '/', $filesfF);
Storage::delete($matchingFiles);
$media->isDefault = 0;
$media->save();
} else {
// * On déplace le fichier dans le répertoire des médias
Storage::move("uploads/" . $filename, $destinationPath . $filename);
// * on créé un média en bdd
$media = new Media();
$media->dossier_id = $dossier_id;
$media->sujet_id = $sujet_id;
// * Par défaut FR
$media->langue_id = 1;
// * Par défaut propriété de la marque
$media->copyright_id = 3;
}//end if
$media->extension = $extension;
$media->folder = $destinationPath;
if ($replaceMediaId == 0) {
$media->libelle = mb_substr($originalName, 0, 50, "UTF-8");
$media->created_by = Auth::user()->id;
}
$media->uid = $uid;
$media->taille = $this->formatBytes($this->getFileSize($destinationPath . $filename));
switch ($extension) {
// * Photo (1)
case 'gif':
case 'jpeg':
case 'jpg':
case 'png':
case 'ai':
case 'pdf':
case 'psd':
$media->typemedia_id = 1;
$im = new \imagick($destinationPath . $filename);
if (in_array($extension, array("pdf", "ai", "psd", "gif"))) {
$im->setIteratorIndex(0);
}
$resolutions = $im->getImageResolution();
$media->resolution = round($resolutions['x'] * 2.54);
$imageprops = $im->getImageGeometry();
$media->largeur = $imageprops['width'];
$media->hauteur = $imageprops['height'];
if ($media->resolution != 0) {
$media->largeur_mm = ceil(($media->largeur * 2.54 / $media->resolution) * 10);
$media->hauteur_mm = ceil(($media->hauteur * 2.54 / $media->resolution) * 10);
} else {
$media->largeur_mm = "";
$media->hauteur_mm = "";
}
$media->save();
$im->destroy();
// ---- Creation des vignettes de base
$this->createSysThumb($media);
// ---- Creation des vignettes en lien avec le sujet du média
if (isset($sujet_id)) {
$this->createThumb($media);
}
break;
// * Illustration/Vectorielle (2)
case 'svg':
$media->typemedia_id = 2;
$media->save();
break;
// * Vidéo (3)
case 'mp4':
$media->typemedia_id = 3;
$getID3 = new \getID3();
if ($fileInfo = $getID3->analyze($destinationPath . $filename)) {
$media->largeur = $fileInfo['video']['resolution_x'];
$media->hauteur = $fileInfo['video']['resolution_y'];
$media->duree = $fileInfo['playtime_string'];
}
$media->save();
break;
// * Audio (4)
case 'mp3':
$media->typemedia_id = 4;
$getID3 = new \getID3();
if ($fileInfo = $getID3->analyze($destinationPath . $filename)) {
$media->duree = $fileInfo['playtime_string'];
}
$media->save();
break;
}//end switch
$id = $media->id;
$media = Media::find($id);
// ---- Index dans Elasticsearch
$media = Media::where('id', $media->id)->first();
$media->addToIndex();
// ---- Historique des actions
$historique = new Historique();
if ($user = Auth::user()) {
$historique->user_id = $user->id;
}
$historique->media_id = $media->id;
if ($replaceMediaId == 0) {
$historique->type = "Création";
$historique->description = "Média ajouté";
} else {
$historique->type = "Modification";
$historique->description = "Le média a été remplacé";
}
$historique->save();
return response()->json($media);
}//end if
}//end if
}//end if
]);
}
/**
* Check if all the parts exist, and
* gather all the parts of the file together
* Asynchronous chunked upload file.
*
* @param string $temp_dir The temporary directory holding all the parts of the file.
* @param string $fileName The original file name.
* @param integer $chunkSize Each chunk size (in bytes).
* @param integer $totalSize Original file size (in bytes).
* @param integer $total_files
* @param string $finalName
* @return boolean
* @param \App\Http\Requests\Bo\Ajax\UploadMediaFileRequest $request
* @param \Pion\Laravel\ChunkUpload\Receiver\FileReceiver $receiver
* @return \Illuminate\Http\JsonResponse
* @throws \Pion\Laravel\ChunkUpload\Exceptions\UploadMissingFileException If failed upload.
*/
private function createFileFromChunks(
string $temp_dir,
string $fileName,
int $chunkSize,
int $totalSize,
int $total_files,
string $finalName
): bool {
// * count all the parts of this file
$total_files_on_server_size = 0;
$temp_total = 0;
foreach (scandir($temp_dir) as $file) {
$temp_total = $total_files_on_server_size;
$tempfilesize = filesize($temp_dir . '/' . $file);
$total_files_on_server_size = $temp_total + $tempfilesize;
}
// * check that all the parts are present
// * If the Size of all the chunks on the server is equal to the size of the file uploaded.
if ($total_files_on_server_size >= $totalSize) {
// * create the final destination file
if (($fp = fopen("uploads/{$finalName}", 'w')) !== false) {
for ($i = 1; $i <= $total_files; $i++) {
fwrite($fp, file_get_contents("{$temp_dir}/{$fileName}.part{$i}"));
}
fclose($fp);
} else {
Log::error('cannot create the destination file');
return false;
}
public function uploadMediaFileIntoDepot(
UploadMediaFileRequest $request,
FileReceiver $receiver,
): JsonResponse {
/** @var \App\Models\Dossier */
$dossier = Dossier::where('id', intval($request->dossier_id))->firstOrFail();
// * rename the temporary directory (to avoid access from other
// * concurrent chunks uploads) and than delete it
if (rename($temp_dir, $temp_dir . '_UNUSED')) {
$this->rrmdir($temp_dir . '_UNUSED');
} else {
$this->rrmdir($temp_dir);
// Check if the upload is success, throw exception or return response you need.
if ($receiver->isUploaded() === false) {
throw new UploadMissingFileException();
}
// Receive the file.
$save = $receiver->receive();
// Check if the upload has finished (in chunk mode it will send smaller files).
if ($save->isFinished()) {
/** @var \App\Models\User */
$user = auth()->user();
$uplodedFile = $save->getFile();
try {
/** @throws \Illuminate\Validation\ValidationException */
$this->validateUploadedFile($uplodedFile);
/** @var \App\Models\Media */
$media = Media::whereId($request->media_id)
->whereHas('dossier', function ($q) use ($dossier) {
$q->whereId($dossier->id);
})->firstOrNew();
// Save the file and return any response you need.
return $this->createOrUpdateMedia($user, $uplodedFile, $media, $dossier);
} catch (ValidationException $e) {
// 400 instead of 422 is for resumablejs to handle properly.
return \response()->json($e->errors(), 400);
}
return true;
}//end if
return false;
} //end if
// We are in chunk mode, lets send the current progress.
/** @var AbstractHandler $handler */
$handler = $save->handler();
return response()->json([
'done' => $handler->getPercentageDone()
]);
}
/**
* Delete a directory RECURSIVELY
* Asynchronous chunked upload file.
*
* @param string $dir Directory path.
* @param \App\Http\Requests\Bo\Ajax\UploadMediaFileRequest $request
* @param \Pion\Laravel\ChunkUpload\Receiver\FileReceiver $receiver
* @param \App\Models\Sujet $sujet
* @param \App\Models\Dossier $dossier
* @return \Illuminate\Http\JsonResponse
* @throws \Pion\Laravel\ChunkUpload\Exceptions\UploadMissingFileException If failed upload.
*/
public function uploadMediaFile(
UploadMediaFileRequest $request,
FileReceiver $receiver,
Sujet $sujet,
Dossier $dossier
): JsonResponse {
// Check if the upload is success, throw exception or return response you need.
if ($receiver->isUploaded() === false) {
throw new UploadMissingFileException();
}
// Receive the file.
$save = $receiver->receive();
// Check if the upload has finished (in chunk mode it will send smaller files).
if ($save->isFinished()) {
/** @var \App\Models\User */
$user = auth()->user();
$uplodedFile = $save->getFile();
try {
/** @throws \Illuminate\Validation\ValidationException */
$this->validateUploadedFile($uplodedFile);
/** @var \App\Models\Media */
$media = Media::query()
->where('id', $request->input('media_id'))
->whereHas('sujet', fn(Builder $q) => $q->where('codeSujet', $sujet->codeSujet))
->whereHas('dossier', fn(Builder $q) => $q->where('id', $dossier->id))
->firstOrNew();
// Save the file and return any response you need.
return $this->createOrUpdateMedia($user, $uplodedFile, $media, $dossier, $sujet);
} catch (ValidationException $e) {
// 400 instead of 422 is for resumablejs to handle properly.
return \response()->json($e->errors(), 400);
}
} //end if
// We are in chunk mode, lets send the current progress.
/** @var AbstractHandler $handler */
$handler = $save->handler();
return response()->json([
'done' => $handler->getPercentageDone()
]);
}
/**
* Validate uploaded file
*
* @param \Illuminate\Http\UploadedFile $file
* @return void
* @link http://php.net/manual/en/function.rmdir.php
* @throws \Illuminate\Validation\ValidationException If uploaded file is not valid.
*/
private function rrmdir(string $dir): void
private function validateUploadedFile(UploadedFile $file)
{
if (is_dir($dir)) {
$objects = scandir($dir);
foreach ($objects as $object) {
if ($object != "." && $object != "..") {
if (filetype($dir . "/" . $object) == "dir") {
$this->rrmdir($dir . "/" . $object);
} else {
unlink($dir . "/" . $object);
}
}
}
reset($objects);
rmdir($dir);
Validator::make([
'file' => $file
], [
'file' => 'required|file|mimes:gif,jpeg,jpg,png,mp4,mp3,psd,ai,svg,pdf'
], [
'files.mimes' => trans('Ce type de fichier n\'est pas accepté.')
])->validate();
$this->validateExtension($file);
}
/**
* Validate file extension matches allowed extensions enum
*
* @param \Illuminate\Http\UploadedFile $file
* @return void
* @throws \Illuminate\Validation\ValidationException If file extension could not be confirmed.
*/
private function validateExtension(UploadedFile $file)
{
if (\is_null(MediaExtension::tryFrom($file->getClientOriginalExtension()))) {
throw ValidationException::withMessages([
'file' => trans('Ce type de fichier n\'est pas accepté.')
]);
}
/** @throws \ValueError If value is not defined in enum */
$mediaExtension = MediaExtension::from($file->getClientOriginalExtension());
if ($file->guessExtension()) {
$mt = new MimeTypes();
$guessedExts = $mt->getExtensions($mt->guessMimeType($file->path()));
if (!\in_array($mediaExtension->value, $guessedExts)) {
$guessedExts = \implode(',', $guessedExts);
throw ValidationException::withMessages([
'file' => trans(
'File guessed extensions :guessedExts does not match file name extension :extension',
[
'guessedExt' => $guessedExts,
'extension' => $mediaExtension->value,
]
)
]);
}
}
}
/**
* Create or update media
*
* @param \App\Models\User $user
* @param \Illuminate\Http\UploadedFile $file
* @param \App\Models\Media $media
* @param \App\Models\Dossier $dossier
* @param \App\Models\Sujet|null $sujet
* @return \Illuminate\Http\JsonResponse
* @throws \Illuminate\Validation\ValidationException If $subject is missing and folder
* is not a system folder (Dossier dépot).
* @phpcs:disable Generic.Metrics.CyclomaticComplexity.TooHigh
* (switch) .
*/
private function createOrUpdateMedia(
User $user,
UploadedFile $file,
Media $media,
Dossier $dossier,
Sujet|null $sujet = null
): JsonResponse {
// phpcs:enable
return DB::transaction(function () use ($user, $file, $media, $dossier, $sujet) {
if (!$dossier->system and !$sujet) {
throw ValidationException::withMessages([
'file' => 'Cannot put media inside a non system folder (depot) without a subject'
]);
}
/** @var \Illuminate\Filesystem\FilesystemAdapter */
$storage = Storage::disk('media');
$isNewMedia = !$media->exists;
$mediaFolderName = $this->getCurentMediaFolderName();
// * File size
$fileSize = $file->getSize();
$sanitizedFileName = ToolboxHelper::sanitizeFileName($file->getClientOriginalName());
/** @var string */
$sanitizedFileBasename = \pathinfo($sanitizedFileName, \PATHINFO_BASENAME);
/** @var string */
$sanitizedFileExtension = \pathinfo($sanitizedFileName, \PATHINFO_EXTENSION);
/** @throws \ValueError If value is not defined in enum */
$mediaExtension = MediaExtension::from($sanitizedFileExtension);
if (!$isNewMedia) {
// Supprimer les vielles images.
$media->deleteFilesOnAllPath();
}
// * New folder attribution
$media->folder = $mediaFolderName;
// * New Image, New uuid.
$media->uid = Str::uuid();
// * New Image extension
$media->extension = $mediaExtension;
// * New Image size
$media->taille = $fileSize ? $fileSize : 0;
// * Le media n'est pas une image par défaut.
$media->isDefault = false;
// * On déplace le fichier dans le répertoire des médias.
/** @throws \FileException */
$file->move($storage->path($media->folder), $media->basename);
// * Si on upload un média pour remplacer un fichier par un autre
if ($isNewMedia) {
$media->libelle = Str::limit($sanitizedFileBasename, 50, '');
$media->owner()->associate($user);
$media->dossier()->associate($dossier);
$media->sujet()->associate($sujet);
// * Par défaut FR
$media->langue()->associate(Langue::getDefaultLang());
// * Par défaut propriété de la marque
$media->copyright()->associate(Copyright::getBrandCopyright());
$media->etiquette()->associate(Etiquette::getDefaultEtiquette());
}
switch ($media->extension) {
case MediaExtension::gif:
case MediaExtension::jpeg:
case MediaExtension::jpg:
case MediaExtension::png:
case MediaExtension::ai:
case MediaExtension::pdf:
case MediaExtension::psd:
$dimensions = ToolboxHelper::getImageDimensions($media->full_path);
if ($dimensions) {
$media->resolution = $dimensions['resolutionX'];
$media->largeur = $dimensions['width'];
$media->hauteur = $dimensions['height'];
$media->largeur_mm = !empty($media->resolution) ? ceil($media->largeur * 10) : 0;
$media->hauteur_mm = !empty($media->resolution) ? ceil($media->hauteur * 10) : 0;
}
// ---- Creation des vignettes de base
$media->createThumbnail();
// ---- Creation des vignettes en lien avec le sujet du média
$media->createSubjectThumbnails();
break;
case MediaExtension::mp4:
if ($dimensions = ToolboxHelper::getId3VideoDimensions($media->full_path)) {
$media->largeur = $dimensions['width'];
$media->hauteur = $dimensions['height'];
}
// Fall-through .
case MediaExtension::mp3:
$media->duree = ToolboxHelper::getId3Playtime($media->full_path);
break;
} //end switch
$media->typeMedia()->associate(TypeMedia::getMediaType($media->extension));
$media->saveOrFail();
Media::flushQueryCache();
// * Get props inserted by DB.
return response()->json($media->refresh());
});
}
}

View file

@ -8,6 +8,7 @@ use App\Models\Sujet;
use Illuminate\Http\Request;
use Adldap\Laravel\Facades\Adldap;
use App\Http\Requests\DestroyUserRequest;
use App\Models\Dossier;
use App\Models\Media;
use Illuminate\Support\Facades\DB;
@ -41,7 +42,7 @@ class UserController extends Controller
$localUser->authorizeRoles(config('roles.const.ADMIN'));
$roles = config('roles.label');
$marques = Marque::orderBy('libMarq', 'asc')->get();
$sujets = Sujet::where('sujet_dam', 1)->orderBy('libelle', 'asc')->get();
$sujets = Sujet::where('sujet_dam', true)->orderBy('libelle', 'asc')->get();
return view('users.create')->with([
'roles' => $roles, 'marques' => $marques,
'sujets' => $sujets
@ -188,11 +189,15 @@ class UserController extends Controller
$userMarques = $user->marques;
$marques = Marque::orderBy('libMarq', 'asc')->get();
$userSujets = $user->sujets;
$sujets = Sujet::where('sujet_dam', 1)->orderBy('libelle', 'asc')->get();
$users = User::select(['id', 'email'])->whereIn('role', [
config('roles.const.ADMIN'), config('roles.const.CONTRIBUTEUR'),
config('roles.const.REDACTEUR')
])->where('id', '!=', $user->id)->get();
$sujets = Sujet::where('sujet_dam', true)->orderBy('libelle', 'asc')->get();
$users = User::select(['id', 'email'])
->whereIn('role', [
config('roles.const.ADMIN'), config('roles.const.CONTRIBUTEUR'),
config('roles.const.REDACTEUR')
])
->where('id', '!=', $user->id)
->orderBy('email')
->get();
return view('users.edit')->with([
'user' => $user, 'roles' => $roles, 'users' => $users,
'userMarques' => $userMarques, 'marques' => $marques,
@ -293,23 +298,22 @@ class UserController extends Controller
/**
* Ajouter un dossier en favoris
*
* @param Request $request
* @param \Illuminate\Http\Request $request
* @param \App\Models\Dossier $dossier
* @return \Illuminate\Http\JsonResponse
*/
public function favorisFolder(Request $request)
public function favorisFolder(Request $request, Dossier $dossier)
{
/** @var \App\Models\User $localUser */
$localUser = auth()->user();
$dossier_id = $request->input('dossier_id');
$favoris = $request->input('favoris');
$dossierFavExists = $localUser->dossiers()
->where('id', $dossier->id)->exists();
$dossierFav = $localUser->dossiers()->where('id', $dossier_id)->first();
if ($favoris === false && $dossierFav) {
$localUser->dossiers()->detach($dossier_id);
} elseif ($favoris === true && !$dossierFav) {
$localUser->dossiers()->attach($dossier_id);
if (!$request->boolean('favoris') and $dossierFavExists) {
$localUser->dossiers()->detach($dossier->id);
} elseif ($request->boolean('favoris') and !$dossierFavExists) {
$localUser->dossiers()->attach($dossier->id);
}
return response()->json();
}

View file

@ -14,7 +14,7 @@ class Kernel extends HttpKernel
* @var array<int, class-string|string>
*/
protected $middleware = [
// \App\Http\Middleware\TrustHosts::class,
\App\Http\Middleware\TrustHosts::class,
\App\Http\Middleware\TrustProxies::class,
\Illuminate\Http\Middleware\HandleCors::class,
\App\Http\Middleware\PreventRequestsDuringMaintenance::class,
@ -31,6 +31,7 @@ class Kernel extends HttpKernel
*/
protected $middlewareGroups = [
'web' => [
'htmlsanitize',
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
@ -41,8 +42,10 @@ class Kernel extends HttpKernel
'api' => [
'throttle:api',
// \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
\App\Http\Middleware\JsonOnly::class,
\App\Http\Middleware\ApiResponse::class,
\App\Http\Middleware\ApiCheckTimeStamps::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class
],
];
@ -64,5 +67,6 @@ class Kernel extends HttpKernel
'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
'htmlsanitize' => \App\Http\Middleware\HtmlSanitizer::class,
];
}

View file

@ -0,0 +1,41 @@
<?php
namespace App\Http\Middleware;
use App\Enums\Legacy\ApiError;
use App\Exceptions\Api\ApiErrorException;
use App\Lib\Helper;
use Illuminate\Http\Request;
use Illuminate\Support\Carbon;
/**
* Check Http Header Timestamp with request timestamp
* to prevent replay attacks
* https://restfulapi.net/security-essentials/
*/
class ApiCheckTimeStamps
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
* @throws ApiErrorException With ApiError::wrongTimeStamp if timestamp has an incorrect format or is timed out.
*/
public function handle(Request $request, \Closure $next)
{
if (Helper::isRequestForApi($request)) {
if (!\is_string($request->timestamp) or !Carbon::hasFormat($request->timestamp, 'Y-m-d H:i:s')) {
throw new ApiErrorException(ApiError::wrongTimeStamp());
}
if (
Carbon::parse($request->timestamp, 'utc')
->isBefore(Carbon::now('UTC')->subSeconds(config('api.timestampTimeout', 10)))
) {
throw new ApiErrorException(ApiError::wrongTimeStamp());
}
}
return $next($request);
}
}

View file

@ -0,0 +1,59 @@
<?php
namespace App\Http\Middleware;
use App\Enums\Legacy\ApiStatus;
use App\Http\Resources\Api\ApiResponse as ApiResponseResource;
use App\Models\Api\ApiToken;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
/**
* Make sur the response is an ApiResource resource.
*/
class ApiResponse
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
* @throws \ErrorException If the received response is not a JsonResponse.
*/
public function handle(Request $request, \Closure $next)
{
/** @var \Illuminate\Http\JsonResponse $response */
$response = $next($request);
if (\get_class($response) !== JsonResponse::class) {
// @codeCoverageIgnoreStart
throw new \ErrorException('All api responses should be json.');
// @codeCoverageIgnoreEnd
}
// Set no cache header.
$response->header('Cache-Control', 'No-Store');
/** @var \App\Models\Api\ApiToken|null */
if (\optional($apiToken = app(ApiToken::class))->id) {
$apiToken->incrementStat();
}
/** @var object $data */
$data = (object)$response->getData();
// If reponse is already a ApiResponse then just return it. This happens if error handler comes into action.
if (
isset($data->status) and
isset($data->version) and
in_array($data->status, [ApiStatus::OK()->value, ApiStatus::KO()->value])
) {
return $response;
}
/** @var \App\Enums\Legacy\ApiStatus $status */
$status = intval($response->getStatusCode() / 100) == 2 ? ApiStatus::OK() : ApiStatus::KO();
return $response->setData(new ApiResponseResource((object)\optional($data)->data ?? $data, $status));
}
}

View file

@ -15,6 +15,7 @@ class Authenticate extends Middleware
*/
protected function redirectTo($request)
{
// phpcs:enable
if (! $request->expectsJson()) {
return route('login');
}

View file

@ -0,0 +1,43 @@
<?php
namespace App\Http\Middleware;
use Illuminate\Http\Request;
class HtmlSanitizer
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @param mixed ...$params
* @return mixed
*/
public function handle(Request $request, \Closure $next, mixed ...$params)
{
$inputs = $request->all();
array_walk_recursive($inputs, function (&$input, $name) use ($params) {
if (!is_string($input)) {
return;
}
if (in_array($name, $params)) {
// ? Is a json input ?
if ($isJson = \json_decode($input, true) and is_array($isJson)) {
// ! Decode each json field recursive.
\array_walk_recursive($isJson, function (&$item) {
if (\is_string($item)) {
$item = clean($item);
}
});
} else {
$input = clean($input);
}
} else {
$input = strip_tags($input);
}
});
$request->merge($inputs);
return $next($request);
}
}

View file

@ -0,0 +1,31 @@
<?php
namespace App\Http\Middleware;
use Illuminate\Http\Request;
/**
* Allow ony json requests
*/
class JsonOnly
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle(Request $request, \Closure $next)
{
if (
!$request->isJson() or
!$request->expectsJson() or
is_null(json_decode($request->getContent()))
) {
abort(400, 'Invalid JSON');
}
return $next($request);
}
}

View file

@ -3,7 +3,6 @@
namespace App\Http\Middleware;
use App\Providers\RouteServiceProvider;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
@ -12,13 +11,14 @@ class RedirectIfAuthenticated
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure(\Illuminate\Http\Request): (\Illuminate\Http\Response|\Illuminate\Http\RedirectResponse) $next
* @param string|null ...$guards
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @param string|null ...$guards
* @return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse
*/
public function handle(Request $request, Closure $next, ...$guards)
public function handle(Request $request, \Closure $next, ...$guards)
{
/** @var array<string|null> */
$guards = empty($guards) ? [null] : $guards;
foreach ($guards as $guard) {

View file

@ -1,10 +1,10 @@
<?php
namespace App\Http\Requests\Bo;
namespace App\Http\Requests\Bo\Ajax;
use Illuminate\Foundation\Http\FormRequest;
class AjaxAddMediasBasketRequest extends FormRequest
class AddMediasBasketRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
@ -13,7 +13,7 @@ class AjaxAddMediasBasketRequest extends FormRequest
*/
public function authorize()
{
return auth()->check();
return auth()->check() and $this->ajax();
}
/**
@ -25,8 +25,8 @@ class AjaxAddMediasBasketRequest extends FormRequest
{
return [
'panier_id' => 'required|exists:paniers,id,deleted_at,NULL',
'medias' => 'required|array',
'medias.*' => 'required|exists:medias,id,deleted_at,NULL'
'mediaList' => 'required|array',
'mediaList.*' => 'required|exists:media,id,deleted_at,NULL'
];
}
}

View file

@ -0,0 +1,33 @@
<?php
namespace App\Http\Requests\Bo\Ajax;
use Illuminate\Foundation\Http\FormRequest;
class ApplyStickerRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize(): bool
{
return auth()->check();
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, mixed>
*/
public function rules(): array
{
return [
'etiquette_id' => 'required|exists:etiquettes,etiquette_id',
'mediaList' => 'required|array',
'mediaList.*' => 'array|min:0',
'mediaList.*.id' => 'required|exists:media,id',
];
}
}

View file

@ -0,0 +1,43 @@
<?php
namespace App\Http\Requests\Bo\Ajax;
use App\Models\Media;
use Illuminate\Foundation\Http\FormRequest;
class CheckRefKeyRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize()
{
return \auth()->check() and $this->ajax();
}
/**
* Prepare the data for validation.
*
* @return void
*/
protected function prepareForValidation()
{
$this->merge([
'media' => Media::where('id', $this->media_id)->first()
]);
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, mixed>
*/
public function rules()
{
return [
'media_id' => 'required|exists:media,id,deleted_at,NULL'
];
}
}

View file

@ -1,11 +1,11 @@
<?php
namespace App\Http\Requests\Bo;
namespace App\Http\Requests\Bo\Ajax;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Carbon;
class AjaxCreateBasketRequest extends FormRequest
class CreateBasketRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
@ -18,7 +18,7 @@ class AjaxCreateBasketRequest extends FormRequest
'title' => $this->title ??
sprintf('Panier du %s', Carbon::now()->format('d/m/Y'))
]);
return auth()->check();
return auth()->check() and $this->ajax();
}
/**

View file

@ -0,0 +1,31 @@
<?php
namespace App\Http\Requests\Bo\Ajax;
use Illuminate\Foundation\Http\FormRequest;
class DeleteMediaRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize()
{
return auth()->check() and $this->ajax();
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'mediaList' => 'required|array',
'mediaList.*' => 'required|exists:media,id,deleted_at,NULL'
];
}
}

View file

@ -1,10 +1,10 @@
<?php
namespace App\Http\Requests\Bo;
namespace App\Http\Requests\Bo\Ajax;
use Illuminate\Foundation\Http\FormRequest;
class AjaxDestroyBasketRequest extends FormRequest
class DestroyBasketRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
@ -13,7 +13,7 @@ class AjaxDestroyBasketRequest extends FormRequest
*/
public function authorize()
{
return auth()->check();
return auth()->check() and $this->ajax();
}
/**

View file

@ -0,0 +1,30 @@
<?php
namespace App\Http\Requests\Bo\Ajax;
use Illuminate\Foundation\Http\FormRequest;
class FetchFoldersRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize(): bool
{
return auth()->check() and $this->ajax();
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, mixed>
*/
public function rules()
{
return [
'sujetId' => 'required|exists:sujets,codeSujet'
];
}
}

View file

@ -0,0 +1,30 @@
<?php
namespace App\Http\Requests\Bo\Ajax;
use Illuminate\Foundation\Http\FormRequest;
class GetArticleRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize(): bool
{
return \auth()->check();
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, mixed>
*/
public function rules()
{
return [
'arcadia_id' => 'required|exists:articles,noArt'
];
}
}

View file

@ -0,0 +1,30 @@
<?php
namespace App\Http\Requests\Bo\Ajax;
use Illuminate\Foundation\Http\FormRequest;
class LoadFoldersRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize()
{
return \auth()->check() and $this->ajax();
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, mixed>
*/
public function rules()
{
return [
'sujet_id' => 'required|exists:sujets,codeSujet'
];
}
}

View file

@ -0,0 +1,33 @@
<?php
namespace App\Http\Requests\Bo\Ajax;
use Illuminate\Foundation\Http\FormRequest;
class MoveMediaRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize(): bool
{
return auth()->check() and $this->ajax();
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, mixed>
*/
public function rules()
{
return [
'dossier_id' => 'required|numeric',
'mediaList' => 'required|array',
'mediaList.*' => 'required|array',
'mediaList.*.id' => 'required|exists:media,id',
];
}
}

View file

@ -1,10 +1,10 @@
<?php
namespace App\Http\Requests\Bo;
namespace App\Http\Requests\Bo\Ajax;
use Illuminate\Foundation\Http\FormRequest;
class AjaxRemoveMediasBasketRequest extends FormRequest
class RemoveMediasBasketRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
@ -13,7 +13,7 @@ class AjaxRemoveMediasBasketRequest extends FormRequest
*/
public function authorize()
{
return auth()->check();
return auth()->check() and $this->ajax();
}
/**
@ -25,7 +25,7 @@ class AjaxRemoveMediasBasketRequest extends FormRequest
{
return [
'panier_id' => 'required|exists:paniers,id,deleted_at,NULL',
'media_id' => 'required|exists:medias,id,deleted_at,NULL'
'media_id' => 'required|exists:media,id,deleted_at,NULL'
];
}
}

View file

@ -1,10 +1,10 @@
<?php
namespace App\Http\Requests\Bo;
namespace App\Http\Requests\Bo\Ajax;
use Illuminate\Foundation\Http\FormRequest;
class AjaxSaveBasketPropsRequest extends FormRequest
class SaveBasketPropsRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
@ -16,7 +16,7 @@ class AjaxSaveBasketPropsRequest extends FormRequest
$this->merge([
'statut' => $this->statut ? true : false
]);
return \auth()->check();
return auth()->check() and $this->ajax();
}
/**

View file

@ -0,0 +1,47 @@
<?php
namespace App\Http\Requests\Bo\Ajax;
use App\Enums\ContactType;
use App\Rules\EnumIsValidRule;
use Illuminate\Foundation\Http\FormRequest;
class SendContactMessageRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize()
{
return auth()->check() and $this->ajax();
}
/**
* Prepare the data for validation.
*
* @return void
*/
protected function prepareForValidation()
{
$this->merge([
'url' => strval($this->url),
'type' => intval($this->type)
]);
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, mixed>
*/
public function rules()
{
return [
'type' => ['required', new EnumIsValidRule(ContactType::class)],
'url' => 'nullable|url',
'message' => 'required|string|min:20'
];
}
}

View file

@ -0,0 +1,30 @@
<?php
namespace App\Http\Requests\Bo\Ajax;
use Illuminate\Foundation\Http\FormRequest;
class StoreMediaFileRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize()
{
return auth()->check() and $this->ajax();
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, mixed>
*/
public function rules()
{
return [
'media_id' => 'nullable|exists:media,id'
];
}
}

View file

@ -0,0 +1,118 @@
<?php
namespace App\Http\Requests\Bo\Ajax;
use App\Enums\MediaStatut;
use Illuminate\Foundation\Http\FormRequest;
class UpdateMediaRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize(): bool
{
return auth()->check() and $this->ajax();
}
/**
* Prepare the data for validation.
*
* @return void
*/
protected function prepareForValidation()
{
$this->merge([
'tags' => $this->tags ?? [],
'articles' => $this->articles ?? [],
'marques' => $this->marques ?? [],
'societes' => $this->societes ?? []
]);
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, mixed>
*/
public function rules()
{
$rules = [
'statut' => 'required|numeric|in:' . \implode(',', collect(MediaStatut::cases())->pluck('value')->all()),
'version' => 'required|numeric|min:1',
'type_vue_id' => 'required|exists:type_vues,codeTypevue',
'variante_id' => 'required|exists:variantes,codeVariante',
'langue_id' => 'required|exists:langues,langue_id',
];
// ? Est lié à arcadia ?
if ($this->media->arcadia_id) {
// Ne pas autoriser brouillon .
$rules['statut'] = 'required|numeric|in:' . \implode(',', [
MediaStatut::public->value, MediaStatut::public_etendu->value
]);
unset($rules['version']);
unset($rules['type_vue_id']);
unset($rules['variante_id']);
unset($rules['langue_id']);
}
// Article.
if ($this->sujet_id == 1) {
$rules['articles'] = 'present|array|min:0';
$rules['articles.*'] = 'array';
$rules['articles.*.id'] = 'exists:articles,id';
}
// Marque.
if ($this->sujet_id == 2) {
$rules['marques'] = 'present|array|min:0';
$rules['marques.*'] = 'array';
$rules['marques.*.codMarq'] = 'exists:marques,codMarq';
}
// Sociétés.
if ($this->sujet_id == 4) {
$rules['societes'] = 'present|array|min:0';
$rules['societes.*'] = 'array';
$rules['societes.*.id'] = 'exists:societes,id';
}
return \array_merge($rules, [
'libelle' => 'required|string|min:2|max:50',
'commentaire' => 'nullable|string|min:2',
'copyright_id' => 'required|exists:copyrights,copyright_id',
// Tags.
'tags' => 'present|array|min:0',
'tags.*.libelle' => 'min:2|max:50',
'etiquette_id' => 'required|exists:etiquettes,etiquette_id',
]);
}
/**
* Get custom attributes for validator errors.
*
* @return array<string,string>
*/
public function attributes(): array
{
return [
'libelle' => trans('libelle'),
'commentaire' => trans('commentaire'),
'societes' => trans('societes'),
'societes.*' => trans('societes'),
'societes.*.id' => trans('societes'),
'marques' => trans('marques'),
'marques.*' => trans('marques'),
'marques.*.codMarq' => trans('marques'),
'copyright_id' => trans('copyright'),
'etiquette_id' => trans('etiquette'),
'type_vue_id' => trans('type de vue'),
'variante_id' => trans('type de variante'),
'tags' => trans('nuage de tags'),
'tags.*.libelle' => trans('libellé dans le nuage de tags'),
];
}
}

View file

@ -0,0 +1,31 @@
<?php
namespace App\Http\Requests\Bo\Ajax;
use Illuminate\Foundation\Http\FormRequest;
class UploadMediaFileRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize()
{
return auth()->check();
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, mixed>
*/
public function rules()
{
return [
'media_id' => 'nullable|exists:media,id',
'dossier_id' => 'sometimes|exists:dossiers,id'
];
}
}

View file

@ -0,0 +1,16 @@
<?php
namespace App\Http\Requests\Bo;
class DownloadMediaPublicRequest extends DownloadMediaRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize(): bool
{
return true;
}
}

View file

@ -4,14 +4,14 @@ namespace App\Http\Requests\Bo;
use Illuminate\Foundation\Http\FormRequest;
class AjaxDeleteMediaRequest extends FormRequest
class DownloadMediaRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize()
public function authorize(): bool
{
return auth()->check();
}
@ -19,13 +19,13 @@ class AjaxDeleteMediaRequest extends FormRequest
/**
* Get the validation rules that apply to the request.
*
* @return array
* @return array<string, mixed>
*/
public function rules()
{
return [
'medias' => 'required|array',
'medias.*' => 'exists:medias,id,deleted_at,NULL'
'mediaList' => 'required|array',
'mediaList.*' => 'required|exists:media,id',
];
}
}

View file

@ -0,0 +1,31 @@
<?php
namespace App\Http\Requests\Bo\Folders;
use Illuminate\Foundation\Http\FormRequest;
class StoreDepotFolderRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize(): bool
{
return auth()->check();
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, mixed>
*/
public function rules()
{
return [
'libelle' => 'required|string|min:4|max:255|unique:dossiers,libelle',
'description' => 'nullable|string|min:4'
];
}
}

View file

@ -0,0 +1,56 @@
<?php
namespace App\Http\Requests\Bo\Folders;
use Illuminate\Foundation\Http\FormRequest;
class StoreFolderRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize(): bool
{
return auth()->check();
}
/**
* Prepare the data for validation.
*
* @return void
*/
protected function prepareForValidation()
{
$this->merge([
'sub' => $this->sub ? true : false,
'is_private' => $this->status ? true : false,
'marques' => !$this->marques ? null : \explode(',', \strval($this->marques))
]);
if ($this->sub) {
$this->merge([
'is_private' => false
]);
}
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, mixed>
*/
public function rules()
{
return [
'parent_id' => 'sometimes|exists:dossiers,id',
'libelle' => 'required|string|min:4|max:255|unique:dossiers,libelle',
'description' => 'nullable|string|min:4',
'sujet' => 'required_unless:sub,true|exists:sujets,codeSujet',
'is_private' => 'required|boolean',
'marques' => 'nullable|array',
'marques.*' => 'required|exists:marques,codMarq',
'sub' => 'required|boolean'
];
}
}

View file

@ -0,0 +1,20 @@
<?php
namespace App\Http\Requests\Bo\Folders;
class UpdateFolderRequest extends StoreFolderRequest
{
/**
* Get the validation rules that apply to the request.
*
* @return array<string, mixed>
*/
public function rules()
{
$rules = \array_merge(parent::rules(), [
'libelle' => 'required|string|min:4|max:255|unique:dossiers,libelle,' . $this->dossier->id,
]);
unset($rules['parent_id']);
return $rules;
}
}

View file

@ -0,0 +1,94 @@
<?php
namespace App\Http\Requests\Bo;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Validator;
class LoadMediaRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize(): bool
{
return auth()->check();
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, mixed>
*/
public function rules()
{
return [
'offset' => 'sometimes|numeric',
'limit' => 'sometimes|numeric|gt:0|lte:50',
'folder' => 'sometimes|numeric',
'filters' => 'sometimes|array',
'filters.*' => 'required|array',
'filters.*.legend' => 'required|string',
'filters.*.libelle' => 'required|string',
'filters.*.order' => 'sometimes|in:desc,asc',
'filters.*.type' => 'required|in:order,dossier_id,type_media_id,type_vue_id,etiquette_id,arcadia_id,search',
'filters.*.value' => [
'nullable', function ($attribute, $value, $fail) {
$index = intval(collect(\explode('.', $attribute))->get(1));
$filterType = \strval(collect(collect($this->filters)->get($index))->get('type'));
switch ($filterType) {
case 'type_vue_id':
$attribute = \str_replace('.', '_', $attribute);
Validator::validate([$attribute => $value], [
$attribute => 'required|array',
"$attribute.*" => 'required|array',
"$attribute.*.codeTypevue" => 'required|exists:type_vues,codeTypevue',
]);
return;
case 'type_media_id':
$attribute = \str_replace('.', '_', $attribute);
Validator::validate([$attribute => $value], [
$attribute => 'required|array',
"$attribute.*" => 'required|array',
"$attribute.*.codeTypemedia" => 'required|exists:type_media,codeTypemedia',
]);
return;
case 'etiquette_id':
$attribute = \str_replace('.', '_', $attribute);
Validator::validate([$attribute => $value], [
$attribute => 'required|array',
"$attribute.*" => 'required|array',
"$attribute.*.etiquette_id" => 'required|exists:etiquettes,etiquette_id',
]);
return;
case 'dossier_id':
$attribute = \str_replace('.', '_', $attribute);
if (\intval($value) === 0) {
return;
}
Validator::validate([$attribute => $value], [
$attribute => 'required|exists:dossiers,id'
]);
return;
case 'order':
if (!\in_array($value, ['created_at', 'updated_at', 'version', 'libelle.keyword'])) {
$fail("Unhandled filter type order for '$value'");
}
return;
case 'search':
$attribute = \str_replace('.', '_', $attribute);
Validator::validate([$attribute => $value], [
$attribute => 'required|string'
]);
return;
default:
$fail("Unhandled filter type '$filterType'");
return;
} //end switch
}
],
];
}
}

View file

@ -0,0 +1,37 @@
<?php
namespace App\Http\Requests\Bo;
use Illuminate\Foundation\Http\FormRequest;
class ShowInterfaceRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize(): bool
{
return auth()->check();
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, mixed>
*/
public function rules()
{
return [
// * Dossier
'd' => 'sometimes|numeric',
// * Sujet
's' => 'sometimes|exists:sujets,codeSujet',
// * text search
't' => 'sometimes|string',
// * Recherche
'r' => 'sometimes|exists:recherches,id'
];
}
}

View file

@ -0,0 +1,42 @@
<?php
namespace App\Http\Resources\Api;
use App\Enums\Legacy\ApiStatus;
use Illuminate\Http\Resources\Json\JsonResource;
use stdClass;
class ApiResponse extends JsonResource
{
/** @var stdClass */
protected $data;
/** @var \App\Enums\ApiStatus */
protected $status;
/**
* @param stdClass $data
* @param ApiStatus $status
*/
public function __construct(stdClass $data = null, ApiStatus $status)
{
$this->status = $status;
$this->data = $data;
}
/**
* Transform the resource into an array.
*
* @param mixed $request
* @return array
*/
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass, Squiz.Commenting.FunctionComment.Missing
public function toArray($request): array
{
return [
'status' => $this->status->value,
'data' => (array)$this->data,
'version' => config('api.version')
];
}
}

View file

@ -0,0 +1,43 @@
<?php
namespace App\Http\Resources\Api;
use App\Models\Media;
use Illuminate\Http\Resources\Json\ResourceCollection as JsonResourceCollection;
class MediaListResource extends JsonResourceCollection
{
/**
* Add media thumbs into collection
*
* @var boolean
*/
private $addThumb = false;
/**
* Create a new resource instance.
*
* @param mixed $resource
* @param boolean $addThumb
* @return void
*/
public function __construct(mixed $resource, bool $addThumb = false)
{
$this->addThumb = $addThumb;
parent::__construct($resource);
}
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass, Squiz.Commenting.FunctionComment.Missing
public function toArray($request)
{
return $this->collection->map(function (Media $media) use ($request) {
return collect((new MediaResource($media, $this->addThumb))->toArray($request));
});
}
}

View file

@ -0,0 +1,51 @@
<?php
namespace App\Http\Resources\Api;
use Illuminate\Http\Resources\Json\JsonResource;
use Illuminate\Support\Facades\Storage;
class MediaPictureResource extends JsonResource
{
/** @var array */
private static $skeleton = [
'base64_bin' => '',
'uid' => '',
'extension' => ''
];
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
* + base64_bin string
* + uid string
* + extension string
*/
//phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass, Squiz.Commenting.FunctionComment.Missing
public function toArray($request)
{
/** @var \Illuminate\Filesystem\FilesystemAdapter */
$storage = Storage::disk('media');
return array_intersect_key(
array_merge([
'base64_bin' => \base64_encode((string)$storage->get($this->relative_path)),
'uid' => $this->uid,
'extension' => $this->extension->value
]),
$this->getSkeleton()
);
}
/**
* Returns the resource skeletton
*
* @return array
*/
public static function getSkeleton(): array
{
return self::$skeleton;
}
}

View file

@ -0,0 +1,91 @@
<?php
namespace App\Http\Resources\Api;
use App\Models\Media;
use Illuminate\Http\Resources\Json\JsonResource;
use Illuminate\Support\Facades\Storage;
class MediaResource extends JsonResource
{
/**
* Add media thumbs into collection
*
* @var boolean
*/
private $addThumb = false;
/** @var array */
private static $skeleton = [
'id' => 0,
'arcadia_id' => 0,
'libelle' => '',
'uid' => '',
'version' => '',
'created_at' => '',
'updated_at' => '',
];
/**
* Create a new resource instance.
*
* @param \App\Models\Media $resource
* @param boolean $addThumb
* @return void
*/
public function __construct(Media $resource, bool $addThumb = false)
{
$this->addThumb = $addThumb;
$this->resource = $resource;
}
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
* + id integer
* + arcadia_id integer
* + libelle string
* + uid string
* + version integer
* + created_at datetime
* + updated_at datetime
*/
//phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass, Squiz.Commenting.FunctionComment.Missing
public function toArray($request)
{
$data = array_intersect_key(
array_merge([
'id' => $this->id,
'arcadia_id' => $this->arcadia_id,
'libelle' => $this->libelle,
'uid' => $this->uid,
'version' => $this->version,
'created_at' => $this->created_at,
'updated_at' => $this->updated_at,
]),
$this->getSkeleton()
);
if ($this->addThumb) {
/** @var \Illuminate\Filesystem\FilesystemAdapter */
$storage = Storage::disk('media');
$data['base64_thumb_bin'] = \base64_encode($storage->get($this->thumb_relative_path));
}
return $data;
}
/**
* Returns the resource skeletton
*
* @return array
*/
public static function getSkeleton(): array
{
return self::$skeleton;
}
}

View file

@ -0,0 +1,30 @@
<?php
namespace App\Http\Resources;
use App\Models\Dossier;
use Illuminate\Http\Resources\Json\ResourceCollection as JsonResourceCollection;
class NestedDossiersListResource extends JsonResourceCollection
{
/**
* Transform the resource into an array.
*
* @param mixed $request
* @return array
*/
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass, Squiz.Commenting.FunctionComment.Missing
public function toArray($request)
{
return $this->collection->map(function (Dossier $dossier) {
return [
'id' => $dossier->id,
'libelle' => $dossier->libelle,
'system' => $dossier->system,
'favoris' => $dossier->favoris,
'mediacount' => $dossier->mediacount,
'sousdossiers' => new self($dossier->sousdossiers)
];
});
}
}

View file

@ -1,198 +0,0 @@
<?php
namespace App\Http\Traits;
use App\Models\Format;
use App\Scopes\SystemScope;
use DOMDocument;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
trait ArcadiaLink
{
public static function checkRequiredValues($media)
{
$required = ['sujet_id', 'typemedia_id', 'typevue_id', 'variante_id', 'langue_id', 'version'];
foreach ($required as $v) {
if ($media->$v === null) {
return false;
}
}
return true;
}
public static function create($media)
{
if (!self::checkRequiredValues($media)) {
return;
}
$media->isLocked = 1;
$media->save();
self::convert(self::getXML($media, "C"), $media);
}
public static function update($media)
{
if ($media->arcadia_id === null) {
return;
}
if (!self::checkRequiredValues($media)) {
return;
}
$media->isLocked = 1;
$media->save();
self::convert(self::getXML($media, "M"), $media);
}
public static function delete($media)
{
if ($media->arcadia_id === null) {
return;
}
if (!self::checkRequiredValues($media)) {
return;
}
$media->isLocked = 1;
$media->save();
self::convert(self::getXML($media, "S"), $media);
}
public static function reactivate($media)
{
if ($media->arcadia_id === null) {
return;
}
if (!self::checkRequiredValues($media)) {
return;
}
$media->isLocked = 1;
$media->save();
self::convert(self::getXML($media, "R"), $media);
}
private static function convert($xml, $media)
{
$uid = sprintf('%04x%04x-%04x-%04x-%04x-%04x%04x%04x', mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0x0fff) | 0x4000, mt_rand(0, 0x3fff) | 0x8000, mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff));
Storage::disk('arcadia_link_s')->put("$uid.xml", $xml);
self::compress($uid, $media);
}
private static function compress($uid, $media)
{
try {
$zip_file = storage_path('arcadia_link/send') . "/$media->id-$uid.zip";
$zip = new \ZipArchive();
$zip->open($zip_file, \ZipArchive::CREATE | \ZipArchive::OVERWRITE);
$zip->addFile(storage_path('arcadia_link/send') . "/$uid.xml", 'media.xml');
$zip->addFile(public_path() . "/$media->folder$media->uid.$media->extension", "$media->uid.$media->extension");
$zip->close();
unset($zip);
unlink(storage_path('arcadia_link/send') . "/$uid.xml");
// On insert dans la table de supervision
$s = new \App\Models\ArcadiaLink();
$s->media_id = $media->id;
if ($media->arcadia_id !== null) {
$s->arcadia_id = $media->arcadia_id;
}
$s->filename = "$media->id-$uid.zip";
$s->save();
} catch (Exception $e) {
// echo "Exception : " . $e;
// TODO : Envoi mail Log
Log::emergency("Erreur création .zip : $e (Traits/ArcadiaLink)");
}//end try
}
private static function getXML($media, $mouvement)
{
$refKey = self::getRefKey($media);
$formats = self::getFormats();
$dom = new DOMDocument('1.0', 'UTF-8');
$dom->formatOutput = true;
$root = $dom->createElement('MEDIA_VISUEL');
$dom->appendChild($root);
$root->appendChild($dom->createElement('codeArcadia'))->appendChild($dom->createCDATASection("$media->arcadia_id"));
$root->appendChild($dom->createElement('code_DAM'))->appendChild($dom->createCDATASection("$media->id"));
$root->appendChild($dom->createElement('TypeMouvement'))->appendChild($dom->createCDATASection("$mouvement"));
$cleRef = $root->appendChild($dom->createElement('cle_Referentiel'));
foreach ($refKey as $k) {
if ($media->sujet_id === 1) {
$cleRef->appendChild($dom->createElement('id'))->appendChild($dom->createCDATASection("$k->noArt"));
} elseif ($media->sujet_id === 2) {
$cleRef->appendChild($dom->createElement('id'))->appendChild($dom->createCDATASection("$k->codMarq"));
} elseif ($media->sujet_id === 4) {
$cleRef->appendChild($dom->createElement('id'))->appendChild($dom->createCDATASection("$k->id"));
}
}
$root->appendChild($dom->createElement('libExtension'))->appendChild($dom->createCDATASection("$media->extension"));
$root->appendChild($dom->createElement('nbPxLargeur'))->appendChild($dom->createCDATASection("$media->largeur"));
$root->appendChild($dom->createElement('largeurMm'))->appendChild($dom->createCDATASection("$media->largeur_mm"));
$root->appendChild($dom->createElement('nbPxHauteur'))->appendChild($dom->createCDATASection("$media->hauteur"));
$root->appendChild($dom->createElement('hauteurMm'))->appendChild($dom->createCDATASection("$media->hauteur_mm"));
$root->appendChild($dom->createElement('pppLargeur'))->appendChild($dom->createCDATASection("$media->resolution"));
$root->appendChild($dom->createElement('version'))->appendChild($dom->createCDATASection("$media->version"));
$root->appendChild($dom->createElement('codeTypeMedia'))->appendChild($dom->createCDATASection("$media->typemedia_id"));
$root->appendChild($dom->createElement('codeTypeVue'))->appendChild($dom->createCDATASection("$media->typevue_id"));
$root->appendChild($dom->createElement('commentaire'))->appendChild($dom->createCDATASection("$media->commentaire"));
$root->appendChild($dom->createElement('codeSujet'))->appendChild($dom->createCDATASection("$media->sujet_id"));
$root->appendChild($dom->createElement('langue'))->appendChild($dom->createCDATASection("$media->langue_id"));
$root->appendChild($dom->createElement('copyright'))->appendChild($dom->createCDATASection("$media->copyright_id"));
$root->appendChild($dom->createElement('codeVariante'))->appendChild($dom->createCDATASection("$media->variante_id"));
$root->appendChild($dom->createElement('dateCreation'))->appendChild($dom->createCDATASection("$media->created_at"));
$root->appendChild($dom->createElement('dateModification'))->appendChild($dom->createCDATASection("$media->updated_at"));
$root->appendChild($dom->createElement('datePublication'))->appendChild($dom->createCDATASection("$media->published_at"));
$root->appendChild($dom->createElement('libelle'))->appendChild($dom->createCDATASection("$media->libelle"));
$root->appendChild($dom->createElement('NomFichier'))->appendChild($dom->createCDATASection("$media->uid.$media->extension"));
$mediaFormat = $root->appendChild($dom->createElement('formats'));
foreach ($formats as $k) {
$format = $mediaFormat->appendChild($dom->createElement('format'));
$format->appendChild($dom->createElement('id'))->appendChild($dom->createCDATASection($k['id']));
$format->appendChild($dom->createElement('libelle'))->appendChild($dom->createCDATASection($k['libelle']));
$format->appendChild($dom->createElement('extension'))->appendChild($dom->createCDATASection("$media->extension"));
$format->appendChild($dom->createElement('largeur'))->appendChild($dom->createCDATASection($k['largeur']));
$format->appendChild($dom->createElement('hauteur'))->appendChild($dom->createCDATASection($k['hauteur']));
$format->appendChild($dom->createElement('lien'))->appendChild($dom->createCDATASection(env('APP_URL') . "/$media->folder$media->uid" . "_" . $k['libelle'] . ".$media->extension"));
}
return $dom->saveXML();
}
public static function getRefKey($media)
{
switch ($media->sujet_id) {
case 1:
return $media->articles()->get();
break;
case 2:
return $media->marques()->get();
break;
case 4:
return $media->societes()->get();
break;
default:
return [];
}
}
private static function getFormats()
{
return Format::withoutGlobalScope(SystemScope::class)
->where('system', true)->get();
}
}

View file

@ -1,239 +0,0 @@
<?php
namespace App\Http\Traits;
use App\Models\Format;
use App\Models\Media;
use Illuminate\Support\Facades\Storage;
trait MediaTools
{
public function getFileSize($file_path)
{
return $this->fixIntegerOverflow(filesize($file_path));
}
/**
* Create thumbnail image for a Media
*
* @param Media $media
* @return void
* @throws \ImagickException On errors.
*/
public function createThumb(Media $media)
{
/** @var \Illuminate\Support\Collection<\App\Models\Format> $formats */
$formats = optional($media->sujet)->formats ?? collect();
$mediaPath = public_path("/{$media->folder}{$media->uid}.{$media->extension}");
$im = new \Imagick($mediaPath);
$im->setImageBackgroundColor('#ffffff');
$im->setImageAlphaChannel(\Imagick::ALPHACHANNEL_REMOVE);
$im->mergeImageLayers(\Imagick::LAYERMETHOD_FLATTEN);
$im->stripImage();
if (in_array($media->extension, array("pdf", "ai", "psd"))) {
$im->setIteratorIndex(0);
}
foreach ($formats as $format) {
$fp = public_path("/{$media->folder}{$media->uid}_{$format->libelle}.{$format->extension}");
$targetWidth = \floatval($format->largeur);
if (\floatval($format->hauteur) == 0) {
$targetHeight = \floatval(floor(($media->hauteur * $format->largeur) / $media->largeur));
} else {
$targetHeight = \floatval($format->hauteur);
}
$im->setImageFormat($format->extension);
if ($media->largeur >= $targetWidth || $media->hauteur >= $targetHeight) {
$im->thumbnailImage($targetWidth, $targetHeight, true);
}
$im->writeImage($fp);
}
$im->destroy();
unset($im);
}
/**
* Create Sys thumbnail image for a Media.
*
* @param \App\Models\Media $media
* @return void
* @throws \ImagickException On errors.
*/
public function createSysThumb(Media $media)
{
$mediaPath = public_path("/{$media->folder}{$media->uid}.{$media->extension}");
$im = new \Imagick();
$im->readImage($mediaPath);
if (in_array($media->extension, array("pdf", "ai", "psd"))) {
$extension = "png";
$im->setIteratorIndex(0);
} else {
/** @var string */
$extension = $media->extension;
}
/*
Handle CMJN - DOESN'T WORK
if ($im->getImageColorspace() === \Imagick::COLORSPACE_CMYK){
$im->transformImageColorspace(\Imagick::COLORSPACE_RGB);
}
*/
// * create preview
/** @var \App\Models\Format $format */
$format = Format::withoutGlobalScope('App\Scopes\SystemScope')
->where('libelle', '=', 'preview')->firstOrFail();
$fp = public_path("/{$media->folder}{$media->uid}_preview.{$extension}");
if ($media->extension == "gif") {
if ($media->largeur >= $format->largeur || $media->hauteur >= $format->hauteur || $media->largeur == "") {
$im = $im->coalesceImages();
foreach ($im as $frame) {
$frame->thumbnailImage($format->largeur, $format->hauteur);
$frame->setImagePage($format->largeur, $format->hauteur, 0, 0);
}
$im = $im->deconstructImages();
}
$im->writeImages($fp, true);
} else {
if ($media->largeur >= $format->largeur || $media->hauteur >= $format->hauteur || $media->largeur == "") {
$im->thumbnailImage($format->largeur, $format->hauteur, true);
}
$im->writeImage($fp);
}
// * create thumb
/** @var \App\Models\Format $format */
$format = Format::withoutGlobalScope('App\Scopes\SystemScope')
->where('libelle', '=', 'thumb')->firstOrFail();
if ($extension != "png") {
$im->setImageBackgroundColor('#ffffff');
$im->setImageAlphaChannel(\Imagick::ALPHACHANNEL_REMOVE);
$im->mergeImageLayers(\Imagick::LAYERMETHOD_FLATTEN);
$im->stripImage();
}
$fp = public_path("/{$media->folder}{$media->uid}_thumb.{$extension}");
$im->thumbnailImage($format->largeur, $format->hauteur, true);
$im->writeImage($fp);
$im->destroy();
unset($im);
}
/**
* Returns the media folder path name
*
* @return string
* @throws \ErrorException If creating the folder fails.
*/
public function getMediaFolder(): string
{
$mediaPath = "medias/";
$directories = Storage::allDirectories($mediaPath);
$selectedFolder = "";
// * on obtient le dernier dossier créé
/** @var string $foldername */
foreach ($directories as $key => $foldername) {
if ($selectedFolder < $foldername) {
$selectedFolder = $foldername;
}
}
// * si on n'a pas de dossier, on le créé
if ($selectedFolder == "") {
$selectedFolder = $mediaPath . uniqid();
if (!Storage::makeDirectory($selectedFolder)) {
throw new \ErrorException("Could not create $selectedFolder");
}
} else {
// * on compte le nombre de fichier
$files = Storage::allFiles($selectedFolder);
// * si on en a plus de 1000, on créé un nouveau dossier
if (count($files) > 1000) {
$selectedFolder = $mediaPath . uniqid();
if (!Storage::makeDirectory($selectedFolder)) {
throw new \ErrorException("Could not create $selectedFolder");
}
}
}
return $selectedFolder;
}
public function fixIntegerOverflow($size)
{
if ($size < 0) {
$size += 2.0 * (PHP_INT_MAX + 1);
}
return $size;
}
/**
* Convert file size to human readable string
*
* @param integer $octet
* @param integer $precision
* @return string
*/
public function formatBytes(int $octet, int $precision = 2): string
{
// * Array contenant les differents unités
$unite = array('octet', 'Ko', 'Mo', 'Go');
switch (true) {
// * octet
case $octet < 1000:
return $octet . $unite[0];
// * ko
case $octet < 1000000:
return $octet . $unite[1];
// * Mo ou Go
default:
if ($octet < 1000000000) {
// * Mo
$mo = round($octet / (1024 * 1024), $precision);
return $mo . $unite[2];
} else {
// * Go
$go = round($octet / (1024 * 1024 * 1024), $precision);
return $go . $unite[3];
}
}
}
/**
* Renvoi le chemin attendu d'un média selon le format demandé
*
* @param \App\Models\Media $media
* @param \App\Models\Format $format
* @return string
* @throws \ErrorException If Format is not handled.
*/
public function getMediaPath(Media $media, Format $format): string
{
// * format spécifique
if ($format->system) {
if (in_array($media->extension, array("gif", "jpeg", "jpg", "png", "pdf", "ai", "psd"))) {
return public_path("/{$media->folder}{$media->uid}_{$format->libelle}.{$format->extension}");
}
return public_path("/{$media->folder}{$media->uid}.{$media->extension}");
}
// * format système (thumb et preview)
if ($format->system) {
if (in_array($media->extension, array("gif", "jpeg", "jpg", "png"))) {
// * Gestion du cas de format qui fixe l'extension
if ($format->extension != '') {
$file_extension = $format->extension;
} else {
$file_extension = $media->extension;
}
return public_path("/{$media->folder}{$media->uid}_{$format->libelle}.{$file_extension}");
} elseif (in_array($media->extension, array("pdf", "ai", "psd"))) {
return public_path("/{$media->folder}{$media->uid}_{$format->libelle}.png");
}
return public_path("/{$media->folder}.{$media->uid}.{$media->extension}");
}
throw new \ErrorException("Unhandled Format $format->id $format->libelle");
}
}

View file

@ -3,7 +3,6 @@
namespace App\Jobs\Arcadia;
use App\Exceptions\AbortXmlImportException;
use App\Exceptions\ElasticSearchException;
use App\Models\ArcadiaLink;
use App\Models\Media;
use Illuminate\Database\Eloquent\ModelNotFoundException;
@ -40,7 +39,9 @@ class XmlArcadiaMediasIn extends XmlJobs
*
* @var array
*/
protected $nullableFields = [];
protected $nullableFields = [
'version'
];
/**
* Fields that are unique
@ -62,7 +63,7 @@ class XmlArcadiaMediasIn extends XmlJobs
$this->parseData('MEDIA_VISUEL', Media::class, function (\SimpleXMLElement $xmlData, array &$line) {
// * After XML parse Callback.
// * Unlock media
$line['isLocked'] = 0;
$line['isLocked'] = false;
}, function (array $uniqueColumnsKey, $line) {
// * DO the DB save yourself callback
try {
@ -70,17 +71,14 @@ class XmlArcadiaMediasIn extends XmlJobs
$media = Media::withTrashed()->where($uniqueColumnsKey)->firstOrFail();
$media->arcadia_id = $line['arcadia_id'];
$media->version = $line['version'];
$media->isLocked = 0;
$media->version = $line['version'] ?? 1;
$media->isLocked = false;
$media->saveOrFail();
} catch (ModelNotFoundException $e) {
$e = new AbortXmlImportException(
sprintf('La fiche media \'%s\' ne peut être trouvé', \json_encode($uniqueColumnsKey)),
0,
1,
__FILE__,
__LINE__,
$e
);
throw $e;
@ -96,11 +94,8 @@ class XmlArcadiaMediasIn extends XmlJobs
$media = Media::withTrashed()->findOrFail($line['id']);
} catch (ModelNotFoundException $e) {
$e = new AbortXmlImportException(
"La fiche media '%s' ne peut être trouvé",
\sprintf('La fiche media \'%s\' ne peut être trouvé', $line['id']),
0,
1,
__FILE__,
__LINE__,
$e
);
throw $e;
@ -108,36 +103,16 @@ class XmlArcadiaMediasIn extends XmlJobs
try {
// * Suppression dans la supervision Arcadia Link
/** @var \App\Models\ArcadiaLink|null */
$arcadiaLink = ArcadiaLink::where('media_id', $media->id)->first();
if ($arcadiaLink) {
$arcadiaLink->delete();
$arcadiaLink->deleteOrFail();
}
// * Index ElasticSearch
$esExist = Media::searchByQuery([
'match' => ['arcadia_id' => $media->arcadia_id ?? '']
]);
// * Remove all from elastic if any duplicates
if ($esExist->totalHits() > 0) {
$media->removeFromIndex();
}
if (!$media->trashed()) {
// * Save into elastic
$media->addToIndex();
}
} catch (\Exception $e) {
$report = new ElasticSearchException(
'Something went wrong when updating media into elastic search',
0,
1,
__FILE__,
__LINE__,
$e
);
report($report);
throw new AbortXmlImportException($report->getMessage());
// * Save into elastic if media is not soft deleted, remove otherwise.
!$media->trashed() ? $media->addToElasticSearch() : $media->removeFromElasticSearch();
} catch (\RuntimeException $e) {
throw new AbortXmlImportException($e->getMessage());
} //end try
});
}

View file

@ -21,15 +21,6 @@ class SendMediasModificationsReport implements ShouldQueue
use Queueable;
use SerializesModels;
/**
* Create a new job instance.
*
* @return void
*/
public function __construct()
{
}
/**
* Execute the job.
*
@ -37,28 +28,31 @@ class SendMediasModificationsReport implements ShouldQueue
*/
public function handle()
{
// Gather Medias to report
// filtered by subject (2,4)
$now = Carbon::now();
$yesterday = $now->subDay(1);
$arSujets = [];
$onesujet = Sujet::find(2);
array_push($arSujets, $onesujet->libelle);
$onesujet = Sujet::find(4);
array_push($arSujets, $onesujet->libelle);
// Gather Medias to report.
$subjectsToFetch = [
// * Marques
2,
// * Sociétés
4,
];
$sujets = implode(', ', $arSujets);
$yesterday = Carbon::now()->subDay(1);
$medias = Media::whereDate('updated_at', '>=', $yesterday)
/** @var \Illuminate\Database\Eloquent\Collection<\App\Models\Sujet> */
$sujets = Sujet::whereIn('codeSujet', $subjectsToFetch)->get();
/** @var \Illuminate\Database\Eloquent\Collection<\App\Models\Media> */
$mediaList = Media::whereDate('updated_at', '>=', $yesterday)
->whereIn('sujet_id', [2,4])
->get();
// send mail
/** @var \App\Models\Setting|null Send mail */
$toMailSetting = Setting::where('key', 'media_report_to_mail')->first();
if (empty($toMailSetting->value)) {
// pas d'envoi car pas de mail cible
} else {
$email = $toMailSetting->value;
Mail::to($email)->send(new SendMediaReportMail($medias, $yesterday, $sujets));
if ($toMailSetting and !empty(\strval(optional($toMailSetting)->value))) {
Mail::to(\strval($toMailSetting->value))->send(new SendMediaReportMail(
$mediaList,
$yesterday,
$sujets->pluck('libelle')->implode(', ')
));
}
}
}

View file

@ -0,0 +1,80 @@
<?php
namespace App\Jobs;
use App\Models\Api\ApiToken;
use App\Models\Api\ApiTokenStat;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\Middleware\WithoutOverlapping;
use Illuminate\Queue\SerializesModels;
/**
* @codeCoverageIgnore
*/
class SetApiTokenStats implements ShouldQueue
{
use Dispatchable;
use InteractsWithQueue;
use Queueable;
use SerializesModels;
private const KEY = '2202020';
/** @var \App\Models\Api\ApiToken */
protected $apiToken;
/**
* Create a new job instance.
*
* @param \App\Models\Api\ApiToken $apiToken
* @return void
*/
public function __construct(ApiToken $apiToken)
{
$this->apiToken = $apiToken;
}
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
$date = date('Y-m-d');
$apiTokenStat = ApiTokenStat::where('date', $date)
->whereHas('apiToken', function ($q) {
$q->where('id', $this->apiToken->id);
})->firstOrNew();
$apiTokenStat->date = $date;
$apiTokenStat->nb_calls += 1;
$apiTokenStat->apiToken()->associate($this->apiToken);
$apiTokenStat->saveOrFail();
}
/**
* Determine the time at which the job should timeout.
*
* @return \DateTime
*/
public function retryUntil()
{
return now()->addMinutes(rand(0, 360));
}
/**
* Get the middleware the job should pass through.
*
* @return array
*/
public function middleware()
{
return [
(new WithoutOverlapping(self::KEY))
->releaseAfter(rand(90, 180))
];
}
}

View file

@ -0,0 +1,294 @@
<?php
namespace App\Lib;
use App\Enums\EdiMouvementType;
use App\Models\ArcadiaLink;
use App\Models\Format;
use App\Models\Media;
use App\Scopes\SystemScope;
use DOMDocument;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
class ArcadiaLinkUtilities
{
/**
* Create Media
*
* @param \App\Models\Media $media
* @return boolean
*/
public static function create(Media $media): bool
{
return self::sendMediaToArcadia($media, EdiMouvementType::create);
}
/**
* Update Media
*
* @param \App\Models\Media $media
* @return boolean
*/
public static function update(Media $media): bool
{
return self::sendMediaToArcadia($media, EdiMouvementType::update);
}
/**
* Delete Media
*
* @param \App\Models\Media $media
* @return boolean
*/
public static function delete(Media $media): bool
{
return self::sendMediaToArcadia($media, EdiMouvementType::delete);
}
/**
* Reactivate Media
*
* @param \App\Models\Media $media
* @return boolean
*/
public static function reactivate(Media $media): bool
{
return self::sendMediaToArcadia($media, EdiMouvementType::reactivate);
}
/**
* Send Media mouvement to Arcadia
*
* @param \App\Models\Media $media
* @param \App\Enums\EdiMouvementType $ediMouvementType
* @return boolean
*/
private static function sendMediaToArcadia(Media $media, EdiMouvementType $ediMouvementType): bool
{
if (
$ediMouvementType !== EdiMouvementType::create and
$media->arcadia_id === null and
!self::hasRequiredValues($media)
) {
return false;
}
$media->fresh();
$media->isLocked = true;
try {
if (
!($media->saveQuietly() and
self::compress(self::generateXml($media, $ediMouvementType), $media)
)
) {
return false;
}
/** @throws \RuntimeException */
$media->addToElasticSearch();
return true;
} catch (\RuntimeException | \Throwable $e) {
report($e);
return false;
}
}
/**
* Check has required values
*
* @param \App\Models\Media $media
* @return boolean
*/
private static function hasRequiredValues(Media $media): bool
{
$required = ['sujet_id', 'type_media_id', 'type_vue_id', 'variante_id', 'langue_id', 'version'];
foreach ($required as $v) {
if ($media->{$v} === null) {
return false;
}
}
return true;
}
/**
* Compress Xml File into zip
*
* @param string $xml
* @param \App\Models\Media $media
* @return boolean
*/
private static function compress(string $xml, Media $media): bool
{
/** @var \Illuminate\Filesystem\FilesystemAdapter */
$storage = Storage::disk('arcadia_link_s');
$uuid = Str::uuid();
$filename = "{$media->id}-{$uuid}.zip";
$zip = new \ZipArchive();
$success = $zip->open(
$storage->path($filename),
\ZipArchive::CREATE | \ZipArchive::OVERWRITE
);
$success = $zip->addFromString('media.xml', $xml) && $success;
$success = $zip->addFile($media->full_path, $media->basename) && $success;
$success = $zip->close() && $success;
unset($zip);
// On insert dans la table de supervision.
$arcadiaLink = new ArcadiaLink();
$arcadiaLink->media_id = $media->id;
if ($media->arcadia_id !== null) {
$arcadiaLink->arcadia_id = $media->arcadia_id;
}
$arcadiaLink->filename = $filename;
return $success and $arcadiaLink->save();
}
/**
* Generate Xml
*
* @param \App\Models\Media $media
* @param \App\Enums\EdiMouvementType $mouvement
* @return string
* @throws \RuntimeException If Generating XML Fails.
*/
private static function generateXml(Media $media, EdiMouvementType $mouvement): string
{
$refKey = self::getRefKey($media);
$formats = self::getFormats();
$dom = new DOMDocument('1.0', 'UTF-8');
$dom->formatOutput = true;
try {
$root = $dom->createElement('MEDIA_VISUEL');
$dom->appendChild($root);
$root->appendChild($dom->createElement('codeArcadia'))
->appendChild($dom->createCDATASection("$media->arcadia_id"));
$root->appendChild($dom->createElement('code_DAM'))
->appendChild($dom->createCDATASection("$media->id"));
$root->appendChild($dom->createElement('TypeMouvement'))
->appendChild($dom->createCDATASection("$mouvement->value"));
$cleRef = $root->appendChild($dom->createElement('cle_Referentiel'));
foreach ($refKey as $model) {
if ($media->sujet_id === 1) {
$cleRef->appendChild($dom->createElement('id'))
->appendChild($dom->createCDATASection("$model->noArt"));
} elseif ($media->sujet_id === 2) {
$cleRef->appendChild($dom->createElement('id'))
->appendChild($dom->createCDATASection("$model->codMarq"));
} elseif ($media->sujet_id === 4) {
$cleRef->appendChild($dom->createElement('id'))
->appendChild($dom->createCDATASection("$model->id"));
}
}
$root->appendChild($dom->createElement('libExtension'))
->appendChild($dom->createCDATASection($media->extension->value));
$root->appendChild($dom->createElement('nbPxLargeur'))
->appendChild($dom->createCDATASection("$media->largeur"));
$root->appendChild($dom->createElement('largeurMm'))
->appendChild($dom->createCDATASection("$media->largeur_mm"));
$root->appendChild($dom->createElement('nbPxHauteur'))
->appendChild($dom->createCDATASection("$media->hauteur"));
$root->appendChild($dom->createElement('hauteurMm'))
->appendChild($dom->createCDATASection("$media->hauteur_mm"));
$root->appendChild($dom->createElement('pppLargeur'))
->appendChild($dom->createCDATASection("$media->resolution"));
$root->appendChild($dom->createElement('version'))
->appendChild($dom->createCDATASection("$media->version"));
$root->appendChild($dom->createElement('codeTypeMedia'))
->appendChild($dom->createCDATASection("$media->type_media_id"));
$root->appendChild($dom->createElement('codeTypeVue'))
->appendChild($dom->createCDATASection("$media->type_vue_id"));
$root->appendChild($dom->createElement('commentaire'))
->appendChild($dom->createCDATASection("$media->commentaire"));
$root->appendChild($dom->createElement('codeSujet'))
->appendChild($dom->createCDATASection("$media->sujet_id"));
$root->appendChild($dom->createElement('langue'))
->appendChild($dom->createCDATASection("$media->langue_id"));
$root->appendChild($dom->createElement('copyright'))
->appendChild($dom->createCDATASection("$media->copyright_id"));
$root->appendChild($dom->createElement('codeVariante'))
->appendChild($dom->createCDATASection("$media->variante_id"));
$root->appendChild($dom->createElement('dateCreation'))
->appendChild($dom->createCDATASection("$media->created_at"));
$root->appendChild($dom->createElement('dateModification'))
->appendChild($dom->createCDATASection("$media->updated_at"));
$root->appendChild($dom->createElement('datePublication'))
->appendChild($dom->createCDATASection("$media->published_at"));
$root->appendChild($dom->createElement('libelle'))
->appendChild($dom->createCDATASection("$media->libelle"));
$root->appendChild($dom->createElement('NomFichier'))
->appendChild($dom->createCDATASection("$media->uid.{$media->extension->value}"));
$mediaFormat = $root->appendChild($dom->createElement('formats'));
foreach ($formats as $formatModel) {
$format = $mediaFormat
->appendChild($dom->createElement('format'));
$format->appendChild($dom->createElement('id'))
->appendChild($dom->createCDATASection($formatModel['id']));
$format->appendChild($dom->createElement('libelle'))
->appendChild($dom->createCDATASection($formatModel['libelle']));
$format->appendChild($dom->createElement('extension'))
->appendChild($dom->createCDATASection($media->extension->value));
$format->appendChild($dom->createElement('largeur'))
->appendChild($dom->createCDATASection($formatModel['largeur']));
$format->appendChild($dom->createElement('hauteur'))
->appendChild($dom->createCDATASection($formatModel['hauteur']));
$format->appendChild($dom->createElement('lien'))
->appendChild($dom->createCDATASection(\sprintf(
"%s/{$media->folder}/{$media->uid}_%s.{$media->extension->value}",
config('app.url'),
$formatModel['libelle']
)));
}
} catch (\DOMException | \Exception $e) {
report($e);
throw new \RuntimeException('Failed to generate XML', 0, $e);
} //end try
if (!($xml = $dom->saveXML())) {
throw new \RuntimeException('Failed to generate XML');
}
return $xml;
}
/**
* Get Ref Key
*
* @param \App\Models\Media $media
* @return \Illuminate\Support\Collection<\App\Models\Article|\App\Models\Marque|\App\Models\Societe>
*/
public static function getRefKey(Media $media): Collection
{
switch ($media->sujet_id) {
case 1:
return $media->articles;
case 2:
return $media->marques;
case 4:
return $media->societes;
default:
return collect();
}
}
/**
* Get Formats
*
* @return \Illuminate\Support\Collection<\App\Models\Format>
*/
private static function getFormats(): Collection
{
return Format::withoutGlobalScope(SystemScope::class)
->where('system', true)->get();
}
}

165
app/Lib/Helper.php Normal file
View file

@ -0,0 +1,165 @@
<?php
namespace App\Lib;
use App\Exceptions\Api\DamException;
use Illuminate\Contracts\Encryption\DecryptException;
use Illuminate\Http\Request;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Crypt;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Str;
/**
* Helper Class
*/
class Helper
{
/**
* Package infos
*
* @var array
*/
private static $packageInfos;
/**
* Tells if the request is within api routes
*
* @param Request $request
* @return boolean
*/
public static function isRequestForApi(Request $request): bool
{
// * Small hack to ensure to know if route is From APIv1.
$routePrefix = (strpos(static::getRoutePrefix($request), 'api/') ? 'api/' : null) ??
(strpos($request->path(), 'api/') === 0 ? 'api/' : null) ?? 'none';
return strpos($routePrefix, 'api/') === 0;
}
/**
* Try to get current route name
*
* @param Request $request
* @return string|null
*/
public static function getRouteName(Request $request)
{
return Route::currentRouteName() ?? ($request->route() ? $request->route()->getName() : null);
}
/**
* Get the bearer token from request
*
* @param Request $request
* @return string|null
*/
public static function getBearerTokenFromRequest(Request $request): ?string
{
$header = $request->header('Authorization', '');
if (Str::contains($header, 'Bearer ')) {
$auths = \explode(',', $header);
foreach ($auths as $auth) {
if (Str::contains($auth, 'Bearer ')) {
return Str::substr($auth, 7);
}
}
}
return null;
}
/**
* Try to get current route prefix
*
* @param Request $request
* @return string|null
*/
public static function getRoutePrefix(Request $request)
{
return (Route::getCurrentRoute() ? Route::getCurrentRoute()->getPrefix() : null) ??
($request->route() ? $request->route()->getPrefix() : null);
}
/**
* Try to get route from request
*
* @param Request $request
* @return \Illuminate\Routing\Route|null
*/
public static function getRoute(Request $request)
{
return Route::getRoutes()->getByName(static::getRouteName($request));
}
/**
* Get composer.json infos
*
* @return array
*/
public static function getPackageInfo(): array
{
if (!static::$packageInfos) {
static::$packageInfos = \json_decode(\file_get_contents(__DIR__ . '/../../composer.json'), true);
}
return static::$packageInfos;
}
/**
* Calculate the request signature
*
* @param string $secret
* @param string $uri
* @param string $method
* @param array $parameters
* @return string request signature
* @throws \App\Exceptions\Api\DamException If an error occurs.
*/
public static function signRequest(string $secret, string $uri, string $method, array $parameters): string
{
try {
$uri = collect(\parse_url($uri));
$method = \strtolower($method);
// * Remove signature from parameters to calculate the hashmac.
$signature = collect([
'uri' => sprintf(
'%s://%s%s',
$uri->get('scheme'),
$uri->get('host'),
$uri->get('path')
),
'method' => $method
]);
$parameters = collect(Arr::dot($parameters));
// * Remove signature from parameters.
$parameters->pull('signature');
// * Natural sort all parameters.
$parameters = $parameters->sortKeys(\SORT_NATURAL)
->map(function ($value, $key) {
return sprintf('%s=%s', \rawurlencode($key), \rawurlencode($value));
});
$signature->push($parameters->implode('&'));
$calculatedHash = base64_encode(hash_hmac(
'sha256',
$signature->implode('\n'),
Crypt::decryptString($secret),
true
));
if (!$calculatedHash) {
throw new DamException('Failed to calculate requests hash');
}
return $calculatedHash;
} catch (DecryptException $e) {
throw new DamException('Failed to decrypt service credentials', 0, $e);
} catch (\Exception $e) {
throw new DamException('An error occured when authenticating service', 0, $e);
} //end try
}
}

255
app/Lib/ToolboxHelper.php Normal file
View file

@ -0,0 +1,255 @@
<?php
namespace App\Lib;
use App\Enums\MediaExtension;
use getID3;
use Illuminate\Support\Str;
use Imagick;
use ImagickException;
class ToolboxHelper
{
/**
* Get ID3 Play time string
*
* @param string $filepath
* @return string|null
*/
public static function getId3Playtime(string $filepath): ?string
{
$getID3 = new getID3();
$results = $getID3->analyze($filepath);
if (array_key_exists('error', $results)) {
return null;
}
if (array_key_exists('playtime_string', $results)) {
return \strval($results['playtime_string']);
}
return null;
}
/**
* Get ID3 video dimensions
*
* @param string $filepath
* @return array|null Containing 'width' and 'height'
*/
public static function getId3VideoDimensions(string $filepath): ?array
{
$getID3 = new getID3();
$results = $getID3->analyze($filepath);
if (array_key_exists('error', $results)) {
return null;
}
if (isset($results['video']['resolution_x']) and isset($results['video']['resolution_y'])) {
return [
'width' => \intval($results['video']['resolution_x']),
'height' => \intval($results['video']['resolution_y'])
];
}
return null;
}
/**
* resolution in pixels per centimeter
*
* @param string $filepath
* @return array|null Containing 'resolutionX', 'resolutionY', 'width' and 'height'
*/
public static function getImageDimensions(string $filepath): ?array
{
$ext = \pathinfo($filepath, \PATHINFO_EXTENSION);
try {
$im = new Imagick($filepath);
if (
in_array($ext, [
MediaExtension::pdf->value,
MediaExtension::ai->value,
MediaExtension::psd->value,
MediaExtension::gif->value
])
) {
$im->setIteratorIndex(0);
}
$r = $im->getImageResolution();
$u = $im->getImageUnits();
// Force image resolution to pixel per centimeters.
if ($u == Imagick::RESOLUTION_PIXELSPERINCH) {
$r['x'] = (int)round($r['x'] / 2.54);
$r['y'] = (int)round($r['y'] / 2.54);
$im->setImageUnits(Imagick::RESOLUTION_PIXELSPERCENTIMETER);
$im->setImageResolution($r['x'], $r['y']);
// Note that the number type is double again.
$r = $im->getImageResolution();
}
$imageprops = $im->getImageGeometry();
$im->destroy();
return [
'resolutionX' => \intval($r['x']),
'resolutionY' => \intval($r['y']),
'width' => \intval($imageprops['width']),
'height' => \intval($imageprops['height'])
];
} catch (ImagickException $e) {
report($e);
} //end try
return null;
}
/**
* Sanitize File name for uploaded files
*
* @param string $filename The original uploaded client file name.
* @param boolean $beautify Beautify the filename.
* @return string The sanitized file name.
*/
public static function sanitizeFileName(string $filename, bool $beautify = true): string
{
// * Sanitize filename
// phpcs:disable Generic.Files.LineLength.TooLong
$filename = preg_replace(
'~
[<>:"/\\\|?*]| # file system reserved https://en.wikipedia.org/wiki/Filename#Reserved_characters_and_words
[\x00-\x1F]| # control characters http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247%28v=vs.85%29.aspx
[\x7F\xA0\xAD]| # non-printing characters DEL, NO-BREAK SPACE, SOFT HYPHEN
[#\[\]@!$&\'()+,;=]| # URI reserved https://tools.ietf.org/html/rfc3986#section-2.2
[{}^\~`] # URL unsafe characters https://www.ietf.org/rfc/rfc1738.txt
~x',
'-',
$filename
);
// phpcs:enable
// * Avoids '.' '..' or '.hiddenFiles' .
$filename = Str::of($filename)->ltrim('.-');
// * Optional beautification
if ($beautify) {
$filename = static::beautifyFilename($filename);
}
// * Maximize filename length to 255 bytes http://serverfault.com/a/9548/44086
/** @var string */
$extension = pathinfo($filename, \PATHINFO_EXTENSION);
/** @var string */
$filename = pathinfo($filename, \PATHINFO_FILENAME);
$encoding = mb_detect_encoding($filename);
$encoding = $encoding ? $encoding : null;
return \mb_strcut(
$filename,
0,
255 - (!empty($extension) ? strlen($extension) + 1 : 0),
$encoding
) . (!empty($extension) ? ".$extension" : '');
}
/**
* Beautify a filename
*
* @param string $filename
* @return string
*/
private static function beautifyFilename(string $filename): string
{
// Reduce consecutive characters.
$filename = preg_replace([
// * "file name.zip" becomes 'file-name.zip'
'/ +/',
// * "file___name.zip" becomes 'file-name.zip'
'/_+/',
// * "file---name.zip" becomes 'file-name.zip'
'/-+/'
], '-', $filename);
$filename = preg_replace([
// * "file--.--.-.--name.zip" becomes "file.name.zip"
'/-*\.-*/',
// * "file...name..zip" becomes "file.name.zip"
'/\.{2,}/'
], '.', $filename);
// Lowercase for windows/unix interoperability http://support.microsoft.com/kb/100625 .
$filename = mb_strtolower($filename, mb_detect_encoding($filename));
// * ".file-name.-" becomes "file-name"
$filename = trim($filename, '.-');
return $filename;
}
/**
* Convert file size to human readable string
*
* @param mixed $octet
* @param integer $precision
* @return string
*/
public static function formatBytes(mixed $octet, int $precision = 2): string
{
// * Array contenant les differents unités
$unite = ['octet', 'Kio', 'Mio', 'Gio'];
$octet = \floatval($octet);
$Kio = 1024;
$Mio = 1024 * $Kio;
$Gio = 1024 * $Mio;
switch (true) {
// * octet
case $octet < $Kio:
return $octet . $unite[0];
// * kio
case $octet < $Mio:
return $octet . $unite[1];
// * Mio
case $octet < $Gio:
return \round($octet / $Mio, $precision) . $unite[2];
// * Gio
default:
// * Gio
return \round($octet / $Gio, $precision) . $unite[3];
}
}
/**
* * Register CSP report URI
*
* Put this function inside RouteServiceProvider,
* and call it in RouteServiceProvider::boot
*
* ! Use this in conjonction with kwaadpepper/exception-handler
*
* @return void
*/
public static function cspReportRoute()
{
// * CSP REPORT URI, Please add /csp-report to VerifyCsrfToken $except middleware
\Illuminate\Support\Facades\Route::post('/csp-report', function () {
$isCspRequest = request()->header('Content-Type') === 'application/csp-report';
if (!$isCspRequest or !request()->json('csp-report') or !is_array(request()->json('csp-report'))) {
abort(401);
}
// * Flatten if needed
$cspReport = collect(request()->json('csp-report'))
->flatMap(function ($v, $k) {
return [$k => is_array($v) ? collect($v)->flatten()->implode(', ') : $v];
})->all();
// * Filter array keys
$cspReport = collect(array_intersect_key($cspReport, [
'blocked-uri' => '', 'document-uri' => '', 'original-policy' => '',
'referrer' => '', 'violated-directive' => ''
]));
report(new \Illuminate\Http\Client\HttpClientException(sprintf(
"CSP REPORT for %s : \n %s",
$cspReport->get('document-uri'),
$cspReport->implode("\n")
)));
})->middleware('throttle:5,2')
->withoutMiddleware(\App\Http\Middleware\VerifyCsrfToken::class)
->name('cspReportUri');
}
}

View file

@ -0,0 +1,26 @@
<?php
namespace App\Listeners;
use App\Events\MediaSearched;
use App\Models\Recherche;
class CleanUserOldSearches
{
/**
* Handle the event.
*
* @param \App\Events\MediaSearched $event
* @return void
* @throws \RuntimeException If linking with Arcadia failed.
*/
public function handle(MediaSearched $event)
{
// * Purge des anciennes recherches
$count = Recherche::where('user_id', $event->getUser()->id)->count();
Recherche::where('user_id', $event->getUser()->id)->orderBy("updated_at", "desc")
->skip(10)->take($count)->get()->each(function (Recherche $recherche) {
$recherche->deleteOrFail();
});
}
}

View file

@ -0,0 +1,26 @@
<?php
namespace App\Listeners;
use App\Events\MediaLinkWithArcadia;
use App\Lib\ArcadiaLinkUtilities;
class CreateIntoArcadia
{
/**
* Handle the event.
*
* @param \App\Events\MediaLinkWithArcadia $event
* @return void
* @throws \RuntimeException If linking with Arcadia failed.
*/
public function handle(MediaLinkWithArcadia $event)
{
$media = $event->getMedia();
// * Link To arcadia
if (!ArcadiaLinkUtilities::create($media)) {
throw new \RuntimeException('Failed to link media with Arcadia');
}
}
}

View file

@ -0,0 +1,80 @@
<?php
namespace App\Listeners;
use App\Enums\EventHistoryType;
use App\Events\MediaChangedLabel;
use App\Events\MediaCreated;
use App\Events\MediaDestroyed;
use App\Events\MediaLinkWithArcadia;
use App\Events\MediaMoved;
use App\Events\MediaUpdated;
use App\Models\Historique;
class HistorizeMediaEvent
{
/**
* Handle the event.
*
* @param \App\Events\MediaCreated|\App\Events\MediaLinkWithArcadia $event
* @return void
* @throws \RuntimeException If saving historic to database failed.
*/
public function handle(MediaCreated | MediaLinkWithArcadia $event)
{
$historique = new Historique();
$type = EventHistoryType::created;
$description = trans('Média ajouté');
if ($event instanceof MediaMoved) {
$type = EventHistoryType::moved;
$description = $event->getMedia()->dossier_id > 0 ?
trans('Déplacement dans le dossier :libelle', [
'libelle' => $event->getMedia()->dossier->libelle
]) : trans('Média déplacé à la racine');
}
if ($event instanceof MediaUpdated) {
$type = EventHistoryType::changed;
$description = trans('Le média a été mis à jour');
}
if ($event instanceof MediaDestroyed) {
$type = EventHistoryType::destroyed;
$description = trans('Le média a été supprimé');
}
if ($event instanceof MediaChangedLabel) {
$type = EventHistoryType::labelled;
$description = sprintf(
'%s -> %s',
\optional($event->getOldLabel())->libelle ?? trans('aucune'),
\optional($event->getNewLabel())->libelle ?? trans('aucune')
);
}
if ($event instanceof MediaLinkWithArcadia) {
$type = EventHistoryType::changed;
$description = trans('Ajout de la liaison Arcadia');
}
$historique->user()->associate($event->getUser());
$historique->media()->associate($event->getMedia());
$historique->type = $type;
$historique->description = $description;
if (!$historique->save()) {
throw new \RuntimeException('Failed to save historic to Database');
}
}
/**
* Handle a job failure.
*
* @param mixed $event
* @param \Throwable $exception
* @return void
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundBeforeLastUsed
*/
public function failed(mixed $event, \Throwable $exception)
{
//phpcs:enable
report($exception);
}
}

Some files were not shown because too many files have changed in this diff Show more