Compare commits

..

46 Commits

Author SHA1 Message Date
966f9f7e33 Release v4.13.0 2026-02-23 17:13:24 +01:00
7a5300b713 Merge branch '495-fix-quote-notification-email' into 'master'
Remove unused method `sendNotificationEmailsToAddressesEmails` from `NotificationMailer`

Closes #495

See merge request Chill-Projet/chill-bundles!967
2026-02-23 15:49:39 +00:00
dc3a585e5b Remove unused method sendNotificationEmailsToAddressesEmails from NotificationMailer 2026-02-23 15:49:39 +00:00
7712d76889 Merge branch '494-titre-toute-la-journée-tronqué-sur-la-page-mes-rendez-vous' into 'master'
Resolve "Titre 'Toute la journée' tronqué sur la page Mes Rendez-vous"

Closes #494

See merge request Chill-Projet/chill-bundles!965
2026-02-23 15:08:48 +00:00
Boris Waaub
69bb7026c9 Resolve "Titre 'Toute la journée' tronqué sur la page Mes Rendez-vous" 2026-02-23 15:08:48 +00:00
acd7240903 Merge branch '501-fix-deprecation-markdown-parser' into 'master'
Resolve "Depréciation dans le paquet de transformation markdown"

Closes #501

See merge request Chill-Projet/chill-bundles!966
2026-02-23 14:50:15 +00:00
22049558da Resolve "Depréciation dans le paquet de transformation markdown" 2026-02-23 14:50:14 +00:00
c0f2f3f3e0 Merge branch '500-limit-public-download' into 'master'
Resolve "Téléchargement des documents d'un workflow: limiter à 30 téléchargements plutôt que 100"

Closes #500

See merge request Chill-Projet/chill-bundles!964
2026-02-23 14:24:53 +00:00
bf56b3cc65 Resolve "Téléchargement des documents d'un workflow: limiter à 30 téléchargements plutôt que 100" 2026-02-23 14:24:53 +00:00
f85973f7ae Merge branch '499-fix-loading-postal-code' into 'master'
Resolve "Des codes postaux marqués comme supprimés apparaissent toujours dans la recherche d'adresse"

Closes #499

See merge request Chill-Projet/chill-bundles!963
2026-02-23 14:16:46 +00:00
f1446d7abe Resolve "Des codes postaux marqués comme supprimés apparaissent toujours dans la recherche d'adresse" 2026-02-23 14:16:45 +00:00
76d675ac02 Fixed translations of address in exports (addresse -> adresse) 2026-02-17 14:11:15 +01:00
cf0a2b7393 Merge branch '438-parcours-designer-comme-adresse-du-parcours-to-be-green' into 'master'
Resolve "Parcours - "Désigner comme adresse du parcours" to be green"

Closes #438

See merge request Chill-Projet/chill-bundles!958
2026-02-12 08:50:00 +00:00
Boris Waaub
80b05a8133 Resolve "Parcours - "Désigner comme adresse du parcours" to be green" 2026-02-12 08:50:00 +00:00
69aba8d9c9 Merge branch 'changie/add-mr-to-question' into 'master'
Changie/add mr to question

See merge request Chill-Projet/chill-bundles!960
2026-02-11 13:27:48 +00:00
a87d936828 Changie/add mr to question 2026-02-11 13:27:48 +00:00
290fa7a77c Merge branch '498-fix-workflow-initiator' into 'master'
Take workflow creator into account when granting edit permissions on documents

Closes #498

See merge request Chill-Projet/chill-bundles!959
2026-02-10 15:05:50 +00:00
0e1d233d79 Take workflow creator into account when granting edit permissions on documents 2026-02-10 15:05:49 +00:00
3402e4863f Release v4.12.1 2026-02-01 18:52:21 +01:00
1f0974ea68 Merge branch 'cs/update-cs-fixer-3.93' into 'master'
Update PHP-CS-Fixer to version 3.93.0 in composer dependencies

See merge request Chill-Projet/chill-bundles!955
2026-01-29 12:27:56 +00:00
9997fb287a Update PHP-CS-Fixer to version 3.93.0 in composer dependencies 2026-01-29 12:27:56 +00:00
f9a9de1148 Merge branch '496-allow-remove-double-refid-ban-address-importer' into 'master'
Adding the option to deal with duplicate addresses in the BAN importer

Closes #496

See merge request Chill-Projet/chill-bundles!954
2026-01-27 10:26:57 +00:00
juminet
c34f720f94 Adding the option to deal with duplicate addresses in the BAN importer 2026-01-27 10:26:57 +00:00
e1b1f592fa Merge branch 'zimbra/use-delegated-admin' into 'master'
[Zimbra] Use admin delegated account for authenticating users against Zimbra

See merge request Chill-Projet/chill-bundles!952
2026-01-22 14:39:46 +00:00
8546f4dadc [Zimbra] Use admin delegated account for authenticating users against Zimbra 2026-01-22 14:39:46 +00:00
4028c020ee Release v4.12.0 2026-01-15 18:02:12 +01:00
0d4eef6a0c Merge branch '493-fix-stored-object-workflow-permission' into 'master'
Fix issues with permission for stored objects associated with workflows

Closes #493

See merge request Chill-Projet/chill-bundles!951
2026-01-15 16:54:37 +00:00
b6152d5356 Fix issues with permission for stored objects associated with workflows 2026-01-15 16:54:37 +00:00
8b708f8c73 fix CommentInput: replace deprecated value binding with model-value 2026-01-15 14:53:40 +01:00
8d5b200107 Restrict ux-translator version to 2.31.0 2026-01-15 14:44:05 +01:00
a9e9207d5a Update php-cs-fixer version 2026-01-15 13:41:00 +01:00
3915574ed4 phpstan error fix 2026-01-15 13:40:46 +01:00
f3217d22ef Fix: acc periods of which user is the referrer should not be included if when the list is filtered by center and none of the participations are part of the center 2026-01-15 13:25:54 +01:00
06c5affbe7 Increase delay for removing stale workflows from 90 to 180 days
- Updated `KEEP_INTERVAL` in `CancelStaleWorkflowCronJob` to `P180D`.
2026-01-15 10:08:40 +01:00
bf461a1211 Merge branch '473-display-bundles-version' into 'master'
Resolve "Afficher le numéro de version de Chill dans l'UX"

Closes #473

See merge request Chill-Projet/chill-bundles!947
2026-01-13 15:35:26 +00:00
3f0ad51114 Resolve "Afficher le numéro de version de Chill dans l'UX" 2026-01-13 15:35:26 +00:00
a4de8eaab3 Merge branch '489-fix-desactivation-date-goarls-results' into 'master'
Fix issue with goal/result deactivation date handling and improve formatting

Closes #489

See merge request Chill-Projet/chill-bundles!949
2026-01-13 15:32:08 +00:00
2feb137ac2 Fix issue with goal/result deactivation date handling and improve formatting 2026-01-13 15:32:07 +00:00
5ea74d118b Merge branch '490-fix-double-notification' into 'master'
Prevent notifications from being sent when the user signs a document he asked to himself

Closes #490

See merge request Chill-Projet/chill-bundles!950
2026-01-13 15:31:50 +00:00
8eb7a55ef5 Prevent notifications from being sent when the user signs a document he asked to himself 2026-01-13 15:31:49 +00:00
281887355f Fix calculation of budget balance 2026-01-12 10:34:30 +01:00
47b285b584 Fix export group by center for persons without a center in CenterAggregator.php 2025-12-30 13:01:56 +01:00
7c9b4d02f6 Fix ordering of social actions
Actions with a closing date in the future should be considered as 'still open'.
2025-12-18 11:08:18 +01:00
3ff9bba4de Fix the condition to display concerned persons in calendar list items. 2025-12-18 10:24:24 +01:00
c0f9e953fb Update to v4.11.0 2025-12-17 16:56:35 +01:00
a49ea2b6b9 Fix translation syntax
Cannot start with %, wrap translation value in double quotes
2025-12-17 16:54:33 +01:00
256 changed files with 2015 additions and 1540 deletions

View File

@@ -1,6 +0,0 @@
kind: Feature
body: 'Add filtering to admin lists: social actions, social issues, goals, results, and evaluations'
time: 2025-12-10T03:20:45.135973502+01:00
custom:
Issue: "478"
SchemaChange: No schema change

View File

@@ -1,7 +0,0 @@
kind: Fixed
body: |
Fix migration query after previous fix
time: 2025-12-11T21:49:08.899926492+01:00
custom:
Issue: "466"
SchemaChange: No schema change

9
.changes/v4.11.0.md Normal file
View File

@@ -0,0 +1,9 @@
## v4.11.0 - 2025-12-17
### Feature
* ([#478](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/478)) Add filtering to admin lists: social actions, social issues, goals, results, and evaluations
### Fixed
* ([#466](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/466)) Fix migration query after previous fix
* Fix translation key/value
Cannot start with % and should be wrapped in "".

16
.changes/v4.12.0.md Normal file
View File

@@ -0,0 +1,16 @@
## v4.12.0 - 2026-01-15
### Feature
* ([#473](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/473)) Display version of chill bundles in application footer
* Increase the delay before removing stale workflow from 90 days to 180 days.
### Fixed
* ([#480](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/480)) Fix the condition to display concerned persons in calendar list items.
* ([#481](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/481)) Fix ordering of social actions: actions with a closing date in the future should be considered as 'still open'.
* ([#477](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/477)) Fix export group by center for persons without a center in CenterAggregator.php
* Fix the calculation of budget balance to only take into account resources and charges that are still actual
* ([#489](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/489)) Fix desactivation date for Goals and results
* ([#490](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/490)) Prevent sending a notification when the user signs the document himself
* ([#491](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/491)) Fix: acc periods of which user is the referrer should not be included if when the list is filtered by center and none of the participations are part of the center
* ([#492](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/492)) fix CommentInput: replace deprecated value binding with model-value
* ([#493](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/493)) fix issue with stored object permissions associated with workflows (as attachment, or through a related entity)
BC: the constructor's signature of `\Chill\DocStoreBundle\Security\Authorization\StoredObjectVoter\AbstractStoredObjectVoter` has changed.

4
.changes/v4.12.1.md Normal file
View File

@@ -0,0 +1,4 @@
## v4.12.1 - 2026-02-01
### Fixed
* ([#496](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/496)) Add the option to deal with duplicate address in BAN adress importer

15
.changes/v4.13.0.md Normal file
View File

@@ -0,0 +1,15 @@
## v4.13.0 - 2026-02-23
### Feature
* ([#500](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/500)) ([!964](https://gitlab.com/Chill-Projet/chill-bundles/-/merge_requests/964)) Limit the number of public download of stored object to 30 downloads
* ([#495](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/495)) ([!967](https://gitlab.com/Chill-Projet/chill-bundles/-/merge_requests/967)) Send email related to notification in both html and txt format, and render quote correctly
### Fixed
* ([#438](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/438)) Change wrong color of submit button "Désigner comme adresse du parcours"
* ([#498](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/498)) For giving edit permissions on documents, take into account the workflow creator
* Fixed mispelling of address in translations: addresse -> adresse
* ([#499](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/499)) ([!963](https://gitlab.com/Chill-Projet/chill-bundles/-/merge_requests/963)) Fix: some postal code appears in the UI, although they are marked as deleted
* ([#501](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/501)) ([!966](https://gitlab.com/Chill-Projet/chill-bundles/-/merge_requests/966)) Fix deprecation in the markdown rendering
* ([#494](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/494)) ([!965](https://gitlab.com/Chill-Projet/chill-bundles/-/merge_requests/965)) Remove unused all-day slot display
### DX
* ([!960](https://gitlab.com/Chill-Projet/chill-bundles/-/merge_requests/960)) Configure changie to ask for merge request number for a better tracking of changes

View File

@@ -7,7 +7,7 @@ versionFormat: '## {{.Version}} - {{.Time.Format "2006-01-02"}}'
kindFormat: '### {{.Kind}}' kindFormat: '### {{.Kind}}'
# Note: it is possible to add a `.custom.Long` text manually into the yaml file produced by `changie new`. This will add a long description. # Note: it is possible to add a `.custom.Long` text manually into the yaml file produced by `changie new`. This will add a long description.
changeFormat: >- changeFormat: >-
* {{ if not (eq .Custom.Issue "") }}([#{{ .Custom.Issue }}](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/{{ .Custom.Issue }})) {{ end }}{{ .Body }} {{ if and .Custom.SchemaChange (ne .Custom.SchemaChange "No schema change") }} * {{ if not (eq .Custom.Issue "") }}([#{{ .Custom.Issue }}](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/{{ .Custom.Issue }})) {{ end }}{{ if not (eq .Custom.MR "") }}([!{{ .Custom.MR }}](https://gitlab.com/Chill-Projet/chill-bundles/-/merge_requests/{{ .Custom.MR }})) {{ end }}{{ .Body }} {{ if and .Custom.SchemaChange (ne .Custom.SchemaChange "No schema change") }}
**Schema Change**: {{ .Custom.SchemaChange }} **Schema Change**: {{ .Custom.SchemaChange }}
{{- end -}} {{- end -}}
@@ -30,6 +30,12 @@ custom:
type: int type: int
minInt: 1 minInt: 1
- key: MR
label: Merge request number (on chill-bundles repository) (optional)
optional: true
type: int
minInt: 1
body: body:
# allow multiline messages # allow multiline messages
block: true block: true

13
.gitignore vendored
View File

@@ -50,12 +50,15 @@ phpstan.neon
###> symfony/phpunit-bridge ### ###> symfony/phpunit-bridge ###
###< symfony/phpunit-bridge ### ###< symfony/phpunit-bridge ###
###> symfony/webpack-encore-bundle ###
/node_modules/
/public/build/
npm-debug.log
yarn-error.log
###< symfony/webpack-encore-bundle ###
###> friendsofphp/php-cs-fixer ### ###> friendsofphp/php-cs-fixer ###
/.php-cs-fixer.php /.php-cs-fixer.php
/.php-cs-fixer.cache /.php-cs-fixer.cache
###< friendsofphp/php-cs-fixer ### ###< friendsofphp/php-cs-fixer ###
###> pentatrion/vite-bundle ###
/node_modules/
/public/build/
###< pentatrion/vite-bundle ###

View File

@@ -6,6 +6,54 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html),
and is generated by [Changie](https://github.com/miniscruff/changie). and is generated by [Changie](https://github.com/miniscruff/changie).
## v4.13.0 - 2026-02-23
### Feature
* ([#500](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/500)) ([!964](https://gitlab.com/Chill-Projet/chill-bundles/-/merge_requests/964)) Limit the number of public download of stored object to 30 downloads
* ([#495](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/495)) ([!967](https://gitlab.com/Chill-Projet/chill-bundles/-/merge_requests/967)) Send email related to notification in both html and txt format, and render quote correctly
### Fixed
* ([#438](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/438)) Change wrong color of submit button "Désigner comme adresse du parcours"
* ([#498](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/498)) For giving edit permissions on documents, take into account the workflow creator
* Fixed mispelling of address in translations: addresse -> adresse
* ([#499](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/499)) ([!963](https://gitlab.com/Chill-Projet/chill-bundles/-/merge_requests/963)) Fix: some postal code appears in the UI, although they are marked as deleted
* ([#501](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/501)) ([!966](https://gitlab.com/Chill-Projet/chill-bundles/-/merge_requests/966)) Fix deprecation in the markdown rendering
* ([#494](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/494)) ([!965](https://gitlab.com/Chill-Projet/chill-bundles/-/merge_requests/965)) Remove unused all-day slot display
### DX
* ([!960](https://gitlab.com/Chill-Projet/chill-bundles/-/merge_requests/960)) Configure changie to ask for merge request number for a better tracking of changes
## v4.12.1 - 2026-02-01
### Fixed
* ([#496](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/496)) Add the option to deal with duplicate address in BAN adress importer
## v4.12.0 - 2026-01-15
### Feature
* ([#473](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/473)) Display version of chill bundles in application footer
* Increase the delay before removing stale workflow from 90 days to 180 days.
### Fixed
* ([#480](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/480)) Fix the condition to display concerned persons in calendar list items.
* ([#481](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/481)) Fix ordering of social actions: actions with a closing date in the future should be considered as 'still open'.
* ([#477](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/477)) Fix export group by center for persons without a center in CenterAggregator.php
* Fix the calculation of budget balance to only take into account resources and charges that are still actual
* ([#489](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/489)) Fix desactivation date for Goals and results
* ([#490](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/490)) Prevent sending a notification when the user signs the document himself
* ([#491](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/491)) Fix: acc periods of which user is the referrer should not be included if when the list is filtered by center and none of the participations are part of the center
* ([#492](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/492)) fix CommentInput: replace deprecated value binding with model-value
* ([#493](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/493)) fix issue with stored object permissions associated with workflows (as attachment, or through a related entity)
BC: the constructor's signature of `\Chill\DocStoreBundle\Security\Authorization\StoredObjectVoter\AbstractStoredObjectVoter` has changed.
## v4.11.0 - 2025-12-17
### Feature
* ([#478](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/478)) Add filtering to admin lists: social actions, social issues, goals, results, and evaluations
### Fixed
* ([#466](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/466)) Fix migration query after previous fix
* Fix translation key/value
Cannot start with % and should be wrapped in "".
## v4.10.1 - 2025-12-11 ## v4.10.1 - 2025-12-11
### Fixed ### Fixed
* Fix missing translation variable in NewLocation component * Fix missing translation variable in NewLocation component

View File

@@ -21,6 +21,7 @@
"ext-openssl": "*", "ext-openssl": "*",
"ext-redis": "*", "ext-redis": "*",
"ext-zlib": "*", "ext-zlib": "*",
"composer-runtime-api": "*",
"champs-libres/wopi-bundle": "dev-symfony-v5@dev", "champs-libres/wopi-bundle": "dev-symfony-v5@dev",
"champs-libres/wopi-lib": "dev-master@dev", "champs-libres/wopi-lib": "dev-master@dev",
"doctrine/data-fixtures": "^1.8", "doctrine/data-fixtures": "^1.8",
@@ -37,7 +38,6 @@
"ocramius/package-versions": "^1.10 || ^2", "ocramius/package-versions": "^1.10 || ^2",
"odolbeau/phone-number-bundle": "^3.6", "odolbeau/phone-number-bundle": "^3.6",
"ovh/ovh": "^3.0", "ovh/ovh": "^3.0",
"pentatrion/vite-bundle": "^8.2",
"phpoffice/phpspreadsheet": "^1.16", "phpoffice/phpspreadsheet": "^1.16",
"ramsey/uuid-doctrine": "^1.7", "ramsey/uuid-doctrine": "^1.7",
"sensio/framework-extra-bundle": "^5.5", "sensio/framework-extra-bundle": "^5.5",
@@ -83,8 +83,9 @@
"symfony/templating": "^5.4", "symfony/templating": "^5.4",
"symfony/translation": "^5.4", "symfony/translation": "^5.4",
"symfony/twig-bundle": "^5.4", "symfony/twig-bundle": "^5.4",
"symfony/ux-translator": "^2.22", "symfony/ux-translator": "2.31.0",
"symfony/validator": "^5.4", "symfony/validator": "^5.4",
"symfony/webpack-encore-bundle": "^1.11",
"symfony/workflow": "^5.4", "symfony/workflow": "^5.4",
"symfony/yaml": "^5.4", "symfony/yaml": "^5.4",
"thenetworg/oauth2-azure": "^2.0", "thenetworg/oauth2-azure": "^2.0",
@@ -97,7 +98,7 @@
"require-dev": { "require-dev": {
"doctrine/doctrine-fixtures-bundle": "^3.3", "doctrine/doctrine-fixtures-bundle": "^3.3",
"fakerphp/faker": "^1.13", "fakerphp/faker": "^1.13",
"friendsofphp/php-cs-fixer": "3.65.0", "friendsofphp/php-cs-fixer": "3.93.0",
"jangregor/phpstan-prophecy": "^1.0", "jangregor/phpstan-prophecy": "^1.0",
"nelmio/alice": "^3.8", "nelmio/alice": "^3.8",
"nikic/php-parser": "^4.15", "nikic/php-parser": "^4.15",

View File

@@ -17,6 +17,7 @@ return [
Symfony\Bundle\DebugBundle\DebugBundle::class => ['dev' => true], Symfony\Bundle\DebugBundle\DebugBundle::class => ['dev' => true],
Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true], Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true],
Symfony\Bundle\MonologBundle\MonologBundle::class => ['all' => true], Symfony\Bundle\MonologBundle\MonologBundle::class => ['all' => true],
Symfony\WebpackEncoreBundle\WebpackEncoreBundle::class => ['all' => true],
Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true], Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true],
Twig\Extra\TwigExtraBundle\TwigExtraBundle::class => ['all' => true], Twig\Extra\TwigExtraBundle\TwigExtraBundle::class => ['all' => true],
Chill\ActivityBundle\ChillActivityBundle::class => ['all' => true], Chill\ActivityBundle\ChillActivityBundle::class => ['all' => true],
@@ -36,5 +37,4 @@ return [
Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true], Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true],
Symfony\UX\Translator\UxTranslatorBundle::class => ['all' => true], Symfony\UX\Translator\UxTranslatorBundle::class => ['all' => true],
loophp\PsrHttpMessageBridgeBundle\PsrHttpMessageBridgeBundle::class => ['all' => true], loophp\PsrHttpMessageBridgeBundle\PsrHttpMessageBridgeBundle::class => ['all' => true],
Pentatrion\ViteBundle\PentatrionViteBundle::class => ['all' => true],
]; ];

View File

@@ -1,8 +1,6 @@
framework: framework:
assets: assets:
# Disable Encore-style manifest for default assets package. Vite (Pentatrion) uses public/build/.vite/manifest.json json_manifest_path: '%kernel.project_dir%/public/build/manifest.json'
# internally for vite_entry_* helpers; Symfony's asset() should not depend on a manifest.
json_manifest_path: null
when@test: when@test:
framework: framework:

View File

@@ -0,0 +1,41 @@
webpack_encore:
# The path where Encore is building the assets - i.e. Encore.setOutputPath()
output_path: '%kernel.project_dir%/public/build'
# If multiple builds are defined (as shown below), you can disable the default build:
# output_path: false
# Set attributes that will be rendered on all script and link tags
script_attributes:
defer: true
# Uncomment (also under link_attributes) if using Turbo Drive
# https://turbo.hotwired.dev/handbook/drive#reloading-when-assets-change
# 'data-turbo-track': reload
# link_attributes:
# Uncomment if using Turbo Drive
# 'data-turbo-track': reload
# If using Encore.enableIntegrityHashes() and need the crossorigin attribute (default: false, or use 'anonymous' or 'use-credentials')
# crossorigin: 'anonymous'
# Preload all rendered script and link tags automatically via the HTTP/2 Link header
# preload: true
# Throw an exception if the entrypoints.json file is missing or an entry is missing from the data
# strict_mode: false
# If you have multiple builds:
# builds:
# frontend: '%kernel.project_dir%/public/frontend/build'
# pass the build name as the 3rd argument to the Twig functions
# {{ encore_entry_script_tags('entry1', null, 'frontend') }}
#when@prod:
# webpack_encore:
# # Cache the entrypoints.json (rebuild Symfony's cache when entrypoints.json changes)
# # Available in version 1.2
# cache: true
when@test:
webpack_encore:
strict_mode: false

View File

@@ -1,9 +0,0 @@
when@dev:
_pentatrion_vite:
prefix: /build
resource: "@PentatrionViteBundle/Resources/config/routing.yaml"
_profiler_vite:
path: /_profiler/vite
defaults:
_controller: Pentatrion\ViteBundle\Controller\ProfilerController::info

View File

@@ -1,15 +1,6 @@
{ {
"name": "chill-bundles", "name": "chill",
"private": true,
"version": "2.0.0", "version": "2.0.0",
"description": "Chill web application frontend (Vite build at repo root)",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview",
"eslint": "eslint . --ext .js,.ts,.vue"
},
"devDependencies": { "devDependencies": {
"@alexlafroscia/yaml-merge": "^4.0.0", "@alexlafroscia/yaml-merge": "^4.0.0",
"@apidevtools/swagger-cli": "^4.0.4", "@apidevtools/swagger-cli": "^4.0.4",
@@ -20,11 +11,11 @@
"@hotwired/stimulus": "^3.0.0", "@hotwired/stimulus": "^3.0.0",
"@luminateone/eslint-baseline": "^1.0.9", "@luminateone/eslint-baseline": "^1.0.9",
"@symfony/stimulus-bridge": "^3.2.0", "@symfony/stimulus-bridge": "^3.2.0",
"@symfony/webpack-encore": "^4.1.0",
"@tsconfig/node20": "^20.1.4", "@tsconfig/node20": "^20.1.4",
"@types/dompurify": "^3.0.5", "@types/dompurify": "^3.0.5",
"@types/eslint__js": "^8.42.3", "@types/eslint__js": "^8.42.3",
"@typescript-eslint/parser": "^8.12.2", "@typescript-eslint/parser": "^8.12.2",
"@vitejs/plugin-vue": "^5.1.4",
"bindings": "^1.5.0", "bindings": "^1.5.0",
"bootstrap": "^5.3.6", "bootstrap": "^5.3.6",
"chokidar": "^3.5.1", "chokidar": "^3.5.1",
@@ -49,9 +40,9 @@
"ts-loader": "^9.3.1", "ts-loader": "^9.3.1",
"typescript": "^5.6.3", "typescript": "^5.6.3",
"typescript-eslint": "^8.13.0", "typescript-eslint": "^8.13.0",
"vite": "^5.4.8", "vue-loader": "^17.0.0",
"vite-plugin-symfony": "^8", "webpack": "^5.75.0",
"vue-loader": "^17.0.0" "webpack-cli": "^5.0.1"
}, },
"dependencies": { "dependencies": {
"@fullcalendar/core": "^6.1.4", "@fullcalendar/core": "^6.1.4",
@@ -80,5 +71,21 @@
"vue-multiselect": "3.0.0-alpha.2", "vue-multiselect": "3.0.0-alpha.2",
"vue-toast-notification": "^3.1.2", "vue-toast-notification": "^3.1.2",
"vuex": "^4.0.0" "vuex": "^4.0.0"
} },
"browserslist": [
"defaults and fully supports es6-module and not dead"
],
"scripts": {
"dev-server": "encore dev-server",
"dev": "encore dev",
"watch": "encore dev --watch",
"build": "encore production --progress",
"specs-build": "yaml-merge src/Bundle/ChillMainBundle/chill.api.specs.yaml src/Bundle/ChillPersonBundle/chill.api.specs.yaml src/Bundle/ChillCalendarBundle/chill.api.specs.yaml src/Bundle/ChillThirdPartyBundle/chill.api.specs.yaml src/Bundle/ChillDocStoreBundle/chill.api.specs.yaml> templates/api/specs.yaml",
"specs-validate": "swagger-cli validate templates/api/specs.yaml",
"specs-create-dir": "mkdir -p templates/api",
"specs": "yarn run specs-create-dir && yarn run specs-build && yarn run specs-validate",
"version": "node --version",
"eslint": "npx eslint-baseline --fix \"src/**/*.{js,ts,vue}\""
},
"private": true
} }

View File

@@ -0,0 +1,3 @@
kind: Added
body: Use admin delegated account for handling authentication
time: 2026-01-22T15:32:23.932994899+01:00

View File

@@ -80,12 +80,19 @@ final readonly class CreateZimbraComponent
$location = $calendar->getCalendar()->getLocation(); $location = $calendar->getCalendar()->getLocation();
$hasLocation = $calendar->getCalendar()->hasLocation(); $hasLocation = $calendar->getCalendar()->hasLocation();
$isPrivate = $calendar->getCalendar()->getAccompanyingPeriod()?->isConfidential() ?? false; $isPrivate = $calendar->getCalendar()->getAccompanyingPeriod()?->isConfidential() ?? false;
} else { } elseif ($calendar instanceof Calendar) {
$startDate = $calendar->getStartDate(); $startDate = $calendar->getStartDate();
$endDate = $calendar->getEndDate(); $endDate = $calendar->getEndDate();
$location = $calendar->getLocation(); $location = $calendar->getLocation();
$hasLocation = $calendar->hasLocation(); $hasLocation = $calendar->hasLocation();
$isPrivate = $calendar->getAccompanyingPeriod()?->isConfidential() ?? false; $isPrivate = $calendar->getAccompanyingPeriod()?->isConfidential() ?? false;
} else {
// Calendar range case
$startDate = $calendar->getStartDate();
$endDate = $calendar->getEndDate();
$location = $calendar->getLocation();
$hasLocation = $calendar->hasLocation();
$isPrivate = false;
} }
$comp = new InviteComponent(); $comp = new InviteComponent();

View File

@@ -11,48 +11,84 @@ declare(strict_types=1);
namespace Chill\ZimbraBundle\Calendar\Connector\ZimbraConnector; namespace Chill\ZimbraBundle\Calendar\Connector\ZimbraConnector;
use Symfony\Component\Clock\ClockInterface;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
use Symfony\Component\HttpClient\Psr18Client; use Symfony\Component\HttpClient\Psr18Client;
use Symfony\Contracts\HttpClient\HttpClientInterface; use Symfony\Contracts\HttpClient\HttpClientInterface;
use Zimbra\Admin\AdminApi;
use Zimbra\Common\Enum\AccountBy; use Zimbra\Common\Enum\AccountBy;
use Zimbra\Common\Soap\ClientFactory; use Zimbra\Common\Soap\ClientFactory;
use Zimbra\Common\Struct\AccountSelector;
use Zimbra\Common\Struct\Header\AccountInfo; use Zimbra\Common\Struct\Header\AccountInfo;
use Zimbra\Mail\MailApi; use Zimbra\Mail\MailApi;
final readonly class SoapClientBuilder final class SoapClientBuilder
{ {
private string $username; private readonly string $username;
private string $password; private readonly string $password;
private string $url; private readonly string $url;
public function __construct(private ParameterBagInterface $parameterBag, private HttpClientInterface $client) private readonly string $adminUrl;
{
private readonly bool $verifyHost;
private readonly bool $verifyPeer;
private readonly bool $adminVerifyHost;
private readonly bool $adminVerifyPeer;
/**
* Keep the cache of the tokens.
*
* @var array<string, array{token: string, expirationTime: \DateTimeImmutable}>
*/
private array $tokenCache = [];
public function __construct(
private readonly ParameterBagInterface $parameterBag,
private readonly HttpClientInterface $client,
private readonly ClockInterface $clock,
) {
$dsn = $this->parameterBag->get('chill_calendar.remote_calendar_dsn'); $dsn = $this->parameterBag->get('chill_calendar.remote_calendar_dsn');
$url = parse_url($dsn); $url = parse_url($dsn);
$this->username = urldecode($url['user']); $this->username = urldecode($url['user']);
$this->password = urldecode($url['pass']); $this->password = urldecode($url['pass']);
if ('zimbra+http' === $url['scheme']) { if ('zimbra+http' === $url['scheme']) {
$scheme = 'http://'; $scheme = 'http';
$port = $url['port'] ?? 80; $port = $url['port'] ?? 80;
} elseif ('zimbra+https' === $url['scheme']) { } elseif ('zimbra+https' === $url['scheme']) {
$scheme = 'https://'; $scheme = 'https';
$port = $url['port'] ?? 443; $port = $url['port'] ?? 443;
} else { } else {
throw new \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException('Unsupported remote calendar scheme: '.$url['scheme']); throw new \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException('Unsupported remote calendar scheme: '.$url['scheme']);
} }
$this->url = $scheme.$url['host'].':'.$port; // get attributes for adminUrl
$query = [];
parse_str($url['query'] ?? '', $query);
$adminPort = $query['adminPort'] ?? '7071';
$adminHost = $query['adminHost'] ?? $url['host'];
$adminScheme = $query['adminScheme'] ?? $scheme;
$this->verifyPeer = (bool) ($query['verifyPeer'] ?? true);
$this->verifyHost = (bool) ($query['verifyHost'] ?? true);
$this->adminVerifyHost = (bool) ($query['adminVerifyHost'] ?? $this->verifyPeer);
$this->adminVerifyPeer = (bool) ($query['adminVerifyPeer'] ?? $this->verifyHost);
$this->url = $scheme.'://'.$url['host'].':'.$port;
$this->adminUrl = $adminScheme.'://'.$adminHost.':'.$adminPort;
} }
private function buildApi(): MailApi private function buildApi(): MailApi
{ {
$baseClient = $this->client->withOptions([ $baseClient = $this->client->withOptions([
'base_uri' => $location = $this->url.'/service/soap', 'base_uri' => $location = $this->url.'/service/soap',
'verify_host' => false, 'verify_host' => $this->verifyHost,
'verify_peer' => false, 'verify_peer' => $this->verifyPeer,
]); ]);
$psr18Client = new Psr18Client($baseClient); $psr18Client = new Psr18Client($baseClient);
$api = new MailApi(); $api = new MailApi();
@@ -62,12 +98,36 @@ final readonly class SoapClientBuilder
return $api; return $api;
} }
private function buildAdminApi(): AdminApi
{
$baseClient = $this->client->withOptions([
'base_uri' => $location = $this->adminUrl.'/service/admin/soap',
'verify_host' => $this->adminVerifyHost,
'verify_peer' => $this->adminVerifyPeer,
]);
$psr18Client = new Psr18Client($baseClient);
$api = new AdminApi();
$client = ClientFactory::create($location, $psr18Client);
$api->setClient($client);
return $api;
}
public function getApiForAccount(string $accountName): MailApi public function getApiForAccount(string $accountName): MailApi
{ {
$api = $this->buildApi(); ['token' => $token, 'expirationTime' => $expirationTime] = $this->tokenCache[$accountName]
$response = $api->authByAccountName($this->username, $this->password); ?? ['token' => null, 'expirationTime' => null];
$token = $response->getAuthToken(); if (null === $token || null === $expirationTime || $expirationTime <= $this->clock->now()) {
$adminApi = $this->buildAdminApi();
$adminApi->auth($this->username, $this->password);
$delegateResponse = $adminApi->delegateAuth(new AccountSelector(AccountBy::NAME, $accountName));
$token = $delegateResponse->getAuthToken();
$expiration = $delegateResponse->getLifetime();
$expirationTime = $this->clock->now()->add(new \DateInterval('PT'.$expiration.'S'));
$this->tokenCache[$accountName] = ['token' => $token, 'expirationTime' => $expirationTime];
}
$apiBy = $this->buildApi(); $apiBy = $this->buildApi();
$apiBy->setAuthToken($token); $apiBy->setAuthToken($token);

View File

@@ -69,7 +69,7 @@ class ChillActivityExtension extends Extension implements PrependExtensionInterf
} }
/** (non-PHPdoc). /** (non-PHPdoc).
* @see \Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface::prepend() * @see PrependExtensionInterface::prepend()
*/ */
public function prependRoutes(ContainerBuilder $container) public function prependRoutes(ContainerBuilder $container)
{ {

View File

@@ -1 +1 @@
import "./chillactivity.scss"; require("./chillactivity.scss");

View File

@@ -124,9 +124,9 @@
{# {{ form(delete_form) }} #} {# {{ form(delete_form) }} #}
{% block js %} {% block js %}
{{ vite_entry_script_tags('mod_pickentity_type') }} {{ encore_entry_script_tags('mod_pickentity_type') }}
{% endblock %} {% endblock %}
{% block css %} {% block css %}
{{ vite_entry_link_tags('mod_pickentity_type') }} {{ encore_entry_link_tags('mod_pickentity_type') }}
{% endblock %} {% endblock %}

View File

@@ -15,7 +15,7 @@
{% block js %} {% block js %}
{{ parent() }} {{ parent() }}
{{ vite_entry_script_tags('mod_async_upload') }} {{ encore_entry_script_tags('mod_async_upload') }}
<script type="text/javascript"> <script type="text/javascript">
window.addEventListener('DOMContentLoaded', function (e) { window.addEventListener('DOMContentLoaded', function (e) {
chill.displayAlertWhenLeavingModifiedForm('form[name="{{ edit_form.vars.form.vars.name }}"]', chill.displayAlertWhenLeavingModifiedForm('form[name="{{ edit_form.vars.form.vars.name }}"]',
@@ -23,12 +23,12 @@
}); });
window.activity = {{ activity_json|json_encode|raw }}; window.activity = {{ activity_json|json_encode|raw }};
</script> </script>
{{ vite_entry_script_tags('vue_activity') }} {{ encore_entry_script_tags('vue_activity') }}
{% endblock %} {% endblock %}
{% block css %} {% block css %}
{{ parent() }} {{ parent() }}
{{ vite_entry_link_tags('mod_async_upload') }} {{ encore_entry_link_tags('mod_async_upload') }}
{{ vite_entry_link_tags('vue_activity') }} {{ encore_entry_link_tags('vue_activity') }}
{{ vite_entry_link_tags('page_edit_activity') }} {{ encore_entry_link_tags('page_edit_activity') }}
{% endblock %} {% endblock %}

View File

@@ -30,7 +30,7 @@
{% endblock %} {% endblock %}
{% block js %} {% block js %}
{{ vite_entry_script_tags('mod_async_upload') }} {{ encore_entry_script_tags('mod_async_upload') }}
<script type="text/javascript"> <script type="text/javascript">
window.addEventListener('DOMContentLoaded', function (e) { window.addEventListener('DOMContentLoaded', function (e) {
chill.displayAlertWhenLeavingModifiedForm('form[name="{{ edit_form.vars.form.vars.name }}"]', chill.displayAlertWhenLeavingModifiedForm('form[name="{{ edit_form.vars.form.vars.name }}"]',
@@ -38,10 +38,10 @@
}); });
window.activity = {{ activity_json|json_encode|raw }}; window.activity = {{ activity_json|json_encode|raw }};
</script> </script>
{{ vite_entry_script_tags('vue_activity') }} {{ encore_entry_script_tags('vue_activity') }}
{% endblock %} {% endblock %}
{% block css %} {% block css %}
{{ vite_entry_link_tags('mod_async_upload') }} {{ encore_entry_link_tags('mod_async_upload') }}
{{ vite_entry_link_tags('vue_activity') }} {{ encore_entry_link_tags('vue_activity') }}
{% endblock %} {% endblock %}

View File

@@ -7,14 +7,14 @@
{% block js %} {% block js %}
{{ parent() }} {{ parent() }}
{{ vite_entry_script_tags('mod_notification_toggle_read_status') }} {{ encore_entry_script_tags('mod_notification_toggle_read_status') }}
{{ vite_entry_script_tags('mod_document_action_buttons_group') }} {{ encore_entry_script_tags('mod_document_action_buttons_group') }}
{% endblock %} {% endblock %}
{% block css %} {% block css %}
{{ parent() }} {{ parent() }}
{{ vite_entry_link_tags('mod_notification_toggle_read_status') }} {{ encore_entry_link_tags('mod_notification_toggle_read_status') }}
{{ vite_entry_link_tags('mod_document_action_buttons_group') }} {{ encore_entry_link_tags('mod_document_action_buttons_group') }}
{% endblock %} {% endblock %}
{% block content %} {% block content %}

View File

@@ -22,14 +22,14 @@
{% block js %} {% block js %}
{{ parent() }} {{ parent() }}
{{ vite_entry_script_tags('mod_notification_toggle_read_status') }} {{ encore_entry_script_tags('mod_notification_toggle_read_status') }}
{{ vite_entry_script_tags('mod_document_action_buttons_group') }} {{ encore_entry_script_tags('mod_document_action_buttons_group') }}
{% endblock %} {% endblock %}
{% block css %} {% block css %}
{{ parent() }} {{ parent() }}
{{ vite_entry_link_tags('mod_notification_toggle_read_status') }} {{ encore_entry_link_tags('mod_notification_toggle_read_status') }}
{{ vite_entry_link_tags('mod_document_action_buttons_group') }} {{ encore_entry_link_tags('mod_document_action_buttons_group') }}
{% endblock %} {% endblock %}
{% block content %} {% block content %}

View File

@@ -121,9 +121,9 @@
{{ form_end(form) }} {{ form_end(form) }}
{% block js %} {% block js %}
{{ vite_entry_script_tags('mod_pickentity_type') }} {{ encore_entry_script_tags('mod_pickentity_type') }}
{% endblock %} {% endblock %}
{% block css %} {% block css %}
{{ vite_entry_link_tags('mod_pickentity_type') }} {{ encore_entry_link_tags('mod_pickentity_type') }}
{% endblock %} {% endblock %}

View File

@@ -15,16 +15,16 @@
{% block js %} {% block js %}
{{ parent() }} {{ parent() }}
{{ vite_entry_script_tags('mod_async_upload') }} {{ encore_entry_script_tags('mod_async_upload') }}
<script type="text/javascript"> <script type="text/javascript">
window.activity = {{ activity_json|json_encode|raw }}; window.activity = {{ activity_json|json_encode|raw }};
{% if default_location is not null %}window.default_location_id = {{ default_location.id }}{% endif %}; {% if default_location is not null %}window.default_location_id = {{ default_location.id }}{% endif %};
</script> </script>
{{ vite_entry_script_tags('vue_activity') }} {{ encore_entry_script_tags('vue_activity') }}
{% endblock %} {% endblock %}
{% block css %} {% block css %}
{{ parent() }} {{ parent() }}
{{ vite_entry_link_tags('mod_async_upload') }} {{ encore_entry_link_tags('mod_async_upload') }}
{{ vite_entry_link_tags('vue_activity') }} {{ encore_entry_link_tags('vue_activity') }}
{% endblock %} {% endblock %}

View File

@@ -14,7 +14,7 @@
{% endblock %} {% endblock %}
{% block js %} {% block js %}
{{ vite_entry_script_tags('mod_async_upload') }} {{ encore_entry_script_tags('mod_async_upload') }}
<script type="text/javascript"> <script type="text/javascript">
window.addEventListener('DOMContentLoaded', function (e) { window.addEventListener('DOMContentLoaded', function (e) {
chill.displayAlertWhenLeavingUnsubmittedForm('form[name="{{ form.vars.form.vars.name }}"]', chill.displayAlertWhenLeavingUnsubmittedForm('form[name="{{ form.vars.form.vars.name }}"]',
@@ -22,10 +22,10 @@
}); });
window.activity = {{ activity_json|json_encode|raw }}; window.activity = {{ activity_json|json_encode|raw }};
</script> </script>
{{ vite_entry_script_tags('vue_activity') }} {{ encore_entry_script_tags('vue_activity') }}
{% endblock %} {% endblock %}
{% block css %} {% block css %}
{{ vite_entry_link_tags('mod_async_upload') }} {{ encore_entry_link_tags('mod_async_upload') }}
{{ vite_entry_link_tags('vue_activity') }} {{ encore_entry_link_tags('vue_activity') }}
{% endblock %} {% endblock %}

View File

@@ -6,16 +6,16 @@
{% block js %} {% block js %}
{{ parent() }} {{ parent() }}
{{ vite_entry_script_tags('mod_notification_toggle_read_status') }} {{ encore_entry_script_tags('mod_notification_toggle_read_status') }}
{{ vite_entry_script_tags('mod_async_upload') }} {{ encore_entry_script_tags('mod_async_upload') }}
{{ vite_entry_script_tags('mod_document_action_buttons_group') }} {{ encore_entry_script_tags('mod_document_action_buttons_group') }}
{% endblock %} {% endblock %}
{% block css %} {% block css %}
{{ parent() }} {{ parent() }}
{{ vite_entry_link_tags('mod_notification_toggle_read_status') }} {{ encore_entry_link_tags('mod_notification_toggle_read_status') }}
{{ vite_entry_link_tags('mod_async_upload') }} {{ encore_entry_link_tags('mod_async_upload') }}
{{ vite_entry_link_tags('mod_document_action_buttons_group') }} {{ encore_entry_link_tags('mod_document_action_buttons_group') }}
{% endblock %} {% endblock %}
{% import '@ChillActivity/ActivityReason/macro.html.twig' as m %} {% import '@ChillActivity/ActivityReason/macro.html.twig' as m %}

View File

@@ -6,14 +6,14 @@
{% block js %} {% block js %}
{{ parent() }} {{ parent() }}
{{ vite_entry_script_tags('mod_notification_toggle_read_status') }} {{ encore_entry_script_tags('mod_notification_toggle_read_status') }}
{{ vite_entry_script_tags('mod_document_action_buttons_group') }} {{ encore_entry_script_tags('mod_document_action_buttons_group') }}
{% endblock %} {% endblock %}
{% block css %} {% block css %}
{{ parent() }} {{ parent() }}
{{ vite_entry_link_tags('mod_notification_toggle_read_status') }} {{ encore_entry_link_tags('mod_notification_toggle_read_status') }}
{{ vite_entry_link_tags('mod_document_action_buttons_group') }} {{ encore_entry_link_tags('mod_document_action_buttons_group') }}
{% endblock %} {% endblock %}
{% import '@ChillActivity/ActivityReason/macro.html.twig' as m %} {% import '@ChillActivity/ActivityReason/macro.html.twig' as m %}

View File

@@ -16,7 +16,8 @@ use Chill\ActivityBundle\Repository\ActivityRepository;
use Chill\DocStoreBundle\Repository\AssociatedEntityToStoredObjectInterface; use Chill\DocStoreBundle\Repository\AssociatedEntityToStoredObjectInterface;
use Chill\DocStoreBundle\Security\Authorization\StoredObjectRoleEnum; use Chill\DocStoreBundle\Security\Authorization\StoredObjectRoleEnum;
use Chill\DocStoreBundle\Security\Authorization\StoredObjectVoter\AbstractStoredObjectVoter; use Chill\DocStoreBundle\Security\Authorization\StoredObjectVoter\AbstractStoredObjectVoter;
use Chill\MainBundle\Workflow\Helper\WorkflowRelatedEntityPermissionHelper; use Chill\MainBundle\Repository\Workflow\EntityWorkflowAttachmentRepository;
use Chill\MainBundle\Workflow\Helper\WorkflowRelatedEntityPermissionHelperInterface;
use Symfony\Component\Security\Core\Security; use Symfony\Component\Security\Core\Security;
class ActivityStoredObjectVoter extends AbstractStoredObjectVoter class ActivityStoredObjectVoter extends AbstractStoredObjectVoter
@@ -24,9 +25,10 @@ class ActivityStoredObjectVoter extends AbstractStoredObjectVoter
public function __construct( public function __construct(
private readonly ActivityRepository $repository, private readonly ActivityRepository $repository,
Security $security, Security $security,
WorkflowRelatedEntityPermissionHelper $workflowDocumentService, WorkflowRelatedEntityPermissionHelperInterface $workflowDocumentService,
EntityWorkflowAttachmentRepository $attachmentRepository,
) { ) {
parent::__construct($security, $workflowDocumentService); parent::__construct($security, $attachmentRepository, $workflowDocumentService);
} }
protected function getRepository(): AssociatedEntityToStoredObjectInterface protected function getRepository(): AssociatedEntityToStoredObjectInterface

View File

@@ -0,0 +1,18 @@
// this file loads all assets from the Chill person bundle
module.exports = function (encore, entries) {
entries.push(__dirname + "/Resources/public/chill/index.js");
encore.addAliases({
ChillActivityAssets: __dirname + "/Resources/public",
});
encore.addEntry(
"page_edit_activity",
__dirname + "/Resources/public/page/edit_activity/index.scss",
);
encore.addEntry(
"vue_activity",
__dirname + "/Resources/public/vuejs/Activity/index.js",
);
};

View File

@@ -1,14 +0,0 @@
export default {
chillBase: [
"./src/Bundle/ChillActivityBundle/Resources/public/chill/index.js",
],
aliases: {
ChillActivityAssets: "./src/Bundle/ChillActivityBundle/Resources/public",
},
inputs: {
page_edit_activity:
"./src/Bundle/ChillActivityBundle/Resources/public/page/edit_activity/index.scss",
vue_activity:
"./src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/index.js",
},
};

View File

@@ -2,12 +2,12 @@
{% block js %} {% block js %}
{{ parent() }} {{ parent() }}
{{ vite_entry_script_tags('mod_pickentity_type') }} {{ encore_entry_script_tags('mod_pickentity_type') }}
{% endblock %} {% endblock %}
{% block css %} {% block css %}
{{ parent() }} {{ parent() }}
{{ vite_entry_link_tags('mod_pickentity_type') }} {{ encore_entry_link_tags('mod_pickentity_type') }}
{% endblock %} {% endblock %}
{% block title %} {% block title %}

View File

@@ -56,7 +56,7 @@ class ChillBudgetExtension extends Extension implements PrependExtensionInterfac
} }
/** (non-PHPdoc). /** (non-PHPdoc).
* @see \Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface::prepend() * @see PrependExtensionInterface::prepend()
*/ */
public function prependRoutes(ContainerBuilder $container) public function prependRoutes(ContainerBuilder $container)
{ {

View File

@@ -1 +1 @@
import("./chillbudget.scss"); require("./chillbudget.scss");

View File

@@ -72,14 +72,20 @@
{% macro table_results(actualCharges, actualResources, results) %} {% macro table_results(actualCharges, actualResources, results) %}
{% set now = date() %}
{% set totalCharges = 0 %} {% set totalCharges = 0 %}
{% for c in actualCharges %} {% for c in actualCharges %}
{% set totalCharges = totalCharges + c.amount %} {% if c.startDate <= now and (c.endDate is null or c.endDate >= now) %}
{% set totalCharges = totalCharges + c.amount %}
{% endif %}
{% endfor %} {% endfor %}
{% set totalResources = 0 %} {% set totalResources = 0 %}
{% for r in actualResources %} {% for r in actualResources %}
{% set totalResources = totalResources + r.amount %} {% if r.startDate <= now and (r.endDate is null or r.endDate >= now) %}
{% set totalResources = totalResources + r.amount %}
{% endif %}
{% endfor %} {% endfor %}
{% set result = (totalResources - totalCharges) %} {% set result = (totalResources - totalCharges) %}

View File

@@ -7,11 +7,11 @@
{% block title title %} {% block title title %}
{% block js %} {% block js %}
{{ vite_entry_script_tags('page_budget') }} {{ encore_entry_script_tags('page_budget') }}
{% endblock %} {% endblock %}
{% block css %} {% block css %}
{{ vite_entry_link_tags('page_budget') }} {{ encore_entry_link_tags('page_budget') }}
{% endblock %} {% endblock %}
{% block content %} {% block content %}

View File

@@ -7,11 +7,11 @@
{% block title title %} {% block title title %}
{% block js %} {% block js %}
{{ vite_entry_script_tags('page_budget') }} {{ encore_entry_script_tags('page_budget') }}
{% endblock %} {% endblock %}
{% block css %} {% block css %}
{{ vite_entry_link_tags('page_budget') }} {{ encore_entry_link_tags('page_budget') }}
{% endblock %} {% endblock %}
{% block content %} {% block content %}

View File

@@ -0,0 +1,8 @@
// this file loads all assets from the Chill budget bundle
module.exports = function (encore, entries) {
encore.addAliases({
ChillBudgetAssets: __dirname + "/Resources/public",
});
encore.addEntry("page_budget", __dirname + "/Resources/public/page/index.js");
};

View File

@@ -1,8 +0,0 @@
export default {
aliases: {
ChillBudgetAssets: "./src/Bundle/ChillBudgetBundle/Resources/public",
},
inputs: {
page_budget: "./src/Bundle/ChillBudgetBundle/Resources/public/page/index.js",
},
};

View File

@@ -1 +1 @@
import("./scss/calendar.scss"); require("./scss/calendar.scss");

View File

@@ -346,6 +346,7 @@ const baseOptions = ref<CalendarOptions>({
center: "title", center: "title",
right: "timeGridWeek,timeGridDay", right: "timeGridWeek,timeGridDay",
}, },
allDaySlot: false,
}); });
const ranges = computed<EventInput[]>(() => { const ranges = computed<EventInput[]>(() => {

View File

@@ -255,3 +255,5 @@ export default {
}, },
}; };
</script> </script>
<style src="vue-multiselect/dist/vue-multiselect.css"></style>

View File

@@ -75,7 +75,7 @@
</div> </div>
{% if calendar.comment.comment is not empty {% if calendar.comment.comment is not empty
or calendar.users|length > 0 or calendar.persons|length > 0
or calendar.thirdParties|length > 0 or calendar.thirdParties|length > 0
or calendar.users|length > 0 %} or calendar.users|length > 0 %}
<div class="item-row details separator"> <div class="item-row details separator">

View File

@@ -20,13 +20,13 @@
window.endDate = {{ entity.endDate|date('Y-m-d H:i:s')|json_encode|raw }}; window.endDate = {{ entity.endDate|date('Y-m-d H:i:s')|json_encode|raw }};
window.mainUser = {{ entity.mainUser.id }}; window.mainUser = {{ entity.mainUser.id }};
</script> </script>
{{ vite_entry_script_tags('vue_calendar') }} {{ encore_entry_script_tags('vue_calendar') }}
{% endblock %} {% endblock %}
{% block css %} {% block css %}
{{ parent() }} {{ parent() }}
{{ vite_entry_link_tags('vue_calendar') }} {{ encore_entry_link_tags('vue_calendar') }}
{{ vite_entry_link_tags('page_calendar') }} {{ encore_entry_link_tags('page_calendar') }}
{% endblock %} {% endblock %}
{% block block_post_menu %} {% block block_post_menu %}

View File

@@ -20,13 +20,13 @@
window.endDate = {{ entity.endDate|date('Y-m-d H:i:s')|json_encode|raw }}; window.endDate = {{ entity.endDate|date('Y-m-d H:i:s')|json_encode|raw }};
window.mainUser = {{ entity.mainUser.id }}; window.mainUser = {{ entity.mainUser.id }};
</script> </script>
{{ vite_entry_script_tags('vue_calendar') }} {{ encore_entry_script_tags('vue_calendar') }}
{% endblock %} {% endblock %}
{% block css %} {% block css %}
{{ parent() }} {{ parent() }}
{{ vite_entry_link_tags('vue_calendar') }} {{ encore_entry_link_tags('vue_calendar') }}
{{ vite_entry_link_tags('page_calendar') }} {{ encore_entry_link_tags('page_calendar') }}
{% endblock %} {% endblock %}
{% block block_post_menu %} {% block block_post_menu %}

View File

@@ -23,10 +23,10 @@
window.endDate = {{ entity.endDate|date('Y-m-d H:i:s')|json_encode|raw }}; window.endDate = {{ entity.endDate|date('Y-m-d H:i:s')|json_encode|raw }};
window.mainUser = {{ entity.mainUser.id }}; window.mainUser = {{ entity.mainUser.id }};
</script> </script>
{{ vite_entry_script_tags('vue_calendar') }} {{ encore_entry_script_tags('vue_calendar') }}
{% endblock %} {% endblock %}
{% block css %} {% block css %}
{{ parent() }} {{ parent() }}
{{ vite_entry_link_tags('vue_calendar') }} {{ encore_entry_link_tags('vue_calendar') }}
{% endblock %} {% endblock %}

View File

@@ -9,14 +9,14 @@
{% block js %} {% block js %}
{{ parent() }} {{ parent() }}
{{ vite_entry_script_tags('mod_answer') }} {{ encore_entry_script_tags('mod_answer') }}
{{ vite_entry_script_tags('mod_document_action_buttons_group') }} {{ encore_entry_script_tags('mod_document_action_buttons_group') }}
{% endblock %} {% endblock %}
{% block css %} {% block css %}
{{ parent() }} {{ parent() }}
{{ vite_entry_link_tags('mod_answer') }} {{ encore_entry_link_tags('mod_answer') }}
{{ vite_entry_link_tags('mod_document_action_buttons_group') }} {{ encore_entry_link_tags('mod_document_action_buttons_group') }}
{% endblock %} {% endblock %}
{% block content %} {% block content %}

View File

@@ -8,14 +8,14 @@
{% block js %} {% block js %}
{{ parent() }} {{ parent() }}
{{ vite_entry_script_tags('mod_answer') }} {{ encore_entry_script_tags('mod_answer') }}
{{ vite_entry_script_tags('mod_document_action_buttons_group') }} {{ encore_entry_script_tags('mod_document_action_buttons_group') }}
{% endblock %} {% endblock %}
{% block css %} {% block css %}
{{ parent() }} {{ parent() }}
{{ vite_entry_link_tags('mod_answer') }} {{ encore_entry_link_tags('mod_answer') }}
{{ vite_entry_link_tags('mod_document_action_buttons_group') }} {{ encore_entry_link_tags('mod_document_action_buttons_group') }}
{% endblock %} {% endblock %}
{% block content %} {% block content %}

View File

@@ -16,10 +16,10 @@
<script type="text/javascript"> <script type="text/javascript">
window.userId = {{ user.id }}; window.userId = {{ user.id }};
</script> </script>
{{ vite_entry_script_tags('vue_mycalendarrange') }} {{ encore_entry_script_tags('vue_mycalendarrange') }}
{% endblock %} {% endblock %}
{% block css %} {% block css %}
{{ parent() }} {{ parent() }}
{{ vite_entry_link_tags('vue_mycalendarrange') }} {{ encore_entry_link_tags('vue_mycalendarrange') }}
{% endblock %} {% endblock %}

View File

@@ -15,18 +15,18 @@
{% block js %} {% block js %}
{{ parent() }} {{ parent() }}
{{ vite_entry_script_tags('mod_pickentity_type') }} {{ encore_entry_script_tags('mod_pickentity_type') }}
<script type="text/javascript"> <script type="text/javascript">
window.entity = {{ entity_json|json_encode|raw }}; window.entity = {{ entity_json|json_encode|raw }};
{% if app.user.currentLocation is not null %}window.default_location_id = {{ app.user.currentLocation.id }};{% endif %}; {% if app.user.currentLocation is not null %}window.default_location_id = {{ app.user.currentLocation.id }};{% endif %};
</script> </script>
{{ vite_entry_script_tags('vue_calendar') }} {{ encore_entry_script_tags('vue_calendar') }}
{% endblock %} {% endblock %}
{% block css %} {% block css %}
{{ parent() }} {{ parent() }}
{{ vite_entry_link_tags('vue_calendar') }} {{ encore_entry_link_tags('vue_calendar') }}
{{ vite_entry_link_tags('mod_pickentity_type') }} {{ encore_entry_link_tags('mod_pickentity_type') }}
{% endblock %} {% endblock %}
{% block block_post_menu %} {% block block_post_menu %}

View File

@@ -15,18 +15,18 @@
{% block js %} {% block js %}
{{ parent() }} {{ parent() }}
{{ vite_entry_script_tags('mod_pickentity_type') }} {{ encore_entry_script_tags('mod_pickentity_type') }}
<script type="text/javascript"> <script type="text/javascript">
window.entity = {{ entity_json|json_encode|raw }}; window.entity = {{ entity_json|json_encode|raw }};
{% if app.user.currentLocation is not null %}window.default_location_id = {{ app.user.currentLocation.id }};{% endif %}; {% if app.user.currentLocation is not null %}window.default_location_id = {{ app.user.currentLocation.id }};{% endif %};
</script> </script>
{{ vite_entry_script_tags('vue_calendar') }} {{ encore_entry_script_tags('vue_calendar') }}
{% endblock %} {% endblock %}
{% block css %} {% block css %}
{{ parent() }} {{ parent() }}
{{ vite_entry_link_tags('vue_calendar') }} {{ encore_entry_link_tags('vue_calendar') }}
{{ vite_entry_link_tags('mod_pickentity_type') }} {{ encore_entry_link_tags('mod_pickentity_type') }}
{% endblock %} {% endblock %}
{% block block_post_menu %} {% block block_post_menu %}

View File

@@ -9,12 +9,12 @@
{% block js %} {% block js %}
{{ parent() }} {{ parent() }}
{{ vite_entry_script_tags('mod_async_upload') }} {{ encore_entry_script_tags('mod_async_upload') }}
{% endblock %} {% endblock %}
{% block css %} {% block css %}
{{ parent() }} {{ parent() }}
{{ vite_entry_link_tags('mod_async_upload') }} {{ encore_entry_link_tags('mod_async_upload') }}
{% endblock %} {% endblock %}
{% block content %} {% block content %}

View File

@@ -8,12 +8,12 @@
{% block js %} {% block js %}
{{ parent() }} {{ parent() }}
{{ vite_entry_script_tags('mod_async_upload') }} {{ encore_entry_script_tags('mod_async_upload') }}
{% endblock %} {% endblock %}
{% block css %} {% block css %}
{{ parent() }} {{ parent() }}
{{ vite_entry_link_tags('mod_async_upload') }} {{ encore_entry_link_tags('mod_async_upload') }}
{% endblock %} {% endblock %}
{% block content %} {% block content %}

View File

@@ -9,12 +9,12 @@
{% block js %} {% block js %}
{{ parent() }} {{ parent() }}
{{ vite_entry_script_tags('mod_async_upload') }} {{ encore_entry_script_tags('mod_async_upload') }}
{% endblock %} {% endblock %}
{% block css %} {% block css %}
{{ parent() }} {{ parent() }}
{{ vite_entry_link_tags('mod_async_upload') }} {{ encore_entry_link_tags('mod_async_upload') }}
{% endblock %} {% endblock %}
{% block content %} {% block content %}

View File

@@ -8,12 +8,12 @@
{% block js %} {% block js %}
{{ parent() }} {{ parent() }}
{{ vite_entry_script_tags('mod_async_upload') }} {{ encore_entry_script_tags('mod_async_upload') }}
{% endblock %} {% endblock %}
{% block css %} {% block css %}
{{ parent() }} {{ parent() }}
{{ vite_entry_link_tags('mod_async_upload') }} {{ encore_entry_link_tags('mod_async_upload') }}
{% endblock %} {% endblock %}
{% block content %} {% block content %}

View File

@@ -29,12 +29,12 @@
{% block js %} {% block js %}
{{ parent() }} {{ parent() }}
{{ vite_entry_script_tags('mod_answer') }} {{ encore_entry_script_tags('mod_answer') }}
{{ vite_entry_script_tags('mod_document_action_buttons_group') }} {{ encore_entry_script_tags('mod_document_action_buttons_group') }}
{% endblock %} {% endblock %}
{% block css %} {% block css %}
{{ parent() }} {{ parent() }}
{{ vite_entry_link_tags('mod_answer') }} {{ encore_entry_link_tags('mod_answer') }}
{{ vite_entry_link_tags('mod_document_action_buttons_group') }} {{ encore_entry_link_tags('mod_document_action_buttons_group') }}
{% endblock %} {% endblock %}

View File

@@ -0,0 +1,25 @@
// this file loads all assets from the Chill calendar bundle
module.exports = function (encore, entries) {
entries.push(__dirname + "/Resources/public/chill/chill.js");
encore.addAliases({
ChillCalendarAssets: __dirname + "/Resources/public",
});
encore.addEntry(
"vue_calendar",
__dirname + "/Resources/public/vuejs/Calendar/index.js",
);
encore.addEntry(
"vue_mycalendarrange",
__dirname + "/Resources/public/vuejs/MyCalendarRange/index2.ts",
);
encore.addEntry(
"page_calendar",
__dirname + "/Resources/public/chill/index.js",
);
encore.addEntry(
"mod_answer",
__dirname + "/Resources/public/module/Invite/answer.js",
);
};

View File

@@ -1,18 +0,0 @@
export default {
chillBase: [
"./src/Bundle/ChillCalendarBundle/Resources/public/chill/chill.js",
],
aliases: {
ChillCalendarAssets: "./src/Bundle/ChillCalendarBundle/Resources/public",
},
inputs: {
vue_calendar:
"./src/Bundle/ChillCalendarBundle/Resources/public/vuejs/Calendar/index.js",
vue_mycalendarrange:
"./src/Bundle/ChillCalendarBundle/Resources/public/vuejs/MyCalendarRange/index2.ts",
page_calendar:
"./src/Bundle/ChillCalendarBundle/Resources/public/chill/index.js",
mod_answer:
"./src/Bundle/ChillCalendarBundle/Resources/public/module/Invite/answer.js",
},
};

View File

@@ -52,7 +52,7 @@ class ChillCustomFieldsExtension extends Extension implements PrependExtensionIn
} }
/** (non-PHPdoc). /** (non-PHPdoc).
* @see \Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface::prepend() * @see PrependExtensionInterface::prepend()
*/ */
public function prepend(ContainerBuilder $container) public function prepend(ContainerBuilder $container)
{ {

View File

@@ -25,7 +25,7 @@ class ChoiceWithOtherType extends AbstractType
private string $otherValueLabel = 'Other value'; private string $otherValueLabel = 'Other value';
/** (non-PHPdoc). /** (non-PHPdoc).
* @see \Symfony\Component\Form\AbstractType::buildForm() * @see AbstractType::buildForm()
*/ */
public function buildForm(FormBuilderInterface $builder, array $options) public function buildForm(FormBuilderInterface $builder, array $options)
{ {
@@ -42,7 +42,7 @@ class ChoiceWithOtherType extends AbstractType
} }
/** (non-PHPdoc). /** (non-PHPdoc).
* @see \Symfony\Component\Form\AbstractType::configureOptions() * @see AbstractType::configureOptions()
*/ */
public function configureOptions(OptionsResolver $resolver) public function configureOptions(OptionsResolver $resolver)
{ {

View File

@@ -22,7 +22,7 @@ use Symfony\Component\Form\FormEvents;
class ChoicesListType extends AbstractType class ChoicesListType extends AbstractType
{ {
/** (non-PHPdoc). /** (non-PHPdoc).
* @see \Symfony\Component\Form\AbstractType::buildForm() * @see AbstractType::buildForm()
*/ */
public function buildForm(FormBuilderInterface $builder, array $options) public function buildForm(FormBuilderInterface $builder, array $options)
{ {

View File

@@ -82,7 +82,7 @@ class CustomFieldProvider implements ContainerAwareInterface
/** /**
* (non-PHPdoc). * (non-PHPdoc).
* *
* @see \Symfony\Component\DependencyInjection\ContainerAwareInterface::setContainer() * @see ContainerAwareInterface::setContainer()
*/ */
public function setContainer(?ContainerInterface $container = null) public function setContainer(?ContainerInterface $container = null)
{ {

View File

@@ -17,9 +17,9 @@
{% block js %} {% block js %}
{{ parent() }} {{ parent() }}
{{ vite_entry_script_tags('mod_async_upload') }} {{ encore_entry_script_tags('mod_async_upload') }}
{% endblock %} {% endblock %}
{% block css %} {% block css %}
{{ vite_entry_link_tags('mod_async_upload') }} {{ encore_entry_link_tags('mod_async_upload') }}
{% endblock %} {% endblock %}

View File

@@ -2,12 +2,12 @@
{% block js %} {% block js %}
{{ parent() }} {{ parent() }}
{{ vite_entry_script_tags('mod_document_action_buttons_group') }} {{ encore_entry_script_tags('mod_document_action_buttons_group') }}
{% endblock %} {% endblock %}
{% block css %} {% block css %}
{{ parent() }} {{ parent() }}
{{ vite_entry_link_tags('mod_document_action_buttons_group') }} {{ encore_entry_link_tags('mod_document_action_buttons_group') }}
{% endblock %} {% endblock %}

View File

@@ -17,9 +17,9 @@
{% block js %} {% block js %}
{{ parent() }} {{ parent() }}
{{ vite_entry_script_tags('mod_async_upload') }} {{ encore_entry_script_tags('mod_async_upload') }}
{% endblock %} {% endblock %}
{% block css %} {% block css %}
{{ vite_entry_link_tags('mod_async_upload') }} {{ encore_entry_link_tags('mod_async_upload') }}
{% endblock %} {% endblock %}

View File

@@ -3,11 +3,11 @@
{% block title 'docgen.Generate a document'|trans %} {% block title 'docgen.Generate a document'|trans %}
{% block js %} {% block js %}
{{ vite_entry_script_tags('mod_pickentity_type') }} {{ encore_entry_script_tags('mod_pickentity_type') }}
{% endblock %} {% endblock %}
{% block css %} {% block css %}
{{ vite_entry_link_tags('mod_pickentity_type') }} {{ encore_entry_link_tags('mod_pickentity_type') }}
{% endblock %} {% endblock %}
{% block content %} {% block content %}

View File

@@ -0,0 +1,11 @@
// this file loads all assets from the Chill DocGenerator bundle
module.exports = function (encore, entries) {
encore.addAliases({
ChillDocGeneratorAssets: __dirname + "/Resources/public",
});
encore.addEntry(
"mod_docgen_picktemplate",
__dirname + "/Resources/public/module/PickTemplate/index.js",
);
};

View File

@@ -1,10 +0,0 @@
export default {
aliases: {
ChillDocGeneratorAssets:
"./src/Bundle/ChillDocGeneratorBundle/Resources/public",
},
inputs: {
mod_docgen_picktemplate:
"./src/Bundle/ChillDocGeneratorBundle/Resources/public/module/PickTemplate/index.js",
},
};

View File

@@ -1,2 +1,2 @@
import("./uploader.js"); require("./uploader.js");
import("./downloader.js"); require("./downloader.js");

View File

@@ -22,8 +22,8 @@ import { initializeButtons } from "./downloader.js";
// load css // load css
//require('dropzone/dist/basic.css'); //require('dropzone/dist/basic.css');
import("dropzone/dist/dropzone.css"); require("dropzone/dist/dropzone.css");
import("./index.scss"); require("./index.scss");
// //
// disable dropzone autodiscover // disable dropzone autodiscover

View File

@@ -366,7 +366,7 @@ import {
} from "../../types"; } from "../../types";
import { makeFetch } from "ChillMainAssets/lib/api/apiMethods"; import { makeFetch } from "ChillMainAssets/lib/api/apiMethods";
import * as pdfjsLib from "pdfjs-dist"; import * as pdfjsLib from "pdfjs-dist";
import type { import {
PDFDocumentProxy, PDFDocumentProxy,
PDFPageProxy, PDFPageProxy,
} from "pdfjs-dist/types/src/display/api"; } from "pdfjs-dist/types/src/display/api";

View File

@@ -42,10 +42,10 @@
{% block js %} {% block js %}
{{ parent() }} {{ parent() }}
{{ vite_entry_script_tags('mod_async_upload') }} {{ encore_entry_script_tags('mod_async_upload') }}
{% endblock %} {% endblock %}
{% block css %} {% block css %}
{{ parent() }} {{ parent() }}
{{ vite_entry_link_tags('mod_async_upload') }} {{ encore_entry_link_tags('mod_async_upload') }}
{% endblock %} {% endblock %}

View File

@@ -38,10 +38,10 @@
{% block js %} {% block js %}
{{ parent() }} {{ parent() }}
{{ vite_entry_script_tags('mod_async_upload') }} {{ encore_entry_script_tags('mod_async_upload') }}
{% endblock %} {% endblock %}
{% block css %} {% block css %}
{{ parent() }} {{ parent() }}
{{ vite_entry_link_tags('mod_async_upload') }} {{ encore_entry_link_tags('mod_async_upload') }}
{% endblock %} {% endblock %}

View File

@@ -12,9 +12,9 @@
{% block css %} {% block css %}
{{ parent() }} {{ parent() }}
{{ vite_entry_link_tags('mod_async_upload') }} {{ encore_entry_link_tags('mod_async_upload') }}
{{ vite_entry_link_tags('mod_entity_workflow_pick') }} {{ encore_entry_link_tags('mod_entity_workflow_pick') }}
{{ vite_entry_link_tags('mod_document_action_buttons_group') }} {{ encore_entry_link_tags('mod_document_action_buttons_group') }}
{% endblock %} {% endblock %}
{% block content %} {% block content %}
@@ -80,7 +80,7 @@
{% block js %} {% block js %}
{{ parent() }} {{ parent() }}
{{ vite_entry_script_tags('mod_async_upload') }} {{ encore_entry_script_tags('mod_async_upload') }}
{{ vite_entry_script_tags('mod_entity_workflow_pick') }} {{ encore_entry_script_tags('mod_entity_workflow_pick') }}
{{ vite_entry_script_tags('mod_document_action_buttons_group') }} {{ encore_entry_script_tags('mod_document_action_buttons_group') }}
{% endblock %} {% endblock %}

View File

@@ -3,14 +3,14 @@ activeRouteKey = '' %} {% block title %}
{{ "Documents" }} {{ "Documents" }}
{% endblock %} {% block js %} {% endblock %} {% block js %}
{{ parent() }} {{ parent() }}
{{ vite_entry_script_tags("mod_docgen_picktemplate") }} {{ encore_entry_script_tags("mod_docgen_picktemplate") }}
{{ vite_entry_script_tags("mod_entity_workflow_pick") }} {{ encore_entry_script_tags("mod_entity_workflow_pick") }}
{{ vite_entry_script_tags("mod_document_action_buttons_group") }} {{ encore_entry_script_tags("mod_document_action_buttons_group") }}
{% endblock %} {% block css %} {% endblock %} {% block css %}
{{ parent() }} {{ parent() }}
{{ vite_entry_script_tags("mod_docgen_picktemplate") }} {{ encore_entry_script_tags("mod_docgen_picktemplate") }}
{{ vite_entry_link_tags("mod_entity_workflow_pick") }} {{ encore_entry_link_tags("mod_entity_workflow_pick") }}
{{ vite_entry_link_tags("mod_document_action_buttons_group") }} {{ encore_entry_link_tags("mod_document_action_buttons_group") }}
{% endblock %} {% block content %} {% endblock %} {% block content %}
<div class="document-list"> <div class="document-list">
<h1>{{ "Documents" }}</h1> <h1>{{ "Documents" }}</h1>

View File

@@ -13,14 +13,14 @@ License * along with this program. If not, see <http://www.gnu.org/licenses/>.
{{ 'Documents for %name%'|trans({ '%name%': person|chill_entity_render_string } ) }} {{ 'Documents for %name%'|trans({ '%name%': person|chill_entity_render_string } ) }}
{% endblock %} {% block js %} {% endblock %} {% block js %}
{{ parent() }} {{ parent() }}
{{ vite_entry_script_tags("mod_docgen_picktemplate") }} {{ encore_entry_script_tags("mod_docgen_picktemplate") }}
{{ vite_entry_script_tags("mod_entity_workflow_pick") }} {{ encore_entry_script_tags("mod_entity_workflow_pick") }}
{{ vite_entry_script_tags("mod_document_action_buttons_group") }} {{ encore_entry_script_tags("mod_document_action_buttons_group") }}
{% endblock %} {% block css %} {% endblock %} {% block css %}
{{ parent() }} {{ parent() }}
{{ vite_entry_link_tags("mod_docgen_picktemplate") }} {{ encore_entry_link_tags("mod_docgen_picktemplate") }}
{{ vite_entry_link_tags("mod_entity_workflow_pick") }} {{ encore_entry_link_tags("mod_entity_workflow_pick") }}
{{ vite_entry_link_tags("mod_document_action_buttons_group") }} {{ encore_entry_link_tags("mod_document_action_buttons_group") }}
{% endblock %} {% block content %} {% endblock %} {% block content %}
<div class="document-list"> <div class="document-list">

View File

@@ -58,9 +58,9 @@
{% endblock %} {% endblock %}
{% block js %} {% block js %}
{{ vite_entry_script_tags('mod_async_upload') }} {{ encore_entry_script_tags('mod_async_upload') }}
{% endblock %} {% endblock %}
{% block css %} {% block css %}
{{ vite_entry_link_tags('mod_async_upload') }} {{ encore_entry_link_tags('mod_async_upload') }}
{% endblock %} {% endblock %}

View File

@@ -54,9 +54,9 @@
{% endblock %} {% endblock %}
{% block js %} {% block js %}
{{ vite_entry_script_tags('mod_async_upload') }} {{ encore_entry_script_tags('mod_async_upload') }}
{% endblock %} {% endblock %}
{% block css %} {% block css %}
{{ vite_entry_link_tags('mod_async_upload') }} {{ encore_entry_link_tags('mod_async_upload') }}
{% endblock %} {% endblock %}

View File

@@ -24,11 +24,11 @@
{% block title %}{{ 'Detail of document of %name%'|trans({ '%name%': person|chill_entity_render_string } ) }}{% endblock %} {% block title %}{{ 'Detail of document of %name%'|trans({ '%name%': person|chill_entity_render_string } ) }}{% endblock %}
{% block js %} {% block js %}
{{ vite_entry_script_tags('mod_document_action_buttons_group') }} {{ encore_entry_script_tags('mod_document_action_buttons_group') }}
{% endblock %} {% endblock %}
{% block css %} {% block css %}
{{ vite_entry_link_tags('mod_document_action_buttons_group') }} {{ encore_entry_link_tags('mod_document_action_buttons_group') }}
{% endblock %} {% endblock %}
{% block content %} {% block content %}

View File

@@ -7,21 +7,21 @@
<link rel="shortcut icon" href="{{ asset('build/images/favicon.ico') }}" type="image/x-icon"> <link rel="shortcut icon" href="{{ asset('build/images/favicon.ico') }}" type="image/x-icon">
<title>Signature</title> <title>Signature</title>
{{ vite_entry_link_tags('mod_bootstrap') }} {{ encore_entry_link_tags('mod_bootstrap') }}
{{ vite_entry_link_tags('mod_forkawesome') }} {{ encore_entry_link_tags('mod_forkawesome') }}
{{ vite_entry_link_tags('chill') }} {{ encore_entry_link_tags('chill') }}
{{ vite_entry_link_tags('vue_document_signature') }} {{ encore_entry_link_tags('vue_document_signature') }}
</head> </head>
<body> <body>
{% block js %} {% block js %}
{{ vite_entry_script_tags('mod_document_action_buttons_group') }} {{ encore_entry_script_tags('mod_document_action_buttons_group') }}
<script type="text/javascript"> <script type="text/javascript">
window.signature = {{ signature|json_encode|raw }}; window.signature = {{ signature|json_encode|raw }};
</script> </script>
{{ vite_entry_script_tags('vue_document_signature') }} {{ encore_entry_script_tags('vue_document_signature') }}
{% endblock %} {% endblock %}
<div class="content" id="content"> <div class="content" id="content">

View File

@@ -2,12 +2,12 @@
{% block css %} {% block css %}
{{ parent() }} {{ parent() }}
{{ vite_entry_link_tags('mod_document_download_button') }} {{ encore_entry_link_tags('mod_document_download_button') }}
{% endblock %} {% endblock %}
{% block js %} {% block js %}
{{ parent() }} {{ parent() }}
{{ vite_entry_script_tags('mod_document_download_button') }} {{ encore_entry_script_tags('mod_document_download_button') }}
{% endblock %} {% endblock %}
{% block title %}{{ 'workflow.public_link.title'|trans }} - {{ title }}{% endblock %} {% block title %}{{ 'workflow.public_link.title'|trans }} - {{ title }}{% endblock %}

View File

@@ -15,7 +15,10 @@ use Chill\DocStoreBundle\Entity\StoredObject;
use Chill\DocStoreBundle\Repository\AssociatedEntityToStoredObjectInterface; use Chill\DocStoreBundle\Repository\AssociatedEntityToStoredObjectInterface;
use Chill\DocStoreBundle\Security\Authorization\StoredObjectRoleEnum; use Chill\DocStoreBundle\Security\Authorization\StoredObjectRoleEnum;
use Chill\DocStoreBundle\Security\Authorization\StoredObjectVoterInterface; use Chill\DocStoreBundle\Security\Authorization\StoredObjectVoterInterface;
use Chill\MainBundle\Workflow\Helper\WorkflowRelatedEntityPermissionHelper; use Chill\MainBundle\Entity\Workflow\EntityWorkflow;
use Chill\MainBundle\Entity\Workflow\EntityWorkflowAttachment;
use Chill\MainBundle\Repository\Workflow\EntityWorkflowAttachmentRepository;
use Chill\MainBundle\Workflow\Helper\WorkflowRelatedEntityPermissionHelperInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Security; use Symfony\Component\Security\Core\Security;
@@ -34,7 +37,8 @@ abstract class AbstractStoredObjectVoter implements StoredObjectVoterInterface
public function __construct( public function __construct(
private readonly Security $security, private readonly Security $security,
private readonly ?WorkflowRelatedEntityPermissionHelper $workflowDocumentService = null, private readonly EntityWorkflowAttachmentRepository $entityWorkflowAttachmentRepository,
private readonly WorkflowRelatedEntityPermissionHelperInterface $workflowDocumentService,
) {} ) {}
public function supports(StoredObjectRoleEnum $attribute, StoredObject $subject): bool public function supports(StoredObjectRoleEnum $attribute, StoredObject $subject): bool
@@ -46,16 +50,6 @@ abstract class AbstractStoredObjectVoter implements StoredObjectVoterInterface
public function voteOnAttribute(StoredObjectRoleEnum $attribute, StoredObject $subject, TokenInterface $token): bool public function voteOnAttribute(StoredObjectRoleEnum $attribute, StoredObject $subject, TokenInterface $token): bool
{ {
// we first try to get the permission from the workflow, as attachement (this is the less intensive query)
$workflowPermissionAsAttachment = match ($attribute) {
StoredObjectRoleEnum::SEE => $this->workflowDocumentService->isAllowedByWorkflowForReadOperation($subject),
StoredObjectRoleEnum::EDIT => $this->workflowDocumentService->isAllowedByWorkflowForWriteOperation($subject),
};
if (WorkflowRelatedEntityPermissionHelper::FORCE_DENIED === $workflowPermissionAsAttachment) {
return false;
}
// Retrieve the related entity // Retrieve the related entity
$entity = $this->getRepository()->findAssociatedEntityToStoredObject($subject); $entity = $this->getRepository()->findAssociatedEntityToStoredObject($subject);
@@ -65,7 +59,7 @@ abstract class AbstractStoredObjectVoter implements StoredObjectVoterInterface
$regularPermission = $this->security->isGranted($voterAttribute, $entity); $regularPermission = $this->security->isGranted($voterAttribute, $entity);
if (!$this->canBeAssociatedWithWorkflow()) { if (!$this->canBeAssociatedWithWorkflow()) {
return $regularPermission; return $this->voteOnStoredObjectAsAttachementOfAWorkflow($attribute, $regularPermission, $subject);
} }
$workflowPermission = match ($attribute) { $workflowPermission = match ($attribute) {
@@ -74,9 +68,41 @@ abstract class AbstractStoredObjectVoter implements StoredObjectVoterInterface
}; };
return match ($workflowPermission) { return match ($workflowPermission) {
WorkflowRelatedEntityPermissionHelper::FORCE_GRANT => true, WorkflowRelatedEntityPermissionHelperInterface::FORCE_GRANT => true,
WorkflowRelatedEntityPermissionHelper::FORCE_DENIED => false, WorkflowRelatedEntityPermissionHelperInterface::FORCE_DENIED => false,
WorkflowRelatedEntityPermissionHelper::ABSTAIN => WorkflowRelatedEntityPermissionHelper::FORCE_GRANT === $workflowPermissionAsAttachment || $regularPermission, WorkflowRelatedEntityPermissionHelperInterface::ABSTAIN => $this->voteOnStoredObjectAsAttachementOfAWorkflow($attribute, $regularPermission, $subject),
}; };
} }
private function voteOnStoredObjectAsAttachementOfAWorkflow(StoredObjectRoleEnum $attribute, bool $regularPermission, StoredObject $storedObject): bool
{
$attachments = $this->entityWorkflowAttachmentRepository->findByStoredObject($storedObject);
// we get all the entity workflows where the stored object is attached
$entityWorkflows = array_map(static fn (EntityWorkflowAttachment $attachment) => $attachment->getEntityWorkflow(), $attachments);
// we compute all the permission for each entity workflow
$permissions = array_map(fn (EntityWorkflow $entityWorkflow): string => match ($attribute) {
StoredObjectRoleEnum::SEE => $this->workflowDocumentService->isAllowedByWorkflowForReadOperation($entityWorkflow),
StoredObjectRoleEnum::EDIT => $this->workflowDocumentService->isAllowedByWorkflowForWriteOperation($entityWorkflow),
}, $entityWorkflows);
// now, we reduce the permissions: abstain are ignored. Between DENIED and and GRANT, DENIED takes precedence
$computedPermission = WorkflowRelatedEntityPermissionHelperInterface::ABSTAIN;
foreach ($permissions as $permission) {
if (WorkflowRelatedEntityPermissionHelperInterface::FORCE_DENIED === $permission) {
return false;
}
if (WorkflowRelatedEntityPermissionHelperInterface::FORCE_GRANT === $permission) {
$computedPermission = WorkflowRelatedEntityPermissionHelperInterface::FORCE_GRANT;
}
}
if (WorkflowRelatedEntityPermissionHelperInterface::ABSTAIN === $computedPermission) {
return $regularPermission;
}
// this is the case where WorkflowRelatedEntityPermissionHelperInterface::FORCE_GRANT is returned
return true;
}
} }

View File

@@ -16,6 +16,7 @@ use Chill\DocStoreBundle\Repository\AccompanyingCourseDocumentRepository;
use Chill\DocStoreBundle\Repository\AssociatedEntityToStoredObjectInterface; use Chill\DocStoreBundle\Repository\AssociatedEntityToStoredObjectInterface;
use Chill\DocStoreBundle\Security\Authorization\AccompanyingCourseDocumentVoter; use Chill\DocStoreBundle\Security\Authorization\AccompanyingCourseDocumentVoter;
use Chill\DocStoreBundle\Security\Authorization\StoredObjectRoleEnum; use Chill\DocStoreBundle\Security\Authorization\StoredObjectRoleEnum;
use Chill\MainBundle\Repository\Workflow\EntityWorkflowAttachmentRepository;
use Chill\MainBundle\Workflow\Helper\WorkflowRelatedEntityPermissionHelper; use Chill\MainBundle\Workflow\Helper\WorkflowRelatedEntityPermissionHelper;
use Symfony\Component\Security\Core\Security; use Symfony\Component\Security\Core\Security;
@@ -25,8 +26,9 @@ final class AccompanyingCourseDocumentStoredObjectVoter extends AbstractStoredOb
private readonly AccompanyingCourseDocumentRepository $repository, private readonly AccompanyingCourseDocumentRepository $repository,
Security $security, Security $security,
WorkflowRelatedEntityPermissionHelper $workflowDocumentService, WorkflowRelatedEntityPermissionHelper $workflowDocumentService,
EntityWorkflowAttachmentRepository $attachmentRepository,
) { ) {
parent::__construct($security, $workflowDocumentService); parent::__construct($security, $attachmentRepository, $workflowDocumentService);
} }
protected function getRepository(): AssociatedEntityToStoredObjectInterface protected function getRepository(): AssociatedEntityToStoredObjectInterface

View File

@@ -16,6 +16,7 @@ use Chill\DocStoreBundle\Repository\AssociatedEntityToStoredObjectInterface;
use Chill\DocStoreBundle\Repository\PersonDocumentRepository; use Chill\DocStoreBundle\Repository\PersonDocumentRepository;
use Chill\DocStoreBundle\Security\Authorization\PersonDocumentVoter; use Chill\DocStoreBundle\Security\Authorization\PersonDocumentVoter;
use Chill\DocStoreBundle\Security\Authorization\StoredObjectRoleEnum; use Chill\DocStoreBundle\Security\Authorization\StoredObjectRoleEnum;
use Chill\MainBundle\Repository\Workflow\EntityWorkflowAttachmentRepository;
use Chill\MainBundle\Workflow\Helper\WorkflowRelatedEntityPermissionHelper; use Chill\MainBundle\Workflow\Helper\WorkflowRelatedEntityPermissionHelper;
use Symfony\Component\Security\Core\Security; use Symfony\Component\Security\Core\Security;
@@ -25,8 +26,9 @@ class PersonDocumentStoredObjectVoter extends AbstractStoredObjectVoter
private readonly PersonDocumentRepository $repository, private readonly PersonDocumentRepository $repository,
Security $security, Security $security,
WorkflowRelatedEntityPermissionHelper $workflowDocumentService, WorkflowRelatedEntityPermissionHelper $workflowDocumentService,
EntityWorkflowAttachmentRepository $attachmentRepository,
) { ) {
parent::__construct($security, $workflowDocumentService); parent::__construct($security, $attachmentRepository, $workflowDocumentService);
} }
protected function getRepository(): AssociatedEntityToStoredObjectInterface protected function getRepository(): AssociatedEntityToStoredObjectInterface

View File

@@ -16,8 +16,11 @@ use Chill\DocStoreBundle\Repository\AssociatedEntityToStoredObjectInterface;
use Chill\DocStoreBundle\Security\Authorization\StoredObjectRoleEnum; use Chill\DocStoreBundle\Security\Authorization\StoredObjectRoleEnum;
use Chill\DocStoreBundle\Security\Authorization\StoredObjectVoter\AbstractStoredObjectVoter; use Chill\DocStoreBundle\Security\Authorization\StoredObjectVoter\AbstractStoredObjectVoter;
use Chill\MainBundle\Entity\User; use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Workflow\Helper\WorkflowRelatedEntityPermissionHelper; use Chill\MainBundle\Entity\Workflow\EntityWorkflowAttachment;
use Chill\MainBundle\Repository\Workflow\EntityWorkflowAttachmentRepository;
use Chill\MainBundle\Workflow\Helper\WorkflowRelatedEntityPermissionHelperInterface;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait; use Prophecy\PhpUnit\ProphecyTrait;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Security\Core\Security; use Symfony\Component\Security\Core\Security;
@@ -31,21 +34,31 @@ class AbstractStoredObjectVoterTest extends TestCase
{ {
use ProphecyTrait; use ProphecyTrait;
/**
* @param array<int, EntityWorkflowAttachment> $attachments
*
* @return void
*/
private function buildStoredObjectVoter( private function buildStoredObjectVoter(
bool $canBeAssociatedWithWorkflow, bool $canBeAssociatedWithWorkflow,
AssociatedEntityToStoredObjectInterface $repository, AssociatedEntityToStoredObjectInterface $repository,
Security $security, Security $security,
?WorkflowRelatedEntityPermissionHelper $workflowDocumentService = null, ?WorkflowRelatedEntityPermissionHelperInterface $workflowDocumentService = null,
array $attachments = [],
): AbstractStoredObjectVoter { ): AbstractStoredObjectVoter {
$attachmentsRepository = $this->prophesize(EntityWorkflowAttachmentRepository::class);
$attachmentsRepository->findByStoredObject(Argument::type(StoredObject::class))->willReturn($attachments);
// Anonymous class extending the abstract class // Anonymous class extending the abstract class
return new class ($canBeAssociatedWithWorkflow, $repository, $security, $workflowDocumentService) extends AbstractStoredObjectVoter { return new class ($canBeAssociatedWithWorkflow, $repository, $security, $attachmentsRepository->reveal(), $workflowDocumentService) extends AbstractStoredObjectVoter {
public function __construct( public function __construct(
private readonly bool $canBeAssociatedWithWorkflow, private readonly bool $canBeAssociatedWithWorkflow,
private readonly AssociatedEntityToStoredObjectInterface $repository, private readonly AssociatedEntityToStoredObjectInterface $repository,
Security $security, Security $security,
?WorkflowRelatedEntityPermissionHelper $workflowDocumentService = null, EntityWorkflowAttachmentRepository $attachmentRepository,
WorkflowRelatedEntityPermissionHelperInterface $workflowDocumentService,
) { ) {
parent::__construct($security, $workflowDocumentService); parent::__construct($security, $attachmentRepository, $workflowDocumentService);
} }
protected function attributeToRole($attribute): string protected function attributeToRole($attribute): string
@@ -72,28 +85,29 @@ class AbstractStoredObjectVoterTest extends TestCase
public function testSupportsOnAttribute(): void public function testSupportsOnAttribute(): void
{ {
$voter = $this->buildStoredObjectVoter(false, new DummyRepository(new \stdClass()), $this->prophesize(Security::class)->reveal(), null); $entityWorkflowService = $this->prophesize(WorkflowRelatedEntityPermissionHelperInterface::class);
$voter = $this->buildStoredObjectVoter(false, new DummyRepository(new \stdClass()), $this->prophesize(Security::class)->reveal(), $entityWorkflowService->reveal());
self::assertTrue($voter->supports(StoredObjectRoleEnum::SEE, new StoredObject())); self::assertTrue($voter->supports(StoredObjectRoleEnum::SEE, new StoredObject()));
$voter = $this->buildStoredObjectVoter(false, new DummyRepository(new User()), $this->prophesize(Security::class)->reveal(), null); $voter = $this->buildStoredObjectVoter(false, new DummyRepository(new User()), $this->prophesize(Security::class)->reveal(), $entityWorkflowService->reveal());
self::assertFalse($voter->supports(StoredObjectRoleEnum::SEE, new StoredObject())); self::assertFalse($voter->supports(StoredObjectRoleEnum::SEE, new StoredObject()));
$voter = $this->buildStoredObjectVoter(false, new DummyRepository(null), $this->prophesize(Security::class)->reveal(), null); $voter = $this->buildStoredObjectVoter(false, new DummyRepository(null), $this->prophesize(Security::class)->reveal(), $entityWorkflowService->reveal());
self::assertFalse($voter->supports(StoredObjectRoleEnum::SEE, new StoredObject())); self::assertFalse($voter->supports(StoredObjectRoleEnum::SEE, new StoredObject()));
} }
/** /**
* @dataProvider dataProviderVoteOnAttributeWithStoredObjectPermission * @dataProvider dataProviderVoteOnAttributeWithWorkflow
*/ */
public function testVoteOnAttributeWithStoredObjectPermission( public function testVoteOnAttributeWithWorkflow(
StoredObjectRoleEnum $attribute, StoredObjectRoleEnum $attribute,
bool $expected, bool $expected,
bool $isGrantedRegularPermission, bool $isGrantedRegularPermission,
string $isGrantedWorkflowPermission, string $isGrantedWorkflowPermission,
string $isGrantedStoredObjectAttachment,
): void { ): void {
$storedObject = new StoredObject(); $storedObject = new StoredObject();
$repository = new DummyRepository($related = new \stdClass()); $repository = new DummyRepository($related = new \stdClass());
@@ -102,31 +116,28 @@ class AbstractStoredObjectVoterTest extends TestCase
$security = $this->prophesize(Security::class); $security = $this->prophesize(Security::class);
$security->isGranted('SOME_ROLE', $related)->willReturn($isGrantedRegularPermission); $security->isGranted('SOME_ROLE', $related)->willReturn($isGrantedRegularPermission);
$workflowRelatedEntityPermissionHelper = $this->prophesize(WorkflowRelatedEntityPermissionHelper::class); $workflowRelatedEntityPermissionHelper = $this->prophesize(WorkflowRelatedEntityPermissionHelperInterface::class);
$security = $this->prophesize(Security::class); $security = $this->prophesize(Security::class);
$security->isGranted('SOME_ROLE', $related)->willReturn($isGrantedRegularPermission); $security->isGranted('SOME_ROLE', $related)->willReturn($isGrantedRegularPermission);
$attachementRepository = $this->prophesize(EntityWorkflowAttachmentRepository::class);
$attachementRepository->findByStoredObject($storedObject)->willReturn([]);
if (StoredObjectRoleEnum::SEE === $attribute) { if (StoredObjectRoleEnum::SEE === $attribute) {
$workflowRelatedEntityPermissionHelper->isAllowedByWorkflowForReadOperation($storedObject)
->shouldBeCalled()
->willReturn($isGrantedStoredObjectAttachment);
$workflowRelatedEntityPermissionHelper->isAllowedByWorkflowForReadOperation($related) $workflowRelatedEntityPermissionHelper->isAllowedByWorkflowForReadOperation($related)
->willReturn($isGrantedWorkflowPermission); ->willReturn($isGrantedWorkflowPermission);
} elseif (StoredObjectRoleEnum::EDIT === $attribute) { } elseif (StoredObjectRoleEnum::EDIT === $attribute) {
$workflowRelatedEntityPermissionHelper->isAllowedByWorkflowForWriteOperation($storedObject)
->shouldBeCalled()
->willReturn($isGrantedStoredObjectAttachment);
$workflowRelatedEntityPermissionHelper->isAllowedByWorkflowForWriteOperation($related) $workflowRelatedEntityPermissionHelper->isAllowedByWorkflowForWriteOperation($related)
->willReturn($isGrantedWorkflowPermission); ->willReturn($isGrantedWorkflowPermission);
} else { } else {
throw new \LogicException('Invalid attribute for StoredObjectVoter'); throw new \LogicException('Invalid attribute for StoredObjectVoter');
} }
$storedObjectVoter = new class ($repository, $workflowRelatedEntityPermissionHelper->reveal(), $security->reveal()) extends AbstractStoredObjectVoter { $storedObjectVoter = new class ($repository, $workflowRelatedEntityPermissionHelper->reveal(), $security->reveal(), $attachementRepository->reveal()) extends AbstractStoredObjectVoter {
public function __construct(private $repository, $helper, $security) public function __construct(private $repository, $helper, $security, EntityWorkflowAttachmentRepository $attachmentRepository)
{ {
parent::__construct($security, $helper); parent::__construct($security, $attachmentRepository, $helper);
} }
protected function getRepository(): AssociatedEntityToStoredObjectInterface protected function getRepository(): AssociatedEntityToStoredObjectInterface
@@ -155,96 +166,64 @@ class AbstractStoredObjectVoterTest extends TestCase
self::assertEquals($expected, $actual); self::assertEquals($expected, $actual);
} }
public static function dataProviderVoteOnAttributeWithStoredObjectPermission(): iterable public static function dataProviderVoteOnAttributeWithWorkflow(): iterable
{ {
foreach (['read' => StoredObjectRoleEnum::SEE, 'write' => StoredObjectRoleEnum::EDIT] as $action => $attribute) { foreach (['read' => StoredObjectRoleEnum::SEE, 'write' => StoredObjectRoleEnum::EDIT] as $action => $attribute) {
yield 'Not related to any workflow nor attachment ('.$action.')' => [ yield 'Not related to any workflow nor attachment ('.$action.')' => [
$attribute, $attribute,
true, true,
true, true,
WorkflowRelatedEntityPermissionHelper::ABSTAIN, WorkflowRelatedEntityPermissionHelperInterface::ABSTAIN,
WorkflowRelatedEntityPermissionHelper::ABSTAIN,
]; ];
yield 'Not related to any workflow nor attachment (refuse) ('.$action.')' => [ yield 'Not related to any workflow nor attachment (refuse) ('.$action.')' => [
$attribute, $attribute,
false, false,
false, false,
WorkflowRelatedEntityPermissionHelper::ABSTAIN, WorkflowRelatedEntityPermissionHelperInterface::ABSTAIN,
WorkflowRelatedEntityPermissionHelper::ABSTAIN,
]; ];
yield 'Is granted by a workflow takes precedence (workflow) ('.$action.')' => [ yield 'Is granted by a workflow takes precedence (workflow) ('.$action.')' => [
$attribute, $attribute,
false, false,
true, true,
WorkflowRelatedEntityPermissionHelper::FORCE_DENIED, WorkflowRelatedEntityPermissionHelperInterface::FORCE_DENIED,
WorkflowRelatedEntityPermissionHelper::ABSTAIN,
]; ];
yield 'Is granted by a workflow takes precedence (stored object) ('.$action.')' => [ yield 'Is granted by a workflow takes precedence (stored object) ('.$action.')' => [
$attribute, $attribute,
false, false,
true,
WorkflowRelatedEntityPermissionHelper::ABSTAIN,
WorkflowRelatedEntityPermissionHelper::FORCE_DENIED,
];
yield 'Is granted by a workflow takes precedence (workflow) although grant ('.$action.')' => [
$attribute,
false, false,
true, WorkflowRelatedEntityPermissionHelperInterface::ABSTAIN,
WorkflowRelatedEntityPermissionHelper::FORCE_DENIED,
WorkflowRelatedEntityPermissionHelper::FORCE_GRANT,
]; ];
yield 'Is granted by a workflow takes precedence (stored object) although grant ('.$action.')' => [ yield 'Is granted by a workflow takes precedence (stored object) although grant ('.$action.')' => [
$attribute, $attribute,
false,
true, true,
WorkflowRelatedEntityPermissionHelper::FORCE_GRANT, true,
WorkflowRelatedEntityPermissionHelper::FORCE_DENIED, WorkflowRelatedEntityPermissionHelperInterface::FORCE_GRANT,
]; ];
yield 'Is granted by a workflow takes precedence (initially refused) (workflow) although grant ('.$action.')' => [ yield 'Is granted by a workflow takes precedence (initially refused) (workflow) although grant ('.$action.')' => [
$attribute, $attribute,
false, false,
false, false,
WorkflowRelatedEntityPermissionHelper::FORCE_DENIED, WorkflowRelatedEntityPermissionHelperInterface::FORCE_DENIED,
WorkflowRelatedEntityPermissionHelper::FORCE_GRANT,
]; ];
yield 'Is granted by a workflow takes precedence (initially refused) (stored object) although grant ('.$action.')' => [ yield 'Is granted by a workflow takes precedence (initially refused) (stored object) although grant ('.$action.')' => [
$attribute,
false,
false,
WorkflowRelatedEntityPermissionHelper::FORCE_GRANT,
WorkflowRelatedEntityPermissionHelper::FORCE_DENIED,
];
yield 'Force grant inverse the regular permission (workflow) ('.$action.')' => [
$attribute, $attribute,
true, true,
false, false,
WorkflowRelatedEntityPermissionHelper::FORCE_GRANT, WorkflowRelatedEntityPermissionHelperInterface::FORCE_GRANT,
WorkflowRelatedEntityPermissionHelper::ABSTAIN,
]; ];
yield 'Force grant inverse the regular permission (so) ('.$action.')' => [
$attribute,
true,
false,
WorkflowRelatedEntityPermissionHelper::ABSTAIN,
WorkflowRelatedEntityPermissionHelper::FORCE_GRANT,
];
} }
} }
/** /**
* @dataProvider dataProviderVoteOnAttributeWithoutStoredObjectPermission * @dataProvider dataProviderVoteOnAttribute
*/ */
public function testVoteOnAttributeWithoutStoredObjectPermission( public function testVoteOnAttribute(
StoredObjectRoleEnum $attribute, StoredObjectRoleEnum $attribute,
bool $expected, bool $expected,
bool $canBeAssociatedWithWorkflow, bool $canBeAssociatedWithWorkflow,
@@ -260,10 +239,7 @@ class AbstractStoredObjectVoterTest extends TestCase
$security = $this->prophesize(Security::class); $security = $this->prophesize(Security::class);
$security->isGranted('SOME_ROLE', $related)->willReturn($isGrantedRegularPermission); $security->isGranted('SOME_ROLE', $related)->willReturn($isGrantedRegularPermission);
$workflowRelatedEntityPermissionHelper = $this->prophesize(WorkflowRelatedEntityPermissionHelper::class); $workflowRelatedEntityPermissionHelper = $this->prophesize(WorkflowRelatedEntityPermissionHelperInterface::class);
$workflowRelatedEntityPermissionHelper->isAllowedByWorkflowForReadOperation($storedObject)->willReturn(WorkflowRelatedEntityPermissionHelper::ABSTAIN);
$workflowRelatedEntityPermissionHelper->isAllowedByWorkflowForWriteOperation($storedObject)->willReturn(WorkflowRelatedEntityPermissionHelper::ABSTAIN);
if (null !== $isGrantedWorkflowPermissionRead) { if (null !== $isGrantedWorkflowPermissionRead) {
$workflowRelatedEntityPermissionHelper->isAllowedByWorkflowForReadOperation($related) $workflowRelatedEntityPermissionHelper->isAllowedByWorkflowForReadOperation($related)
@@ -283,27 +259,155 @@ class AbstractStoredObjectVoterTest extends TestCase
self::assertEquals($expected, $voter->voteOnAttribute($attribute, $storedObject, $token), $message); self::assertEquals($expected, $voter->voteOnAttribute($attribute, $storedObject, $token), $message);
} }
public static function dataProviderVoteOnAttributeWithoutStoredObjectPermission(): iterable public static function dataProviderVoteOnAttribute(): iterable
{ {
// not associated on a workflow // not associated on a workflow
yield [StoredObjectRoleEnum::SEE, true, false, true, null, null, 'not associated on a workflow, granted by regular access, must not rely on helper']; yield [StoredObjectRoleEnum::SEE, true, false, true, null, null, 'not associated on a workflow, granted by regular access, must not rely on helper'];
yield [StoredObjectRoleEnum::SEE, false, false, false, null, null, 'not associated on a workflow, denied by regular access, must not rely on helper']; yield [StoredObjectRoleEnum::SEE, false, false, false, null, null, 'not associated on a workflow, denied by regular access, must not rely on helper'];
// associated on a workflow, read operation // associated on a workflow, read operation
yield [StoredObjectRoleEnum::SEE, true, true, true, WorkflowRelatedEntityPermissionHelper::FORCE_GRANT, null, 'associated on a workflow, read by regular, force grant by workflow, ask for read, should be granted']; yield [StoredObjectRoleEnum::SEE, true, true, true, WorkflowRelatedEntityPermissionHelperInterface::FORCE_GRANT, null, 'associated on a workflow, read by regular, force grant by workflow, ask for read, should be granted'];
yield [StoredObjectRoleEnum::SEE, true, true, true, WorkflowRelatedEntityPermissionHelper::ABSTAIN, null, 'associated on a workflow, read by regular, abstain by workflow, ask for read, should be granted']; yield [StoredObjectRoleEnum::SEE, true, true, true, WorkflowRelatedEntityPermissionHelperInterface::ABSTAIN, null, 'associated on a workflow, read by regular, abstain by workflow, ask for read, should be granted'];
yield [StoredObjectRoleEnum::SEE, false, true, true, WorkflowRelatedEntityPermissionHelper::FORCE_DENIED, null, 'associated on a workflow, read by regular, force grant by workflow, ask for read, should be denied']; yield [StoredObjectRoleEnum::SEE, false, true, true, WorkflowRelatedEntityPermissionHelperInterface::FORCE_DENIED, null, 'associated on a workflow, read by regular, force grant by workflow, ask for read, should be denied'];
yield [StoredObjectRoleEnum::SEE, true, true, false, WorkflowRelatedEntityPermissionHelper::FORCE_GRANT, null, 'associated on a workflow, denied read by regular, force grant by workflow, ask for read, should be granted']; yield [StoredObjectRoleEnum::SEE, true, true, false, WorkflowRelatedEntityPermissionHelperInterface::FORCE_GRANT, null, 'associated on a workflow, denied read by regular, force grant by workflow, ask for read, should be granted'];
yield [StoredObjectRoleEnum::SEE, false, true, false, WorkflowRelatedEntityPermissionHelper::ABSTAIN, null, 'associated on a workflow, denied read by regular, abstain by workflow, ask for read, should be granted']; yield [StoredObjectRoleEnum::SEE, false, true, false, WorkflowRelatedEntityPermissionHelperInterface::ABSTAIN, null, 'associated on a workflow, denied read by regular, abstain by workflow, ask for read, should be granted'];
yield [StoredObjectRoleEnum::SEE, false, true, false, WorkflowRelatedEntityPermissionHelper::FORCE_DENIED, null, 'associated on a workflow, denied read by regular, force grant by workflow, ask for read, should be denied']; yield [StoredObjectRoleEnum::SEE, false, true, false, WorkflowRelatedEntityPermissionHelperInterface::FORCE_DENIED, null, 'associated on a workflow, denied read by regular, force grant by workflow, ask for read, should be denied'];
// association on a workflow, write operation // association on a workflow, write operation
yield [StoredObjectRoleEnum::EDIT, true, true, true, null, WorkflowRelatedEntityPermissionHelper::FORCE_GRANT, 'associated on a workflow, write by regular, force grant by workflow, ask for write, should be granted']; yield [StoredObjectRoleEnum::EDIT, true, true, true, null, WorkflowRelatedEntityPermissionHelperInterface::FORCE_GRANT, 'associated on a workflow, write by regular, force grant by workflow, ask for write, should be granted'];
yield [StoredObjectRoleEnum::EDIT, true, true, true, null, WorkflowRelatedEntityPermissionHelper::ABSTAIN, 'associated on a workflow, write by regular, abstain by workflow, ask for write, should be granted']; yield [StoredObjectRoleEnum::EDIT, true, true, true, null, WorkflowRelatedEntityPermissionHelperInterface::ABSTAIN, 'associated on a workflow, write by regular, abstain by workflow, ask for write, should be granted'];
yield [StoredObjectRoleEnum::EDIT, false, true, true, null, WorkflowRelatedEntityPermissionHelper::FORCE_DENIED, 'associated on a workflow, write by regular, force grant by workflow, ask for write, should be denied']; yield [StoredObjectRoleEnum::EDIT, false, true, true, null, WorkflowRelatedEntityPermissionHelperInterface::FORCE_DENIED, 'associated on a workflow, write by regular, force grant by workflow, ask for write, should be denied'];
yield [StoredObjectRoleEnum::EDIT, true, true, false, null, WorkflowRelatedEntityPermissionHelper::FORCE_GRANT, 'associated on a workflow, denied write by regular, force grant by workflow, ask for write, should be granted']; yield [StoredObjectRoleEnum::EDIT, true, true, false, null, WorkflowRelatedEntityPermissionHelperInterface::FORCE_GRANT, 'associated on a workflow, denied write by regular, force grant by workflow, ask for write, should be granted'];
yield [StoredObjectRoleEnum::EDIT, false, true, false, null, WorkflowRelatedEntityPermissionHelper::ABSTAIN, 'associated on a workflow, denied write by regular, abstain by workflow, ask for write, should be granted']; yield [StoredObjectRoleEnum::EDIT, false, true, false, null, WorkflowRelatedEntityPermissionHelperInterface::ABSTAIN, 'associated on a workflow, denied write by regular, abstain by workflow, ask for write, should be granted'];
yield [StoredObjectRoleEnum::EDIT, false, true, false, null, WorkflowRelatedEntityPermissionHelper::FORCE_DENIED, 'associated on a workflow, denied write by regular, force grant by workflow, ask for write, should be denied']; yield [StoredObjectRoleEnum::EDIT, false, true, false, null, WorkflowRelatedEntityPermissionHelperInterface::FORCE_DENIED, 'associated on a workflow, denied write by regular, force grant by workflow, ask for write, should be denied'];
}
/**
* @dataProvider dataProviderPrecedenceOfDirectAssociationOverWorkflowAttachments
*/
public function testPrecedenceOfDirectAssociationOverWorkflowAttachments(
StoredObjectRoleEnum $attribute,
bool $expected,
bool $regularPermission,
string $directWorkflowPermission,
string $attachmentWorkflowPermission,
string $message,
): void {
$storedObject = new StoredObject();
$repository = new DummyRepository($related = new \stdClass());
$token = new UsernamePasswordToken(new User(), 'dummy');
$security = $this->prophesize(Security::class);
$security->isGranted('SOME_ROLE', $related)->willReturn($regularPermission);
$workflowHelper = $this->prophesize(WorkflowRelatedEntityPermissionHelperInterface::class);
// Direct association permission
if (StoredObjectRoleEnum::SEE === $attribute) {
$workflowHelper->isAllowedByWorkflowForReadOperation($related)
->willReturn($directWorkflowPermission);
} else {
$workflowHelper->isAllowedByWorkflowForWriteOperation($related)
->willReturn($directWorkflowPermission);
}
// Attachment permission
$entityWorkflow = $this->prophesize(\Chill\MainBundle\Entity\Workflow\EntityWorkflow::class)->reveal();
$attachment = $this->prophesize(EntityWorkflowAttachment::class);
$attachment->getEntityWorkflow()->willReturn($entityWorkflow);
if (StoredObjectRoleEnum::SEE === $attribute) {
$workflowHelper->isAllowedByWorkflowForReadOperation($entityWorkflow)
->willReturn($attachmentWorkflowPermission);
} else {
$workflowHelper->isAllowedByWorkflowForWriteOperation($entityWorkflow)
->willReturn($attachmentWorkflowPermission);
}
$voter = $this->buildStoredObjectVoter(
true,
$repository,
$security->reveal(),
$workflowHelper->reveal(),
[$attachment->reveal()]
);
self::assertEquals($expected, $voter->voteOnAttribute($attribute, $storedObject, $token), $message);
}
public static function dataProviderPrecedenceOfDirectAssociationOverWorkflowAttachments(): iterable
{
$cases = [
[
'expected' => true,
'regular' => false,
'direct' => WorkflowRelatedEntityPermissionHelperInterface::FORCE_GRANT,
'attachment' => WorkflowRelatedEntityPermissionHelperInterface::FORCE_DENIED,
'message' => 'Direct FORCE_GRANT should win over attachment FORCE_DENIED',
],
[
'expected' => false,
'regular' => true,
'direct' => WorkflowRelatedEntityPermissionHelperInterface::FORCE_DENIED,
'attachment' => WorkflowRelatedEntityPermissionHelperInterface::FORCE_GRANT,
'message' => 'Direct FORCE_DENIED should win over attachment FORCE_GRANT',
],
[
'expected' => true,
'regular' => false,
'direct' => WorkflowRelatedEntityPermissionHelperInterface::FORCE_GRANT,
'attachment' => WorkflowRelatedEntityPermissionHelperInterface::ABSTAIN,
'message' => 'Direct FORCE_GRANT should win over attachment ABSTAIN',
],
[
'expected' => false,
'regular' => true,
'direct' => WorkflowRelatedEntityPermissionHelperInterface::FORCE_DENIED,
'attachment' => WorkflowRelatedEntityPermissionHelperInterface::ABSTAIN,
'message' => 'Direct FORCE_DENIED should win over attachment ABSTAIN',
],
[
'expected' => true,
'regular' => false,
'direct' => WorkflowRelatedEntityPermissionHelperInterface::ABSTAIN,
'attachment' => WorkflowRelatedEntityPermissionHelperInterface::FORCE_GRANT,
'message' => 'Direct ABSTAIN should let attachment FORCE_GRANT win',
],
[
'expected' => false,
'regular' => true,
'direct' => WorkflowRelatedEntityPermissionHelperInterface::ABSTAIN,
'attachment' => WorkflowRelatedEntityPermissionHelperInterface::FORCE_DENIED,
'message' => 'Direct ABSTAIN should let attachment FORCE_DENIED win',
],
[
'expected' => true,
'regular' => true,
'direct' => WorkflowRelatedEntityPermissionHelperInterface::ABSTAIN,
'attachment' => WorkflowRelatedEntityPermissionHelperInterface::ABSTAIN,
'message' => 'Both ABSTAIN should let regular permission (true) win',
],
[
'expected' => false,
'regular' => false,
'direct' => WorkflowRelatedEntityPermissionHelperInterface::ABSTAIN,
'attachment' => WorkflowRelatedEntityPermissionHelperInterface::ABSTAIN,
'message' => 'Both ABSTAIN should let regular permission (false) win',
],
];
foreach ([StoredObjectRoleEnum::SEE, StoredObjectRoleEnum::EDIT] as $attribute) {
foreach ($cases as $case) {
yield sprintf('%s - %s', $attribute->name, $case['message']) => [
$attribute,
$case['expected'],
$case['regular'],
$case['direct'],
$case['attachment'],
$case['message'],
];
}
}
} }
} }

View File

@@ -0,0 +1,21 @@
module.exports = function (encore) {
encore.addAliases({
ChillDocStoreAssets: __dirname + "/Resources/public",
});
encore.addEntry(
"mod_async_upload",
__dirname + "/Resources/public/module/async_upload/index.ts",
);
encore.addEntry(
"mod_document_action_buttons_group",
__dirname + "/Resources/public/module/document_action_buttons_group/index",
);
encore.addEntry(
"mod_document_download_button",
__dirname + "/Resources/public/module/button_download/index",
);
encore.addEntry(
"vue_document_signature",
__dirname + "/Resources/public/vuejs/DocumentSignature/index",
);
};

View File

@@ -1,15 +0,0 @@
export default {
aliases: {
ChillDocStoreAssets: "./src/Bundle/ChillDocStoreBundle/Resources/public",
},
inputs: {
mod_async_upload:
"./src/Bundle/ChillDocStoreBundle/Resources/public/module/async_upload/index.ts",
mod_document_action_buttons_group:
"./src/Bundle/ChillDocStoreBundle/Resources/public/module/document_action_buttons_group/index.ts",
mod_document_download_button:
"./src/Bundle/ChillDocStoreBundle/Resources/public/module/button_download/index.ts",
vue_document_signature:
"./src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/DocumentSignature/index.ts",
},
};

View File

@@ -52,7 +52,7 @@ class ChillEventExtension extends Extension implements PrependExtensionInterface
} }
/** (non-PHPdoc). /** (non-PHPdoc).
* @see \Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface::prepend() * @see PrependExtensionInterface::prepend()
*/ */
public function prepend(ContainerBuilder $container): void public function prepend(ContainerBuilder $container): void
{ {

View File

@@ -1 +1 @@
import("./chillevent.scss"); require("./chillevent.scss");

View File

@@ -1,10 +1,10 @@
{% extends '@ChillEvent/layout.html.twig' %} {% block js %} {% extends '@ChillEvent/layout.html.twig' %} {% block js %}
{{ vite_entry_script_tags("mod_async_upload") }} {{ encore_entry_script_tags("mod_async_upload") }}
{{ vite_entry_script_tags("mod_pickentity_type") }} {{ encore_entry_script_tags("mod_pickentity_type") }}
{% endblock %} {% block css %} {% endblock %} {% block css %}
{{ vite_entry_link_tags("mod_async_upload") }} {{ encore_entry_link_tags("mod_async_upload") }}
{{ vite_entry_link_tags("mod_pickentity_type") }} {{ encore_entry_link_tags("mod_pickentity_type") }}
{% endblock %} {% block title 'Event edit'|trans %} {% block event_content -%} {% endblock %} {% block title 'Event edit'|trans %} {% block event_content -%}
<div class="col-10"> <div class="col-10">

View File

@@ -1,9 +1,9 @@
{% extends '@ChillEvent/layout.html.twig' %} {% extends '@ChillEvent/layout.html.twig' %}
{% block css %} {% block css %}
{{ vite_entry_link_tags("mod_async_upload") }} {{ encore_entry_link_tags("mod_async_upload") }}
{{ vite_entry_link_tags("mod_pickentity_type") }} {{ encore_entry_link_tags("mod_pickentity_type") }}
{{ vite_entry_link_tags('vue_event') }} {{ encore_entry_link_tags('vue_event') }}
{% endblock %} {% endblock %}
{% block title 'Event creation'|trans %} {% block title 'Event creation'|trans %}
@@ -52,9 +52,9 @@
{% endblock %} {% endblock %}
{% block js %} {% block js %}
{{ vite_entry_script_tags("mod_async_upload") }} {{ encore_entry_script_tags("mod_async_upload") }}
{{ vite_entry_script_tags("mod_pickentity_type") }} {{ encore_entry_script_tags("mod_pickentity_type") }}
{{ vite_entry_script_tags('vue_event') }} {{ encore_entry_script_tags('vue_event') }}
<script type="text/javascript"> <script type="text/javascript">
window.entity = {{ entity_json|json_encode|raw }}; window.entity = {{ entity_json|json_encode|raw }};
{% if app.user.currentLocation is not null %}window.default_location_id = {{ app.user.currentLocation.id }};{% endif %} {% if app.user.currentLocation is not null %}window.default_location_id = {{ app.user.currentLocation.id }};{% endif %}

View File

@@ -1,10 +1,10 @@
{% extends '@ChillEvent/layout.html.twig' %} {% block title 'Events'|trans %} {% {% extends '@ChillEvent/layout.html.twig' %} {% block title 'Events'|trans %} {%
block js %} block js %}
{{ parent() }} {{ parent() }}
{{ vite_entry_script_tags("mod_pickentity_type") }} {{ encore_entry_script_tags("mod_pickentity_type") }}
{% endblock %} {% block css %} {% endblock %} {% block css %}
{{ parent() }} {{ parent() }}
{{ vite_entry_link_tags("mod_pickentity_type") }} {{ encore_entry_link_tags("mod_pickentity_type") }}
{% endblock %} {% block content %} {% endblock %} {% block content %}
<div class="col-10"> <div class="col-10">
<h1>{{ block("title") }}</h1> <h1>{{ block("title") }}</h1>

View File

@@ -6,14 +6,14 @@
{% block js %} {% block js %}
{{ parent() }} {{ parent() }}
{{ vite_entry_script_tags('mod_pickentity_type') }} {{ encore_entry_script_tags('mod_pickentity_type') }}
{{ vite_entry_script_tags('mod_document_action_buttons_group') }} {{ encore_entry_script_tags('mod_document_action_buttons_group') }}
{% endblock %} {% endblock %}
{% block css %} {% block css %}
{{ parent() }} {{ parent() }}
{{ vite_entry_link_tags('mod_pickentity_type') }} {{ encore_entry_link_tags('mod_pickentity_type') }}
{{ vite_entry_link_tags('mod_document_action_buttons_group') }} {{ encore_entry_link_tags('mod_document_action_buttons_group') }}
{% endblock %} {% endblock %}
{% macro insert_onthefly(type, entity, parent = null) %} {% macro insert_onthefly(type, entity, parent = null) %}

View File

@@ -14,6 +14,7 @@ namespace Chill\EventBundle\Security\Authorization;
use Chill\DocStoreBundle\Repository\AssociatedEntityToStoredObjectInterface; use Chill\DocStoreBundle\Repository\AssociatedEntityToStoredObjectInterface;
use Chill\DocStoreBundle\Security\Authorization\StoredObjectRoleEnum; use Chill\DocStoreBundle\Security\Authorization\StoredObjectRoleEnum;
use Chill\DocStoreBundle\Security\Authorization\StoredObjectVoter\AbstractStoredObjectVoter; use Chill\DocStoreBundle\Security\Authorization\StoredObjectVoter\AbstractStoredObjectVoter;
use Chill\MainBundle\Repository\Workflow\EntityWorkflowAttachmentRepository;
use Chill\MainBundle\Workflow\Helper\WorkflowRelatedEntityPermissionHelper; use Chill\MainBundle\Workflow\Helper\WorkflowRelatedEntityPermissionHelper;
use Chill\EventBundle\Entity\Event; use Chill\EventBundle\Entity\Event;
use Chill\EventBundle\Repository\EventRepository; use Chill\EventBundle\Repository\EventRepository;
@@ -26,8 +27,9 @@ class EventStoredObjectVoter extends AbstractStoredObjectVoter
private readonly EventRepository $repository, private readonly EventRepository $repository,
Security $security, Security $security,
WorkflowRelatedEntityPermissionHelper $workflowDocumentService, WorkflowRelatedEntityPermissionHelper $workflowDocumentService,
EntityWorkflowAttachmentRepository $attachmentRepository,
) { ) {
parent::__construct($security, $workflowDocumentService); parent::__construct($security, $attachmentRepository, $workflowDocumentService);
} }
protected function getRepository(): AssociatedEntityToStoredObjectInterface protected function getRepository(): AssociatedEntityToStoredObjectInterface

View File

@@ -0,0 +1,10 @@
module.exports = function (encore, entries) {
entries.push(__dirname + "/Resources/public/chill/index.js");
encore.addEntry(
"vue_event",
__dirname + "/Resources/public/vuejs/index.js",
);
};

View File

@@ -1,8 +0,0 @@
export default {
chillBase: [
"./src/Bundle/ChillEventBundle/Resources/public/chill/index.js",
],
inputs: {
vue_event: "./src/Bundle/ChillEventBundle/Resources/public/vuejs/index.js",
},
};

View File

@@ -1 +1 @@
import("./footer_bandeau.jpg"); require("./footer_bandeau.jpg");

View File

@@ -189,14 +189,14 @@ crud:
title_edit: Rapport "belemmering" bewerken title_edit: Rapport "belemmering" bewerken
title_delete: Belemmering verwijderen title_delete: Belemmering verwijderen
button_delete: Verwijderen button_delete: Verwijderen
confirm_message_delete: %as_string% verwijderen? confirm_message_delete: "%as_string% verwijderen?"
cscv: cscv:
title_new: Nieuw CV voor %person% title_new: Nieuw CV voor %person%
title_view: CV voor %person% title_view: CV voor %person%
title_edit: CV bewerken title_edit: CV bewerken
title_delete: CV verwijderen title_delete: CV verwijderen
button_delete: Verwijderen button_delete: Verwijderen
confirm_message_delete: %as_string% verwijderen? confirm_message_delete: "%as_string% verwijderen?"
no_date: Geen datum aangegeven no_date: Geen datum aangegeven
no_end_date: einddatum onbekend no_end_date: einddatum onbekend
no_start_date: startdatum onbekend no_start_date: startdatum onbekend
@@ -206,7 +206,7 @@ crud:
title_edit: Immersie bewerken title_edit: Immersie bewerken
title_delete: Immersie verwijderen title_delete: Immersie verwijderen
button_delete: Verwijderen button_delete: Verwijderen
confirm_message_delete: %as_string% verwijderen? confirm_message_delete: "%as_string% verwijderen?"
projet_prof: projet_prof:
title_new: Nieuw professioneel project voor %person% title_new: Nieuw professioneel project voor %person%
title_view: Professioneel project voor %person% title_view: Professioneel project voor %person%

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