Compare commits

..

2 Commits

Author SHA1 Message Date
8423c6c99c fix banners: resolve_center is always an iterable 2021-11-22 10:55:38 +01:00
03feb78037 fix down migration 2021-11-22 10:49:56 +01:00
1945 changed files with 74263 additions and 116916 deletions

64
.env Normal file
View File

@@ -0,0 +1,64 @@
##
## Manually dump .env files in .env.local.php with
## `$ composer symfony:dump-env prod`
##
## Project environment
APP_ENV=dev
## Enable debug
APP_DEBUG=true
## Locale
LOCALE=fr
## Framework secret
APP_SECRET=ThisTokenIsNotSoSecretChangeIt
## Symfony/swiftmailer
MAILER_TRANSPORT=smtp
MAILER_HOST=smtp
MAILER_PORT=1025
MAILER_CRYPT=
MAILER_AUTH=
MAILER_USER=
MAILER_PASSWORD=
MAILER_URL=${MAILER_TRANSPORT}://${MAILER_HOST}:${MAILER_PORT}?encryption=${MAILER_CRYPT}&auth_mode=${MAILER_AUTH}&username=${MAILER_USER}&password=${MAILER_PASSWORD}
## Notifications
NOTIFICATION_HOST=localhost:8001
NOTIFICATION_FROM_EMAIL=admin@chill.social
NOTIFICATION_FROM_NAME=Chill
## Gelf
GELF_HOST=gelf
GELF_PORT=12201
## OVH OpenStack Storage User/Role
OS_USERNAME=
OS_PASSWORD=
OS_TENANT_ID=
OS_REGION_NAME=GRA
OS_AUTH_URL=https://auth.cloud.ovh.net/v2.0/
## OVH OpenStack Storage Container
ASYNC_UPLOAD_TEMP_URL_KEY=
ASYNC_UPLOAD_TEMP_URL_BASE_PATH=
ASYNC_UPLOAD_TEMP_URL_CONTAINER=
## Redis Cache
REDIS_HOST=redis
REDIS_PORT=6379
REDIS_URL=redis://${REDIS_HOST}:${REDIS_PORT}
## Twilio
TWILIO_SID=~
TWILIO_SECRET=~
## DOCKER IMAGES REGISTRY
#IMAGE_PHP=
#IMAGE_NGINX=
## DOCKER IMAGES VERSION
#VERSION=test
VERSION=prod

View File

@@ -3,3 +3,4 @@
# Run tests from root to adapt your own environment # Run tests from root to adapt your own environment
KERNEL_CLASS='App\Kernel' KERNEL_CLASS='App\Kernel'
APP_SECRET='$ecretf0rt3st' APP_SECRET='$ecretf0rt3st'
DATABASE_URL=postgresql://postgres:postgres@db:5432/postgres?serverVersion=12&charset=utf8

2
.gitignore vendored
View File

@@ -20,5 +20,3 @@ docs/build/
.phpunit.result.cache .phpunit.result.cache
###< phpunit/phpunit ### ###< phpunit/phpunit ###
/.php-cs-fixer.cache
/.idea/

View File

@@ -1,10 +1,24 @@
--- ---
image: registry.gitlab.com/chill-projet/chill-app/php-base-image:7.4
# Select what we should cache between builds # Select what we should cache between builds
cache: cache:
paths: paths:
- tests/app/vendor/ - tests/app/vendor/
- .cache - .composer
before_script:
# add extensions to postgres
- PGPASSWORD=$POSTGRES_PASSWORD psql -U $POSTGRES_USER -h db -c "CREATE EXTENSION IF NOT EXISTS unaccent; CREATE EXTENSION IF NOT EXISTS pg_trgm;"
# Install and run Composer
- mkdir -p $COMPOSER_HOME
- curl -sS https://getcomposer.org/installer | php
- php -d memory_limit=2G composer.phar install
- php tests/app/bin/console doctrine:migrations:migrate -n
- php -d memory_limit=2G tests/app/bin/console cache:clear --env=dev
- php -d memory_limit=3G tests/app/bin/console doctrine:fixtures:load -n
- php -d memory_limit=2G tests/app/bin/console cache:clear --env=test
- echo "before_script finished"
# Bring in any services we need http://docs.gitlab.com/ee/ci/docker/using_docker_images.html#what-is-a-service # Bring in any services we need http://docs.gitlab.com/ee/ci/docker/using_docker_images.html#what-is-a-service
# See http://docs.gitlab.com/ee/ci/services/README.html for examples. # See http://docs.gitlab.com/ee/ci/services/README.html for examples.
@@ -16,12 +30,9 @@ services:
# Set any variables we need # Set any variables we need
variables: variables:
GIT_DEPTH: 1
# Configure postgres environment variables (https://hub.docker.com/r/_/postgres/) # Configure postgres environment variables (https://hub.docker.com/r/_/postgres/)
POSTGRES_USER: postgres POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres POSTGRES_PASSWORD: postgres
# configure database access
DATABASE_URL: postgresql://postgres:postgres@db:5432/postgres?serverVersion=12&charset=utf8
# fetch the chill-app using git submodules # fetch the chill-app using git submodules
GIT_SUBMODULE_STRATEGY: recursive GIT_SUBMODULE_STRATEGY: recursive
REDIS_HOST: redis REDIS_HOST: redis
@@ -29,74 +40,11 @@ variables:
REDIS_URL: redis://redis:6379 REDIS_URL: redis://redis:6379
# change vendor dir to make the app install into tests/apps # change vendor dir to make the app install into tests/apps
COMPOSER_VENDOR_DIR: tests/app/vendor COMPOSER_VENDOR_DIR: tests/app/vendor
DEFAULT_CARRIER_CODE: BE # cache some composer data
COMPOSER_HOME: .composer
stages:
- Composer install
- Tests
build: # Run our tests
stage: Composer install test:
image: registry.gitlab.com/chill-projet/chill-app/php-base-image:7.4
before_script:
- curl -sS https://getcomposer.org/installer | php
- php -d memory_limit=2G composer.phar config -g cache-dir "$(pwd)/.cache"
script: script:
- php -d memory_limit=2G composer.phar install --optimize-autoloader --no-ansi --no-interaction --no-progress - php -d memory_limit=3G bin/phpunit --colors=never
cache:
paths:
- .cache/
artifacts:
expire_in: 30 min
paths:
- bin
- tests/app/vendor/
code_style:
stage: Tests
image: registry.gitlab.com/chill-projet/chill-app/php-base-image:7.4
script:
- bin/grumphp run --tasks=phpcsfixer
artifacts:
expire_in: 30 min
paths:
- bin
- tests/app/vendor/
phpstan_tests:
stage: Tests
image: registry.gitlab.com/chill-projet/chill-app/php-base-image:7.4
script:
- bin/grumphp run --tasks=phpstan
artifacts:
expire_in: 30 min
paths:
- bin
- tests/app/vendor/
psalm_tests:
stage: Tests
image: registry.gitlab.com/chill-projet/chill-app/php-base-image:7.4
script:
- bin/grumphp run --tasks=psalm
allow_failure: true
artifacts:
expire_in: 30 min
paths:
- bin
- tests/app/vendor/
unit_tests:
stage: Tests
image: registry.gitlab.com/chill-projet/chill-app/php-base-image:7.4
script:
- php tests/app/bin/console doctrine:migrations:migrate -n
- php -d memory_limit=2G tests/app/bin/console cache:clear --env=dev
- php -d memory_limit=3G tests/app/bin/console doctrine:fixtures:load -n
- php -d memory_limit=2G tests/app/bin/console cache:clear --env=test
- php -d memory_limit=4G bin/phpunit --colors=never
artifacts:
expire_in: 30 min
paths:
- bin
- tests/app/vendor/

View File

@@ -1,79 +0,0 @@
<?php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
/** @var \drupol\PhpCsFixerConfigsPhp\Config\Php73 $config */
$config = require __DIR__ . '/tests/app/vendor/drupol/php-conventions/config/php73/php_cs_fixer.config.php';
$config
->getFinder()
->ignoreDotFiles(false)
->notPath('tests/app')
->name(['.php_cs.dist.php']);
$rules = $config->getRules();
$riskyRules = [
'ternary_to_elvis_operator' => false,
'php_unit_mock_short_will_return' => false,
'php_unit_set_up_tear_down_visibility' => false,
'php_unit_construct' => false,
'php_unit_dedicate_assert' => false,
'php_unit_expectation' => false,
'php_unit_mock' => false,
'php_unit_namespaced' => false,
'php_unit_no_expectation_annotation' => false,
'php_unit_test_case_static_method_calls' => false,
'php_unit_test_annotation' => false,
// 'final_internal_class' => false,
// 'strict_param' => false,
// 'declare_strict_types' => false,
// 'strict_comparison' => false,
// 'no_unreachable_default_argument_value' => false,
// 'ereg_to_preg' => false,
// 'ordered_interfaces' => false,
// 'error_suppression' => false,
// 'non_printable_character' => false,
// 'ordered_traits' => false,
// 'no_useless_sprintf' => false,
// 'dir_constant' => false,
// 'no_alias_functions' => false,
// 'implode_call' => false,
// 'combine_nested_dirname' => false,
// 'pow_to_exponentiation' => false,
// 'comment_to_phpdoc' => false,
// 'no_unset_on_property' => false,
// 'native_constant_invocation' => false,
// 'function_to_constant' => false,
// 'is_null' => false,
// 'native_function_invocation' => false,
// 'no_trailing_whitespace_in_string' => false,
// 'array_push' => false,
// 'fopen_flag_order' => false,
// 'fopen_flags' => false,
// 'logical_operators' => false,
// 'modernize_types_casting' => false,
// 'no_homoglyph_names' => false,
// 'no_unneeded_final_method' => false,
// 'random_api_migration' => false,
// 'static_lambda' => false,
// 'set_type_to_cast' => false,
// 'string_line_ending' => false,
// 'psr_autoloading' => false,
];
$rules = array_merge(
$rules,
$riskyRules
);
$rules['header_comment']['header'] = trim(file_get_contents(__DIR__ . '/resource/header.txt'));
return $config->setRules($rules);

View File

@@ -12,433 +12,6 @@ and this project adheres to
<!-- write down unreleased development here --> <!-- write down unreleased development here -->
## Test releases
### 2021-04-26
* [Datepickers] datepickers fixed when using keyboard to enter date (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/545)
* [social_action] Display 'agents traitants' in parcours resumé and social action list (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/568)
* [Person_search] Closed parcours shown within an accordeon that can be opened/closed (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/574)
### 2021-04-24
* [notification email on course designation] allow raw string in email content generation
* [Accompanying period work] list evaluations associated to a work by startDate, and then by id, from the most recent to older
* [Documents] Change wording 'créer' to 'enregistrer' (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/634)
* [Parcours]: The number of 'mes parcours' displayed (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/572)
* [Hompage_widget]: Renaming of tabs and removal of social actions tab (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/570)
* [activity]: Ignore thirdparties when creating a social action via an activity (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/573)
* [parcours]: change wording of warning message and button when user is not associated to a household yet (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/590#note_918370943)
* [Accompanying period work evaluations] list documents associated to a work by creation date, and then by id, from the most recent to older
* [Course comment] add validationConstraint NotNull and NotBlank on comment content, to avoid sql error
* [Notifications] delay the sending of notificaiton to kernel.terminate
* [Notifications / Period user change] fix the sending of notification when user changes
* [Activity form] invert 'incoming' and 'receiving' in Activity form
* [Activity form] keep the same order for 'attendee' field in new and edit form
* [list with period] use "sameas" test operator to introduce requestor in list
* [notification email on course designation] allow raw string in email content generation
* [Accompanying period work] list evaluations associated to a work by startDate, and then by id, from the most recent to older
### 2021-04-13
* [person] household address: add a form for editing the validFrom date (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/541)
* [person] householdmemberseditor: fix composition type bug in select form (vuejs) (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/543)
* [docgen] add more persons choices in docgen for course: amongst requestor (if person), resources of course (if person), and PersonResource (if person);
* [docgen] add a new context with a list of activities in course
* [docgen] add a comment in budget lines
* [notifications] allow to send a notification to an email address. The address receive an access link
* [adresses] add constraints in database to avoid errors later: postcode not null, and validfrom <= validto
* [accompanying work editor] add a label on document title input
### 2021-04-07
* notification list: move action buttons outside of the toggle
* fix detecting of non-read notification
* filter users which are disabled in search user api
* order query for location and add pagination in list
* allow every person which has part for a workflow to see the workflow page
* able to see the workflow if the evaluation document has been deleted
* hardcode the list of supported mime types for edition with collabora
* list of accompanying course: allow to see the pinned comment in list_item
### 2021-04-06
* [main] notification toggle read: correct js syntax for compilation in production (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/548)
* [parcours] Display of interlocuteurs changed to flex-table in parcours edit page to prevent cut-off of information (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/535)
* [activity] espace entre les boutons pour supprimer les documents
### continuous release in February and March
* Creation of PickCivilityType, and implementation in PersonType and ThirdpartyType
* [person] Accompanying course evaluation documents: disable the WOPI edit link if mimetype not supported and if no keyInfos
(https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/585)
* [activity] display error messages above the form in creating a new location (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/481)
* [activity] show required field in activity edit/new by an asterix in the vuejs fields (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/494)
* [ACL] fix allow to see the course, event if the scope'course does not contains the scope's user
* [search] enforce limit of results for fetching rsults by search api https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/576
* [activity] Fix delete button for document (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/554)
* [activity] Add return path the document generation (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/553)
* [person] add person ressource to person docgen normaliser (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/517)
* [person] AccompanyingCourseWorkEdit: fix deleting evaluation documents (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/546)
* [person] AccompanyingCourseWorkEdit: download existing documents (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/512)
* [person] AccompanyingCourseWorkEdit: replace document by a new one (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/511)
* [person] AccompanyingPeriodWork: add referrers to work, add doctrine event listener to add logged user to referrers collection and display a referrers list in work list (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/502)
* [person] AccompanyingPeriodWorkEvaluation: fix circular reference when serialising (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/495)
* [person] order accompanying period by opening date in search persons, person and household period lists (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/493)
* [parcours] autosave of the pinned comment for draft accompanying course (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/477)
* [main] filter user job in undispatch acc period to assign (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/472)
* [main] filter user job in undispatch acc period to assign (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/472)
* [person] Add url in accompanying period work evaluations entity and form (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/476)
* [person] Add document generation in admin and in person/{id}/document (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/464)
* [activity] do not override location if already exist (when validating new activity) (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/470)
* [parcours] Toggle emergency/intensity only by referrer (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/442)
* [docstore] Add an API entrypoint for StoredObject (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/466)
* [person] Add the possibility of uploading existing documents to AccPeriodWorkEvaluationDocument (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/466)
* [person] Add title to AccPeriodWorkEvaluationDocument (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/466)
* [person] Order social issues by the field "ordering" (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/388)
* [Person/Household list] when listing other simultaneous members of an household, exclude the members on person, not on members (avoid to show two membersship with the same person)
* [draft periods] add a delete button (if acl granted) on each draft period listed on draft period page (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/463)
* [Person] Display suffixText in RenderPerson, PersonText.vue, RenderPersonBox.vue (was made for displaying "enfant confie") (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/441)
* [budget]: budget enabled for persons and households (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/469)
* [person] residential address: show residential address or info in PersonRenderBox, refactor Residential Address (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/439)
* [thirdparty] Add a contact to a thirdparty from within onTheFly (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/345)
* [documents] Improve flex-table item-col placement when long buttons and long metadata
* [thirdparty] Fix display of multiple contact badges so they wrap onto next line (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/482)
* [confidential] Fix position of toggle button so it does not cover text nor fall outside of box (no issue)
* [parcours] Fix edit of both thirdparty and contact name (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/474)
* [template] do not list inactive templates (for doc generator)
* [household] bugfix if position of member is null, renderbox no longer throws an error (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/480)
* [parcours] location cannot be removed if linked to a user (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/478)
* [person] email added to twig personRenderbox (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/490)
* [activity] Only youngest descendant is kept for social issues and actions (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/471)
* [person] Add link to current household in person banner (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/484)
* [address] person badge in address history changed to open OnTheFly with all person info (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/489)
* [person] Change 'personne' with 'usager' and '&' with 'ET' (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/499)
* [thirdparty] Add parameter condition to display centers or not (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/500)
* [phonenumber] Remove placeholder in phonenumber field (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/496)
* [person_resource] separate create page created to avoid confusion (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/504)
* [contact] add contact button color changed plus the pipe at the side removed (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/506)
* [thirdparty] For contacts show current civility/profession in edit form + fix saving of edited information (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/491)
* [household] create-edit household composition placed in separate page to avoid confusion (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/505)
* [blur] Improved positioning of toggle icon (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/486)
* [thirdparty] add firstname field to thirdparty 'child' or 'contact' types (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/508)
* [household] create-edit household composition placed in separate page to avoid confusion (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/505)
* [blur] Improved positioning of toggle icon (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/486)
* [parcours] List of parcours for a specific user so they can be reassigned in case of absence (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/509)
* [thirdparty] Thirdparty view page, english text translated (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/534)
* [social_action] Translation changed in evaluation section (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/512)
* [filiation] Possible to add person (or create onthefly) to add to filiation graph + add relation (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/519)
* [household] Within parcours listing page of household add create button (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/560)
* [person_resource] bugfix when adding thirdparty or freetext resource + prevent personOwner themselves to be added. (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/526)
* [aside_activity] style correction + sticky-form create button (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/529)
* [budget] order within the menu adjusted (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/592)
* [onthefly] fix create person. Bug was noticed in filiation (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/591)
* [parcours] Create document buttons made sticky (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/532)
* [person] Trailing guillemet removed (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/530)
* [notification] Display of social action within workflow notification set to display block (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/537)
* [onthefly] trim trailing whitespace in email of person and thirdparty (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/542)
* [action] Only youngest descendant is kept for social issues and actions (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/471)
## Test releases
### test release 2022-02-21
* [notifications] Word 'un' changed to number '1' for notifications in user menu (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/483)
* [documents] 'gabarit' changed to 'modèle' (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/405)
* [person_resources] Menu name and order changed (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/460)
* workflow: fix sending notifications
* [thirdparty] Extend the thirdparty search to thirdparty children (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/448)
* [person]: AddPersons: allow creation of person or thirdparty only (no users) (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/422)
* [person]: AddPersons: allow creation of person or thirdparty depending on allowed types (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/422)
* [person]: AddPersons: add suggestion of name when creating new person or thirdparty (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/422)
* [main] Address: fix small bug: when modifying an address without street (isNoAddress), also check errors if street is an empty string as back-end change null value to empty string for street (and streetNumber)
* [main] Address: stronger client-side validation of addresses (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/449)
* [person] accompanying course: filter suggested entities by open participations (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/415)
[activity] can click through the cross icon for removing person in concerned group (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/476)
[activity] correct associated persons by considering only open participations (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/476)
* [person_resources]: Renderboxes used to display person/thirdparty info (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/465)
* [Household]: Add end date in HouseholdMember form for 'enfant hors menage' (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/434)
* [homepage_widget]: If no sender then display as 'notification automatique' (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/435)
* [parcours]: Order social activities and only display most recent three in parcours resumé (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/481)
* [3party]: 3party: redirect to parent when contact (child) is opened in view page
* [parcours / addresses]: launch an event when a person change address (either through changing household or because the household is associated to a new address). If the person is localising a course, the course location go back to a temporarily address.
* [thirdparty]: address/phonenumber/email/fonction displayed in thirdpartyrenderbox (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/401)
* [thirdparty_contact]: in search results the 'qualité' is displayed (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/465)
* [bug]: fix confidential toggle of address in thirdpartyrenderbox (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/460)
### test release 2022-02-14
* AddPersons: remove ul-li html tags from AddPersons (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/419)
* [doc-generator] do not set required fields for mainPerson, person1, person2 (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement#456)
* [doc-generation] add age and obele in the mainPerson, person1 and person2 list + add obele in person renderString if addAge (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/370)
* [person] accompanying course work: fix on-the-fly update of thirdParty
* fix normalisation of accompanying course requestor api (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/378)
* [person] add a returnPath when clicking on some Person or ThirdParty badge (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/427)
* [person] accompanying course work: fix on-the-fly update of thirdParty
* [on-the-fly] close modal only after validation
* [person] correct thirdparty PATCH url + add email and altnames in AddPerson and serializer (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/433)
* change order for accompanying course work list
* [parcours]: Mes parcours brouillon added to user menu (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/440)
* [Documents]: List view adapted to display more information (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/414)
* [person]: style fix in parcours listing per person. (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/432)
* [parcours]: Only the referrer can toggle the intensity of the parcours (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/442)
* [household]: display address of current household (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/415)
* ajoute un ordre dans les localisation (api)
* [pick entity]: fix translations in modal (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/419)
* [homepage_widget]: fix translation on emergency badge (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/440)
* [person]: create person and household added to button dropdown (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/454)
* display full address in address.text in normalization. Adapt AddressRenderBox
* [address]: Correction residential address 'depuis le' (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/459)
* [Documents]: List view adapted to display more information (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/414)
* [Thirdparty_contact]: address blurred if confidential in view page (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/450)
* [thirdparty] Add a contact to a thirdparty from within onTheFly (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/345)
### test release 2021-02-01
* renommer "dossier numéro" en "parcours numéro" dans les résultats de recherche
* renomme date de début en date d'ouverture dans le formulaire parcours
* [homepage widget] improve content tables, improve counter pluralization with style on number
* [notification lists] add comments counter information
* [workflows] fix popover header with previous transition
* [parcours]: validation + message for closing parcours adjusted.
* [household]: household composition double edit button replaced by a delete action (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/426)
[fast_actions] improve fast-actions buttons override mechanism, fix https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/413
[homepage widget] add vue homepage_widget with asynchone loading, give a global view resume of the user concerned actions, notifications, etc.
* [person]: Comment on marital status is possible even if marital status is not defined (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/421)
* [parcours]: In the list of person results the requestor is not displayed if defined as anonymous (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/424)
* [bugfix]: modal closes and newly created person/thirdparty is selected when multiple persons/thirdparties are created through the modal (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/429)
* [person_resource]: Onthefly button added to view person/thirdparty and badge differentiation for a contact-thirdparty (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/428)
* [workflow][notification] improve how notifications and workflows are 'attached' to entities: contextual list, counter, buttons and vue modal
* [AddAddress] disable multiselect search, and rely only on most pertinent Cities and Street computed backend
* [fast_actions] improve fast-actions buttons override mechanism, fix https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/413
* [homepage widget] add vue homepage_widget with asynchone loading, give a global view resume of the user concerned actions, notifications, etc.
* [thirdparty] Add a contact to a thirdparty from within onTheFly (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/345)
* [homepage widget] add vue homepage_widget with asynchone loading, give a global view resume of the user concerned actions, notifications, etc.
### test release 2021-01-31
* [person] accompanying course: optimisation: do not fetch some resources for the banner (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/409)
* [person] accompanying course: close modal when edit participation (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/420)
* [person] accompanying course: treat validation error when editing on-the-fly entities (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/420)
* [activity] show activity attendee (présence) in the activity list (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/412)
* [activity] admin: change validation rule for social action visible field (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/413)
* [parcours]: component added to change the opening date of a parcours (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/411)
* [search]: listing of parcours display changed (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/410)
* [user]: page with accompanying periods to which is user is referent (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/408)
* [person] age added to renderstring + renderbox/ vue component created to display person text (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/389)
* [household member editor] allow to push to existing household
### test release 2021-01-28
* [person] improve filiations vis graph: disable physics, use chill colors for persons-households-course, increase label of relations, remove labels on household arrows and other improvements (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/286, https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/362)
* [activity] Order activity by date and by id (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/364)
* [main] increase length of 4 Address fields (change to TEXT, no size limits) (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/277)
* [main] Add confidential option for address, in edit and view (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/165)
* [person] name suggestions within create person form when person is created departing from a search input (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/377)
* [person] Add residential address entity, form and list for each person
* [aside_activity]: dynamicUserPickerType used (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/399)
* dispatching list
### test release 2021-01-26
* [parcours] comments truncated if too long + link added (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/406)
* [person]: possibility to add person resources (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/382)
* [person ressources]: module added
### test release 2022-01-24
* [person] name suggestions within create person form when person is created departing from a search input (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/377)
* [notification: formulaire création] descend la box avec la description dans le bas du formulaire
* [notification for activity]: fix link to activity
* [notification] add "URGENT" before accompanying course with emergency = true
* [notification] add a "read more" button on system notification
* [notification] add `[Chill]` in the subject of each notification, automatically
* [notification] add a counter for notification in activity list and accompanying period list, and search results
* [parcours] bugfix if deathdate is not defined (eg. for a thirdparty) parcours is still displayed. Gave error before.
* [workflow] add breadcrumb to show steps
* [popover] add popover html popup mechanism (used by workflow breadcrumb)
* [templates] improve updatedBy macro in item metadatas
* [parcours]: bug fix when comment is pinned all other comments remain in the collection (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/385)
* [workflow]
* add My workflow section with my opened subscriptions
* apply workflow on documents, accompanyingCourseWork and Evaluations
* [wopi-link] a new vue component allow to open wopi link in a fullscreen chill-themed modal
### test release 2022-01-19
* vuejs: add dead information on all on-the-fly person render boxes, in vis graph and other templates (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/271)
* [thirdparty] fix bug in 3rd party view: types was replaced by thirdPartyTypes
* [main] location form type: fix unmapped address field (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/246)
* [activity] fix wrong import of js assets for adding and viewing documents in activity (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/83 & https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/176)
* [person]: space added between deathdate and age in twig renderbox (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/380)
* [forms] dynamic picker types for user/person/thirdparty types created (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/386)
### test release 2022-01-17
* [main] Add editableByUser field to locationType entity, adapt the admin template and add this condition in the location-type endpoint (see https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/297)
* [main] Add mainLocation field to User entity and add it in user form type
* rewrite page which allow to select activity
* [main] Add mainLocation field to User entity and add it in user form type
* [course list in person context] show full username/label for ref
* [accompanying period work] remove the possibility to generate document from an accompanying period work
* vuejs: add validation on required fields for AddPerson, Address and Location components
* vuejs: treat 422 validation errors in locations and AddPerson components
* [person]: space added between deathdate and age in twig renderbox (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/380)
## Test releases
* vuejs: add validation on required fields for AddPerson, Address and Location components
* vuejs: treat 422 validation errors in locations and AddPerson components
* [person]: space added between deathdate and age in twig renderbox (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/380)
### test release 2022-01-12
* fix thirdparty normalizer on telephone field: https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/322
### test release 2022-01-11
* vuejs: translate in French all multiselect widgets
* [address] define address lines according postal standards for France and Belgium (default) and change AddressRender, chill_entity_render_box and AddressRenderBox.vue
* [household] change translations (champs-libres/departement-de-la-vendee/accent-suivi-developpement#109)
* [household] add address i18n in household component (champs-libres/departement-de-la-vendee/accent-suivi-developpement#158)
* [household] add on the fly i18n in household component
* [household] redirect to the household page when a household is created from a person (champs-libres/departement-de-la-vendee/accent-suivi-developpement#175)
* [household] household member editor: display alert if some members have already an household (champs-libres/departement-de-la-vendee/accent-suivi-developpement#172)
* [household] household member editor: do not add in new members if the member is included in the members of household (champs-libres/departement-de-la-vendee/accent-suivi-developpement#123)
* [household] household member editor: remove markNoAddress button (champs-libres/departement-de-la-vendee/accent-suivi-developpement#109)
* [person]: ordering fields in add person (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/61)
* [person]: Add email and alt names in add person (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/61)
* [accompanyingCourse] Add a delete action and delete buttons to delete a accompanying course when step = DRAFT (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/64)
* [accompanyingCourse] Add a administrative location in the accompanying course, set the user current location as default, allow to select a location in a select field and do not allow to confirm the accompanying course if location is empty.
* [accompanyingCourse] Add the administrative location in the available variables for document generation
* AddAddress: optimize loading: wait for the user finish typing;
* UserPicker: fix bug with deprecated role
* docgen: add base context + tests
* docgen: add age for person
* [household menu] fix filiation order https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/265
* [AddAddress]: optimize loading: wait for the user finish typing;
* [UserPicker]: fix bug with deprecated role
* [docgen]: add base context + tests
* [docgen]: add age for person
* [task]: fix dropdown menu style + fix bug in singleTaskController (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/338)
* Household: fix bug when moving person on the same day (see https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/281)
* Household: show date validFrom and validTo when moving
* address reference: add index for refid
* [accompanyingCourse_work] fix styles conflicts + fix bug with remove goal (remove goals one at a time)
* [accompanyingCourse] improve masonry on resume page, add origin
* [notification] new notification interface, can be associated to AccompanyingCourse/Period, Activities.
* List notifications, show, and comment in User section
* Notify button and contextual notification box on associated objects pages
* [accompanyingCourse] add a comment for each resource associated. A modal allow to save comment. Comment is displayed in on-the-fly show modal of the accompanyingCourse context (edit page + resume page).
### test release 2021-12-14
* [asideactivity] creation of aside activity category fixed (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/262)
* [vendee/person] fix typo "situation professionelle" => "situation professionnelle"
* [main] add availableForUsers condition from locationType in the location API endpoint (champs-libres/departement-de-la-vendee/accent-suivi-developpement#248)
* [main] add the current location of the user as API point + add it in the activity location list (champs-libres/departement-de-la-vendee/accent-suivi-developpement#247)
* [activity] improve show/new/edit templates, fix SEE and SEE_DETAILS acl
* [badges] create specific badge for TMS, and make person/thirdparty badges clickable with on-the-fly modal in :
* concerned groups items (activity, calendar)
* accompanyingCourseWork lists
* accompanyingCourse lists
* [acompanyingCourse] add initial comment on Resume page
* [person] create button full width (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/330)
### test release 2021-12-11
* [main] add order field to civility
* [main] change address format in case the country is France, in Address render box and address normalizer
* [person] add validator for accompanying period with a test on social issues (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/76)
* [activity] fix visibility for location
* [origin] fix origin: use correctly the translatable strings
* /!\ everyone must update the origin table. As there is only one row, execute `update chill_person_accompanying_period_origin set label = jsonb_build_object('fr', 'appel téléphonique');`
* [person] redirect bug fixed.
* [action] add an unrelated issue within action creation.
* [origin] fix origin: use correctly the translatable strings
* /!\ everyone must update the origin table. As there is only one row, execute `update chill_person_accompanying_period_origin set label = jsonb_build_object('fr', 'appel téléphonique');`
* [main] change order of civilities in civility fixtures (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/191)
* [person] set min attr in the minimum of children field (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/191)
* [person] add marital status date in person view (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/191)
* [person] show number of children + allow set number of children to null (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/191)
* [person] show acceptSMS option (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/191)
* [person] add death information in person render box in twig and vue render boxes (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/191)
* [asideactivity] creation of aside activity category fixed (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/262)
* [vendee/person] fix typo "situation professionelle" => "situation professionnelle"
* [accompanyingcourse_work] Changes in layout/behavior of edit form (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/321)
* [badge-entity] design coherency between pills badge-person and 3 kinds of badge-thirdparty
* [AddPersons] suggestions row are clickable, not only checkbox
### test release 2021-12-06
* [main] address: use search API end points for getting postal code and reference address (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/316)
* [main] address: in edit mode, select the encoded values in multiselect for address reference and city (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/316)
* [person search] fix bug when using birthdate after and birthdate before
* [person search] increase pertinence when lastname begins with search pattern
* [activity/actions] Améliore la cohérence du design entre
* la page résumé d'un parcours (liste d'actions récentes et liste d'activités récentes)
* la page liste des actions
* la page liste des activités (contexte personne / contexte parcours)
* [household] field to edit wheter person is titulaire of household or not removed (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/322)
* [activity] create work if a work with same social action is not associated to the activity
* [visgraph] improve and fix bugs on vis-network relationship graph
* [bugfix] posting of birth- and deathdate through api fixed.
* [suggestions] improve suggestions lists
### Test release 2021-11-19 - bis
* [household] do not allow to create two addresses on the same date
* [activity] handle case when there is no social action associated to social issue
* [activity] layout for issues / actions
* [activity][bugfix] in edit mode, the form will now load the social action list
### Test release 2021-11-29
* [person] suggest entities (person | thirdparty) when creating/editing the accompanying course (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/119)
* [activity] add custom validation on the Activity class, based on what is required from the ActivityType (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/188)
* [main] translate multiselect messages when selecting/creating address
* [main] set the coordinates of the city when creating a new address OR choosing "pas d'adresse complète"
* Use the user.label in accompanying course banner, instead of username;
* fix: show validation message when closing accompanying course;
* [thirdparty] link from modal to thirdparty detail page fixed (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/228)
* [assets] new asset to style suggestions lists (with add/remove item link) (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/258)
* [accompanyingCourseWorkEdit] improves hyphenation and line breaks for long badges
* [acompanyingCourse] improve Resume page
* complete all needed informations,
* actions and activities are clickables,
* better placement with js masonry blocks on top of content area,
* https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/101
* https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/295
* [activity/calendar] on show page, concerned groups of persons table adapt itself to isVisibles options
* [activity] remove the "plus" button in activity list
* [activity] check ACL on activity list in person context
* [list for accompanying course in person] filter list using ACL
* [validation] toasts are displayed for errors when modifying accompanying course (generalization required).
* [period] only the user can enable confidentiality
* add an endpoint for checking permissions. See https://gitlab.com/Chill-Projet/chill-bundles/-/merge_requests/232
* [activity] for a new activity: suggest and create on-the-fly locations based on the accompanying course location + location of the suggested parties
* [calendar] for a new rdv: suggest and create on-the-fly locations based on the accompanying course location + location of the suggested parties
* [period] Validation added when period is confidential and confirmed -> user cannot be null.
## Test releases
### Test release 2021-11-22
* [activity] delete admin_user_show in twig template because this route is not defined and should be defined
* [activity] suggest requestor, user and ressources for adding persons|user|3rdparty
* [calendar] suggest persons, professionals and invites for adding persons|3rdparty|user
* [activity] take into account the restrictions on person|thirdparties|users visibilities defined in ActivityType
* [main] Add currentLocation to the User entity + add a page for selecting this location + add in the user menu (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/133)
* [activity] add user current location as default location for a new activity (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/133)
* [task] Select2 field in task form to allow search for a user (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/167) * [task] Select2 field in task form to allow search for a user (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/167)
* remove "search by phone configuration option": search by phone is now executed by default * remove "search by phone configuration option": search by phone is now executed by default
* remplacer le classement par ordre alphabétique par un classement par ordre de pertinence, qui tient compte: * remplacer le classement par ordre alphabétique par un classement par ordre de pertinence, qui tient compte:
@@ -455,11 +28,9 @@ and this project adheres to
* [person] create an accompanying course: add client-side validation if no origin (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/210) * [person] create an accompanying course: add client-side validation if no origin (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/210)
* [person] fix bounds for computing current person address: the new address appears immediatly * [person] fix bounds for computing current person address: the new address appears immediatly
* [docgen] create a normalizer and serializer for normalization on doc format * [docgen] create a normalizer and serializer for normalization on doc format
* [person normalization] the key center is now "centers" and is an array. Empty array if no center
* [accompanyingCourse] Ability to close accompanying course (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/296)
* [task] Select2 field in task form to allow search for a user (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/167) ## Test releases
* [list result] show all courses, except ones with period closed
* [accompanyingCourse] improve banner with small carousel to display slide social-issues or slide associated persons (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/69)
### Test release 2021-11-15 ### Test release 2021-11-15
@@ -477,8 +48,6 @@ and this project adheres to
* [person] do not ask for center any more on person creation * [person] do not ask for center any more on person creation
* [3party] do not ask for center any more on 3party creation * [3party] do not ask for center any more on 3party creation
## Test releases
### Test release 2021-11-08 ### Test release 2021-11-08
* [person]: Display the name of a user when searching after a User (TMS) * [person]: Display the name of a user when searching after a User (TMS)
@@ -545,7 +114,7 @@ and this project adheres to
* fast creation buttons * fast creation buttons
* add ordering for types * add ordering for types
* [AccompanyingCourse Resume page] dashboard for AccompanyingCourseWork and for Activities; * [AccompanyingCourse Resume page] badge-title for AccompanyingCourseWork and for Activities;
* Improve badges behaviour with small screens; * Improve badges behaviour with small screens;
* [ThirdParty]: * [ThirdParty]:

View File

@@ -98,7 +98,7 @@ Finalement, il importe ici de définir ce qu'est un bloc, ce qu'est une zone d'a
Par exemple pour mettre un style au titre on précise juste h3 dans la cascade bloc. Par exemple pour mettre un style au titre on précise juste h3 dans la cascade bloc.
```scss ```sass
div.bloc { div.bloc {
// un bloc générique, utilisé à plusieurs endroits // un bloc générique, utilisé à plusieurs endroits
&.bloc-dark { &.bloc-dark {
@@ -381,31 +381,3 @@ A chaque fois qu'un lien est indiqué, vérifier si on ne doit pas utiliser la f
* utiliser `chill_return_path_or` dans le bouton "Cancel"; * utiliser `chill_return_path_or` dans le bouton "Cancel";
* pour les boutons "enregistrer et voir" et "Enregistrer et fermer" => ? * pour les boutons "enregistrer et voir" et "Enregistrer et fermer" => ?
### Assets pour les listes de suggestion
Créer une liste de suggestions à ajouter (tout l'item est cliquable)
```html
<ul class="list-suggest add-items">
<li>
<span>item</span>
</li>
</ul>
```
Créer une liste de suggestions à enlever (avec une croix rouge cliquable, l'ancre a est vide)
```html
<ul class="list-suggest remove-items">
<li>
<span>
item
</span>
</li>
</ul>
```
Créer un titre enlevable (avec une croix rouge cliquable, l'ancre a est vide)
```html
<div class="item-title">
<span>title</span>
</div>
```
Les classes `cols` ou `inline` peuvent être ajoutées à côté de `list-suggest` pour modifier la disposition de la liste.
Dans le dernier exemple, on met une classe `removable` sur le span, si on veut pouvoir enlever l'élément.

View File

@@ -629,12 +629,12 @@ to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found. the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.> chill-bundles
Copyright (C) <year> <name of author> Copyright (C) 2021 Chill Project
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by it under the terms of the GNU Affero General Public License as published
the Free Software Foundation, either version 3 of the License, or by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
This program is distributed in the hope that it will be useful, This program is distributed in the hope that it will be useful,

BIN
composer

Binary file not shown.

View File

@@ -1,86 +1,80 @@
{ {
"name": "chill-project/chill-bundles", "name": "chill-project/chill-bundles",
"description": "Most used bundles for chill-project",
"license": "AGPL-3.0-only",
"type": "library", "type": "library",
"description": "Most used bundles for chill-project",
"keywords": [ "keywords": [
"chill", "chill",
"social worker" "social worker"
], ],
"license": "AGPL-3.0-only",
"require": { "require": {
"php": "^7.4", "champs-libres/async-uploader-bundle": "dev-sf4",
"champs-libres/async-uploader-bundle": "dev-sf4#d57134aee8e504a83c902ff0cf9f8d36ac418290", "champs-libres/wopi-bundle": "dev-master",
"champs-libres/wopi-bundle": "dev-master#59b468503b9413f8d588ef9e626e7675560db3d8", "composer/package-versions-deprecated": "^1.10",
"champs-libres/wopi-lib": "dev-master#0e1da19bb6de820080b8651867a7e475be590060",
"doctrine/doctrine-bundle": "^2.1", "doctrine/doctrine-bundle": "^2.1",
"doctrine/doctrine-migrations-bundle": "^3.0", "doctrine/doctrine-migrations-bundle": "^3.0",
"doctrine/orm": "^2.7", "doctrine/orm": "^2.7",
"erusev/parsedown": "^1.7", "erusev/parsedown": "^1.7",
"graylog2/gelf-php": "^1.5", "graylog2/gelf-php": "^1.5",
"knplabs/knp-menu": "^3.1",
"knplabs/knp-menu-bundle": "^3.0", "knplabs/knp-menu-bundle": "^3.0",
"knplabs/knp-time-bundle": "^1.12", "knplabs/knp-time-bundle": "^1.12",
"league/csv": "^9.7.1", "league/csv": "^9.7.1",
"nyholm/psr7": "^1.4", "nyholm/psr7": "^1.4",
"ocramius/package-versions": "^1.10 || ^2",
"odolbeau/phone-number-bundle": "^3.6",
"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",
"spomky-labs/base64url": "^2.0", "symfony/asset": "4.*",
"symfony/asset": "^4.4", "symfony/browser-kit": "^5.2",
"symfony/browser-kit": "^4.4", "symfony/css-selector": "^5.2",
"symfony/css-selector": "^4.4", "symfony/expression-language": "4.*",
"symfony/expression-language": "^4.4", "symfony/form": "4.*",
"symfony/form": "^4.4", "symfony/intl": "4.*",
"symfony/framework-bundle": "^4.4", "symfony/mime": "^4 || ^5",
"symfony/http-foundation": "^4.4",
"symfony/intl": "^4.4",
"symfony/mailer": "^5.4",
"symfony/mime": "^5.4",
"symfony/monolog-bundle": "^3.5", "symfony/monolog-bundle": "^3.5",
"symfony/security-bundle": "^4.4", "symfony/security-bundle": "4.*",
"symfony/serializer": "^5.3", "symfony/serializer": "^5.2",
"symfony/swiftmailer-bundle": "^3.5", "symfony/swiftmailer-bundle": "^3.5",
"symfony/templating": "^4.4", "symfony/templating": "4.*",
"symfony/translation": "^4.4", "symfony/translation": "4.*",
"symfony/twig-bundle": "^4.4", "symfony/twig-bundle": "^4.4",
"symfony/validator": "^4.4", "symfony/validator": "4.*",
"symfony/webpack-encore-bundle": "^1.11", "symfony/webpack-encore-bundle": "^1.11",
"symfony/workflow": "^4.4", "symfony/workflow": "4.*",
"symfony/yaml": "^4.4", "symfony/yaml": "4.*",
"twig/extra-bundle": "^3.0", "twig/extra-bundle": "^2.12 || ^3.0",
"twig/intl-extra": "^3.0", "twig/intl-extra": "^3.0",
"twig/markdown-extra": "^3.3", "twig/markdown-extra": "^3.3",
"twig/string-extra": "^3.3", "twig/twig": "^2.12 || ^3.0"
"twig/twig": "^3.0"
},
"require-dev": {
"doctrine/doctrine-fixtures-bundle": "^3.3",
"drupol/php-conventions": "^5",
"fakerphp/faker": "^1.13",
"nelmio/alice": "^3.8",
"phpspec/prophecy-phpunit": "^2.0",
"phpstan/phpstan-strict-rules": "^1.0",
"phpunit/phpunit": ">= 7.5",
"symfony/debug-bundle": "^5.1",
"symfony/dotenv": "^4.4",
"symfony/maker-bundle": "^1.20",
"symfony/phpunit-bridge": "^4.4",
"symfony/stopwatch": "^4.4",
"symfony/var-dumper": "^4.4",
"symfony/web-profiler-bundle": "^4.4"
}, },
"conflict": { "conflict": {
"symfony/symfony": "*" "symfony/symfony": "*"
}, },
"require-dev": {
"doctrine/doctrine-fixtures-bundle": "^3.3",
"fakerphp/faker": "^1.13",
"nelmio/alice": "^3.8",
"phpstan/extension-installer": "^1.1",
"phpstan/phpstan": "^1.0",
"phpstan/phpstan-strict-rules": "^1.0",
"phpunit/phpunit": "^7.0",
"symfony/debug-bundle": "^5.1",
"symfony/dotenv": "^5.1",
"symfony/maker-bundle": "^1.20",
"symfony/phpunit-bridge": "^5.2",
"symfony/stopwatch": "^5.1",
"symfony/var-dumper": "4.*",
"symfony/web-profiler-bundle": "^5.0"
},
"config": {
"bin-dir": "bin",
"vendor-dir": "tests/app/vendor"
},
"autoload": { "autoload": {
"psr-4": { "psr-4": {
"Chill\\ActivityBundle\\": "src/Bundle/ChillActivityBundle", "Chill\\ActivityBundle\\": "src/Bundle/ChillActivityBundle",
"Chill\\AsideActivityBundle\\": "src/Bundle/ChillAsideActivityBundle/src",
"Chill\\BudgetBundle\\": "src/Bundle/ChillBudgetBundle", "Chill\\BudgetBundle\\": "src/Bundle/ChillBudgetBundle",
"Chill\\CalendarBundle\\": "src/Bundle/ChillCalendarBundle",
"Chill\\CustomFieldsBundle\\": "src/Bundle/ChillCustomFieldsBundle", "Chill\\CustomFieldsBundle\\": "src/Bundle/ChillCustomFieldsBundle",
"Chill\\DocGeneratorBundle\\": "src/Bundle/ChillDocGeneratorBundle",
"Chill\\DocStoreBundle\\": "src/Bundle/ChillDocStoreBundle", "Chill\\DocStoreBundle\\": "src/Bundle/ChillDocStoreBundle",
"Chill\\EventBundle\\": "src/Bundle/ChillEventBundle", "Chill\\EventBundle\\": "src/Bundle/ChillEventBundle",
"Chill\\FamilyMemberBundle\\": "src/Bundle/ChillFamilyMemberBundle", "Chill\\FamilyMemberBundle\\": "src/Bundle/ChillFamilyMemberBundle",
@@ -89,6 +83,9 @@
"Chill\\ReportBundle\\": "src/Bundle/ChillReportBundle", "Chill\\ReportBundle\\": "src/Bundle/ChillReportBundle",
"Chill\\TaskBundle\\": "src/Bundle/ChillTaskBundle", "Chill\\TaskBundle\\": "src/Bundle/ChillTaskBundle",
"Chill\\ThirdPartyBundle\\": "src/Bundle/ChillThirdPartyBundle", "Chill\\ThirdPartyBundle\\": "src/Bundle/ChillThirdPartyBundle",
"Chill\\AsideActivityBundle\\": "src/Bundle/ChillAsideActivityBundle/src",
"Chill\\DocGeneratorBundle\\": "src/Bundle/ChillDocGeneratorBundle",
"Chill\\CalendarBundle\\": "src/Bundle/ChillCalendarBundle",
"Chill\\WopiBundle\\": "src/Bundle/ChillWopiBundle/src" "Chill\\WopiBundle\\": "src/Bundle/ChillWopiBundle/src"
} }
}, },
@@ -98,24 +95,12 @@
"Chill\\DocGeneratorBundle\\Tests\\": "src/Bundle/ChillDocGeneratorBundle/tests" "Chill\\DocGeneratorBundle\\Tests\\": "src/Bundle/ChillDocGeneratorBundle/tests"
} }
}, },
"config": { "minimum-stability": "dev",
"allow-plugins": { "prefer-stable": true,
"composer/package-versions-deprecated": true,
"ergebnis/composer-normalize": true,
"ocramius/package-versions": true,
"phpro/grumphp": true,
"phpstan/extension-installer": true,
"roave/you-are-using-it-wrong": true
},
"bin-dir": "bin",
"optimize-autoloader": true,
"sort-packages": true,
"vendor-dir": "tests/app/vendor"
},
"scripts": { "scripts": {
"auto-scripts": { "auto-scripts": {
"assets:install %PUBLIC_DIR%": "symfony-cmd", "cache:clear": "symfony-cmd",
"cache:clear": "symfony-cmd" "assets:install %PUBLIC_DIR%": "symfony-cmd"
} }
} }
} }

View File

@@ -1,24 +1,19 @@
<?php <?php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Export\Filter; namespace Chill\PersonBundle\Export\Filter;
use Chill\MainBundle\Export\ExportElementValidatedInterface;
use Chill\MainBundle\Export\FilterInterface; use Chill\MainBundle\Export\FilterInterface;
use DateTime;
use Doctrine\ORM\Query\Expr;
use Symfony\Component\Form\Extension\Core\Type\DateType; use Symfony\Component\Form\Extension\Core\Type\DateType;
use Symfony\Component\Validator\Context\ExecutionContextInterface; use Symfony\Component\Validator\Context\ExecutionContextInterface;
use Symfony\Component\Validator\Constraints;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Doctrine\ORM\Query\Expr;
use Chill\MainBundle\Form\Type\Export\FilterType;
use Symfony\Component\Form\FormError;
use Chill\MainBundle\Export\ExportElementValidatedInterface;
class BirthdateFilter implements ExportElementValidatedInterface, FilterInterface class BirthdateFilter implements FilterInterface, ExportElementValidatedInterface
{ {
// add specific role for this filter // add specific role for this filter
public function addRole() public function addRole()
@@ -27,16 +22,78 @@ class BirthdateFilter implements ExportElementValidatedInterface, FilterInterfac
return null; return null;
} }
// we give information on which type of export this filter applies
public function applyOn()
{
return 'person';
}
public function getTitle()
{
return 'Filter by person\'s birthdate';
}
// we build a form to collect some parameters from the users
public function buildForm(\Symfony\Component\Form\FormBuilderInterface $builder)
{
$builder->add('date_from', DateType::class, array(
'label' => "Born after this date",
'data' => new \DateTime(),
'attr' => array('class' => 'datepicker'),
'widget'=> 'single_text',
'format' => 'dd-MM-yyyy',
));
$builder->add('date_to', DateType::class, array(
'label' => "Born before this date",
'data' => new \DateTime(),
'attr' => array('class' => 'datepicker'),
'widget'=> 'single_text',
'format' => 'dd-MM-yyyy',
));
}
// the form created above must be validated. The process of validation
// is executed here. This function is added by the interface
// `ExportElementValidatedInterface`, and can be ignore if there is
// no need for a validation
public function validateForm($data, ExecutionContextInterface $context)
{
$date_from = $data['date_from'];
$date_to = $data['date_to'];
if ($date_from === null) {
$context->buildViolation('The "date from" should not be empty')
//->atPath('date_from')
->addViolation();
}
if ($date_to === null) {
$context->buildViolation('The "date to" should not be empty')
//->atPath('date_to')
->addViolation();
}
if (
($date_from !== null && $date_to !== null)
&&
$date_from >= $date_to
) {
$context->buildViolation('The date "date to" should be after the '
. 'date given in "date from" field')
->addViolation();
}
}
// here, we alter the query created by Export // here, we alter the query created by Export
public function alterQuery(\Doctrine\ORM\QueryBuilder $qb, $data) public function alterQuery(\Doctrine\ORM\QueryBuilder $qb, $data)
{ {
$where = $qb->getDQLPart('where'); $where = $qb->getDQLPart('where');
// we create the clause here // we create the clause here
$clause = $qb->expr()->between( $clause = $qb->expr()->between('person.birthdate', ':date_from',
'person.birthdate', ':date_to');
':date_from',
':date_to'
);
// we have to take care **not to** remove previous clauses... // we have to take care **not to** remove previous clauses...
if ($where instanceof Expr\Andx) { if ($where instanceof Expr\Andx) {
@@ -51,76 +108,16 @@ class BirthdateFilter implements ExportElementValidatedInterface, FilterInterfac
$qb->setParameter('date_to', $data['date_to']); $qb->setParameter('date_to', $data['date_to']);
} }
// we give information on which type of export this filter applies
public function applyOn()
{
return 'person';
}
// we build a form to collect some parameters from the users
public function buildForm(\Symfony\Component\Form\FormBuilderInterface $builder)
{
$builder->add('date_from', DateType::class, [
'label' => 'Born after this date',
'data' => new DateTime(),
'attr' => ['class' => 'datepicker'],
'widget' => 'single_text',
'format' => 'dd-MM-yyyy',
]);
$builder->add('date_to', DateType::class, [
'label' => 'Born before this date',
'data' => new DateTime(),
'attr' => ['class' => 'datepicker'],
'widget' => 'single_text',
'format' => 'dd-MM-yyyy',
]);
}
// here, we create a simple string which will describe the action of // here, we create a simple string which will describe the action of
// the filter in the Response // the filter in the Response
public function describeAction($data, $format = 'string') public function describeAction($data, $format = 'string')
{ {
return ['Filtered by person\'s birtdate: ' return array('Filtered by person\'s birtdate: '
. 'between %date_from% and %date_to%', [ . 'between %date_from% and %date_to%', array(
'%date_from%' => $data['date_from']->format('d-m-Y'), '%date_from%' => $data['date_from']->format('d-m-Y'),
'%date_to%' => $data['date_to']->format('d-m-Y'), '%date_to%' => $data['date_to']->format('d-m-Y')
], ]; ));
} }
public function getTitle()
{
return 'Filter by person\'s birthdate';
}
// the form created above must be validated. The process of validation
// is executed here. This function is added by the interface
// `ExportElementValidatedInterface`, and can be ignore if there is
// no need for a validation
public function validateForm($data, ExecutionContextInterface $context)
{
$date_from = $data['date_from'];
$date_to = $data['date_to'];
if (null === $date_from) {
$context->buildViolation('The "date from" should not be empty')
//->atPath('date_from')
->addViolation();
}
if (null === $date_to) {
$context->buildViolation('The "date to" should not be empty')
//->atPath('date_to')
->addViolation();
}
if (
(null !== $date_from && null !== $date_to)
&& $date_from >= $date_to
) {
$context->buildViolation('The date "date to" should be after the '
. 'date given in "date from" field')
->addViolation();
}
}
} }

View File

@@ -1,99 +1,62 @@
<?php <?php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Export\Export; namespace Chill\PersonBundle\Export\Export;
use Chill\MainBundle\Export\ExportInterface; use Chill\MainBundle\Export\ExportInterface;
use Chill\MainBundle\Export\FormatterInterface; use Doctrine\ORM\QueryBuilder;
use Chill\PersonBundle\Export\Declarations;
use Chill\PersonBundle\Security\Authorization\PersonVoter;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Query;
use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormBuilderInterface;
use Doctrine\ORM\Query;
use Chill\PersonBundle\Security\Authorization\PersonVoter;
use Symfony\Component\Security\Core\Role\Role; use Symfony\Component\Security\Core\Role\Role;
use Chill\PersonBundle\Export\Declarations;
use Chill\MainBundle\Export\FormatterInterface;
use Doctrine\ORM\EntityManagerInterface;
/**
*
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
class CountPerson implements ExportInterface class CountPerson implements ExportInterface
{ {
/** /**
*
* @var EntityManagerInterface * @var EntityManagerInterface
*/ */
protected $entityManager; protected $entityManager;
public function __construct( public function __construct(
EntityManagerInterface $em EntityManagerInterface $em
) { )
{
$this->entityManager = $em; $this->entityManager = $em;
} }
public function buildForm(FormBuilderInterface $builder)
{
// this export does not add any form
}
public function getAllowedFormattersTypes()
{
return [FormatterInterface::TYPE_TABULAR];
}
public function getDescription()
{
return 'Count peoples by various parameters.';
}
public function getLabels($key, array $values, $data)
{
// the Closure which will be executed by the formatter.
return function ($value) {
switch ($value) {
case '_header':
// we have to process specifically the '_header' string,
// which will be used by the formatter to show a column title
return $this->getTitle();
default:
// for all value, we do not process them and return them
// immediatly
return $value;
}
};
}
public function getQueryKeys($data)
{
// this array match the result keys in the query. We have only
// one column.
return ['export_result'];
}
public function getResult($qb, $data)
{
return $qb->getQuery()->getResult(Query::HYDRATE_SCALAR);
}
public function getTitle()
{
return 'Count peoples';
}
public function getType() public function getType()
{ {
return Declarations::PERSON_TYPE; return Declarations::PERSON_TYPE;
} }
public function initiateQuery(array $requiredModifiers, array $acl, array $data = []) public function getDescription()
{
return "Count peoples by various parameters.";
}
public function getTitle()
{
return "Count peoples";
}
public function requiredRole()
{
return new Role(PersonVoter::STATS);
}
public function initiateQuery(array $requiredModifiers, array $acl, array $data = array())
{ {
// we gather all center the user choose. // we gather all center the user choose.
$centers = array_map(static function ($el) { $centers = array_map(function($el) { return $el['center']; }, $acl);
return $el['center'];
}, $acl);
$qb = $this->entityManager->createQueryBuilder(); $qb = $this->entityManager->createQueryBuilder();
@@ -102,18 +65,54 @@ class CountPerson implements ExportInterface
->join('person.center', 'center') ->join('person.center', 'center')
->andWhere('center IN (:authorized_centers)') ->andWhere('center IN (:authorized_centers)')
->setParameter('authorized_centers', $centers); ->setParameter('authorized_centers', $centers);
;
return $qb; return $qb;
} }
public function requiredRole() public function getResult($qb, $data)
{ {
return new Role(PersonVoter::STATS); return $qb->getQuery()->getResult(Query::HYDRATE_SCALAR);
}
public function getQueryKeys($data)
{
// this array match the result keys in the query. We have only
// one column.
return array('export_result');
}
public function getLabels($key, array $values, $data)
{
// the Closure which will be executed by the formatter.
return function($value) {
switch($value) {
case '_header':
// we have to process specifically the '_header' string,
// which will be used by the formatter to show a column title
return $this->getTitle();
default:
// for all value, we do not process them and return them
// immediatly
return $value;
};
}
public function getAllowedFormattersTypes()
{
return array(FormatterInterface::TYPE_TABULAR);
}
public function buildForm(FormBuilderInterface $builder) {
// this export does not add any form
} }
public function supportsModifiers() public function supportsModifiers()
{ {
// explain the export manager which formatters and filters are allowed // explain the export manager which formatters and filters are allowed
return [Declarations::PERSON_TYPE, Declarations::PERSON_IMPLIED_IN]; return array(Declarations::PERSON_TYPE, Declarations::PERSON_IMPLIED_IN);
} }
} }

View File

@@ -32,7 +32,7 @@ Then, render the pagination using the dedicated twig function.
{% block title 'Item list'|trans %} {% block title 'Item list'|trans %}
{% block content %} {% block personcontent %}
<table> <table>

View File

@@ -1,26 +1,19 @@
<?php <?php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Chill\MyBundle\Controller; namespace Chill\MyBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
class ItemController extends Controller {
class example extends Controller
{
public function yourAction() public function yourAction()
{ {
$em = $this->getDoctrine()->getManager(); $em = $this->getDoctrine()->getManager();
// first, get the number of total item are available // first, get the number of total item are available
$total = $em $total = $em
->createQuery('SELECT COUNT (item.id) FROM ChillMyBundle:Item item') ->createQuery("SELECT COUNT (item.id) FROM ChillMyBundle:Item item")
->getSingleScalarResult(); ->getSingleScalarResult();
// get the PaginatorFactory // get the PaginatorFactory
@@ -32,18 +25,18 @@ class example extends Controller
// launch your query on item. Limit the query to the results // launch your query on item. Limit the query to the results
// for the current page using the paginator // for the current page using the paginator
$items = $em->createQuery('SELECT item FROM ChillMyBundle:Item item WHERE <your clause>') $items = $em->createQuery("SELECT item FROM ChillMyBundle:Item item WHERE <your clause>")
// use the paginator to get the first item number // use the paginator to get the first item number
->setFirstResult($paginator->getCurrentPage()->getFirstItemNumber()) ->setFirstResult($paginator->getCurrentPage()->getFirstItemNumber())
// use the paginator to get the number of items to display // use the paginator to get the number of items to display
->setMaxResults($paginator->getItemsPerPage()); ->setMaxResults($paginator->getItemsPerPage());
return $this->render( return $this->render('ChillMyBundle:Item:list.html.twig', array(
'ChillMyBundle:Item:list.html.twig',
[
'items' => $items, 'items' => $items,
'paginator' => $paginator, 'paginator' => $paginator
]
); );
} }
} }

View File

@@ -1,43 +1,33 @@
<?php <?php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Chill\HealthBundle\Controller; namespace Chill\HealthBundle\Controller;
use Chill\HealthBundle\Security\Authorization\ConsultationVoter; use Chill\HealthBundle\Security\Authorization\ConsultationVoter;
use Chill\PersonBundle\Security\Authorization\PersonVoter;
use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Chill\PersonBundle\Security\Authorization\PersonVoter;
use Symfony\Component\Security\Core\Role\Role; use Symfony\Component\Security\Core\Role\Role;
class ConsultationController extends Controller class ConsultationController extends Controller
{ {
/** /**
*
* @param int $id personId * @param int $id personId
*
* @throws type
*
* @return \Symfony\Component\HttpFoundation\Response * @return \Symfony\Component\HttpFoundation\Response
* @throws type
*/ */
public function listAction($id) public function listAction($id)
{ {
/** @var \Chill\PersonBundle\Entity\Person $person */ /* @var $person \Chill\PersonBundle\Entity\Person */
$person = $this->get('chill.person.repository.person') $person = $this->get('chill.person.repository.person')
->find($id); ->find($id);
if (null === $person) { if ($person === null) {
throw $this->createNotFoundException('The person is not found'); throw $this->createNotFoundException("The person is not found");
} }
$this->denyAccessUnlessGranted(PersonVoter::SEE, $person); $this->denyAccessUnlessGranted(PersonVoter::SEE, $person);
/** @var \Chill\MainBundle\Security\Authorization\AuthorizationHelper $authorizationHelper */ /* @var $authorizationHelper \Chill\MainBundle\Security\Authorization\AuthorizationHelper */
$authorizationHelper = $this->get('chill.main.security.' $authorizationHelper = $this->get('chill.main.security.'
. 'authorization.helper'); . 'authorization.helper');
@@ -56,9 +46,10 @@ class ConsultationController extends Controller
->setParameter('circles', $circles) ->setParameter('circles', $circles)
->getResult(); ->getResult();
return $this->render('ChillHealthBundle:Consultation:list.html.twig', [ return $this->render('ChillHealthBundle:Consultation:list.html.twig', array(
'person' => $person, 'person' => $person,
'consultations' => $consultations, 'consultations' => $consultations
]); ));
} }
} }

View File

@@ -156,7 +156,7 @@ This layout extend `ChillMainBundle::layoutWithVerticalMenu.html.twig` add the p
It proposes 1 new block : It proposes 1 new block :
* content * personcontent
* where to display the information of the person * where to display the information of the person

View File

@@ -1,43 +1,40 @@
<?php <?php
# Chill\MainBundle\DependencyInjection\Configuration.php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Chill\MainBundle\DependencyInjection; namespace Chill\MainBundle\DependencyInjection;
use Chill\MainBundle\DependencyInjection\Widget\AddWidgetConfigurationTrait;
use Symfony\Component\Config\Definition\Builder\TreeBuilder; use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface; use Symfony\Component\Config\Definition\ConfigurationInterface;
use Chill\MainBundle\DependencyInjection\Widget\AddWidgetConfigurationTrait;
use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\ContainerBuilder;
/** /**
* Configure the main bundle. * Configure the main bundle
*/ */
class ChillMainConfiguration implements ConfigurationInterface class Configuration implements ConfigurationInterface
{ {
use AddWidgetConfigurationTrait; use AddWidgetConfigurationTrait;
/** /**
*
* @var ContainerBuilder * @var ContainerBuilder
*/ */
private $containerBuilder; private $containerBuilder;
public function __construct(
array $widgetFactories, public function __construct(array $widgetFactories = array(),
ContainerBuilder $containerBuilder ContainerBuilder $containerBuilder)
) { {
// we register here widget factories (see below) // we register here widget factories (see below)
$this->setWidgetFactories($widgetFactories); $this->setWidgetFactories($widgetFactories);
// we will need the container builder later... // we will need the container builder later...
$this->containerBuilder = $containerBuilder; $this->containerBuilder = $containerBuilder;
} }
/**
* {@inheritDoc}
*/
public function getConfigTreeBuilder() public function getConfigTreeBuilder()
{ {
$treeBuilder = new TreeBuilder(); $treeBuilder = new TreeBuilder();
@@ -47,6 +44,7 @@ class ChillMainConfiguration implements ConfigurationInterface
->children() ->children()
// ... // ...
->arrayNode('widgets') ->arrayNode('widgets')
->canBeDisabled() ->canBeDisabled()
->children() ->children()
@@ -58,6 +56,9 @@ class ChillMainConfiguration implements ConfigurationInterface
->end() // end of root ->end() // end of root
; ;
return $treeBuilder; return $treeBuilder;
} }
} }

View File

@@ -1,19 +1,16 @@
<?php <?php
/** #Chill\MainBundle\DependencyInjection\ChillMainExtension.php
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Chill\MainBundle\DependencyInjection; namespace Chill\MainBundle\DependencyInjection;
use Chill\MainBundle\DependencyInjection\Widget\Factory\WidgetFactoryInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\HttpKernel\DependencyInjection\Extension; use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\Loader;
use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
use Chill\MainBundle\DependencyInjection\Widget\Factory\WidgetFactoryInterface;
use Chill\MainBundle\DependencyInjection\Configuration;
/** /**
* This class load config for chillMainExtension. * This class load config for chillMainExtension.
@@ -21,23 +18,19 @@ use Symfony\Component\HttpKernel\DependencyInjection\Extension;
class ChillMainExtension extends Extension implements Widget\HasWidgetFactoriesExtensionInterface class ChillMainExtension extends Extension implements Widget\HasWidgetFactoriesExtensionInterface
{ {
/** /**
* widget factory. * widget factory
* *
* @var WidgetFactoryInterface[] * @var WidgetFactoryInterface[]
*/ */
protected $widgetFactories = []; protected $widgetFactories = array();
public function addWidgetFactory(WidgetFactoryInterface $factory) public function addWidgetFactory(WidgetFactoryInterface $factory)
{ {
$this->widgetFactories[] = $factory; $this->widgetFactories[] = $factory;
} }
public function getConfiguration(array $config, ContainerBuilder $container)
{
return new Configuration($this->widgetFactories, $container);
}
/** /**
*
* @return WidgetFactoryInterface[] * @return WidgetFactoryInterface[]
*/ */
public function getWidgetFactories() public function getWidgetFactories()
@@ -52,11 +45,15 @@ class ChillMainExtension extends Extension implements Widget\HasWidgetFactoriesE
$config = $this->processConfiguration($configuration, $configs); $config = $this->processConfiguration($configuration, $configs);
// add the key 'widget' without the key 'enable' // add the key 'widget' without the key 'enable'
$container->setParameter( $container->setParameter('chill_main.widgets',
'chill_main.widgets', array('homepage' => $config['widgets']['homepage']));
['homepage' => $config['widgets']['homepage']]
);
// ... // ...
} }
public function getConfiguration(array $config, ContainerBuilder $container)
{
return new Configuration($this->widgetFactories, $container);
}
} }

View File

@@ -1,24 +1,17 @@
<?php <?php
/** # Chill/PersonBundle/Widget/PersonListWidgetFactory
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Widget; namespace Chill\PersonBundle\Widget;
use Chill\MainBundle\DependencyInjection\Widget\Factory\AbstractWidgetFactory; use Chill\MainBundle\DependencyInjection\Widget\Factory\AbstractWidgetFactory;
use Symfony\Component\Config\Definition\Builder\NodeBuilder;
use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Config\Definition\Builder\NodeBuilder;
/** /**
* add configuration for the person_list widget. * add configuration for the person_list widget.
*/ */
class ChillPersonAddAPersonListWidgetFactory extends AbstractWidgetFactory class PersonListWidgetFactory extends AbstractWidgetFactory
{ {
/* /*
* append the option to the configuration * append the option to the configuration
@@ -36,16 +29,27 @@ class ChillPersonAddAPersonListWidgetFactory extends AbstractWidgetFactory
$node->scalarNode('filtering_class') $node->scalarNode('filtering_class')
->defaultNull() ->defaultNull()
->end(); ->end();
} }
/** /*
* return an array with the allowed places where the widget can be rendered. * return an array with the allowed places where the widget can be rendered
* *
* @return string[] * @return string[]
*/ */
public function getAllowedPlaces() public function getAllowedPlaces()
{ {
return ['homepage']; return array('homepage');
}
/*
* return the widget alias
*
* @return string
*/
public function getWidgetAlias()
{
return 'person_list';
} }
/* /*
@@ -61,13 +65,5 @@ class ChillPersonAddAPersonListWidgetFactory extends AbstractWidgetFactory
return 'chill_person.widget.person_list'; return 'chill_person.widget.person_list';
} }
/**
* return the widget alias.
*
* @return string
*/
public function getWidgetAlias()
{
return 'person_list';
}
} }

View File

@@ -1,63 +1,56 @@
<?php <?php
# Chill/PersonBundle/Widget/PersonListWidget.php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Widget; namespace Chill\PersonBundle\Widget;
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
use Chill\MainBundle\Templating\Widget\WidgetInterface; use Chill\MainBundle\Templating\Widget\WidgetInterface;
use Chill\PersonBundle\Security\Authorization\PersonVoter;
use DateTime;
use Doctrine\DBAL\Types\Type;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\EntityRepository; use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\Query\Expr; use Doctrine\ORM\Query\Expr;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage; use Doctrine\DBAL\Types\Type;
use Symfony\Component\Security\Core\Role\Role; use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Core\User\UserInterface;
use Twig_Environment; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage;
use Chill\PersonBundle\Security\Authorization\PersonVoter;
use Symfony\Component\Security\Core\Role\Role;
use Doctrine\ORM\EntityManager;
/** /**
* add a widget with person list. * add a widget with person list.
* *
* The configuration is defined by `PersonListWidgetFactory` * The configuration is defined by `PersonListWidgetFactory`
*/ */
class ChillPersonAddAPersonWidget implements WidgetInterface class PersonListWidget implements WidgetInterface
{ {
/**
* the authorization helper.
*
* @var AuthorizationHelper;
*/
protected $authorizationHelper;
/** /**
* The entity manager. * Repository for persons
*
* @var EntityManager
*/
protected $entityManager;
/**
* Repository for persons.
* *
* @var EntityRepository * @var EntityRepository
*/ */
protected $personRepository; protected $personRepository;
/** /**
* The entity manager
*
* @var EntityManager
*/
protected $entityManager;
/**
* the authorization helper
*
* @var AuthorizationHelper;
*/
protected $authorizationHelper;
/**
*
* @var TokenStorage * @var TokenStorage
*/ */
protected $tokenStorage; protected $tokenStorage;
/** /**
*
* @var UserInterface * @var UserInterface
*/ */
protected $user; protected $user;
@@ -75,11 +68,13 @@ class ChillPersonAddAPersonWidget implements WidgetInterface
} }
/** /**
* @param type $place
* *
* @param type $place
* @param array $context
* @param array $config
* @return string * @return string
*/ */
public function render(Twig_Environment $env, $place, array $context, array $config) public function render(\Twig_Environment $env, $place, array $context, array $config)
{ {
$qb = $this->personRepository $qb = $this->personRepository
->createQueryBuilder('person'); ->createQueryBuilder('person');
@@ -91,8 +86,9 @@ class ChillPersonAddAPersonWidget implements WidgetInterface
$and->add($qb->expr()->in('person.center', ':centers')); $and->add($qb->expr()->in('person.center', ':centers'));
$qb->setParameter('centers', $centers); $qb->setParameter('centers', $centers);
// add the "only active" where clause // add the "only active" where clause
if (true === $config['only_active']) { if ($config['only_active'] === true) {
$qb->join('person.accompanyingPeriods', 'ap'); $qb->join('person.accompanyingPeriods', 'ap');
$or = new Expr\Orx(); $or = new Expr\Orx();
// add the case where closingDate IS NULL // add the case where closingDate IS NULL
@@ -105,7 +101,7 @@ class ChillPersonAddAPersonWidget implements WidgetInterface
(new Expr())->between(':now', 'ap.openingDate', 'ap.closingDate') (new Expr())->between(':now', 'ap.openingDate', 'ap.closingDate')
); );
$and->add($or); $and->add($or);
$qb->setParameter('now', new DateTime(), Type::DATE); $qb->setParameter('now', new \DateTime(), Type::DATE);
} }
// adding the where clause to the query // adding the where clause to the query
@@ -117,15 +113,17 @@ class ChillPersonAddAPersonWidget implements WidgetInterface
return $env->render( return $env->render(
'ChillPersonBundle:Widget:homepage_person_list.html.twig', 'ChillPersonBundle:Widget:homepage_person_list.html.twig',
['persons' => $persons] array('persons' => $persons)
); );
} }
/** /**
*
* @return UserInterface * @return UserInterface
*/ */
private function getUser() private function getUser()
{ {
// return a user // return a user
} }
} }

View File

@@ -1,48 +1,51 @@
<?php <?php
/** # Chill/PersonBundle/DependencyInjection/ChillPersonExtension.php
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Chill\PersonBundle\DependencyInjection; namespace Chill\PersonBundle\DependencyInjection;
use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface; use Symfony\Component\Config\FileLocator;
use Symfony\Component\HttpKernel\DependencyInjection\Extension; use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\Loader;
use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
use Chill\MainBundle\DependencyInjection\MissingBundleException;
use Chill\PersonBundle\Security\Authorization\PersonVoter;
/** /**
* This is the class that loads and manages your bundle configuration. * This is the class that loads and manages your bundle configuration
* *
* To learn more see {@link http://symfony.com/doc/current/cookbook/bundles/extension.html} * To learn more see {@link http://symfony.com/doc/current/cookbook/bundles/extension.html}
*/ */
class ChillPersonExtension extends Extension implements PrependExtensionInterface class ChillPersonExtension extends Extension implements PrependExtensionInterface
{ {
/**
* {@inheritDoc}
*/
public function load(array $configs, ContainerBuilder $container) public function load(array $configs, ContainerBuilder $container)
{ {
// ... // ...
} }
/** /**
* Add a widget "add a person" on the homepage, automatically. *
* Add a widget "add a person" on the homepage, automatically
* *
* @param \Chill\PersonBundle\DependencyInjection\containerBuilder $container * @param \Chill\PersonBundle\DependencyInjection\containerBuilder $container
*/ */
public function prepend(ContainerBuilder $container) public function prepend(ContainerBuilder $container)
{ {
$container->prependExtensionConfig('chill_main', [ $container->prependExtensionConfig('chill_main', array(
'widgets' => [ 'widgets' => array(
'homepage' => [ 'homepage' => array(
[ array(
'widget_alias' => 'add_person', 'widget_alias' => 'add_person',
'order' => 2, 'order' => 2
], )
], )
], )
]); ));
} }
} }

View File

@@ -1,39 +0,0 @@
imports:
- {
resource: tests/app/vendor/drupol/php-conventions/config/php73/grumphp.yml,
}
parameters:
tasks.phpcsfixer.config: .php_cs.dist.php
tasks.license.name: AGPL-3.0
tasks.license.holder: Champs-Libres
tasks.license.date_from: 2001
tasks.phpcsfixer.allow_risky: true
tasks.phpcsfixer.diff: true
tasks.phpstan.level: 1
tasks.phpstan.blocking: true
tasks.phpstan.ignore_patterns:
- "/.github/"
- "/.idea/"
- "/build/"
- "/benchmarks/"
- "/docs/"
- "/node_modules/"
- "/resource/"
- "/spec/"
- "/var/"
- "/vendor/"
# Psalm
tasks.psalm.blocking: true
tasks.psalm.ignore_patterns:
- "/.github/"
- "/.idea/"
- "/build/"
- "/benchmarks/"
- "/node_modules/"
- "/resource/"
- "/spec/"
- "/var/"
- "/vendor/"

3
package.json.txt Normal file
View File

@@ -0,0 +1,3 @@
add npm/yarn dependency in package.json :
"select2-bootstrap-theme": "0.1.0-beta.10",

View File

@@ -10,6 +10,16 @@ parameters:
count: 1 count: 1
path: src/Bundle/ChillCustomFieldsBundle/Controller/CustomFieldsGroupController.php path: src/Bundle/ChillCustomFieldsBundle/Controller/CustomFieldsGroupController.php
-
message: "#^Instantiated class PhpOffice\\\\PhpWord\\\\TemplateProcessor not found\\.$#"
count: 1
path: src/Bundle/ChillDocGeneratorBundle/Controller/DocGeneratorController.php
-
message: "#^Instantiated class PhpOffice\\\\PhpWord\\\\TemplateProcessor not found\\.$#"
count: 1
path: src/Bundle/ChillDocGeneratorBundle/Controller/DocGeneratorTemplateController.php
- -
message: "#^Variable \\$participation might not be defined\\.$#" message: "#^Variable \\$participation might not be defined\\.$#"
count: 3 count: 3
@@ -80,6 +90,11 @@ parameters:
count: 1 count: 1
path: src/Bundle/ChillPersonBundle/Form/ChoiceLoader/PersonChoiceLoader.php path: src/Bundle/ChillPersonBundle/Form/ChoiceLoader/PersonChoiceLoader.php
-
message: "#^Foreach overwrites \\$action with its value variable\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php
- -
message: "#^Foreach overwrites \\$action with its value variable\\.$#" message: "#^Foreach overwrites \\$action with its value variable\\.$#"
count: 1 count: 1

View File

@@ -25,6 +25,41 @@ parameters:
count: 1 count: 1
path: src/Bundle/ChillActivityBundle/Repository/ActivityACLAwareRepository.php path: src/Bundle/ChillActivityBundle/Repository/ActivityACLAwareRepository.php
-
message: "#^Access to an undefined property Chill\\\\PersonBundle\\\\Household\\\\MembersEditorFactory\\:\\:\\$validator\\.$#"
count: 2
path: src/Bundle/ChillPersonBundle/Household/MembersEditorFactory.php
-
message: "#^Parameter \\$action of method Chill\\\\PersonBundle\\\\Repository\\\\AccompanyingPeriod\\\\AccompanyingPeriodWorkRepository\\:\\:buildQueryBySocialActionWithDescendants\\(\\) has invalid type Chill\\\\PersonBundle\\\\Repository\\\\AccompanyingPeriod\\\\SocialAction\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php
-
message: "#^Parameter \\$action of method Chill\\\\PersonBundle\\\\Repository\\\\AccompanyingPeriod\\\\AccompanyingPeriodWorkRepository\\:\\:countBySocialActionWithDescendants\\(\\) has invalid type Chill\\\\PersonBundle\\\\Repository\\\\AccompanyingPeriod\\\\SocialAction\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php
-
message: "#^Undefined variable\\: \\$action$#"
count: 1
path: src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php
-
message: "#^Undefined variable\\: \\$limit$#"
count: 1
path: src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php
-
message: "#^Undefined variable\\: \\$offset$#"
count: 1
path: src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php
-
message: "#^Undefined variable\\: \\$orderBy$#"
count: 1
path: src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php
- -
message: "#^Variable variables are not allowed\\.$#" message: "#^Variable variables are not allowed\\.$#"
count: 4 count: 4
@@ -35,6 +70,11 @@ parameters:
count: 1 count: 1
path: src/Bundle/ChillPersonBundle/Serializer/Normalizer/MembersEditorNormalizer.php path: src/Bundle/ChillPersonBundle/Serializer/Normalizer/MembersEditorNormalizer.php
-
message: "#^Undefined variable\\: \\$value$#"
count: 1
path: src/Bundle/ChillPersonBundle/Validator/Constraints/AccompanyingPeriod/LocationValidityValidator.php
- -
message: "#^Undefined variable\\: \\$choiceSlug$#" message: "#^Undefined variable\\: \\$choiceSlug$#"
count: 1 count: 1

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,15 @@
parameters: parameters:
ignoreErrors: ignoreErrors:
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillActivityBundle/DataFixtures/ORM/LoadActivitytACL.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillActivityBundle/Entity/Activity.php
- -
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#" message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1 count: 1
@@ -10,6 +20,11 @@ parameters:
count: 1 count: 1
path: src/Bundle/ChillActivityBundle/Entity/ActivityReasonCategory.php path: src/Bundle/ChillActivityBundle/Entity/ActivityReasonCategory.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillActivityBundle/Export/Export/ListActivity.php
- -
message: "#^Method Chill\\\\ActivityBundle\\\\Export\\\\Export\\\\StatActivityDuration\\:\\:getDescription\\(\\) should return string but return statement is missing\\.$#" message: "#^Method Chill\\\\ActivityBundle\\\\Export\\\\Export\\\\StatActivityDuration\\:\\:getDescription\\(\\) should return string but return statement is missing\\.$#"
count: 1 count: 1
@@ -20,6 +35,11 @@ parameters:
count: 1 count: 1
path: src/Bundle/ChillActivityBundle/Export/Export/StatActivityDuration.php path: src/Bundle/ChillActivityBundle/Export/Export/StatActivityDuration.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillActivityBundle/Form/ActivityType.php
- -
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#" message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1 count: 1
@@ -35,6 +55,36 @@ parameters:
count: 2 count: 2
path: src/Bundle/ChillActivityBundle/Form/ActivityType.php path: src/Bundle/ChillActivityBundle/Form/ActivityType.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillActivityBundle/Menu/AdminMenuBuilder.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillActivityBundle/Repository/ActivityACLAwareRepository.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillActivityBundle/Security/Authorization/ActivityStatsVoter.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 2
path: src/Bundle/ChillActivityBundle/Timeline/TimelineActivityProvider.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillAsideActivityBundle/src/Form/AsideActivityFormType.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillAsideActivityBundle/src/Menu/AdminMenuBuilder.php
- -
message: "#^Only booleans are allowed in an if condition, mixed given\\.$#" message: "#^Only booleans are allowed in an if condition, mixed given\\.$#"
count: 3 count: 3
@@ -45,16 +95,36 @@ parameters:
count: 2 count: 2
path: src/Bundle/ChillBudgetBundle/Form/ResourceType.php path: src/Bundle/ChillBudgetBundle/Form/ResourceType.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 2
path: src/Bundle/ChillBudgetBundle/Security/Authorization/BudgetElementVoter.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillCalendarBundle/Entity/Calendar.php
- -
message: "#^Only booleans are allowed in an if condition, mixed given\\.$#" message: "#^Only booleans are allowed in an if condition, mixed given\\.$#"
count: 1 count: 1
path: src/Bundle/ChillCustomFieldsBundle/Command/CreateFieldsOnGroupCommand.php path: src/Bundle/ChillCustomFieldsBundle/Command/CreateFieldsOnGroupCommand.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillCustomFieldsBundle/Controller/CustomFieldsGroupController.php
- -
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#" message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1 count: 1
path: src/Bundle/ChillCustomFieldsBundle/CustomFields/AbstractCustomField.php path: src/Bundle/ChillCustomFieldsBundle/CustomFields/AbstractCustomField.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 3
path: src/Bundle/ChillCustomFieldsBundle/CustomFields/CustomFieldChoice.php
- -
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#" message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 4 count: 4
@@ -120,11 +190,21 @@ parameters:
count: 1 count: 1
path: src/Bundle/ChillDocStoreBundle/Entity/DocumentCategory.php path: src/Bundle/ChillDocStoreBundle/Entity/DocumentCategory.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillEventBundle/Entity/Participation.php
- -
message: "#^Method Chill\\\\EventBundle\\\\Entity\\\\Participation\\:\\:offsetGet\\(\\) should return mixed but return statement is missing\\.$#" message: "#^Method Chill\\\\EventBundle\\\\Entity\\\\Participation\\:\\:offsetGet\\(\\) should return mixed but return statement is missing\\.$#"
count: 1 count: 1
path: src/Bundle/ChillEventBundle/Entity/Participation.php path: src/Bundle/ChillEventBundle/Entity/Participation.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillEventBundle/Form/ChoiceLoader/EventChoiceLoader.php
- -
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#" message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1 count: 1
@@ -145,6 +225,11 @@ parameters:
count: 1 count: 1
path: src/Bundle/ChillEventBundle/Form/StatusType.php path: src/Bundle/ChillEventBundle/Form/StatusType.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillEventBundle/Form/Type/PickEventType.php
- -
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#" message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 2 count: 2
@@ -155,6 +240,16 @@ parameters:
count: 1 count: 1
path: src/Bundle/ChillEventBundle/Search/EventSearch.php path: src/Bundle/ChillEventBundle/Search/EventSearch.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 2
path: src/Bundle/ChillEventBundle/Security/Authorization/EventVoter.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 2
path: src/Bundle/ChillEventBundle/Security/Authorization/ParticipationVoter.php
- -
message: "#^Casting to string something that's already string\\.$#" message: "#^Casting to string something that's already string\\.$#"
count: 5 count: 5
@@ -165,6 +260,11 @@ parameters:
count: 2 count: 2
path: src/Bundle/ChillFamilyMembersBundle/Form/FamilyMemberType.php path: src/Bundle/ChillFamilyMembersBundle/Form/FamilyMemberType.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 2
path: src/Bundle/ChillFamilyMembersBundle/Security/Voter/FamilyMemberVoter.php
- -
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#" message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1 count: 1
@@ -185,6 +285,16 @@ parameters:
count: 1 count: 1
path: src/Bundle/ChillMainBundle/CRUD/Resolver/Resolver.php path: src/Bundle/ChillMainBundle/CRUD/Resolver/Resolver.php
-
message: "#^Call to function array_search\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/CRUD/Routing/CRUDRoutesLoader.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 2
path: src/Bundle/ChillMainBundle/CRUD/Routing/CRUDRoutesLoader.php
- -
message: "#^Method Chill\\\\MainBundle\\\\Command\\\\ChillImportUsersCommand\\:\\:execute\\(\\) should return int but return statement is missing\\.$#" message: "#^Method Chill\\\\MainBundle\\\\Command\\\\ChillImportUsersCommand\\:\\:execute\\(\\) should return int but return statement is missing\\.$#"
count: 1 count: 1
@@ -195,6 +305,11 @@ parameters:
count: 1 count: 1
path: src/Bundle/ChillMainBundle/Command/ChillImportUsersCommand.php path: src/Bundle/ChillMainBundle/Command/ChillImportUsersCommand.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 2
path: src/Bundle/ChillMainBundle/Command/ChillUserSendRenewPasswordCodeCommand.php
- -
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#" message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1 count: 1
@@ -205,6 +320,11 @@ parameters:
count: 1 count: 1
path: src/Bundle/ChillMainBundle/Command/ChillUserSendRenewPasswordCodeCommand.php path: src/Bundle/ChillMainBundle/Command/ChillUserSendRenewPasswordCodeCommand.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 2
path: src/Bundle/ChillMainBundle/Command/LoadAndUpdateLanguagesCommand.php
- -
message: "#^Method Chill\\\\MainBundle\\\\Command\\\\LoadAndUpdateLanguagesCommand\\:\\:execute\\(\\) should return int but return statement is missing\\.$#" message: "#^Method Chill\\\\MainBundle\\\\Command\\\\LoadAndUpdateLanguagesCommand\\:\\:execute\\(\\) should return int but return statement is missing\\.$#"
count: 1 count: 1
@@ -240,6 +360,36 @@ parameters:
count: 1 count: 1
path: src/Bundle/ChillMainBundle/Controller/PostalCodeController.php path: src/Bundle/ChillMainBundle/Controller/PostalCodeController.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 2
path: src/Bundle/ChillMainBundle/DataFixtures/ORM/LoadLanguages.php
-
message: "#^Call to function array_search\\(\\) requires parameter \\#3 to be set\\.$#"
count: 5
path: src/Bundle/ChillMainBundle/DependencyInjection/CompilerPass/ExportsCompilerPass.php
-
message: "#^Call to function array_search\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/DependencyInjection/CompilerPass/SearchableServicesCompilerPass.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/DependencyInjection/Configuration.php
-
message: "#^Call to function array_search\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/DependencyInjection/Widget/AbstractWidgetsCompilerPass.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/DependencyInjection/Widget/AbstractWidgetsCompilerPass.php
- -
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#" message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1 count: 1
@@ -253,8 +403,23 @@ parameters:
- -
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#" message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1 count: 1
path: src/Bundle/ChillMainBundle/Entity/Embeddable/CommentEmbeddable.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Entity/User.php path: src/Bundle/ChillMainBundle/Entity/User.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Entity/User.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 6
path: src/Bundle/ChillMainBundle/Export/ExportManager.php
- -
message: "#^Only booleans are allowed in a ternary operator condition, mixed given\\.$#" message: "#^Only booleans are allowed in a ternary operator condition, mixed given\\.$#"
count: 1 count: 1
@@ -285,26 +450,81 @@ parameters:
count: 1 count: 1
path: src/Bundle/ChillMainBundle/Form/Type/ChillTextareaType.php path: src/Bundle/ChillMainBundle/Form/Type/ChillTextareaType.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Form/Type/ComposedRoleScopeType.php
- -
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#" message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 3 count: 3
path: src/Bundle/ChillMainBundle/Form/Type/DataTransformer/DateIntervalTransformer.php path: src/Bundle/ChillMainBundle/Form/Type/DataTransformer/DateIntervalTransformer.php
-
message: "#^Only booleans are allowed in a negated boolean, mixed given\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Form/Type/DataTransformer/ObjectToIdTransformer.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Form/Type/Export/PickCenterType.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Form/Type/TranslatableStringFormType.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 2
path: src/Bundle/ChillMainBundle/Phonenumber/PhonenumberHelper.php
- -
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#" message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 2 count: 2
path: src/Bundle/ChillMainBundle/Phonenumber/PhonenumberHelper.php path: src/Bundle/ChillMainBundle/Phonenumber/PhonenumberHelper.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Search/Entity/SearchUserApiProvider.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 2
path: src/Bundle/ChillMainBundle/Security/Authorization/AbstractChillVoter.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 2
path: src/Bundle/ChillMainBundle/Security/Authorization/DefaultVoterHelper.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 2
path: src/Bundle/ChillMainBundle/Security/ParentRoleHelper.php
- -
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#" message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1 count: 1
path: src/Bundle/ChillMainBundle/Security/PasswordRecover/PasswordRecoverEvent.php path: src/Bundle/ChillMainBundle/Security/PasswordRecover/PasswordRecoverEvent.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Security/PasswordRecover/PasswordRecoverVoter.php
- -
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#" message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1 count: 1
path: src/Bundle/ChillMainBundle/Security/PasswordRecover/TokenManager.php path: src/Bundle/ChillMainBundle/Security/PasswordRecover/TokenManager.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 3
path: src/Bundle/ChillMainBundle/Templating/Entity/AddressRender.php
- -
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#" message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1 count: 1
@@ -315,6 +535,21 @@ parameters:
count: 1 count: 1
path: src/Bundle/ChillMainBundle/Timeline/TimelineBuilder.php path: src/Bundle/ChillMainBundle/Timeline/TimelineBuilder.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 2
path: src/Bundle/ChillMainBundle/Validation/Validator/RoleScopeScopePresence.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Validation/Validator/ValidPhonenumber.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Actions/Remove/PersonMove.php
- -
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#" message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1 count: 1
@@ -340,11 +575,51 @@ parameters:
count: 1 count: 1
path: src/Bundle/ChillPersonBundle/Command/ChillPersonMoveCommand.php path: src/Bundle/ChillPersonBundle/Command/ChillPersonMoveCommand.php
-
message: "#^Call to function array_search\\(\\) requires parameter \\#3 to be set\\.$#"
count: 3
path: src/Bundle/ChillPersonBundle/Command/ImportPeopleFromCSVCommand.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 3
path: src/Bundle/ChillPersonBundle/Command/ImportPeopleFromCSVCommand.php
- -
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#" message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 6 count: 6
path: src/Bundle/ChillPersonBundle/Command/ImportPeopleFromCSVCommand.php path: src/Bundle/ChillPersonBundle/Command/ImportPeopleFromCSVCommand.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Controller/PersonController.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/DependencyInjection/CompilerPass/AccompanyingPeriodTimelineCompilerPass.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Entity/Person.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 2
path: src/Bundle/ChillPersonBundle/Entity/PersonPhone.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 2
path: src/Bundle/ChillPersonBundle/Export/AbstractAccompanyingPeriodExportElement.php
- -
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#" message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1 count: 1
@@ -355,11 +630,26 @@ parameters:
count: 1 count: 1
path: src/Bundle/ChillPersonBundle/Export/Aggregator/NationalityAggregator.php path: src/Bundle/ChillPersonBundle/Export/Aggregator/NationalityAggregator.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 3
path: src/Bundle/ChillPersonBundle/Export/Export/ListPerson.php
- -
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#" message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 2 count: 2
path: src/Bundle/ChillPersonBundle/Export/Export/ListPerson.php path: src/Bundle/ChillPersonBundle/Export/Export/ListPerson.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Export/Filter/GenderFilter.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Form/ChoiceLoader/PersonChoiceLoader.php
- -
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#" message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1 count: 1
@@ -380,6 +670,26 @@ parameters:
count: 1 count: 1
path: src/Bundle/ChillPersonBundle/Form/Type/PersonPhoneType.php path: src/Bundle/ChillPersonBundle/Form/Type/PersonPhoneType.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Form/Type/PickPersonType.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Household/MembersEditor.php
-
message: "#^Method Chill\\\\PersonBundle\\\\Repository\\\\AccompanyingPeriod\\\\AccompanyingPeriodWorkRepository\\:\\:buildQueryBySocialActionWithDescendants\\(\\) has invalid return type Chill\\\\PersonBundle\\\\Repository\\\\AccompanyingPeriod\\\\QueryBuilder\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 2
path: src/Bundle/ChillPersonBundle/Repository/PersonRepository.php
- -
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#" message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 3 count: 3
@@ -390,26 +700,71 @@ parameters:
count: 1 count: 1
path: src/Bundle/ChillPersonBundle/Search/PersonSearch.php path: src/Bundle/ChillPersonBundle/Search/PersonSearch.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Search/SearchPersonApiProvider.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Serializer/Normalizer/AccompanyingPeriodWorkDenormalizer.php
- -
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#" message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 2 count: 2
path: src/Bundle/ChillPersonBundle/Templating/Entity/ClosingMotiveRender.php path: src/Bundle/ChillPersonBundle/Templating/Entity/ClosingMotiveRender.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Timeline/AbstractTimelineAccompanyingPeriod.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillReportBundle/DataFixtures/ORM/LoadReportACL.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillReportBundle/DataFixtures/ORM/LoadReports.php
- -
message: "#^Method Chill\\\\ReportBundle\\\\DataFixtures\\\\ORM\\\\LoadReports\\:\\:getRandomChoice\\(\\) should return array\\<string\\>\\|string but return statement is missing\\.$#" message: "#^Method Chill\\\\ReportBundle\\\\DataFixtures\\\\ORM\\\\LoadReports\\:\\:getRandomChoice\\(\\) should return array\\<string\\>\\|string but return statement is missing\\.$#"
count: 1 count: 1
path: src/Bundle/ChillReportBundle/DataFixtures/ORM/LoadReports.php path: src/Bundle/ChillReportBundle/DataFixtures/ORM/LoadReports.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 4
path: src/Bundle/ChillReportBundle/Export/Export/ReportList.php
- -
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#" message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 2 count: 2
path: src/Bundle/ChillReportBundle/Export/Export/ReportList.php path: src/Bundle/ChillReportBundle/Export/Export/ReportList.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillReportBundle/Security/Authorization/ReportVoter.php
- -
message: "#^Method Chill\\\\ReportBundle\\\\Security\\\\Authorization\\\\ReportVoter\\:\\:supports\\(\\) should return bool but return statement is missing\\.$#" message: "#^Method Chill\\\\ReportBundle\\\\Security\\\\Authorization\\\\ReportVoter\\:\\:supports\\(\\) should return bool but return statement is missing\\.$#"
count: 1 count: 1
path: src/Bundle/ChillReportBundle/Security/Authorization/ReportVoter.php path: src/Bundle/ChillReportBundle/Security/Authorization/ReportVoter.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 4
path: src/Bundle/ChillReportBundle/Timeline/TimelineReportProvider.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillTaskBundle/DataFixtures/ORM/LoadTaskACL.php
- -
message: "#^Casting to string something that's already string\\.$#" message: "#^Casting to string something that's already string\\.$#"
count: 3 count: 3
@@ -435,11 +790,31 @@ parameters:
count: 1 count: 1
path: src/Bundle/ChillTaskBundle/Timeline/SingleTaskTaskLifeCycleEventTimelineProvider.php path: src/Bundle/ChillTaskBundle/Timeline/SingleTaskTaskLifeCycleEventTimelineProvider.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillTaskBundle/Timeline/TaskLifeCycleEventTimelineProvider.php
- -
message: "#^Method Chill\\\\TaskBundle\\\\Timeline\\\\TaskLifeCycleEventTimelineProvider\\:\\:getTransitionByName\\(\\) should return Symfony\\\\Component\\\\Workflow\\\\Transition but return statement is missing\\.$#" message: "#^Method Chill\\\\TaskBundle\\\\Timeline\\\\TaskLifeCycleEventTimelineProvider\\:\\:getTransitionByName\\(\\) should return Symfony\\\\Component\\\\Workflow\\\\Transition but return statement is missing\\.$#"
count: 1 count: 1
path: src/Bundle/ChillTaskBundle/Timeline/TaskLifeCycleEventTimelineProvider.php path: src/Bundle/ChillTaskBundle/Timeline/TaskLifeCycleEventTimelineProvider.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillThirdPartyBundle/DependencyInjection/CompilerPass/ThirdPartyTypeCompilerPass.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 4
path: src/Bundle/ChillThirdPartyBundle/Entity/ThirdParty.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillThirdPartyBundle/Form/ChoiceLoader/ThirdPartyChoiceLoader.php
- -
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#" message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1 count: 1
@@ -450,8 +825,28 @@ parameters:
count: 1 count: 1
path: src/Bundle/ChillThirdPartyBundle/Repository/ThirdPartyRepository.php path: src/Bundle/ChillThirdPartyBundle/Repository/ThirdPartyRepository.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillThirdPartyBundle/Search/ThirdPartyApiSearch.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1
path: src/Bundle/ChillThirdPartyBundle/Search/ThirdPartyApiSearch.php
- -
message: "#^Method Chill\\\\ThirdPartyBundle\\\\Search\\\\ThirdPartySearch\\:\\:renderResult\\(\\) should return string but return statement is missing\\.$#" message: "#^Method Chill\\\\ThirdPartyBundle\\\\Search\\\\ThirdPartySearch\\:\\:renderResult\\(\\) should return string but return statement is missing\\.$#"
count: 1 count: 1
path: src/Bundle/ChillThirdPartyBundle/Search/ThirdPartySearch.php path: src/Bundle/ChillThirdPartyBundle/Search/ThirdPartySearch.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillThirdPartyBundle/Security/Voter/ThirdPartyVoter.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1
path: src/Bundle/ChillThirdPartyBundle/Templating/Entity/ThirdPartyRender.php

View File

@@ -3,7 +3,6 @@ parameters:
paths: paths:
- src/ - src/
excludePaths: excludePaths:
- docs/
- src/Bundle/*/Tests/* - src/Bundle/*/Tests/*
- src/Bundle/*/tests/* - src/Bundle/*/tests/*
- src/Bundle/*/Test/* - src/Bundle/*/Test/*
@@ -19,8 +18,7 @@ parameters:
- src/Bundle/*/src/Resources/* - src/Bundle/*/src/Resources/*
includes: includes:
- phpstan-baseline.neon
- phpstan-critical.neon
- phpstan-deprecations.neon
- phpstan-types.neon - phpstan-types.neon
- phpstan-critical.neon
- phpstan-baseline.neon

View File

@@ -2,7 +2,7 @@
<!-- https://phpunit.readthedocs.io/en/latest/configuration.html --> <!-- https://phpunit.readthedocs.io/en/latest/configuration.html -->
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" <phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="tests/app/vendor/phpunit/phpunit/phpunit.xsd" xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
backupGlobals="false" backupGlobals="false"
colors="true" colors="true"
bootstrap="tests/app/tests/bootstrap.php" bootstrap="tests/app/tests/bootstrap.php"

File diff suppressed because it is too large Load Diff

View File

@@ -1,16 +0,0 @@
<?xml version="1.0"?>
<psalm
errorLevel="7"
resolveFromConfigFile="true"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="https://getpsalm.org/schema/config"
xsi:schemaLocation="https://getpsalm.org/schema/config tests/app/vendor/vimeo/psalm/config.xsd"
errorBaseline="psalm-baseline.xml"
>
<projectFiles>
<directory name="src" />
<ignoreFiles>
<directory name="./tests/" />
</ignoreFiles>
</projectFiles>
</psalm>

View File

@@ -1,5 +0,0 @@
Chill is a software for social workers
For the full copyright and license information, please view
the LICENSE file that was distributed with this source code.

View File

@@ -1,14 +1,5 @@
<?php <?php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Chill\ActivityBundle; namespace Chill\ActivityBundle;
use Symfony\Component\HttpKernel\Bundle\Bundle; use Symfony\Component\HttpKernel\Bundle\Bundle;

View File

@@ -1,25 +1,15 @@
<?php <?php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1); declare(strict_types=1);
namespace Chill\ActivityBundle\Controller; namespace Chill\ActivityBundle\Controller;
use Chill\ActivityBundle\Entity\Activity;
use Chill\ActivityBundle\Entity\ActivityReason; use Chill\ActivityBundle\Entity\ActivityReason;
use Chill\ActivityBundle\Form\ActivityType;
use Chill\ActivityBundle\Repository\ActivityACLAwareRepositoryInterface; use Chill\ActivityBundle\Repository\ActivityACLAwareRepositoryInterface;
use Chill\ActivityBundle\Repository\ActivityRepository; use Chill\ActivityBundle\Repository\ActivityRepository;
use Chill\ActivityBundle\Repository\ActivityTypeCategoryRepository; use Chill\ActivityBundle\Repository\ActivityTypeCategoryRepository;
use Chill\ActivityBundle\Repository\ActivityTypeRepository; use Chill\ActivityBundle\Repository\ActivityTypeRepository;
use Chill\ActivityBundle\Security\Authorization\ActivityVoter; use Chill\ActivityBundle\Security\Authorization\ActivityVoter;
use Chill\MainBundle\Entity\Embeddable\CommentEmbeddable;
use Chill\MainBundle\Repository\LocationRepository; use Chill\MainBundle\Repository\LocationRepository;
use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\Person; use Chill\PersonBundle\Entity\Person;
@@ -27,49 +17,46 @@ use Chill\PersonBundle\Privacy\PrivacyEvent;
use Chill\PersonBundle\Repository\AccompanyingPeriodRepository; use Chill\PersonBundle\Repository\AccompanyingPeriodRepository;
use Chill\PersonBundle\Repository\PersonRepository; use Chill\PersonBundle\Repository\PersonRepository;
use Chill\ThirdPartyBundle\Repository\ThirdPartyRepository; use Chill\ThirdPartyBundle\Repository\ThirdPartyRepository;
use DateTime;
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityManagerInterface;
use InvalidArgumentException;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
use RuntimeException;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\FormInterface; use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Role\Role; use Symfony\Component\Security\Core\Role\Role;
use Chill\ActivityBundle\Entity\Activity;
use Chill\ActivityBundle\Form\ActivityType;
use Chill\MainBundle\Entity\Embeddable\CommentEmbeddable;
use Symfony\Component\Serializer\SerializerInterface; use Symfony\Component\Serializer\SerializerInterface;
use function array_key_exists;
final class ActivityController extends AbstractController final class ActivityController extends AbstractController
{ {
private AccompanyingPeriodRepository $accompanyingPeriodRepository;
private ActivityACLAwareRepositoryInterface $activityACLAwareRepository;
private ActivityRepository $activityRepository;
private ActivityTypeCategoryRepository $activityTypeCategoryRepository;
private ActivityTypeRepository $activityTypeRepository;
private EntityManagerInterface $entityManager;
private EventDispatcherInterface $eventDispatcher; private EventDispatcherInterface $eventDispatcher;
private LocationRepository $locationRepository;
private LoggerInterface $logger; private LoggerInterface $logger;
private PersonRepository $personRepository;
private SerializerInterface $serializer; private SerializerInterface $serializer;
private ActivityACLAwareRepositoryInterface $activityACLAwareRepository;
private ActivityTypeRepository $activityTypeRepository;
private ThirdPartyRepository $thirdPartyRepository; private ThirdPartyRepository $thirdPartyRepository;
private PersonRepository $personRepository;
private LocationRepository $locationRepository;
private EntityManagerInterface $entityManager;
private ActivityRepository $activityRepository;
private AccompanyingPeriodRepository $accompanyingPeriodRepository;
private ActivityTypeCategoryRepository $activityTypeCategoryRepository;
public function __construct( public function __construct(
ActivityACLAwareRepositoryInterface $activityACLAwareRepository, ActivityACLAwareRepositoryInterface $activityACLAwareRepository,
ActivityTypeRepository $activityTypeRepository, ActivityTypeRepository $activityTypeRepository,
@@ -98,177 +85,6 @@ final class ActivityController extends AbstractController
$this->serializer = $serializer; $this->serializer = $serializer;
} }
/**
* Deletes a Activity entity.
*
* @param mixed $id
*/
public function deleteAction(Request $request, $id)
{
$view = null;
[$person, $accompanyingPeriod] = $this->getEntity($request);
$activity = $this->activityRepository->find($id);
if (!$activity) {
throw $this->createNotFoundException('Unable to find Activity entity.');
}
if ($activity->getAccompanyingPeriod() instanceof AccompanyingPeriod) {
$view = 'ChillActivityBundle:Activity:confirm_deleteAccompanyingCourse.html.twig';
$accompanyingPeriod = $activity->getAccompanyingPeriod();
} else {
$view = 'ChillActivityBundle:Activity:confirm_deletePerson.html.twig';
}
// TODO
// $this->denyAccessUnlessGranted('CHILL_ACTIVITY_DELETE', $activity);
$form = $this->createDeleteForm($activity->getId(), $person, $accompanyingPeriod);
if ($request->getMethod() === Request::METHOD_DELETE) {
$form->handleRequest($request);
if ($form->isValid()) {
$this->logger->notice('An activity has been removed', [
'by_user' => $this->getUser()->getUsername(),
'activity_id' => $activity->getId(),
'person_id' => $activity->getPerson() ? $activity->getPerson()->getId() : null,
'comment' => $activity->getComment()->getComment(),
'scope_id' => $activity->getScope() ? $activity->getScope()->getId() : null,
'reasons_ids' => $activity->getReasons()
->map(
static fn (ActivityReason $ar): int => $ar->getId()
)
->toArray(),
'type_id' => $activity->getActivityType()->getId(),
'duration' => $activity->getDurationTime() ? $activity->getDurationTime()->format('U') : null,
'date' => $activity->getDate()->format('Y-m-d'),
'attendee' => $activity->getAttendee(),
]);
$this->entityManager->remove($activity);
$this->entityManager->flush();
$this->addFlash('success', $this->get('translator')
->trans('The activity has been successfully removed.'));
$params = $this->buildParamsToUrl($person, $accompanyingPeriod);
return $this->redirectToRoute('chill_activity_activity_list', $params);
}
}
if (null === $view) {
throw $this->createNotFoundException('Template not found');
}
return $this->render($view, [
'activity' => $activity,
'delete_form' => $form->createView(),
'person' => $person,
'accompanyingCourse' => $accompanyingPeriod,
]);
}
/**
* Displays a form to edit an existing Activity entity.
*/
public function editAction(int $id, Request $request): Response
{
$view = null;
$entity = $this->activityRepository->find($id);
if (null === $entity) {
throw $this->createNotFoundException('Unable to find Activity entity.');
}
$accompanyingPeriod = $entity->getAccompanyingPeriod();
$person = $entity->getPerson();
if ($entity->getAccompanyingPeriod() instanceof AccompanyingPeriod) {
$view = 'ChillActivityBundle:Activity:editAccompanyingCourse.html.twig';
$accompanyingPeriod = $entity->getAccompanyingPeriod();
} else {
$view = 'ChillActivityBundle:Activity:editPerson.html.twig';
}
// TODO
// $this->denyAccessUnlessGranted('CHILL_ACTIVITY_UPDATE', $entity);
$form = $this->createForm(ActivityType::class, $entity, [
'center' => $entity->getCenter(),
'role' => new Role('CHILL_ACTIVITY_UPDATE'),
'activityType' => $entity->getActivityType(),
'accompanyingPeriod' => $accompanyingPeriod,
]);
if ($form->has('documents')) {
$form->add('gendocTemplateId', HiddenType::class, [
'mapped' => false,
'data' => null,
'attr' => [
// required for js
'data-template-id' => 'data-template-id',
],
]);
}
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$this->entityManager->persist($entity);
$this->entityManager->flush();
$params = $this->buildParamsToUrl($person, $accompanyingPeriod);
$params['id'] = $entity->getId();
if ($form->has('gendocTemplateId') && null !== $form['gendocTemplateId']->getData()) {
return $this->redirectToRoute(
'chill_docgenerator_generate_from_template',
[
'template' => $form->get('gendocTemplateId')->getData(),
'entityClassName' => Activity::class,
'entityId' => $entity->getId(),
'returnPath' => $this->generateUrl('chill_activity_activity_edit', $params),
]
);
}
$this->addFlash('success', $this->get('translator')->trans('Success : activity updated!'));
return $this->redirectToRoute('chill_activity_activity_show', $params);
}
$deleteForm = $this->createDeleteForm($entity->getId(), $person, $accompanyingPeriod);
/*
* TODO
$event = new PrivacyEvent($person, array(
'element_class' => Activity::class,
'element_id' => $entity->getId(),
'action' => 'edit'
));
$this->eventDispatcher->dispatch(PrivacyEvent::PERSON_PRIVACY_EVENT, $event);
*/
if (null === $view) {
throw $this->createNotFoundException('Template not found');
}
$activity_array = $this->serializer->normalize($entity, 'json', ['groups' => 'read']);
return $this->render($view, [
'entity' => $entity,
'edit_form' => $form->createView(),
'delete_form' => $deleteForm->createView(),
'person' => $person,
'accompanyingCourse' => $accompanyingPeriod,
'activity_json' => $activity_array,
]);
}
/** /**
* Lists all Activity entities. * Lists all Activity entities.
*/ */
@@ -283,11 +99,11 @@ final class ActivityController extends AbstractController
if ($person instanceof Person) { if ($person instanceof Person) {
$this->denyAccessUnlessGranted(ActivityVoter::SEE, $person); $this->denyAccessUnlessGranted(ActivityVoter::SEE, $person);
$activities = $this->activityACLAwareRepository $activities = $this->activityACLAwareRepository
->findByPerson($person, ActivityVoter::SEE, 0, null, ['date' => 'DESC', 'id' => 'DESC']); ->findByPerson($person, ActivityVoter::SEE, 0, null);
$event = new PrivacyEvent($person, [ $event = new PrivacyEvent($person, [
'element_class' => Activity::class, 'element_class' => Activity::class,
'action' => 'list', 'action' => 'list'
]); ]);
$this->eventDispatcher->dispatch(PrivacyEvent::PERSON_PRIVACY_EVENT, $event); $this->eventDispatcher->dispatch(PrivacyEvent::PERSON_PRIVACY_EVENT, $event);
@@ -296,7 +112,7 @@ final class ActivityController extends AbstractController
$this->denyAccessUnlessGranted(ActivityVoter::SEE, $accompanyingPeriod); $this->denyAccessUnlessGranted(ActivityVoter::SEE, $accompanyingPeriod);
$activities = $this->activityACLAwareRepository $activities = $this->activityACLAwareRepository
->findByAccompanyingPeriod($accompanyingPeriod, ActivityVoter::SEE, 0, null, ['date' => 'DESC', 'id' => 'DESC']); ->findByAccompanyingPeriod($accompanyingPeriod, ActivityVoter::SEE);
$view = 'ChillActivityBundle:Activity:listAccompanyingCourse.html.twig'; $view = 'ChillActivityBundle:Activity:listAccompanyingCourse.html.twig';
} }
@@ -311,175 +127,6 @@ final class ActivityController extends AbstractController
); );
} }
public function newAction(Request $request): Response
{
$view = null;
[$person, $accompanyingPeriod] = $this->getEntity($request);
if ($accompanyingPeriod instanceof AccompanyingPeriod) {
$view = 'ChillActivityBundle:Activity:newAccompanyingCourse.html.twig';
} elseif ($person instanceof Person) {
$view = 'ChillActivityBundle:Activity:newPerson.html.twig';
}
$activityType_id = $request->get('activityType_id', 0);
$activityType = $this->activityTypeRepository->find($activityType_id);
if (isset($activityType) && !$activityType->isActive()) {
throw new InvalidArgumentException('Activity type must be active');
}
$activityData = null;
if ($request->query->has('activityData')) {
$activityData = $request->query->get('activityData');
}
if (
!$activityType instanceof \Chill\ActivityBundle\Entity\ActivityType
|| !$activityType->isActive()
) {
$params = $this->buildParamsToUrl($person, $accompanyingPeriod);
if (null !== $activityData) {
$params['activityData'] = $activityData;
}
return $this->redirectToRoute('chill_activity_activity_select_type', $params);
}
$entity = new Activity();
$entity->setUser($this->getUser());
if ($person instanceof Person) {
$entity->setPerson($person);
}
if ($accompanyingPeriod instanceof AccompanyingPeriod) {
$entity->setAccompanyingPeriod($accompanyingPeriod);
}
$entity->setActivityType($activityType);
$entity->setDate(new DateTime('now'));
if ($request->query->has('activityData')) {
$activityData = $request->query->get('activityData');
if (array_key_exists('durationTime', $activityData)) {
$durationTimeInMinutes = $activityData['durationTime'];
$hours = floor($durationTimeInMinutes / 60);
$minutes = $durationTimeInMinutes % 60;
$duration = DateTime::createFromFormat('H:i', $hours . ':' . $minutes);
if ($duration) {
$entity->setDurationTime($duration);
}
}
if (array_key_exists('date', $activityData)) {
$date = DateTime::createFromFormat('Y-m-d', $activityData['date']);
if ($date) {
$entity->setDate($date);
}
}
if (array_key_exists('personsId', $activityData)) {
foreach ($activityData['personsId'] as $personId) {
$concernedPerson = $this->personRepository->find($personId);
$entity->addPerson($concernedPerson);
}
}
if (array_key_exists('professionalsId', $activityData)) {
foreach ($activityData['professionalsId'] as $professionalsId) {
$professional = $this->thirdPartyRepository->find($professionalsId);
$entity->addThirdParty($professional);
}
}
if (array_key_exists('location', $activityData)) {
$location = $this->locationRepository->find($activityData['location']);
$entity->setLocation($location);
}
if (array_key_exists('comment', $activityData)) {
$comment = new CommentEmbeddable();
$comment->setComment($activityData['comment']);
$comment->setUserId($this->getUser()->getid());
$comment->setDate(new DateTime('now'));
$entity->setComment($comment);
}
}
$this->denyAccessUnlessGranted(ActivityVoter::CREATE, $entity);
$form = $this->createForm(ActivityType::class, $entity, [
'center' => $entity->getCenter(),
'role' => new Role('CHILL_ACTIVITY_CREATE'),
'activityType' => $entity->getActivityType(),
'accompanyingPeriod' => $accompanyingPeriod,
]);
if ($form->has('documents')) {
$form->add('gendocTemplateId', HiddenType::class, [
'mapped' => false,
'data' => null,
'attr' => [
// required for js
'data-template-id' => 'data-template-id',
],
]);
}
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$this->entityManager->persist($entity);
$this->entityManager->flush();
if ($form->has('gendocTemplateId') && null !== $form['gendocTemplateId']->getData()) {
return $this->redirectToRoute(
'chill_docgenerator_generate_from_template',
[
'template' => $form->get('gendocTemplateId')->getData(),
'entityClassName' => Activity::class,
'entityId' => $entity->getId(),
'returnPath' => $this->generateUrl('chill_activity_activity_edit', [
'id' => $entity->getId(),
]),
]
);
}
$this->addFlash('success', $this->get('translator')->trans('Success : activity created!'));
$params = $this->buildParamsToUrl($person, $accompanyingPeriod);
$params['id'] = $entity->getId();
return $this->redirectToRoute('chill_activity_activity_show', $params);
}
if (null === $view) {
throw $this->createNotFoundException('Template not found');
}
$activity_array = $this->serializer->normalize($entity, 'json', ['groups' => 'read']);
$defaultLocation = $this->getUser()->getCurrentLocation();
return $this->render($view, [
'person' => $person,
'accompanyingCourse' => $accompanyingPeriod,
'entity' => $entity,
'form' => $form->createView(),
'activity_json' => $activity_array,
'default_location' => $defaultLocation,
]);
}
public function selectTypeAction(Request $request): Response public function selectTypeAction(Request $request): Response
{ {
$view = null; $view = null;
@@ -512,7 +159,7 @@ final class ActivityController extends AbstractController
]; ];
} }
if (null === $view) { if ($view === null) {
throw $this->createNotFoundException('Template not found'); throw $this->createNotFoundException('Template not found');
} }
@@ -524,23 +171,158 @@ final class ActivityController extends AbstractController
]); ]);
} }
public function showAction(Request $request, int $id): Response public function newAction(Request $request): Response
{ {
$entity = $this->activityRepository->find($id); $view = null;
if (null === $entity) { [$person, $accompanyingPeriod] = $this->getEntity($request);
throw $this->createNotFoundException('Unable to find Activity entity.');
if ($accompanyingPeriod instanceof AccompanyingPeriod) {
$view = 'ChillActivityBundle:Activity:newAccompanyingCourse.html.twig';
} elseif ($person instanceof Person) {
$view = 'ChillActivityBundle:Activity:newPerson.html.twig';
} }
$accompanyingPeriod = $entity->getAccompanyingPeriod(); $activityType_id = $request->get('activityType_id', 0);
$person = $entity->getPerson(); $activityType = $this->activityTypeRepository->find($activityType_id);
if (isset($activityType) && !$activityType->isActive()) {
throw new \InvalidArgumentException('Activity type must be active');
}
$activityData = null;
if ($request->query->has('activityData')) {
$activityData = $request->query->get('activityData');
}
if (!$activityType instanceof \Chill\ActivityBundle\Entity\ActivityType ||
!$activityType->isActive()) {
$params = $this->buildParamsToUrl($person, $accompanyingPeriod);
if (null !== $activityData) {
$params['activityData'] = $activityData;
}
return $this->redirectToRoute('chill_activity_activity_select_type', $params);
}
$entity = new Activity();
$entity->setUser($this->getUser());
if ($person instanceof Person) {
$entity->setPerson($person);
}
if ($accompanyingPeriod instanceof AccompanyingPeriod) {
$entity->setAccompanyingPeriod($accompanyingPeriod);
}
$entity->setType($activityType);
$entity->setDate(new \DateTime('now'));
if ($request->query->has('activityData')) {
$activityData = $request->query->get('activityData');
if (array_key_exists('durationTime', $activityData)) {
$durationTimeInMinutes = $activityData['durationTime'];
$hours = floor($durationTimeInMinutes / 60);
$minutes = $durationTimeInMinutes % 60;
$duration = \DateTime::createFromFormat("H:i", $hours.':'.$minutes);
if ($duration) {
$entity->setDurationTime($duration);
}
}
if (array_key_exists('date', $activityData)) {
$date = \DateTime::createFromFormat('Y-m-d', $activityData['date']);
if ($date) {
$entity->setDate($date);
}
}
if (array_key_exists('personsId', $activityData)) {
foreach($activityData['personsId'] as $personId){
$concernedPerson = $this->personRepository->find($personId);
$entity->addPerson($concernedPerson);
}
}
if (array_key_exists('professionalsId', $activityData)) {
foreach($activityData['professionalsId'] as $professionalsId){
$professional = $this->thirdPartyRepository->find($professionalsId);
$entity->addThirdParty($professional);
}
}
if (array_key_exists('location', $activityData)) {
$location = $this->locationRepository->find($activityData['location']);
$entity->setLocation($location);
}
if (array_key_exists('comment', $activityData)) {
$comment = new CommentEmbeddable();
$comment->setComment($activityData['comment']);
$comment->setUserId($this->getUser()->getid());
$comment->setDate(new \DateTime('now'));
$entity->setComment($comment);
}
}
// TODO revoir le Voter de Activity pour tenir compte qu'une activité peut appartenir a une période
// $this->denyAccessUnlessGranted('CHILL_ACTIVITY_CREATE', $entity);
$form = $this->createForm(ActivityType::class, $entity, [
'center' => $entity->getCenter(),
'role' => new Role('CHILL_ACTIVITY_CREATE'),
'activityType' => $entity->getType(),
'accompanyingPeriod' => $accompanyingPeriod,
])->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$this->entityManager->persist($entity);
$this->entityManager->flush();
$this->addFlash('success', $this->get('translator')->trans('Success : activity created!'));
$params = $this->buildParamsToUrl($person, $accompanyingPeriod);
$params['id'] = $entity->getId();
return $this->redirectToRoute('chill_activity_activity_show', $params);
}
if ($view === null) {
throw $this->createNotFoundException('Template not found');
}
$activity_array = $this->serializer->normalize($entity, 'json', ['groups' => 'read']);
return $this->render($view, [
'person' => $person,
'accompanyingCourse' => $accompanyingPeriod,
'entity' => $entity,
'form' => $form->createView(),
'activity_json' => $activity_array
]);
}
public function showAction(Request $request, $id): Response
{
$view = null;
[$person, $accompanyingPeriod] = $this->getEntity($request);
if ($accompanyingPeriod instanceof AccompanyingPeriod) { if ($accompanyingPeriod instanceof AccompanyingPeriod) {
$view = 'ChillActivityBundle:Activity:showAccompanyingCourse.html.twig'; $view = 'ChillActivityBundle:Activity:showAccompanyingCourse.html.twig';
} elseif ($person instanceof Person) { } elseif ($person instanceof Person) {
$view = 'ChillActivityBundle:Activity:showPerson.html.twig'; $view = 'ChillActivityBundle:Activity:showPerson.html.twig';
} else { }
throw new RuntimeException('the activity should be linked with a period or person');
$entity = $this->activityRepository->find($id);
if (null === $entity) {
throw $this->createNotFoundException('Unable to find Activity entity.');
} }
if (null !== $accompanyingPeriod) { if (null !== $accompanyingPeriod) {
@@ -549,7 +331,8 @@ final class ActivityController extends AbstractController
$entity->personsNotAssociated = $entity->getPersonsNotAssociated(); $entity->personsNotAssociated = $entity->getPersonsNotAssociated();
} }
$this->denyAccessUnlessGranted(ActivityVoter::SEE, $entity); // TODO revoir le Voter de Activity pour tenir compte qu'une activité peut appartenir a une période
// $this->denyAccessUnlessGranted('CHILL_ACTIVITY_SEE', $entity);
$deleteForm = $this->createDeleteForm($entity->getId(), $person, $accompanyingPeriod); $deleteForm = $this->createDeleteForm($entity->getId(), $person, $accompanyingPeriod);
@@ -563,7 +346,7 @@ final class ActivityController extends AbstractController
$this->eventDispatcher->dispatch(PrivacyEvent::PERSON_PRIVACY_EVENT, $event); $this->eventDispatcher->dispatch(PrivacyEvent::PERSON_PRIVACY_EVENT, $event);
*/ */
if (null === $view) { if ($view === null) {
throw $this->createNotFoundException('Template not found'); throw $this->createNotFoundException('Template not found');
} }
@@ -575,19 +358,146 @@ final class ActivityController extends AbstractController
]); ]);
} }
private function buildParamsToUrl(?Person $person, ?AccompanyingPeriod $accompanyingPeriod): array /**
* Displays a form to edit an existing Activity entity.
*/
public function editAction($id, Request $request): Response
{ {
$params = []; $view = null;
if (null !== $person) { [$person, $accompanyingPeriod] = $this->getEntity($request);
$params['person_id'] = $person->getId();
if ($accompanyingPeriod instanceof AccompanyingPeriod) {
$view = 'ChillActivityBundle:Activity:editAccompanyingCourse.html.twig';
} elseif ($person instanceof Person) {
$view = 'ChillActivityBundle:Activity:editPerson.html.twig';
} }
if (null !== $accompanyingPeriod) { $entity = $this->activityRepository->find($id);
$params['accompanying_period_id'] = $accompanyingPeriod->getId();
if (null === $entity) {
throw $this->createNotFoundException('Unable to find Activity entity.');
} }
return $params; // TODO
// $this->denyAccessUnlessGranted('CHILL_ACTIVITY_UPDATE', $entity);
$form = $this->createForm(ActivityType::class, $entity, [
'center' => $entity->getCenter(),
'role' => new Role('CHILL_ACTIVITY_UPDATE'),
'activityType' => $entity->getType(),
'accompanyingPeriod' => $accompanyingPeriod,
])->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$this->entityManager->persist($entity);
$this->entityManager->flush();
$this->addFlash('success', $this->get('translator')->trans('Success : activity updated!'));
$params = $this->buildParamsToUrl($person, $accompanyingPeriod);
$params['id'] = $entity->getId();
return $this->redirectToRoute('chill_activity_activity_show', $params);
}
$deleteForm = $this->createDeleteForm($entity->getId(), $person, $accompanyingPeriod);
/*
* TODO
$event = new PrivacyEvent($person, array(
'element_class' => Activity::class,
'element_id' => $entity->getId(),
'action' => 'edit'
));
$this->eventDispatcher->dispatch(PrivacyEvent::PERSON_PRIVACY_EVENT, $event);
*/
if ($view === null) {
throw $this->createNotFoundException('Template not found');
}
$activity_array = $this->serializer->normalize($entity, 'json', ['groups' => 'read']);
return $this->render($view, [
'entity' => $entity,
'edit_form' => $form->createView(),
'delete_form' => $deleteForm->createView(),
'person' => $person,
'accompanyingCourse' => $accompanyingPeriod,
'activity_json' => $activity_array
]);
}
/**
* Deletes a Activity entity.
*/
public function deleteAction(Request $request, $id)
{
$view = null;
[$person, $accompanyingPeriod] = $this->getEntity($request);
if ($accompanyingPeriod instanceof AccompanyingPeriod) {
$view = 'ChillActivityBundle:Activity:confirm_deleteAccompanyingCourse.html.twig';
} elseif ($person instanceof Person) {
$view = 'ChillActivityBundle:Activity:confirm_deletePerson.html.twig';
}
$activity = $this->activityRepository->find($id);
if (!$activity) {
throw $this->createNotFoundException('Unable to find Activity entity.');
}
// TODO
// $this->denyAccessUnlessGranted('CHILL_ACTIVITY_DELETE', $activity);
$form = $this->createDeleteForm($activity->getId(), $person, $accompanyingPeriod);
if ($request->getMethod() === Request::METHOD_DELETE) {
$form->handleRequest($request);
if ($form->isValid()) {
$this->logger->notice("An activity has been removed", [
'by_user' => $this->getUser()->getUsername(),
'activity_id' => $activity->getId(),
'person_id' => $activity->getPerson() ? $activity->getPerson()->getId() : null,
'comment' => $activity->getComment()->getComment(),
'scope_id' => $activity->getScope() ? $activity->getScope()->getId() : null,
'reasons_ids' => $activity->getReasons()
->map(
static fn (ActivityReason $ar): int => $ar->getId()
)
->toArray(),
'type_id' => $activity->getType()->getId(),
'duration' => $activity->getDurationTime() ? $activity->getDurationTime()->format('U') : null,
'date' => $activity->getDate()->format('Y-m-d'),
'attendee' => $activity->getAttendee()
]);
$this->entityManager->remove($activity);
$this->entityManager->flush();
$this->addFlash('success', $this->get('translator')
->trans("The activity has been successfully removed."));
$params = $this->buildParamsToUrl($person, $accompanyingPeriod);
return $this->redirectToRoute('chill_activity_activity_list', $params);
}
}
if ($view === null) {
throw $this->createNotFoundException('Template not found');
}
return $this->render($view, [
'activity' => $activity,
'delete_form' => $form->createView(),
'person' => $person,
'accompanyingCourse' => $accompanyingPeriod,
]);
} }
/** /**
@@ -613,7 +523,7 @@ final class ActivityController extends AbstractController
$person_id = $request->get('person_id'); $person_id = $request->get('person_id');
$person = $this->personRepository->find($person_id); $person = $this->personRepository->find($person_id);
if (null === $person) { if ($person === null) {
throw $this->createNotFoundException('Person not found'); throw $this->createNotFoundException('Person not found');
} }
@@ -622,19 +532,34 @@ final class ActivityController extends AbstractController
$accompanying_period_id = $request->get('accompanying_period_id'); $accompanying_period_id = $request->get('accompanying_period_id');
$accompanyingPeriod = $this->accompanyingPeriodRepository->find($accompanying_period_id); $accompanyingPeriod = $this->accompanyingPeriodRepository->find($accompanying_period_id);
if (null === $accompanyingPeriod) { if ($accompanyingPeriod === null) {
throw $this->createNotFoundException('Accompanying Period not found'); throw $this->createNotFoundException('Accompanying Period not found');
} }
// TODO Add permission // TODO Add permission
// $this->denyAccessUnlessGranted('CHILL_PERSON_SEE', $person); // $this->denyAccessUnlessGranted('CHILL_PERSON_SEE', $person);
} else { } else {
throw $this->createNotFoundException('Person or Accompanying Period not found'); throw $this->createNotFoundException("Person or Accompanying Period not found");
} }
return [ return [
$person, $person,
$accompanyingPeriod, $accompanyingPeriod
]; ];
} }
private function buildParamsToUrl(?Person $person, ?AccompanyingPeriod $accompanyingPeriod): array
{
$params = [];
if (null !== $person) {
$params['person_id'] = $person->getId();
}
if (null !== $accompanyingPeriod) {
$params['accompanying_period_id'] = $accompanyingPeriod->getId();
}
return $params;
}
} }

View File

@@ -1,29 +1,38 @@
<?php <?php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Chill\ActivityBundle\Controller; namespace Chill\ActivityBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Chill\ActivityBundle\Entity\ActivityReasonCategory; use Chill\ActivityBundle\Entity\ActivityReasonCategory;
use Chill\ActivityBundle\Form\ActivityReasonCategoryType; use Chill\ActivityBundle\Form\ActivityReasonCategoryType;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\HttpFoundation\Request;
/** /**
* ActivityReasonCategory controller. * ActivityReasonCategory controller.
*
*/ */
class ActivityReasonCategoryController extends AbstractController class ActivityReasonCategoryController extends AbstractController
{ {
/**
* Lists all ActivityReasonCategory entities.
*
*/
public function indexAction()
{
$em = $this->getDoctrine()->getManager();
$entities = $em->getRepository('ChillActivityBundle:ActivityReasonCategory')->findAll();
return $this->render('ChillActivityBundle:ActivityReasonCategory:index.html.twig', array(
'entities' => $entities,
));
}
/** /**
* Creates a new ActivityReasonCategory entity. * Creates a new ActivityReasonCategory entity.
*
*/ */
public function createAction(Request $request) public function createAction(Request $request)
{ {
@@ -36,19 +45,71 @@ class ActivityReasonCategoryController extends AbstractController
$em->persist($entity); $em->persist($entity);
$em->flush(); $em->flush();
return $this->redirect($this->generateUrl('chill_activity_activityreasoncategory_show', ['id' => $entity->getId()])); return $this->redirect($this->generateUrl('chill_activity_activityreasoncategory_show', array('id' => $entity->getId())));
} }
return $this->render('ChillActivityBundle:ActivityReasonCategory:new.html.twig', [ return $this->render('ChillActivityBundle:ActivityReasonCategory:new.html.twig', array(
'entity' => $entity, 'entity' => $entity,
'form' => $form->createView(), 'form' => $form->createView(),
]); ));
}
/**
* Creates a form to create a ActivityReasonCategory entity.
*
* @param ActivityReasonCategory $entity The entity
*
* @return \Symfony\Component\Form\Form The form
*/
private function createCreateForm(ActivityReasonCategory $entity)
{
$form = $this->createForm(ActivityReasonCategoryType::class, $entity, array(
'action' => $this->generateUrl('chill_activity_activityreasoncategory_create'),
'method' => 'POST',
));
$form->add('submit', SubmitType::class, array('label' => 'Create'));
return $form;
}
/**
* Displays a form to create a new ActivityReasonCategory entity.
*
*/
public function newAction()
{
$entity = new ActivityReasonCategory();
$form = $this->createCreateForm($entity);
return $this->render('ChillActivityBundle:ActivityReasonCategory:new.html.twig', array(
'entity' => $entity,
'form' => $form->createView(),
));
}
/**
* Finds and displays a ActivityReasonCategory entity.
*
*/
public function showAction($id)
{
$em = $this->getDoctrine()->getManager();
$entity = $em->getRepository('ChillActivityBundle:ActivityReasonCategory')->find($id);
if (!$entity) {
throw $this->createNotFoundException('Unable to find ActivityReasonCategory entity.');
}
return $this->render('ChillActivityBundle:ActivityReasonCategory:show.html.twig', array(
'entity' => $entity,
));
} }
/** /**
* Displays a form to edit an existing ActivityReasonCategory entity. * Displays a form to edit an existing ActivityReasonCategory entity.
* *
* @param mixed $id
*/ */
public function editAction($id) public function editAction($id)
{ {
@@ -62,64 +123,33 @@ class ActivityReasonCategoryController extends AbstractController
$editForm = $this->createEditForm($entity); $editForm = $this->createEditForm($entity);
return $this->render('ChillActivityBundle:ActivityReasonCategory:edit.html.twig', [ return $this->render('ChillActivityBundle:ActivityReasonCategory:edit.html.twig', array(
'entity' => $entity, 'entity' => $entity,
'edit_form' => $editForm->createView(), 'edit_form' => $editForm->createView(),
]); ));
} }
/** /**
* Lists all ActivityReasonCategory entities. * Creates a form to edit a ActivityReasonCategory entity.
*/
public function indexAction()
{
$em = $this->getDoctrine()->getManager();
$entities = $em->getRepository('ChillActivityBundle:ActivityReasonCategory')->findAll();
return $this->render('ChillActivityBundle:ActivityReasonCategory:index.html.twig', [
'entities' => $entities,
]);
}
/**
* Displays a form to create a new ActivityReasonCategory entity.
*/
public function newAction()
{
$entity = new ActivityReasonCategory();
$form = $this->createCreateForm($entity);
return $this->render('ChillActivityBundle:ActivityReasonCategory:new.html.twig', [
'entity' => $entity,
'form' => $form->createView(),
]);
}
/**
* Finds and displays a ActivityReasonCategory entity.
* *
* @param mixed $id * @param ActivityReasonCategory $entity The entity
*
* @return \Symfony\Component\Form\Form The form
*/ */
public function showAction($id) private function createEditForm(ActivityReasonCategory $entity)
{ {
$em = $this->getDoctrine()->getManager(); $form = $this->createForm(ActivityReasonCategoryType::class, $entity, array(
'action' => $this->generateUrl('chill_activity_activityreasoncategory_update', array('id' => $entity->getId())),
'method' => 'PUT',
));
$entity = $em->getRepository('ChillActivityBundle:ActivityReasonCategory')->find($id); $form->add('submit', SubmitType::class, array('label' => 'Update'));
if (!$entity) { return $form;
throw $this->createNotFoundException('Unable to find ActivityReasonCategory entity.');
} }
return $this->render('ChillActivityBundle:ActivityReasonCategory:show.html.twig', [
'entity' => $entity,
]);
}
/** /**
* Edits an existing ActivityReasonCategory entity. * Edits an existing ActivityReasonCategory entity.
* *
* @param mixed $id
*/ */
public function updateAction(Request $request, $id) public function updateAction(Request $request, $id)
{ {
@@ -137,50 +167,12 @@ class ActivityReasonCategoryController extends AbstractController
if ($editForm->isValid()) { if ($editForm->isValid()) {
$em->flush(); $em->flush();
return $this->redirect($this->generateUrl('chill_activity_activityreasoncategory_edit', ['id' => $id])); return $this->redirect($this->generateUrl('chill_activity_activityreasoncategory_edit', array('id' => $id)));
} }
return $this->render('ChillActivityBundle:ActivityReasonCategory:edit.html.twig', [ return $this->render('ChillActivityBundle:ActivityReasonCategory:edit.html.twig', array(
'entity' => $entity, 'entity' => $entity,
'edit_form' => $editForm->createView(), 'edit_form' => $editForm->createView(),
]); ));
}
/**
* Creates a form to create a ActivityReasonCategory entity.
*
* @param ActivityReasonCategory $entity The entity
*
* @return \Symfony\Component\Form\Form The form
*/
private function createCreateForm(ActivityReasonCategory $entity)
{
$form = $this->createForm(ActivityReasonCategoryType::class, $entity, [
'action' => $this->generateUrl('chill_activity_activityreasoncategory_create'),
'method' => 'POST',
]);
$form->add('submit', SubmitType::class, ['label' => 'Create']);
return $form;
}
/**
* Creates a form to edit a ActivityReasonCategory entity.
*
* @param ActivityReasonCategory $entity The entity
*
* @return \Symfony\Component\Form\Form The form
*/
private function createEditForm(ActivityReasonCategory $entity)
{
$form = $this->createForm(ActivityReasonCategoryType::class, $entity, [
'action' => $this->generateUrl('chill_activity_activityreasoncategory_update', ['id' => $entity->getId()]),
'method' => 'PUT',
]);
$form->add('submit', SubmitType::class, ['label' => 'Update']);
return $form;
} }
} }

View File

@@ -1,29 +1,38 @@
<?php <?php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Chill\ActivityBundle\Controller; namespace Chill\ActivityBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Chill\ActivityBundle\Entity\ActivityReason; use Chill\ActivityBundle\Entity\ActivityReason;
use Chill\ActivityBundle\Form\ActivityReasonType; use Chill\ActivityBundle\Form\ActivityReasonType;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\HttpFoundation\Request;
/** /**
* ActivityReason controller. * ActivityReason controller.
*
*/ */
class ActivityReasonController extends AbstractController class ActivityReasonController extends AbstractController
{ {
/**
* Lists all ActivityReason entities.
*
*/
public function indexAction()
{
$em = $this->getDoctrine()->getManager();
$entities = $em->getRepository('ChillActivityBundle:ActivityReason')->findAll();
return $this->render('ChillActivityBundle:ActivityReason:index.html.twig', array(
'entities' => $entities,
));
}
/** /**
* Creates a new ActivityReason entity. * Creates a new ActivityReason entity.
*
*/ */
public function createAction(Request $request) public function createAction(Request $request)
{ {
@@ -36,19 +45,71 @@ class ActivityReasonController extends AbstractController
$em->persist($entity); $em->persist($entity);
$em->flush(); $em->flush();
return $this->redirect($this->generateUrl('chill_activity_activityreason', ['id' => $entity->getId()])); return $this->redirect($this->generateUrl('chill_activity_activityreason', array('id' => $entity->getId())));
} }
return $this->render('ChillActivityBundle:ActivityReason:new.html.twig', [ return $this->render('ChillActivityBundle:ActivityReason:new.html.twig', array(
'entity' => $entity, 'entity' => $entity,
'form' => $form->createView(), 'form' => $form->createView(),
]); ));
}
/**
* Creates a form to create a ActivityReason entity.
*
* @param ActivityReason $entity The entity
*
* @return \Symfony\Component\Form\Form The form
*/
private function createCreateForm(ActivityReason $entity)
{
$form = $this->createForm(ActivityReasonType::class, $entity, array(
'action' => $this->generateUrl('chill_activity_activityreason_create'),
'method' => 'POST',
));
$form->add('submit', SubmitType::class, array('label' => 'Create'));
return $form;
}
/**
* Displays a form to create a new ActivityReason entity.
*
*/
public function newAction()
{
$entity = new ActivityReason();
$form = $this->createCreateForm($entity);
return $this->render('ChillActivityBundle:ActivityReason:new.html.twig', array(
'entity' => $entity,
'form' => $form->createView(),
));
}
/**
* Finds and displays a ActivityReason entity.
*
*/
public function showAction($id)
{
$em = $this->getDoctrine()->getManager();
$entity = $em->getRepository('ChillActivityBundle:ActivityReason')->find($id);
if (!$entity) {
throw $this->createNotFoundException('Unable to find ActivityReason entity.');
}
return $this->render('ChillActivityBundle:ActivityReason:show.html.twig', array(
'entity' => $entity,
));
} }
/** /**
* Displays a form to edit an existing ActivityReason entity. * Displays a form to edit an existing ActivityReason entity.
* *
* @param mixed $id
*/ */
public function editAction($id) public function editAction($id)
{ {
@@ -62,64 +123,33 @@ class ActivityReasonController extends AbstractController
$editForm = $this->createEditForm($entity); $editForm = $this->createEditForm($entity);
return $this->render('ChillActivityBundle:ActivityReason:edit.html.twig', [ return $this->render('ChillActivityBundle:ActivityReason:edit.html.twig', array(
'entity' => $entity, 'entity' => $entity,
'edit_form' => $editForm->createView(), 'edit_form' => $editForm->createView()
]); ));
} }
/** /**
* Lists all ActivityReason entities. * Creates a form to edit a ActivityReason entity.
*/
public function indexAction()
{
$em = $this->getDoctrine()->getManager();
$entities = $em->getRepository('ChillActivityBundle:ActivityReason')->findAll();
return $this->render('ChillActivityBundle:ActivityReason:index.html.twig', [
'entities' => $entities,
]);
}
/**
* Displays a form to create a new ActivityReason entity.
*/
public function newAction()
{
$entity = new ActivityReason();
$form = $this->createCreateForm($entity);
return $this->render('ChillActivityBundle:ActivityReason:new.html.twig', [
'entity' => $entity,
'form' => $form->createView(),
]);
}
/**
* Finds and displays a ActivityReason entity.
* *
* @param mixed $id * @param ActivityReason $entity The entity
*
* @return \Symfony\Component\Form\Form The form
*/ */
public function showAction($id) private function createEditForm(ActivityReason $entity)
{ {
$em = $this->getDoctrine()->getManager(); $form = $this->createForm(ActivityReasonType::class, $entity, array(
'action' => $this->generateUrl('chill_activity_activityreason_update', array('id' => $entity->getId())),
'method' => 'PUT',
));
$entity = $em->getRepository('ChillActivityBundle:ActivityReason')->find($id); $form->add('submit', SubmitType::class, array('label' => 'Update'));
if (!$entity) { return $form;
throw $this->createNotFoundException('Unable to find ActivityReason entity.');
} }
return $this->render('ChillActivityBundle:ActivityReason:show.html.twig', [
'entity' => $entity,
]);
}
/** /**
* Edits an existing ActivityReason entity. * Edits an existing ActivityReason entity.
* *
* @param mixed $id
*/ */
public function updateAction(Request $request, $id) public function updateAction(Request $request, $id)
{ {
@@ -137,50 +167,12 @@ class ActivityReasonController extends AbstractController
if ($editForm->isValid()) { if ($editForm->isValid()) {
$em->flush(); $em->flush();
return $this->redirect($this->generateUrl('chill_activity_activityreason', ['id' => $id])); return $this->redirect($this->generateUrl('chill_activity_activityreason', array('id' => $id)));
} }
return $this->render('ChillActivityBundle:ActivityReason:edit.html.twig', [ return $this->render('ChillActivityBundle:ActivityReason:edit.html.twig', array(
'entity' => $entity, 'entity' => $entity,
'edit_form' => $editForm->createView(), 'edit_form' => $editForm->createView()
]); ));
}
/**
* Creates a form to create a ActivityReason entity.
*
* @param ActivityReason $entity The entity
*
* @return \Symfony\Component\Form\Form The form
*/
private function createCreateForm(ActivityReason $entity)
{
$form = $this->createForm(ActivityReasonType::class, $entity, [
'action' => $this->generateUrl('chill_activity_activityreason_create'),
'method' => 'POST',
]);
$form->add('submit', SubmitType::class, ['label' => 'Create']);
return $form;
}
/**
* Creates a form to edit a ActivityReason entity.
*
* @param ActivityReason $entity The entity
*
* @return \Symfony\Component\Form\Form The form
*/
private function createEditForm(ActivityReason $entity)
{
$form = $this->createForm(ActivityReasonType::class, $entity, [
'action' => $this->generateUrl('chill_activity_activityreason_update', ['id' => $entity->getId()]),
'method' => 'PUT',
]);
$form->add('submit', SubmitType::class, ['label' => 'Update']);
return $form;
} }
} }

View File

@@ -1,14 +1,5 @@
<?php <?php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Chill\ActivityBundle\Controller; namespace Chill\ActivityBundle\Controller;
use Chill\MainBundle\CRUD\Controller\CRUDController; use Chill\MainBundle\CRUD\Controller\CRUDController;
@@ -18,8 +9,10 @@ use Symfony\Component\HttpFoundation\Request;
class AdminActivityPresenceController extends CRUDController class AdminActivityPresenceController extends CRUDController
{ {
/** /**
* @param string $action
* @param \Doctrine\ORM\QueryBuilder|mixed $query * @param \Doctrine\ORM\QueryBuilder|mixed $query
* * @param Request $request
* @param PaginatorInterface $paginator
* @return \Doctrine\ORM\QueryBuilder|mixed * @return \Doctrine\ORM\QueryBuilder|mixed
*/ */
protected function orderQuery(string $action, $query, Request $request, PaginatorInterface $paginator) protected function orderQuery(string $action, $query, Request $request, PaginatorInterface $paginator)

View File

@@ -1,14 +1,5 @@
<?php <?php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Chill\ActivityBundle\Controller; namespace Chill\ActivityBundle\Controller;
use Chill\MainBundle\CRUD\Controller\CRUDController; use Chill\MainBundle\CRUD\Controller\CRUDController;
@@ -18,8 +9,10 @@ use Symfony\Component\HttpFoundation\Request;
class AdminActivityTypeCategoryController extends CRUDController class AdminActivityTypeCategoryController extends CRUDController
{ {
/** /**
* @param string $action
* @param \Doctrine\ORM\QueryBuilder|mixed $query * @param \Doctrine\ORM\QueryBuilder|mixed $query
* * @param Request $request
* @param PaginatorInterface $paginator
* @return \Doctrine\ORM\QueryBuilder|mixed * @return \Doctrine\ORM\QueryBuilder|mixed
*/ */
protected function orderQuery(string $action, $query, Request $request, PaginatorInterface $paginator) protected function orderQuery(string $action, $query, Request $request, PaginatorInterface $paginator)

View File

@@ -1,14 +1,5 @@
<?php <?php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Chill\ActivityBundle\Controller; namespace Chill\ActivityBundle\Controller;
use Chill\MainBundle\CRUD\Controller\CRUDController; use Chill\MainBundle\CRUD\Controller\CRUDController;
@@ -18,14 +9,15 @@ use Symfony\Component\HttpFoundation\Request;
class AdminActivityTypeController extends CRUDController class AdminActivityTypeController extends CRUDController
{ {
/** /**
* @param string $action
* @param \Doctrine\ORM\QueryBuilder|mixed $query * @param \Doctrine\ORM\QueryBuilder|mixed $query
* * @param Request $request
* @param PaginatorInterface $paginator
* @return \Doctrine\ORM\QueryBuilder|mixed * @return \Doctrine\ORM\QueryBuilder|mixed
*/ */
protected function orderQuery(string $action, $query, Request $request, PaginatorInterface $paginator) protected function orderQuery(string $action, $query, Request $request, PaginatorInterface $paginator)
{ {
/** @var \Doctrine\ORM\QueryBuilder $query */ /** @var \Doctrine\ORM\QueryBuilder $query */
return $query->orderBy('e.ordering', 'ASC') return $query->orderBy('e.ordering', 'ASC');
->addOrderBy('e.id', 'ASC');
} }
} }

View File

@@ -1,20 +1,33 @@
<?php <?php
/** /*
* Chill is a software for social workers * Chill is a software for social workers
* Copyright (C) 2015 Champs Libres <info@champs-libres.coop>
* *
* For the full copyright and license information, please view * This program is free software: you can redistribute it and/or modify
* the LICENSE file that was distributed with this source code. * it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
declare(strict_types=1);
namespace Chill\ActivityBundle\Controller; namespace Chill\ActivityBundle\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
/** /**
* Controller for activity configuration. * Controller for activity configuration
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
* @author Champs Libres <info@champs-libres.coop>
*/ */
class AdminController extends AbstractController class AdminController extends AbstractController
{ {

View File

@@ -1,36 +1,48 @@
<?php <?php
/** /*
* Chill is a software for social workers * Chill is a software for social workers
* *
* For the full copyright and license information, please view * Copyright (C) 2014-2015, Champs Libres Cooperative SCRLFS,
* the LICENSE file that was distributed with this source code. * <http://www.champs-libres.coop>, <info@champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
declare(strict_types=1);
namespace Chill\ActivityBundle\DataFixtures\ORM; namespace Chill\ActivityBundle\DataFixtures\ORM;
use Chill\ActivityBundle\Entity\Activity;
use Chill\MainBundle\DataFixtures\ORM\LoadScopes;
use Chill\MainBundle\DataFixtures\ORM\LoadUsers;
use Chill\PersonBundle\Entity\Person; use Chill\PersonBundle\Entity\Person;
use Doctrine\Common\DataFixtures\AbstractFixture; use Doctrine\Common\DataFixtures\AbstractFixture;
use Doctrine\Common\DataFixtures\OrderedFixtureInterface; use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityManagerInterface;
use Doctrine\Persistence\ObjectManager; use Doctrine\Persistence\ObjectManager;
use Faker\Factory as FakerFactory; use Faker\Factory as FakerFactory;
use Chill\ActivityBundle\Entity\Activity;
use Chill\MainBundle\DataFixtures\ORM\LoadUsers;
use Chill\ActivityBundle\DataFixtures\ORM\LoadActivityReason;
use Chill\ActivityBundle\DataFixtures\ORM\LoadActivityType;
use Chill\MainBundle\DataFixtures\ORM\LoadScopes;
class LoadActivity extends AbstractFixture implements OrderedFixtureInterface class LoadActivity extends AbstractFixture implements OrderedFixtureInterface
{ {
use \Symfony\Component\DependencyInjection\ContainerAwareTrait; use \Symfony\Component\DependencyInjection\ContainerAwareTrait;
private EntityManagerInterface $em;
/** /**
* @var \Faker\Generator * @var \Faker\Generator
*/ */
private $faker; private $faker;
private EntityManagerInterface $em;
public function __construct(EntityManagerInterface $em) public function __construct(EntityManagerInterface $em)
{ {
@@ -43,53 +55,30 @@ class LoadActivity extends AbstractFixture implements OrderedFixtureInterface
return 16400; return 16400;
} }
public function load(ObjectManager $manager) /**
* Return a random scope
*
* @return \Chill\MainBundle\Entity\Scope
*/
private function getRandomScope()
{ {
$persons = $this->em $scopeRef = LoadScopes::$references[array_rand(LoadScopes::$references)];
->getRepository(Person::class) return $this->getReference($scopeRef);
->findAll();
foreach ($persons as $person) {
$activityNbr = mt_rand(0, 3);
for ($i = 0; $i < $activityNbr; ++$i) {
$activity = $this->newRandomActivity($person);
if (null !== $activity) {
$manager->persist($activity);
}
}
}
$manager->flush();
}
public function newRandomActivity($person): ?Activity
{
$activity = (new Activity())
->setUser($this->getRandomUser())
->setPerson($person)
->setDate($this->faker->dateTimeThisYear())
->setDurationTime($this->faker->dateTime(36000))
->setType($this->getRandomActivityType())
->setScope($this->getRandomScope());
// ->setAttendee($this->faker->boolean())
for ($i = 0; mt_rand(0, 4) > $i; ++$i) {
$reason = $this->getRandomActivityReason();
if (null !== $reason) {
$activity->addReason($reason);
} else {
return null;
}
}
return $activity;
} }
/** /**
* Return a random activityReason. * Return a random activityType
*
* @return \Chill\ActivityBundle\Entity\ActivityType
*/
private function getRandomActivityType()
{
$typeRef = LoadActivityType::$references[array_rand(LoadActivityType::$references)];
return $this->getReference($typeRef);
}
/**
* Return a random activityReason
* *
* @return \Chill\ActivityBundle\Entity\ActivityReason * @return \Chill\ActivityBundle\Entity\ActivityReason
*/ */
@@ -101,38 +90,57 @@ class LoadActivity extends AbstractFixture implements OrderedFixtureInterface
} }
/** /**
* Return a random activityType. * Return a random user
*
* @return \Chill\ActivityBundle\Entity\ActivityType
*/
private function getRandomActivityType()
{
$typeRef = LoadActivityType::$references[array_rand(LoadActivityType::$references)];
return $this->getReference($typeRef);
}
/**
* Return a random scope.
*
* @return \Chill\MainBundle\Entity\Scope
*/
private function getRandomScope()
{
$scopeRef = LoadScopes::$references[array_rand(LoadScopes::$references)];
return $this->getReference($scopeRef);
}
/**
* Return a random user.
* *
* @return \Chill\MainBundle\Entity\User * @return \Chill\MainBundle\Entity\User
*/ */
private function getRandomUser() private function getRandomUser()
{ {
$userRef = array_rand(LoadUsers::$refs); $userRef = array_rand(LoadUsers::$refs);
return $this->getReference($userRef); return $this->getReference($userRef);
} }
public function newRandomActivity($person): ?Activity
{
$activity = (new Activity())
->setUser($this->getRandomUser())
->setPerson($person)
->setDate($this->faker->dateTimeThisYear())
->setDurationTime($this->faker->dateTime(36000))
->setType($this->getRandomActivityType())
->setScope($this->getRandomScope())
;
// ->setAttendee($this->faker->boolean())
for ($i = 0; $i < rand(0, 4); $i++) {
$reason = $this->getRandomActivityReason();
if (null !== $reason) {
$activity->addReason($reason);
} else {
return null;
}
}
return $activity;
}
public function load(ObjectManager $manager)
{
$persons = $this->em
->getRepository(Person::class)
->findAll();
foreach ($persons as $person) {
$activityNbr = rand(0,3);
for ($i = 0; $i < $activityNbr; $i ++) {
$activity = $this->newRandomActivity($person);
if (null !== $activity) {
$manager->persist($activity);
}
}
}
$manager->flush();
}
} }

View File

@@ -1,23 +1,15 @@
<?php <?php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Chill\ActivityBundle\DataFixtures\ORM; namespace Chill\ActivityBundle\DataFixtures\ORM;
use Chill\ActivityBundle\Entity\Activity;
use Chill\MainBundle\DataFixtures\ORM\LoadAbstractNotificationsTrait;
use Doctrine\Common\DataFixtures\AbstractFixture; use Doctrine\Common\DataFixtures\AbstractFixture;
use Doctrine\Common\DataFixtures\DependentFixtureInterface; use Doctrine\Common\DataFixtures\DependentFixtureInterface;
use Chill\ActivityBundle\Entity\Activity;
use Chill\MainBundle\DataFixtures\ORM\LoadAbstractNotificationsTrait;
use Chill\ActivityBundle\DataFixtures\ORM\LoadActivity;
/** /**
* Load notififications into database. * Load notififications into database
*/ */
class LoadActivityNotifications extends AbstractFixture implements DependentFixtureInterface class LoadActivityNotifications extends AbstractFixture implements DependentFixtureInterface
{ {
@@ -33,9 +25,9 @@ class LoadActivityNotifications extends AbstractFixture implements DependentFixt
'center a_social', 'center a_social',
'center a_administrative', 'center a_administrative',
'center a_direction', 'center a_direction',
'multi_center', 'multi_center'
],
], ],
]
]; ];
public function getDependencies() public function getDependencies()

View File

@@ -1,58 +1,71 @@
<?php <?php
/** /*
* Chill is a software for social workers * Chill is a software for social workers
* *
* For the full copyright and license information, please view * Copyright (C) 2014-2015, Champs Libres Cooperative SCRLFS,
* the LICENSE file that was distributed with this source code. * <http://www.champs-libres.coop>, <info@champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
declare(strict_types=1);
namespace Chill\ActivityBundle\DataFixtures\ORM; namespace Chill\ActivityBundle\DataFixtures\ORM;
use Chill\ActivityBundle\Entity\ActivityReason;
use Doctrine\Common\DataFixtures\AbstractFixture; use Doctrine\Common\DataFixtures\AbstractFixture;
use Doctrine\Common\DataFixtures\OrderedFixtureInterface; use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
use Doctrine\Persistence\ObjectManager; use Doctrine\Persistence\ObjectManager;
use Chill\ActivityBundle\Entity\ActivityReason;
/** /**
* Description of LoadActivityReason. * Description of LoadActivityReason
*
* @author Champs-Libres Coop
*/ */
class LoadActivityReason extends AbstractFixture implements OrderedFixtureInterface class LoadActivityReason extends AbstractFixture implements OrderedFixtureInterface
{ {
public static $references = [];
public function getOrder() public function getOrder()
{ {
return 16300; return 16300;
} }
public static $references = array();
public function load(ObjectManager $manager) public function load(ObjectManager $manager)
{ {
$reasons = [ $reasons = [
[ [
'name' => ['fr' => 'Recherche logement', 'en' => 'Housing research', 'nl' => 'Woning zoektoch'], 'name' => ['fr' => 'Recherche logement', 'en' => 'Housing research', 'nl' => 'Woning zoektoch'],
'category' => 'cat_Housing', ], 'category' => 'cat_Housing'],
[ [
'name' => ['fr' => 'Problème avec propriétaire', 'en' => 'Landlord problems', 'nl' => 'Huisbaas problemen'], 'name' => ['fr' => 'Problème avec propriétaire', 'en' => 'Landlord problems', 'nl' => 'Huisbaas problemen'],
'category' => 'cat_Housing', ], 'category' => 'cat_Housing'],
[ [
'name' => ['fr' => 'Retard de payement', 'en' => 'Payement problems', 'nl' => 'Betalings vertragingen'], 'name' => ['fr' => 'Retard de payement', 'en' => 'Payement problems', 'nl' => 'Betalings vertragingen'],
'category' => 'cat_Housing', ], 'category' => 'cat_Housing'],
[ [
'name' => ['fr' => 'Explication législation', 'en' => 'Legislation explanation', 'nl' => 'Legislative uitleg'], 'name' => ['fr' => 'Explication législation', 'en' => 'Legislation explanation', 'nl' => 'Legislative uitleg'],
'category' => 'cat_Unemployment procedure', ], 'category' => 'cat_Unemployment procedure'],
[ [
'name' => ['fr' => 'Coaching entretien d\'activation', 'en' => 'Interview coaching', 'nl' => 'Interview coaching'], 'name' => ['fr' => 'Coaching entretien d\'activation', 'en' => 'Interview coaching', 'nl' => 'Interview coaching'],
'category' => 'cat_Unemployment procedure', ], 'category' => 'cat_Unemployment procedure'],
[ [
'name' => ['fr' => 'Récupération des allocations', 'en' => 'Allowance recovery', 'nl' => 'Terugwinning van de uitkeringen'], 'name' => ['fr' => 'Récupération des allocations', 'en' => 'Allowance recovery', 'nl' => 'Terugwinning van de uitkeringen'],
'category' => 'cat_Unemployment procedure', ], 'category' => 'cat_Unemployment procedure']
]; ];
foreach ($reasons as $r) { foreach ($reasons as $r) {
echo 'Creating activity reason : ' . $r['name']['en'] . "\n"; print "Creating activity reason : " . $r['name']['en'] . "\n";
$activityReason = (new ActivityReason()) $activityReason = (new ActivityReason())
->setName(($r['name'])) ->setName(($r['name']))
->setActive(true) ->setActive(true)

View File

@@ -1,23 +1,35 @@
<?php <?php
/** /*
* Chill is a software for social workers * Chill is a software for social workers
* *
* For the full copyright and license information, please view * Copyright (C) 2014-2015, Champs Libres Cooperative SCRLFS,
* the LICENSE file that was distributed with this source code. * <http://www.champs-libres.coop>, <info@champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
declare(strict_types=1);
namespace Chill\ActivityBundle\DataFixtures\ORM; namespace Chill\ActivityBundle\DataFixtures\ORM;
use Chill\ActivityBundle\Entity\ActivityReasonCategory;
use Doctrine\Common\DataFixtures\AbstractFixture; use Doctrine\Common\DataFixtures\AbstractFixture;
use Doctrine\Common\DataFixtures\OrderedFixtureInterface; use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
use Doctrine\Persistence\ObjectManager; use Doctrine\Persistence\ObjectManager;
use Chill\ActivityBundle\Entity\ActivityReasonCategory;
/** /**
* Description of LoadActivityReasonCategory. * Description of LoadActivityReasonCategory
*
* @author Champs-Libres Coop
*/ */
class LoadActivityReasonCategory extends AbstractFixture implements OrderedFixtureInterface class LoadActivityReasonCategory extends AbstractFixture implements OrderedFixtureInterface
{ {
@@ -29,20 +41,21 @@ class LoadActivityReasonCategory extends AbstractFixture implements OrderedFixtu
public function load(ObjectManager $manager) public function load(ObjectManager $manager)
{ {
$categs = [ $categs = [
['name' => ['fr' => 'Logement', 'en' => 'Housing', 'nl' => 'Woning']], ['name' =>
['name' => ['fr' => 'Démarches chômage', 'en' => 'Unemployment procedure', 'nl' => 'Werkloosheid werkwijze']], ['fr' => 'Logement', 'en' => 'Housing', 'nl' => 'Woning']],
['name' =>
['fr' => 'Démarches chômage', 'en' => 'Unemployment procedure', 'nl' => 'Werkloosheid werkwijze']],
]; ];
foreach ($categs as $c) { foreach ($categs as $c) {
echo 'Creating activity reason category : ' . $c['name']['en'] . "\n"; print "Creating activity reason category : " . $c['name']['en'] . "\n";
$activityReasonCategory = (new ActivityReasonCategory()) $activityReasonCategory = (new ActivityReasonCategory())
->setName(($c['name'])) ->setName(($c['name']))
->setActive(true); ->setActive(true);
$manager->persist($activityReasonCategory); $manager->persist($activityReasonCategory);
$this->addReference( $this->addReference(
'cat_'.$c['name']['en'], 'cat_'.$c['name']['en'],
$activityReasonCategory $activityReasonCategory);
);
} }
$manager->flush(); $manager->flush();

View File

@@ -1,60 +1,79 @@
<?php <?php
/** /*
* Chill is a software for social workers * Chill is a software for social workers
* *
* For the full copyright and license information, please view * Copyright (C) 2014-2015, Champs Libres Cooperative SCRLFS,
* the LICENSE file that was distributed with this source code. * <http://www.champs-libres.coop>, <info@champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
declare(strict_types=1);
namespace Chill\ActivityBundle\DataFixtures\ORM; namespace Chill\ActivityBundle\DataFixtures\ORM;
use Chill\ActivityBundle\Entity\ActivityType;
use Doctrine\Bundle\FixturesBundle\Fixture; use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Common\DataFixtures\OrderedFixtureInterface; use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
use Doctrine\Persistence\ObjectManager; use Doctrine\Persistence\ObjectManager;
use Chill\ActivityBundle\Entity\ActivityType;
/** /**
* Description of LoadActivityType. * Description of LoadActivityType
*
* @author Champs-Libres Coop
*/ */
class LoadActivityType extends Fixture implements OrderedFixtureInterface class LoadActivityType extends Fixture implements OrderedFixtureInterface
{ {
public static $references = [];
public function getOrder() public function getOrder()
{ {
return 16100; return 16100;
} }
public static $references = array();
public function load(ObjectManager $manager) public function load(ObjectManager $manager)
{ {
$types = [ $types = [
// Exange # Exange
[ [
'name' => ['fr' => 'Entretien physique avec l\'usager'], 'name' =>
'category' => 'exchange', ], ['fr' => 'Entretien physique avec l\'usager'],
'category' => 'exchange' ],
[ [
'name' => ['fr' => 'Appel téléphonique', 'en' => 'Telephone call', 'nl' => 'Telefoon appel'], 'name' =>
'category' => 'exchange', ], ['fr' => 'Appel téléphonique', 'en' => 'Telephone call', 'nl' => 'Telefoon appel'],
'category' => 'exchange' ],
[ [
'name' => ['fr' => 'Courriel', 'en' => 'Email', 'nl' => 'Email'], 'name' =>
'category' => 'exchange', ], ['fr' => 'Courriel', 'en' => 'Email', 'nl' => 'Email'],
// Meeting 'category' => 'exchange' ],
# Meeting
[ [
'name' => ['fr' => 'Point technique encadrant'], 'name' =>
'category' => 'meeting', ], ['fr' => 'Point technique encadrant'],
'category' => 'meeting' ],
[ [
'name' => ['fr' => 'Réunion avec des partenaires'], 'name' =>
'category' => 'meeting', ], ['fr' => 'Réunion avec des partenaires'],
'category' => 'meeting' ],
[ [
'name' => ['fr' => 'Commission pluridisciplinaire et pluri-institutionnelle'], 'name' =>
'category' => 'meeting', ], ['fr' => 'Commission pluridisciplinaire et pluri-institutionnelle'],
'category' => 'meeting' ],
]; ];
foreach ($types as $t) { foreach ($types as $t) {
echo 'Creating activity type : ' . $t['name']['fr'] . ' (cat:' . $t['category'] . " \n"; print "Creating activity type : " . $t['name']['fr'] . " (cat:". $t['category'] . " \n";
$activityType = (new ActivityType()) $activityType = (new ActivityType())
->setName(($t['name'])) ->setName(($t['name']))
->setCategory($this->getReference('activity_type_cat_'.$t['category'])) ->setCategory($this->getReference('activity_type_cat_'.$t['category']))

View File

@@ -1,27 +1,40 @@
<?php <?php
/** /*
* Chill is a software for social workers * Chill is a software for social workers
* *
* For the full copyright and license information, please view * Copyright (C) 2014-2021, Champs Libres Cooperative SCRLFS,
* the LICENSE file that was distributed with this source code. * <http://www.champs-libres.coop>, <info@champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
declare(strict_types=1);
namespace Chill\ActivityBundle\DataFixtures\ORM; namespace Chill\ActivityBundle\DataFixtures\ORM;
use Chill\ActivityBundle\Entity\ActivityTypeCategory;
use Doctrine\Bundle\FixturesBundle\Fixture; use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Common\DataFixtures\OrderedFixtureInterface; use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
use Doctrine\Persistence\ObjectManager; use Doctrine\Persistence\ObjectManager;
use Chill\ActivityBundle\Entity\ActivityTypeCategory;
/** /**
* Fixtures for ActivityTypeCategory. * Fixtures for ActivityTypeCategory
*
* @author Champs-Libres Coop
*/ */
class LoadActivityTypeCategory extends Fixture implements OrderedFixtureInterface class LoadActivityTypeCategory extends Fixture implements OrderedFixtureInterface
{ {
public static $references = []; public static $references = array();
public function getOrder() public function getOrder()
{ {
@@ -42,7 +55,7 @@ class LoadActivityTypeCategory extends Fixture implements OrderedFixtureInterfac
]; ];
foreach ($categories as $cat) { foreach ($categories as $cat) {
echo 'Creating activity type category : ' . $cat['ref'] . "\n"; print "Creating activity type category : " . $cat['ref'] . "\n";
$newCat = (new ActivityTypeCategory()) $newCat = (new ActivityTypeCategory())
->setName(($cat['name'])); ->setName(($cat['name']));

View File

@@ -1,30 +1,37 @@
<?php <?php
/** /*
* Chill is a software for social workers * Copyright (C) 2015 Julien Fastré <julien.fastre@champs-libres.coop>
* *
* For the full copyright and license information, please view * This program is free software: you can redistribute it and/or modify
* the LICENSE file that was distributed with this source code. * it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
declare(strict_types=1);
namespace Chill\ActivityBundle\DataFixtures\ORM; namespace Chill\ActivityBundle\DataFixtures\ORM;
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
use Chill\ActivityBundle\Security\Authorization\ActivityVoter;
use Chill\MainBundle\DataFixtures\ORM\LoadPermissionsGroup;
use Chill\MainBundle\DataFixtures\ORM\LoadScopes;
use Chill\MainBundle\Entity\RoleScope;
use Doctrine\Common\DataFixtures\AbstractFixture; use Doctrine\Common\DataFixtures\AbstractFixture;
use Doctrine\Common\DataFixtures\OrderedFixtureInterface; use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
use Doctrine\Persistence\ObjectManager; use Doctrine\Persistence\ObjectManager;
use Chill\MainBundle\DataFixtures\ORM\LoadPermissionsGroup;
use function in_array; use Chill\MainBundle\Entity\RoleScope;
use Chill\MainBundle\DataFixtures\ORM\LoadScopes;
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
/** /**
* Add a role CHILL_ACTIVITY_UPDATE & CHILL_ACTIVITY_CREATE for all groups except administrative, * Add a role CHILL_ACTIVITY_UPDATE & CHILL_ACTIVITY_CREATE for all groups except administrative,
* and a role CHILL_ACTIVITY_SEE for administrative. * and a role CHILL_ACTIVITY_SEE for administrative
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/ */
class LoadActivitytACL extends AbstractFixture implements OrderedFixtureInterface class LoadActivitytACL extends AbstractFixture implements OrderedFixtureInterface
{ {
@@ -33,11 +40,11 @@ class LoadActivitytACL extends AbstractFixture implements OrderedFixtureInterfac
return 16000; return 16000;
} }
public function load(ObjectManager $manager) public function load(ObjectManager $manager)
{ {
foreach (LoadPermissionsGroup::$refs as $permissionsGroupRef) { foreach (LoadPermissionsGroup::$refs as $permissionsGroupRef) {
$permissionsGroup = $this->getReference($permissionsGroupRef); $permissionsGroup = $this->getReference($permissionsGroupRef);
foreach (LoadScopes::$references as $scopeRef){ foreach (LoadScopes::$references as $scopeRef){
$scope = $this->getReference($scopeRef); $scope = $this->getReference($scopeRef);
//create permission group //create permission group
@@ -46,33 +53,24 @@ class LoadActivitytACL extends AbstractFixture implements OrderedFixtureInterfac
if ($scope->getName()['en'] === 'administrative') { if ($scope->getName()['en'] === 'administrative') {
break 2; // we do not want any power on administrative break 2; // we do not want any power on administrative
} }
break; break;
case 'administrative': case 'administrative':
case 'direction': case 'direction':
if (in_array($scope->getName()['en'], ['administrative', 'social'], true)) { if (in_array($scope->getName()['en'], array('administrative', 'social'))) {
break 2; // we do not want any power on social or administrative break 2; // we do not want any power on social or administrative
} }
break; break;
} }
printf( printf("Adding CHILL_ACTIVITY_UPDATE & CHILL_ACTIVITY_CREATE & CHILL_ACTIVITY_DELETE, and stats and list permissions to %s "
'Adding CHILL_ACTIVITY_UPDATE & CHILL_ACTIVITY_CREATE & CHILL_ACTIVITY_DELETE, and stats and list permissions to %s '
. "permission group, scope '%s' \n", . "permission group, scope '%s' \n",
$permissionsGroup->getName(), $permissionsGroup->getName(), $scope->getName()['en']);
$scope->getName()['en']
);
$roleScopeUpdate = (new RoleScope()) $roleScopeUpdate = (new RoleScope())
->setRole('CHILL_ACTIVITY_UPDATE') ->setRole('CHILL_ACTIVITY_UPDATE')
->setScope($scope); ->setScope($scope);
$permissionsGroup->addRoleScope($roleScopeUpdate); $permissionsGroup->addRoleScope($roleScopeUpdate);
$roleScopeCreate = (new RoleScope()) $roleScopeCreate = (new RoleScope())
->setRole(ActivityVoter::CREATE_ACCOMPANYING_COURSE) ->setRole('CHILL_ACTIVITY_CREATE')
->setScope($scope);
$roleScopeCreate = (new RoleScope())
->setRole(ActivityVoter::CREATE_PERSON)
->setScope($scope); ->setScope($scope);
$permissionsGroup->addRoleScope($roleScopeCreate); $permissionsGroup->addRoleScope($roleScopeCreate);
$roleScopeDelete = (new RoleScope()) $roleScopeDelete = (new RoleScope())
@@ -80,18 +78,22 @@ class LoadActivitytACL extends AbstractFixture implements OrderedFixtureInterfac
->setScope($scope); ->setScope($scope);
$permissionsGroup->addRoleScope($roleScopeDelete); $permissionsGroup->addRoleScope($roleScopeDelete);
$roleScopeList = (new RoleScope()) $roleScopeList = (new RoleScope())
->setRole(ActivityStatsVoter::LISTS); ->setRole(ActivityStatsVoter::LISTS)
;
$permissionsGroup->addRoleScope($roleScopeList); $permissionsGroup->addRoleScope($roleScopeList);
$roleScopeStat = (new RoleScope()) $roleScopeStat = (new RoleScope())
->setRole(ActivityStatsVoter::STATS); ->setRole(ActivityStatsVoter::STATS)
;
$permissionsGroup->addRoleScope($roleScopeStat); $permissionsGroup->addRoleScope($roleScopeStat);
$manager->persist($roleScopeUpdate); $manager->persist($roleScopeUpdate);
$manager->persist($roleScopeCreate); $manager->persist($roleScopeCreate);
$manager->persist($roleScopeDelete); $manager->persist($roleScopeDelete);
} }
} }
$manager->flush(); $manager->flush();
} }
} }

View File

@@ -1,30 +1,45 @@
<?php <?php
/** /*
* Chill is a software for social workers * Chill is a software for social workers
* *
* For the full copyright and license information, please view * Copyright (C) 2014-2015, Champs Libres Cooperative SCRLFS,
* the LICENSE file that was distributed with this source code. * <http://www.champs-libres.coop>, <info@champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
declare(strict_types=1);
namespace Chill\ActivityBundle\DependencyInjection; namespace Chill\ActivityBundle\DependencyInjection;
use Chill\ActivityBundle\Security\Authorization\ActivityVoter;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface; use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\Loader;
use Symfony\Component\HttpKernel\DependencyInjection\Extension; use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\Loader;
use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
use Chill\ActivityBundle\Security\Authorization\ActivityVoter;
/** /**
* This is the class that loads and manages your bundle configuration. * This is the class that loads and manages your bundle configuration
* *
* To learn more see {@link http://symfony.com/doc/current/cookbook/bundles/extension.html} * To learn more see {@link http://symfony.com/doc/current/cookbook/bundles/extension.html}
*/ */
class ChillActivityExtension extends Extension implements PrependExtensionInterface class ChillActivityExtension extends Extension implements PrependExtensionInterface
{ {
/**
* {@inheritdoc}
*/
public function load(array $configs, ContainerBuilder $container) public function load(array $configs, ContainerBuilder $container)
{ {
$configuration = new Configuration(); $configuration = new Configuration();
@@ -41,7 +56,6 @@ class ChillActivityExtension extends Extension implements PrependExtensionInterf
$loader->load('services/form.yaml'); $loader->load('services/form.yaml');
$loader->load('services/templating.yaml'); $loader->load('services/templating.yaml');
$loader->load('services/accompanyingPeriodConsistency.yaml'); $loader->load('services/accompanyingPeriodConsistency.yaml');
$loader->load('services/doctrine.entitylistener.yaml');
} }
public function prepend(ContainerBuilder $container) public function prepend(ContainerBuilder $container)
@@ -51,38 +65,33 @@ class ChillActivityExtension extends Extension implements PrependExtensionInterf
$this->prependCruds($container); $this->prependCruds($container);
} }
public function prependAuthorization(ContainerBuilder $container) /* (non-PHPdoc)
{
$container->prependExtensionConfig('security', [
'role_hierarchy' => [
ActivityVoter::UPDATE => [ActivityVoter::SEE_DETAILS],
ActivityVoter::CREATE_PERSON => [ActivityVoter::SEE_DETAILS],
ActivityVoter::CREATE_ACCOMPANYING_COURSE => [ActivityVoter::SEE_DETAILS],
ActivityVoter::DELETE => [ActivityVoter::SEE_DETAILS],
ActivityVoter::SEE_DETAILS => [ActivityVoter::SEE],
ActivityVoter::FULL => [
ActivityVoter::CREATE_PERSON,
ActivityVoter::CREATE_ACCOMPANYING_COURSE,
ActivityVoter::DELETE,
ActivityVoter::UPDATE,
],
],
]);
}
/** (non-PHPdoc).
* @see \Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface::prepend() * @see \Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface::prepend()
*/ */
public function prependRoutes(ContainerBuilder $container) public function prependRoutes(ContainerBuilder $container)
{ {
//add routes for custom bundle //add routes for custom bundle
$container->prependExtensionConfig('chill_main', [ $container->prependExtensionConfig('chill_main', array(
'routing' => [ 'routing' => array(
'resources' => [ 'resources' => array(
'@ChillActivityBundle/config/routes.yaml', '@ChillActivityBundle/config/routes.yaml'
], )
], )
]); ));
}
public function prependAuthorization(ContainerBuilder $container)
{
$container->prependExtensionConfig('security', array(
'role_hierarchy' => array(
ActivityVoter::UPDATE => array(ActivityVoter::SEE_DETAILS),
ActivityVoter::CREATE => array(ActivityVoter::SEE_DETAILS),
ActivityVoter::DELETE => array(ActivityVoter::SEE_DETAILS),
ActivityVoter::SEE_DETAILS => array(ActivityVoter::SEE),
ActivityVoter::FULL => [ActivityVoter::CREATE, ActivityVoter::DELETE,
ActivityVoter::UPDATE],
)
));
} }
protected function prependCruds(ContainerBuilder $container) protected function prependCruds(ContainerBuilder $container)
@@ -98,7 +107,7 @@ class ChillActivityExtension extends Extension implements PrependExtensionInterf
'actions' => [ 'actions' => [
'index' => [ 'index' => [
'template' => '@ChillActivity/ActivityType/index.html.twig', 'template' => '@ChillActivity/ActivityType/index.html.twig',
'role' => 'ROLE_ADMIN', 'role' => 'ROLE_ADMIN'
], ],
'new' => [ 'new' => [
'role' => 'ROLE_ADMIN', 'role' => 'ROLE_ADMIN',
@@ -107,8 +116,8 @@ class ChillActivityExtension extends Extension implements PrependExtensionInterf
'edit' => [ 'edit' => [
'role' => 'ROLE_ADMIN', 'role' => 'ROLE_ADMIN',
'template' => '@ChillActivity/ActivityType/edit.html.twig', 'template' => '@ChillActivity/ActivityType/edit.html.twig',
], ]
], ]
], ],
[ [
'class' => \Chill\ActivityBundle\Entity\ActivityTypeCategory::class, 'class' => \Chill\ActivityBundle\Entity\ActivityTypeCategory::class,
@@ -119,7 +128,7 @@ class ChillActivityExtension extends Extension implements PrependExtensionInterf
'actions' => [ 'actions' => [
'index' => [ 'index' => [
'template' => '@ChillActivity/ActivityTypeCategory/index.html.twig', 'template' => '@ChillActivity/ActivityTypeCategory/index.html.twig',
'role' => 'ROLE_ADMIN', 'role' => 'ROLE_ADMIN'
], ],
'new' => [ 'new' => [
'role' => 'ROLE_ADMIN', 'role' => 'ROLE_ADMIN',
@@ -128,8 +137,8 @@ class ChillActivityExtension extends Extension implements PrependExtensionInterf
'edit' => [ 'edit' => [
'role' => 'ROLE_ADMIN', 'role' => 'ROLE_ADMIN',
'template' => '@ChillActivity/ActivityTypeCategory/edit.html.twig', 'template' => '@ChillActivity/ActivityTypeCategory/edit.html.twig',
], ]
], ]
], ],
[ [
'class' => \Chill\ActivityBundle\Entity\ActivityPresence::class, 'class' => \Chill\ActivityBundle\Entity\ActivityPresence::class,
@@ -140,7 +149,7 @@ class ChillActivityExtension extends Extension implements PrependExtensionInterf
'actions' => [ 'actions' => [
'index' => [ 'index' => [
'template' => '@ChillActivity/ActivityPresence/index.html.twig', 'template' => '@ChillActivity/ActivityPresence/index.html.twig',
'role' => 'ROLE_ADMIN', 'role' => 'ROLE_ADMIN'
], ],
'new' => [ 'new' => [
'role' => 'ROLE_ADMIN', 'role' => 'ROLE_ADMIN',
@@ -149,10 +158,10 @@ class ChillActivityExtension extends Extension implements PrependExtensionInterf
'edit' => [ 'edit' => [
'role' => 'ROLE_ADMIN', 'role' => 'ROLE_ADMIN',
'template' => '@ChillActivity/ActivityPresence/edit.html.twig', 'template' => '@ChillActivity/ActivityPresence/edit.html.twig',
]
]
], ],
], ]
],
],
]); ]);
} }
} }

View File

@@ -1,28 +1,20 @@
<?php <?php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Chill\ActivityBundle\DependencyInjection; namespace Chill\ActivityBundle\DependencyInjection;
use Symfony\Component\Config\Definition\Builder\TreeBuilder; use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface; use Symfony\Component\Config\Definition\ConfigurationInterface;
use function is_int;
/** /**
* This is the class that validates and merges configuration from your app/config files. * This is the class that validates and merges configuration from your app/config files
* *
* To learn more see {@link http://symfony.com/doc/current/cookbook/bundles/extension.html#cookbook-bundles-extension-config-class} * To learn more see {@link http://symfony.com/doc/current/cookbook/bundles/extension.html#cookbook-bundles-extension-config-class}
*/ */
class Configuration implements ConfigurationInterface class Configuration implements ConfigurationInterface
{ {
/**
* {@inheritdoc}
*/
public function getConfigTreeBuilder() public function getConfigTreeBuilder()
{ {
$treeBuilder = new TreeBuilder('chill_activity'); $treeBuilder = new TreeBuilder('chill_activity');
@@ -37,7 +29,7 @@ class Configuration implements ConfigurationInterface
->isRequired() ->isRequired()
->requiresAtLeastOneElement() ->requiresAtLeastOneElement()
->defaultValue( ->defaultValue(
[ array(
[ 'label' => '5 minutes', 'seconds' => 300], [ 'label' => '5 minutes', 'seconds' => 300],
[ 'label' => '10 minutes', 'seconds' => 600], [ 'label' => '10 minutes', 'seconds' => 600],
[ 'label' => '15 minutes', 'seconds' => 900], [ 'label' => '15 minutes', 'seconds' => 900],
@@ -50,25 +42,27 @@ class Configuration implements ConfigurationInterface
[ 'label' => '1 hour 30', 'seconds' => 5400], [ 'label' => '1 hour 30', 'seconds' => 5400],
[ 'label' => '1 hour 45', 'seconds' => 6300], [ 'label' => '1 hour 45', 'seconds' => 6300],
[ 'label' => '2 hours', 'seconds' => 7200], [ 'label' => '2 hours', 'seconds' => 7200],
] )
) )
->info('The intervals of time to show in activity form') ->info('The intervals of time to show in activity form')
->prototype('array') ->prototype('array')
->children() ->children()
->scalarNode('seconds') ->scalarNode('seconds')
->info('The number of seconds of this duration. Must be an integer.') ->info("The number of seconds of this duration. Must be an integer.")
->cannotBeEmpty() ->cannotBeEmpty()
->validate() ->validate()
->ifTrue(static function ($data) { ->ifTrue(function($data) {
return !is_int($data); return !is_int($data);
})->thenInvalid('The value %s is not a valid integer') })->thenInvalid("The value %s is not a valid integer")
->end() ->end()
->end() ->end()
->scalarNode('label') ->scalarNode('label')
->cannotBeEmpty() ->cannotBeEmpty()
->info('The label to show into fields') ->info("The label to show into fields")
->end() ->end()
->end() ->end()
->end() ->end()
// ->validate() // ->validate()
// //
@@ -92,6 +86,7 @@ class Configuration implements ConfigurationInterface
// ->end() // ->end()
->end() ->end()
->end() ->end()
->end() ->end()
->end(); ->end();

View File

@@ -1,63 +1,119 @@
<?php <?php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Chill\ActivityBundle\Entity; namespace Chill\ActivityBundle\Entity;
use Chill\ActivityBundle\Validator\Constraints as ActivityValidator; use Chill\DocStoreBundle\Entity\Document;
use Chill\DocStoreBundle\Entity\StoredObject;
use Chill\MainBundle\Entity\Center;
use Chill\MainBundle\Entity\Embeddable\CommentEmbeddable; use Chill\MainBundle\Entity\Embeddable\CommentEmbeddable;
use Chill\MainBundle\Entity\HasCenterInterface;
use Chill\MainBundle\Entity\HasScopeInterface;
use Chill\MainBundle\Entity\Location; use Chill\MainBundle\Entity\Location;
use Chill\MainBundle\Entity\Scope;
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Validator\Constraints\Entity\UserCircleConsistency;
use Chill\PersonBundle\AccompanyingPeriod\SocialIssueConsistency\AccompanyingPeriodLinkedWithSocialIssuesEntityInterface; use Chill\PersonBundle\AccompanyingPeriod\SocialIssueConsistency\AccompanyingPeriodLinkedWithSocialIssuesEntityInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Entity\SocialWork\SocialAction; use Chill\PersonBundle\Entity\SocialWork\SocialAction;
use Chill\PersonBundle\Entity\SocialWork\SocialIssue; use Chill\PersonBundle\Entity\SocialWork\SocialIssue;
use Chill\ThirdPartyBundle\Entity\ThirdParty; use Chill\ThirdPartyBundle\Entity\ThirdParty;
use DateTime;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
use Chill\MainBundle\Entity\Scope;
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Entity\Center;
use Chill\PersonBundle\Entity\Person;
use Chill\MainBundle\Entity\HasCenterInterface;
use Chill\MainBundle\Entity\HasScopeInterface;
use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Collections\ArrayCollection;
use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Serializer\Annotation\DiscriminatorMap;
use Symfony\Component\Serializer\Annotation\Groups; use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Serializer\Annotation\SerializedName; use Symfony\Component\Serializer\Annotation\DiscriminatorMap;
/** /**
* Class Activity. * Class Activity
* *
* @package Chill\ActivityBundle\Entity
* @ORM\Entity(repositoryClass="Chill\ActivityBundle\Repository\ActivityRepository") * @ORM\Entity(repositoryClass="Chill\ActivityBundle\Repository\ActivityRepository")
* @ORM\Table(name="activity") * @ORM\Table(name="activity")
* @ORM\HasLifecycleCallbacks * @ORM\HasLifecycleCallbacks()
* @DiscriminatorMap(typeProperty="type", mapping={ * @DiscriminatorMap(typeProperty="type", mapping={
* "activity": Activity::class * "activity"=Activity::class
* }) * })
* @ActivityValidator\ActivityValidity */
*
* TODO see if necessary /*
* UserCircleConsistency( * TODO : revoir
* @UserCircleConsistency(
* "CHILL_ACTIVITY_SEE_DETAILS", * "CHILL_ACTIVITY_SEE_DETAILS",
* getUserFunction="getUser", * getUserFunction="getUser",
* path="scope") * path="scope")
*/ */
class Activity implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterface, HasCenterInterface, HasScopeInterface
{
public const SENTRECEIVED_RECEIVED = 'received';
public const SENTRECEIVED_SENT = 'sent'; class Activity implements HasCenterInterface, HasScopeInterface, AccompanyingPeriodLinkedWithSocialIssuesEntityInterface
{
const SENTRECEIVED_SENT = 'sent';
const SENTRECEIVED_RECEIVED = 'received';
/**
* @ORM\Id
* @ORM\Column(name="id", type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
* @Groups({"read"})
*/
private ?int $id = null;
/**
* @ORM\ManyToOne(targetEntity="Chill\MainBundle\Entity\User")
*/
private User $user;
/**
* @ORM\Column(type="datetime")
*/
private \DateTime $date;
/**
* @ORM\Column(type="time", nullable=true)
*/
private ?\DateTime $durationTime = null;
/**
* @ORM\Column(type="time", nullable=true)
*/
private ?\DateTime $travelTime = null;
/**
* @ORM\ManyToOne(targetEntity="Chill\ActivityBundle\Entity\ActivityPresence")
*/
private ?ActivityPresence $attendee = null;
/**
* @ORM\ManyToMany(targetEntity="Chill\ActivityBundle\Entity\ActivityReason")
*/
private Collection $reasons;
/**
* @ORM\ManyToMany(targetEntity="Chill\PersonBundle\Entity\SocialWork\SocialIssue")
* @ORM\JoinTable(name="chill_activity_activity_chill_person_socialissue")
* @Groups({"read"})
*/
private Collection $socialIssues;
/**
* @ORM\ManyToMany(targetEntity="Chill\PersonBundle\Entity\SocialWork\SocialAction")
* @ORM\JoinTable(name="chill_activity_activity_chill_person_socialaction")
* @Groups({"read"})
*/
private Collection $socialActions;
/**
* @ORM\ManyToOne(targetEntity="Chill\ActivityBundle\Entity\ActivityType")
*/
private ActivityType $type;
/**
* @ORM\ManyToOne(targetEntity="Chill\MainBundle\Entity\Scope")
*/
private ?Scope $scope = null;
/**
* @ORM\ManyToOne(targetEntity="Chill\PersonBundle\Entity\Person")
*/
private ?Person $person = null;
/** /**
* @ORM\ManyToOne(targetEntity="Chill\PersonBundle\Entity\AccompanyingPeriod") * @ORM\ManyToOne(targetEntity="Chill\PersonBundle\Entity\AccompanyingPeriod")
@@ -65,31 +121,22 @@ class Activity implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterfac
*/ */
private ?AccompanyingPeriod $accompanyingPeriod = null; private ?AccompanyingPeriod $accompanyingPeriod = null;
/**
* @ORM\ManyToOne(targetEntity="Chill\ActivityBundle\Entity\ActivityType")
* @Groups({"read", "docgen:read"})
* @SerializedName("activityType")
* @ORM\JoinColumn(name="type_id")
*/
private ActivityType $activityType;
/**
* @ORM\ManyToOne(targetEntity="Chill\ActivityBundle\Entity\ActivityPresence")
* @Groups({"docgen:read"})
*/
private ?ActivityPresence $attendee = null;
/** /**
* @ORM\Embedded(class="Chill\MainBundle\Entity\Embeddable\CommentEmbeddable", columnPrefix="comment_") * @ORM\Embedded(class="Chill\MainBundle\Entity\Embeddable\CommentEmbeddable", columnPrefix="comment_")
* @Groups({"docgen:read"})
*/ */
private CommentEmbeddable $comment; private CommentEmbeddable $comment;
/** /**
* @ORM\Column(type="datetime") * @ORM\ManyToMany(targetEntity="Chill\PersonBundle\Entity\Person")
* @Groups({"docgen:read"}) * @Groups({"read"})
*/ */
private DateTime $date; private ?Collection $persons = null;
/**
* @ORM\ManyToMany(targetEntity="Chill\ThirdPartyBundle\Entity\ThirdParty")
* @Groups({"read"})
*/
private ?Collection $thirdParties = null;
/** /**
* @ORM\ManyToMany(targetEntity="Chill\DocStoreBundle\Entity\StoredObject", cascade={"persist"}) * @ORM\ManyToMany(targetEntity="Chill\DocStoreBundle\Entity\StoredObject", cascade={"persist"})
@@ -97,95 +144,27 @@ class Activity implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterfac
private Collection $documents; private Collection $documents;
/** /**
* @ORM\Column(type="time", nullable=true) * @ORM\ManyToMany(targetEntity="Chill\MainBundle\Entity\User")
* @Groups({"read"})
*/ */
private ?DateTime $durationTime = null; private ?Collection $users = null;
/** /**
* @ORM\Column(type="boolean", options={"default": false}) * @ORM\Column(type="boolean", options={"default"=false})
* @Groups({"docgen:read"})
*/ */
private bool $emergency = false; private bool $emergency = false;
/** /**
* @ORM\Id * @ORM\Column(type="string", options={"default"=""})
* @ORM\Column(name="id", type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
* @Groups({"read", "docgen:read"})
*/
private ?int $id = null;
/**
* @ORM\ManyToOne(targetEntity="Chill\MainBundle\Entity\Location")
* @groups({"read", "docgen:read"})
*/
private ?Location $location = null;
/**
* @ORM\ManyToOne(targetEntity="Chill\PersonBundle\Entity\Person")
*/
private ?Person $person = null;
/**
* @ORM\ManyToMany(targetEntity="Chill\PersonBundle\Entity\Person")
* @Groups({"read", "docgen:read"})
*/
private ?Collection $persons = null;
/**
* @ORM\ManyToMany(targetEntity="Chill\ActivityBundle\Entity\ActivityReason")
* @Groups({"docgen:read"})
*/
private Collection $reasons;
/**
* @ORM\ManyToOne(targetEntity="Chill\MainBundle\Entity\Scope")
* @Groups({"docgen:read"})
*/
private ?Scope $scope = null;
/**
* @ORM\Column(type="string", options={"default": ""})
* @Groups({"docgen:read"})
*/ */
private string $sentReceived = ''; private string $sentReceived = '';
/** /**
* @ORM\ManyToMany(targetEntity="Chill\PersonBundle\Entity\SocialWork\SocialAction") * @ORM\ManyToOne(targetEntity="Chill\MainBundle\Entity\Location")
* @ORM\JoinTable(name="chill_activity_activity_chill_person_socialaction") * @groups({"read"})
* @Groups({"read", "docgen:read"})
*/ */
private Collection $socialActions; private ?Location $location = null;
/**
* @ORM\ManyToMany(targetEntity="Chill\PersonBundle\Entity\SocialWork\SocialIssue")
* @ORM\JoinTable(name="chill_activity_activity_chill_person_socialissue")
* @Groups({"read", "docgen:read"})
*/
private Collection $socialIssues;
/**
* @ORM\ManyToMany(targetEntity="Chill\ThirdPartyBundle\Entity\ThirdParty")
* @Groups({"read", "docgen:read"})
*/
private ?Collection $thirdParties = null;
/**
* @ORM\Column(type="time", nullable=true)
*/
private ?DateTime $travelTime = null;
/**
* @ORM\ManyToOne(targetEntity="Chill\MainBundle\Entity\User")
* @Groups({"docgen:read"})
*/
private User $user;
/**
* @ORM\ManyToMany(targetEntity="Chill\MainBundle\Entity\User")
* @Groups({"read", "docgen:read"})
*/
private ?Collection $users = null;
public function __construct() public function __construct()
{ {
@@ -199,27 +178,71 @@ class Activity implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterfac
$this->socialActions = new ArrayCollection(); $this->socialActions = new ArrayCollection();
} }
public function addDocument(StoredObject $document): self public function getId(): ?int
{ {
$this->documents[] = $document; return $this->id;
}
public function setUser(UserInterface $user): self
{
$this->user = $user;
return $this; return $this;
} }
/** public function getUser(): User
* Add a person to the person list.
*/
public function addPerson(?Person $person): self
{ {
if (null !== $person) { return $this->user;
if (!$this->persons->contains($person)) {
$this->persons[] = $person;
}
} }
public function setDate(\DateTime $date): self
{
$this->date = $date;
return $this; return $this;
} }
public function getDate(): \DateTime
{
return $this->date;
}
public function setDurationTime(?\DateTime $durationTime): self
{
$this->durationTime = $durationTime;
return $this;
}
public function getDurationTime(): ?\DateTime
{
return $this->durationTime;
}
public function setTravelTime(\DateTime $travelTime): self
{
$this->travelTime = $travelTime;
return $this;
}
public function getTravelTime(): ?\DateTime
{
return $this->travelTime;
}
public function setAttendee(ActivityPresence $attendee): self
{
$this->attendee = $attendee;
return $this;
}
public function getAttendee(): ?ActivityPresence
{
return $this->attendee;
}
public function addReason(ActivityReason $reason): self public function addReason(ActivityReason $reason): self
{ {
$this->reasons->add($reason); $this->reasons->add($reason);
@@ -227,26 +250,28 @@ class Activity implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterfac
return $this; return $this;
} }
public function addSocialAction(SocialAction $socialAction): self public function removeReason(ActivityReason $reason): void
{ {
if (!$this->socialActions->contains($socialAction)) { $this->reasons->removeElement($reason);
$this->socialActions[] = $socialAction;
$this->ensureSocialActionConsistency();
} }
public function getReasons(): Collection
{
return $this->reasons;
}
public function setReasons(?ArrayCollection $reasons): self
{
$this->reasons = $reasons;
return $this; return $this;
} }
/** public function getSocialIssues(): Collection
* Add a social issue. {
* return $this->socialIssues;
* Note: the social issue consistency (the fact that only yougest social issues }
* are kept) is processed by an entity listener:
*
* @see{\Chill\PersonBundle\AccompanyingPeriod\SocialIssueConsistency\AccompanyingPeriodSocialIssueConsistencyEntityListener}
*
* @return $this
*/
public function addSocialIssue(SocialIssue $socialIssue): self public function addSocialIssue(SocialIssue $socialIssue): self
{ {
if (!$this->socialIssues->contains($socialIssue)) { if (!$this->socialIssues->contains($socialIssue)) {
@@ -256,46 +281,88 @@ class Activity implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterfac
return $this; return $this;
} }
public function addThirdParty(?ThirdParty $thirdParty): self public function removeSocialIssue(SocialIssue $socialIssue): self
{ {
if (null !== $thirdParty) { $this->socialIssues->removeElement($socialIssue);
if (!$this->thirdParties->contains($thirdParty)) {
$this->thirdParties[] = $thirdParty; return $this;
} }
public function getSocialActions(): Collection
{
return $this->socialActions;
}
public function addSocialAction(SocialAction $socialAction): self
{
if (!$this->socialActions->contains($socialAction)) {
$this->socialActions[] = $socialAction;
} }
return $this; return $this;
} }
public function addUser(?User $user): self public function removeSocialAction(SocialAction $socialAction): self
{ {
if (null !== $user) { $this->socialActions->removeElement($socialAction);
if (!$this->users->contains($user)) {
$this->users[] = $user;
}
}
return $this; return $this;
} }
public function setType(ActivityType $type): self
{
$this->type = $type;
return $this;
}
public function getType(): ActivityType
{
return $this->type;
}
public function setScope(Scope $scope): self
{
$this->scope = $scope;
return $this;
}
public function getScope(): ?Scope
{
return $this->scope;
}
public function setPerson(?Person $person): self
{
$this->person = $person;
return $this;
}
public function getPerson(): ?Person
{
return $this->person;
}
public function getAccompanyingPeriod(): ?AccompanyingPeriod public function getAccompanyingPeriod(): ?AccompanyingPeriod
{ {
return $this->accompanyingPeriod; return $this->accompanyingPeriod;
} }
public function getActivityType(): ActivityType public function setAccompanyingPeriod(?AccompanyingPeriod $accompanyingPeriod): self
{ {
return $this->activityType; $this->accompanyingPeriod = $accompanyingPeriod;
}
public function getAttendee(): ?ActivityPresence return $this;
{
return $this->attendee;
} }
/** /**
* get the center * get the center
* center is extracted from person. * center is extracted from person
*/ */
public function getCenter(): ?Center public function getCenter(): ?Center
{ {
@@ -311,51 +378,28 @@ class Activity implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterfac
return $this->comment; return $this->comment;
} }
public function getDate(): DateTime public function setComment(CommentEmbeddable $comment): self
{ {
return $this->date; $this->comment = $comment;
}
public function getDocuments(): Collection return $this;
{
return $this->documents;
} }
/** /**
* @Groups({"docgen:read"}) * Add a person to the person list
*/ */
public function getDurationMinute(): int public function addPerson(?Person $person): self
{ {
if (null === $this->durationTime) { if (null !== $person) {
return 0; $this->persons[] = $person;
} }
return (int) round(($this->durationTime->getTimestamp() + $this->durationTime->getOffset()) / 60.0, 0); return $this;
} }
public function getDurationTime(): ?DateTime public function removePerson(Person $person): void
{ {
return $this->durationTime; $this->persons->removeElement($person);
}
public function getEmergency(): bool
{
return $this->emergency;
}
public function getId(): ?int
{
return $this->id;
}
public function getLocation(): ?Location
{
return $this->location;
}
public function getPerson(): ?Person
{
return $this->person;
} }
public function getPersons(): Collection public function getPersons(): Collection
@@ -367,16 +411,13 @@ class Activity implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterfac
{ {
if (null !== $this->accompanyingPeriod) { if (null !== $this->accompanyingPeriod) {
$personsAssociated = []; $personsAssociated = [];
foreach ($this->accompanyingPeriod->getParticipations() as $participation) {
foreach ($this->accompanyingPeriod->getOpenParticipations() as $participation) {
if ($this->persons->contains($participation->getPerson())) { if ($this->persons->contains($participation->getPerson())) {
$personsAssociated[] = $participation->getPerson(); $personsAssociated[] = $participation->getPerson();
} }
} }
return $personsAssociated; return $personsAssociated;
} }
return []; return [];
} }
@@ -384,199 +425,16 @@ class Activity implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterfac
{ {
if (null !== $this->accompanyingPeriod) { if (null !== $this->accompanyingPeriod) {
$personsNotAssociated = []; $personsNotAssociated = [];
// TODO better semantic with: return $this->persons->filter(...);
foreach ($this->persons as $person) { foreach ($this->persons as $person) {
if ($this->accompanyingPeriod->getOpenParticipationContainsPerson($person) === null) { if (!in_array($person, $this->getPersonsAssociated())) {
$personsNotAssociated[] = $person; $personsNotAssociated[] = $person;
} }
} }
return $personsNotAssociated; return $personsNotAssociated;
} }
return []; return [];
} }
public function getReasons(): Collection
{
return $this->reasons;
}
public function getScope(): ?Scope
{
return $this->scope;
}
public function getSentReceived(): string
{
return $this->sentReceived;
}
public function getSocialActions(): Collection
{
return $this->socialActions;
}
public function getSocialIssues(): Collection
{
return $this->socialIssues;
}
public function getThirdParties(): Collection
{
return $this->thirdParties;
}
public function getTravelTime(): ?DateTime
{
return $this->travelTime;
}
/**
* @Groups({"docgen:read"})
*/
public function getTravelTimeMinute(): int
{
if (null === $this->travelTime) {
return 0;
}
return (int) round(($this->travelTime->getTimestamp() + $this->travelTime->getOffset()) / 60.0, 0);
}
/**
* @deprecated
*/
public function getType(): ActivityType
{
return $this->activityType;
}
public function getUser(): User
{
return $this->user;
}
public function getUsers(): Collection
{
return $this->users;
}
public function isEmergency(): bool
{
return $this->getEmergency();
}
public function removeDocument(StoredObject $document): void
{
$this->documents->removeElement($document);
}
public function removePerson(Person $person): void
{
$this->persons->removeElement($person);
}
public function removeReason(ActivityReason $reason): void
{
$this->reasons->removeElement($reason);
}
public function removeSocialAction(SocialAction $socialAction): self
{
$this->socialActions->removeElement($socialAction);
return $this;
}
public function removeSocialIssue(SocialIssue $socialIssue): self
{
$this->socialIssues->removeElement($socialIssue);
return $this;
}
public function removeThirdParty(ThirdParty $thirdParty): void
{
$this->thirdParties->removeElement($thirdParty);
}
public function removeUser(User $user): void
{
$this->users->removeElement($user);
}
public function setAccompanyingPeriod(?AccompanyingPeriod $accompanyingPeriod): self
{
$this->accompanyingPeriod = $accompanyingPeriod;
return $this;
}
public function setActivityType(ActivityType $activityType): self
{
$this->activityType = $activityType;
return $this;
}
public function setAttendee(ActivityPresence $attendee): self
{
$this->attendee = $attendee;
return $this;
}
public function setComment(CommentEmbeddable $comment): self
{
$this->comment = $comment;
return $this;
}
public function setDate(DateTime $date): self
{
$this->date = $date;
return $this;
}
public function setDocuments(Collection $documents): self
{
$this->documents = $documents;
return $this;
}
public function setDurationTime(?DateTime $durationTime): self
{
$this->durationTime = $durationTime;
return $this;
}
public function setEmergency(bool $emergency): self
{
$this->emergency = $emergency;
return $this;
}
public function setLocation(?Location $location): Activity
{
$this->location = $location;
return $this;
}
public function setPerson(?Person $person): self
{
$this->person = $person;
return $this;
}
public function setPersons(?Collection $persons): self public function setPersons(?Collection $persons): self
{ {
$this->persons = $persons; $this->persons = $persons;
@@ -584,25 +442,22 @@ class Activity implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterfac
return $this; return $this;
} }
public function setReasons(?ArrayCollection $reasons): self public function addThirdParty(?ThirdParty $thirdParty): self
{ {
$this->reasons = $reasons; if (null !== $thirdParty) {
$this->thirdParties[] = $thirdParty;
}
return $this; return $this;
} }
public function setScope(Scope $scope): self public function removeThirdParty(ThirdParty $thirdParty): void
{ {
$this->scope = $scope; $this->thirdParties->removeElement($thirdParty);
return $this;
} }
public function setSentReceived(?string $sentReceived): self public function getThirdParties(): Collection
{ {
$this->sentReceived = (string) $sentReceived; return $this->thirdParties;
return $this;
} }
public function setThirdParties(?Collection $thirdParties): self public function setThirdParties(?Collection $thirdParties): self
@@ -612,30 +467,48 @@ class Activity implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterfac
return $this; return $this;
} }
public function setTravelTime(DateTime $travelTime): self public function addDocument(Document $document): self
{ {
$this->travelTime = $travelTime; $this->documents[] = $document;
return $this; return $this;
} }
/** public function removeDocument(Document $document): void
* @deprecated
*/
public function setType(ActivityType $activityType): self
{ {
$this->activityType = $activityType; $this->documents->removeElement($document);
}
public function getDocuments(): Collection
{
return $this->documents;
}
public function setDocuments(Collection $documents): self
{
$this->documents = $documents;
return $this; return $this;
} }
public function setUser(UserInterface $user): self public function addUser(?User $user): self
{ {
$this->user = $user; if (null !== $user) {
$this->users[] = $user;
}
return $this; return $this;
} }
public function removeUser(User $user): void
{
$this->users->removeElement($user);
}
public function getUsers(): Collection
{
return $this->users;
}
public function setUsers(?Collection $users): self public function setUsers(?Collection $users): self
{ {
$this->users = $users; $this->users = $users;
@@ -643,12 +516,50 @@ class Activity implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterfac
return $this; return $this;
} }
private function ensureSocialActionConsistency(): void public function isEmergency(): bool
{ {
$ancestors = SocialAction::findAncestorSocialActions($this->getSocialActions()); return $this->getEmergency();
}
foreach ($ancestors as $ancestor) { public function getEmergency(): bool
$this->removeSocialAction($ancestor); {
} return $this->emergency;
}
public function setEmergency(bool $emergency): self
{
$this->emergency = $emergency;
return $this;
}
public function getSentReceived(): string
{
return $this->sentReceived;
}
public function setSentReceived(?string $sentReceived): self
{
$this->sentReceived = (string) $sentReceived;
return $this;
}
/**
* @return Location|null
*/
public function getLocation(): ?Location
{
return $this->location;
}
/**
* @param Location|null $location
* @return Activity
*/
public function setLocation(?Location $location): Activity
{
$this->location = $location;
return $this;
} }
} }

View File

@@ -1,48 +1,71 @@
<?php <?php
/** /*
* Chill is a software for social workers
* *
* For the full copyright and license information, please view * Copyright (C) 2015, Champs Libres Cooperative SCRLFS, <http://www.champs-libres.coop>
* the LICENSE file that was distributed with this source code. *
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
declare(strict_types=1);
namespace Chill\ActivityBundle\Entity; namespace Chill\ActivityBundle\Entity;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Annotation as Serializer;
/** /**
* Class ActivityPresence. * Class ActivityPresence
* *
* @ORM\Entity * @package Chill\ActivityBundle\Entity
* @ORM\Entity()
* @ORM\Table(name="activitytpresence") * @ORM\Table(name="activitytpresence")
* @ORM\HasLifecycleCallbacks * @ORM\HasLifecycleCallbacks()
*/ */
class ActivityPresence class ActivityPresence
{ {
/**
* @ORM\Column(type="boolean")
*/
private bool $active = true;
/** /**
* @ORM\Id * @ORM\Id
* @ORM\Column(name="id", type="integer") * @ORM\Column(name="id", type="integer")
* @ORM\GeneratedValue(strategy="AUTO") * @ORM\GeneratedValue(strategy="AUTO")
* @Serializer\Groups({"docgen:read"})
*/ */
private ?int $id; private ?int $id;
/** /**
* @ORM\Column(type="json") * @ORM\Column(type="json")
* @Serializer\Groups({"docgen:read"})
* @Serializer\Context({"is-translatable": true}, groups={"docgen:read"})
*/ */
private array $name = []; private array $name = [];
/**
* @ORM\Column(type="boolean")
*/
private bool $active = true;
public function getId(): int
{
return $this->id;
}
public function setName(array $name): self
{
$this->name = $name;
return $this;
}
public function getName(): array
{
return $this->name;
}
/** /**
* Get active * Get active
* return true if the category type is active. * return true if the category type is active.
@@ -52,19 +75,9 @@ class ActivityPresence
return $this->active; return $this->active;
} }
public function getId(): int
{
return $this->id;
}
public function getName(): array
{
return $this->name;
}
/** /**
* Is active * Is active
* return true if the category type is active. * return true if the category type is active
*/ */
public function isActive(): bool public function isActive(): bool
{ {
@@ -73,7 +86,7 @@ class ActivityPresence
/** /**
* Set active * Set active
* set to true if the category type is active. * set to true if the category type is active
*/ */
public function setActive(bool $active): self public function setActive(bool $active): self
{ {
@@ -81,11 +94,4 @@ class ActivityPresence
return $this; return $this;
} }
public function setName(array $name): self
{
$this->name = $name;
return $this;
}
} }

View File

@@ -1,43 +1,40 @@
<?php <?php
/** /*
* Chill is a software for social workers
* *
* For the full copyright and license information, please view * Copyright (C) 2015, Champs Libres Cooperative SCRLFS, <http://www.champs-libres.coop>
* the LICENSE file that was distributed with this source code. *
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
declare(strict_types=1);
namespace Chill\ActivityBundle\Entity; namespace Chill\ActivityBundle\Entity;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
use Chill\ActivityBundle\Entity\ActivityReasonCategory;
/** /**
* Class ActivityReason. * Class ActivityReason
* *
* @ORM\Entity * @package Chill\ActivityBundle\Entity
* @ORM\Entity()
* @ORM\Table(name="activityreason") * @ORM\Table(name="activityreason")
* @ORM\HasLifecycleCallbacks * @ORM\HasLifecycleCallbacks()
*/ */
class ActivityReason class ActivityReason
{ {
/** /**
* @var bool * @var integer
* @ORM\Column(type="boolean")
*/
private $active = true;
/**
* @var ActivityReasonCategory
* @ORM\ManyToOne(
* targetEntity="Chill\ActivityBundle\Entity\ActivityReasonCategory",
* inversedBy="reasons")
*/
private $category;
/**
* @var int
* *
* @ORM\Id * @ORM\Id
* @ORM\Column(name="id", type="integer") * @ORM\Column(name="id", type="integer")
@@ -52,17 +49,86 @@ class ActivityReason
private $name; private $name;
/** /**
* Get active. * @var ActivityReasonCategory
* * @ORM\ManyToOne(
* @return bool * targetEntity="Chill\ActivityBundle\Entity\ActivityReasonCategory",
* inversedBy="reasons")
*/ */
public function getActive() private $category;
/**
* @var boolean
* @ORM\Column(type="boolean")
*/
private $active = true;
/**
* Get id
*
* @return integer
*/
public function getId()
{ {
return $this->active; return $this->id;
} }
/** /**
* Get category. * Set name
*
* @param array $name
* @return ActivityReason
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* @return array | string
*/
public function getName($locale = null)
{
if ($locale) {
if (isset($this->name[$locale])) {
return $this->name[$locale];
} else {
foreach ($this->name as $name) {
if (!empty($name)) {
return $name;
}
}
}
return '';
} else {
return $this->name;
}
}
/**
* Set category of the reason. If you set to the reason an inactive
* category, the reason will become inactive
*
* @param ActivityReasonCategory $category
* @return ActivityReason
*/
public function setCategory(ActivityReasonCategory $category)
{
if($this->category !== $category && ! $category->getActive()) {
$this->setActive(False);
}
$this->category = $category;
return $this;
}
/**
* Get category
* *
* @return ActivityReasonCategory * @return ActivityReasonCategory
*/ */
@@ -72,46 +138,9 @@ class ActivityReason
} }
/** /**
* Get id. * Set active
*
* @return int
*/
public function getId()
{
return $this->id;
}
/**
* Get name.
*
* @param mixed|null $locale
*
* @return array | string
*/
public function getName($locale = null)
{
if ($locale) {
if (isset($this->name[$locale])) {
return $this->name[$locale];
}
foreach ($this->name as $name) {
if (!empty($name)) {
return $name;
}
}
return '';
}
return $this->name;
}
/**
* Set active.
*
* @param bool $active
* *
* @param boolean $active
* @return ActivityReason * @return ActivityReason
*/ */
public function setActive($active) public function setActive($active)
@@ -122,33 +151,13 @@ class ActivityReason
} }
/** /**
* Set category of the reason. If you set to the reason an inactive * Get active
* category, the reason will become inactive.
* *
* @return ActivityReason * @return boolean
*/ */
public function setCategory(ActivityReasonCategory $category) public function getActive()
{ {
if ($this->category !== $category && !$category->getActive()) { return $this->active;
$this->setActive(false);
}
$this->category = $category;
return $this;
}
/**
* Set name.
*
* @param array $name
*
* @return ActivityReason
*/
public function setName($name)
{
$this->name = $name;
return $this;
} }
} }

View File

@@ -1,36 +1,39 @@
<?php <?php
/** /*
* Chill is a software for social workers * Copyright (C) 2015, Champs Libres Cooperative SCRLFS, <http://www.champs-libres.coop>
* *
* For the full copyright and license information, please view * This program is free software: you can redistribute it and/or modify
* the LICENSE file that was distributed with this source code. * it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
declare(strict_types=1);
namespace Chill\ActivityBundle\Entity; namespace Chill\ActivityBundle\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/** /**
* Class ActivityReasonCategory. * Class ActivityReasonCategory
* *
* @ORM\Entity * @package Chill\ActivityBundle\Entity
* @ORM\Entity()
* @ORM\Table(name="activityreasoncategory") * @ORM\Table(name="activityreasoncategory")
* @ORM\HasLifecycleCallbacks * @ORM\HasLifecycleCallbacks()
*/ */
class ActivityReasonCategory class ActivityReasonCategory
{ {
/** /**
* @var bool * @var integer
* @ORM\Column(type="boolean")
*/
private $active = true;
/**
* @var int
* *
* @ORM\Id * @ORM\Id
* @ORM\Column(name="id", type="integer") * @ORM\Column(name="id", type="integer")
@@ -45,8 +48,13 @@ class ActivityReasonCategory
private $name; private $name;
/** /**
* Array of ActivityReason. * @var boolean
* * @ORM\Column(type="boolean")
*/
private $active = true;
/**
* Array of ActivityReason
* @var ArrayCollection * @var ArrayCollection
* @ORM\OneToMany( * @ORM\OneToMany(
* targetEntity="Chill\ActivityBundle\Entity\ActivityReason", * targetEntity="Chill\ActivityBundle\Entity\ActivityReason",
@@ -71,19 +79,9 @@ class ActivityReasonCategory
} }
/** /**
* Get active. * Get id
* *
* @return bool * @return integer
*/
public function getActive()
{
return $this->active;
}
/**
* Get id.
*
* @return int
*/ */
public function getId() public function getId()
{ {
@@ -91,9 +89,20 @@ class ActivityReasonCategory
} }
/** /**
* Get name. * Set name
* *
* @param mixed|null $locale * @param array $name
* @return ActivityReasonCategory
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
* *
* @return array * @return array
*/ */
@@ -102,27 +111,25 @@ class ActivityReasonCategory
if ($locale) { if ($locale) {
if (isset($this->name[$locale])) { if (isset($this->name[$locale])) {
return $this->name[$locale]; return $this->name[$locale];
} } else {
foreach ($this->name as $name) { foreach ($this->name as $name) {
if (!empty($name)) { if (!empty($name)) {
return $name; return $name;
} }
} }
return '';
} }
return '';
} else {
return $this->name; return $this->name;
} }
}
/** /**
* Declare a category as active (or not). When a category is set * Declare a category as active (or not). When a category is set
* as unactive, all the reason have this entity as category is also * as unactive, all the reason have this entity as category is also
* set as unactive. * set as unactive
*
* @param bool $active
* *
* @param boolean $active
* @return ActivityReasonCategory * @return ActivityReasonCategory
*/ */
public function setActive($active) public function setActive($active)
@@ -139,16 +146,13 @@ class ActivityReasonCategory
} }
/** /**
* Set name. * Get active
* *
* @param array $name * @return boolean
*
* @return ActivityReasonCategory
*/ */
public function setName($name) public function getActive()
{ {
$this->name = $name; return $this->active;
}
}
return $this;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,12 +1,5 @@
<?php <?php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1); declare(strict_types=1);
namespace Chill\ActivityBundle\Entity; namespace Chill\ActivityBundle\Entity;
@@ -14,17 +7,12 @@ namespace Chill\ActivityBundle\Entity;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
/** /**
* @ORM\Entity * @ORM\Entity()
* @ORM\Table(name="activitytypecategory") * @ORM\Table(name="activitytypecategory")
* @ORM\HasLifecycleCallbacks * @ORM\HasLifecycleCallbacks()
*/ */
class ActivityTypeCategory class ActivityTypeCategory
{ {
/**
* @ORM\Column(type="boolean")
*/
private bool $active = true;
/** /**
* @ORM\Id * @ORM\Id
* @ORM\Column(name="id", type="integer") * @ORM\Column(name="id", type="integer")
@@ -38,10 +26,38 @@ class ActivityTypeCategory
private array $name = []; private array $name = [];
/** /**
* @ORM\Column(type="float", options={"default": "0.0"}) * @ORM\Column(type="boolean")
*/
private bool $active = true;
/**
* @ORM\Column(type="float", options={"default"="0.0"})
*/ */
private float $ordering = 0.0; private float $ordering = 0.0;
public function getId(): ?int
{
return $this->id;
}
/**
* Set name
*/
public function setName(array $name): self
{
$this->name = $name;
return $this;
}
/**
* Get name
*/
public function getName(): array
{
return $this->name;
}
/** /**
* Get active * Get active
* return true if the category type is active. * return true if the category type is active.
@@ -51,27 +67,9 @@ class ActivityTypeCategory
return $this->active; return $this->active;
} }
public function getId(): ?int
{
return $this->id;
}
/**
* Get name.
*/
public function getName(): array
{
return $this->name;
}
public function getOrdering(): float
{
return $this->ordering;
}
/** /**
* Is active * Is active
* return true if the category type is active. * return true if the category type is active
*/ */
public function isActive(): bool public function isActive(): bool
{ {
@@ -80,7 +78,7 @@ class ActivityTypeCategory
/** /**
* Set active * Set active
* set to true if the category type is active. * set to true if the category type is active
*/ */
public function setActive(bool $active): self public function setActive(bool $active): self
{ {
@@ -89,14 +87,9 @@ class ActivityTypeCategory
return $this; return $this;
} }
/** public function getOrdering(): float
* Set name.
*/
public function setName(array $name): self
{ {
$this->name = $name; return $this->ordering;
return $this;
} }
public function setOrdering(float $ordering): self public function setOrdering(float $ordering): self

View File

@@ -1,74 +0,0 @@
<?php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Chill\ActivityBundle\EntityListener;
use Chill\ActivityBundle\Entity\Activity;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork;
use Chill\PersonBundle\Repository\AccompanyingPeriod\AccompanyingPeriodWorkRepository;
use DateTimeImmutable;
use Doctrine\ORM\EntityManagerInterface;
use function in_array;
class ActivityEntityListener
{
private EntityManagerInterface $em;
private AccompanyingPeriodWorkRepository $workRepository;
public function __construct(EntityManagerInterface $em, AccompanyingPeriodWorkRepository $workRepository)
{
$this->em = $em;
$this->workRepository = $workRepository;
}
public function persistActionToCourse(Activity $activity)
{
if ($activity->getAccompanyingPeriod() instanceof AccompanyingPeriod) {
$period = $activity->getAccompanyingPeriod();
$accompanyingCourseWorks = $this->workRepository->findByAccompanyingPeriod($period);
$periodActions = [];
$now = new DateTimeImmutable();
foreach ($accompanyingCourseWorks as $key => $work) {
// take only the actions which are still opened
if ($work->getEndDate() === null || $work->getEndDate() > ($activity->getDate() ?? $now)) {
$periodActions[$key] = spl_object_hash($work->getSocialAction());
}
}
$associatedPersons = $activity->getPersonsAssociated();
$associatedThirdparties = $activity->getThirdParties();
foreach ($activity->getSocialActions() as $action) {
if (in_array(spl_object_hash($action), $periodActions, true)) {
continue;
}
$newAction = new AccompanyingPeriodWork();
$newAction->setSocialAction($action);
$period->addWork($newAction);
$date = DateTimeImmutable::createFromMutable($activity->getDate());
$newAction->setStartDate($date);
foreach ($associatedPersons as $person) {
$newAction->addPerson($person);
}
$this->em->persist($newAction);
$this->em->flush();
}
}
}
}

View File

@@ -1,33 +1,22 @@
<?php <?php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1); declare(strict_types=1);
namespace Chill\ActivityBundle\Export\Aggregator; namespace Chill\ActivityBundle\Export\Aggregator;
use Chill\ActivityBundle\Repository\ActivityReasonCategoryRepository; use Chill\ActivityBundle\Repository\ActivityReasonCategoryRepository;
use Chill\ActivityBundle\Repository\ActivityReasonRepository; use Chill\ActivityBundle\Repository\ActivityReasonRepository;
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\MainBundle\Export\ExportElementValidatedInterface;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface; use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Doctrine\ORM\Query\Expr\Join;
use Doctrine\ORM\QueryBuilder;
use RuntimeException;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormBuilderInterface;
use Doctrine\ORM\QueryBuilder;
use Chill\MainBundle\Export\AggregatorInterface;
use Symfony\Component\Security\Core\Role\Role; use Symfony\Component\Security\Core\Role\Role;
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Doctrine\ORM\Query\Expr\Join;
use Chill\MainBundle\Export\ExportElementValidatedInterface;
use Symfony\Component\Validator\Context\ExecutionContextInterface; use Symfony\Component\Validator\Context\ExecutionContextInterface;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use function array_key_exists;
use function count;
class ActivityReasonAggregator implements AggregatorInterface, ExportElementValidatedInterface class ActivityReasonAggregator implements AggregatorInterface, ExportElementValidatedInterface
{ {
@@ -47,47 +36,42 @@ class ActivityReasonAggregator implements AggregatorInterface, ExportElementVali
$this->translatableStringHelper = $translatableStringHelper; $this->translatableStringHelper = $translatableStringHelper;
} }
public function addRole()
{
return new Role(ActivityStatsVoter::STATS);
}
public function alterQuery(QueryBuilder $qb, $data) public function alterQuery(QueryBuilder $qb, $data)
{ {
// add select element // add select element
if ('reasons' === $data['level']) { if ($data['level'] === 'reasons') {
$elem = 'reasons.id'; $elem = 'reasons.id';
$alias = 'activity_reasons_id'; $alias = 'activity_reasons_id';
} elseif ('categories' === $data['level']) { } elseif ($data['level'] === 'categories') {
$elem = 'category.id'; $elem = 'category.id';
$alias = 'activity_categories_id'; $alias = 'activity_categories_id';
} else { } else {
throw new RuntimeException('The data provided are not recognized.'); throw new \RuntimeException('The data provided are not recognized.');
} }
$qb->addSelect($elem.' as '.$alias); $qb->addSelect($elem.' as '.$alias);
// make a jointure only if needed // make a jointure only if needed
$join = $qb->getDQLPart('join'); $join = $qb->getDQLPart('join');
if ( if (
( (array_key_exists('activity', $join)
array_key_exists('activity', $join) &&
&& !$this->checkJoinAlreadyDefined($join['activity'], 'reasons') !$this->checkJoinAlreadyDefined($join['activity'], 'reasons')
) )
|| (!array_key_exists('activity', $join)) OR
(! array_key_exists('activity', $join))
) { ) {
$qb->add( $qb->add(
'join', 'join',
[ [
'activity' => new Join(Join::INNER_JOIN, 'activity.reasons', 'reasons'), 'activity' => new Join(Join::INNER_JOIN, 'activity.reasons', 'reasons')
], ],
true true
); );
} }
// join category if necessary // join category if necessary
if ('activity_categories_id' === $alias) { if ($alias === 'activity_categories_id') {
// add join only if needed // add join only if needed
if (!$this->checkJoinAlreadyDefined($qb->getDQLPart('join')['activity'], 'category')) { if (!$this->checkJoinAlreadyDefined($qb->getDQLPart('join')['activity'], 'category')) {
$qb->join('reasons.category', 'category'); $qb->join('reasons.category', 'category');
@@ -104,6 +88,24 @@ class ActivityReasonAggregator implements AggregatorInterface, ExportElementVali
} }
} }
/**
* Check if a join between Activity and another alias
*
* @param Join[] $joins
* @param string $alias the alias to search for
* @return boolean
*/
private function checkJoinAlreadyDefined(array $joins, $alias)
{
foreach ($joins as $join) {
if ($join->getAlias() === $alias) {
return true;
}
}
return false;
}
public function applyOn() public function applyOn()
{ {
return 'activity'; return 'activity';
@@ -117,36 +119,51 @@ class ActivityReasonAggregator implements AggregatorInterface, ExportElementVali
[ [
'choices' => [ 'choices' => [
'By reason' => 'reasons', 'By reason' => 'reasons',
'By category of reason' => 'categories', 'By category of reason' => 'categories'
], ],
'multiple' => false, 'multiple' => false,
'expanded' => true, 'expanded' => true,
'label' => "Reason's level", 'label' => "Reason's level"
] ]
); );
} }
public function validateForm($data, ExecutionContextInterface $context)
{
if ($data['level'] === null) {
$context
->buildViolation("The reasons's level should not be empty.")
->addViolation();
}
}
public function getTitle()
{
return 'Aggregate by activity reason';
}
public function addRole()
{
return new Role(ActivityStatsVoter::STATS);
}
public function getLabels($key, array $values, $data) public function getLabels($key, array $values, $data)
{ {
// for performance reason, we load data from db only once // for performance reason, we load data from db only once
switch ($data['level']) { switch ($data['level']) {
case 'reasons': case 'reasons':
$this->activityReasonRepository->findBy(['id' => $values]); $this->activityReasonRepository->findBy(['id' => $values]);
break; break;
case 'categories': case 'categories':
$this->activityReasonCategoryRepository->findBy(['id' => $values]); $this->activityReasonCategoryRepository->findBy(['id' => $values]);
break; break;
default: default:
throw new RuntimeException(sprintf("The level data '%s' is invalid.", $data['level'])); throw new \RuntimeException(sprintf("The level data '%s' is invalid.", $data['level']));
} }
return function($value) use ($data) { return function($value) use ($data) {
if ('_header' === $value) { if ($value === '_header') {
return 'reasons' === $data['level'] ? 'Group by reasons' : 'Group by categories of reason'; return $data['level'] === 'reasons' ? 'Group by reasons' : 'Group by categories of reason';
} }
switch ($data['level']) { switch ($data['level']) {
@@ -154,63 +171,32 @@ class ActivityReasonAggregator implements AggregatorInterface, ExportElementVali
$r = $this->activityReasonRepository->find($value); $r = $this->activityReasonRepository->find($value);
return sprintf( return sprintf(
'%s > %s', "%s > %s",
$this->translatableStringHelper->localize($r->getCategory()->getName()), $this->translatableStringHelper->localize($r->getCategory()->getName()),
$this->translatableStringHelper->localize($r->getName()) $this->translatableStringHelper->localize($r->getName())
); );
case 'categories': case 'categories':
$c = $this->activityReasonCategoryRepository->find($value); $c = $this->activityReasonCategoryRepository->find($value);
return $this->translatableStringHelper->localize($c->getName()); return $this->translatableStringHelper->localize($c->getName());
} }
}; };
} }
public function getQueryKeys($data) public function getQueryKeys($data)
{ {
// add select element // add select element
if ('reasons' === $data['level']) { if ($data['level'] === 'reasons') {
return ['activity_reasons_id']; return array('activity_reasons_id');
} }
if ('categories' === $data['level']) { if ($data['level'] === 'categories') {
return ['activity_categories_id']; return array ('activity_categories_id');
} }
throw new RuntimeException('The data provided are not recognised.'); throw new \RuntimeException('The data provided are not recognised.');
} }
public function getTitle()
{
return 'Aggregate by activity reason';
}
public function validateForm($data, ExecutionContextInterface $context)
{
if (null === $data['level']) {
$context
->buildViolation("The reasons's level should not be empty.")
->addViolation();
}
}
/**
* Check if a join between Activity and another alias.
*
* @param Join[] $joins
* @param string $alias the alias to search for
*
* @return bool
*/
private function checkJoinAlreadyDefined(array $joins, $alias)
{
foreach ($joins as $join) {
if ($join->getAlias() === $alias) {
return true;
}
}
return false;
}
} }

View File

@@ -1,34 +1,26 @@
<?php <?php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1); declare(strict_types=1);
namespace Chill\ActivityBundle\Export\Aggregator; namespace Chill\ActivityBundle\Export\Aggregator;
use Chill\ActivityBundle\Repository\ActivityTypeRepository; use Chill\ActivityBundle\Repository\ActivityTypeRepository;
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface; use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Closure;
use Doctrine\ORM\Query\Expr\Join;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormBuilderInterface;
use Doctrine\ORM\QueryBuilder;
use Chill\MainBundle\Export\AggregatorInterface;
use Symfony\Component\Security\Core\Role\Role; use Symfony\Component\Security\Core\Role\Role;
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
use Doctrine\ORM\Query\Expr\Join;
class ActivityTypeAggregator implements AggregatorInterface class ActivityTypeAggregator implements AggregatorInterface
{ {
public const KEY = 'activity_type_aggregator';
protected ActivityTypeRepository $activityTypeRepository; protected ActivityTypeRepository $activityTypeRepository;
protected TranslatableStringHelperInterface $translatableStringHelper; protected TranslatableStringHelperInterface $translatableStringHelper;
public const KEY = 'activity_type_aggregator';
public function __construct( public function __construct(
ActivityTypeRepository $activityTypeRepository, ActivityTypeRepository $activityTypeRepository,
TranslatableStringHelperInterface $translatableStringHelper TranslatableStringHelperInterface $translatableStringHelper
@@ -37,11 +29,6 @@ class ActivityTypeAggregator implements AggregatorInterface
$this->translatableStringHelper = $translatableStringHelper; $this->translatableStringHelper = $translatableStringHelper;
} }
public function addRole()
{
return new Role(ActivityStatsVoter::STATS);
}
public function alterQuery(QueryBuilder $qb, $data) public function alterQuery(QueryBuilder $qb, $data)
{ {
// add select element // add select element
@@ -51,6 +38,24 @@ class ActivityTypeAggregator implements AggregatorInterface
$qb->addGroupBy(self::KEY); $qb->addGroupBy(self::KEY);
} }
/**
* Check if a join between Activity and another alias
*
* @param Join[] $joins
* @param string $alias the alias to search for
* @return boolean
*/
private function checkJoinAlreadyDefined(array $joins, $alias)
{
foreach ($joins as $join) {
if ($join->getAlias() === $alias) {
return true;
}
}
return false;
}
public function applyOn() public function applyOn()
{ {
return 'activity'; return 'activity';
@@ -61,13 +66,23 @@ class ActivityTypeAggregator implements AggregatorInterface
// no form required for this aggregator // no form required for this aggregator
} }
public function getLabels($key, array $values, $data): Closure public function getTitle()
{
return 'Aggregate by activity type';
}
public function addRole()
{
return new Role(ActivityStatsVoter::STATS);
}
public function getLabels($key, array $values, $data): \Closure
{ {
// for performance reason, we load data from db only once // for performance reason, we load data from db only once
$this->activityTypeRepository->findBy(['id' => $values]); $this->activityTypeRepository->findBy(['id' => $values]);
return function($value): string { return function($value): string {
if ('_header' === $value) { if ($value === '_header') {
return 'Activity type'; return 'Activity type';
} }
@@ -82,27 +97,4 @@ class ActivityTypeAggregator implements AggregatorInterface
return [self::KEY]; return [self::KEY];
} }
public function getTitle()
{
return 'Aggregate by activity type';
}
/**
* Check if a join between Activity and another alias.
*
* @param Join[] $joins
* @param string $alias the alias to search for
*
* @return bool
*/
private function checkJoinAlreadyDefined(array $joins, $alias)
{
foreach ($joins as $join) {
if ($join->getAlias() === $alias) {
return true;
}
}
return false;
}
} }

View File

@@ -1,23 +1,13 @@
<?php <?php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Chill\ActivityBundle\Export\Aggregator; namespace Chill\ActivityBundle\Export\Aggregator;
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\MainBundle\Repository\UserRepository; use Chill\MainBundle\Repository\UserRepository;
use Closure;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormBuilderInterface;
use Doctrine\ORM\QueryBuilder;
use Chill\MainBundle\Export\AggregatorInterface;
use Symfony\Component\Security\Core\Role\Role; use Symfony\Component\Security\Core\Role\Role;
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
class ActivityUserAggregator implements AggregatorInterface class ActivityUserAggregator implements AggregatorInterface
{ {
@@ -55,13 +45,13 @@ class ActivityUserAggregator implements AggregatorInterface
// nothing to add // nothing to add
} }
public function getLabels($key, $values, $data): Closure public function getLabels($key, $values, $data): \Closure
{ {
// preload users at once // preload users at once
$this->userRepository->findBy(['id' => $values]); $this->userRepository->findBy(['id' => $values]);
return function($value) { return function($value) {
if ('_header' === $value) { if ($value === '_header') {
return 'activity user'; return 'activity user';
} }

View File

@@ -1,24 +1,16 @@
<?php <?php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1); declare(strict_types=1);
namespace Chill\ActivityBundle\Export\Export; namespace Chill\ActivityBundle\Export\Export;
use Chill\ActivityBundle\Repository\ActivityRepository; use Chill\ActivityBundle\Repository\ActivityRepository;
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
use Chill\MainBundle\Export\ExportInterface; use Chill\MainBundle\Export\ExportInterface;
use Chill\MainBundle\Export\FormatterInterface; use Chill\MainBundle\Export\FormatterInterface;
use Doctrine\ORM\Query;
use LogicException;
use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Security\Core\Role\Role; use Symfony\Component\Security\Core\Role\Role;
use Doctrine\ORM\Query;
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
class CountActivity implements ExportInterface class CountActivity implements ExportInterface
{ {
@@ -32,11 +24,7 @@ class CountActivity implements ExportInterface
public function buildForm(FormBuilderInterface $builder) public function buildForm(FormBuilderInterface $builder)
{ {
}
public function getAllowedFormattersTypes()
{
return [FormatterInterface::TYPE_TABULAR];
} }
public function getDescription() public function getDescription()
@@ -44,25 +32,6 @@ class CountActivity implements ExportInterface
return 'Count activities by various parameters.'; return 'Count activities by various parameters.';
} }
public function getLabels($key, array $values, $data)
{
if ('export_count_activity' !== $key) {
throw new LogicException("the key {$key} is not used by this export");
}
return static fn ($value) => '_header' === $value ? 'Number of activities' : $value;
}
public function getQueryKeys($data)
{
return ['export_count_activity'];
}
public function getResult($qb, $data)
{
return $qb->getQuery()->getResult(Query::HYDRATE_SCALAR);
}
public function getTitle() public function getTitle()
{ {
return 'Count activities'; return 'Count activities';
@@ -73,7 +42,7 @@ class CountActivity implements ExportInterface
return 'activity'; return 'activity';
} }
public function initiateQuery(array $requiredModifiers, array $acl, array $data = []) public function initiateQuery(array $requiredModifiers, array $acl, array $data = array())
{ {
$centers = array_map(static fn($el) => $el['center'], $acl); $centers = array_map(static fn($el) => $el['center'], $acl);
@@ -90,13 +59,38 @@ class CountActivity implements ExportInterface
return $qb; return $qb;
} }
public function supportsModifiers()
{
return ['person', 'activity'];
}
public function requiredRole() public function requiredRole()
{ {
return new Role(ActivityStatsVoter::STATS); return new Role(ActivityStatsVoter::STATS);
} }
public function supportsModifiers() public function getAllowedFormattersTypes()
{ {
return ['person', 'activity']; return [FormatterInterface::TYPE_TABULAR];
} }
public function getLabels($key, array $values, $data)
{
if ($key !== 'export_count_activity') {
throw new \LogicException("the key $key is not used by this export");
}
return static fn($value) => $value === '_header' ? 'Number of activities' : $value;
}
public function getQueryKeys($data)
{
return ['export_count_activity'];
}
public function getResult($qb, $data)
{
return $qb->getQuery()->getResult(Query::HYDRATE_SCALAR);
}
} }

View File

@@ -1,41 +1,35 @@
<?php <?php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1); declare(strict_types=1);
namespace Chill\ActivityBundle\Export\Export; namespace Chill\ActivityBundle\Export\Export;
use Chill\ActivityBundle\Entity\ActivityReason;
use Chill\ActivityBundle\Repository\ActivityRepository; use Chill\ActivityBundle\Repository\ActivityRepository;
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
use Chill\MainBundle\Export\FormatterInterface;
use Chill\MainBundle\Export\ListInterface; use Chill\MainBundle\Export\ListInterface;
use Chill\ActivityBundle\Entity\ActivityReason;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface; use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use DateTime;
use Doctrine\DBAL\Exception\InvalidArgumentException; use Doctrine\DBAL\Exception\InvalidArgumentException;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Query;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Security\Core\Role\Role; use Symfony\Component\Security\Core\Role\Role;
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
use Symfony\Component\Form\FormBuilderInterface;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Translation\TranslatorInterface;
use Symfony\Component\Validator\Constraints\Callback; use Symfony\Component\Validator\Constraints\Callback;
use Doctrine\ORM\Query;
use Chill\MainBundle\Export\FormatterInterface;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Validator\Context\ExecutionContextInterface; use Symfony\Component\Validator\Context\ExecutionContextInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
use function array_key_exists;
use function count;
use function in_array;
class ListActivity implements ListInterface class ListActivity implements ListInterface
{ {
private ActivityRepository $activityRepository;
protected EntityManagerInterface $entityManager; protected EntityManagerInterface $entityManager;
protected TranslatorInterface $translator;
protected TranslatableStringHelperInterface $translatableStringHelper;
protected array $fields = [ protected array $fields = [
'id', 'id',
'date', 'date',
@@ -47,15 +41,9 @@ class ListActivity implements ListInterface
'type_name', 'type_name',
'person_firstname', 'person_firstname',
'person_lastname', 'person_lastname',
'person_id', 'person_id'
]; ];
protected TranslatableStringHelperInterface $translatableStringHelper;
protected TranslatorInterface $translator;
private ActivityRepository $activityRepository;
public function __construct( public function __construct(
EntityManagerInterface $em, EntityManagerInterface $em,
TranslatorInterface $translator, TranslatorInterface $translator,
@@ -76,15 +64,16 @@ class ListActivity implements ListInterface
'choices' => array_combine($this->fields, $this->fields), 'choices' => array_combine($this->fields, $this->fields),
'label' => 'Fields to include in export', 'label' => 'Fields to include in export',
'constraints' => [new Callback([ 'constraints' => [new Callback([
'callback' => static function ($selected, ExecutionContextInterface $context) { 'callback' => function($selected, ExecutionContextInterface $context) {
if (count($selected) === 0) { if (count($selected) === 0) {
$context->buildViolation('You must select at least one element') $context->buildViolation('You must select at least one element')
->atPath('fields') ->atPath('fields')
->addViolation(); ->addViolation();
} }
}, }
])], ])]
]); ]);
} }
public function getAllowedFormattersTypes() public function getAllowedFormattersTypes()
@@ -94,43 +83,43 @@ class ListActivity implements ListInterface
public function getDescription() public function getDescription()
{ {
return 'List activities'; return "List activities";
} }
public function getLabels($key, array $values, $data) public function getLabels($key, array $values, $data)
{ {
switch ($key) { switch ($key)
{
case 'date' : case 'date' :
return static function($value) { return static function($value) {
if ('_header' === $value) { if ($value === '_header') {
return 'date'; return 'date';
} }
$date = DateTime::createFromFormat('Y-m-d H:i:s', $value); $date = \DateTime::createFromFormat('Y-m-d H:i:s', $value);
return $date->format('d-m-Y'); return $date->format('d-m-Y');
}; };
case 'attendee': case 'attendee':
return static function($value) { return static function($value) {
if ('_header' === $value) { if ($value === '_header') {
return 'attendee'; return 'attendee';
} }
return $value ? 1 : 0; return $value ? 1 : 0;
}; };
case 'list_reasons' : case 'list_reasons' :
$activityRepository = $this->activityRepository; $activityRepository = $this->activityRepository;
return function($value) use ($activityRepository): string { return function($value) use ($activityRepository): string {
if ('_header' === $value) { if ($value === '_header') {
return 'activity reasons'; return 'activity reasons';
} }
$activity = $activityRepository->find($value); $activity = $activityRepository->find($value);
return implode(', ', array_map(function (ActivityReason $r) { return implode(", ", array_map(function(ActivityReason $r) {
return '"'. return '"'.
$this->translatableStringHelper->localize($r->getCategory()->getName()) $this->translatableStringHelper->localize($r->getCategory()->getName())
.' > '. .' > '.
@@ -138,28 +127,25 @@ class ListActivity implements ListInterface
.'"'; .'"';
}, $activity->getReasons()->toArray())); }, $activity->getReasons()->toArray()));
}; };
case 'circle_name' : case 'circle_name' :
return function($value) { return function($value) {
if ('_header' === $value) { if ($value === '_header') {
return 'circle'; return 'circle';
} }
return $this->translatableStringHelper->localize(json_decode($value, true)); return $this->translatableStringHelper->localize(json_decode($value, true));
}; };
case 'type_name' : case 'type_name' :
return function($value) { return function($value) {
if ('_header' === $value) { if ($value === '_header') {
return 'activity type'; return 'activity type';
} }
return $this->translatableStringHelper->localize(json_decode($value, true)); return $this->translatableStringHelper->localize(json_decode($value, true));
}; };
default: default:
return static function($value) use ($key) { return static function($value) use ($key) {
if ('_header' === $value) { if ($value === '_header') {
return $key; return $key;
} }
@@ -190,12 +176,10 @@ class ListActivity implements ListInterface
public function initiateQuery(array $requiredModifiers, array $acl, array $data = []) public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
{ {
$centers = array_map(static function ($el) { $centers = array_map(function($el) { return $el['center']; }, $acl);
return $el['center'];
}, $acl);
// throw an error if any fields are present // throw an error if any fields are present
if (!array_key_exists('fields', $data)) { if (!\array_key_exists('fields', $data)) {
throw new InvalidArgumentException('Any fields have been checked.'); throw new InvalidArgumentException('Any fields have been checked.');
} }
@@ -209,58 +193,42 @@ class ListActivity implements ListInterface
->setParameter('authorized_centers', $centers); ->setParameter('authorized_centers', $centers);
foreach ($this->fields as $f) { foreach ($this->fields as $f) {
if (in_array($f, $data['fields'], true)) { if (in_array($f, $data['fields'])) {
switch ($f) { switch ($f) {
case 'id': case 'id':
$qb->addSelect('activity.id AS id'); $qb->addSelect('activity.id AS id');
break; break;
case 'person_firstname': case 'person_firstname':
$qb->addSelect('person.firstName AS person_firstname'); $qb->addSelect('person.firstName AS person_firstname');
break; break;
case 'person_lastname': case 'person_lastname':
$qb->addSelect('person.lastName AS person_lastname'); $qb->addSelect('person.lastName AS person_lastname');
break; break;
case 'person_id': case 'person_id':
$qb->addSelect('person.id AS person_id'); $qb->addSelect('person.id AS person_id');
break; break;
case 'user_username': case 'user_username':
$qb->join('activity.user', 'user'); $qb->join('activity.user', 'user');
$qb->addSelect('user.username AS user_username'); $qb->addSelect('user.username AS user_username');
break; break;
case 'circle_name': case 'circle_name':
$qb->join('activity.scope', 'circle'); $qb->join('activity.scope', 'circle');
$qb->addSelect('circle.name AS circle_name'); $qb->addSelect('circle.name AS circle_name');
break; break;
case 'type_name': case 'type_name':
$qb->join('activity.type', 'type'); $qb->join('activity.type', 'type');
$qb->addSelect('type.name AS type_name'); $qb->addSelect('type.name AS type_name');
break; break;
case 'list_reasons': case 'list_reasons':
// this is a trick... The reasons is filled with the // this is a trick... The reasons is filled with the
// activity id which will be used to load reasons // activity id which will be used to load reasons
$qb->addSelect('activity.id AS list_reasons'); $qb->addSelect('activity.id AS list_reasons');
break; break;
default: default:
$qb->addSelect(sprintf('activity.%s as %s', $f, $f)); $qb->addSelect(sprintf('activity.%s as %s', $f, $f));
break; break;
} }
} }
} }
@@ -276,4 +244,5 @@ class ListActivity implements ListInterface
{ {
return ['activity', 'person']; return ['activity', 'person'];
} }
} }

View File

@@ -1,24 +1,16 @@
<?php <?php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1); declare(strict_types=1);
namespace Chill\ActivityBundle\Export\Export; namespace Chill\ActivityBundle\Export\Export;
use Chill\ActivityBundle\Repository\ActivityRepository; use Chill\ActivityBundle\Repository\ActivityRepository;
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
use Chill\MainBundle\Export\ExportInterface; use Chill\MainBundle\Export\ExportInterface;
use Chill\MainBundle\Export\FormatterInterface; use Chill\MainBundle\Export\FormatterInterface;
use Doctrine\ORM\Query;
use LogicException;
use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Security\Core\Role\Role; use Symfony\Component\Security\Core\Role\Role;
use Doctrine\ORM\Query;
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
/** /**
* This export allow to compute stats on activity duration. * This export allow to compute stats on activity duration.
@@ -27,6 +19,8 @@ use Symfony\Component\Security\Core\Role\Role;
*/ */
class StatActivityDuration implements ExportInterface class StatActivityDuration implements ExportInterface
{ {
private ActivityRepository $activityRepository;
public const SUM = 'sum'; public const SUM = 'sum';
/** /**
@@ -34,8 +28,6 @@ class StatActivityDuration implements ExportInterface
*/ */
protected string $action; protected string $action;
private ActivityRepository $activityRepository;
/** /**
* @param string $action the stat to perform * @param string $action the stat to perform
*/ */
@@ -49,46 +41,22 @@ class StatActivityDuration implements ExportInterface
public function buildForm(FormBuilderInterface $builder) public function buildForm(FormBuilderInterface $builder)
{ {
}
public function getAllowedFormattersTypes()
{
return [FormatterInterface::TYPE_TABULAR];
} }
public function getDescription() public function getDescription()
{ {
if (self::SUM === $this->action) { if ($this->action === self::SUM) {
return 'Sum activities duration by various parameters.'; return 'Sum activities duration by various parameters.';
} }
} }
public function getLabels($key, array $values, $data)
{
if ('export_stat_activity' !== $key) {
throw new LogicException(sprintf('The key %s is not used by this export', $key));
}
$header = self::SUM === $this->action ? 'Sum of activities duration' : false;
return static fn (string $value) => '_header' === $value ? $header : $value;
}
public function getQueryKeys($data)
{
return ['export_stat_activity'];
}
public function getResult($qb, $data)
{
return $qb->getQuery()->getResult(Query::HYDRATE_SCALAR);
}
public function getTitle() public function getTitle()
{ {
if (self::SUM === $this->action) { if ($this->action === self::SUM) {
return 'Sum activity duration'; return 'Sum activity duration';
} }
} }
public function getType() public function getType()
@@ -107,7 +75,7 @@ class StatActivityDuration implements ExportInterface
$select = null; $select = null;
if (self::SUM === $this->action) { if ($this->action === self::SUM) {
$select = 'SUM(activity.durationTime) AS export_stat_activity'; $select = 'SUM(activity.durationTime) AS export_stat_activity';
} }
@@ -118,13 +86,40 @@ class StatActivityDuration implements ExportInterface
->setParameter(':centers', $centers); ->setParameter(':centers', $centers);
} }
public function supportsModifiers()
{
return ['person', 'activity'];
}
public function requiredRole() public function requiredRole()
{ {
return new Role(ActivityStatsVoter::STATS); return new Role(ActivityStatsVoter::STATS);
} }
public function supportsModifiers() public function getAllowedFormattersTypes()
{ {
return ['person', 'activity']; return [FormatterInterface::TYPE_TABULAR];
} }
public function getLabels($key, array $values, $data)
{
if ($key !== 'export_stat_activity') {
throw new \LogicException(sprintf('The key %s is not used by this export', $key));
}
$header = $this->action === self::SUM ? 'Sum of activities duration' : false;
return static fn(string $value) => $value === '_header' ? $header : $value;
}
public function getQueryKeys($data)
{
return ['export_stat_activity'];
}
public function getResult($qb, $data)
{
return $qb->getQuery()->getResult(Query::HYDRATE_SCALAR);
}
} }

View File

@@ -1,33 +1,25 @@
<?php <?php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1); declare(strict_types=1);
namespace Chill\ActivityBundle\Export\Filter; namespace Chill\ActivityBundle\Export\Filter;
use Chill\MainBundle\Export\FilterInterface; use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Form\Type\Export\FilterType;
use DateTime;
use Doctrine\ORM\Query\Expr;
use Doctrine\ORM\QueryBuilder; use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\Extension\Core\Type\DateType;
use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormError;
use Symfony\Component\Form\FormEvent; use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents; use Symfony\Component\Form\FormEvents;
use Symfony\Contracts\Translation\TranslatorInterface; use Symfony\Component\Form\Extension\Core\Type\DateType;
use Symfony\Component\Form\FormError;
use Chill\MainBundle\Form\Type\Export\FilterType;
use Doctrine\ORM\Query\Expr;
use Symfony\Component\Translation\TranslatorInterface;
class ActivityDateFilter implements FilterInterface class ActivityDateFilter implements FilterInterface
{ {
protected TranslatorInterface $translator; protected TranslatorInterface $translator;
public function __construct(TranslatorInterface $translator) function __construct(TranslatorInterface $translator)
{ {
$this->translator = $translator; $this->translator = $translator;
} }
@@ -40,11 +32,8 @@ class ActivityDateFilter implements FilterInterface
public function alterQuery(QueryBuilder $qb, $data) public function alterQuery(QueryBuilder $qb, $data)
{ {
$where = $qb->getDQLPart('where'); $where = $qb->getDQLPart('where');
$clause = $qb->expr()->between( $clause = $qb->expr()->between('activity.date', ':date_from',
'activity.date', ':date_to');
':date_from',
':date_to'
);
if ($where instanceof Expr\Andx) { if ($where instanceof Expr\Andx) {
$where->add($clause); $where->add($clause);
@@ -69,7 +58,7 @@ class ActivityDateFilter implements FilterInterface
DateType::class, DateType::class,
[ [
'label' => 'Activities after this date', 'label' => 'Activities after this date',
'data' => new DateTime(), 'data' => new \DateTime(),
'attr' => ['class' => 'datepicker'], 'attr' => ['class' => 'datepicker'],
'widget'=> 'single_text', 'widget'=> 'single_text',
'format' => 'dd-MM-yyyy', 'format' => 'dd-MM-yyyy',
@@ -81,7 +70,7 @@ class ActivityDateFilter implements FilterInterface
DateType::class, DateType::class,
[ [
'label' => 'Activities before this date', 'label' => 'Activities before this date',
'data' => new DateTime(), 'data' => new \DateTime(),
'attr' => ['class' => 'datepicker'], 'attr' => ['class' => 'datepicker'],
'widget'=> 'single_text', 'widget'=> 'single_text',
'format' => 'dd-MM-yyyy', 'format' => 'dd-MM-yyyy',
@@ -89,41 +78,38 @@ class ActivityDateFilter implements FilterInterface
); );
$builder->addEventListener(FormEvents::POST_SUBMIT, function(FormEvent $event) { $builder->addEventListener(FormEvents::POST_SUBMIT, function(FormEvent $event) {
/** @var \Symfony\Component\Form\FormInterface $filterForm */ /* @var $filterForm \Symfony\Component\Form\FormInterface */
$filterForm = $event->getForm()->getParent(); $filterForm = $event->getForm()->getParent();
$enabled = $filterForm->get(FilterType::ENABLED_FIELD)->getData(); $enabled = $filterForm->get(FilterType::ENABLED_FIELD)->getData();
if (true === $enabled) { if ($enabled === true) {
// if the filter is enabled, add some validation // if the filter is enabled, add some validation
$form = $event->getForm(); $form = $event->getForm();
$date_from = $form->get('date_from')->getData(); $date_from = $form->get('date_from')->getData();
$date_to = $form->get('date_to')->getData(); $date_to = $form->get('date_to')->getData();
// check that fields are not empty // check that fields are not empty
if (null === $date_from) { if ($date_from === null) {
$form->get('date_from')->addError(new FormError( $form->get('date_from')->addError(new FormError(
$this->translator->trans('This field ' $this->translator->trans('This field '
. 'should not be empty') . 'should not be empty')));
));
} }
if ($date_to === null) {
if (null === $date_to) {
$form->get('date_to')->addError(new FormError( $form->get('date_to')->addError(new FormError(
$this->translator->trans('This field ' $this->translator->trans('This field '
. 'should not be empty') . 'should not be empty')));
));
} }
// check that date_from is before date_to // check that date_from is before date_to
if ( if (
(null !== $date_from && null !== $date_to) ($date_from !== null && $date_to !== null)
&& $date_from >= $date_to &&
$date_from >= $date_to
) { ) {
$form->get('date_to')->addError(new FormError( $form->get('date_to')->addError(new FormError(
$this->translator->trans('This date should be after ' $this->translator->trans('This date should be after '
. 'the date given in "Implied in an activity after ' . 'the date given in "Implied in an activity after '
. 'this date" field') . 'this date" field')));
));
} }
} }
}); });
@@ -135,8 +121,8 @@ class ActivityDateFilter implements FilterInterface
'Filtered by date of activity: only between %date_from% and %date_to%', 'Filtered by date of activity: only between %date_from% and %date_to%',
[ [
'%date_from%' => $data['date_from']->format('d-m-Y'), '%date_from%' => $data['date_from']->format('d-m-Y'),
'%date_to%' => $data['date_to']->format('d-m-Y'), '%date_to%' => $data['date_to']->format('d-m-Y')
], ]
]; ];
} }
@@ -144,4 +130,5 @@ class ActivityDateFilter implements FilterInterface
{ {
return 'Filtered by date activity'; return 'Filtered by date activity';
} }
} }

View File

@@ -1,40 +1,30 @@
<?php <?php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1); declare(strict_types=1);
namespace Chill\ActivityBundle\Export\Filter; namespace Chill\ActivityBundle\Export\Filter;
use Chill\ActivityBundle\Entity\ActivityReason;
use Chill\ActivityBundle\Repository\ActivityReasonRepository; use Chill\ActivityBundle\Repository\ActivityReasonRepository;
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
use Chill\MainBundle\Export\ExportElementValidatedInterface;
use Chill\MainBundle\Export\FilterInterface; use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface; use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Doctrine\ORM\Query\Expr;
use Doctrine\ORM\Query\Expr\Join;
use Doctrine\ORM\QueryBuilder; use Doctrine\ORM\QueryBuilder;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Chill\ActivityBundle\Entity\ActivityReason;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Doctrine\ORM\Query\Expr;
use Symfony\Component\Security\Core\Role\Role; use Symfony\Component\Security\Core\Role\Role;
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
use Doctrine\ORM\Query\Expr\Join;
use Symfony\Component\Validator\Context\ExecutionContextInterface; use Symfony\Component\Validator\Context\ExecutionContextInterface;
use Chill\MainBundle\Export\ExportElementValidatedInterface;
use function array_key_exists; class ActivityReasonFilter implements FilterInterface, ExportElementValidatedInterface
use function count;
class ActivityReasonFilter implements ExportElementValidatedInterface, FilterInterface
{ {
protected ActivityReasonRepository $activityReasonRepository;
protected TranslatableStringHelperInterface $translatableStringHelper; protected TranslatableStringHelperInterface $translatableStringHelper;
protected ActivityReasonRepository $activityReasonRepository;
public function __construct( public function __construct(
TranslatableStringHelper $helper, TranslatableStringHelper $helper,
ActivityReasonRepository $activityReasonRepository ActivityReasonRepository $activityReasonRepository
@@ -43,11 +33,6 @@ class ActivityReasonFilter implements ExportElementValidatedInterface, FilterInt
$this->activityReasonRepository = $activityReasonRepository; $this->activityReasonRepository = $activityReasonRepository;
} }
public function addRole()
{
return new Role(ActivityStatsVoter::STATS);
}
public function alterQuery(QueryBuilder $qb, $data) public function alterQuery(QueryBuilder $qb, $data)
{ {
$where = $qb->getDQLPart('where'); $where = $qb->getDQLPart('where');
@@ -56,15 +41,16 @@ class ActivityReasonFilter implements ExportElementValidatedInterface, FilterInt
//dump($join); //dump($join);
// add a join to reasons only if needed // add a join to reasons only if needed
if ( if (
( (array_key_exists('activity', $join)
array_key_exists('activity', $join) &&
&& !$this->checkJoinAlreadyDefined($join['activity'], 'reasons') !$this->checkJoinAlreadyDefined($join['activity'], 'reasons')
) )
|| (!array_key_exists('activity', $join)) ||
(! array_key_exists('activity', $join))
) { ) {
$qb->add( $qb->add(
'join', 'join',
['activity' => new Join(Join::INNER_JOIN, 'activity.reasons', 'reasons')], array('activity' => new Join(Join::INNER_JOIN, 'activity.reasons', 'reasons')),
true true
); );
} }
@@ -79,6 +65,23 @@ class ActivityReasonFilter implements ExportElementValidatedInterface, FilterInt
$qb->setParameter('selected_activity_reasons', $data['reasons']); $qb->setParameter('selected_activity_reasons', $data['reasons']);
} }
/**
* Check if a join between Activity and Reason is already defined
*
* @param Join[] $joins
* @return boolean
*/
private function checkJoinAlreadyDefined(array $joins, $alias): bool
{
foreach ($joins as $join) {
if ($join->getAlias() === $alias) {
return true;
}
}
return false;
}
public function applyOn() public function applyOn()
{ {
return 'activity'; return 'activity';
@@ -91,24 +94,17 @@ class ActivityReasonFilter implements ExportElementValidatedInterface, FilterInt
'choice_label' => fn(ActivityReason $reason) => $this->translatableStringHelper->localize($reason->getName()), 'choice_label' => fn(ActivityReason $reason) => $this->translatableStringHelper->localize($reason->getName()),
'group_by' => fn(ActivityReason $reason) => $this->translatableStringHelper->localize($reason->getCategory()->getName()), 'group_by' => fn(ActivityReason $reason) => $this->translatableStringHelper->localize($reason->getCategory()->getName()),
'multiple' => true, 'multiple' => true,
'expanded' => false, 'expanded' => false
]); ]);
} }
public function describeAction($data, $format = 'string') public function validateForm($data, ExecutionContextInterface $context)
{ {
// collect all the reasons'name used in this filter in one array if ($data['reasons'] === null || count($data['reasons']) === 0) {
$reasonsNames = array_map( $context
fn (ActivityReason $r): string => '"' . $this->translatableStringHelper->localize($r->getName()) . '"', ->buildViolation('At least one reason must be chosen')
$this->activityReasonRepository->findBy(['id' => $data['reasons']->toArray()]) ->addViolation();
); }
return [
'Filtered by reasons: only %list%',
[
'%list%' => implode(', ', $reasonsNames),
],
];
} }
public function getTitle() public function getTitle()
@@ -116,29 +112,24 @@ class ActivityReasonFilter implements ExportElementValidatedInterface, FilterInt
return 'Filter by reason'; return 'Filter by reason';
} }
public function validateForm($data, ExecutionContextInterface $context) public function addRole()
{ {
if (null === $data['reasons'] || count($data['reasons']) === 0) { return new Role(ActivityStatsVoter::STATS);
$context
->buildViolation('At least one reason must be chosen')
->addViolation();
}
} }
/** public function describeAction($data, $format = 'string')
* Check if a join between Activity and Reason is already defined.
*
* @param Join[] $joins
* @param mixed $alias
*/
private function checkJoinAlreadyDefined(array $joins, $alias): bool
{ {
foreach ($joins as $join) { // collect all the reasons'name used in this filter in one array
if ($join->getAlias() === $alias) { $reasonsNames = array_map(
return true; fn(ActivityReason $r): string => '"' . $this->translatableStringHelper->localize($r->getName()) . '"',
} $this->activityReasonRepository->findBy(array('id' => $data['reasons']->toArray()))
} );
return false; return [
'Filtered by reasons: only %list%',
[
'%list%' => implode(", ", $reasonsNames),
]
];
} }
} }

View File

@@ -1,38 +1,29 @@
<?php <?php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1); declare(strict_types=1);
namespace Chill\ActivityBundle\Export\Filter; namespace Chill\ActivityBundle\Export\Filter;
use Chill\ActivityBundle\Entity\ActivityType;
use Chill\ActivityBundle\Repository\ActivityTypeRepository; use Chill\ActivityBundle\Repository\ActivityTypeRepository;
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
use Chill\MainBundle\Export\ExportElementValidatedInterface;
use Chill\MainBundle\Export\FilterInterface; use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface; use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Doctrine\ORM\Query\Expr;
use Doctrine\ORM\Query\Expr\Join;
use Doctrine\ORM\QueryBuilder; use Doctrine\ORM\QueryBuilder;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Doctrine\ORM\Query\Expr;
use Symfony\Component\Security\Core\Role\Role; use Symfony\Component\Security\Core\Role\Role;
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
use Doctrine\ORM\Query\Expr\Join;
use Symfony\Component\Validator\Context\ExecutionContextInterface; use Symfony\Component\Validator\Context\ExecutionContextInterface;
use Chill\MainBundle\Export\ExportElementValidatedInterface;
use Chill\ActivityBundle\Entity\ActivityType;
use function count; class ActivityTypeFilter implements FilterInterface, ExportElementValidatedInterface
class ActivityTypeFilter implements ExportElementValidatedInterface, FilterInterface
{ {
protected ActivityTypeRepository $activityTypeRepository;
protected TranslatableStringHelperInterface $translatableStringHelper; protected TranslatableStringHelperInterface $translatableStringHelper;
protected ActivityTypeRepository $activityTypeRepository;
public function __construct( public function __construct(
TranslatableStringHelperInterface $translatableStringHelper, TranslatableStringHelperInterface $translatableStringHelper,
ActivityTypeRepository $activityTypeRepository ActivityTypeRepository $activityTypeRepository
@@ -41,11 +32,6 @@ class ActivityTypeFilter implements ExportElementValidatedInterface, FilterInter
$this->activityTypeRepository = $activityTypeRepository; $this->activityTypeRepository = $activityTypeRepository;
} }
public function addRole()
{
return new Role(ActivityStatsVoter::STATS);
}
public function alterQuery(QueryBuilder $qb, $data) public function alterQuery(QueryBuilder $qb, $data)
{ {
$where = $qb->getDQLPart('where'); $where = $qb->getDQLPart('where');
@@ -61,6 +47,23 @@ class ActivityTypeFilter implements ExportElementValidatedInterface, FilterInter
$qb->setParameter('selected_activity_types', $data['types']); $qb->setParameter('selected_activity_types', $data['types']);
} }
/**
* Check if a join between Activity and Reason is already defined
*
* @param Join[] $joins
* @return boolean
*/
private function checkJoinAlreadyDefined(array $joins, $alias)
{
foreach ($joins as $join) {
if ($join->getAlias() === $alias) {
return true;
}
}
return false;
}
public function applyOn() public function applyOn()
{ {
return 'activity'; return 'activity';
@@ -75,11 +78,30 @@ class ActivityTypeFilter implements ExportElementValidatedInterface, FilterInter
'class' => ActivityType::class, 'class' => ActivityType::class,
'choice_label' => fn(ActivityType $type) => $this->translatableStringHelper->localize($type->getName()), 'choice_label' => fn(ActivityType $type) => $this->translatableStringHelper->localize($type->getName()),
'multiple' => true, 'multiple' => true,
'expanded' => false, 'expanded' => false
] ]
); );
} }
public function validateForm($data, ExecutionContextInterface $context)
{
if ($data['types'] === null || count($data['types']) === 0) {
$context
->buildViolation('At least one type must be chosen')
->addViolation();
}
}
public function getTitle()
{
return 'Filter by activity type';
}
public function addRole()
{
return new Role(ActivityStatsVoter::STATS);
}
public function describeAction($data, $format = 'string') public function describeAction($data, $format = 'string')
{ {
// collect all the reasons'name used in this filter in one array // collect all the reasons'name used in this filter in one array
@@ -91,41 +113,8 @@ class ActivityTypeFilter implements ExportElementValidatedInterface, FilterInter
return [ return [
'Filtered by activity type: only %list%', 'Filtered by activity type: only %list%',
[ [
'%list%' => implode(', ', $reasonsNames), '%list%' => implode(", ", $reasonsNames),
], ]
]; ];
} }
public function getTitle()
{
return 'Filter by activity type';
}
public function validateForm($data, ExecutionContextInterface $context)
{
if (null === $data['types'] || count($data['types']) === 0) {
$context
->buildViolation('At least one type must be chosen')
->addViolation();
}
}
/**
* Check if a join between Activity and Reason is already defined.
*
* @param Join[] $joins
* @param mixed $alias
*
* @return bool
*/
private function checkJoinAlreadyDefined(array $joins, $alias)
{
foreach ($joins as $join) {
if ($join->getAlias() === $alias) {
return true;
}
}
return false;
}
} }

View File

@@ -1,45 +1,35 @@
<?php <?php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1); declare(strict_types=1);
namespace Chill\ActivityBundle\Export\Filter; namespace Chill\ActivityBundle\Export\Filter;
use Chill\ActivityBundle\Entity\ActivityReason;
use Chill\ActivityBundle\Repository\ActivityReasonRepository; use Chill\ActivityBundle\Repository\ActivityReasonRepository;
use Chill\MainBundle\Export\ExportElementValidatedInterface;
use Chill\MainBundle\Export\FilterInterface; use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Form\Type\Export\FilterType;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface; use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Chill\PersonBundle\Export\Declarations;
use DateTime;
use Doctrine\ORM\Query\Expr;
use Doctrine\ORM\QueryBuilder; use Doctrine\ORM\QueryBuilder;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\Extension\Core\Type\DateType;
use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormError;
use Symfony\Component\Form\FormEvent; use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents; use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\Extension\Core\Type\DateType;
use Symfony\Component\Form\FormError;
use Chill\MainBundle\Form\Type\Export\FilterType;
use Doctrine\ORM\Query\Expr;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Chill\ActivityBundle\Entity\ActivityReason;
use Chill\PersonBundle\Export\Declarations;
use Symfony\Component\Form\FormInterface; use Symfony\Component\Form\FormInterface;
use Symfony\Component\Translation\TranslatorInterface;
use Symfony\Component\Validator\Context\ExecutionContextInterface; use Symfony\Component\Validator\Context\ExecutionContextInterface;
use Symfony\Contracts\Translation\TranslatorInterface; use Chill\MainBundle\Export\ExportElementValidatedInterface;
use function count; class PersonHavingActivityBetweenDateFilter implements FilterInterface, ExportElementValidatedInterface
class PersonHavingActivityBetweenDateFilter implements ExportElementValidatedInterface, FilterInterface
{ {
protected ActivityReasonRepository $activityReasonRepository;
protected TranslatableStringHelperInterface $translatableStringHelper; protected TranslatableStringHelperInterface $translatableStringHelper;
protected ActivityReasonRepository $activityReasonRepository;
protected TranslatorInterface $translator; protected TranslatorInterface $translator;
public function __construct( public function __construct(
@@ -76,8 +66,7 @@ class PersonHavingActivityBetweenDateFilter implements ExportElementValidatedInt
$sqb->andWhere( $sqb->andWhere(
$sqb->expr()->in( $sqb->expr()->in(
'reasons_person_having_activity', 'reasons_person_having_activity', ':person_having_activity_reasons'
':person_having_activity_reasons'
) )
); );
@@ -91,14 +80,10 @@ class PersonHavingActivityBetweenDateFilter implements ExportElementValidatedInt
} }
$qb->add('where', $where); $qb->add('where', $where);
$qb->setParameter( $qb->setParameter('person_having_activity_between_date_from',
'person_having_activity_between_date_from', $data['date_from']);
$data['date_from'] $qb->setParameter('person_having_activity_between_date_to',
); $data['date_to']);
$qb->setParameter(
'person_having_activity_between_date_to',
$data['date_to']
);
$qb->setParameter('person_having_activity_reasons', $data['reasons']); $qb->setParameter('person_having_activity_reasons', $data['reasons']);
} }
@@ -111,7 +96,7 @@ class PersonHavingActivityBetweenDateFilter implements ExportElementValidatedInt
{ {
$builder->add('date_from', DateType::class, [ $builder->add('date_from', DateType::class, [
'label' => 'Implied in an activity after this date', 'label' => 'Implied in an activity after this date',
'data' => new DateTime(), 'data' => new \DateTime(),
'attr' => ['class' => 'datepicker'], 'attr' => ['class' => 'datepicker'],
'widget'=> 'single_text', 'widget'=> 'single_text',
'format' => 'dd-MM-yyyy', 'format' => 'dd-MM-yyyy',
@@ -119,7 +104,7 @@ class PersonHavingActivityBetweenDateFilter implements ExportElementValidatedInt
$builder->add('date_to', DateType::class, [ $builder->add('date_to', DateType::class, [
'label' => 'Implied in an activity before this date', 'label' => 'Implied in an activity before this date',
'data' => new DateTime(), 'data' => new \DateTime(),
'attr' => ['class' => 'datepicker'], 'attr' => ['class' => 'datepicker'],
'widget'=> 'single_text', 'widget'=> 'single_text',
'format' => 'dd-MM-yyyy', 'format' => 'dd-MM-yyyy',
@@ -132,50 +117,55 @@ class PersonHavingActivityBetweenDateFilter implements ExportElementValidatedInt
'data' => $this->activityReasonRepository->findAll(), 'data' => $this->activityReasonRepository->findAll(),
'multiple' => true, 'multiple' => true,
'expanded' => false, 'expanded' => false,
'label' => 'Activity reasons for those activities', 'label' => 'Activity reasons for those activities'
]); ]);
$builder->addEventListener(FormEvents::POST_SUBMIT, function(FormEvent $event) { $builder->addEventListener(FormEvents::POST_SUBMIT, function(FormEvent $event) {
/** @var FormInterface $filterForm */ /* @var FormInterface $filterForm */
$filterForm = $event->getForm()->getParent(); $filterForm = $event->getForm()->getParent();
$enabled = $filterForm->get(FilterType::ENABLED_FIELD)->getData(); $enabled = $filterForm->get(FilterType::ENABLED_FIELD)->getData();
if (true === $enabled) { if ($enabled === true) {
// if the filter is enabled, add some validation // if the filter is enabled, add some validation
$form = $event->getForm(); $form = $event->getForm();
$date_from = $form->get('date_from')->getData(); $date_from = $form->get('date_from')->getData();
$date_to = $form->get('date_to')->getData(); $date_to = $form->get('date_to')->getData();
// check that fields are not empty // check that fields are not empty
if (null === $date_from) { if ($date_from === null) {
$form->get('date_from')->addError(new FormError( $form->get('date_from')->addError(new FormError(
$this->translator->trans('This field ' $this->translator->trans('This field '
. 'should not be empty') . 'should not be empty')));
));
} }
if ($date_to === null) {
if (null === $date_to) {
$form->get('date_to')->addError(new FormError( $form->get('date_to')->addError(new FormError(
$this->translator->trans('This field ' $this->translator->trans('This field '
. 'should not be empty') . 'should not be empty')));
));
} }
// check that date_from is before date_to // check that date_from is before date_to
if ( if (
(null !== $date_from && null !== $date_to) ($date_from !== null && $date_to !== null)
&& $date_from >= $date_to &&
$date_from >= $date_to
) { ) {
$form->get('date_to')->addError(new FormError( $form->get('date_to')->addError(new FormError(
$this->translator->trans('This date ' $this->translator->trans('This date '
. 'should be after the date given in "Implied in an ' . 'should be after the date given in "Implied in an '
. 'activity after this date" field') . 'activity after this date" field')));
));
} }
} }
}); });
} }
public function validateForm($data, ExecutionContextInterface $context)
{
if ($data['reasons'] === null || count($data['reasons']) === 0) {
$context->buildViolation('At least one reason must be chosen')
->addViolation();
}
}
public function describeAction($data, $format = 'string') public function describeAction($data, $format = 'string')
{ {
return [ return [
@@ -185,13 +175,13 @@ class PersonHavingActivityBetweenDateFilter implements ExportElementValidatedInt
'%date_from%' => $data['date_from']->format('d-m-Y'), '%date_from%' => $data['date_from']->format('d-m-Y'),
'%date_to%' => $data['date_to']->format('d-m-Y'), '%date_to%' => $data['date_to']->format('d-m-Y'),
'%reasons_name%' => implode( '%reasons_name%' => implode(
', ', ", ",
array_map( array_map(
fn(ActivityReason $r): string => '"' . $this->translatableStringHelper->localize($r->getName()) . '"', fn(ActivityReason $r): string => '"' . $this->translatableStringHelper->localize($r->getName()) . '"',
$data['reasons'] $data['reasons']
) )
), )
], ]
]; ];
} }
@@ -200,11 +190,4 @@ class PersonHavingActivityBetweenDateFilter implements ExportElementValidatedInt
return 'Filtered by person having an activity in a period'; return 'Filtered by person having an activity in a period';
} }
public function validateForm($data, ExecutionContextInterface $context)
{
if (null === $data['reasons'] || count($data['reasons']) === 0) {
$context->buildViolation('At least one reason must be chosen')
->addViolation();
}
}
} }

View File

@@ -1,22 +1,13 @@
<?php <?php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Chill\ActivityBundle\Form; namespace Chill\ActivityBundle\Form;
use Chill\ActivityBundle\Entity\ActivityPresence; use Chill\ActivityBundle\Entity\ActivityPresence;
use Chill\MainBundle\Form\Type\TranslatableStringFormType;
use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Chill\MainBundle\Form\Type\TranslatableStringFormType;
class ActivityPresenceType extends AbstractType class ActivityPresenceType extends AbstractType
{ {
@@ -24,19 +15,19 @@ class ActivityPresenceType extends AbstractType
{ {
$builder $builder
->add('name', TranslatableStringFormType::class) ->add('name', TranslatableStringFormType::class)
->add('active', ChoiceType::class, [ ->add('active', ChoiceType::class, array(
'choices' => [ 'choices' => array(
'Yes' => true, 'Yes' => true,
'No' => false, 'No' => false
], ),
'expanded' => true, 'expanded' => true
]); ));
} }
public function configureOptions(OptionsResolver $resolver): void public function configureOptions(OptionsResolver $resolver): void
{ {
$resolver->setDefaults([ $resolver->setDefaults(array(
'data_class' => ActivityPresence::class, 'data_class' => ActivityPresence::class
]); ));
} }
} }

View File

@@ -1,29 +1,25 @@
<?php <?php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Chill\ActivityBundle\Form; namespace Chill\ActivityBundle\Form;
use Chill\MainBundle\Form\Type\TranslatableStringFormType;
use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Chill\MainBundle\Form\Type\TranslatableStringFormType;
class ActivityReasonCategoryType extends AbstractType class ActivityReasonCategoryType extends AbstractType
{ {
/**
* @param FormBuilderInterface $builder
* @param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options) public function buildForm(FormBuilderInterface $builder, array $options)
{ {
$builder $builder
->add('name', TranslatableStringFormType::class) ->add('name', TranslatableStringFormType::class)
->add('active', CheckboxType::class, ['required' => false]); ->add('active', CheckboxType::class, array('required' => false))
;
} }
/** /**
@@ -31,9 +27,9 @@ class ActivityReasonCategoryType extends AbstractType
*/ */
public function configureOptions(OptionsResolver $resolver) public function configureOptions(OptionsResolver $resolver)
{ {
$resolver->setDefaults([ $resolver->setDefaults(array(
'data_class' => 'Chill\ActivityBundle\Entity\ActivityReasonCategory', 'data_class' => 'Chill\ActivityBundle\Entity\ActivityReasonCategory'
]); ));
} }
/** /**

View File

@@ -1,38 +1,37 @@
<?php <?php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Chill\ActivityBundle\Form; namespace Chill\ActivityBundle\Form;
use Chill\ActivityBundle\Form\Type\TranslatableActivityReasonCategory;
use Chill\MainBundle\Form\Type\TranslatableStringFormType;
use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Chill\MainBundle\Form\Type\TranslatableStringFormType;
use Chill\ActivityBundle\Form\Type\TranslatableActivityReasonCategory;
class ActivityReasonType extends AbstractType class ActivityReasonType extends AbstractType
{ {
/**
* @param FormBuilderInterface $builder
* @param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options) public function buildForm(FormBuilderInterface $builder, array $options)
{ {
$builder $builder
->add('name', TranslatableStringFormType::class) ->add('name', TranslatableStringFormType::class)
->add('active', CheckboxType::class, ['required' => false]) ->add('active', CheckboxType::class, array('required' => false))
->add('category', TranslatableActivityReasonCategory::class); ->add('category', TranslatableActivityReasonCategory::class)
;
} }
/**
* @param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver) public function configureOptions(OptionsResolver $resolver)
{ {
$resolver->setDefaults([ $resolver->setDefaults(array(
'data_class' => 'Chill\ActivityBundle\Entity\ActivityReason', 'data_class' => 'Chill\ActivityBundle\Entity\ActivityReason'
]); ));
} }
/** /**

View File

@@ -1,14 +1,5 @@
<?php <?php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Chill\ActivityBundle\Form; namespace Chill\ActivityBundle\Form;
use Chill\ActivityBundle\Entity\Activity; use Chill\ActivityBundle\Entity\Activity;
@@ -16,57 +7,53 @@ use Chill\ActivityBundle\Entity\ActivityPresence;
use Chill\ActivityBundle\Entity\ActivityReason; use Chill\ActivityBundle\Entity\ActivityReason;
use Chill\DocStoreBundle\Form\StoredObjectType; use Chill\DocStoreBundle\Form\StoredObjectType;
use Chill\MainBundle\Entity\Location; use Chill\MainBundle\Entity\Location;
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Form\Type\ChillCollectionType; use Chill\MainBundle\Form\Type\ChillCollectionType;
use Chill\MainBundle\Form\Type\ChillDateType;
use Chill\MainBundle\Form\Type\CommentType; use Chill\MainBundle\Form\Type\CommentType;
use Chill\MainBundle\Form\Type\ScopePickerType;
use Chill\MainBundle\Form\Type\UserPickerType;
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Chill\PersonBundle\Entity\Person; use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Entity\SocialWork\SocialAction;
use Chill\PersonBundle\Entity\SocialWork\SocialIssue; use Chill\PersonBundle\Entity\SocialWork\SocialIssue;
use Chill\PersonBundle\Templating\Entity\SocialActionRender; use Chill\PersonBundle\Entity\SocialWork\SocialAction;
use Chill\PersonBundle\Templating\Entity\SocialIssueRender;
use Chill\ThirdPartyBundle\Entity\ThirdParty; use Chill\ThirdPartyBundle\Entity\ThirdParty;
use DateInterval;
use DateTime;
use DateTimeZone;
use Doctrine\ORM\EntityRepository; use Doctrine\ORM\EntityRepository;
use Doctrine\Persistence\ObjectManager;
use RuntimeException;
use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\CallbackTransformer;
use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToTimestampTransformer;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType; use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
use Doctrine\Persistence\ObjectManager;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Chill\MainBundle\Entity\User;
use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToTimestampTransformer;
use Symfony\Component\Form\FormEvent; use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents; use Symfony\Component\Form\FormEvents;
use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; use Chill\MainBundle\Form\Type\UserPickerType;
use Chill\MainBundle\Form\Type\ScopePickerType;
use function in_array; use Chill\MainBundle\Form\Type\ChillDateType;
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
use Symfony\Component\Form\CallbackTransformer;
use Chill\PersonBundle\Form\DataTransformer\PersonToIdTransformer;
use Chill\PersonBundle\Templating\Entity\SocialIssueRender;
use Chill\PersonBundle\Templating\Entity\SocialActionRender;
class ActivityType extends AbstractType class ActivityType extends AbstractType
{ {
protected User $user;
protected AuthorizationHelper $authorizationHelper; protected AuthorizationHelper $authorizationHelper;
protected ObjectManager $om; protected ObjectManager $om;
protected SocialActionRender $socialActionRender; protected TranslatableStringHelper $translatableStringHelper;
protected SocialIssueRender $socialIssueRender; protected SocialIssueRender $socialIssueRender;
protected SocialActionRender $socialActionRender;
protected array $timeChoices; protected array $timeChoices;
protected TranslatableStringHelper $translatableStringHelper;
protected User $user;
public function __construct ( public function __construct (
TokenStorageInterface $tokenStorage, TokenStorageInterface $tokenStorage,
AuthorizationHelper $authorizationHelper, AuthorizationHelper $authorizationHelper,
@@ -77,7 +64,7 @@ class ActivityType extends AbstractType
SocialActionRender $socialActionRender SocialActionRender $socialActionRender
) { ) {
if (!$tokenStorage->getToken()->getUser() instanceof User) { if (!$tokenStorage->getToken()->getUser() instanceof User) {
throw new RuntimeException('you should have a valid user'); throw new \RuntimeException("you should have a valid user");
} }
$this->user = $tokenStorage->getToken()->getUser(); $this->user = $tokenStorage->getToken()->getUser();
@@ -113,71 +100,62 @@ class ActivityType extends AbstractType
'center' => $options['center'], 'center' => $options['center'],
'role' => $options['role'], 'role' => $options['role'],
// TODO make required again once scope and rights are fixed // TODO make required again once scope and rights are fixed
'required' => false, 'required' => false
]); ]);
} }
/** @var ? \Chill\PersonBundle\Entity\AccompanyingPeriod $accompanyingPeriod */ /** @var ? \Chill\PersonBundle\Entity\AccompanyingPeriod $accompanyingPeriod */
$accompanyingPeriod = null; $accompanyingPeriod = NULL;
if ($options['accompanyingPeriod']) { if ($options['accompanyingPeriod']) {
$accompanyingPeriod = $options['accompanyingPeriod']; $accompanyingPeriod = $options['accompanyingPeriod'];
} }
if ($activityType->isVisible('socialIssues') && $accompanyingPeriod) { if ($activityType->isVisible('socialIssues') && $accompanyingPeriod) {
$builder->add('socialIssues', HiddenType::class, [ $builder->add('socialIssues', HiddenType::class);
'required' => $activityType->getSocialIssuesVisible() === 2,
]);
$builder->get('socialIssues') $builder->get('socialIssues')
->addModelTransformer(new CallbackTransformer( ->addModelTransformer(new CallbackTransformer(
static function (iterable $socialIssuesAsIterable): string { function (iterable $socialIssuesAsIterable): string {
$socialIssueIds = []; $socialIssueIds = [];
foreach ($socialIssuesAsIterable as $value) { foreach ($socialIssuesAsIterable as $value) {
$socialIssueIds[] = $value->getId(); $socialIssueIds[] = $value->getId();
} }
return implode(',', $socialIssueIds); return implode(',', $socialIssueIds);
}, },
function (?string $socialIssuesAsString): array { function (?string $socialIssuesAsString): array {
if (null === $socialIssuesAsString) { if (null === $socialIssuesAsString) {
return []; return [];
} }
return array_map( return array_map(
fn(string $id): ?SocialIssue => $this->om->getRepository(SocialIssue::class)->findOneBy(['id' => (int) $id]), fn(string $id): ?SocialIssue => $this->om->getRepository(SocialIssue::class)->findOneBy(['id' => (int) $id]),
explode(',', $socialIssuesAsString) explode(',', $socialIssuesAsString)
); );
} }
)); ))
;
} }
if ($activityType->isVisible('socialActions') && $accompanyingPeriod) { if ($activityType->isVisible('socialActions') && $accompanyingPeriod) {
$builder->add('socialActions', HiddenType::class, [ $builder->add('socialActions', HiddenType::class);
'required' => $activityType->getSocialActionsVisible() === 2,
]);
$builder->get('socialActions') $builder->get('socialActions')
->addModelTransformer(new CallbackTransformer( ->addModelTransformer(new CallbackTransformer(
static function (iterable $socialActionsAsIterable): string { function (iterable $socialActionsAsIterable): string {
$socialActionIds = []; $socialActionIds = [];
foreach ($socialActionsAsIterable as $value) { foreach ($socialActionsAsIterable as $value) {
$socialActionIds[] = $value->getId(); $socialActionIds[] = $value->getId();
} }
return implode(',', $socialActionIds); return implode(',', $socialActionIds);
}, },
function (?string $socialActionsAsString): array { function (?string $socialActionsAsString): array {
if (null === $socialActionsAsString) { if (null === $socialActionsAsString) {
return []; return [];
} }
return array_map( return array_map(
fn(string $id): ?SocialAction => $this->om->getRepository(SocialAction::class)->findOneBy(['id' => (int) $id]), fn(string $id): ?SocialAction => $this->om->getRepository(SocialAction::class)->findOneBy(['id' => (int) $id]),
explode(',', $socialActionsAsString) explode(',', $socialActionsAsString)
); );
} }
)); ))
;
} }
if ($activityType->isVisible('date')) { if ($activityType->isVisible('date')) {
@@ -210,7 +188,7 @@ class ActivityType extends AbstractType
'choice_label' => function (ActivityPresence $activityPresence) { 'choice_label' => function (ActivityPresence $activityPresence) {
return $this->translatableStringHelper->localize($activityPresence->getName()); return $this->translatableStringHelper->localize($activityPresence->getName());
}, },
'query_builder' => static function (EntityRepository $er) { 'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('a') return $er->createQueryBuilder('a')
->where('a.active = true'); ->where('a.active = true');
}, },
@@ -222,7 +200,7 @@ class ActivityType extends AbstractType
'label' => $activityType->getLabel('user'), 'label' => $activityType->getLabel('user'),
'required' => $activityType->isRequired('user'), 'required' => $activityType->isRequired('user'),
'center' => $options['center'], 'center' => $options['center'],
'role' => $options['role'], 'role' => $options['role']
]); ]);
} }
@@ -235,8 +213,8 @@ class ActivityType extends AbstractType
'choice_label' => function (ActivityReason $activityReason) { 'choice_label' => function (ActivityReason $activityReason) {
return $this->translatableStringHelper->localize($activityReason->getName()); return $this->translatableStringHelper->localize($activityReason->getName());
}, },
'attr' => ['class' => 'select2 '], 'attr' => array('class' => 'select2 '),
'query_builder' => static function (EntityRepository $er) { 'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('a') return $er->createQueryBuilder('a')
->where('a.active = true'); ->where('a.active = true');
}, },
@@ -255,52 +233,42 @@ class ActivityType extends AbstractType
$builder->add('persons', HiddenType::class); $builder->add('persons', HiddenType::class);
$builder->get('persons') $builder->get('persons')
->addModelTransformer(new CallbackTransformer( ->addModelTransformer(new CallbackTransformer(
static function (iterable $personsAsIterable): string { function (iterable $personsAsIterable): string {
$personIds = []; $personIds = [];
foreach ($personsAsIterable as $value) { foreach ($personsAsIterable as $value) {
$personIds[] = $value->getId(); $personIds[] = $value->getId();
} }
return implode(',', $personIds); return implode(',', $personIds);
}, },
function (?string $personsAsString): array { function (?string $personsAsString): array {
if (null === $personsAsString) {
return [];
}
return array_map( return array_map(
fn(string $id): ?Person => $this->om->getRepository(Person::class)->findOneBy(['id' => (int) $id]), fn(string $id): ?Person => $this->om->getRepository(Person::class)->findOneBy(['id' => (int) $id]),
explode(',', $personsAsString) explode(',', $personsAsString)
); );
} }
)); ))
;
} }
if ($activityType->isVisible('thirdParties')) { if ($activityType->isVisible('thirdParties')) {
$builder->add('thirdParties', HiddenType::class); $builder->add('thirdParties', HiddenType::class);
$builder->get('thirdParties') $builder->get('thirdParties')
->addModelTransformer(new CallbackTransformer( ->addModelTransformer(new CallbackTransformer(
static function (iterable $thirdpartyAsIterable): string { function (iterable $thirdpartyAsIterable): string {
$thirdpartyIds = []; $thirdpartyIds = [];
foreach ($thirdpartyAsIterable as $value) { foreach ($thirdpartyAsIterable as $value) {
$thirdpartyIds[] = $value->getId(); $thirdpartyIds[] = $value->getId();
} }
return implode(',', $thirdpartyIds); return implode(',', $thirdpartyIds);
}, },
function (?string $thirdpartyAsString): array { function (?string $thirdpartyAsString): array {
if (null === $thirdpartyAsString) {
return [];
}
return array_map( return array_map(
fn(string $id): ?ThirdParty => $this->om->getRepository(ThirdParty::class)->findOneBy(['id' => (int) $id]), fn(string $id): ?ThirdParty => $this->om->getRepository(ThirdParty::class)->findOneBy(['id' => (int) $id]),
explode(',', $thirdpartyAsString) explode(',', $thirdpartyAsString)
); );
} }
)); ))
;
} }
if ($activityType->isVisible('documents')) { if ($activityType->isVisible('documents')) {
@@ -309,10 +277,8 @@ class ActivityType extends AbstractType
'label' => $activityType->getLabel('documents'), 'label' => $activityType->getLabel('documents'),
'required' => $activityType->isRequired('documents'), 'required' => $activityType->isRequired('documents'),
'allow_add' => true, 'allow_add' => true,
'allow_delete' => true,
'button_add_label' => 'activity.Insert a document', 'button_add_label' => 'activity.Insert a document',
'button_remove_label' => 'activity.Remove a document', 'button_remove_label' => 'activity.Remove a document'
'empty_collection_explain' => 'No documents',
]); ]);
} }
@@ -320,45 +286,38 @@ class ActivityType extends AbstractType
$builder->add('users', HiddenType::class); $builder->add('users', HiddenType::class);
$builder->get('users') $builder->get('users')
->addModelTransformer(new CallbackTransformer( ->addModelTransformer(new CallbackTransformer(
static function (iterable $usersAsIterable): string { function (iterable $usersAsIterable): string {
$userIds = []; $userIds = [];
foreach ($usersAsIterable as $value) { foreach ($usersAsIterable as $value) {
$userIds[] = $value->getId(); $userIds[] = $value->getId();
} }
return implode(',', $userIds); return implode(',', $userIds);
}, },
function (?string $usersAsString): array { function (?string $usersAsString): array {
if (null === $usersAsString) {
return [];
}
return array_map( return array_map(
fn(string $id): ?User => $this->om->getRepository(User::class)->findOneBy(['id' => (int) $id]), fn(string $id): ?User => $this->om->getRepository(User::class)->findOneBy(['id' => (int) $id]),
explode(',', $usersAsString) explode(',', $usersAsString)
); );
} }
)); ))
;
} }
if ($activityType->isVisible('location')) { if ($activityType->isVisible('location')) {
$builder->add('location', HiddenType::class, [ $builder->add('location', HiddenType::class)
'required' => $activityType->getLocationVisible() === 2,
])
->get('location') ->get('location')
->addModelTransformer(new CallbackTransformer( ->addModelTransformer(new CallbackTransformer(
static function (?Location $location): string { function (?Location $location): string {
if (null === $location) { if (null === $location) {
return ''; return '';
} }
return $location->getId();
return (string) $location->getId();
}, },
function (?string $id): ?Location { function (?string $id): ?Location {
return $this->om->getRepository(Location::class)->findOneBy(['id' => (int) $id]); return $this->om->getRepository(Location::class)->findOneBy(['id' => (int) $id]);
} }
)); ))
;
} }
if ($activityType->isVisible('emergency')) { if ($activityType->isVisible('emergency')) {
@@ -373,8 +332,8 @@ class ActivityType extends AbstractType
'label' => $activityType->getLabel('sentReceived'), 'label' => $activityType->getLabel('sentReceived'),
'required' => $activityType->isRequired('sentReceived'), 'required' => $activityType->isRequired('sentReceived'),
'choices' => [ 'choices' => [
'Received' => Activity::SENTRECEIVED_RECEIVED,
'Sent' => Activity::SENTRECEIVED_SENT, 'Sent' => Activity::SENTRECEIVED_SENT,
'Received' => Activity::SENTRECEIVED_RECEIVED,
], ],
]); ]);
} }
@@ -388,7 +347,7 @@ class ActivityType extends AbstractType
->addModelTransformer($durationTimeTransformer); ->addModelTransformer($durationTimeTransformer);
$builder->get($fieldName) $builder->get($fieldName)
->addEventListener(FormEvents::PRE_SET_DATA, static function (FormEvent $formEvent) use ( ->addEventListener(FormEvents::PRE_SET_DATA, function(FormEvent $formEvent) use (
$timeChoices, $timeChoices,
$builder, $builder,
$durationTimeTransformer, $durationTimeTransformer,
@@ -397,25 +356,24 @@ class ActivityType extends AbstractType
) { ) {
// set the timezone to GMT, and fix the difference between current and GMT // set the timezone to GMT, and fix the difference between current and GMT
// the datetimetransformer will then handle timezone as GMT // the datetimetransformer will then handle timezone as GMT
$timezoneUTC = new DateTimeZone('GMT'); $timezoneUTC = new \DateTimeZone('GMT');
/** @var DateTime $data */ /* @var $data \DateTime */
$data = $formEvent->getData() === null ? $data = $formEvent->getData() === NULL ?
DateTime::createFromFormat('U', '300') : \DateTime::createFromFormat('U', 300) :
$formEvent->getData(); $formEvent->getData();
$seconds = $data->getTimezone()->getOffset($data); $seconds = $data->getTimezone()->getOffset($data);
$data->setTimeZone($timezoneUTC); $data->setTimeZone($timezoneUTC);
$data->add(new DateInterval('PT' . $seconds . 'S')); $data->add(new \DateInterval('PT'.$seconds.'S'));
// test if the timestamp is in the choices. // test if the timestamp is in the choices.
// If not, recreate the field with the new timestamp // If not, recreate the field with the new timestamp
if (!in_array($data->getTimestamp(), $timeChoices, true)) { if (!in_array($data->getTimestamp(), $timeChoices)) {
// the data are not in the possible values. add them // the data are not in the possible values. add them
$timeChoices[$data->format('H:i')] = $data->getTimestamp(); $timeChoices[$data->format('H:i')] = $data->getTimestamp();
$form = $builder->create($fieldName, ChoiceType::class, array_merge( $form = $builder->create($fieldName, ChoiceType::class, array_merge(
$durationTimeOptions, $durationTimeOptions, [
[
'choices' => $timeChoices, 'choices' => $timeChoices,
'auto_initialize' => false, 'auto_initialize' => false
] ]
)); ));
$form->addModelTransformer($durationTimeTransformer); $form->addModelTransformer($durationTimeTransformer);
@@ -425,10 +383,12 @@ class ActivityType extends AbstractType
} }
} }
public function configureOptions(OptionsResolver $resolver): void public function configureOptions(OptionsResolver $resolver): void
{ {
$resolver->setDefaults([ $resolver->setDefaults([
'data_class' => Activity::class, 'data_class' => Activity::class
]); ]);
$resolver $resolver
@@ -436,7 +396,8 @@ class ActivityType extends AbstractType
->setAllowedTypes('center', ['null', 'Chill\MainBundle\Entity\Center']) ->setAllowedTypes('center', ['null', 'Chill\MainBundle\Entity\Center'])
->setAllowedTypes('role', 'Symfony\Component\Security\Core\Role\Role') ->setAllowedTypes('role', 'Symfony\Component\Security\Core\Role\Role')
->setAllowedTypes('activityType', \Chill\ActivityBundle\Entity\ActivityType::class) ->setAllowedTypes('activityType', \Chill\ActivityBundle\Entity\ActivityType::class)
->setAllowedTypes('accompanyingPeriod', [\Chill\PersonBundle\Entity\AccompanyingPeriod::class, 'null']); ->setAllowedTypes('accompanyingPeriod', [\Chill\PersonBundle\Entity\AccompanyingPeriod::class, 'null'])
;
} }
public function getBlockPrefix(): string public function getBlockPrefix(): string

View File

@@ -1,23 +1,14 @@
<?php <?php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Chill\ActivityBundle\Form; namespace Chill\ActivityBundle\Form;
use Chill\ActivityBundle\Entity\ActivityTypeCategory; use Chill\ActivityBundle\Entity\ActivityTypeCategory;
use Chill\MainBundle\Form\Type\TranslatableStringFormType;
use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\NumberType; use Symfony\Component\Form\Extension\Core\Type\NumberType;
use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Chill\MainBundle\Form\Type\TranslatableStringFormType;
class ActivityTypeCategoryType extends AbstractType class ActivityTypeCategoryType extends AbstractType
{ {
@@ -25,23 +16,24 @@ class ActivityTypeCategoryType extends AbstractType
{ {
$builder $builder
->add('name', TranslatableStringFormType::class) ->add('name', TranslatableStringFormType::class)
->add('active', ChoiceType::class, [ ->add('active', ChoiceType::class, array(
'choices' => [ 'choices' => array(
'Yes' => true, 'Yes' => true,
'No' => false, 'No' => false
], ),
'expanded' => true, 'expanded' => true
]) ))
->add('ordering', NumberType::class, [ ->add('ordering', NumberType::class, [
'required' => true, 'required' => true,
'scale' => 5, 'scale' => 5
]); ])
;
} }
public function configureOptions(OptionsResolver $resolver): void public function configureOptions(OptionsResolver $resolver): void
{ {
$resolver->setDefaults([ $resolver->setDefaults(array(
'data_class' => ActivityTypeCategory::class, 'data_class' => ActivityTypeCategory::class
]); ));
} }
} }

View File

@@ -1,27 +1,18 @@
<?php <?php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Chill\ActivityBundle\Form; namespace Chill\ActivityBundle\Form;
use Chill\ActivityBundle\Entity\ActivityTypeCategory; use Chill\ActivityBundle\Entity\ActivityTypeCategory;
use Chill\ActivityBundle\Form\Type\ActivityFieldPresence; use Chill\ActivityBundle\Form\Type\ActivityFieldPresence;
use Chill\MainBundle\Form\Type\TranslatableStringFormType;
use Chill\MainBundle\Templating\TranslatableStringHelper; use Chill\MainBundle\Templating\TranslatableStringHelper;
use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\NumberType; use Symfony\Component\Form\Extension\Core\Type\NumberType;
use Symfony\Component\Form\Extension\Core\Type\TextType; use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Chill\MainBundle\Form\Type\TranslatableStringFormType;
class ActivityTypeType extends AbstractType class ActivityTypeType extends AbstractType
{ {
@@ -39,9 +30,9 @@ class ActivityTypeType extends AbstractType
->add('active', ChoiceType::class, [ ->add('active', ChoiceType::class, [
'choices' => [ 'choices' => [
'Yes' => true, 'Yes' => true,
'No' => false, 'No' => false
], ],
'expanded' => true, 'expanded' => true
]) ])
->add('category', EntityType::class, [ ->add('category', EntityType::class, [
'class' => ActivityTypeCategory::class, 'class' => ActivityTypeCategory::class,
@@ -51,16 +42,16 @@ class ActivityTypeType extends AbstractType
]) ])
->add('ordering', NumberType::class, [ ->add('ordering', NumberType::class, [
'required' => true, 'required' => true,
'scale' => 5, 'scale' => 5
]); ])
;
$fields = [ $fields = [
'persons', 'user', 'date', 'location', 'persons', 'persons', 'user', 'date', 'place', 'persons',
'thirdParties', 'durationTime', 'travelTime', 'attendee', 'thirdParties', 'durationTime', 'travelTime', 'attendee',
'reasons', 'comment', 'sentReceived', 'documents', 'reasons', 'comment', 'sentReceived', 'documents',
'emergency', 'socialIssues', 'socialActions', 'users', 'emergency', 'accompanyingPeriod', 'socialData', 'users'
]; ];
foreach ($fields as $field) { foreach ($fields as $field) {
$builder $builder
->add($field.'Visible', ActivityFieldPresence::class) ->add($field.'Visible', ActivityFieldPresence::class)
@@ -73,8 +64,8 @@ class ActivityTypeType extends AbstractType
public function configureOptions(OptionsResolver $resolver) public function configureOptions(OptionsResolver $resolver)
{ {
$resolver->setDefaults([ $resolver->setDefaults(array(
'data_class' => \Chill\ActivityBundle\Entity\ActivityType::class, 'data_class' => \Chill\ActivityBundle\Entity\ActivityType::class
]); ));
} }
} }

View File

@@ -1,14 +1,5 @@
<?php <?php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Chill\ActivityBundle\Form\Type; namespace Chill\ActivityBundle\Form\Type;
use Chill\ActivityBundle\Entity\ActivityType; use Chill\ActivityBundle\Entity\ActivityType;
@@ -18,21 +9,21 @@ use Symfony\Component\OptionsResolver\OptionsResolver;
class ActivityFieldPresence extends AbstractType class ActivityFieldPresence extends AbstractType
{ {
public function getParent()
{
return ChoiceType::class;
}
public function configureOptions(OptionsResolver $resolver) public function configureOptions(OptionsResolver $resolver)
{ {
$resolver->setDefaults( $resolver->setDefaults(
[ array(
'choices' => [ 'choices' => [
'Invisible' => ActivityType::FIELD_INVISIBLE, 'Invisible' => ActivityType::FIELD_INVISIBLE,
'Optional' => ActivityType::FIELD_OPTIONAL, 'Optional' => ActivityType::FIELD_OPTIONAL,
'Required' => ActivityType::FIELD_REQUIRED, 'Required' => ActivityType::FIELD_REQUIRED,
], ],
] )
); );
} }
public function getParent()
{
return ChoiceType::class;
}
} }

View File

@@ -1,39 +1,54 @@
<?php <?php
/** /*
* Chill is a software for social workers * Chill is a software for social workers
* *
* For the full copyright and license information, please view * Copyright (C) 2014-2020, Champs Libres Cooperative SCRLFS,
* the LICENSE file that was distributed with this source code. * <http://www.champs-libres.coop>, <info@champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
declare(strict_types=1);
namespace Chill\ActivityBundle\Form\Type; namespace Chill\ActivityBundle\Form\Type;
use Chill\ActivityBundle\Entity\ActivityReason;
use Chill\ActivityBundle\Templating\Entity\ActivityReasonRender;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Doctrine\ORM\EntityRepository;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\AbstractType;
use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Doctrine\ORM\EntityRepository;
use Chill\ActivityBundle\Entity\ActivityReason;
use Chill\ActivityBundle\Templating\Entity\ActivityReasonRender;
/** /**
* FormType to choose amongst activity reasons. * FormType to choose amongst activity reasons
*
*/ */
class TranslatableActivityReason extends AbstractType class TranslatableActivityReason extends AbstractType
{ {
/**
* @var ActivityReasonRender
*/
protected $reasonRender;
/** /**
*
* @var TranslatableStringHelper * @var TranslatableStringHelper
*/ */
protected $translatableStringHelper; protected $translatableStringHelper;
/**
*
* @var ActivityReasonRender
*/
protected $reasonRender;
public function __construct( public function __construct(
TranslatableStringHelper $translatableStringHelper, TranslatableStringHelper $translatableStringHelper,
ActivityReasonRender $reasonRender ActivityReasonRender $reasonRender
@@ -42,30 +57,6 @@ class TranslatableActivityReason extends AbstractType
$this->reasonRender = $reasonRender; $this->reasonRender = $reasonRender;
} }
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(
[
'class' => 'ChillActivityBundle:ActivityReason',
'choice_label' => function (ActivityReason $choice) {
return $this->reasonRender->renderString($choice, []);
},
'group_by' => function (ActivityReason $choice): ?string {
if (null !== $category = $choice->getCategory()) {
return $this->translatableStringHelper->localize($category->getName());
}
return null;
},
'query_builder' => static function (EntityRepository $er) {
return $er->createQueryBuilder('r')
->where('r.active = true');
},
'attr' => ['class' => ' select2 '],
]
);
}
public function getBlockPrefix() public function getBlockPrefix()
{ {
return 'translatable_activity_reason'; return 'translatable_activity_reason';
@@ -75,4 +66,28 @@ class TranslatableActivityReason extends AbstractType
{ {
return EntityType::class; return EntityType::class;
} }
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(
array(
'class' => 'ChillActivityBundle:ActivityReason',
'choice_label' => function(ActivityReason $choice) {
return $this->reasonRender->renderString($choice, []);
},
'group_by' => function(ActivityReason $choice): ?string {
if (null !== $category = $choice->getCategory()) {
return $this->translatableStringHelper->localize($category->getName());
}
return null;
},
'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('r')
->where('r.active = true');
},
'attr' => [ 'class' => ' select2 ']
)
);
}
} }

View File

@@ -1,25 +1,40 @@
<?php <?php
/** /*
* Chill is a software for social workers * Chill is a software for social workers
* *
* For the full copyright and license information, please view * Copyright (C) 2014-2015, Champs Libres Cooperative SCRLFS,
* the LICENSE file that was distributed with this source code. * <http://www.champs-libres.coop>, <info@champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
declare(strict_types=1);
namespace Chill\ActivityBundle\Form\Type; namespace Chill\ActivityBundle\Form\Type;
use Doctrine\ORM\EntityRepository;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\AbstractType;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Doctrine\ORM\EntityRepository;
/** /**
* Description of TranslatableActivityReasonCategory. * Description of TranslatableActivityReasonCategory
*
* @author Champs-Libres Coop
*/ */
class TranslatableActivityReasonCategory extends AbstractType class TranslatableActivityReasonCategory extends AbstractType
{ {
/** /**
@@ -32,21 +47,6 @@ class TranslatableActivityReasonCategory extends AbstractType
$this->requestStack = $requestStack; $this->requestStack = $requestStack;
} }
public function configureOptions(OptionsResolver $resolver)
{
$locale = $this->requestStack->getCurrentRequest()->getLocale();
$resolver->setDefaults(
[
'class' => 'ChillActivityBundle:ActivityReasonCategory',
'choice_label' => 'name[' . $locale . ']',
'query_builder' => static function (EntityRepository $er) {
return $er->createQueryBuilder('c')
->where('c.active = true');
},
]
);
}
public function getBlockPrefix() public function getBlockPrefix()
{ {
return 'translatable_activity_reason_category'; return 'translatable_activity_reason_category';
@@ -56,4 +56,19 @@ class TranslatableActivityReasonCategory extends AbstractType
{ {
return EntityType::class; return EntityType::class;
} }
public function configureOptions(OptionsResolver $resolver)
{
$locale = $this->requestStack->getCurrentRequest()->getLocale();
$resolver->setDefaults(
array(
'class' => 'ChillActivityBundle:ActivityReasonCategory',
'choice_label' => 'name['.$locale.']',
'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('c')
->where('c.active = true');
}
)
);
}
} }

View File

@@ -1,32 +1,25 @@
<?php <?php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1); declare(strict_types=1);
namespace Chill\ActivityBundle\Form\Type; namespace Chill\ActivityBundle\Form\Type;
use Chill\ActivityBundle\Entity\ActivityType;
use Chill\ActivityBundle\Repository\ActivityTypeRepository; use Chill\ActivityBundle\Repository\ActivityTypeRepository;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface; use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Doctrine\DBAL\Types\Types; use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\QueryBuilder; use Doctrine\ORM\QueryBuilder;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Chill\ActivityBundle\Entity\ActivityType;
class TranslatableActivityType extends AbstractType class TranslatableActivityType extends AbstractType
{ {
protected ActivityTypeRepository $activityTypeRepository;
protected TranslatableStringHelperInterface $translatableStringHelper; protected TranslatableStringHelperInterface $translatableStringHelper;
protected ActivityTypeRepository $activityTypeRepository;
public function __construct( public function __construct(
TranslatableStringHelperInterface $helper, TranslatableStringHelperInterface $helper,
ActivityTypeRepository $activityTypeRepository ActivityTypeRepository $activityTypeRepository
@@ -35,32 +28,6 @@ class TranslatableActivityType extends AbstractType
$this->activityTypeRepository = $activityTypeRepository; $this->activityTypeRepository = $activityTypeRepository;
} }
public function buildForm(FormBuilderInterface $builder, array $options)
{
/** @var QueryBuilder $qb */
$qb = $options['query_builder'];
if (true === $options['active_only']) {
$qb->where($qb->expr()->eq('at.active', ':active'));
$qb->setParameter('active', true, Types::BOOLEAN);
}
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(
[
'class' => ActivityType::class,
'active_only' => true,
'query_builder' => $this->activityTypeRepository
->createQueryBuilder('at'),
'choice_label' => function (ActivityType $type) {
return $this->translatableStringHelper->localize($type->getName());
},
]
);
}
public function getBlockPrefix() public function getBlockPrefix()
{ {
return 'translatable_activity_type'; return 'translatable_activity_type';
@@ -70,4 +37,29 @@ class TranslatableActivityType extends AbstractType
{ {
return EntityType::class; return EntityType::class;
} }
public function buildForm(FormBuilderInterface $builder, array $options) {
/* @var QueryBuilder $qb */
$qb = $options['query_builder'];
if ($options['active_only'] === true) {
$qb->where($qb->expr()->eq('at.active', ':active'));
$qb->setParameter('active', true, Types::BOOLEAN);
}
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(
array(
'class' => ActivityType::class,
'active_only' => true,
'query_builder' => $this->activityTypeRepository
->createQueryBuilder('at'),
'choice_label' => function (ActivityType $type) {
return $this->translatableStringHelper->localize($type->getName());
}
)
);
}
} }

View File

@@ -1,29 +1,22 @@
<?php <?php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Chill\ActivityBundle\Menu; namespace Chill\ActivityBundle\Menu;
use Chill\ActivityBundle\Security\Authorization\ActivityVoter; use Chill\ActivityBundle\Security\Authorization\ActivityVoter;
use Chill\MainBundle\Routing\LocalMenuBuilderInterface; use Chill\MainBundle\Routing\LocalMenuBuilderInterface;
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Knp\Menu\MenuItem; use Knp\Menu\MenuItem;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Security; use Symfony\Component\Security\Core\Security;
use Symfony\Contracts\Translation\TranslatorInterface; use Symfony\Contracts\Translation\TranslatorInterface;
class AccompanyingCourseMenuBuilder implements LocalMenuBuilderInterface class AccompanyingCourseMenuBuilder implements LocalMenuBuilderInterface
{ {
protected Security $security;
protected TranslatorInterface $translator; protected TranslatorInterface $translator;
protected Security $security;
public function __construct( public function __construct(
Security $security, Security $security,
TranslatorInterface $translator TranslatorInterface $translator
@@ -31,26 +24,23 @@ class AccompanyingCourseMenuBuilder implements LocalMenuBuilderInterface
$this->security = $security; $this->security = $security;
$this->translator = $translator; $this->translator = $translator;
} }
public static function getMenuIds(): array
{
return ['accompanyingCourse'];
}
public function buildMenu($menuId, MenuItem $menu, array $parameters) public function buildMenu($menuId, MenuItem $menu, array $parameters)
{ {
$period = $parameters['accompanyingCourse']; $period = $parameters['accompanyingCourse'];
if ( if (AccompanyingPeriod::STEP_DRAFT !== $period->getStep()
AccompanyingPeriod::STEP_DRAFT !== $period->getStep() && $this->security->isGranted(ActivityVoter::SEE, $period)) {
&& $this->security->isGranted(ActivityVoter::SEE, $period)
) {
$menu->addChild($this->translator->trans('Activity'), [ $menu->addChild($this->translator->trans('Activity'), [
'route' => 'chill_activity_activity_list', 'route' => 'chill_activity_activity_list',
'routeParameters' => [ 'routeParameters' => [
'accompanying_period_id' => $period->getId(), 'accompanying_period_id' => $period->getId(),
], ]) ]])
->setExtras(['order' => 40]); ->setExtras(['order' => 40]);
} }
} }
public static function getMenuIds(): array
{
return ['accompanyingCourse'];
}
} }

View File

@@ -1,12 +1,4 @@
<?php <?php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1); declare(strict_types=1);
namespace Chill\ActivityBundle\Menu; namespace Chill\ActivityBundle\Menu;
@@ -15,8 +7,6 @@ use Chill\MainBundle\Routing\LocalMenuBuilderInterface;
use Knp\Menu\MenuItem; use Knp\Menu\MenuItem;
use Symfony\Component\Security\Core\Security; use Symfony\Component\Security\Core\Security;
use function in_array;
final class AdminMenuBuilder implements LocalMenuBuilderInterface final class AdminMenuBuilder implements LocalMenuBuilderInterface
{ {
private Security $security; private Security $security;
@@ -26,33 +16,33 @@ final class AdminMenuBuilder implements LocalMenuBuilderInterface
$this->security = $security; $this->security = $security;
} }
public static function getMenuIds(): array
{
return ['admin_index', 'admin_section', 'admin_activity'];
}
public function buildMenu($menuId, MenuItem $menu, array $parameters) public function buildMenu($menuId, MenuItem $menu, array $parameters)
{ {
if (!$this->security->isGranted('ROLE_ADMIN')) { if (!$this->security->isGranted('ROLE_ADMIN')) {
return; return;
} }
if (in_array($menuId, ['admin_index', 'admin_section'], true)) { if (in_array($menuId, ['admin_index', 'admin_section'])) {
$menu->addChild('Activities', [ $menu->addChild('Activities', [
'route' => 'chill_admin_activity_index', 'route' => 'chill_admin_activity_index'
]) ])
->setExtras([ ->setExtras([
'order' => 2000, 'order' => 2000,
'explain' => 'Activity configuration', 'explain' => "Activity configuration"
]); ]);
} else { } else {
$menu $menu
->addChild('Activities', [ ->addChild('Activities', [
'route' => 'chill_admin_activity_index', 'route' => 'chill_admin_activity_index'
]) ])
->setExtras([ ->setExtras([
'order' => '60', 'order' => '60'
]); ]);
} }
} }
public static function getMenuIds(): array
{
return ['admin_index', 'admin_section', 'admin_activity'];
}
} }

View File

@@ -1,56 +1,68 @@
<?php <?php
/*
/** * Copyright (C) 2018 Julien Fastré <julien.fastre@champs-libres.coop>
* Chill is a software for social workers
* *
* For the full copyright and license information, please view * This program is free software: you can redistribute it and/or modify
* the LICENSE file that was distributed with this source code. * it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
declare(strict_types=1);
namespace Chill\ActivityBundle\Menu; namespace Chill\ActivityBundle\Menu;
use Chill\ActivityBundle\Security\Authorization\ActivityVoter;
use Chill\MainBundle\Routing\LocalMenuBuilderInterface; use Chill\MainBundle\Routing\LocalMenuBuilderInterface;
use Knp\Menu\MenuItem; use Knp\Menu\MenuItem;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface; use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
use Symfony\Contracts\Translation\TranslatorInterface; use Chill\ActivityBundle\Security\Authorization\ActivityVoter;
use Symfony\Component\Translation\TranslatorInterface;
/**
*
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
class PersonMenuBuilder implements LocalMenuBuilderInterface class PersonMenuBuilder implements LocalMenuBuilderInterface
{ {
/** /**
* @var AuthorizationCheckerInterface *
*/
protected $authorizationChecker;
/**
* @var TranslatorInterface * @var TranslatorInterface
*/ */
protected $translator; protected $translator;
/**
*
* @var AuthorizationCheckerInterface
*/
protected $authorizationChecker;
public function __construct( public function __construct(
AuthorizationCheckerInterface $authorizationChecker, AuthorizationCheckerInterface $authorizationChecker,
TranslatorInterface $translator TranslatorInterface $translator)
) { {
$this->translator = $translator; $this->translator = $translator;
$this->authorizationChecker = $authorizationChecker; $this->authorizationChecker = $authorizationChecker;
} }
public function buildMenu($menuId, MenuItem $menu, array $parameters) public function buildMenu($menuId, MenuItem $menu, array $parameters)
{ {
/** @var \Chill\PersonBundle\Entity\Person $person */ /* @var $person \Chill\PersonBundle\Entity\Person */
$person = $parameters['person']; $person = $parameters['person'];
if ($this->authorizationChecker->isGranted(ActivityVoter::SEE, $person)) { if ($this->authorizationChecker->isGranted(ActivityVoter::SEE, $person)) {
$menu->addChild( $menu->addChild(
$this->translator->trans('Activity list'), $this->translator->trans('Activity list'), [
[
'route' => 'chill_activity_activity_list', 'route' => 'chill_activity_activity_list',
'routeParameters' => [ 'person_id' => $person->getId() ], 'routeParameters' => [ 'person_id' => $person->getId() ],
] ])
) ->setExtra('order', 201)
->setExtra('order', 201); ;
} }
} }

View File

@@ -1,45 +0,0 @@
<?php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Chill\ActivityBundle\Notification;
use Chill\ActivityBundle\Entity\Activity;
use Chill\ActivityBundle\Repository\ActivityRepository;
use Chill\MainBundle\Entity\Notification;
use Chill\MainBundle\Notification\NotificationHandlerInterface;
final class ActivityNotificationHandler implements NotificationHandlerInterface
{
private ActivityRepository $activityRepository;
public function __construct(ActivityRepository $activityRepository)
{
$this->activityRepository = $activityRepository;
}
public function getTemplate(Notification $notification, array $options = []): string
{
return '@ChillActivity/Activity/showInNotification.html.twig';
}
public function getTemplateData(Notification $notification, array $options = []): array
{
return [
'notification' => $notification,
'activity' => $this->activityRepository->find($notification->getRelatedEntityId()),
];
}
public function supports(Notification $notification, array $options = []): bool
{
return $notification->getRelatedEntityClass() === Activity::class;
}
}

View File

@@ -0,0 +1,24 @@
<?php
namespace Chill\ActivityBundle\Notification;
use Chill\MainBundle\Entity\Notification;
use Chill\ActivityBundle\Entity\Activity;
final class ActivityNotificationRenderer
{
public function supports(Notification $notification, array $options = []): bool
{
return $notification->getRelatedEntityClass() == Activity::class;
}
public function getTemplate()
{
return '@ChillActivity/Activity/showInNotification.html.twig';
}
public function getTemplateData(Notification $notification)
{
return ['notification' => $notification];
}
}

View File

@@ -1,55 +1,60 @@
<?php <?php
/** /*
* Chill is a software for social workers * Chill is a software for social workers
* *
* For the full copyright and license information, please view * Copyright (C) 2021, Champs Libres Cooperative SCRLFS,
* the LICENSE file that was distributed with this source code. * <http://www.champs-libres.coop>, <info@champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
declare(strict_types=1);
namespace Chill\ActivityBundle\Repository; namespace Chill\ActivityBundle\Repository;
use Chill\ActivityBundle\Entity\Activity; use Chill\ActivityBundle\Entity\Activity;
use Chill\ActivityBundle\Entity\ActivityPresence; use Chill\MainBundle\Security\Resolver\CenterResolverDispatcher;
use Chill\ActivityBundle\Entity\ActivityType;
use Chill\ActivityBundle\Security\Authorization\ActivityVoter;
use Chill\MainBundle\Entity\Location;
use Chill\MainBundle\Entity\LocationType;
use Chill\MainBundle\Entity\Scope;
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
use Chill\MainBundle\Security\Resolver\CenterResolverDispatcherInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\Person; use Chill\PersonBundle\Entity\Person;
use Doctrine\DBAL\Types\Types; use Chill\ActivityBundle\Repository\ActivityRepository;
use Doctrine\ORM\AbstractQuery; use Chill\ActivityBundle\Security\Authorization\ActivityVoter;
use Doctrine\ORM\EntityManagerInterface; use Chill\MainBundle\Entity\Scope;
use Doctrine\ORM\Query\ResultSetMappingBuilder; use Doctrine\ORM\QueryBuilder;
use Doctrine\ORM\Query\Expr\Orx;
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Role\Role; use Symfony\Component\Security\Core\Role\Role;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Security\Core\Security; use Symfony\Component\Security\Core\Security;
use function count;
use function in_array;
final class ActivityACLAwareRepository implements ActivityACLAwareRepositoryInterface final class ActivityACLAwareRepository implements ActivityACLAwareRepositoryInterface
{ {
private AuthorizationHelper $authorizationHelper; private AuthorizationHelper $authorizationHelper;
private CenterResolverDispatcherInterface $centerResolverDispatcher; private TokenStorageInterface $tokenStorage;
private EntityManagerInterface $em;
private ActivityRepository $repository; private ActivityRepository $repository;
private EntityManagerInterface $em;
private Security $security; private Security $security;
private TokenStorageInterface $tokenStorage; private CenterResolverDispatcher $centerResolverDispatcher;
public function __construct( public function __construct(
AuthorizationHelper $authorizationHelper, AuthorizationHelper $authorizationHelper,
CenterResolverDispatcherInterface $centerResolverDispatcher, CenterResolverDispatcher $centerResolverDispatcher,
TokenStorageInterface $tokenStorage, TokenStorageInterface $tokenStorage,
ActivityRepository $repository, ActivityRepository $repository,
EntityManagerInterface $em, EntityManagerInterface $em,
@@ -63,112 +68,18 @@ final class ActivityACLAwareRepository implements ActivityACLAwareRepositoryInte
$this->security = $security; $this->security = $security;
} }
public function findByAccompanyingPeriod(AccompanyingPeriod $period, string $role, ?int $start = 0, ?int $limit = 1000, ?array $orderBy = []): array
{
$user = $this->security->getUser();
$center = $this->centerResolverDispatcher->resolveCenter($period);
if (0 === count($orderBy)) {
$orderBy = ['date' => 'DESC'];
}
$scopes = $this->authorizationHelper
->getReachableCircles($user, $role, $center);
return $this->em->getRepository(Activity::class)
->findByAccompanyingPeriod($period, $scopes, true, $limit, $start, $orderBy);
}
public function findByAccompanyingPeriodSimplified(AccompanyingPeriod $period, ?int $limit = 1000): array
{
$rsm = new ResultSetMappingBuilder($this->em);
$sql = '
SELECT
a.id AS activity_id,
date,
CASE WHEN durationtime IS NOT NULL THEN (EXTRACT(EPOCH from durationtime) / 60)::int ELSE 0 END AS durationtimeminute,
attendee_id,
comment_comment,
emergency,
sentreceived,
CASE WHEN traveltime IS NOT NULL THEN (EXTRACT(EPOCH from traveltime) / 60)::int ELSE 0 END AS traveltimeminute,
t.id AS type_id, t.name as type_name,
p.id AS presence_id, p.name AS presence_name,
location.id AS location_id, location.address_id, location.name AS location_name, location.phonenumber1, location.phonenumber2, location.email,
location.locationtype_id, locationtype.title AS locationtype_title,
users.userids AS userids,
thirdparties.thirdpartyids,
persons.personids,
actions.socialactionids,
issues.socialissueids
FROM activity a
LEFT JOIN chill_main_location location ON a.location_id = location.id
LEFT JOIN chill_main_location_type locationtype ON location.locationtype_id = locationtype.id
LEFT JOIN activitytpresence p ON a.attendee_id = p.id
LEFT JOIN activitytype t ON a.type_id = t.id
LEFT JOIN LATERAL (SELECT jsonb_agg(user_id) userids, activity_id FROM activity_user AS au WHERE a.id = au.activity_id GROUP BY activity_id) AS users ON TRUE
LEFT JOIN LATERAL (SELECT jsonb_agg(thirdparty_id) thirdpartyids, activity_id FROM activity_thirdparty AS au WHERE a.id = au.activity_id GROUP BY activity_id) AS thirdparties ON TRUE
LEFT JOIN LATERAL (SELECT jsonb_agg(person_id) personids, activity_id FROM activity_person AS au WHERE a.id = au.activity_id GROUP BY activity_id) AS persons ON TRUE
LEFT JOIN LATERAL (SELECT jsonb_agg(socialaction_id) socialactionids, activity_id FROM chill_activity_activity_chill_person_socialaction AS au WHERE a.id = au.activity_id GROUP BY activity_id) AS actions ON TRUE
LEFT JOIN LATERAL (SELECT jsonb_agg(socialissue_id) socialissueids, activity_id FROM chill_activity_activity_chill_person_socialissue AS au WHERE a.id = au.activity_id GROUP BY activity_id) AS issues ON TRUE
WHERE accompanyingperiod_id = ?
ORDER BY a.date DESC, a.id DESC
LIMIT ?
';
$rsm
->addEntityResult(Activity::class, 'a')
->addFieldResult('a', 'activity_id', 'id')
->addFieldResult('a', 'date', 'date')
->addFieldResult('a', 'comment', 'comment')
->addFieldResult('a', 'sentreceived', 'sentReceived')
->addFieldResult('a', 'emergency', 'emergency')
->addJoinedEntityResult(Location::class, 'location', 'a', 'location')
->addFieldResult('location', 'location_id', 'id')
->addFieldResult('location', 'location_name', 'name')
->addFieldResult('location', 'phonenumber1', 'phonenumber1')
->addFieldResult('location', 'phonenumber2', 'phonenumber2')
->addFieldResult('location', 'email', 'email')
->addJoinedEntityResult(LocationType::class, 'locationType', 'location', 'locationType')
->addFieldResult('locationType', 'locationtype_id', 'id')
->addFieldResult('locationType', 'locationtype_title', 'title')
->addJoinedEntityResult(ActivityType::class, 'activityType', 'a', 'activityType')
->addFieldResult('activityType', 'type_id', 'id')
->addFieldResult('activityType', 'type_name', 'name')
->addJoinedEntityResult(ActivityPresence::class, 'activityPresence', 'a', 'attendee')
->addFieldResult('activityPresence', 'presence_id', 'id')
->addFieldResult('activityPresence', 'presence_name', 'name')
// results which cannot be mapped into entity
->addScalarResult('comment_comment', 'comment', Types::TEXT)
->addScalarResult('userids', 'userIds', Types::JSON)
->addScalarResult('thirdpartyids', 'thirdPartyIds', Types::JSON)
->addScalarResult('personids', 'personIds', Types::JSON)
->addScalarResult('socialactionids', 'socialActionIds', Types::JSON)
->addScalarResult('socialissueids', 'socialIssueIds', Types::JSON)
->addScalarResult('durationtimeminute', 'durationTimeMinute', Types::INTEGER)
->addScalarResult('traveltimeminute', 'travelTimeMinute', Types::INTEGER);
$nq = $this->em->createNativeQuery($sql, $rsm);
$nq->setParameter(0, $period->getId())->setParameter(1, $limit);
return $nq->getResult(AbstractQuery::HYDRATE_ARRAY);
}
/** /**
* @param Person $person
* @param string $role
* @param int|null $start
* @param int|null $limit
* @param array $orderBy * @param array $orderBy
* * @return array|Activity[]
* @return Activity[]|array
*/ */
public function findByPerson(Person $person, string $role, ?int $start = 0, ?int $limit = 1000, ?array $orderBy = []): array public function findByPerson(Person $person, string $role, ?int $start = 0, ?int $limit = 1000, ?array $orderBy = []): array
{ {
$user = $this->security->getUser(); $user = $this->security->getUser();
$center = $this->centerResolverDispatcher->resolveCenter($person); $center = $this->centerResolverDispatcher->resolveCenter($person);
if (0 === count($orderBy)) { if (0 === count($orderBy)) {
$orderBy = ['date' => 'DESC']; $orderBy = ['date' => 'DESC'];
} }
@@ -178,6 +89,22 @@ final class ActivityACLAwareRepository implements ActivityACLAwareRepositoryInte
return $this->em->getRepository(Activity::class) return $this->em->getRepository(Activity::class)
->findByPersonImplied($person, $reachableScopes, $orderBy, $limit, $start); ->findByPersonImplied($person, $reachableScopes, $orderBy, $limit, $start);
;
}
public function findByAccompanyingPeriod(AccompanyingPeriod $period, string $role, ?int $start = 0, ?int $limit = 1000, ?array $orderBy = []): array
{
$user = $this->security->getUser();
$center = $this->centerResolverDispatcher->resolveCenter($period);
if (0 === count($orderBy)) {
$orderBy = ['date' => 'DESC'];
}
$scopes = $this->authorizationHelper
->getReachableCircles($user, $role, $center);
return $this->em->getRepository(Activity::class)
->findByAccompanyingPeriod($period, $scopes, true, $limit, $start, $orderBy);
} }
public function queryTimelineIndexer(string $context, array $args = []): array public function queryTimelineIndexer(string $context, array $args = []): array
@@ -195,7 +122,7 @@ final class ActivityACLAwareRepository implements ActivityACLAwareRepositoryInte
.'.'.$metadataActivity->getColumnName('date'), .'.'.$metadataActivity->getColumnName('date'),
'FROM' => $from, 'FROM' => $from,
'WHERE' => $where, 'WHERE' => $where,
'parameters' => $parameters, 'parameters' => $parameters
]; ];
} }
@@ -210,7 +137,8 @@ final class ActivityACLAwareRepository implements ActivityACLAwareRepositoryInte
.$metadataPerson->getTableName().'.'. .$metadataPerson->getTableName().'.'.
$associationMapping['joinColumns'][0]['referencedColumnName'] $associationMapping['joinColumns'][0]['referencedColumnName']
.' = ' .' = '
. $associationMapping['joinColumns'][0]['name']; .$associationMapping['joinColumns'][0]['name']
;
} }
private function getWhereClause(string $context, array $args): array private function getWhereClause(string $context, array $args): array
@@ -224,19 +152,18 @@ final class ActivityACLAwareRepository implements ActivityACLAwareRepositoryInte
$activityToScope = $metadataActivity->getAssociationMapping('scope')['joinColumns'][0]['name']; $activityToScope = $metadataActivity->getAssociationMapping('scope')['joinColumns'][0]['name'];
$personToCenter = $metadataPerson->getAssociationMapping('center')['joinColumns'][0]['name']; $personToCenter = $metadataPerson->getAssociationMapping('center')['joinColumns'][0]['name'];
// acls: // acls:
$role = new Role(ActivityVoter::SEE); $role = new Role(ActivityVoter::SEE);
$reachableCenters = $this->authorizationHelper->getReachableCenters( $reachableCenters = $this->authorizationHelper->getReachableCenters($this->tokenStorage->getToken()->getUser(),
$this->tokenStorage->getToken()->getUser(), $role);
$role
);
if (count($reachableCenters) === 0) { if (count($reachableCenters) === 0) {
// insert a dummy condition // insert a dummy condition
return 'FALSE = TRUE'; return 'FALSE = TRUE';
} }
if ('person' === $context) { if ($context === 'person') {
// we start with activities having the person_id linked to person // we start with activities having the person_id linked to person
$where .= sprintf('%s = ? AND ', $activityToPerson); $where .= sprintf('%s = ? AND ', $activityToPerson);
$parameters[] = $person->getId(); $parameters[] = $person->getId();
@@ -245,24 +172,21 @@ final class ActivityACLAwareRepository implements ActivityACLAwareRepositoryInte
// we add acl (reachable center and scopes) // we add acl (reachable center and scopes)
$where .= '('; // first loop for the for centers $where .= '('; // first loop for the for centers
$centersI = 0; // like centers#i $centersI = 0; // like centers#i
foreach ($reachableCenters as $center) { foreach ($reachableCenters as $center) {
// we pass if not in centers // we pass if not in centers
if (!in_array($center, $args['centers'], true)) { if (!\in_array($center, $args['centers'])) {
continue; continue;
} }
// we get all the reachable scopes for this center // we get all the reachable scopes for this center
$reachableScopes = $this->authorizationHelper->getReachableScopes($this->tokenStorage->getToken()->getUser(), $role, $center); $reachableScopes = $this->authorizationHelper->getReachableScopes($this->tokenStorage->getToken()->getUser(), $role, $center);
// we get the ids for those scopes // we get the ids for those scopes
$reachablesScopesId = array_map( $reachablesScopesId = array_map(
static function (Scope $scope) { function(Scope $scope) { return $scope->getId(); },
return $scope->getId();
},
$reachableScopes $reachableScopes
); );
// if not the first center // if not the first center
if (0 < $centersI) { if ($centersI > 0) {
$where .= ') OR ('; $where .= ') OR (';
} }
@@ -275,20 +199,21 @@ final class ActivityACLAwareRepository implements ActivityACLAwareRepositoryInte
$scopesI = 0; //like scope#i $scopesI = 0; //like scope#i
foreach ($reachablesScopesId as $scopeId) { foreach ($reachablesScopesId as $scopeId) {
if (0 < $scopesI) { if ($scopesI > 0) {
$where .= ' OR '; $where .= ' OR ';
} }
$where .= sprintf(' %s.%s = ? ', $metadataActivity->getTableName(), $activityToScope); $where .= sprintf(' %s.%s = ? ', $metadataActivity->getTableName(), $activityToScope);
$parameters[] = $scopeId; $parameters[] = $scopeId;
++$scopesI; $scopesI ++;
} }
// close loop for scopes // close loop for scopes
$where .= ') '; $where .= ') ';
++$centersI; $centersI++;
} }
// close loop for centers // close loop for centers
$where .= ')'; $where .= ')';
return [$where, $parameters]; return [$where, $parameters];
} }
} }

View File

@@ -1,14 +1,5 @@
<?php <?php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Chill\ActivityBundle\Repository; namespace Chill\ActivityBundle\Repository;
use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Entity\AccompanyingPeriod;
@@ -17,21 +8,12 @@ use Chill\PersonBundle\Entity\Person;
interface ActivityACLAwareRepositoryInterface interface ActivityACLAwareRepositoryInterface
{ {
/** /**
* @return Activity[]|array * @return array|Activity[]
*/
public function findByAccompanyingPeriod(AccompanyingPeriod $period, string $role, ?int $start = 0, ?int $limit = 1000, ?array $orderBy = []): array;
/**
* Return a list of activities, simplified as array (not object).
*
* The aim of this method is to get a long list of activities and keep performance.
*
* @return array an array of array, each item representing an activity
*/
public function findByAccompanyingPeriodSimplified(AccompanyingPeriod $period, ?int $limit = 1000): array;
/**
* @return Activity[]|array
*/ */
public function findByPerson(Person $person, string $role, ?int $start = 0, ?int $limit = 1000, ?array $orderBy = []): array; public function findByPerson(Person $person, string $role, ?int $start = 0, ?int $limit = 1000, ?array $orderBy = []): array;
/**
* @return array|Activity[]
*/
public function findByAccompanyingPeriod(AccompanyingPeriod $period, string $role, ?int $start = 0, ?int $limit = 1000, ?array $orderBy = []): array;
} }

View File

@@ -1,12 +1,5 @@
<?php <?php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1); declare(strict_types=1);
namespace Chill\ActivityBundle\Repository; namespace Chill\ActivityBundle\Repository;

View File

@@ -1,12 +1,5 @@
<?php <?php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1); declare(strict_types=1);
namespace Chill\ActivityBundle\Repository; namespace Chill\ActivityBundle\Repository;

View File

@@ -1,12 +1,5 @@
<?php <?php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1); declare(strict_types=1);
namespace Chill\ActivityBundle\Repository; namespace Chill\ActivityBundle\Repository;
@@ -31,8 +24,34 @@ class ActivityRepository extends ServiceEntityRepository
} }
/** /**
* @deprecated use @see{ActivityACLAwareRepositoryInterface::findByAccompanyingPeriod} * @return Activity[]
* */
public function findByPersonImplied(Person $person, array $scopes, ?array $orderBy = [ 'date' => 'DESC'], ?int $limit = 100, ?int $offset = 0): array
{
$qb = $this->createQueryBuilder('a');
$qb->select('a');
$qb
->where($qb->expr()->in('a.scope', ':scopes'))
->setParameter('scopes', $scopes)
->andWhere(
$qb->expr()->orX(
$qb->expr()->eq('a.person', ':person'),
':person MEMBER OF a.persons'
)
)
->setParameter('person', $person);
foreach ($orderBy as $k => $dir) {
$qb->addOrderBy('a.'.$k, $dir);
}
$qb->setMaxResults($limit)->setFirstResult($offset);
return $qb->getQuery()->getResult();
}
/**
* @return Activity[] * @return Activity[]
*/ */
public function findByAccompanyingPeriod(AccompanyingPeriod $period, array $scopes, ?bool $allowNullScope = false, ?int $limit = 100, ?int $offset = 0, array $orderBy = ['date' => 'desc']): array public function findByAccompanyingPeriod(AccompanyingPeriod $period, array $scopes, ?bool $allowNullScope = false, ?int $limit = 100, ?int $offset = 0, array $orderBy = ['date' => 'desc']): array
@@ -69,32 +88,4 @@ class ActivityRepository extends ServiceEntityRepository
return $qb->getQuery()->getResult(); return $qb->getQuery()->getResult();
} }
/**
* @return Activity[]
*/
public function findByPersonImplied(Person $person, array $scopes, ?array $orderBy = ['date' => 'DESC'], ?int $limit = 100, ?int $offset = 0): array
{
$qb = $this->createQueryBuilder('a');
$qb->select('a');
$qb
->where($qb->expr()->in('a.scope', ':scopes'))
->setParameter('scopes', $scopes)
->andWhere(
$qb->expr()->orX(
$qb->expr()->eq('a.person', ':person'),
':person MEMBER OF a.persons'
)
)
->setParameter('person', $person);
foreach ($orderBy as $k => $dir) {
$qb->addOrderBy('a.' . $k, $dir);
}
$qb->setMaxResults($limit)->setFirstResult($offset);
return $qb->getQuery()->getResult();
}
} }

View File

@@ -1,12 +1,5 @@
<?php <?php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1); declare(strict_types=1);
namespace Chill\ActivityBundle\Repository; namespace Chill\ActivityBundle\Repository;

View File

@@ -1,12 +1,5 @@
<?php <?php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1); declare(strict_types=1);
namespace Chill\ActivityBundle\Repository; namespace Chill\ActivityBundle\Repository;

View File

@@ -9,9 +9,8 @@ div.new-activity-select-type {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
flex-wrap: wrap; flex-wrap: wrap;
justify-content: flex-start; justify-content: center;
gap: 12px; gap: 12px;
margin-bottom: 30px;
div.bloc { div.bloc {
width: 200px; width: 200px;
@@ -25,18 +24,14 @@ div.new-activity-select-type {
} }
//// ACTIVITY LIST PAGE //// ACTIVITY LIST PAGE
// precise dashboard specific details // precise badge-title specific details
p.date-label {
display: inline-block;
margin: 0 0.5em 0 0;
font-weight: 700;
font-size: 18pt;
}
div.dashboard,
h4.badge-title,
h3.badge-title,
h2.badge-title { h2.badge-title {
div.duration {
font-size: smaller;
padding-left: 1em;
margin-top: 1em;
}
ul.list-content { ul.list-content {
font-size: 70%; font-size: 70%;
list-style-type: none; list-style-type: none;
@@ -44,13 +39,16 @@ h2.badge-title {
margin: 0; margin: 0;
li { li {
margin-bottom: 0.2em; margin-bottom: 0.2em;
// exception: change bg color for action badges above dashboard // exception: change bg color for action badges above badge-title
.bg-light { .bg-light {
background-color: $chill-light-gray !important; background-color: $chill-light-gray !important;
} }
} }
} }
} }
div.main {
padding: 1em;
}
//// ACTIVITY SHOW AND FORM PAGES //// ACTIVITY SHOW AND FORM PAGES
// Exceptions for flex-bloc in concerned-groups // Exceptions for flex-bloc in concerned-groups

View File

@@ -1,23 +0,0 @@
div.chill-dropzone__below-zone {
a.btn-delete {
display: none;
}
}
ul[data-collection-name="documents"] {
button.remove-entry {
margin: 0.5rem 0!important;
}
}
// do it in js does not work
// document.addEventListener('DOMContentLoaded', e => {
// const dropzoneBelow = document.querySelectorAll('div.chill-dropzone__below-zone');
// dropzoneBelow.forEach(
// d => {
// const a = d.querySelector('a.btn-delete');
// d.removeChild(a);
// }
// )
// });

View File

@@ -1,7 +1,7 @@
<template> <template>
<concerned-groups v-if="hasPerson"></concerned-groups> <concerned-groups></concerned-groups>
<social-issues-acc v-if="hasSocialIssues"></social-issues-acc> <social-issues-acc></social-issues-acc>
<location v-if="hasLocation"></location> <location></location>
</template> </template>
<script> <script>
@@ -11,7 +11,6 @@ import Location from './components/Location.vue';
export default { export default {
name: "App", name: "App",
props: ['hasSocialIssues', 'hasLocation', 'hasPerson'],
components: { components: {
ConcernedGroups, ConcernedGroups,
SocialIssuesAcc, SocialIssuesAcc,

View File

@@ -1,5 +1,4 @@
import { getSocialIssues } from 'ChillPersonAssets/vuejs/AccompanyingCourse/api.js'; import { getSocialIssues } from 'ChillPersonAssets/vuejs/AccompanyingCourse/api.js';
import { fetchResults } from 'ChillMainAssets/lib/api/apiMethods';
/* /*
* Load socialActions by socialIssue (id) * Load socialActions by socialIssue (id)
@@ -13,28 +12,33 @@ const getSocialActionByIssue = (id) => {
}); });
}; };
const getLocations = () => fetchResults('/api/1.0/main/location.json'); /*
* Load Locations
const getLocationTypes = () => fetchResults('/api/1.0/main/location-type.json'); */
const getLocations = () => {
const getUserCurrentLocation = const url = `/api/1.0/main/location.json`;
() => fetch('/api/1.0/main/user-current-location.json') return fetch(url)
.then(response => { .then(response => {
if (response.ok) { return response.json(); } if (response.ok) { return response.json(); }
throw Error('Error with request resource response'); throw Error('Error with request resource response');
}); });
/*
* Load Location Type by defaultFor
* @param {string} entity - can be "person" or "thirdparty"
*/
const getLocationTypeByDefaultFor = (entity) => {
return getLocationTypes().then(results =>
results.filter(t => t.defaultFor === entity)[0]
);
}; };
/*
* Load Location Types
*/
const getLocationTypes = () => {
const url = `/api/1.0/main/location-type.json`;
return fetch(url)
.then(response => {
if (response.ok) { return response.json(); }
throw Error('Error with request resource response');
});
};
/*
* Post a Location
*/
const postLocation = (body) => { const postLocation = (body) => {
const url = `/api/1.0/main/location.json`; const url = `/api/1.0/main/location.json`;
return fetch(url, { return fetch(url, {
@@ -55,7 +59,5 @@ export {
getSocialActionByIssue, getSocialActionByIssue,
getLocations, getLocations,
getLocationTypes, getLocationTypes,
getLocationTypeByDefaultFor, postLocation
postLocation,
getUserCurrentLocation
}; };

View File

@@ -1,36 +1,34 @@
<template> <template>
<teleport to="#add-persons" v-if="isComponentVisible"> <teleport to="#add-persons">
<div class="flex-bloc concerned-groups" :class="getContext"> <div class="flex-bloc concerned-groups" :class="getContext">
<persons-bloc <persons-bloc
v-for="bloc in contextPersonsBlocs" v-for="bloc in contextPersonsBlocs"
v-bind:key="bloc.key" v-bind:key="bloc.key"
v-bind:bloc="bloc" v-bind:bloc="bloc"
v-bind:blocWidth="getBlocWidth"
v-bind:setPersonsInBloc="setPersonsInBloc"> v-bind:setPersonsInBloc="setPersonsInBloc">
</persons-bloc> </persons-bloc>
</div> </div>
<div v-if="getContext === 'accompanyingCourse' && suggestedEntities.length > 0">
<ul class="list-suggest add-items inline"> <div v-if="getContext === 'accompanyingCourse' && filterSuggestedPersons.length > 0">
<li v-for="(p, i) in suggestedEntities" @click="addSuggestedEntity(p)" :key="`suggestedEntities-${i}`"> <ul class="list-unstyled">
<person-text v-if="p.type === 'person'" :person="p"></person-text> <li v-for="p in filterSuggestedPersons" @click="addNewPerson(p)">
<span v-else>{{ p.text }}</span> <span class="badge bg-primary" style="cursor: pointer;">
<i class="fa fa-plus fa-fw text-success"></i>
{{ p.text }}
</span>
</li> </li>
</ul> </ul>
</div> </div>
<ul class="record_actions">
<li class="add-persons">
<add-persons <add-persons
buttonTitle="activity.add_persons" buttonTitle="activity.add_persons"
modalTitle="activity.add_persons" modalTitle="activity.add_persons"
v-bind:key="addPersons.key" v-bind:key="addPersons.key"
v-bind:options="addPersonsOptions" v-bind:options="addPersons.options"
@addNewPersons="addNewPersons" @addNewPersons="addNewPersons"
ref="addPersons"> ref="addPersons">
</add-persons> </add-persons>
</li>
</ul>
</teleport> </teleport>
</template> </template>
@@ -39,14 +37,12 @@
import { mapState, mapGetters } from 'vuex'; import { mapState, mapGetters } from 'vuex';
import AddPersons from 'ChillPersonAssets/vuejs/_components/AddPersons.vue'; import AddPersons from 'ChillPersonAssets/vuejs/_components/AddPersons.vue';
import PersonsBloc from './ConcernedGroups/PersonsBloc.vue'; import PersonsBloc from './ConcernedGroups/PersonsBloc.vue';
import PersonText from 'ChillPersonAssets/vuejs/_components/Entity/PersonText.vue';
export default { export default {
name: "ConcernedGroups", name: "ConcernedGroups",
components: { components: {
AddPersons, AddPersons,
PersonsBloc, PersonsBloc
PersonText
}, },
data() { data() {
return { return {
@@ -59,35 +55,38 @@ export default {
{ key: 'personsAssociated', { key: 'personsAssociated',
title: 'activity.bloc_persons_associated', title: 'activity.bloc_persons_associated',
persons: [], persons: [],
included: window.activity ? window.activity.activityType.personsVisible !== 0 : true included: false
}, },
{ key: 'personsNotAssociated', { key: 'personsNotAssociated',
title: 'activity.bloc_persons_not_associated', title: 'activity.bloc_persons_not_associated',
persons: [], persons: [],
included: window.activity ? window.activity.activityType.personsVisible !== 0 : true included: false
}, },
{ key: 'thirdparty', { key: 'thirdparty',
title: 'activity.bloc_thirdparty', title: 'activity.bloc_thirdparty',
persons: [], persons: [],
included: window.activity ? window.activity.activityType.thirdPartiesVisible !== 0 : true included: true
}, },
{ key: 'users', { key: 'users',
title: 'activity.bloc_users', title: 'activity.bloc_users',
persons: [], persons: [],
included: window.activity ? window.activity.activityType.usersVisible !== 0 : true included: true
}, },
], ],
addPersons: { addPersons: {
key: 'activity' key: 'activity',
options: {
type: ['person', 'thirdparty', 'user'],
priority: null,
uniq: false,
button: {
size: 'btn-sm'
}
}
} }
} }
}, },
computed: { computed: {
isComponentVisible() {
return window.activity
? (window.activity.activityType.personsVisible !== 0 || window.activity.activityType.thirdPartiesVisible !== 0 || window.activity.activityType.usersVisible !== 0)
: true
},
...mapState({ ...mapState({
persons: state => state.activity.persons, persons: state => state.activity.persons,
thirdParties: state => state.activity.thirdParties, thirdParties: state => state.activity.thirdParties,
@@ -95,40 +94,13 @@ export default {
accompanyingCourse: state => state.activity.accompanyingPeriod accompanyingCourse: state => state.activity.accompanyingPeriod
}), }),
...mapGetters([ ...mapGetters([
'suggestedEntities' 'filterSuggestedPersons'
]), ]),
getContext() { getContext() {
return (this.accompanyingCourse) ? "accompanyingCourse" : "person"; return (this.accompanyingCourse) ? "accompanyingCourse" : "person";
}, },
contextPersonsBlocs() { contextPersonsBlocs() {
return this.personsBlocs.filter(bloc => bloc.included !== false); return this.personsBlocs.filter(bloc => bloc.included !== false);
},
addPersonsOptions() {
let optionsType = [];
if (window.activity) {
if (window.activity.activityType.personsVisible !== 0) {
optionsType.push('person')
}
if (window.activity.activityType.thirdPartiesVisible !== 0) {
optionsType.push('thirdparty')
}
if (window.activity.activityType.usersVisible !== 0) {
optionsType.push('user')
}
} else {
optionsType = ['person', 'thirdparty', 'user'];
}
return {
type: optionsType,
priority: null,
uniq: false,
button: {
size: 'btn-sm'
}
}
},
getBlocWidth() {
return Math.round(100/(this.contextPersonsBlocs.length)) + '%';
} }
}, },
mounted() { mounted() {
@@ -199,7 +171,7 @@ export default {
}, },
addNewPersons({ selected, modal }) { addNewPersons({ selected, modal }) {
console.log('@@@ CLICK button addNewPersons', selected); console.log('@@@ CLICK button addNewPersons', selected);
selected.forEach((item) => { selected.forEach(function(item) {
this.$store.dispatch('addPersonsInvolved', item); this.$store.dispatch('addPersonsInvolved', item);
}, this }, this
); );
@@ -207,7 +179,7 @@ export default {
modal.showModal = false; modal.showModal = false;
this.setPersonsInBloc(); this.setPersonsInBloc();
}, },
addSuggestedEntity(person) { addNewPerson(person) {
this.$store.dispatch('addPersonsInvolved', { result: person, type: 'person' }); this.$store.dispatch('addPersonsInvolved', { result: person, type: 'person' });
this.setPersonsInBloc(); this.setPersonsInBloc();
}, },

View File

@@ -1,29 +1,26 @@
<template> <template>
<li> <li>
<span :title="person.text" @click.prevent="$emit('remove', person)"> <span class="badge bg-primary" :title="person.text">
<span class="chill_denomination"> <span class="chill_denomination">
<person-text :person="person" :isCut="true"></person-text> {{ textCutted }}
</span> </span>
<a class="fa fa-fw fa-times text-danger text-decoration-none"
@click.prevent="$emit('remove', person)">
</a>
</span> </span>
</li> </li>
</template> </template>
<script> <script>
import PersonText from 'ChillPersonAssets/vuejs/_components/Entity/PersonText.vue';
export default { export default {
name: "PersonBadge", name: "PersonBadge",
props: ['person'], props: ['person'],
components: { computed: {
PersonText textCutted() {
let more = (this.person.text.length > 15) ?'…' : '';
return this.person.text.slice(0,15) + more;
}
}, },
// computed: {
// textCutted() {
// let more = (this.person.text.length > 15) ?'…' : '';
// return this.person.text.slice(0,15) + more;
// }
// },
emits: ['remove'], emits: ['remove'],
} }
</script> </script>

View File

@@ -1,11 +1,11 @@
<template> <template>
<div class="item-bloc" :style="{ 'flex-basis': blocWidth }"> <div class="item-bloc">
<div class="item-row"> <div class="item-row">
<div class="item-col"> <div class="item-col">
<h4>{{ $t(bloc.title) }}</h4> <h4>{{ $t(bloc.title) }}</h4>
</div> </div>
<div class="item-col"> <div class="item-col">
<ul class="list-suggest remove-items"> <ul class="list-content">
<person-badge <person-badge
v-for="person in bloc.persons" v-for="person in bloc.persons"
v-bind:key="person.id" v-bind:key="person.id"
@@ -25,7 +25,7 @@ export default {
components: { components: {
PersonBadge PersonBadge
}, },
props: ['bloc', 'setPersonsInBloc', 'blocWidth'], props: ['bloc', 'setPersonsInBloc'],
methods: { methods: {
removePerson(item) { removePerson(item) {
console.log('@@ CLICK remove person: item', item); console.log('@@ CLICK remove person: item', item);

View File

@@ -1,10 +1,11 @@
<template> <template>
<teleport to="#location"> <teleport to="#location">
<div class="mb-3 row"> <div class="mb-3 row">
<label :class="locationClassList"> <label class="col-form-label col-sm-4">
{{ $t("activity.location") }} {{ $t('activity.location') }}
</label> </label>
<div class="col-sm-8"> <div class="col-sm-8">
<VueMultiselect <VueMultiselect
name="selectLocation" name="selectLocation"
id="selectLocation" id="selectLocation"
@@ -15,63 +16,59 @@
:searchable="true" :searchable="true"
:placeholder="$t('activity.choose_location')" :placeholder="$t('activity.choose_location')"
:custom-label="customLabel" :custom-label="customLabel"
:select-label="$t('multiselect.select_label')" :options="locations"
:deselect-label="$t('multiselect.deselect_label')" v-model="location">
:selected-label="$t('multiselect.selected_label')"
:options="availableLocations"
group-values="locations"
group-label="locationGroup"
v-model="location"
>
</VueMultiselect> </VueMultiselect>
<new-location v-bind:availableLocations="availableLocations"></new-location>
<new-location v-bind:locations="locations"></new-location>
</div> </div>
</div> </div>
</teleport> </teleport>
</template> </template>
<script> <script>
import { mapState, mapGetters } from "vuex"; import { mapState } from "vuex";
import VueMultiselect from "vue-multiselect"; import VueMultiselect from 'vue-multiselect';
import NewLocation from "./Location/NewLocation.vue"; import NewLocation from './Location/NewLocation.vue';
import { getLocations } from '../api.js';
export default { export default {
name: "Location", name: "Location",
components: { components: {
NewLocation, NewLocation,
VueMultiselect, VueMultiselect
}, },
data() { data() {
return { return {
locationClassList: locations: []
`col-form-label col-sm-4 ${document.querySelector('input#chill_activitybundle_activity_location').getAttribute('required') ? 'required' : ''}`,
} }
}, },
computed: { computed: {
...mapState(["activity", "availableLocations"]), ...mapState(['activity']),
...mapGetters(["suggestedEntities"]),
location: { location: {
get() { get() {
return this.activity.location; return this.activity.location;
}, },
set(value) { set(value) {
this.$store.dispatch("updateLocation", value); this.$store.dispatch('updateLocation', value);
}, }
}
}, },
mounted() {
this.getLocationsList();
}, },
methods: { methods: {
labelAccompanyingCourseLocation(value) { getLocationsList() {
return `${value.address.text} (${value.locationType.title.fr})` getLocations().then(response => new Promise(resolve => {
console.log('getLocations', response);
this.locations = response.results;
resolve();
}))
}, },
customLabel(value) { customLabel(value) {
return value.locationType return `${value.locationType.title.fr} ${value.name ? value.name : ''}`;
? value.name }
? value.name === '__AccompanyingCourseLocation__' }
? this.labelAccompanyingCourseLocation(value) }
: `${value.name} (${value.locationType.title.fr})`
: value.locationType.title.fr
: '';
},
},
};
</script> </script>

View File

@@ -18,10 +18,13 @@
</template> </template>
<template v-slot:body> <template v-slot:body>
<form> <form>
<div class="alert alert-warning" v-if="errors.length"> <div class="form-floating mb-3">
<p v-if="errors.length">
<b>{{ $t('activity.errors') }}</b>
<ul> <ul>
<li v-for="(e, i) in errors" :key="i">{{ e }}</li> <li v-for="error in errors" :key="error">{{ error }}</li>
</ul> </ul>
</p>
</div> </div>
<div class="form-floating mb-3"> <div class="form-floating mb-3">
@@ -59,7 +62,6 @@
<input class="form-control form-control-lg" id="email" v-model="inputEmail" placeholder /> <input class="form-control form-control-lg" id="email" v-model="inputEmail" placeholder />
<label for="email">{{ $t('activity.location_fields.email') }}</label> <label for="email">{{ $t('activity.location_fields.email') }}</label>
</div> </div>
</form> </form>
</template> </template>
<template v-slot:footer> <template v-slot:footer>
@@ -79,8 +81,7 @@
import Modal from 'ChillMainAssets/vuejs/_components/Modal.vue'; import Modal from 'ChillMainAssets/vuejs/_components/Modal.vue';
import AddAddress from "ChillMainAssets/vuejs/Address/components/AddAddress.vue"; import AddAddress from "ChillMainAssets/vuejs/Address/components/AddAddress.vue";
import { mapState } from "vuex"; import { mapState } from "vuex";
import { getLocationTypes } from "../../api"; import { getLocationTypes, postLocation } from "../../api";
import { makeFetch } from 'ChillMainAssets/lib/api/apiMethods';
export default { export default {
name: "NewLocation", name: "NewLocation",
@@ -88,7 +89,7 @@ export default {
Modal, Modal,
AddAddress, AddAddress,
}, },
props: ['availableLocations'], props: ['locations'],
data() { data() {
return { return {
errors: [], errors: [],
@@ -213,15 +214,18 @@ export default {
return cond; return cond;
}, },
getLocationTypesList() { getLocationTypesList() {
getLocationTypes().then(results => { getLocationTypes().then(response => new Promise(resolve => {
this.locationTypes = results.filter(t => t.availableForUsers === true); console.log('getLocationTypes', response);
}) this.locationTypes = response.results.filter(t => t.availableForUsers === true);
resolve();
}))
}, },
openModal() { openModal() {
this.modal.showModal = true; this.modal.showModal = true;
}, },
saveNewLocation() { saveNewLocation() {
if (this.checkForm()) { if (this.checkForm()) {
console.log('saveNewLocation', this.selected);
let body = { let body = {
type: 'location', type: 'location',
name: this.selected.name, name: this.selected.name,
@@ -240,28 +244,24 @@ export default {
} }
}); });
} }
postLocation(body)
makeFetch('POST', '/api/1.0/main/location.json', body) .then(
.then(response => { location => new Promise(resolve => {
this.$store.dispatch('addAvailableLocationGroup', { console.log('postLocation', location);
locationGroup: 'Localisations nouvellement créées', this.locations.push(location);
locations: [response] this.$store.dispatch('updateLocation', location);
}); resolve();
this.$store.dispatch('updateLocation', response);
this.modal.showModal = false; this.modal.showModal = false;
}) })
.catch((error) => { ).catch(
if (error.name === 'ValidationException') { err => {
for (let v of error.violations) { this.errors.push(err.message);
this.errors.push(v);
} }
} else { );
this.errors.push('An error occurred');
}
})
}; };
}, },
submitNewAddress(payload) { submitNewAddress(payload) {
console.log('submitNewAddress', payload);
this.selected.addressId = payload.addressId; this.selected.addressId = payload.addressId;
this.addAddress.context.addressId = payload.addressId; this.addAddress.context.addressId = payload.addressId;
this.addAddress.context.edit = true; this.addAddress.context.edit = true;

View File

@@ -3,15 +3,15 @@
<div class="mb-3 row"> <div class="mb-3 row">
<div class="col-4"> <div class="col-4">
<label :class="socialIssuesClassList">{{ $t('activity.social_issues') }}</label> <label class="col-form-label">{{ $t('activity.social_issues') }}</label>
</div> </div>
<div class="col-8"> <div class="col-8">
<check-social-issue <check-social-issue
v-for="issue in socialIssuesList" v-for="issue in socialIssuesList"
:key="issue.id" v-bind:key="issue.id"
:issue="issue" v-bind:issue="issue"
:selection="socialIssuesSelected" v-bind:selection="socialIssuesSelected"
@updateSelected="updateIssuesSelected"> @updateSelected="updateIssuesSelected">
</check-social-issue> </check-social-issue>
@@ -21,18 +21,18 @@
label="text" label="text"
track-by="id" track-by="id"
open-direction="bottom" open-direction="bottom"
:close-on-select="true" v-bind:close-on-select="true"
:preserve-search="false" v-bind:preserve-search="false"
:reset-after="true" v-bind:reset-after="true"
:hide-selected="true" v-bind:hide-selected="true"
:taggable="false" v-bind:taggable="false"
:multiple="false" v-bind:multiple="false"
:searchable="true" v-bind:searchable="true"
:allow-empty="true" v-bind:allow-empty="true"
:show-labels="false" v-bind:show-labels="false"
:loading="issueIsLoading" v-bind:loading="issueIsLoading"
:placeholder="$t('activity.choose_other_social_issue')" v-bind:placeholder="$t('activity.choose_other_social_issue')"
:options="socialIssuesOther" v-bind:options="socialIssuesOther"
@select="addIssueInList"> @select="addIssueInList">
</VueMultiselect> </VueMultiselect>
</div> </div>
@@ -42,7 +42,7 @@
<div class="mb-3 row"> <div class="mb-3 row">
<div class="col-4"> <div class="col-4">
<label :class="socialActionsClassList">{{ $t('activity.social_actions') }}</label> <label class="col-form-label">{{ $t('activity.social_actions') }}</label>
</div> </div>
<div class="col-8"> <div class="col-8">
@@ -50,26 +50,19 @@
<i class="chill-green fa fa-circle-o-notch fa-spin fa-lg"></i> <i class="chill-green fa fa-circle-o-notch fa-spin fa-lg"></i>
</div> </div>
<span v-else-if="socialIssuesSelected.length === 0" class="inline-choice chill-no-data-statement mt-3">
{{ $t('activity.select_first_a_social_issue') }}
</span>
<template v-else-if="socialActionsList.length > 0">
<check-social-action <check-social-action
v-if="socialIssuesSelected.length || socialActionsSelected.length" v-if="socialIssuesSelected.length || socialActionsSelected.length"
v-for="action in socialActionsList" v-for="action in socialActionsList"
:key="action.id" v-bind:key="action.id"
:action="action" v-bind:action="action"
:selection="socialActionsSelected" v-bind:selection="socialActionsSelected"
@updateSelected="updateActionsSelected"> @updateSelected="updateActionsSelected">
</check-social-action> </check-social-action>
</template>
<span v-else-if="actionAreLoaded && socialActionsList.length === 0" class="inline-choice chill-no-data-statement mt-3"> <span v-else class="inline-choice chill-no-data-statement mt-3">
{{ $t('activity.social_action_list_empty') }} {{ $t('activity.select_first_a_social_issue') }}
</span> </span>
</div> </div>
</div> </div>
@@ -92,12 +85,7 @@ export default {
data() { data() {
return { return {
issueIsLoading: false, issueIsLoading: false,
actionIsLoading: false, actionIsLoading: false
actionAreLoaded: false,
socialIssuesClassList:
`col-form-label ${document.querySelector('input#chill_activitybundle_activity_socialIssues').getAttribute('required') ? 'required' : ''}`,
socialActionsClassList:
`col-form-label ${document.querySelector('input#chill_activitybundle_activity_socialActions').getAttribute('required') ? 'required' : ''}`,
} }
}, },
computed: { computed: {
@@ -111,7 +99,7 @@ export default {
return this.$store.state.socialIssuesOther; return this.$store.state.socialIssuesOther;
}, },
socialActionsList() { socialActionsList() {
return this.$store.getters.socialActionsListSorted; return this.$store.state.socialActionsList;
}, },
socialActionsSelected() { socialActionsSelected() {
return this.$store.state.activity.socialActions; return this.$store.state.activity.socialActions;
@@ -121,7 +109,6 @@ export default {
/* Load others issues in multiselect /* Load others issues in multiselect
*/ */
this.issueIsLoading = true; this.issueIsLoading = true;
this.actionAreLoaded = false;
getSocialIssues().then(response => new Promise((resolve, reject) => { getSocialIssues().then(response => new Promise((resolve, reject) => {
this.$store.commit('updateIssuesOther', response.results); this.$store.commit('updateIssuesOther', response.results);
@@ -154,8 +141,6 @@ export default {
this.$store.commit('filterList', 'actions'); this.$store.commit('filterList', 'actions');
this.issueIsLoading = false; this.issueIsLoading = false;
this.actionAreLoaded = true;
this.updateActionsList();
resolve(); resolve();
})); }));
}, },
@@ -188,6 +173,7 @@ export default {
to get social actions concerned to get social actions concerned
*/ */
updateActionsList() { updateActionsList() {
//console.log('updateActionsList');
this.resetActionsList(); this.resetActionsList();
this.socialIssuesSelected.forEach(item => { this.socialIssuesSelected.forEach(item => {
@@ -202,7 +188,6 @@ export default {
this.$store.commit('filterList', 'actions'); this.$store.commit('filterList', 'actions');
this.actionIsLoading = false; this.actionIsLoading = false;
this.actionAreLoaded = true;
resolve(); resolve();
})); }));
}, this); }, this);
@@ -211,7 +196,6 @@ export default {
*/ */
resetActionsList() { resetActionsList() {
this.$store.commit('resetActionsList'); this.$store.commit('resetActionsList');
this.actionAreLoaded = false;
this.socialActionsSelected.forEach(item => { this.socialActionsSelected.forEach(item => {
this.$store.commit('addActionInList', item); this.$store.commit('addActionInList', item);
}, this); }, this);

View File

@@ -10,7 +10,7 @@
v-bind:value="action" v-bind:value="action"
/> />
<label class="form-check-label" v-bind:for="action.id"> <label class="form-check-label" v-bind:for="action.id">
<span class="badge bg-light text-dark">{{ action.text }}</span> {{ action.text }}
</label> </label>
</div> </div>
@@ -34,15 +34,3 @@ export default {
} }
} }
</script> </script>
<style lang="scss" scoped>
@import 'ChillMainAssets/module/bootstrap/shared';
@import 'ChillPersonAssets/chill/scss/mixins';
@import 'ChillMainAssets/chill/scss/chill_variables';
span.badge {
@include badge_social($social-action-color);
font-size: 95%;
margin-bottom: 5px;
margin-right: 1em;
}
</style>

View File

@@ -10,7 +10,7 @@
v-bind:value="issue" v-bind:value="issue"
/> />
<label class="form-check-label" v-bind:for="issue.id"> <label class="form-check-label" v-bind:for="issue.id">
<span class="badge bg-chill-l-gray text-dark">{{ issue.text }}</span> {{ issue.text }}
</label> </label>
</div> </div>
@@ -34,15 +34,3 @@ export default {
} }
} }
</script> </script>
<style lang="scss" scoped>
@import 'ChillMainAssets/module/bootstrap/shared';
@import 'ChillPersonAssets/chill/scss/mixins';
@import 'ChillMainAssets/chill/scss/chill_variables';
span.badge {
@include badge_social($social-issue-color);
font-size: 95%;
margin-bottom: 5px;
margin-right: 1em;
}
</style>

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