mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-09-30 18:39:43 +00:00
Compare commits
152 Commits
chill-bund
...
v3.0.0-RC4
Author | SHA1 | Date | |
---|---|---|---|
ead1abb825
|
|||
54d045f261
|
|||
702a5a27d2 | |||
|
41dd4d89f7 | ||
|
3a7ed7ef8f | ||
2c99ea17d4 | |||
18df08e8c3 | |||
db3961275b
|
|||
cd488d7576 | |||
436661d952 | |||
c3b8d42047 | |||
|
9c28df25a1 | ||
d88b5a0098
|
|||
|
c5a24e8ac5 | ||
|
d9c50cffb7 | ||
|
25ccb16308 | ||
|
ba25c181f5 | ||
4c3bfc90b5 | |||
145419a76b | |||
b1ba5cc608 | |||
87a6757e5e | |||
bd41308bbd
|
|||
f8fa96d836
|
|||
d28cec3786
|
|||
7cd36cd483
|
|||
d3d98cdec2
|
|||
49dd7f94fa
|
|||
916724c0c5
|
|||
|
102d0dad94 | ||
|
8d225dd68c | ||
|
61d0005be8 | ||
47f4cfddbb
|
|||
e95f9e9846 | |||
1f4bef754d
|
|||
19e34d5dc0
|
|||
fab00f679c
|
|||
791b3776c5
|
|||
6bd38f1a58
|
|||
68d21c9267
|
|||
e7ca89e0c1
|
|||
fc8bc33ba9
|
|||
cbd9489810
|
|||
90b615c5b2
|
|||
5ca222b501 | |||
3e4495dd6e
|
|||
bca0d04201
|
|||
f66ac50571 | |||
b454774836
|
|||
008f344e49
|
|||
90bfd87ec6
|
|||
cc0030c1cd
|
|||
d60ba3ecb2
|
|||
cd5001ac74 | |||
98f47ac512
|
|||
31b541d12f
|
|||
72045ce082
|
|||
0bfb3de465
|
|||
9ec4c77fb7
|
|||
77c53972c8
|
|||
350d991a85
|
|||
0ce9cdd07a
|
|||
1993fac1c4
|
|||
83883567a2
|
|||
29d57934a1
|
|||
f43d79c940
|
|||
be730679c8
|
|||
f62f1891d8
|
|||
ebb856fe85
|
|||
61877e0157
|
|||
4c3f082163
|
|||
35109133f6
|
|||
a220dad83b
|
|||
9eb571549b
|
|||
db8257d230 | |||
bce93efe83 | |||
06401af801 | |||
ea1d4c48f2
|
|||
6db36d5ab6
|
|||
59f721934e
|
|||
|
33cba27dd4 | ||
84ce8a93f3
|
|||
ab5f2ffb65
|
|||
73bae8ccb9
|
|||
dcfa569e3a
|
|||
4b07fe3622
|
|||
48bf359d2e
|
|||
60c7ea601c
|
|||
a7ec7c9f37 | |||
c9e13be736 | |||
b9b342fe44 | |||
31f29f0bc5 | |||
0bc9fff825 | |||
25f93e8a89 | |||
4e0d8e4def | |||
1ecc825945 | |||
addc623add | |||
1b96deb4ee | |||
f510acd170 | |||
835409cb94 | |||
2121b3ef28 | |||
6c9101c167 | |||
b46883fe36 | |||
8d58805abd | |||
c3a799cb7d | |||
bc683b28d6 | |||
d91b1a70bf | |||
853014d8d2 | |||
ad6154a1e4 | |||
50c04382ef | |||
d62e9ce269 | |||
2149ef1cb4 | |||
d15fbadd27 | |||
fbbf421d8b | |||
fe695f1a14 | |||
d0ec6f9819 | |||
0b739fda34 | |||
9b8e143855 | |||
a533ab77ed | |||
087032881b | |||
82667a1c0f | |||
db6408926b | |||
f5c7ab6ef0 | |||
a13ada2937 | |||
3be8a39a1a | |||
d7eb1e01da | |||
bd62202d22 | |||
0e3de2ec8a | |||
aa2a398f9e | |||
33187448a0 | |||
a4482ad28b | |||
8ed5a023e8 | |||
653ac1d62b | |||
499009ac43 | |||
192b161e78 | |||
1b1f355123 | |||
39a863448c | |||
0c1a4a5f59 | |||
6f358ee1a9 | |||
0f36b9349b | |||
d18cc29acf | |||
4220d1a2d3 | |||
1ae27152c2 | |||
b946f8c10a | |||
62d6106801 | |||
89fb87f71f | |||
1337360690 | |||
9324c33caf | |||
c2dd9ef676 | |||
a42d7231d9 | |||
38deaf6f36 | |||
04fc5b6614 | |||
384b2be577 |
6
.changes/unreleased/Feature-20240530-160003.yaml
Normal file
6
.changes/unreleased/Feature-20240530-160003.yaml
Normal file
@@ -0,0 +1,6 @@
|
||||
kind: Feature
|
||||
body: |
|
||||
Upgrade import of address list to the last version of compiled addresses of belgian-best-address
|
||||
time: 2024-05-30T16:00:03.440767606+02:00
|
||||
custom:
|
||||
Issue: ""
|
6
.changes/unreleased/Feature-20240531-190242.yaml
Normal file
6
.changes/unreleased/Feature-20240531-190242.yaml
Normal file
@@ -0,0 +1,6 @@
|
||||
kind: Feature
|
||||
body: |
|
||||
Upgrade CKEditor and refactor configuration with use of typescript
|
||||
time: 2024-05-31T19:02:42.776662753+02:00
|
||||
custom:
|
||||
Issue: ""
|
21
.changes/v2.20.0.md
Normal file
21
.changes/v2.20.0.md
Normal file
@@ -0,0 +1,21 @@
|
||||
## v2.20.0 - 2024-06-05
|
||||
### Fixed
|
||||
* ([#170](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/170)) Display agents traitants instead of accompanying period referrer in export list social actions.
|
||||
* Added translations for choices of durations (> 5 hours)
|
||||
### Feature
|
||||
* ([#145](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/145)) Allow to open documents in LibreOffice locally (need configuration within security);
|
||||
|
||||
This endpoint should be added to make the endpoint works properly:
|
||||
|
||||
```yaml
|
||||
security:
|
||||
firewalls:
|
||||
dav:
|
||||
pattern: ^/dav
|
||||
provider: chain_provider
|
||||
stateless: true
|
||||
guard:
|
||||
authenticators:
|
||||
- Chill\DocStoreBundle\Security\Guard\JWTOnDavUrlAuthenticator
|
||||
|
||||
```
|
3
.changes/v2.20.1.md
Normal file
3
.changes/v2.20.1.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## v2.20.1 - 2024-06-05
|
||||
### Fixed
|
||||
* Do not allow StoredObjectCreated for edit and convert buttons
|
31
.changes/v2.21.0.md
Normal file
31
.changes/v2.21.0.md
Normal file
@@ -0,0 +1,31 @@
|
||||
## v2.21.0 - 2024-06-18
|
||||
### Feature
|
||||
* Add flash menu buttons in search results, to open directly a new calendar, or a new activity in an accompanying period
|
||||
* ([#122](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/122)) Improve the list of calendar in the search results: make all calendar clicable, and display a list of calendars
|
||||
* ([#282](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/282)) [export] add start date and end date on filters "filter course by referrer job" and "filter course by referrer scope"
|
||||
* ([#282](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/282)) [export] the aggregator "Group by referrer" now accept a date range.
|
||||
* ([#282](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/282)) [export] add date range on "group course by referrer's scope"
|
||||
* ([#282](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/282)) [export] add date range on "group course by referrer's jobs"
|
||||
* ([#168](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/168) In the UX, display user job and service at the time when he performs an action:
|
||||
now, the job and service is shown:
|
||||
* at the activity's date,
|
||||
* at the appointment's date,
|
||||
* when the user is marked as referrer for an accompanying period work,
|
||||
* when the user apply a transition in a workflow,
|
||||
* when the user updates or creates "something" ("created/updated by ... at ..."),
|
||||
* or when he wrote a comment,
|
||||
* …
|
||||
|
||||
### Traduction francophone
|
||||
* Ajout d'un menu "flash" dans les résultats de recherche, pour créer un rendez-vous ou un échange dans un parcours depuis les résultats de recherche;
|
||||
* Améliore la liste des rendez-vous dans les résultats de recherche: les rendez-vous sont cliquables;
|
||||
* [exports] Ajout d'intervalles de dates pour des filtres et regroupements des parcours par référent, métier du référent, service du référent;
|
||||
* Affiche le métier et le service des utilisateurs à la date à laquelle il a exécuté une action. Le métier et le service est affiché:
|
||||
* à la date d'un échange,
|
||||
* au jour d'un rendez-vous,
|
||||
* quand l'utilisateur est devenu référent d'un parcours d'accompagnement,
|
||||
* quand il a appliqué une transition sur un workflow,
|
||||
* quand il a mise à jour ou créé une fiche, dans les mentions "créé / mise à jour par ..., le ...",
|
||||
* quand il a mis à jour un commentaire,
|
||||
* …
|
||||
|
6
.changes/v2.22.0.md
Normal file
6
.changes/v2.22.0.md
Normal file
@@ -0,0 +1,6 @@
|
||||
## v2.22.0 - 2024-06-25
|
||||
### Feature
|
||||
* ([#216](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/216)) [event bundle] exports added for the event module
|
||||
|
||||
### Traduction francophone
|
||||
* Exports sont ajoutés pour la module événement.
|
5
.changes/v2.22.1.md
Normal file
5
.changes/v2.22.1.md
Normal file
@@ -0,0 +1,5 @@
|
||||
## v2.22.1 - 2024-07-01
|
||||
### Fixed
|
||||
* Remove debug word
|
||||
### DX
|
||||
* Add a command for reading official address DB from Luxembourg and update chill addresses
|
3
.changes/v2.22.2.md
Normal file
3
.changes/v2.22.2.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## v2.22.2 - 2024-07-03
|
||||
### Fixed
|
||||
* Remove scope required for event participation stats
|
75
CHANGELOG.md
75
CHANGELOG.md
@@ -6,6 +6,81 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html),
|
||||
and is generated by [Changie](https://github.com/miniscruff/changie).
|
||||
|
||||
|
||||
## v2.22.2 - 2024-07-03
|
||||
### Fixed
|
||||
* Remove scope required for event participation stats
|
||||
|
||||
## v2.22.1 - 2024-07-01
|
||||
### Fixed
|
||||
* Remove debug word
|
||||
### DX
|
||||
* Add a command for reading official address DB from Luxembourg and update chill addresses
|
||||
|
||||
## v2.22.0 - 2024-06-25
|
||||
### Feature
|
||||
* ([#216](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/216)) [event bundle] exports added for the event module
|
||||
|
||||
### Traduction francophone
|
||||
* Exports sont ajoutés pour la module événement.
|
||||
|
||||
## v2.21.0 - 2024-06-18
|
||||
### Feature
|
||||
* Add flash menu buttons in search results, to open directly a new calendar, or a new activity in an accompanying period
|
||||
* ([#122](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/122)) Improve the list of calendar in the search results: make all calendar clicable, and display a list of calendars
|
||||
* ([#282](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/282)) [export] add start date and end date on filters "filter course by referrer job" and "filter course by referrer scope"
|
||||
* ([#282](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/282)) [export] the aggregator "Group by referrer" now accept a date range.
|
||||
* ([#282](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/282)) [export] add date range on "group course by referrer's scope"
|
||||
* ([#282](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/282)) [export] add date range on "group course by referrer's jobs"
|
||||
* ([#168](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/168) In the UX, display user job and service at the time when he performs an action:
|
||||
now, the job and service is shown:
|
||||
* at the activity's date,
|
||||
* at the appointment's date,
|
||||
* when the user is marked as referrer for an accompanying period work,
|
||||
* when the user apply a transition in a workflow,
|
||||
* when the user updates or creates "something" ("created/updated by ... at ..."),
|
||||
* or when he wrote a comment,
|
||||
* …
|
||||
|
||||
### Traduction francophone
|
||||
* Ajout d'un menu "flash" dans les résultats de recherche, pour créer un rendez-vous ou un échange dans un parcours depuis les résultats de recherche;
|
||||
* Améliore la liste des rendez-vous dans les résultats de recherche: les rendez-vous sont cliquables;
|
||||
* [exports] Ajout d'intervalles de dates pour des filtres et regroupements des parcours par référent, métier du référent, service du référent;
|
||||
* Affiche le métier et le service des utilisateurs à la date à laquelle il a exécuté une action. Le métier et le service est affiché:
|
||||
* à la date d'un échange,
|
||||
* au jour d'un rendez-vous,
|
||||
* quand l'utilisateur est devenu référent d'un parcours d'accompagnement,
|
||||
* quand il a appliqué une transition sur un workflow,
|
||||
* quand il a mise à jour ou créé une fiche, dans les mentions "créé / mise à jour par ..., le ...",
|
||||
* quand il a mis à jour un commentaire,
|
||||
* …
|
||||
|
||||
|
||||
## v2.20.1 - 2024-06-05
|
||||
### Fixed
|
||||
* Do not allow StoredObjectCreated for edit and convert buttons
|
||||
|
||||
## v2.20.0 - 2024-06-05
|
||||
### Fixed
|
||||
* ([#170](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/170)) Display agents traitants instead of accompanying period referrer in export list social actions.
|
||||
* Added translations for choices of durations (> 5 hours)
|
||||
### Feature
|
||||
* ([#145](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/145)) Allow to open documents in LibreOffice locally (need configuration within security);
|
||||
|
||||
This endpoint should be added to make the endpoint works properly:
|
||||
|
||||
```yaml
|
||||
security:
|
||||
firewalls:
|
||||
dav:
|
||||
pattern: ^/dav
|
||||
provider: chain_provider
|
||||
stateless: true
|
||||
guard:
|
||||
authenticators:
|
||||
- Chill\DocStoreBundle\Security\Guard\JWTOnDavUrlAuthenticator
|
||||
|
||||
```
|
||||
|
||||
## v2.19.0 - 2024-05-14
|
||||
### Feature
|
||||
* ([#197](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/197)) Make the script which subscribe to microsoft calendars changes more tolerant to errors or missing configuration on the microsoft side
|
||||
|
@@ -55,7 +55,7 @@ Arborescence:
|
||||
- person
|
||||
- personvendee
|
||||
- household_edit_metadata
|
||||
- index.ts
|
||||
- index.js
|
||||
```
|
||||
|
||||
## Organisation des feuilles de styles
|
||||
|
@@ -19,7 +19,6 @@
|
||||
"doctrine/doctrine-migrations-bundle": "^3.0",
|
||||
"doctrine/orm": "^2.13.0",
|
||||
"erusev/parsedown": "^1.7",
|
||||
"graylog2/gelf-php": "^1.5",
|
||||
"knplabs/knp-menu-bundle": "^3.0",
|
||||
"knplabs/knp-time-bundle": "^1.12",
|
||||
"knpuniversity/oauth2-client-bundle": "^2.10",
|
||||
@@ -92,12 +91,12 @@
|
||||
"phpstan/phpstan": "^1.9",
|
||||
"phpstan/phpstan-deprecation-rules": "^1.1",
|
||||
"phpstan/phpstan-strict-rules": "^1.0",
|
||||
"phpunit/phpunit": ">= 7.5",
|
||||
"phpunit/phpunit": "^10.5.24",
|
||||
"rector/rector": "^1.1.0",
|
||||
"symfony/debug-bundle": "^5.4",
|
||||
"symfony/dotenv": "^5.4",
|
||||
"symfony/maker-bundle": "^1.20",
|
||||
"symfony/phpunit-bridge": "^5.4",
|
||||
"symfony/phpunit-bridge": "^7.1",
|
||||
"symfony/runtime": "^5.4",
|
||||
"symfony/stopwatch": "^5.4",
|
||||
"symfony/var-dumper": "^5.4"
|
||||
@@ -119,7 +118,6 @@
|
||||
"Chill\\PersonBundle\\": "src/Bundle/ChillPersonBundle",
|
||||
"Chill\\ReportBundle\\": "src/Bundle/ChillReportBundle",
|
||||
"Chill\\TaskBundle\\": "src/Bundle/ChillTaskBundle",
|
||||
"Chill\\TicketBundle\\": "src/Bundle/ChillTicketBundle/src",
|
||||
"Chill\\ThirdPartyBundle\\": "src/Bundle/ChillThirdPartyBundle",
|
||||
"Chill\\WopiBundle\\": "src/Bundle/ChillWopiBundle/src",
|
||||
"Chill\\Utils\\Rector\\": "utils/rector/src"
|
||||
@@ -127,9 +125,8 @@
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"App\\": "tests",
|
||||
"App\\": "tests/",
|
||||
"Chill\\DocGeneratorBundle\\Tests\\": "src/Bundle/ChillDocGeneratorBundle/tests",
|
||||
"Chill\\TicketBundle\\Tests\\": "src/Bundle/ChillTicketBundle/tests",
|
||||
"Chill\\WopiBundle\\Tests\\": "src/Bundle/ChillDocGeneratorBundle/tests",
|
||||
"Chill\\Utils\\Rector\\Tests\\": "utils/rector/tests"
|
||||
}
|
||||
@@ -151,6 +148,7 @@
|
||||
"scripts": {
|
||||
"auto-scripts": {
|
||||
"cache:clear": "symfony-cmd"
|
||||
}
|
||||
},
|
||||
"php-cs-fixer": "php-cs-fixer fix --config=./.php-cs-fixer.dist.php --show-progress=none"
|
||||
}
|
||||
}
|
||||
|
@@ -56,7 +56,7 @@ We strongly encourage you to initialize a git repository at this step, to track
|
||||
cat <<< "$(jq '.extra.symfony += {"endpoint": ["flex://defaults", "https://gitlab.com/api/v4/projects/57371968/repository/files/index.json/raw?ref=main"]}' composer.json)" > composer.json
|
||||
# install chill and some dependencies
|
||||
# TODO fix the suffix "alpha1" and replace by ^3.0.0 when version 3.0.0 will be released
|
||||
symfony composer require chill-project/chill-bundles v3.0.0-alpha1 champs-libres/wopi-lib dev-master@dev champs-libres/wopi-bundle dev-master@dev
|
||||
symfony composer require chill-project/chill-bundles v3.0.0-RC3 champs-libres/wopi-lib dev-master@dev champs-libres/wopi-bundle dev-master@dev
|
||||
|
||||
We encourage you to accept the inclusion of the "Docker configuration from recipes": this is the documented way to run the database.
|
||||
You must also accept to configure recipes from the contrib repository, unless you want to configure the bundles manually).
|
||||
@@ -95,7 +95,7 @@ custom developments. But most of the time, this should be fine.
|
||||
|
||||
You have to configure some local variables, which are described in the :code:`.env` file. The secrets should not be stored
|
||||
in this :code:`.env` file, but instead using the `secrets management tool <https://symfony.com/doc/current/configuration/secrets.html>`_
|
||||
or in the :code:`.env.local` file, which should not be commited to the git repository.
|
||||
or in the :code:`.env.local` file, which should not be committed to the git repository.
|
||||
|
||||
You do not need to set variables for the smtp server, redis server and relatorio server, as they are generated automatically
|
||||
by the symfony server, from the docker compose services.
|
||||
@@ -110,10 +110,15 @@ you can either:
|
||||
.. code-block:: env
|
||||
|
||||
ADMIN_PASSWORD=\$2y\$13\$iyvJLuT4YEa6iWXyQV4/N.hNHpNG8kXlYDkkt5MkYy4FXcSwYAwmm
|
||||
# note: if you copy-paste the line above, the password will be "admin".
|
||||
|
||||
- add the generated password to the secrets manager (**note**: you must add the generated hashed password to the secrets env,
|
||||
not the password in clear text).
|
||||
|
||||
- set up the jwt authentication bundle
|
||||
|
||||
Some environment variables are available for the JWT authentication bundle in the :code:`.env` file.
|
||||
|
||||
Prepare migrations and other tools
|
||||
**********************************
|
||||
|
||||
@@ -130,6 +135,8 @@ To continue the installation process, you will have to run migrations:
|
||||
symfony console messenger:setup-transports
|
||||
# prepare some views
|
||||
symfony console chill:db:sync-views
|
||||
# generate jwt token, required for some api features (webdav access, ...)
|
||||
symfony console lexik:jwt:generate-keypair
|
||||
|
||||
.. warning::
|
||||
|
||||
@@ -164,7 +171,7 @@ can rely on the whole chill framework, meaning there is no need to add them to t
|
||||
You will require some bundles to have the following development tools:
|
||||
|
||||
- add fixtures
|
||||
- add profiler and var-dumper to debug
|
||||
- add profiler and debug bundle
|
||||
|
||||
Install fixtures
|
||||
****************
|
||||
@@ -179,7 +186,7 @@ Install fixtures
|
||||
This will generate user accounts, centers, and some basic configuration.
|
||||
|
||||
The accounts created are: :code:`center a_social`, :code:`center b_social`, :code:`center a_direction`, ... The full list is
|
||||
visibile in the "users" table: :code:`docker compose exec database psql -U app -c "SELECT username FROM users"`.
|
||||
visible in the "users" table: :code:`docker compose exec database psql -U app -c "SELECT username FROM users"`.
|
||||
|
||||
The password is always :code:`password`.
|
||||
|
||||
@@ -192,7 +199,7 @@ Add web profiler and debugger
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
symfony composer require --dev symfony/web-profiler-bundle symfony/var-dumper
|
||||
symfony composer require --dev symfony/web-profiler-bundle symfony/debug-bundle
|
||||
|
||||
Working on chill bundles
|
||||
************************
|
||||
|
17
package.json
17
package.json
@@ -6,15 +6,16 @@
|
||||
"@apidevtools/swagger-cli": "^4.0.4",
|
||||
"@babel/core": "^7.20.5",
|
||||
"@babel/preset-env": "^7.20.2",
|
||||
"@ckeditor/ckeditor5-build-classic": "^35.3.2",
|
||||
"@ckeditor/ckeditor5-dev-utils": "^31.1.13",
|
||||
"@ckeditor/ckeditor5-build-classic": "^41.4.2",
|
||||
"@ckeditor/ckeditor5-dev-utils": "^40.2.0",
|
||||
"@ckeditor/ckeditor5-dev-webpack-plugin": "^31.1.13",
|
||||
"@ckeditor/ckeditor5-markdown-gfm": "^35.3.2",
|
||||
"@ckeditor/ckeditor5-theme-lark": "^35.3.2",
|
||||
"@ckeditor/ckeditor5-vue": "^4.0.1",
|
||||
"@ckeditor/ckeditor5-dev-translations": "^40.2.0",
|
||||
"@ckeditor/ckeditor5-markdown-gfm": "^41.4.2",
|
||||
"@ckeditor/ckeditor5-theme-lark": "^41.4.2",
|
||||
"@ckeditor/ckeditor5-vue": "^5.1.0",
|
||||
"@symfony/webpack-encore": "^4.1.0",
|
||||
"@tsconfig/node14": "^1.0.1",
|
||||
"@types/dompurify": "^3.0.5",
|
||||
"@types/dompurify": "^3.0.5",
|
||||
"bindings": "^1.5.0",
|
||||
"bootstrap": "5.2.3",
|
||||
"chokidar": "^3.5.1",
|
||||
@@ -31,7 +32,7 @@
|
||||
"select2-bootstrap-theme": "0.1.0-beta.10",
|
||||
"style-loader": "^3.3.1",
|
||||
"ts-loader": "^9.3.1",
|
||||
"typescript": "^4.7.2",
|
||||
"typescript": "^5.4.5",
|
||||
"vue-loader": "^17.0.0",
|
||||
"webpack": "^5.75.0",
|
||||
"webpack-cli": "^5.0.1"
|
||||
@@ -45,9 +46,11 @@
|
||||
"@fullcalendar/vue3": "^6.1.4",
|
||||
"@popperjs/core": "^2.9.2",
|
||||
"@types/leaflet": "^1.9.3",
|
||||
"@types/dompurify": "^3.0.5",
|
||||
"dropzone": "^5.7.6",
|
||||
"es6-promise": "^4.2.8",
|
||||
"leaflet": "^1.7.1",
|
||||
"marked": "^12.0.2",
|
||||
"masonry-layout": "^4.2.2",
|
||||
"mime": "^4.0.0",
|
||||
"swagger-ui": "^4.15.5",
|
||||
|
@@ -49,10 +49,6 @@
|
||||
<!-- temporarily removed, the time to find a fix -->
|
||||
<exclude>src/Bundle/ChillPersonBundle/Tests/Controller/PersonDuplicateControllerViewTest.php</exclude>
|
||||
</testsuite>
|
||||
|
||||
<testsuite name="TicketBundle">
|
||||
<directory suffix="Test.php">src/Bundle/ChillTicketBundle/tests/</directory>
|
||||
</testsuite>
|
||||
<!--
|
||||
<testsuite name="ReportBundle">
|
||||
<directory suffix="Test.php">src/Bundle/ChillReportBundle/Tests/</directory>
|
||||
|
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\ActivityBundle\Menu;
|
||||
|
||||
use Chill\ActivityBundle\Security\Authorization\ActivityVoter;
|
||||
use Chill\MainBundle\Routing\LocalMenuBuilderInterface;
|
||||
use Knp\Menu\MenuItem;
|
||||
use Symfony\Component\Security\Core\Security;
|
||||
|
||||
final readonly class AccompanyingCourseQuickMenuBuilder implements LocalMenuBuilderInterface
|
||||
{
|
||||
public function __construct(private Security $security) {}
|
||||
|
||||
public static function getMenuIds(): array
|
||||
{
|
||||
return ['accompanying_course_quick_menu'];
|
||||
}
|
||||
|
||||
public function buildMenu($menuId, MenuItem $menu, array $parameters)
|
||||
{
|
||||
/** @var \Chill\PersonBundle\Entity\AccompanyingPeriod $accompanyingCourse */
|
||||
$accompanyingCourse = $parameters['accompanying-course'];
|
||||
|
||||
if ($this->security->isGranted(ActivityVoter::CREATE, $accompanyingCourse)) {
|
||||
$menu
|
||||
->addChild('Create a new activity in accompanying course', [
|
||||
'route' => 'chill_activity_activity_new',
|
||||
'routeParameters' => [
|
||||
// 'activityType_id' => '',
|
||||
'accompanying_period_id' => $accompanyingCourse->getId(),
|
||||
],
|
||||
])
|
||||
->setExtras([
|
||||
'order' => 10,
|
||||
'icon' => 'plus',
|
||||
])
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
@@ -68,7 +68,7 @@
|
||||
<div class="wl-col title"><h3>{{ 'Referrer'|trans }}</h3></div>
|
||||
<div class="wl-col list">
|
||||
<p class="wl-item">
|
||||
<span class="badge-user">{{ activity.user|chill_entity_render_box }}</span>
|
||||
<span class="badge-user">{{ activity.user|chill_entity_render_box({'at_date': activity.date}) }}</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -87,7 +87,7 @@
|
||||
<li>
|
||||
{% if bloc.type == 'user' %}
|
||||
<span class="badge-user">
|
||||
{{ item|chill_entity_render_box({'render': 'raw', 'addAltNames': false }) }}
|
||||
{{ item|chill_entity_render_box({'render': 'raw', 'addAltNames': false, 'at_date': entity.date }) }}
|
||||
</span>
|
||||
{% else %}
|
||||
{{ _self.insert_onthefly(bloc.type, item) }}
|
||||
@@ -114,7 +114,7 @@
|
||||
<li>
|
||||
{% if bloc.type == 'user' %}
|
||||
<span class="badge-user">
|
||||
{{ item|chill_entity_render_box({'render': 'raw', 'addAltNames': false }) }}
|
||||
{{ item|chill_entity_render_box({'render': 'raw', 'addAltNames': false, 'at_date': entity.date }) }}
|
||||
</span>
|
||||
{% else %}
|
||||
{{ _self.insert_onthefly(bloc.type, item) }}
|
||||
@@ -142,7 +142,7 @@
|
||||
<span class="wl-item">
|
||||
{% if bloc.type == 'user' %}
|
||||
<span class="badge-user">
|
||||
{{ item|chill_entity_render_box({'render': 'raw', 'addAltNames': false }) }}
|
||||
{{ item|chill_entity_render_box({'render': 'raw', 'addAltNames': false, 'at_date': entity.date }) }}
|
||||
{%- if context == 'calendar_accompanyingCourse' or context == 'calendar_person' %}
|
||||
{% set invite = entity.inviteForUser(item) %}
|
||||
{% if invite is not null %}
|
||||
|
@@ -41,7 +41,7 @@
|
||||
{% if activity.user and t.userVisible %}
|
||||
<li>
|
||||
<span class="item-key">{{ 'Referrer'|trans ~ ': ' }}</span>
|
||||
<span class="badge-user">{{ activity.user|chill_entity_render_box }}</span>
|
||||
<span class="badge-user">{{ activity.user|chill_entity_render_box({'at_date': activity.date}) }}</span>
|
||||
</li>
|
||||
{% endif %}
|
||||
|
||||
|
@@ -37,7 +37,7 @@
|
||||
{%- if entity.user is not null %}
|
||||
<dt class="inline">{{ 'Referrer'|trans|capitalize }}</dt>
|
||||
<dd>
|
||||
<span class="badge-user">{{ entity.user|chill_entity_render_box }}</span>
|
||||
<span class="badge-user">{{ entity.user|chill_entity_render_box({'at_date': entity.date}) }}</span>
|
||||
</dd>
|
||||
{% endif %}
|
||||
|
||||
|
@@ -145,7 +145,7 @@ class ActivityVoter extends AbstractChillVoter implements ProvideRoleHierarchyIn
|
||||
throw new \RuntimeException('Could not determine context of activity.');
|
||||
}
|
||||
} elseif ($subject instanceof AccompanyingPeriod) {
|
||||
if (AccompanyingPeriod::STEP_CLOSED === $subject->getStep()) {
|
||||
if (AccompanyingPeriod::STEP_CLOSED === $subject->getStep() || AccompanyingPeriod::STEP_DRAFT === $subject->getStep()) {
|
||||
if (\in_array($attribute, [self::UPDATE, self::CREATE, self::DELETE], true)) {
|
||||
return false;
|
||||
}
|
||||
|
@@ -60,7 +60,7 @@ final class TranslatableActivityTypeTest extends KernelTestCase
|
||||
$this->assertInstanceOf(
|
||||
ActivityType::class,
|
||||
$form->getData()['type'],
|
||||
'The data is an instance of Chill\\ActivityBundle\\Entity\\ActivityType'
|
||||
'The data is an instance of Chill\ActivityBundle\Entity\ActivityType'
|
||||
);
|
||||
$this->assertEquals($type->getId(), $form->getData()['type']->getId());
|
||||
|
||||
|
@@ -77,6 +77,18 @@ Choose a type: Choisir un type
|
||||
4 hours: 4 heures
|
||||
4 hours 30: 4 heures 30
|
||||
5 hours: 5 heures
|
||||
5 hours 30: 5 heure 30
|
||||
6 hours: 6 heures
|
||||
6 hours 30: 6 heure 30
|
||||
7 hours: 7 heures
|
||||
7 hours 30: 7 heure 30
|
||||
8 hours: 8 heures
|
||||
8 hours 30: 8 heure 30
|
||||
9 hours: 9 heures
|
||||
9 hours 30: 9 heure 30
|
||||
10 hours: 10 heures
|
||||
11 hours: 11 heures
|
||||
12 hours: 12 heures
|
||||
Concerned groups: Parties concernées par l'échange
|
||||
Persons in accompanying course: Usagers du parcours
|
||||
Third persons: Tiers non-pro.
|
||||
@@ -210,6 +222,7 @@ Documents label: Libellé du champ Documents
|
||||
# activity type category admin
|
||||
ActivityTypeCategory list: Liste des catégories des types d'échange
|
||||
Create a new activity type category: Créer une nouvelle catégorie de type d'échange
|
||||
Create a new activity in accompanying course: Créer un échange dans le parcours
|
||||
|
||||
# activity delete
|
||||
Remove activity: Supprimer un échange
|
||||
|
@@ -49,13 +49,13 @@
|
||||
<li>
|
||||
<span>
|
||||
<abbr class="referrer" title={{ 'Created by'|trans }}>{{ 'By'|trans }}:</abbr>
|
||||
<b>{{ entity.createdBy|chill_entity_render_box }}</b>
|
||||
<b>{{ entity.createdBy|chill_entity_render_box({'at_date': entity.date}) }}</b>
|
||||
</span>
|
||||
</li>
|
||||
<li>
|
||||
<span>
|
||||
<abbr class="referrer" title={{ 'Created for'|trans }}>{{ 'For'|trans }}:</abbr>
|
||||
<b>{{ entity.agent|chill_entity_render_box }}</b>
|
||||
<b>{{ entity.agent|chill_entity_render_box({'at_date': entity.date}) }}</b>
|
||||
|
||||
</span>
|
||||
</li>
|
||||
|
@@ -18,11 +18,11 @@
|
||||
<dd>{{ entity.type|chill_entity_render_box }}</dd>
|
||||
|
||||
<dt class="inline">{{ 'Created by'|trans }}</dt>
|
||||
<dd>{{ entity.createdBy }}</dd>
|
||||
<dd>{{ entity.createdBy|chill_entity_render_box({'at_date': entity.date}) }}</dd>
|
||||
|
||||
<dt class="inline">{{ 'Created for'|trans }}</dt>
|
||||
<dd>{{ entity.agent }}</dd>
|
||||
|
||||
<dd>{{ entity.agent|chill_entity_render_box({'at_date': entity.date}) }}</dd>
|
||||
|
||||
<dt class="inline">{{ 'Asideactivity location'|trans }}</dt>
|
||||
{%- if entity.location.name is defined -%}
|
||||
<dd>{{ entity.location.name }}</dd>
|
||||
|
@@ -72,21 +72,21 @@ days: jours
|
||||
1 hour 30: 1 heure 30
|
||||
1 hour 45: 1 heure 45
|
||||
2 hours: 2 heures
|
||||
2 hours 30: 2 heure 30
|
||||
2 hours 30: 2 heures 30
|
||||
3 hours: 3 heures
|
||||
3 hours 30: 3 heure 30
|
||||
3 hours 30: 3 heures 30
|
||||
4 hours: 4 heures
|
||||
4 hours 30: 4 heure 30
|
||||
4 hours 30: 4 heures 30
|
||||
5 hours: 5 heures
|
||||
5 hours 30: 5 heure 30
|
||||
5 hours 30: 5 heures 30
|
||||
6 hours: 6 heures
|
||||
6 hours 30: 6 heure 30
|
||||
6 hours 30: 6 heures 30
|
||||
7 hours: 7 heures
|
||||
7 hours 30: 7 heure 30
|
||||
7 hours 30: 7 heures 30
|
||||
8 hours: 8 heures
|
||||
8 hours 30: 8 heure 30
|
||||
8 hours 30: 8 heures 30
|
||||
9 hours: 9 heures
|
||||
9 hours 30: 9 heure 30
|
||||
9 hours 30: 9 heures 30
|
||||
10 hours: 10 heures
|
||||
1/2 day: 1/2 jour
|
||||
1 day: 1 jour
|
||||
|
@@ -440,6 +440,16 @@ class Calendar implements TrackCreationInterface, TrackUpdateInterface, HasCente
|
||||
return $this->startDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* get the date of the calendar.
|
||||
*
|
||||
* Useful for showing the date of the calendar event, required by twig in some places.
|
||||
*/
|
||||
public function getDate(): ?\DateTimeImmutable
|
||||
{
|
||||
return $this->getStartDate();
|
||||
}
|
||||
|
||||
public function getStatus(): ?string
|
||||
{
|
||||
return $this->status;
|
||||
|
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\CalendarBundle\Menu;
|
||||
|
||||
use Chill\CalendarBundle\Security\Voter\CalendarVoter;
|
||||
use Chill\MainBundle\Routing\LocalMenuBuilderInterface;
|
||||
use Knp\Menu\MenuItem;
|
||||
use Symfony\Component\Security\Core\Security;
|
||||
|
||||
final readonly class AccompanyingCourseQuickMenuBuilder implements LocalMenuBuilderInterface
|
||||
{
|
||||
public function __construct(private Security $security) {}
|
||||
|
||||
public static function getMenuIds(): array
|
||||
{
|
||||
return ['accompanying_course_quick_menu'];
|
||||
}
|
||||
|
||||
public function buildMenu($menuId, MenuItem $menu, array $parameters)
|
||||
{
|
||||
/** @var \Chill\PersonBundle\Entity\AccompanyingPeriod $accompanyingCourse */
|
||||
$accompanyingCourse = $parameters['accompanying-course'];
|
||||
|
||||
if ($this->security->isGranted(CalendarVoter::CREATE, $accompanyingCourse)) {
|
||||
$menu
|
||||
->addChild('Create a new calendar in accompanying course', [
|
||||
'route' => 'chill_calendar_calendar_new',
|
||||
'routeParameters' => [
|
||||
'accompanying_period_id' => $accompanyingCourse->getId(),
|
||||
],
|
||||
])
|
||||
->setExtras([
|
||||
'order' => 20,
|
||||
'icon' => 'plus',
|
||||
])
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
@@ -37,12 +37,12 @@ class RemoteEventConverter
|
||||
* valid when the remote string contains also a timezone, like in
|
||||
* lastModifiedDate.
|
||||
*/
|
||||
final public const REMOTE_DATETIMEZONE_FORMAT = 'Y-m-d\\TH:i:s.u?P';
|
||||
final public const REMOTE_DATETIMEZONE_FORMAT = 'Y-m-d\TH:i:s.u?P';
|
||||
|
||||
/**
|
||||
* Same as above, but sometimes the date is expressed with only 6 milliseconds.
|
||||
*/
|
||||
final public const REMOTE_DATETIMEZONE_FORMAT_ALT = 'Y-m-d\\TH:i:s.uP';
|
||||
final public const REMOTE_DATETIMEZONE_FORMAT_ALT = 'Y-m-d\TH:i:s.uP';
|
||||
|
||||
private const REMOTE_DATE_FORMAT = 'Y-m-d\TH:i:s.u0';
|
||||
|
||||
|
@@ -1 +1,2 @@
|
||||
import './scss/badge.scss';
|
||||
import './scss/calendar-list.scss';
|
||||
|
@@ -0,0 +1,26 @@
|
||||
ul.calendar-list {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
& > li {
|
||||
display: inline-block;
|
||||
}
|
||||
& > li:nth-child(n+2) {
|
||||
margin-left: 0.25rem;
|
||||
}
|
||||
}
|
||||
|
||||
div.calendar-list {
|
||||
|
||||
ul.calendar-list {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
& > a.calendar-list__global {
|
||||
display: inline-block;;
|
||||
padding: 0.2rem;
|
||||
min-width: 2rem;
|
||||
border: 1px solid var(--bs-chill-blue);
|
||||
border-radius: 0.25rem;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
@@ -55,7 +55,7 @@
|
||||
<div class="item-col">
|
||||
<ul class="list-content">
|
||||
{% if calendar.mainUser is not empty %}
|
||||
<span class="badge-user">{{ calendar.mainUser|chill_entity_render_box }}</span>
|
||||
<span class="badge-user">{{ calendar.mainUser|chill_entity_render_box({'at_date': calendar.startDate}) }}</span>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
@@ -132,7 +132,7 @@
|
||||
<li class="cancel">
|
||||
<span class="createdBy">
|
||||
{{ 'Created by'|trans }}
|
||||
<b>{{ calendar.activity.createdBy|chill_entity_render_string }}</b>, {{ 'on'|trans }} {{ calendar.activity.createdAt|format_datetime('short', 'short') }}
|
||||
<b>{{ calendar.activity.createdBy|chill_entity_render_string({'at_date': calendar.activity.createdAt}) }}</b>, {{ 'on'|trans }} {{ calendar.activity.createdAt|format_datetime('short', 'short') }}
|
||||
</span>
|
||||
</li>
|
||||
{% if is_granted('CHILL_ACTIVITY_SEE', calendar.activity) %}
|
||||
|
@@ -89,7 +89,7 @@ class CalendarVoter extends AbstractChillVoter implements ProvideRoleHierarchyIn
|
||||
switch ($attribute) {
|
||||
case self::SEE:
|
||||
case self::CREATE:
|
||||
if (AccompanyingPeriod::STEP_DRAFT === $subject->getStep()) {
|
||||
if (AccompanyingPeriod::STEP_DRAFT === $subject->getStep() || AccompanyingPeriod::STEP_CLOSED === $subject->getStep()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@@ -26,6 +26,7 @@ The calendar item has been successfully removed.: Le rendez-vous a été supprim
|
||||
From the day: Du
|
||||
to the day: au
|
||||
Transform to activity: Transformer en échange
|
||||
Create a new calendar in accompanying course: Créer un rendez-vous dans le parcours
|
||||
Will send SMS: Un SMS de rappel sera envoyé
|
||||
Will not send SMS: Aucun SMS de rappel ne sera envoyé
|
||||
SMS already sent: Un SMS a été envoyé
|
||||
|
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\DocGeneratorBundle\Test;
|
||||
|
||||
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
|
||||
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
|
||||
|
||||
/**
|
||||
* @template T of object
|
||||
*/
|
||||
abstract class DocGenNormalizerTestAbstract extends KernelTestCase
|
||||
{
|
||||
public function testNullValueHasSameKeysAsNull(): void
|
||||
{
|
||||
$normalizedObject = $this->getNormalizer()->normalize($this->provideNotNullObject(), 'docgen', [
|
||||
AbstractNormalizer::GROUPS => ['docgen:read'], 'docgen:expects' => $this->provideDocGenExpectClass(),
|
||||
]);
|
||||
$nullNormalizedObject = $this->getNormalizer()->normalize(null, 'docgen', [
|
||||
AbstractNormalizer::GROUPS => ['docgen:read'], 'docgen:expects' => $this->provideDocGenExpectClass(),
|
||||
]);
|
||||
|
||||
self::assertEqualsCanonicalizing(array_keys($normalizedObject), array_keys($nullNormalizedObject));
|
||||
self::assertArrayHasKey('isNull', $nullNormalizedObject, 'each object must have an "isNull" key');
|
||||
self::assertTrue($nullNormalizedObject['isNull'], 'isNull key must be true for null objects');
|
||||
self::assertFalse($normalizedObject['isNull'], 'isNull key must be false for null objects');
|
||||
|
||||
foreach ($normalizedObject as $key => $value) {
|
||||
if (in_array($key, ['isNull', 'type'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (is_array($value)) {
|
||||
if (array_is_list($value)) {
|
||||
self::assertEquals([], $nullNormalizedObject[$key], "list must be serialized as an empty array, in {$key}");
|
||||
} else {
|
||||
self::assertEqualsCanonicalizing(array_keys($value), array_keys($nullNormalizedObject[$key]), "sub-object must have the same keys, in {$key}");
|
||||
}
|
||||
} elseif (is_string($value)) {
|
||||
self::assertEquals('', $nullNormalizedObject[$key], 'strings must be ');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return T
|
||||
*/
|
||||
abstract public function provideNotNullObject(): object;
|
||||
|
||||
/**
|
||||
* @return class-string<T>
|
||||
*/
|
||||
abstract public function provideDocGenExpectClass(): string;
|
||||
|
||||
abstract public function getNormalizer(): NormalizerInterface;
|
||||
}
|
@@ -313,4 +313,19 @@ class StoredObject implements Document, TrackCreationInterface
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function saveHistory(): void
|
||||
{
|
||||
if ('' === $this->getFilename()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->datas['history'][] = [
|
||||
'filename' => $this->getFilename(),
|
||||
'iv' => $this->getIv(),
|
||||
'key_infos' => $this->getKeyInfos(),
|
||||
'type' => $this->getType(),
|
||||
'before' => (new \DateTimeImmutable('now'))->getTimestamp(),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@@ -57,8 +57,8 @@ class StoredObjectDataMapper implements DataMapperInterface
|
||||
|
||||
/** @var StoredObject $viewData */
|
||||
if ($viewData->getFilename() !== $forms['stored_object']->getData()['filename']) {
|
||||
// we do not want to erase the previous object
|
||||
$viewData = new StoredObject();
|
||||
// we want to keep the previous history
|
||||
$viewData->saveHistory();
|
||||
}
|
||||
|
||||
$viewData->setFilename($forms['stored_object']->getData()['filename']);
|
||||
|
@@ -4,13 +4,13 @@
|
||||
Actions
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<li v-if="props.canEdit && is_extension_editable(props.storedObject.type)">
|
||||
<li v-if="props.canEdit && is_extension_editable(props.storedObject.type) && props.storedObject.status !== 'stored_object_created'">
|
||||
<wopi-edit-button :stored-object="props.storedObject" :classes="{'dropdown-item': true}" :execute-before-leave="props.executeBeforeLeave"></wopi-edit-button>
|
||||
</li>
|
||||
<li v-if="props.canEdit && is_extension_editable(props.storedObject.type) && props.davLink !== undefined && props.davLinkExpiration !== undefined">
|
||||
<desktop-edit-button :classes="{'dropdown-item': true}" :edit-link="props.davLink" :expiration-link="props.davLinkExpiration"></desktop-edit-button>
|
||||
</li>
|
||||
<li v-if="props.storedObject.type != 'application/pdf' && is_extension_viewable(props.storedObject.type) && props.canConvertPdf">
|
||||
<li v-if="props.storedObject.type != 'application/pdf' && is_extension_viewable(props.storedObject.type) && props.canConvertPdf && props.storedObject.status !== 'stored_object_created'">
|
||||
<convert-button :stored-object="props.storedObject" :filename="filename" :classes="{'dropdown-item': true}"></convert-button>
|
||||
</li>
|
||||
<li v-if="props.canDownload">
|
||||
|
@@ -13,7 +13,7 @@ import {reactive} from "vue";
|
||||
import {StoredObject, StoredObjectCreated} from "../../types";
|
||||
|
||||
interface ConvertButtonConfig {
|
||||
storedObject: StoredObject|StoredObjectCreated,
|
||||
storedObject: StoredObject,
|
||||
classes: { [key: string]: boolean},
|
||||
filename?: string,
|
||||
};
|
||||
|
@@ -11,7 +11,7 @@ import {build_wopi_editor_link} from "./helpers";
|
||||
import {StoredObject, StoredObjectCreated, WopiEditButtonExecutableBeforeLeaveFunction} from "../../types";
|
||||
|
||||
interface WopiEditButtonConfig {
|
||||
storedObject: StoredObject|StoredObjectCreated,
|
||||
storedObject: StoredObject,
|
||||
returnPath?: string,
|
||||
classes: {[k: string] : boolean},
|
||||
executeBeforeLeave?: WopiEditButtonExecutableBeforeLeaveFunction,
|
||||
|
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\DocStoreBundle\Tests\Entity;
|
||||
|
||||
use Chill\DocStoreBundle\Entity\StoredObject;
|
||||
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @coversNothing
|
||||
*/
|
||||
class StoredObjectTest extends KernelTestCase
|
||||
{
|
||||
public function testSaveHistory(): void
|
||||
{
|
||||
$storedObject = new StoredObject();
|
||||
$storedObject
|
||||
->setFilename('test_0')
|
||||
->setIv([2, 4, 6, 8])
|
||||
->setKeyInfos(['key' => ['data0' => 'data0']])
|
||||
->setType('text/html');
|
||||
|
||||
$storedObject->saveHistory();
|
||||
|
||||
$storedObject
|
||||
->setFilename('test_1')
|
||||
->setIv([8, 10, 12])
|
||||
->setKeyInfos(['key' => ['data1' => 'data1']])
|
||||
->setType('text/text');
|
||||
|
||||
$storedObject->saveHistory();
|
||||
|
||||
self::assertEquals('test_0', $storedObject->getDatas()['history'][0]['filename']);
|
||||
self::assertEquals([2, 4, 6, 8], $storedObject->getDatas()['history'][0]['iv']);
|
||||
self::assertEquals(['key' => ['data0' => 'data0']], $storedObject->getDatas()['history'][0]['key_infos']);
|
||||
self::assertEquals('text/html', $storedObject->getDatas()['history'][0]['type']);
|
||||
|
||||
self::assertEquals('test_1', $storedObject->getDatas()['history'][1]['filename']);
|
||||
self::assertEquals([8, 10, 12], $storedObject->getDatas()['history'][1]['iv']);
|
||||
self::assertEquals(['key' => ['data1' => 'data1']], $storedObject->getDatas()['history'][1]['key_infos']);
|
||||
self::assertEquals('text/text', $storedObject->getDatas()['history'][1]['type']);
|
||||
}
|
||||
}
|
@@ -56,14 +56,14 @@ class StoredObjectTypeTest extends TypeTestCase
|
||||
{"filename":"abcdef","iv":[10, 15, 20, 30],"keyInfos":[],"type":"text/html","status":"object_store_created"}
|
||||
JSON];
|
||||
$model = new StoredObject();
|
||||
$originalObjectId = spl_object_id($model);
|
||||
$originalObjectId = spl_object_hash($model);
|
||||
$form = $this->factory->create(StoredObjectType::class, $model, ['has_title' => true]);
|
||||
|
||||
$form->submit($formData);
|
||||
|
||||
$this->assertTrue($form->isSynchronized());
|
||||
$model = $form->getData();
|
||||
$this->assertNotEquals($originalObjectId, spl_object_hash($model));
|
||||
$this->assertEquals($originalObjectId, spl_object_hash($model));
|
||||
$this->assertEquals('abcdef', $model->getFilename());
|
||||
$this->assertEquals([10, 15, 20, 30], $model->getIv());
|
||||
$this->assertEquals('text/html', $model->getType());
|
||||
|
@@ -15,7 +15,7 @@ use Chill\EventBundle\Entity\Event;
|
||||
use Chill\EventBundle\Entity\Participation;
|
||||
use Chill\EventBundle\Form\EventType;
|
||||
use Chill\EventBundle\Form\Type\PickEventType;
|
||||
use Chill\EventBundle\Security\Authorization\EventVoter;
|
||||
use Chill\EventBundle\Security\EventVoter;
|
||||
use Chill\MainBundle\Entity\Center;
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\MainBundle\Pagination\PaginatorFactory;
|
||||
@@ -418,7 +418,6 @@ final class EventController extends AbstractController
|
||||
$builder->add('event_id', HiddenType::class, [
|
||||
'data' => $event->getId(),
|
||||
]);
|
||||
dump($event->getId());
|
||||
|
||||
return $builder->getForm();
|
||||
}
|
||||
|
@@ -15,7 +15,7 @@ use Chill\EventBundle\Entity\Event;
|
||||
use Chill\EventBundle\Entity\Participation;
|
||||
use Chill\EventBundle\Form\ParticipationType;
|
||||
use Chill\EventBundle\Repository\EventRepository;
|
||||
use Chill\EventBundle\Security\Authorization\ParticipationVoter;
|
||||
use Chill\EventBundle\Security\ParticipationVoter;
|
||||
use Chill\PersonBundle\Repository\PersonRepository;
|
||||
use Chill\PersonBundle\Security\Authorization\PersonVoter;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
|
@@ -11,8 +11,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace Chill\EventBundle\DependencyInjection;
|
||||
|
||||
use Chill\EventBundle\Security\Authorization\EventVoter;
|
||||
use Chill\EventBundle\Security\Authorization\ParticipationVoter;
|
||||
use Chill\EventBundle\Security\EventVoter;
|
||||
use Chill\EventBundle\Security\ParticipationVoter;
|
||||
use Symfony\Component\Config\FileLocator;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
|
||||
@@ -33,12 +33,13 @@ class ChillEventExtension extends Extension implements PrependExtensionInterface
|
||||
|
||||
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../config'));
|
||||
$loader->load('services.yaml');
|
||||
$loader->load('services/authorization.yaml');
|
||||
$loader->load('services/security.yaml');
|
||||
$loader->load('services/fixtures.yaml');
|
||||
$loader->load('services/forms.yaml');
|
||||
$loader->load('services/repositories.yaml');
|
||||
$loader->load('services/search.yaml');
|
||||
$loader->load('services/timeline.yaml');
|
||||
$loader->load('services/export.yaml');
|
||||
}
|
||||
|
||||
/** (non-PHPdoc).
|
||||
|
@@ -0,0 +1,110 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\EventBundle\Export\Aggregator;
|
||||
|
||||
use Chill\EventBundle\Export\Declarations;
|
||||
use Chill\MainBundle\Export\AggregatorInterface;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
class EventDateAggregator implements AggregatorInterface
|
||||
{
|
||||
private const CHOICES = [
|
||||
'by month' => 'month',
|
||||
'by week' => 'week',
|
||||
'by year' => 'year',
|
||||
];
|
||||
|
||||
private const DEFAULT_CHOICE = 'year';
|
||||
|
||||
public function addRole(): ?string
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function alterQuery(QueryBuilder $qb, $data)
|
||||
{
|
||||
$order = null;
|
||||
|
||||
switch ($data['frequency']) {
|
||||
case 'month':
|
||||
$fmt = 'YYYY-MM';
|
||||
|
||||
break;
|
||||
|
||||
case 'week':
|
||||
$fmt = 'YYYY-IW';
|
||||
|
||||
break;
|
||||
|
||||
case 'year':
|
||||
$fmt = 'YYYY';
|
||||
$order = 'DESC';
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new \RuntimeException(sprintf("The frequency data '%s' is invalid.", $data['frequency']));
|
||||
}
|
||||
|
||||
$qb->addSelect(sprintf("TO_CHAR(event.date, '%s') AS date_aggregator", $fmt));
|
||||
$qb->addGroupBy('date_aggregator');
|
||||
$qb->addOrderBy('date_aggregator', $order);
|
||||
}
|
||||
|
||||
public function applyOn(): string
|
||||
{
|
||||
return Declarations::EVENT;
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder)
|
||||
{
|
||||
$builder->add('frequency', ChoiceType::class, [
|
||||
'choices' => self::CHOICES,
|
||||
'multiple' => false,
|
||||
'expanded' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
public function getFormDefaultData(): array
|
||||
{
|
||||
return ['frequency' => self::DEFAULT_CHOICE];
|
||||
}
|
||||
|
||||
public function getLabels($key, array $values, $data)
|
||||
{
|
||||
return static function ($value) use ($data): string {
|
||||
if ('_header' === $value) {
|
||||
return 'by '.$data['frequency'];
|
||||
}
|
||||
|
||||
if (null === $value) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return match ($data['frequency']) {
|
||||
default => $value,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
public function getQueryKeys($data): array
|
||||
{
|
||||
return ['date_aggregator'];
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return 'Group event by date';
|
||||
}
|
||||
}
|
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\EventBundle\Export\Aggregator;
|
||||
|
||||
use Chill\EventBundle\Export\Declarations;
|
||||
use Chill\EventBundle\Repository\EventTypeRepository;
|
||||
use Chill\MainBundle\Export\AggregatorInterface;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
class EventTypeAggregator implements AggregatorInterface
|
||||
{
|
||||
final public const KEY = 'event_type_aggregator';
|
||||
|
||||
public function __construct(protected EventTypeRepository $eventTypeRepository, protected TranslatableStringHelperInterface $translatableStringHelper) {}
|
||||
|
||||
public function addRole(): ?string
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function alterQuery(QueryBuilder $qb, $data)
|
||||
{
|
||||
if (!\in_array('eventtype', $qb->getAllAliases(), true)) {
|
||||
$qb->leftJoin('event.type', 'eventtype');
|
||||
}
|
||||
|
||||
$qb->addSelect(sprintf('IDENTITY(event.type) AS %s', self::KEY));
|
||||
$qb->addGroupBy(self::KEY);
|
||||
}
|
||||
|
||||
public function applyOn(): string
|
||||
{
|
||||
return Declarations::EVENT;
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder)
|
||||
{
|
||||
// no form required for this aggregator
|
||||
}
|
||||
|
||||
public function getFormDefaultData(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getLabels($key, array $values, $data): \Closure
|
||||
{
|
||||
return function (int|string|null $value): string {
|
||||
if ('_header' === $value) {
|
||||
return 'Event type';
|
||||
}
|
||||
|
||||
if (null === $value || '' === $value || null === $t = $this->eventTypeRepository->find($value)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $this->translatableStringHelper->localize($t->getName());
|
||||
};
|
||||
}
|
||||
|
||||
public function getQueryKeys($data): array
|
||||
{
|
||||
return [self::KEY];
|
||||
}
|
||||
|
||||
public function getTitle()
|
||||
{
|
||||
return 'Group by event type';
|
||||
}
|
||||
}
|
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\EventBundle\Export\Aggregator;
|
||||
|
||||
use Chill\EventBundle\Export\Declarations;
|
||||
use Chill\EventBundle\Repository\RoleRepository;
|
||||
use Chill\MainBundle\Export\AggregatorInterface;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
class RoleAggregator implements AggregatorInterface
|
||||
{
|
||||
final public const KEY = 'part_role_aggregator';
|
||||
|
||||
public function __construct(protected RoleRepository $roleRepository, protected TranslatableStringHelperInterface $translatableStringHelper) {}
|
||||
|
||||
public function addRole(): ?string
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function alterQuery(QueryBuilder $qb, $data)
|
||||
{
|
||||
if (!\in_array('event_part', $qb->getAllAliases(), true)) {
|
||||
$qb->leftJoin('event_part.role', 'role');
|
||||
}
|
||||
|
||||
$qb->addSelect(sprintf('IDENTITY(event_part.role) AS %s', self::KEY));
|
||||
$qb->addGroupBy(self::KEY);
|
||||
}
|
||||
|
||||
public function applyOn(): string
|
||||
{
|
||||
return Declarations::EVENT_PARTICIPANTS;
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder)
|
||||
{
|
||||
// no form required for this aggregator
|
||||
}
|
||||
|
||||
public function getFormDefaultData(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getLabels($key, array $values, $data): \Closure
|
||||
{
|
||||
return function (int|string|null $value): string {
|
||||
if ('_header' === $value) {
|
||||
return 'Participant role';
|
||||
}
|
||||
|
||||
if (null === $value || '' === $value || null === $r = $this->roleRepository->find($value)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $this->translatableStringHelper->localize($r->getName());
|
||||
};
|
||||
}
|
||||
|
||||
public function getQueryKeys($data): array
|
||||
{
|
||||
return [self::KEY];
|
||||
}
|
||||
|
||||
public function getTitle()
|
||||
{
|
||||
return 'Group by participant role';
|
||||
}
|
||||
}
|
22
src/Bundle/ChillEventBundle/Export/Declarations.php
Normal file
22
src/Bundle/ChillEventBundle/Export/Declarations.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\EventBundle\Export;
|
||||
|
||||
/**
|
||||
* This class declare constants used for the export framework.
|
||||
*/
|
||||
abstract class Declarations
|
||||
{
|
||||
final public const EVENT = 'event';
|
||||
|
||||
final public const EVENT_PARTICIPANTS = 'event_participants';
|
||||
}
|
@@ -0,0 +1,125 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\EventBundle\Export\Export;
|
||||
|
||||
use Chill\EventBundle\Export\Declarations;
|
||||
use Chill\EventBundle\Repository\ParticipationRepository;
|
||||
use Chill\EventBundle\Security\ParticipationVoter;
|
||||
use Chill\MainBundle\Export\ExportInterface;
|
||||
use Chill\MainBundle\Export\FormatterInterface;
|
||||
use Chill\MainBundle\Export\GroupedExportInterface;
|
||||
use Chill\PersonBundle\Entity\Person\PersonCenterHistory;
|
||||
use Doctrine\ORM\Query;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Chill\PersonBundle\Export\Declarations as PersonDeclarations;
|
||||
|
||||
readonly class CountEventParticipations implements ExportInterface, GroupedExportInterface
|
||||
{
|
||||
private bool $filterStatsByCenters;
|
||||
|
||||
public function __construct(
|
||||
private ParticipationRepository $participationRepository,
|
||||
ParameterBagInterface $parameterBag,
|
||||
) {
|
||||
$this->filterStatsByCenters = $parameterBag->get('chill_main')['acl']['filter_stats_by_center'];
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder) {}
|
||||
|
||||
public function getFormDefaultData(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getAllowedFormattersTypes()
|
||||
{
|
||||
return [FormatterInterface::TYPE_TABULAR];
|
||||
}
|
||||
|
||||
public function getDescription()
|
||||
{
|
||||
return 'Count participants to an event by various parameters.';
|
||||
}
|
||||
|
||||
public function getGroup(): string
|
||||
{
|
||||
return 'Exports of events';
|
||||
}
|
||||
|
||||
public function getLabels($key, array $values, $data)
|
||||
{
|
||||
if ('export_count_event_participants' !== $key) {
|
||||
throw new \LogicException("the key {$key} is not used by this export");
|
||||
}
|
||||
|
||||
return static fn ($value) => '_header' === $value ? 'Count event participants' : $value;
|
||||
}
|
||||
|
||||
public function getQueryKeys($data)
|
||||
{
|
||||
return ['export_count_event_participants'];
|
||||
}
|
||||
|
||||
public function getResult($query, $data)
|
||||
{
|
||||
return $query->getQuery()->getResult(Query::HYDRATE_SCALAR);
|
||||
}
|
||||
|
||||
public function getTitle()
|
||||
{
|
||||
return 'Count event participants';
|
||||
}
|
||||
|
||||
public function getType(): string
|
||||
{
|
||||
return Declarations::EVENT_PARTICIPANTS;
|
||||
}
|
||||
|
||||
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
|
||||
{
|
||||
$centers = array_map(static fn ($el) => $el['center'], $acl);
|
||||
|
||||
$qb = $this->participationRepository
|
||||
->createQueryBuilder('event_part')
|
||||
->join('event_part.person', 'person');
|
||||
|
||||
$qb->select('COUNT(event_part.id) as export_count_event_participants');
|
||||
|
||||
if ($this->filterStatsByCenters) {
|
||||
$qb
|
||||
->andWhere(
|
||||
$qb->expr()->exists(
|
||||
'SELECT 1 FROM '.PersonCenterHistory::class.' acl_count_person_history WHERE acl_count_person_history.person = person
|
||||
AND acl_count_person_history.center IN (:authorized_centers)
|
||||
'
|
||||
)
|
||||
)
|
||||
->setParameter('authorized_centers', $centers);
|
||||
}
|
||||
|
||||
return $qb;
|
||||
}
|
||||
|
||||
public function requiredRole(): string
|
||||
{
|
||||
return ParticipationVoter::STATS;
|
||||
}
|
||||
|
||||
public function supportsModifiers()
|
||||
{
|
||||
return [
|
||||
Declarations::EVENT_PARTICIPANTS,
|
||||
PersonDeclarations::PERSON_TYPE,
|
||||
];
|
||||
}
|
||||
}
|
126
src/Bundle/ChillEventBundle/Export/Export/CountEvents.php
Normal file
126
src/Bundle/ChillEventBundle/Export/Export/CountEvents.php
Normal file
@@ -0,0 +1,126 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\EventBundle\Export\Export;
|
||||
|
||||
use Chill\EventBundle\Repository\EventRepository;
|
||||
use Chill\EventBundle\Security\EventVoter;
|
||||
use Chill\MainBundle\Export\ExportInterface;
|
||||
use Chill\MainBundle\Export\FormatterInterface;
|
||||
use Chill\MainBundle\Export\GroupedExportInterface;
|
||||
use Chill\PersonBundle\Entity\Person\PersonCenterHistory;
|
||||
use Doctrine\ORM\Query;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Chill\EventBundle\Export\Declarations;
|
||||
use Chill\PersonBundle\Export\Declarations as PersonDeclarations;
|
||||
|
||||
readonly class CountEvents implements ExportInterface, GroupedExportInterface
|
||||
{
|
||||
private bool $filterStatsByCenters;
|
||||
|
||||
public function __construct(
|
||||
private EventRepository $eventRepository,
|
||||
ParameterBagInterface $parameterBag,
|
||||
) {
|
||||
$this->filterStatsByCenters = $parameterBag->get('chill_main')['acl']['filter_stats_by_center'];
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder) {}
|
||||
|
||||
public function getFormDefaultData(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getAllowedFormattersTypes()
|
||||
{
|
||||
return [FormatterInterface::TYPE_TABULAR];
|
||||
}
|
||||
|
||||
public function getDescription()
|
||||
{
|
||||
return 'Count events by various parameters.';
|
||||
}
|
||||
|
||||
public function getGroup(): string
|
||||
{
|
||||
return 'Exports of events';
|
||||
}
|
||||
|
||||
public function getLabels($key, array $values, $data)
|
||||
{
|
||||
if ('export_count_event' !== $key) {
|
||||
throw new \LogicException("the key {$key} is not used by this export");
|
||||
}
|
||||
|
||||
return static fn ($value) => '_header' === $value ? 'Number of events' : $value;
|
||||
}
|
||||
|
||||
public function getQueryKeys($data)
|
||||
{
|
||||
return ['export_count_event'];
|
||||
}
|
||||
|
||||
public function getResult($query, $data)
|
||||
{
|
||||
return $query->getQuery()->getResult(Query::HYDRATE_SCALAR);
|
||||
}
|
||||
|
||||
public function getTitle()
|
||||
{
|
||||
return 'Count events';
|
||||
}
|
||||
|
||||
public function getType(): string
|
||||
{
|
||||
return Declarations::EVENT;
|
||||
}
|
||||
|
||||
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
|
||||
{
|
||||
$centers = array_map(static fn ($el) => $el['center'], $acl);
|
||||
|
||||
$qb = $this->eventRepository
|
||||
->createQueryBuilder('event')
|
||||
->leftJoin('event.participations', 'epart')
|
||||
->leftJoin('epart.person', 'person');
|
||||
|
||||
$qb->select('COUNT(event.id) as export_count_event');
|
||||
|
||||
if ($this->filterStatsByCenters) {
|
||||
$qb
|
||||
->andWhere(
|
||||
$qb->expr()->exists(
|
||||
'SELECT 1 FROM '.PersonCenterHistory::class.' acl_count_person_history WHERE acl_count_person_history.person = person
|
||||
AND acl_count_person_history.center IN (:authorized_centers)
|
||||
'
|
||||
)
|
||||
)
|
||||
->setParameter('authorized_centers', $centers);
|
||||
}
|
||||
|
||||
return $qb;
|
||||
}
|
||||
|
||||
public function requiredRole(): string
|
||||
{
|
||||
return EventVoter::STATS;
|
||||
}
|
||||
|
||||
public function supportsModifiers()
|
||||
{
|
||||
return [
|
||||
Declarations::EVENT,
|
||||
PersonDeclarations::PERSON_TYPE,
|
||||
];
|
||||
}
|
||||
}
|
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\EventBundle\Export\Filter;
|
||||
|
||||
use Chill\EventBundle\Export\Declarations;
|
||||
use Chill\MainBundle\Export\FilterInterface;
|
||||
use Chill\MainBundle\Form\Type\PickRollingDateType;
|
||||
use Chill\MainBundle\Service\RollingDate\RollingDate;
|
||||
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
|
||||
use Doctrine\ORM\Query\Expr;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
class EventDateFilter implements FilterInterface
|
||||
{
|
||||
public function __construct(protected TranslatorInterface $translator, private readonly RollingDateConverterInterface $rollingDateConverter) {}
|
||||
|
||||
public function addRole(): ?string
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function alterQuery(QueryBuilder $qb, $data)
|
||||
{
|
||||
$where = $qb->getDQLPart('where');
|
||||
$clause = $qb->expr()->between(
|
||||
'event.date',
|
||||
':date_from',
|
||||
':date_to'
|
||||
);
|
||||
|
||||
if ($where instanceof Expr\Andx) {
|
||||
$where->add($clause);
|
||||
} else {
|
||||
$where = $qb->expr()->andX($clause);
|
||||
}
|
||||
|
||||
$qb->add('where', $where);
|
||||
$qb->setParameter(
|
||||
'date_from',
|
||||
$this->rollingDateConverter->convert($data['date_from'])
|
||||
);
|
||||
$qb->setParameter(
|
||||
'date_to',
|
||||
$this->rollingDateConverter->convert($data['date_to'])
|
||||
);
|
||||
}
|
||||
|
||||
public function applyOn(): string
|
||||
{
|
||||
return Declarations::EVENT;
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder)
|
||||
{
|
||||
$builder
|
||||
->add('date_from', PickRollingDateType::class, [
|
||||
'label' => 'Events after this date',
|
||||
])
|
||||
->add('date_to', PickRollingDateType::class, [
|
||||
'label' => 'Events before this date',
|
||||
]);
|
||||
}
|
||||
|
||||
public function getFormDefaultData(): array
|
||||
{
|
||||
return ['date_from' => new RollingDate(RollingDate::T_YEAR_PREVIOUS_START), 'date_to' => new RollingDate(RollingDate::T_TODAY)];
|
||||
}
|
||||
|
||||
public function describeAction($data, $format = 'string')
|
||||
{
|
||||
return [
|
||||
'Filtered by date of event: only between %date_from% and %date_to%',
|
||||
[
|
||||
'%date_from%' => $this->rollingDateConverter->convert($data['date_from'])->format('d-m-Y'),
|
||||
'%date_to%' => $this->rollingDateConverter->convert($data['date_to'])->format('d-m-Y'),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function getTitle()
|
||||
{
|
||||
return 'Filtered by event date';
|
||||
}
|
||||
}
|
@@ -0,0 +1,94 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\EventBundle\Export\Filter;
|
||||
|
||||
use Chill\EventBundle\Entity\EventType;
|
||||
use Chill\EventBundle\Export\Declarations;
|
||||
use Chill\EventBundle\Repository\EventTypeRepository;
|
||||
use Chill\MainBundle\Export\ExportElementValidatedInterface;
|
||||
use Chill\MainBundle\Export\FilterInterface;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
||||
|
||||
class EventTypeFilter implements ExportElementValidatedInterface, FilterInterface
|
||||
{
|
||||
public function __construct(
|
||||
protected TranslatableStringHelperInterface $translatableStringHelper,
|
||||
protected EventTypeRepository $eventTypeRepository
|
||||
) {}
|
||||
|
||||
public function addRole(): ?string
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function alterQuery(QueryBuilder $qb, $data)
|
||||
{
|
||||
$clause = $qb->expr()->in('event.type', ':selected_event_types');
|
||||
|
||||
$qb->andWhere($clause);
|
||||
$qb->setParameter('selected_event_types', $data['types']);
|
||||
}
|
||||
|
||||
public function applyOn(): string
|
||||
{
|
||||
return Declarations::EVENT;
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder)
|
||||
{
|
||||
$builder->add('types', EntityType::class, [
|
||||
'choices' => $this->eventTypeRepository->findAllActive(),
|
||||
'class' => EventType::class,
|
||||
'choice_label' => fn (EventType $ety) => $this->translatableStringHelper->localize($ety->getName()),
|
||||
'multiple' => true,
|
||||
'expanded' => false,
|
||||
'attr' => [
|
||||
'class' => 'select2',
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
public function getFormDefaultData(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function describeAction($data, $format = 'string')
|
||||
{
|
||||
$typeNames = array_map(
|
||||
fn (EventType $t): string => $this->translatableStringHelper->localize($t->getName()),
|
||||
$this->eventTypeRepository->findBy(['id' => $data['types'] instanceof \Doctrine\Common\Collections\Collection ? $data['types']->toArray() : $data['types']])
|
||||
);
|
||||
|
||||
return ['Filtered by event type: only %list%', [
|
||||
'%list%' => implode(', ', $typeNames),
|
||||
]];
|
||||
}
|
||||
|
||||
public function getTitle()
|
||||
{
|
||||
return 'Filtered by event type';
|
||||
}
|
||||
|
||||
public function validateForm($data, ExecutionContextInterface $context)
|
||||
{
|
||||
if (null === $data['types'] || 0 === \count($data['types'])) {
|
||||
$context
|
||||
->buildViolation('At least one type must be chosen')
|
||||
->addViolation();
|
||||
}
|
||||
}
|
||||
}
|
94
src/Bundle/ChillEventBundle/Export/Filter/RoleFilter.php
Normal file
94
src/Bundle/ChillEventBundle/Export/Filter/RoleFilter.php
Normal file
@@ -0,0 +1,94 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\EventBundle\Export\Filter;
|
||||
|
||||
use Chill\EventBundle\Entity\Role;
|
||||
use Chill\EventBundle\Export\Declarations;
|
||||
use Chill\EventBundle\Repository\RoleRepository;
|
||||
use Chill\MainBundle\Export\ExportElementValidatedInterface;
|
||||
use Chill\MainBundle\Export\FilterInterface;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
||||
|
||||
class RoleFilter implements ExportElementValidatedInterface, FilterInterface
|
||||
{
|
||||
public function __construct(
|
||||
protected TranslatableStringHelperInterface $translatableStringHelper,
|
||||
protected RoleRepository $roleRepository
|
||||
) {}
|
||||
|
||||
public function addRole(): ?string
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function alterQuery(QueryBuilder $qb, $data)
|
||||
{
|
||||
$clause = $qb->expr()->in('event_part.role', ':selected_part_roles');
|
||||
|
||||
$qb->andWhere($clause);
|
||||
$qb->setParameter('selected_part_roles', $data['part_roles']);
|
||||
}
|
||||
|
||||
public function applyOn(): string
|
||||
{
|
||||
return Declarations::EVENT_PARTICIPANTS;
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder)
|
||||
{
|
||||
$builder->add('part_roles', EntityType::class, [
|
||||
'choices' => $this->roleRepository->findAllActive(),
|
||||
'class' => Role::class,
|
||||
'choice_label' => fn (Role $r) => $this->translatableStringHelper->localize($r->getName()),
|
||||
'multiple' => true,
|
||||
'expanded' => false,
|
||||
'attr' => [
|
||||
'class' => 'select2',
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
public function getFormDefaultData(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function describeAction($data, $format = 'string')
|
||||
{
|
||||
$roleNames = array_map(
|
||||
fn (Role $r): string => $this->translatableStringHelper->localize($r->getName()),
|
||||
$this->roleRepository->findBy(['id' => $data['part_roles'] instanceof \Doctrine\Common\Collections\Collection ? $data['part_roles']->toArray() : $data['part_roles']])
|
||||
);
|
||||
|
||||
return ['Filtered by participant roles: only %list%', [
|
||||
'%list%' => implode(', ', $roleNames),
|
||||
]];
|
||||
}
|
||||
|
||||
public function getTitle()
|
||||
{
|
||||
return 'Filter by participant roles';
|
||||
}
|
||||
|
||||
public function validateForm($data, ExecutionContextInterface $context)
|
||||
{
|
||||
if (null === $data['part_roles'] || 0 === \count($data['part_roles'])) {
|
||||
$context
|
||||
->buildViolation('At least one role must be chosen')
|
||||
->addViolation();
|
||||
}
|
||||
}
|
||||
}
|
@@ -11,7 +11,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace Chill\EventBundle\Menu;
|
||||
|
||||
use Chill\EventBundle\Security\Authorization\EventVoter;
|
||||
use Chill\EventBundle\Security\EventVoter;
|
||||
use Chill\MainBundle\Routing\LocalMenuBuilderInterface;
|
||||
use Knp\Menu\MenuItem;
|
||||
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
|
||||
|
@@ -11,7 +11,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace Chill\EventBundle\Menu;
|
||||
|
||||
use Chill\EventBundle\Security\Authorization\EventVoter;
|
||||
use Chill\EventBundle\Security\EventVoter;
|
||||
use Chill\MainBundle\Routing\LocalMenuBuilderInterface;
|
||||
use Knp\Menu\MenuItem;
|
||||
use Symfony\Component\Security\Core\Security;
|
||||
|
@@ -13,7 +13,7 @@ namespace Chill\EventBundle\Repository;
|
||||
|
||||
use Chill\EventBundle\Entity\Event;
|
||||
use Chill\EventBundle\Entity\Participation;
|
||||
use Chill\EventBundle\Security\Authorization\EventVoter;
|
||||
use Chill\EventBundle\Security\EventVoter;
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\MainBundle\Security\Authorization\AuthorizationHelperForCurrentUserInterface;
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
|
@@ -12,13 +12,57 @@ declare(strict_types=1);
|
||||
namespace Chill\EventBundle\Repository;
|
||||
|
||||
use Chill\EventBundle\Entity\Role;
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelper;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Doctrine\Persistence\ObjectRepository;
|
||||
|
||||
class RoleRepository extends ServiceEntityRepository
|
||||
readonly class RoleRepository implements ObjectRepository
|
||||
{
|
||||
public function __construct(ManagerRegistry $registry)
|
||||
private EntityRepository $repository;
|
||||
|
||||
public function __construct(EntityManagerInterface $entityManager, private TranslatableStringHelper $translatableStringHelper)
|
||||
{
|
||||
parent::__construct($registry, Role::class);
|
||||
$this->repository = $entityManager->getRepository(Role::class);
|
||||
}
|
||||
|
||||
public function createQueryBuilder(string $alias, ?string $indexBy = null): QueryBuilder
|
||||
{
|
||||
return $this->repository->createQueryBuilder($alias, $indexBy);
|
||||
}
|
||||
|
||||
public function find($id)
|
||||
{
|
||||
return $this->repository->find($id);
|
||||
}
|
||||
|
||||
public function findAll(): array
|
||||
{
|
||||
return $this->repository->findAll();
|
||||
}
|
||||
|
||||
public function findAllActive(): array
|
||||
{
|
||||
$roles = $this->repository->findBy(['active' => true]);
|
||||
|
||||
usort($roles, fn (Role $a, Role $b) => $this->translatableStringHelper->localize($a->getName()) <=> $this->translatableStringHelper->localize($b->getName()));
|
||||
|
||||
return $roles;
|
||||
}
|
||||
|
||||
public function findBy(array $criteria, ?array $orderBy = null, ?int $limit = null, ?int $offset = null): array
|
||||
{
|
||||
return $this->repository->findBy($criteria, $orderBy, $limit, $offset);
|
||||
}
|
||||
|
||||
public function findOneBy(array $criteria)
|
||||
{
|
||||
return $this->repository->findOneBy($criteria);
|
||||
}
|
||||
|
||||
public function getClassName(): string
|
||||
{
|
||||
return Role::class;
|
||||
}
|
||||
}
|
||||
|
@@ -9,18 +9,19 @@ declare(strict_types=1);
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\EventBundle\Security\Authorization;
|
||||
namespace Chill\EventBundle\Security;
|
||||
|
||||
use Chill\EventBundle\Entity\Event;
|
||||
use Chill\MainBundle\Entity\Center;
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\MainBundle\Security\Authorization\AbstractChillVoter;
|
||||
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
|
||||
use Chill\MainBundle\Security\Authorization\VoterHelperFactoryInterface;
|
||||
use Chill\MainBundle\Security\Authorization\VoterHelperInterface;
|
||||
use Chill\MainBundle\Security\ProvideRoleHierarchyInterface;
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use Chill\PersonBundle\Security\Authorization\PersonVoter;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||
use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface;
|
||||
|
||||
/**
|
||||
* Description of EventVoter.
|
||||
@@ -42,61 +43,46 @@ class EventVoter extends AbstractChillVoter implements ProvideRoleHierarchyInter
|
||||
|
||||
final public const UPDATE = 'CHILL_EVENT_UPDATE';
|
||||
|
||||
/**
|
||||
* @var AccessDecisionManagerInterface
|
||||
*/
|
||||
protected $accessDecisionManager;
|
||||
final public const STATS = 'CHILL_EVENT_STATS';
|
||||
|
||||
/**
|
||||
* @var AuthorizationHelper
|
||||
*/
|
||||
protected $authorizationHelper;
|
||||
|
||||
/**
|
||||
* @var LoggerInterface
|
||||
*/
|
||||
protected $logger;
|
||||
private readonly VoterHelperInterface $voterHelper;
|
||||
|
||||
public function __construct(
|
||||
AccessDecisionManagerInterface $accessDecisionManager,
|
||||
AuthorizationHelper $authorizationHelper,
|
||||
LoggerInterface $logger
|
||||
private readonly AuthorizationHelper $authorizationHelper,
|
||||
private readonly LoggerInterface $logger,
|
||||
VoterHelperFactoryInterface $voterHelperFactory
|
||||
) {
|
||||
$this->accessDecisionManager = $accessDecisionManager;
|
||||
$this->authorizationHelper = $authorizationHelper;
|
||||
$this->logger = $logger;
|
||||
$this->voterHelper = $voterHelperFactory
|
||||
->generate(self::class)
|
||||
->addCheckFor(null, [self::SEE])
|
||||
->addCheckFor(Event::class, [...self::ROLES])
|
||||
->addCheckFor(Person::class, [self::SEE, self::CREATE])
|
||||
->addCheckFor(Center::class, [self::STATS])
|
||||
->build();
|
||||
}
|
||||
|
||||
public function getRoles(): array
|
||||
{
|
||||
return self::ROLES;
|
||||
return [...self::ROLES, self::STATS];
|
||||
}
|
||||
|
||||
public function getRolesWithHierarchy(): array
|
||||
{
|
||||
return [
|
||||
'Event' => self::ROLES,
|
||||
'Event' => $this->getRoles(),
|
||||
];
|
||||
}
|
||||
|
||||
public function getRolesWithoutScope(): array
|
||||
{
|
||||
return [];
|
||||
return [self::ROLES, self::STATS];
|
||||
}
|
||||
|
||||
public function supports($attribute, $subject)
|
||||
{
|
||||
return ($subject instanceof Event && \in_array($attribute, self::ROLES, true))
|
||||
|| ($subject instanceof Person && \in_array($attribute, [self::CREATE, self::SEE], true))
|
||||
|| (null === $subject && self::SEE === $attribute);
|
||||
return $this->voterHelper->supports($attribute, $subject);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $attribute
|
||||
* @param Event $subject
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function voteOnAttribute($attribute, $subject, TokenInterface $token)
|
||||
{
|
||||
$this->logger->debug(sprintf('Voting from %s class', self::class));
|
||||
@@ -118,15 +104,5 @@ class EventVoter extends AbstractChillVoter implements ProvideRoleHierarchyInter
|
||||
->getReachableCenters($token->getUser(), $attribute);
|
||||
|
||||
return \count($centers) > 0;
|
||||
|
||||
if (!$this->accessDecisionManager->decide($token, [PersonVoter::SEE], $person)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->authorizationHelper->userHasAccess(
|
||||
$token->getUser(),
|
||||
$subject,
|
||||
$attribute
|
||||
);
|
||||
}
|
||||
}
|
@@ -9,18 +9,19 @@ declare(strict_types=1);
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\EventBundle\Security\Authorization;
|
||||
namespace Chill\EventBundle\Security;
|
||||
|
||||
use Chill\EventBundle\Entity\Participation;
|
||||
use Chill\MainBundle\Entity\Center;
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\MainBundle\Security\Authorization\AbstractChillVoter;
|
||||
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
|
||||
use Chill\MainBundle\Security\Authorization\VoterHelperFactoryInterface;
|
||||
use Chill\MainBundle\Security\Authorization\VoterHelperInterface;
|
||||
use Chill\MainBundle\Security\ProvideRoleHierarchyInterface;
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use Chill\PersonBundle\Security\Authorization\PersonVoter;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||
use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface;
|
||||
|
||||
class ParticipationVoter extends AbstractChillVoter implements ProvideRoleHierarchyInterface
|
||||
{
|
||||
@@ -39,58 +40,48 @@ class ParticipationVoter extends AbstractChillVoter implements ProvideRoleHierar
|
||||
|
||||
final public const UPDATE = 'CHILL_EVENT_PARTICIPATION_UPDATE';
|
||||
|
||||
/**
|
||||
* @var AccessDecisionManagerInterface
|
||||
*/
|
||||
protected $accessDecisionManager;
|
||||
final public const STATS = 'CHILL_EVENT_PARTICIPATION_STATS';
|
||||
|
||||
/**
|
||||
* @var AuthorizationHelper
|
||||
*/
|
||||
protected $authorizationHelper;
|
||||
|
||||
/**
|
||||
* @var LoggerInterface
|
||||
*/
|
||||
protected $logger;
|
||||
private readonly VoterHelperInterface $voterHelper;
|
||||
|
||||
public function __construct(
|
||||
AccessDecisionManagerInterface $accessDecisionManager,
|
||||
AuthorizationHelper $authorizationHelper,
|
||||
LoggerInterface $logger
|
||||
private readonly AuthorizationHelper $authorizationHelper,
|
||||
private readonly LoggerInterface $logger,
|
||||
VoterHelperFactoryInterface $voterHelperFactory
|
||||
) {
|
||||
$this->accessDecisionManager = $accessDecisionManager;
|
||||
$this->authorizationHelper = $authorizationHelper;
|
||||
$this->logger = $logger;
|
||||
$this->voterHelper = $voterHelperFactory
|
||||
->generate(self::class)
|
||||
->addCheckFor(null, [self::SEE])
|
||||
->addCheckFor(Participation::class, [...self::ROLES])
|
||||
->addCheckFor(Person::class, [self::SEE, self::CREATE])
|
||||
->addCheckFor(Center::class, [self::STATS])
|
||||
->build();
|
||||
}
|
||||
|
||||
public function getRoles(): array
|
||||
{
|
||||
return self::ROLES;
|
||||
return [...self::ROLES, self::STATS];
|
||||
}
|
||||
|
||||
public function getRolesWithHierarchy(): array
|
||||
{
|
||||
return [
|
||||
'Event' => self::ROLES,
|
||||
'Participation' => $this->getRoles(),
|
||||
];
|
||||
}
|
||||
|
||||
public function getRolesWithoutScope(): array
|
||||
{
|
||||
return [];
|
||||
return [self::ROLES, self::STATS];
|
||||
}
|
||||
|
||||
public function supports($attribute, $subject)
|
||||
{
|
||||
return ($subject instanceof Participation && \in_array($attribute, self::ROLES, true))
|
||||
|| ($subject instanceof Person && \in_array($attribute, [self::CREATE, self::SEE], true))
|
||||
|| (null === $subject && self::SEE === $attribute);
|
||||
return $this->voterHelper->supports($attribute, $subject);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $attribute
|
||||
* @param Participation $subject
|
||||
* @param string $attribute
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
@@ -115,15 +106,5 @@ class ParticipationVoter extends AbstractChillVoter implements ProvideRoleHierar
|
||||
->getReachableCenters($token->getUser(), $attribute);
|
||||
|
||||
return \count($centers) > 0;
|
||||
|
||||
if (!$this->accessDecisionManager->decide($token, [PersonVoter::SEE], $person)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->authorizationHelper->userHasAccess(
|
||||
$token->getUser(),
|
||||
$subject,
|
||||
$attribute
|
||||
);
|
||||
}
|
||||
}
|
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\EventBundle\Tests\Export;
|
||||
|
||||
use Chill\EventBundle\Export\Export\CountEventParticipations;
|
||||
use Doctrine\ORM\AbstractQuery;
|
||||
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @coversNothing
|
||||
*/
|
||||
class CountEventParticipationsTest extends KernelTestCase
|
||||
{
|
||||
private CountEventParticipations $countEventParticipations;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
self::bootKernel();
|
||||
$this->countEventParticipations = self::getContainer()->get(CountEventParticipations::class);
|
||||
}
|
||||
|
||||
public function testExecuteQuery(): void
|
||||
{
|
||||
$qb = $this->countEventParticipations->initiateQuery([], [], [])
|
||||
->setMaxResults(1);
|
||||
|
||||
$results = $qb->getQuery()->getResult(AbstractQuery::HYDRATE_ARRAY);
|
||||
|
||||
self::assertIsArray($results, 'smoke test: test that the result is an array');
|
||||
}
|
||||
}
|
43
src/Bundle/ChillEventBundle/Tests/Export/CountEventTest.php
Normal file
43
src/Bundle/ChillEventBundle/Tests/Export/CountEventTest.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\EventBundle\Tests\Export;
|
||||
|
||||
use Chill\EventBundle\Export\Export\CountEvents;
|
||||
use Doctrine\ORM\AbstractQuery;
|
||||
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @coversNothing
|
||||
*/
|
||||
class CountEventTest extends KernelTestCase
|
||||
{
|
||||
private CountEvents $countEvents;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
self::bootKernel();
|
||||
$this->countEvents = self::getContainer()->get(CountEvents::class);
|
||||
}
|
||||
|
||||
public function testExecuteQuery(): void
|
||||
{
|
||||
$qb = $this->countEvents->initiateQuery([], [], [])
|
||||
->setMaxResults(1);
|
||||
|
||||
$results = $qb->getQuery()->getResult(AbstractQuery::HYDRATE_ARRAY);
|
||||
|
||||
self::assertIsArray($results, 'smoke test: test that the result is an array');
|
||||
}
|
||||
}
|
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Export\aggregators;
|
||||
|
||||
use Chill\EventBundle\Entity\Event;
|
||||
use Chill\EventBundle\Export\Aggregator\EventDateAggregator;
|
||||
use Chill\MainBundle\Test\Export\AbstractAggregatorTest;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @coversNothing
|
||||
*/
|
||||
class EventDateAggregatorTest extends AbstractAggregatorTest
|
||||
{
|
||||
private $aggregator;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
self::bootKernel();
|
||||
|
||||
$this->aggregator = self::getContainer()->get(EventDateAggregator::class);
|
||||
}
|
||||
|
||||
public function getAggregator()
|
||||
{
|
||||
return $this->aggregator;
|
||||
}
|
||||
|
||||
public function getFormData(): array|\Generator
|
||||
{
|
||||
yield ['frequency' => 'YYYY'];
|
||||
yield ['frequency' => 'YYYY-MM'];
|
||||
yield ['frequency' => 'YYYY-IV'];
|
||||
}
|
||||
|
||||
public function getQueryBuilders(): array
|
||||
{
|
||||
self::bootKernel();
|
||||
|
||||
$em = self::getContainer()->get(EntityManagerInterface::class);
|
||||
|
||||
return [
|
||||
$em->createQueryBuilder()
|
||||
->select('event.id')
|
||||
->from(Event::class, 'event'),
|
||||
];
|
||||
}
|
||||
}
|
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Export\aggregators;
|
||||
|
||||
use Chill\EventBundle\Entity\Event;
|
||||
use Chill\EventBundle\Export\Aggregator\EventTypeAggregator;
|
||||
use Chill\MainBundle\Test\Export\AbstractAggregatorTest;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @coversNothing
|
||||
*/
|
||||
class EventTypeAggregatorTest extends AbstractAggregatorTest
|
||||
{
|
||||
private $aggregator;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
self::bootKernel();
|
||||
|
||||
$this->aggregator = self::getContainer()->get(EventTypeAggregator::class);
|
||||
}
|
||||
|
||||
public function getAggregator()
|
||||
{
|
||||
return $this->aggregator;
|
||||
}
|
||||
|
||||
public function getFormData(): array
|
||||
{
|
||||
return [
|
||||
[],
|
||||
];
|
||||
}
|
||||
|
||||
public function getQueryBuilders(): array
|
||||
{
|
||||
self::bootKernel();
|
||||
|
||||
$em = self::getContainer()->get(EntityManagerInterface::class);
|
||||
|
||||
return [
|
||||
$em->createQueryBuilder()
|
||||
->select('event.id')
|
||||
->from(Event::class, 'event'),
|
||||
];
|
||||
}
|
||||
}
|
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Export\aggregators;
|
||||
|
||||
use Chill\EventBundle\Entity\Event;
|
||||
use Chill\EventBundle\Entity\Participation;
|
||||
use Chill\EventBundle\Export\Aggregator\RoleAggregator;
|
||||
use Chill\MainBundle\Test\Export\AbstractAggregatorTest;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @coversNothing
|
||||
*/
|
||||
class RoleAggregatorTest extends AbstractAggregatorTest
|
||||
{
|
||||
private $aggregator;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
self::bootKernel();
|
||||
|
||||
$this->aggregator = self::getContainer()->get(RoleAggregator::class);
|
||||
}
|
||||
|
||||
public function getAggregator()
|
||||
{
|
||||
return $this->aggregator;
|
||||
}
|
||||
|
||||
public function getFormData(): array
|
||||
{
|
||||
return [
|
||||
[],
|
||||
];
|
||||
}
|
||||
|
||||
public function getQueryBuilders(): array
|
||||
{
|
||||
self::bootKernel();
|
||||
|
||||
$em = self::getContainer()->get(EntityManagerInterface::class);
|
||||
|
||||
return [
|
||||
$em->createQueryBuilder()
|
||||
->select('event.id')
|
||||
->from(Event::class, 'event'),
|
||||
$em->createQueryBuilder()
|
||||
->select('event_part')
|
||||
->from(Participation::class, 'event_part'),
|
||||
];
|
||||
}
|
||||
}
|
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Export\filters;
|
||||
|
||||
use Chill\EventBundle\Entity\Event;
|
||||
use Chill\EventBundle\Export\Filter\EventDateFilter;
|
||||
use Chill\MainBundle\Service\RollingDate\RollingDate;
|
||||
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
|
||||
use Chill\MainBundle\Test\Export\AbstractFilterTest;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @coversNothing
|
||||
*/
|
||||
class EventDateFilterTest extends AbstractFilterTest
|
||||
{
|
||||
private RollingDateConverterInterface $rollingDateConverter;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
self::bootKernel();
|
||||
|
||||
$this->rollingDateConverter = self::getContainer()->get(RollingDateConverterInterface::class);
|
||||
}
|
||||
|
||||
public function getFilter()
|
||||
{
|
||||
return new EventDateFilter($this->rollingDateConverter);
|
||||
}
|
||||
|
||||
public function getFormData()
|
||||
{
|
||||
return [
|
||||
[
|
||||
'date_from' => new RollingDate(RollingDate::T_YEAR_CURRENT_START),
|
||||
'date_to' => new RollingDate(RollingDate::T_TODAY),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function getQueryBuilders(): array
|
||||
{
|
||||
self::bootKernel();
|
||||
|
||||
$em = self::getContainer()->get(EntityManagerInterface::class);
|
||||
|
||||
return [
|
||||
$em->createQueryBuilder()
|
||||
->select('event.id')
|
||||
->from(Event::class, 'event'),
|
||||
];
|
||||
}
|
||||
}
|
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Export\filters;
|
||||
|
||||
use Chill\EventBundle\Entity\Event;
|
||||
use Chill\EventBundle\Entity\EventType;
|
||||
use Chill\EventBundle\Export\Filter\EventTypeFilter;
|
||||
use Chill\MainBundle\Test\Export\AbstractFilterTest;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @coversNothing
|
||||
*/
|
||||
class EventTypeFilterTest extends AbstractFilterTest
|
||||
{
|
||||
private EventTypeFilter $filter;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
self::bootKernel();
|
||||
$this->filter = self::getContainer()->get(EventTypeFilter::class);
|
||||
}
|
||||
|
||||
public function getFilter(): EventTypeFilter|\Chill\MainBundle\Export\FilterInterface
|
||||
{
|
||||
return $this->filter;
|
||||
}
|
||||
|
||||
public function getFormData()
|
||||
{
|
||||
self::bootKernel();
|
||||
|
||||
$em = self::getContainer()->get(EntityManagerInterface::class);
|
||||
|
||||
$array = $em->createQueryBuilder()
|
||||
->from(EventType::class, 'et')
|
||||
->select('et')
|
||||
->getQuery()
|
||||
->getResult();
|
||||
|
||||
$data = [];
|
||||
|
||||
foreach ($array as $a) {
|
||||
$data[] = [
|
||||
'types' => new ArrayCollection([$a]),
|
||||
];
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function getQueryBuilders()
|
||||
{
|
||||
self::bootKernel();
|
||||
|
||||
$em = self::getContainer()->get(EntityManagerInterface::class);
|
||||
|
||||
return [
|
||||
$em->createQueryBuilder()
|
||||
->select('event.id')
|
||||
->from(Event::class, 'event'),
|
||||
];
|
||||
}
|
||||
}
|
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Export\filters;
|
||||
|
||||
use Chill\EventBundle\Entity\Event;
|
||||
use Chill\EventBundle\Entity\Participation;
|
||||
use Chill\EventBundle\Entity\Role;
|
||||
use Chill\EventBundle\Export\Filter\RoleFilter;
|
||||
use Chill\MainBundle\Test\Export\AbstractFilterTest;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @coversNothing
|
||||
*/
|
||||
class RoleFilterTest extends AbstractFilterTest
|
||||
{
|
||||
private RoleFilter $filter;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
self::bootKernel();
|
||||
|
||||
$this->filter = self::getContainer()->get(RoleFilter::class);
|
||||
}
|
||||
|
||||
public function getFilter()
|
||||
{
|
||||
return $this->filter;
|
||||
}
|
||||
|
||||
public function getFormData(): array
|
||||
{
|
||||
self::bootKernel();
|
||||
$em = self::getContainer()->get(EntityManagerInterface::class);
|
||||
|
||||
$array = $em->createQueryBuilder()
|
||||
->from(Role::class, 'r')
|
||||
->select('r')
|
||||
->getQuery()
|
||||
->setMaxResults(1)
|
||||
->getResult();
|
||||
|
||||
$data = [];
|
||||
|
||||
foreach ($array as $a) {
|
||||
$data[] = [
|
||||
'roles' => new ArrayCollection([$a]),
|
||||
];
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function getQueryBuilders()
|
||||
{
|
||||
self::bootKernel();
|
||||
|
||||
$em = self::getContainer()->get(EntityManagerInterface::class);
|
||||
|
||||
return [
|
||||
$em->createQueryBuilder()
|
||||
->select('event.id')
|
||||
->from(Event::class, 'event'),
|
||||
$em->createQueryBuilder()
|
||||
->select('event_part')
|
||||
->from(Participation::class, 'event_part'),
|
||||
];
|
||||
}
|
||||
}
|
@@ -12,7 +12,7 @@ declare(strict_types=1);
|
||||
namespace Chill\EventBundle\Tests\Repository;
|
||||
|
||||
use Chill\EventBundle\Repository\EventACLAwareRepository;
|
||||
use Chill\EventBundle\Security\Authorization\EventVoter;
|
||||
use Chill\EventBundle\Security\EventVoter;
|
||||
use Chill\MainBundle\Entity\Center;
|
||||
use Chill\MainBundle\Entity\Scope;
|
||||
use Chill\MainBundle\Entity\User;
|
||||
|
@@ -1,18 +0,0 @@
|
||||
services:
|
||||
chill_event.event_voter:
|
||||
class: Chill\EventBundle\Security\Authorization\EventVoter
|
||||
arguments:
|
||||
- "@security.access.decision_manager"
|
||||
- "@chill.main.security.authorization.helper"
|
||||
- "@logger"
|
||||
tags:
|
||||
- { name: security.voter }
|
||||
|
||||
chill_event.event_participation:
|
||||
class: Chill\EventBundle\Security\Authorization\ParticipationVoter
|
||||
arguments:
|
||||
- "@security.access.decision_manager"
|
||||
- "@chill.main.security.authorization.helper"
|
||||
- "@logger"
|
||||
tags:
|
||||
- { name: security.voter }
|
41
src/Bundle/ChillEventBundle/config/services/export.yaml
Normal file
41
src/Bundle/ChillEventBundle/config/services/export.yaml
Normal file
@@ -0,0 +1,41 @@
|
||||
services:
|
||||
_defaults:
|
||||
autowire: true
|
||||
autoconfigure: true
|
||||
|
||||
# indicators
|
||||
|
||||
Chill\EventBundle\Export\Export\CountEvents:
|
||||
tags:
|
||||
- { name: chill.export, alias: 'count_events' }
|
||||
Chill\EventBundle\Export\Export\CountEventParticipations:
|
||||
tags:
|
||||
- { name: chill.export, alias: 'count_event_participants' }
|
||||
|
||||
# filters
|
||||
|
||||
Chill\EventBundle\Export\Filter\EventDateFilter:
|
||||
tags:
|
||||
- { name: chill.export_filter, alias: 'event_date_filter' }
|
||||
|
||||
Chill\EventBundle\Export\Filter\EventTypeFilter:
|
||||
tags:
|
||||
- { name: chill.export_filter, alias: 'event_type_filter' }
|
||||
|
||||
Chill\EventBundle\Export\Filter\RoleFilter:
|
||||
tags:
|
||||
- { name: chill.export_filter, alias: 'role_filter' }
|
||||
|
||||
# aggregators
|
||||
|
||||
Chill\EventBundle\Export\Aggregator\EventTypeAggregator:
|
||||
tags:
|
||||
- { name: chill.export_aggregator, alias: event_type_aggregator }
|
||||
|
||||
Chill\EventBundle\Export\Aggregator\EventDateAggregator:
|
||||
tags:
|
||||
- { name: chill.export_aggregator, alias: event_date_aggregator }
|
||||
|
||||
Chill\EventBundle\Export\Aggregator\RoleAggregator:
|
||||
tags:
|
||||
- { name: chill.export_aggregator, alias: role_aggregator }
|
14
src/Bundle/ChillEventBundle/config/services/security.yaml
Normal file
14
src/Bundle/ChillEventBundle/config/services/security.yaml
Normal file
@@ -0,0 +1,14 @@
|
||||
services:
|
||||
Chill\EventBundle\Security\EventVoter:
|
||||
autowire: true
|
||||
autoconfigure: true
|
||||
tags:
|
||||
- { name: security.voter }
|
||||
- { name: chill.role }
|
||||
|
||||
Chill\EventBundle\Security\ParticipationVoter:
|
||||
autowire: true
|
||||
autoconfigure: true
|
||||
tags:
|
||||
- { name: security.voter }
|
||||
- { name: chill.role }
|
@@ -81,9 +81,31 @@ Pick an event: Choisir un événement
|
||||
Pick a type of event: Choisir un type d'événement
|
||||
Pick a moderator: Choisir un animateur
|
||||
|
||||
# exports
|
||||
Select a format: Choisir un format
|
||||
Export: Exporter
|
||||
|
||||
Count events: Nombre d'événements
|
||||
Count events by various parameters.: Compte le nombre d'événements selon divers critères
|
||||
Exports of events: Exports d'événements
|
||||
|
||||
Filtered by event date: Filtrer par date d'événement
|
||||
'Filtered by date of event: only between %date_from% and %date_to%': "Filtré par date d'événement: uniquement entre le %date_from% et le %date_to%"
|
||||
Events after this date: Événements après cette date
|
||||
Events before this date: Événements avant cette date
|
||||
Filtered by event type: Filtrer par type d'événement
|
||||
'Filtered by event type: only %list%': "Filtré par type: uniquement %list%"
|
||||
Group event by date: Grouper par date d'événement
|
||||
Group by event type: Grouper par type d'événement
|
||||
|
||||
Count event participants: Nombre de participations
|
||||
Count participants to an event by various parameters.: Compte le nombre de participations selon divers critères
|
||||
Exports of event participants: Exports de participations
|
||||
'Filtered by participant roles: only %list%': "Filtré par rôles de participation: uniquement %list%"
|
||||
Filter by participant roles: Filtrer par rôles de participation
|
||||
Part roles: Rôles de participation
|
||||
Group by participant role: Grouper par rôle de participation
|
||||
|
||||
|
||||
Events configuration: Configuration des événements
|
||||
Events configuration menu: Menu des événements
|
||||
|
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\MainBundle\Command;
|
||||
|
||||
use Chill\MainBundle\Service\Import\AddressReferenceLU;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class LoadAddressesLUFromBDAddressCommand extends Command
|
||||
{
|
||||
protected static $defaultDescription = 'Import LUX addresses from BD addresses (see https://data.public.lu/fr/datasets/adresses-georeferencees-bd-adresses/)';
|
||||
|
||||
public function __construct(
|
||||
private readonly AddressReferenceLU $addressImporter,
|
||||
) {
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function configure()
|
||||
{
|
||||
$this->setName('chill:main:address-ref-lux');
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$this->addressImporter->import();
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
}
|
@@ -632,7 +632,7 @@ class ExportController extends AbstractController
|
||||
}
|
||||
}
|
||||
|
||||
private function rebuildRawData(string $key): array
|
||||
private function rebuildRawData(?string $key): array
|
||||
{
|
||||
if (null === $key) {
|
||||
throw $this->createNotFoundException('key does not exists');
|
||||
|
@@ -61,8 +61,6 @@ final class PermissionsGroupController extends AbstractController
|
||||
$form = $this->createAddRoleScopeForm($permissionsGroup);
|
||||
$form->handleRequest($request);
|
||||
|
||||
dump($form->isSubmitted());
|
||||
|
||||
if ($form->isSubmitted() && $form->isValid()) {
|
||||
$roleScope = $this->getPersistentRoleScopeBy(
|
||||
$form['composed_role_scope']->getData()->getRole(),
|
||||
|
@@ -1,16 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\MainBundle\Controller;
|
||||
|
||||
use Chill\MainBundle\CRUD\Controller\ApiController;
|
||||
|
||||
class UserGroupApiController extends ApiController {}
|
@@ -1,68 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\MainBundle\DataFixtures\ORM;
|
||||
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\MainBundle\Entity\UserGroup;
|
||||
use Doctrine\Bundle\FixturesBundle\Fixture;
|
||||
use Doctrine\Bundle\FixturesBundle\FixtureGroupInterface;
|
||||
use Doctrine\Persistence\ObjectManager;
|
||||
|
||||
class LoadUserGroup extends Fixture implements FixtureGroupInterface
|
||||
{
|
||||
public static function getGroups(): array
|
||||
{
|
||||
return ['user-group'];
|
||||
}
|
||||
|
||||
public function load(ObjectManager $manager)
|
||||
{
|
||||
$centerASocial = $manager->getRepository(User::class)->findOneBy(['username' => 'center a_social']);
|
||||
$centerBSocial = $manager->getRepository(User::class)->findOneBy(['username' => 'center b_social']);
|
||||
$multiCenter = $manager->getRepository(User::class)->findOneBy(['username' => 'multi_center']);
|
||||
$administrativeA = $manager->getRepository(User::class)->findOneBy(['username' => 'center a_administrative']);
|
||||
$administrativeB = $manager->getRepository(User::class)->findOneBy(['username' => 'center b_administrative']);
|
||||
|
||||
$level1 = $this->generateLevelGroup('Niveau 1', '#eec84aff', '#000000ff', 'level');
|
||||
$level1->addUser($centerASocial)->addUser($centerBSocial);
|
||||
$manager->persist($level1);
|
||||
|
||||
$level2 = $this->generateLevelGroup('Niveau 2', ' #e2793dff', '#000000ff', 'level');
|
||||
$level2->addUser($multiCenter);
|
||||
$manager->persist($level2);
|
||||
|
||||
$level3 = $this->generateLevelGroup('Niveau 3', ' #df4949ff', '#000000ff', 'level');
|
||||
$level3->addUser($multiCenter);
|
||||
$manager->persist($level3);
|
||||
|
||||
$tss = $this->generateLevelGroup('Travailleur sociaux', '#43b29dff', '#000000ff', '');
|
||||
$tss->addUser($multiCenter)->addUser($centerASocial)->addUser($centerBSocial);
|
||||
$manager->persist($tss);
|
||||
$admins = $this->generateLevelGroup('Administratif', '#334d5cff', '#000000ff', '');
|
||||
$admins->addUser($administrativeA)->addUser($administrativeB);
|
||||
$manager->persist($admins);
|
||||
|
||||
$manager->flush();
|
||||
}
|
||||
|
||||
private function generateLevelGroup(string $title, string $backgroundColor, string $foregroundColor, string $excludeKey): UserGroup
|
||||
{
|
||||
$userGroup = new UserGroup();
|
||||
|
||||
return $userGroup
|
||||
->setLabel(['fr' => $title])
|
||||
->setBackgroundColor($backgroundColor)
|
||||
->setForegroundColor($foregroundColor)
|
||||
->setExcludeKey($excludeKey)
|
||||
;
|
||||
}
|
||||
}
|
@@ -24,7 +24,6 @@ use Chill\MainBundle\Controller\LocationTypeController;
|
||||
use Chill\MainBundle\Controller\NewsItemController;
|
||||
use Chill\MainBundle\Controller\RegroupmentController;
|
||||
use Chill\MainBundle\Controller\UserController;
|
||||
use Chill\MainBundle\Controller\UserGroupApiController;
|
||||
use Chill\MainBundle\Controller\UserJobApiController;
|
||||
use Chill\MainBundle\Controller\UserJobController;
|
||||
use Chill\MainBundle\DependencyInjection\Widget\Factory\WidgetFactoryInterface;
|
||||
@@ -60,7 +59,6 @@ use Chill\MainBundle\Entity\LocationType;
|
||||
use Chill\MainBundle\Entity\NewsItem;
|
||||
use Chill\MainBundle\Entity\Regroupment;
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\MainBundle\Entity\UserGroup;
|
||||
use Chill\MainBundle\Entity\UserJob;
|
||||
use Chill\MainBundle\Form\CenterType;
|
||||
use Chill\MainBundle\Form\CivilityType;
|
||||
@@ -805,21 +803,6 @@ class ChillMainExtension extends Extension implements
|
||||
],
|
||||
],
|
||||
],
|
||||
[
|
||||
'class' => UserGroup::class,
|
||||
'controller' => UserGroupApiController::class,
|
||||
'name' => 'user-group',
|
||||
'base_path' => '/api/1.0/main/user-group',
|
||||
'base_role' => 'ROLE_USER',
|
||||
'actions' => [
|
||||
'_index' => [
|
||||
'methods' => [
|
||||
Request::METHOD_GET => true,
|
||||
Request::METHOD_HEAD => true,
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
@@ -43,7 +43,7 @@ class ShortMessageCompilerPass implements CompilerPassInterface
|
||||
$defaultTransporter = new Reference(NullShortMessageSender::class);
|
||||
} elseif ('ovh' === $dsn['scheme']) {
|
||||
if (!class_exists('\\'.\Ovh\Api::class)) {
|
||||
throw new RuntimeException('Class \\Ovh\\Api not found');
|
||||
throw new RuntimeException('Class \Ovh\Api not found');
|
||||
}
|
||||
|
||||
foreach (['user', 'host', 'pass'] as $component) {
|
||||
|
@@ -216,13 +216,13 @@ class User implements UserInterface, \Stringable, PasswordAuthenticatedUserInter
|
||||
return $this->mainLocation;
|
||||
}
|
||||
|
||||
public function getMainScope(?\DateTimeImmutable $at = null): ?Scope
|
||||
public function getMainScope(?\DateTimeImmutable $atDate = null): ?Scope
|
||||
{
|
||||
$at ??= new \DateTimeImmutable('now');
|
||||
$atDate ??= new \DateTimeImmutable('now');
|
||||
|
||||
foreach ($this->scopeHistories as $scopeHistory) {
|
||||
if ($at >= $scopeHistory->getStartDate() && (
|
||||
null === $scopeHistory->getEndDate() || $at < $scopeHistory->getEndDate()
|
||||
if ($atDate >= $scopeHistory->getStartDate() && (
|
||||
null === $scopeHistory->getEndDate() || $atDate < $scopeHistory->getEndDate()
|
||||
)) {
|
||||
return $scopeHistory->getScope();
|
||||
}
|
||||
@@ -265,13 +265,13 @@ class User implements UserInterface, \Stringable, PasswordAuthenticatedUserInter
|
||||
return $this->salt;
|
||||
}
|
||||
|
||||
public function getUserJob(?\DateTimeImmutable $at = null): ?UserJob
|
||||
public function getUserJob(?\DateTimeImmutable $atDate = null): ?UserJob
|
||||
{
|
||||
$at ??= new \DateTimeImmutable('now');
|
||||
$atDate ??= new \DateTimeImmutable('now');
|
||||
|
||||
foreach ($this->jobHistories as $jobHistory) {
|
||||
if ($at >= $jobHistory->getStartDate() && (
|
||||
null === $jobHistory->getEndDate() || $at < $jobHistory->getEndDate()
|
||||
if ($atDate >= $jobHistory->getStartDate() && (
|
||||
null === $jobHistory->getEndDate() || $atDate < $jobHistory->getEndDate()
|
||||
)) {
|
||||
return $jobHistory->getJob();
|
||||
}
|
||||
@@ -285,6 +285,11 @@ class User implements UserInterface, \Stringable, PasswordAuthenticatedUserInter
|
||||
return $this->jobHistories;
|
||||
}
|
||||
|
||||
public function getUserScopeHistories(): Collection
|
||||
{
|
||||
return $this->scopeHistories;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ArrayCollection|UserJobHistory[]
|
||||
*/
|
||||
|
@@ -1,139 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\MainBundle\Entity;
|
||||
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Symfony\Component\Serializer\Annotation as Serializer;
|
||||
|
||||
#[ORM\Entity]
|
||||
#[ORM\Table(name: 'chill_main_user_group')]
|
||||
#[Serializer\DiscriminatorMap(typeProperty: 'type', mapping: ['user_group' => UserGroup::class])]
|
||||
class UserGroup
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::INTEGER, nullable: false)]
|
||||
#[Serializer\Groups(['read'])]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::JSON, nullable: false, options: ['default' => '[]'])]
|
||||
#[Serializer\Groups(['read'])]
|
||||
private array $label = [];
|
||||
|
||||
/**
|
||||
* @var \Doctrine\Common\Collections\Collection<int, \Chill\MainBundle\Entity\User>
|
||||
*/
|
||||
#[ORM\ManyToMany(targetEntity: User::class)]
|
||||
#[ORM\JoinTable(name: 'chill_main_user_group_user')]
|
||||
private Collection $users;
|
||||
|
||||
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::TEXT, nullable: false, options: ['default' => '#ffffffff'])]
|
||||
#[Serializer\Groups(['read'])]
|
||||
private string $backgroundColor = '#ffffffff';
|
||||
|
||||
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::TEXT, nullable: false, options: ['default' => '#000000ff'])]
|
||||
#[Serializer\Groups(['read'])]
|
||||
private string $foregroundColor = '#000000ff';
|
||||
|
||||
/**
|
||||
* Groups with same exclude key are mutually exclusive: adding one in a many-to-one relationship
|
||||
* will exclude others.
|
||||
*
|
||||
* An empty string means "no exclusion"
|
||||
*/
|
||||
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::TEXT, nullable: false, options: ['default' => ''])]
|
||||
#[Serializer\Groups(['read'])]
|
||||
private string $excludeKey = '';
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->users = new ArrayCollection();
|
||||
}
|
||||
|
||||
public function addUser(User $user): self
|
||||
{
|
||||
if (!$this->users->contains($user)) {
|
||||
$this->users[] = $user;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function removeUser(User $user): self
|
||||
{
|
||||
if ($this->users->contains($user)) {
|
||||
$this->users->removeElement($user);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getLabel(): array
|
||||
{
|
||||
return $this->label;
|
||||
}
|
||||
|
||||
public function getUsers(): Collection
|
||||
{
|
||||
return $this->users;
|
||||
}
|
||||
|
||||
public function getForegroundColor(): string
|
||||
{
|
||||
return $this->foregroundColor;
|
||||
}
|
||||
|
||||
public function getExcludeKey(): string
|
||||
{
|
||||
return $this->excludeKey;
|
||||
}
|
||||
|
||||
public function getBackgroundColor(): string
|
||||
{
|
||||
return $this->backgroundColor;
|
||||
}
|
||||
|
||||
public function setForegroundColor(string $foregroundColor): self
|
||||
{
|
||||
$this->foregroundColor = $foregroundColor;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setBackgroundColor(string $backgroundColor): self
|
||||
{
|
||||
$this->backgroundColor = $backgroundColor;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setExcludeKey(string $excludeKey): self
|
||||
{
|
||||
$this->excludeKey = $excludeKey;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setLabel(array $label): self
|
||||
{
|
||||
$this->label = $label;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\MainBundle\Export;
|
||||
|
||||
/**
|
||||
* Transform data from filter.
|
||||
*
|
||||
* This interface defines a method for transforming filter's form data before it is processed.
|
||||
*
|
||||
* You can implement this interface on @see{FilterInterface} or @see{AggregatorInterface}, to allow to transform existing data in saved exports
|
||||
* and replace it with some default values, or new default values.
|
||||
*/
|
||||
interface DataTransformerInterface
|
||||
{
|
||||
public function transformData(?array $before): array;
|
||||
}
|
@@ -190,7 +190,7 @@ class ExportManager
|
||||
// throw an error if the export require other modifier, which is
|
||||
// not allowed when the export return a `NativeQuery`
|
||||
if (\count($export->supportsModifiers()) > 0) {
|
||||
throw new \LogicException("The export with alias `{$exportAlias}` return ".'a `\\Doctrine\\ORM\\NativeQuery` and supports modifiers, which is not allowed. Either the method `supportsModifiers` should return an empty array, or return a `Doctrine\\ORM\\QueryBuilder`');
|
||||
throw new \LogicException("The export with alias `{$exportAlias}` return ".'a `\Doctrine\ORM\NativeQuery` and supports modifiers, which is not allowed. Either the method `supportsModifiers` should return an empty array, or return a `Doctrine\ORM\QueryBuilder`');
|
||||
}
|
||||
} elseif ($query instanceof QueryBuilder) {
|
||||
// handle filters
|
||||
@@ -203,7 +203,7 @@ class ExportManager
|
||||
'dql' => $query->getDQL(),
|
||||
]);
|
||||
} else {
|
||||
throw new \UnexpectedValueException('The method `intiateQuery` should return a `\\Doctrine\\ORM\\NativeQuery` or a `Doctrine\\ORM\\QueryBuilder` object.');
|
||||
throw new \UnexpectedValueException('The method `intiateQuery` should return a `\Doctrine\ORM\NativeQuery` or a `Doctrine\ORM\QueryBuilder` object.');
|
||||
}
|
||||
|
||||
$result = $export->getResult($query, $data[ExportType::EXPORT_KEY]);
|
||||
|
@@ -32,6 +32,9 @@ interface FilterInterface extends ModifierInterface
|
||||
|
||||
/**
|
||||
* Get the default data, that can be use as "data" for the form.
|
||||
*
|
||||
* In case of adding new parameters to a filter, you can implement a @see{DataTransformerFilterInterface} to
|
||||
* transforme the filters's data saved in an export to the desired state.
|
||||
*/
|
||||
public function getFormDefaultData(): array;
|
||||
|
||||
|
@@ -11,7 +11,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace Chill\MainBundle\Form\Type\Export;
|
||||
|
||||
use Chill\MainBundle\Export\DataTransformerInterface;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\CallbackTransformer;
|
||||
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\FormType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
@@ -19,9 +21,7 @@ use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
class AggregatorType extends AbstractType
|
||||
{
|
||||
public function __construct() {}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$exportManager = $options['export_manager'];
|
||||
$aggregator = $exportManager->getAggregator($options['aggregator_alias']);
|
||||
@@ -32,17 +32,24 @@ class AggregatorType extends AbstractType
|
||||
'required' => false,
|
||||
]);
|
||||
|
||||
$filterFormBuilder = $builder->create('form', FormType::class, [
|
||||
$aggregatorFormBuilder = $builder->create('form', FormType::class, [
|
||||
'compound' => true,
|
||||
'required' => false,
|
||||
'error_bubbling' => false,
|
||||
]);
|
||||
$aggregator->buildForm($filterFormBuilder);
|
||||
$aggregator->buildForm($aggregatorFormBuilder);
|
||||
|
||||
$builder->add($filterFormBuilder);
|
||||
if ($aggregator instanceof DataTransformerInterface) {
|
||||
$aggregatorFormBuilder->addViewTransformer(new CallbackTransformer(
|
||||
fn (?array $data) => $data,
|
||||
fn (?array $data) => $aggregator->transformData($data),
|
||||
));
|
||||
}
|
||||
|
||||
$builder->add($aggregatorFormBuilder);
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setRequired('aggregator_alias')
|
||||
->setRequired('export_manager')
|
||||
|
@@ -11,8 +11,10 @@ declare(strict_types=1);
|
||||
|
||||
namespace Chill\MainBundle\Form\Type\Export;
|
||||
|
||||
use Chill\MainBundle\Export\DataTransformerInterface;
|
||||
use Chill\MainBundle\Export\FilterInterface;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\CallbackTransformer;
|
||||
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\FormType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
@@ -41,6 +43,13 @@ class FilterType extends AbstractType
|
||||
]);
|
||||
$filter->buildForm($filterFormBuilder);
|
||||
|
||||
if ($filter instanceof DataTransformerInterface) {
|
||||
$filterFormBuilder->addViewTransformer(new CallbackTransformer(
|
||||
fn (?array $data) => $data,
|
||||
fn (?array $data) => $filter->transformData($data),
|
||||
));
|
||||
}
|
||||
|
||||
$builder->add($filterFormBuilder);
|
||||
}
|
||||
|
||||
|
@@ -76,24 +76,6 @@ final class PhonenumberHelper implements PhoneNumberHelperInterface
|
||||
->formatOutOfCountryCallingNumber($phoneNumber, $this->config['default_carrier_code']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws NumberParseException
|
||||
*/
|
||||
public function parse(string $phoneNumber): PhoneNumber
|
||||
{
|
||||
$sanitizedPhoneNumber = $phoneNumber;
|
||||
|
||||
if (str_starts_with($sanitizedPhoneNumber, '00')) {
|
||||
$sanitizedPhoneNumber = '+'.substr($sanitizedPhoneNumber, 2, null);
|
||||
}
|
||||
|
||||
if (!str_starts_with($sanitizedPhoneNumber, '+') && !str_starts_with($sanitizedPhoneNumber, '0')) {
|
||||
$sanitizedPhoneNumber = '+'.$sanitizedPhoneNumber;
|
||||
}
|
||||
|
||||
return $this->phoneNumberUtil->parse($sanitizedPhoneNumber, $this->config['default_carrier_code']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get type (mobile, landline, ...) for phone number.
|
||||
*/
|
||||
|
@@ -59,6 +59,10 @@ export const ISOToDatetime = (str: string|null): Date|null => {
|
||||
[hours, minutes, seconds] = time.split(':').map(s => parseInt(s));
|
||||
;
|
||||
|
||||
if ('0000' === timezone) {
|
||||
return new Date(Date.UTC(year, month-1, date, hours, minutes, seconds));
|
||||
}
|
||||
|
||||
return new Date(year, month-1, date, hours, minutes, seconds);
|
||||
}
|
||||
|
||||
|
@@ -15,9 +15,9 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
var mime = require('mime')
|
||||
import mime from 'mime';
|
||||
|
||||
var download_report = (url, container) => {
|
||||
export const download_report = (url, container) => {
|
||||
var download_text = container.dataset.downloadText,
|
||||
alias = container.dataset.alias;
|
||||
|
||||
@@ -43,7 +43,14 @@ var download_report = (url, container) => {
|
||||
content = URL.createObjectURL(blob);
|
||||
}
|
||||
|
||||
extension = mime.getExtension(type);
|
||||
const extensions = new Map();
|
||||
extensions.set('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'xlsx');
|
||||
extensions.set('application/vnd.oasis.opendocument.spreadsheet', 'ods');
|
||||
extensions.set('application/vnd.ms-excel', 'xlsx');
|
||||
extensions.set('text/csv', 'csv');
|
||||
extensions.set('text/csv; charset=utf-8', 'csv');
|
||||
|
||||
extension = extensions.get(type);
|
||||
|
||||
link.appendChild(document.createTextNode(download_text));
|
||||
link.classList.add("btn", "btn-action");
|
||||
@@ -55,7 +62,7 @@ var download_report = (url, container) => {
|
||||
container.innerHTML = "";
|
||||
container.appendChild(link);
|
||||
}).catch(function(error) {
|
||||
console.log(error);
|
||||
console.error(error);
|
||||
var problem_text =
|
||||
document.createTextNode("Problem during download");
|
||||
|
||||
@@ -63,5 +70,3 @@ var download_report = (url, container) => {
|
||||
.replaceChild(problem_text, container.firstChild);
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = download_report;
|
||||
|
@@ -39,23 +39,5 @@ ClassicEditor.defaultConfig = {
|
||||
'redo'
|
||||
]
|
||||
},
|
||||
language: 'fr'
|
||||
language: 'fr',
|
||||
};
|
||||
|
||||
|
||||
let Fields = [];
|
||||
Fields.push.apply(Fields, document.querySelectorAll('textarea[ckeditor]'));
|
||||
// enable for custom fields
|
||||
//Fields.push.apply(Fields, document.querySelectorAll('.cf-fields textarea'));
|
||||
|
||||
Fields.forEach(function(field) {
|
||||
ClassicEditor
|
||||
.create( field )
|
||||
.then( editor => {
|
||||
//console.log( 'CkEditor was initialized', editor );
|
||||
})
|
||||
.catch( error => {
|
||||
console.error( error.stack );
|
||||
})
|
||||
;
|
||||
});
|
@@ -0,0 +1,15 @@
|
||||
import ClassicEditor from "./editor_config";
|
||||
|
||||
const ckeditorFields: NodeListOf<HTMLTextAreaElement> = document.querySelectorAll('textarea[ckeditor]');
|
||||
ckeditorFields.forEach((field: HTMLTextAreaElement): void => {
|
||||
ClassicEditor
|
||||
.create( field )
|
||||
.then( editor => {
|
||||
//console.log( 'CkEditor was initialized', editor );
|
||||
})
|
||||
.catch( error => {
|
||||
console.error( error.stack );
|
||||
})
|
||||
;
|
||||
});
|
||||
//Fields.push.apply(Fields, document.querySelectorAll('.cf-fields textarea'));
|
@@ -0,0 +1,16 @@
|
||||
import {download_report} from "../../lib/download-report/download-report";
|
||||
|
||||
window.addEventListener("DOMContentLoaded", function(e) {
|
||||
const export_generate_url = window.export_generate_url;
|
||||
|
||||
if (typeof export_generate_url === 'undefined') {
|
||||
console.error('Alias not found!');
|
||||
throw new Error('Alias not found!');
|
||||
}
|
||||
|
||||
const query = window.location.search,
|
||||
container = document.querySelector("#download_container")
|
||||
;
|
||||
|
||||
download_report(export_generate_url + query.toString(), container);
|
||||
});
|
@@ -1,175 +1,164 @@
|
||||
export interface DateTime {
|
||||
datetime: string;
|
||||
datetime8601: string;
|
||||
datetime: string;
|
||||
datetime8601: string
|
||||
}
|
||||
|
||||
export interface Civility {
|
||||
id: number;
|
||||
// TODO
|
||||
id: number;
|
||||
// TODO
|
||||
}
|
||||
|
||||
export interface Job {
|
||||
id: number;
|
||||
type: "user_job";
|
||||
label: {
|
||||
fr: string; // could have other key. How to do that in ts ?
|
||||
};
|
||||
id: number;
|
||||
type: "user_job";
|
||||
label: {
|
||||
"fr": string; // could have other key. How to do that in ts ?
|
||||
}
|
||||
}
|
||||
|
||||
export interface Center {
|
||||
id: number;
|
||||
type: "center";
|
||||
name: string;
|
||||
id: number;
|
||||
type: "center";
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface Scope {
|
||||
id: number;
|
||||
type: "scope";
|
||||
name: {
|
||||
fr: string;
|
||||
};
|
||||
id: number;
|
||||
type: "scope";
|
||||
name: {
|
||||
"fr": string
|
||||
}
|
||||
}
|
||||
|
||||
export interface User {
|
||||
type: "user";
|
||||
id: number;
|
||||
username: string;
|
||||
text: string;
|
||||
text_without_absence: string;
|
||||
email: string;
|
||||
user_job: Job;
|
||||
label: string;
|
||||
// todo: mainCenter; mainJob; etc..
|
||||
type: "user";
|
||||
id: number;
|
||||
username: string;
|
||||
text: string;
|
||||
text_without_absence: string;
|
||||
email: string;
|
||||
user_job: Job;
|
||||
label: string;
|
||||
// todo: mainCenter; mainJob; etc..
|
||||
}
|
||||
|
||||
export interface UserGroup {
|
||||
type: "chill_main_user_group" | "user_group";
|
||||
id: number;
|
||||
label: TranslatableString;
|
||||
backgroundColor: string;
|
||||
foregroundColor: string;
|
||||
excludeKey: string;
|
||||
}
|
||||
|
||||
export type UserGroupOrUser = User | UserGroup;
|
||||
|
||||
export interface UserAssociatedInterface {
|
||||
type: "user";
|
||||
id: number;
|
||||
}
|
||||
|
||||
export type TranslatableString = {
|
||||
fr?: string;
|
||||
nl?: string;
|
||||
type: "user";
|
||||
id: number;
|
||||
};
|
||||
|
||||
export type TranslatableString = {
|
||||
fr?: string;
|
||||
nl?: string;
|
||||
}
|
||||
|
||||
export interface Postcode {
|
||||
id: number;
|
||||
name: string;
|
||||
code: string;
|
||||
center: Point;
|
||||
id: number;
|
||||
name: string;
|
||||
code: string;
|
||||
center: Point;
|
||||
}
|
||||
|
||||
export type Point = {
|
||||
type: "Point";
|
||||
coordinates: [lat: number, lon: number];
|
||||
};
|
||||
|
||||
export interface Country {
|
||||
id: number;
|
||||
name: TranslatableString;
|
||||
code: string;
|
||||
type: "Point";
|
||||
coordinates: [lat: number, lon: number];
|
||||
}
|
||||
|
||||
export type AddressRefStatus = "match" | "to_review" | "reviewed";
|
||||
export interface Country {
|
||||
id: number;
|
||||
name: TranslatableString;
|
||||
code: string;
|
||||
}
|
||||
|
||||
export type AddressRefStatus = 'match'|'to_review'|'reviewed';
|
||||
|
||||
export interface Address {
|
||||
type: "address";
|
||||
address_id: number;
|
||||
text: string;
|
||||
street: string;
|
||||
streetNumber: string;
|
||||
postcode: Postcode;
|
||||
country: Country;
|
||||
floor: string | null;
|
||||
corridor: string | null;
|
||||
steps: string | null;
|
||||
flat: string | null;
|
||||
buildingName: string | null;
|
||||
distribution: string | null;
|
||||
extra: string | null;
|
||||
confidential: boolean;
|
||||
lines: string[];
|
||||
addressReference: AddressReference | null;
|
||||
validFrom: DateTime;
|
||||
validTo: DateTime | null;
|
||||
point: Point | null;
|
||||
refStatus: AddressRefStatus;
|
||||
isNoAddress: boolean;
|
||||
type: "address";
|
||||
address_id: number;
|
||||
text: string;
|
||||
street: string;
|
||||
streetNumber: string;
|
||||
postcode: Postcode;
|
||||
country: Country;
|
||||
floor: string | null;
|
||||
corridor: string | null;
|
||||
steps: string | null;
|
||||
flat: string | null;
|
||||
buildingName: string | null;
|
||||
distribution: string | null;
|
||||
extra: string | null;
|
||||
confidential: boolean;
|
||||
lines: string[];
|
||||
addressReference: AddressReference | null;
|
||||
validFrom: DateTime;
|
||||
validTo: DateTime | null;
|
||||
point: Point | null;
|
||||
refStatus: AddressRefStatus;
|
||||
isNoAddress: boolean;
|
||||
}
|
||||
|
||||
export interface AddressWithPoint extends Address {
|
||||
point: Point;
|
||||
point: Point
|
||||
}
|
||||
|
||||
export interface AddressReference {
|
||||
id: number;
|
||||
createdAt: DateTime | null;
|
||||
deletedAt: DateTime | null;
|
||||
municipalityCode: string;
|
||||
point: Point;
|
||||
postcode: Postcode;
|
||||
refId: string;
|
||||
source: string;
|
||||
street: string;
|
||||
streetNumber: string;
|
||||
updatedAt: DateTime | null;
|
||||
id: number;
|
||||
createdAt: DateTime | null;
|
||||
deletedAt: DateTime | null;
|
||||
municipalityCode: string;
|
||||
point: Point;
|
||||
postcode: Postcode;
|
||||
refId: string;
|
||||
source: string;
|
||||
street: string;
|
||||
streetNumber: string;
|
||||
updatedAt: DateTime | null;
|
||||
}
|
||||
|
||||
export interface SimpleGeographicalUnit {
|
||||
id: number;
|
||||
layerId: number;
|
||||
unitName: string;
|
||||
unitRefId: string;
|
||||
id: number;
|
||||
layerId: number;
|
||||
unitName: string;
|
||||
unitRefId: string;
|
||||
}
|
||||
|
||||
export interface GeographicalUnitLayer {
|
||||
id: number;
|
||||
name: TranslatableString;
|
||||
refId: string;
|
||||
id: number;
|
||||
name: TranslatableString;
|
||||
refId: string;
|
||||
}
|
||||
|
||||
export interface Location {
|
||||
type: "location";
|
||||
id: number;
|
||||
active: boolean;
|
||||
address: Address | null;
|
||||
availableForUsers: boolean;
|
||||
createdAt: DateTime | null;
|
||||
createdBy: User | null;
|
||||
updatedAt: DateTime | null;
|
||||
updatedBy: User | null;
|
||||
email: string | null;
|
||||
name: string;
|
||||
phonenumber1: string | null;
|
||||
phonenumber2: string | null;
|
||||
locationType: LocationType;
|
||||
type: "location";
|
||||
id: number;
|
||||
active: boolean;
|
||||
address: Address | null;
|
||||
availableForUsers: boolean;
|
||||
createdAt: DateTime | null;
|
||||
createdBy: User | null;
|
||||
updatedAt: DateTime | null;
|
||||
updatedBy: User | null;
|
||||
email: string | null
|
||||
name: string;
|
||||
phonenumber1: string | null;
|
||||
phonenumber2: string | null;
|
||||
locationType: LocationType;
|
||||
}
|
||||
|
||||
export interface LocationAssociated {
|
||||
type: "location";
|
||||
id: number;
|
||||
type: "location";
|
||||
id: number;
|
||||
}
|
||||
|
||||
export interface LocationType {
|
||||
type: "location-type";
|
||||
id: number;
|
||||
active: boolean;
|
||||
addressRequired: "optional" | "required";
|
||||
availableForUsers: boolean;
|
||||
editableByUsers: boolean;
|
||||
contactData: "optional" | "required";
|
||||
title: TranslatableString;
|
||||
type: "location-type";
|
||||
id: number;
|
||||
active: boolean;
|
||||
addressRequired: "optional" | "required";
|
||||
availableForUsers: boolean;
|
||||
editableByUsers: boolean;
|
||||
contactData: "optional" | "required";
|
||||
title: TranslatableString;
|
||||
}
|
||||
|
||||
export interface NewsItemType {
|
||||
|
@@ -139,7 +139,7 @@ const postprocess = (html: string): string => {
|
||||
}
|
||||
|
||||
const convertMarkdownToHtml = (markdown: string): string => {
|
||||
marked.use({'hooks': {postprocess, preprocess}});
|
||||
marked.use({'hooks': {postprocess, preprocess}, 'async': false});
|
||||
const rawHtml = marked(markdown) as string;
|
||||
return rawHtml;
|
||||
};
|
||||
|
@@ -1,24 +1,13 @@
|
||||
<template>
|
||||
<span class="chill-entity entity-user">
|
||||
{{ user.label }}
|
||||
<span class="user-job" v-if="user.user_job !== null"
|
||||
>({{ user.user_job.label.fr }})</span
|
||||
>
|
||||
<span class="main-scope" v-if="user.main_scope !== null"
|
||||
>({{ user.main_scope.name.fr }})</span
|
||||
>
|
||||
<span
|
||||
v-if="user.isAbsent"
|
||||
class="badge bg-danger rounded-pill"
|
||||
:title="Absent"
|
||||
>A</span
|
||||
>
|
||||
</span>
|
||||
<span class="chill-entity entity-user">
|
||||
{{ user.label }}
|
||||
<span class="user-job" v-if="user.user_job !== null">({{ user.user_job.label.fr }})</span> <span class="main-scope" v-if="user.main_scope !== null">({{ user.main_scope.name.fr }})</span> <span v-if="user.isAbsent" class="badge bg-danger rounded-pill" :title="Absent">A</span>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "UserRenderBoxBadge",
|
||||
props: ["user"],
|
||||
};
|
||||
props: ['user'],
|
||||
}
|
||||
</script>
|
||||
|
@@ -0,0 +1,306 @@
|
||||
{% extends '@ChillMain/layout.html.twig' %}
|
||||
|
||||
{% block title %}
|
||||
SASS Assets Catalogue
|
||||
{% endblock %}
|
||||
|
||||
{% block css %}
|
||||
<style media="screen">
|
||||
h2 { margin: 1.5em 0; }
|
||||
div.flex-table ul, div.flex-bloc ul { padding-left: 1rem; }
|
||||
div.flex-table div.item-bloc div.item-row div.item-col:first-child { flex-basis: 20%; }
|
||||
div.flex-bloc div.item-bloc { flex-basis: 50%; }
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="col-md-10">
|
||||
<h1 class="display-4">{{ block('title') }}</h1>
|
||||
|
||||
<b>Voir aussi: </b>
|
||||
<a href="{{ path('sass_assets_test1') }}">Test 1</a> |
|
||||
<a href="{{ path('sass_assets_test2') }}">Test 2</a>
|
||||
|
||||
<h2>Flex-table et flex-bloc</h2>
|
||||
<p>Base d'un placement flex alternatif à l'usage des tables.
|
||||
Flex-table et flex-bloc utilisent la même structure html (seul la root class change).
|
||||
Le placement est responsive.
|
||||
La bordure utilise box-shadow pour simuler border-collapse (table).
|
||||
</p>
|
||||
<p>Une classe separator peut être appliquée sur item-row</p>
|
||||
|
||||
<xmp>
|
||||
<div class="flex-table">
|
||||
<div class="item-bloc">
|
||||
<div class="item-row">
|
||||
<div class="item-col"></div>
|
||||
<div class="item-col"></div>
|
||||
</div>
|
||||
<div class="item-row separator">
|
||||
<div class="item-col"></div>
|
||||
<div class="item-col"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item-bloc">
|
||||
<div class="item-row">
|
||||
<div class="item-col"></div>
|
||||
<div class="item-col"></div>
|
||||
</div>
|
||||
<div class="item-row">
|
||||
<div class="item-col"></div>
|
||||
<div class="item-col"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</xmp>
|
||||
|
||||
<h3>Flex-table</h3>
|
||||
<p>On fixe manuellement la largeur de la première colonne :
|
||||
<pre>div.flex-table div.item-bloc div.item-row div.item-col:first-child { flex-basis: 20%; }</pre>
|
||||
</p>
|
||||
<div class="flex-table debug">
|
||||
<div class="item-bloc">
|
||||
<div class="item-row">
|
||||
<div class="item-col">Title row1</div>
|
||||
<div class="item-col">
|
||||
<ul class="list-content">
|
||||
<li>Nam rhoncus tristique ligula, tincidunt iaculis augue tincidunt ac viverra et a dui.</li>
|
||||
<li>Nam rhoncus tristique ligula, tincidunt iaculis augue tincidunt ac viverra et a dui.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item-row separator">
|
||||
<div class="item-col">Title row2</div>
|
||||
<div class="item-col">Nam rhoncus tristique ligula, tincidunt iaculis augue tincidunt ac viverra et a dui.</div>
|
||||
</div>
|
||||
<div class="item-row">
|
||||
<div class="item-col">Title row3</div>
|
||||
<div class="item-col">Nam rhoncus tristique ligula, tincidunt iaculis augue tincidunt ac viverra et a dui.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item-bloc">
|
||||
<div class="item-row">
|
||||
<div class="item-col">Title row1</div>
|
||||
<div class="item-col">
|
||||
<ul class="list-content">
|
||||
<li>Nam rhoncus tristique ligula, tincidunt iaculis augue tincidunt ac viverra et a dui.</li>
|
||||
<li>Nam rhoncus tristique ligula, tincidunt iaculis augue tincidunt ac viverra et a dui.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item-row">
|
||||
<div class="item-col">Title row2</div>
|
||||
<div class="item-col">Nam rhoncus tristique ligula, tincidunt iaculis augue tincidunt ac viverra et a dui.</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3>Flex-bloc</h3>
|
||||
<p>On fixe manuellement la largeur des blocs :
|
||||
<pre>div.flex-bloc div.item-bloc { flex-basis: 50%; }</pre>
|
||||
</p>
|
||||
<div class="flex-bloc debug">
|
||||
<div class="item-bloc">
|
||||
<div class="item-row">
|
||||
<div class="item-col">Title row1</div>
|
||||
<div class="item-col">
|
||||
<ul class="list-content">
|
||||
<li>Nam rhoncus tristique ligula, tincidunt iaculis augue tincidunt ac viverra et a dui.</li>
|
||||
<li>Nam rhoncus tristique ligula, tincidunt iaculis augue tincidunt ac viverra et a dui.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item-row separator">
|
||||
<div class="item-col">Title row2</div>
|
||||
<div class="item-col">Nam rhoncus tristique ligula, tincidunt iaculis augue tincidunt ac viverra et a dui.</div>
|
||||
</div>
|
||||
<div class="item-row">
|
||||
<div class="item-col">Title row3</div>
|
||||
<div class="item-col">Nam rhoncus tristique ligula, tincidunt iaculis augue tincidunt ac viverra et a dui.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item-bloc">
|
||||
<div class="item-row">
|
||||
<div class="item-col">Title row1</div>
|
||||
<div class="item-col">
|
||||
<ul class="list-content">
|
||||
<li>Nam rhoncus tristique ligula, tincidunt iaculis augue tincidunt ac viverra et a dui.</li>
|
||||
<li>Nam rhoncus tristique ligula, tincidunt iaculis augue tincidunt ac viverra et a dui.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item-row">
|
||||
<div class="item-col">Title row2</div>
|
||||
<div class="item-col">Nam rhoncus tristique ligula, tincidunt iaculis augue tincidunt ac viverra et a dui.</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2>Wrap-list</h2>
|
||||
<p>Une liste inline qui s'aligne, puis glisse sous son titre.</p>
|
||||
<div class="wrap-list debug">
|
||||
<div class="wl-row">
|
||||
<div class="wl-col title">Usagers concernés</div>
|
||||
<div class="wl-col list">
|
||||
<p class="wl-item"><a href="#">Gaston Bah</a></p>
|
||||
<p class="wl-item"><a href="#">Alain Bah</a></p>
|
||||
<p class="wl-item"><a href="#">Adèle Gaillot</a></p>
|
||||
<p class="wl-item"><a href="#">Corentine Bah</a></p>
|
||||
<p class="wl-item"><a href="#">Justin Bah</a></p>
|
||||
<p class="wl-item"><a href="#">Michel Sardou</a></p>
|
||||
<p class="wl-item"><a href="#">Carine Rousseau</a></p>
|
||||
<p class="wl-item"><a href="#">Mohamed Martin</a></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="wl-row">
|
||||
<div class="wl-col title">Problématiques sociales</div>
|
||||
<div class="wl-col list">
|
||||
<p class="wl-item"><a href="#">Gaston Bah</a></p>
|
||||
<p class="wl-item"><a href="#">Alain Bah</a></p>
|
||||
<p class="wl-item"><a href="#">Adèle Gaillot</a></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<xmp>
|
||||
<div class="wrap-list">
|
||||
<div class="wl-row">
|
||||
<div class="wl-col title">title</div>
|
||||
<div class="wl-col list">
|
||||
<p class="wl-item">item</p>
|
||||
<p class="wl-item">item</p>
|
||||
...
|
||||
</div>
|
||||
</div>
|
||||
...
|
||||
</div>
|
||||
</xmp>
|
||||
|
||||
<h2>Wrap-header</h2>
|
||||
<p>Réglage d'une zone de titre sur 2 lignes.</p>
|
||||
<div class="wrap-header debug">
|
||||
<div class="wh-row">
|
||||
<div class="wh-col">
|
||||
<span class="h3"><b>Title</b></span>
|
||||
<span class="badge rounded-pill bg-danger">badge</span>
|
||||
</div>
|
||||
<div class="wh-col">
|
||||
<span class="badge rounded-pill bg-primary">badge</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="wh-row">
|
||||
<div class="wh-col">from startdate to enddate</div>
|
||||
<div class="wh-col">text</div>
|
||||
</div>
|
||||
</div>
|
||||
<xmp>
|
||||
<div class="wrap-header">
|
||||
<div class="wh-row">
|
||||
<div class="wh-col">line1 left</div>
|
||||
<div class="wh-col">line1 right</div>
|
||||
</div>
|
||||
<div class="wh-row">
|
||||
<div class="wh-col">line2 left</div>
|
||||
<div class="wh-col">line2 right</div>
|
||||
</div>
|
||||
</div>
|
||||
</xmp>
|
||||
|
||||
<h2>Float-button top</h2>
|
||||
<p>Une zone de bouton flotte à droite d'un contenu. On peut voir en faisant varier la largeur que celui-ci vient s'adapter harmonieusement autour des boutons.</p>
|
||||
<div class="float-button top debug">
|
||||
<div class="box">
|
||||
<div class="action">
|
||||
<ul class="record_actions">
|
||||
<li><button type="button" name="button">Annuler</button></li>
|
||||
<li><button type="button" name="button">Voir</button></li>
|
||||
<li><button type="button" name="button">Enregistrer</button></li>
|
||||
</ul>
|
||||
</div>
|
||||
Nam rhoncus tristique ligula, tincidunt iaculis augue tincidunt ac. Proin fermentum mauris quam, ut suscipit nisl auctor at. Ut vestibulum ligula eget ex congue, efficitur interdum ipsum tincidunt. Integer id sapien et nibh tristique viverra et a dui. Ut blandit pharetra consectetur. Sed scelerisque eget purus at tempus. Etiam sit amet tellus et eros semper tempor. Curabitur suscipit pulvinar enim at lobortis. Ut nisl augue, cursus vel hendrerit sed, posuere vel sapien. Proin hendrerit arcu velit, eu ultrices dui interdum eget. Vestibulum consectetur sodales enim a accumsan. In vitae tristique leo, a fringilla nisl.
|
||||
</div>
|
||||
</div>
|
||||
<xmp>
|
||||
<div class="float-button top">
|
||||
<div class="box">
|
||||
<div class="action">
|
||||
floating button
|
||||
</div>
|
||||
content ...
|
||||
</div>
|
||||
</div>
|
||||
</xmp>
|
||||
|
||||
<h2>Float-button bottom</h2>
|
||||
<p>Avec la même structure, on accroche la zone de bouton en bas, toujours à droite. Voir <a href="https://css-tricks.com/float-an-element-to-the-bottom-corner/">source</a>. </p>
|
||||
<div class="float-button bottom debug">
|
||||
<div class="box">
|
||||
<div class="action">
|
||||
<ul class="record_actions">
|
||||
<li><button type="button" name="button">Annuler</button></li>
|
||||
<li><button type="button" name="button">Voir</button></li>
|
||||
<li><button type="button" name="button">Enregistrer</button></li>
|
||||
</ul>
|
||||
</div>
|
||||
Nam rhoncus tristique ligula, tincidunt iaculis augue tincidunt ac. Proin fermentum mauris quam, ut suscipit nisl auctor at. Ut vestibulum ligula eget ex congue, efficitur interdum ipsum tincidunt. Integer id sapien et nibh tristique viverra et a dui. Ut blandit pharetra consectetur. Sed scelerisque eget purus at tempus. Etiam sit amet tellus et eros semper tempor. Curabitur suscipit pulvinar enim at lobortis. Ut nisl augue, cursus vel hendrerit sed, posuere vel sapien. Proin hendrerit arcu velit, eu ultrices dui interdum eget. Vestibulum consectetur sodales enim a accumsan. In vitae tristique leo, a fringilla nisl.
|
||||
</div>
|
||||
</div>
|
||||
<xmp>
|
||||
<div class="float-button bottom">
|
||||
<div class="box">
|
||||
<div class="action">
|
||||
floating button
|
||||
</div>
|
||||
content ...
|
||||
</div>
|
||||
</div>
|
||||
</xmp>
|
||||
|
||||
|
||||
<h1>Buttons</h1>
|
||||
<ul class="record_actions">
|
||||
<li><a href="#" class="btn btn-submit">submit</a></li>
|
||||
<li><a href="#" class="btn btn-save">save</a></li>
|
||||
<li><a href="#" class="btn btn-create">create</a></li>
|
||||
<li><a href="#" class="btn btn-new">new</a></li>
|
||||
<li><a href="#" class="btn btn-duplicate">duplicate</a></li>
|
||||
<li><a href="#" class="btn btn-not-duplicate">not-duplicate</a></li>
|
||||
<li><a href="#" class="btn btn-reset">reset</a></li>
|
||||
<li><a href="#" class="btn btn-delete">delete</a></li>
|
||||
<li><a href="#" class="btn btn-danger">danger</a></li>
|
||||
<li><a href="#" class="btn btn-remove">remove</a></li>
|
||||
<li><a href="#" class="btn btn-unlink">unlink</a></li>
|
||||
<li><a href="#" class="btn btn-action">action</a></li>
|
||||
<li><a href="#" class="btn btn-edit">edit</a></li>
|
||||
<li><a href="#" class="btn btn-update">update</a></li>
|
||||
<li><a href="#" class="btn btn-show">show</a></li>
|
||||
<li><a href="#" class="btn btn-view">view</a></li>
|
||||
<li><a href="#" class="btn btn-misc">misc</a></li>
|
||||
<li><a href="#" class="btn btn-cancel">cancel</a></li>
|
||||
<li><a href="#" class="btn btn-choose">choose</a></li>
|
||||
<li><a href="#" class="btn btn-notify">notify</a></li>
|
||||
<li><a href="#" class="btn btn-tpchild">tpchild</a></li>
|
||||
<li><a href="#" class="btn btn-chill-beige">my button</a></li>
|
||||
</ul>
|
||||
|
||||
<h2>Variants of <pre>record_actions</pre></h2>
|
||||
|
||||
<h3><pre>small</pre></h3>
|
||||
|
||||
<ul class="record_actions small">
|
||||
<li><a href="#" class="btn btn-create"></a></li>
|
||||
</ul>
|
||||
|
||||
<h3><pre>inline</pre></h3>
|
||||
|
||||
<div>
|
||||
This is inline and small
|
||||
|
||||
<ul class="record_actions small inline">
|
||||
<li><a href="#" class="btn btn-create"></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<xmp><a class="btn btn-submit">Text</a></xmp>
|
||||
Toutes les classes btn-* de bootstrap sont fonctionnelles
|
||||
</div>
|
||||
{% endblock %}
|
@@ -0,0 +1,84 @@
|
||||
{% extends '@ChillMain/layout.html.twig' %}
|
||||
|
||||
{% block title %}
|
||||
SASS Assets Tests - page 1
|
||||
{% endblock %}
|
||||
|
||||
{% block css %}
|
||||
<style media="screen">
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="col-md-8">
|
||||
<h1>CSS Tests - page 1 : float-button</h1>
|
||||
|
||||
<h2>1) avec des li</h2>
|
||||
<div class="float-button bottom debug">
|
||||
<div class="box">
|
||||
<div class="action">
|
||||
<ul class="record_actions">
|
||||
<li><button type="button" name="button">Annuler</button></li>
|
||||
<li><button type="button" name="button">Voir</button></li>
|
||||
<li><button type="button" name="button">Enregistrer</button></li>
|
||||
</ul>
|
||||
</div>
|
||||
<ul class="list-content fa-ul">
|
||||
<li><i class="fa fa-li fa-file-text-o"></i>Sed efficitur magna vel massa efficitur venenatis. Sed odio massa, scelerisque sit amet mauris eu, tristique dictum arcu. Sed posuere, elit eget cursus rhoncus, arcu ligula blandit nisi, in vulputate eros massa non risus.</li>
|
||||
<li><i class="fa fa-li fa-map-marker"></i>
|
||||
<div class="chill-entity entity-address my-3" data-v-8b2170ec="">
|
||||
<div class="address multiline" data-v-8b2170ec="">
|
||||
<p class="street" data-v-8b2170ec="">97, chemin Franck Julien, </p>
|
||||
<p class="postcode" data-v-8b2170ec="">1000 Bruxelles</p>
|
||||
<p class="country" data-v-8b2170ec="">Belgique</p>
|
||||
</div>
|
||||
<div class="address-more" data-v-8b2170ec="">
|
||||
<div data-v-8b2170ec="">
|
||||
<span class="corridor" data-v-8b2170ec="">
|
||||
<b data-v-8b2170ec="">Couloir</b>: 3
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li><i class="fa fa-li fa-mobile"></i><a href="tel: +33 8 27 17 12 19">+33 8 27 17 12 19</a></li>
|
||||
<li><i class="fa fa-li fa-envelope-o"></i><a href="mailto: gusikowski.yesenia@hotmail.com">gusikowski.yesenia@hotmail.com</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<h2>2) avec des p</h2>
|
||||
|
||||
<div class="float-button bottom debug">
|
||||
<div class="box">
|
||||
<div class="action">
|
||||
<ul class="record_actions">
|
||||
<li><button type="button" name="button">Annuler</button></li>
|
||||
<li><button type="button" name="button">Voir</button></li>
|
||||
<li><button type="button" name="button">Enregistrer</button></li>
|
||||
</ul>
|
||||
</div>
|
||||
<p>Voir <a href="https://css-tricks.com/float-an-element-to-the-bottom-corner/">trick</a>.</p>
|
||||
<p>Sed efficitur magna vel massa efficitur venenatis. Sed odio massa, scelerisque sit amet mauris eu, tristique dictum arcu. Sed posuere, elit eget cursus rhoncus, arcu ligula blandit nisi, in vulputate eros massa non risus. Proin lacinia, sapien in pharetra ultricies, justo urna fermentum lectus, non tempor ipsum leo a ante. Aenean porta, ipsum in fringilla hendrerit, nisi justo vestibulum ex, non lacinia risus felis vitae diam. Curabitur sem eros, consectetur a auctor vel, facilisis sit amet sem.</p>
|
||||
<p>Aenean finibus a nisl a scelerisque. Donec bibendum facilisis odio id euismod. Pellentesque luctus justo ligula, eget dictum ligula ultrices quis. Pellentesque at nunc est. Aenean luctus, tortor in lacinia porta, ex nisl dignissim magna, non vehicula elit risus at elit. Suspendisse in velit non augue egestas laoreet. Etiam blandit lacus at semper aliquam. Integer leo nunc, condimentum sagittis accumsan sit amet, consectetur vel massa. Aenean convallis nibh vel augue ullamcorper tempus. Integer eu laoreet sapien.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2>3) avec des div</h2>
|
||||
<div class="float-button bottom debug">
|
||||
<div class="box">
|
||||
<div class="action">
|
||||
<ul class="record_actions">
|
||||
<li><button type="button" name="button">Annuler</button></li>
|
||||
<li><button type="button" name="button">Voir</button></li>
|
||||
<li><button type="button" name="button">Enregistrer</button></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>Voir <a href="https://css-tricks.com/float-an-element-to-the-bottom-corner/">trick</a>.</div>
|
||||
<div>Sed efficitur magna vel massa efficitur venenatis. Sed odio massa, scelerisque sit amet mauris eu, tristique dictum arcu. Sed posuere, elit eget cursus rhoncus, arcu ligula blandit nisi, in vulputate eros massa non risus. Proin lacinia, sapien in pharetra ultricies, justo urna fermentum lectus, non tempor ipsum leo a ante. Aenean porta, ipsum in fringilla hendrerit, nisi justo vestibulum ex, non lacinia risus felis vitae diam.
|
||||
<a href="#">Curabitur</a> sem eros, consectetur a auctor vel, facilisis sit amet sem.</div>
|
||||
<div>Aenean finibus a nisl a scelerisque. Donec bibendum facilisis odio id euismod. Pellentesque luctus justo ligula, eget dictum ligula ultrices quis. Pellentesque at nunc est. Aenean luctus, tortor in lacinia porta, ex nisl dignissim magna, non vehicula elit risus at elit. Suspendisse in velit non augue egestas laoreet. Etiam blandit lacus at semper aliquam. Integer leo nunc, condimentum sagittis accumsan sit amet, consectetur vel massa. Aenean convallis nibh vel augue ullamcorper tempus. Integer eu laoreet sapien.</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
{% endblock %}
|
@@ -0,0 +1,78 @@
|
||||
{% extends '@ChillMain/layout.html.twig' %}
|
||||
|
||||
{% block title %}
|
||||
SASS Assets Tests - page 2
|
||||
{% endblock %}
|
||||
|
||||
{% block css %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="col-md-10">
|
||||
<h1>CSS Tests - page 2: grid layout</h1>
|
||||
|
||||
<h2>1) mgrid 1-2: avec grid-column et grid-row</h2>
|
||||
|
||||
<div class="mgrid debug">
|
||||
<div class="area1">
|
||||
Nam rhoncus tristique ligula, tincidunt iaculis augue tincidunt ac. Proin fermentum mauris quam, ut suscipit nisl auctor at. Ut vestibulum ligula eget ex congue, efficitur interdum ipsum tincidunt. Integer id sapien et nibh tristique viverra et a dui. Ut blandit pharetra consectetur. Sed scelerisque eget purus at tempus. Etiam sit amet tellus et eros semper tempor. Curabitur suscipit pulvinar enim at lobortis. Ut nisl augue, cursus vel hendrerit sed, posuere vel sapien. Proin hendrerit arcu velit, eu ultrices dui interdum eget. Vestibulum consectetur sodales enim a accumsan. In vitae tristique leo, a fringilla nisl.
|
||||
</div>
|
||||
<div class="area2">
|
||||
<ul class="record_actions">
|
||||
<li><button type="button" name="button">Annuler</button></li>
|
||||
<li><button type="button" name="button">Voir</button></li>
|
||||
<li><button type="button" name="button">Enregistrer</button></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2>2) lgrid 3-4: avec grid-template-areas</h2>
|
||||
|
||||
<div class="lgrid debug">
|
||||
<div class="area3">
|
||||
<i>La zone qu'on crée avec les noms doit être rectangulaires. Actuellement, il n'existe pas de méthode pour créer une zone avec une forme de L (bien que la spécification indique qu'une prochaine version pourrait couvrir cette fonctionnalité).
|
||||
[...] Si des zones ne sont pas rectangulaires, cela sera également considéré comme invalide.</i>
|
||||
Voir sur MDN: <a target="_blank" href="https://developer.mozilla.org/fr/docs/Web/CSS/CSS_Grid_Layout/Grid_Template_Areas#occuper_plusieurs_cellules">Définir des zones sur une grille</a>
|
||||
|
||||
</div>
|
||||
<div class="area4">
|
||||
<ul class="record_actions">
|
||||
<li><button type="button" name="button">Annuler</button></li>
|
||||
<li><button type="button" name="button">Voir</button></li>
|
||||
<li><button type="button" name="button">Enregistrer</button></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2>3) cgrid 5-6-7-8: avec masonry</h2>
|
||||
<p>Expérimental: dans FF <i>about:config</i>, il faut mettre <i>layout.css.grid-template-masonry-value.enabled = true</i></p>
|
||||
|
||||
<div class="cgrid debug">
|
||||
<div class="item">
|
||||
1 Integer id sapien et nibh tristique viverra et a dui. Ut blandit pharetra consectetur. Sed scelerisque eget purus at tempus. Etiam sit amet tellus et eros semper tempor. Curabitur suscipit pulvinar enim at lobortis. Ut nisl augue, cursus vel hendrerit sed, posuere vel sapien. Proin hendrerit arcu velit, eu ultrices dui interdum eget. Vestibulum consectetur sodales enim a accumsan. In vitae tristique leo, a fringilla nisl.
|
||||
</div>
|
||||
<div class="item">
|
||||
2 Sed scelerisque eget purus at tempus. Etiam sit amet tellus et eros semper tempor. Curabitur suscipit pulvinar enim at lobortis. Ut nisl augue, cursus vel hendrerit sed, posuere vel sapien. Proin hendrerit arcu velit, eu ultrices dui interdum eget. Vestibulum consectetur sodales enim a accumsan. In vitae tristique leo, a fringilla nisl.
|
||||
</div>
|
||||
<div class="item">
|
||||
3 Curabitur suscipit pulvinar enim at lobortis. Ut nisl augue, cursus vel hendrerit sed, posuere vel sapien. Proin hendrerit arcu velit, eu ultrices dui interdum eget. Vestibulum consectetur sodales enim a accumsan. In vitae tristique leo, a fringilla nisl.
|
||||
</div>
|
||||
<div class="item">
|
||||
4 Proin hendrerit arcu velit, eu ultrices dui interdum eget. Vestibulum consectetur sodales enim a accumsan. In vitae tristique leo, a fringilla nisl.
|
||||
</div>
|
||||
<div class="item">
|
||||
5 Proin hendrerit arcu velit, eu ultrices dui interdum eget. Vestibulum consectetur sodales enim a accumsan.
|
||||
</div>
|
||||
<div class="item">
|
||||
6 Proin hendrerit arcu velit, eu ultrices dui interdum eget. Vestibulum consectetur sodales enim.
|
||||
</div>
|
||||
<div class="item">
|
||||
7 Proin hendrerit arcu velit, eu ultrices dui interdum eget.
|
||||
</div>
|
||||
<div class="item">
|
||||
8 Eu ultrices dui interdum eget.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
{% endblock %}
|
@@ -40,10 +40,10 @@
|
||||
{{ 'by_user'|trans ~ ' ' }}
|
||||
{% endif %}
|
||||
<span class="user">
|
||||
{{ user|chill_entity_render_box(options['user']) }}
|
||||
{{ user|chill_entity_render_box({'at_date': comment.date}) }}
|
||||
</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</blockquote>
|
||||
{{ closing_box|raw }}
|
||||
{{ closing_box|raw }}
|
||||
|
@@ -1,10 +1,10 @@
|
||||
<span class="chill-entity entity-user">
|
||||
{{- user.label }}
|
||||
{%- if opts['user_job'] and user.userJob(opts['at']) is not null %}
|
||||
<span class="user-job">({{ user.userJob(opts['at']).label|localize_translatable_string }})</span>
|
||||
{%- if opts['user_job'] and user.userJob(opts['at_date']) is not null %}
|
||||
<span class="user-job">({{ user.userJob(opts['at_date']).label|localize_translatable_string }})</span>
|
||||
{%- endif -%}
|
||||
{%- if opts['main_scope'] and user.mainScope(opts['at']) is not null %}
|
||||
<span class="main-scope">({{ user.mainScope(opts['at']).name|localize_translatable_string }})</span>
|
||||
{%- if opts['main_scope'] and user.mainScope(opts['at_date']) is not null %}
|
||||
<span class="main-scope">({{ user.mainScope(opts['at_date']).name|localize_translatable_string }})</span>
|
||||
{%- endif -%}
|
||||
{%- if opts['absence'] and user.isAbsent %}
|
||||
<span class="badge bg-danger rounded-pill" title="{{ 'absence.Absent'|trans|escape('html_attr') }}">{{ 'absence.A'|trans }}</span>
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user