mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-06-07 18:44:08 +00:00
Merge remote-tracking branch 'origin/master' into issue541_change_moving_date
This commit is contained in:
commit
405694a0b4
44
CHANGELOG.md
44
CHANGELOG.md
@ -12,10 +12,37 @@ and this project adheres to
|
|||||||
|
|
||||||
<!-- write down unreleased development here -->
|
<!-- write down unreleased development here -->
|
||||||
* [person] household address: add a form for editing the validFrom date (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/541)
|
* [person] household address: add a form for editing the validFrom date (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/541)
|
||||||
|
* [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
|
||||||
|
|
||||||
|
|
||||||
|
## Test releases
|
||||||
|
|
||||||
|
### 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
|
* [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)
|
(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] 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)
|
* [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
|
* [ACL] fix allow to see the course, event if the scope'course does not contains the scope's user
|
||||||
@ -79,8 +106,7 @@ and this project adheres to
|
|||||||
* [parcours] Create document buttons made sticky (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/532)
|
* [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)
|
* [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)
|
* [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)
|
||||||
## Test releases
|
|
||||||
|
|
||||||
### test release 2022-02-21
|
### test release 2022-02-21
|
||||||
|
|
||||||
@ -108,8 +134,6 @@ and this project adheres to
|
|||||||
* [bug]: fix confidential toggle of address in thirdpartyrenderbox (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/460)
|
* [bug]: fix confidential toggle of address in thirdpartyrenderbox (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/460)
|
||||||
|
|
||||||
|
|
||||||
## Test releases
|
|
||||||
* Creation of PickCivilityType, and implementation in PersonType and ThirdpartyType
|
|
||||||
|
|
||||||
### test release 2022-02-14
|
### test release 2022-02-14
|
||||||
|
|
||||||
@ -136,6 +160,7 @@ and this project adheres to
|
|||||||
* [address]: Correction residential address 'depuis le' (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/459)
|
* [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)
|
* [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_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
|
### test release 2021-02-01
|
||||||
@ -158,18 +183,9 @@ and this project adheres to
|
|||||||
* [fast_actions] improve fast-actions buttons override mechanism, fix https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/413
|
* [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.
|
* [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)
|
* [thirdparty] Add a contact to a thirdparty from within onTheFly (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/345)
|
||||||
|
|
||||||
|
|
||||||
## Test releases
|
|
||||||
=======
|
|
||||||
* [homepage widget] add vue homepage_widget with asynchone loading, give a global view resume of the user concerned actions, notifications, etc.
|
* [homepage widget] add vue homepage_widget with asynchone loading, give a global view resume of the user concerned actions, notifications, etc.
|
||||||
>>>>>>> issue422_and_others_on_AddPersons
|
|
||||||
|
|
||||||
=======
|
|
||||||
* [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)
|
|
||||||
|
|
||||||
>>>>>>> b0d50d315c8e00959a967badac9cf5057ab2b4bc
|
|
||||||
### test release 2021-01-31
|
### 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: optimisation: do not fetch some resources for the banner (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/409)
|
||||||
|
@ -450,18 +450,8 @@ parameters:
|
|||||||
count: 1
|
count: 1
|
||||||
path: src/Bundle/ChillThirdPartyBundle/Repository/ThirdPartyRepository.php
|
path: src/Bundle/ChillThirdPartyBundle/Repository/ThirdPartyRepository.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: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/Bundle/ChillThirdPartyBundle/Templating/Entity/ThirdPartyRender.php
|
|
||||||
|
|
||||||
|
@ -12,13 +12,20 @@ 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\ActivityBundle\Entity\ActivityType;
|
||||||
use Chill\ActivityBundle\Security\Authorization\ActivityVoter;
|
use Chill\ActivityBundle\Security\Authorization\ActivityVoter;
|
||||||
|
use Chill\MainBundle\Entity\Location;
|
||||||
|
use Chill\MainBundle\Entity\LocationType;
|
||||||
use Chill\MainBundle\Entity\Scope;
|
use Chill\MainBundle\Entity\Scope;
|
||||||
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
|
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
|
||||||
use Chill\MainBundle\Security\Resolver\CenterResolverDispatcherInterface;
|
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 Doctrine\ORM\AbstractQuery;
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
use Doctrine\ORM\Query\ResultSetMappingBuilder;
|
||||||
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 Symfony\Component\Security\Core\Security;
|
use Symfony\Component\Security\Core\Security;
|
||||||
@ -72,6 +79,86 @@ final class ActivityACLAwareRepository implements ActivityACLAwareRepositoryInte
|
|||||||
->findByAccompanyingPeriod($period, $scopes, true, $limit, $start, $orderBy);
|
->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 array $orderBy
|
* @param array $orderBy
|
||||||
*
|
*
|
||||||
|
@ -21,6 +21,15 @@ interface ActivityACLAwareRepositoryInterface
|
|||||||
*/
|
*/
|
||||||
public function findByAccompanyingPeriod(AccompanyingPeriod $period, string $role, ?int $start = 0, ?int $limit = 1000, ?array $orderBy = []): array;
|
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
|
* @return Activity[]|array
|
||||||
*/
|
*/
|
||||||
|
@ -4,6 +4,13 @@ div.chill-dropzone__below-zone {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ul[data-collection-name="documents"] {
|
||||||
|
button.remove-entry {
|
||||||
|
margin: 0.5rem 0!important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// do it in js does not work
|
// do it in js does not work
|
||||||
// document.addEventListener('DOMContentLoaded', e => {
|
// document.addEventListener('DOMContentLoaded', e => {
|
||||||
// const dropzoneBelow = document.querySelectorAll('div.chill-dropzone__below-zone');
|
// const dropzoneBelow = document.querySelectorAll('div.chill-dropzone__below-zone');
|
||||||
|
@ -0,0 +1,286 @@
|
|||||||
|
<?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\Service\DocGenerator;
|
||||||
|
|
||||||
|
use Chill\ActivityBundle\Entity\ActivityPresence;
|
||||||
|
use Chill\ActivityBundle\Entity\ActivityType;
|
||||||
|
use Chill\ActivityBundle\Repository\ActivityACLAwareRepositoryInterface;
|
||||||
|
use Chill\DocGeneratorBundle\Context\DocGeneratorContextWithAdminFormInterface;
|
||||||
|
use Chill\DocGeneratorBundle\Context\DocGeneratorContextWithPublicFormInterface;
|
||||||
|
use Chill\DocGeneratorBundle\Entity\DocGeneratorTemplate;
|
||||||
|
use Chill\DocStoreBundle\Entity\StoredObject;
|
||||||
|
use Chill\MainBundle\Entity\Location;
|
||||||
|
use Chill\MainBundle\Entity\User;
|
||||||
|
use Chill\MainBundle\Repository\UserRepository;
|
||||||
|
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||||
|
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
||||||
|
use Chill\PersonBundle\Entity\Person;
|
||||||
|
use Chill\PersonBundle\Entity\SocialWork\SocialAction;
|
||||||
|
use Chill\PersonBundle\Entity\SocialWork\SocialIssue;
|
||||||
|
use Chill\PersonBundle\Repository\PersonRepository;
|
||||||
|
use Chill\PersonBundle\Repository\SocialWork\SocialActionRepository;
|
||||||
|
use Chill\PersonBundle\Repository\SocialWork\SocialIssueRepository;
|
||||||
|
use Chill\PersonBundle\Service\DocGenerator\AccompanyingPeriodContext;
|
||||||
|
use Chill\ThirdPartyBundle\Entity\ThirdParty;
|
||||||
|
use Chill\ThirdPartyBundle\Repository\ThirdPartyRepository;
|
||||||
|
use DateTime;
|
||||||
|
use libphonenumber\PhoneNumber;
|
||||||
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
|
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
|
||||||
|
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
|
||||||
|
|
||||||
|
class ListActivitiesByAccompanyingPeriodContext implements
|
||||||
|
DocGeneratorContextWithAdminFormInterface,
|
||||||
|
DocGeneratorContextWithPublicFormInterface
|
||||||
|
{
|
||||||
|
private AccompanyingPeriodContext $accompanyingPeriodContext;
|
||||||
|
|
||||||
|
private ActivityACLAwareRepositoryInterface $activityACLAwareRepository;
|
||||||
|
|
||||||
|
private NormalizerInterface $normalizer;
|
||||||
|
|
||||||
|
private PersonRepository $personRepository;
|
||||||
|
|
||||||
|
private SocialActionRepository $socialActionRepository;
|
||||||
|
|
||||||
|
private SocialIssueRepository $socialIssueRepository;
|
||||||
|
|
||||||
|
private ThirdPartyRepository $thirdPartyRepository;
|
||||||
|
|
||||||
|
private TranslatableStringHelperInterface $translatableStringHelper;
|
||||||
|
|
||||||
|
private UserRepository $userRepository;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
AccompanyingPeriodContext $accompanyingPeriodContext,
|
||||||
|
ActivityACLAwareRepositoryInterface $activityACLAwareRepository,
|
||||||
|
NormalizerInterface $normalizer,
|
||||||
|
PersonRepository $personRepository,
|
||||||
|
SocialActionRepository $socialActionRepository,
|
||||||
|
SocialIssueRepository $socialIssueRepository,
|
||||||
|
ThirdPartyRepository $thirdPartyRepository,
|
||||||
|
TranslatableStringHelperInterface $translatableStringHelper,
|
||||||
|
UserRepository $userRepository
|
||||||
|
) {
|
||||||
|
$this->accompanyingPeriodContext = $accompanyingPeriodContext;
|
||||||
|
$this->activityACLAwareRepository = $activityACLAwareRepository;
|
||||||
|
$this->normalizer = $normalizer;
|
||||||
|
$this->personRepository = $personRepository;
|
||||||
|
$this->socialActionRepository = $socialActionRepository;
|
||||||
|
$this->socialIssueRepository = $socialIssueRepository;
|
||||||
|
$this->thirdPartyRepository = $thirdPartyRepository;
|
||||||
|
$this->translatableStringHelper = $translatableStringHelper;
|
||||||
|
$this->userRepository = $userRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function adminFormReverseTransform(array $data): array
|
||||||
|
{
|
||||||
|
return $this->accompanyingPeriodContext->adminFormReverseTransform($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function adminFormTransform(array $data): array
|
||||||
|
{
|
||||||
|
return $this->accompanyingPeriodContext->adminFormTransform($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function buildAdminForm(FormBuilderInterface $builder): void
|
||||||
|
{
|
||||||
|
$this->accompanyingPeriodContext->buildAdminForm($builder);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function buildPublicForm(FormBuilderInterface $builder, DocGeneratorTemplate $template, $entity): void
|
||||||
|
{
|
||||||
|
$this->accompanyingPeriodContext->buildPublicForm($builder, $template, $entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getData(DocGeneratorTemplate $template, $entity, array $contextGenerationData = []): array
|
||||||
|
{
|
||||||
|
$data = $this->accompanyingPeriodContext->getData($template, $entity, $contextGenerationData);
|
||||||
|
|
||||||
|
$data['activities'] = $this->getActivitiesSimplified($entity);
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDescription(): string
|
||||||
|
{
|
||||||
|
return 'docgen.Accompanying period with a list of activities description';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getEntityClass(): string
|
||||||
|
{
|
||||||
|
return AccompanyingPeriod::class;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFormData(DocGeneratorTemplate $template, $entity): array
|
||||||
|
{
|
||||||
|
return $this->accompanyingPeriodContext->getFormData($template, $entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getKey(): string
|
||||||
|
{
|
||||||
|
return self::class;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getName(): string
|
||||||
|
{
|
||||||
|
return 'docgen.Accompanying period with a list of activities';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hasAdminForm(): bool
|
||||||
|
{
|
||||||
|
return $this->accompanyingPeriodContext->hasAdminForm();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hasPublicForm(DocGeneratorTemplate $template, $entity): bool
|
||||||
|
{
|
||||||
|
return $this->accompanyingPeriodContext->hasPublicForm($template, $entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function storeGenerated(DocGeneratorTemplate $template, StoredObject $storedObject, object $entity, array $contextGenerationData): void
|
||||||
|
{
|
||||||
|
$this->accompanyingPeriodContext->storeGenerated($template, $storedObject, $entity, $contextGenerationData);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getActivitiesSimplified(AccompanyingPeriod $period)
|
||||||
|
{
|
||||||
|
$activities =
|
||||||
|
$this->activityACLAwareRepository->findByAccompanyingPeriodSimplified($period);
|
||||||
|
$results = [];
|
||||||
|
|
||||||
|
foreach ($activities as $row) {
|
||||||
|
$activity = $row[0];
|
||||||
|
|
||||||
|
$activity['date'] = $this->normalizer->normalize($activity['date'], 'docgen', [
|
||||||
|
AbstractNormalizer::GROUPS => ['docgen:read'], 'docgen:expects' => DateTime::class,
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (null === $activity['location']) {
|
||||||
|
$activity['location'] = $this->normalizer->normalize(null, 'docgen', [
|
||||||
|
AbstractNormalizer::GROUPS => ['docgen:read'], 'docgen:expects' => Location::class,
|
||||||
|
]);
|
||||||
|
$activity['location']['type'] = 'location';
|
||||||
|
} else {
|
||||||
|
$activity['location']['isNull'] = false;
|
||||||
|
$activity['location']['type'] = 'location';
|
||||||
|
|
||||||
|
foreach (['1', '2'] as $key) {
|
||||||
|
$activity['location']['phonenumber' . $key] = $this->normalizer->normalize(
|
||||||
|
$activity['location']['phonenumber' . $key],
|
||||||
|
'docgen',
|
||||||
|
[AbstractNormalizer::GROUPS => ['docgen:read'], 'docgen:expects' => PhoneNumber::class]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_numeric($activity['location']['locationType']['id'])) {
|
||||||
|
$activity['location']['locationType']['title'] = $this->translatableStringHelper->localize(
|
||||||
|
$activity['location']['locationType']['title']
|
||||||
|
);
|
||||||
|
$activity['location']['locationType']['isNull'] = false;
|
||||||
|
$activity['location']['locationType']['type'] = 'locationType';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null !== $activity['activityType']) {
|
||||||
|
$activity['activityType']['name'] = $this->translatableStringHelper->localize(
|
||||||
|
$activity['activityType']['name']
|
||||||
|
);
|
||||||
|
$activity['activityType']['isNull'] = false;
|
||||||
|
$activity['activityType']['type'] = 'activityType';
|
||||||
|
} else {
|
||||||
|
$activity['activityType'] = $this->normalizer->normalize(null, 'docgen', [
|
||||||
|
AbstractNormalizer::GROUPS => ['docgen:read'], 'docgen:expects' => ActivityType::class,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null !== $activity['attendee']) {
|
||||||
|
$activity['attendee']['name'] = $this->translatableStringHelper->localize(
|
||||||
|
$activity['attendee']['name']
|
||||||
|
);
|
||||||
|
$activity['attendee']['isNull'] = false;
|
||||||
|
$activity['attendee']['type'] = 'activityPresence';
|
||||||
|
} else {
|
||||||
|
$activity['attendee'] = $this->normalizer->normalize(null, 'docgen', [
|
||||||
|
AbstractNormalizer::GROUPS => ['docgen:read'], 'docgen:expects' => ActivityPresence::class,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$activity['comment'] = (string) $row['comment'];
|
||||||
|
$activity['travelTimeMinute'] = $row['travelTimeMinute'];
|
||||||
|
$activity['durationTimeMinute'] = $row['durationTimeMinute'];
|
||||||
|
|
||||||
|
if (null !== $row['userIds']) {
|
||||||
|
foreach ($row['userIds'] as $id) {
|
||||||
|
$activity['users'][] = $this->normalizer->normalize(
|
||||||
|
$this->userRepository->find($id),
|
||||||
|
'docgen',
|
||||||
|
[AbstractNormalizer::GROUPS => ['docgen:read'], 'docgen:expects' => User::class]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$activity['users'] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null !== $row['personIds']) {
|
||||||
|
foreach ($row['personIds'] as $id) {
|
||||||
|
$activity['persons'][] = $this->normalizer->normalize(
|
||||||
|
$this->personRepository->find($id),
|
||||||
|
'docgen',
|
||||||
|
[AbstractNormalizer::GROUPS => ['docgen:read'], 'docgen:expects' => Person::class]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$activity['persons'] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null !== $row['thirdPartyIds']) {
|
||||||
|
foreach ($row['thirdPartyIds'] as $id) {
|
||||||
|
$activity['thirdParties'][] = $this->normalizer->normalize(
|
||||||
|
$this->thirdPartyRepository->find($id),
|
||||||
|
'docgen',
|
||||||
|
[AbstractNormalizer::GROUPS => ['docgen:read'], 'docgen:expects' => ThirdParty::class]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$activity['thirdParties'] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null !== $row['socialActionIds']) {
|
||||||
|
foreach ($row['socialActionIds'] as $id) {
|
||||||
|
$activity['socialActions'][] = $this->normalizer->normalize(
|
||||||
|
$this->socialActionRepository->find($id),
|
||||||
|
'docgen',
|
||||||
|
[AbstractNormalizer::GROUPS => ['docgen:read'], 'docgen:expects' => SocialAction::class]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$activity['socialActions'] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null !== $row['socialIssueIds']) {
|
||||||
|
foreach ($row['socialIssueIds'] as $id) {
|
||||||
|
$activity['socialIssues'][] = $this->normalizer->normalize(
|
||||||
|
$this->socialIssueRepository->find($id),
|
||||||
|
'docgen',
|
||||||
|
[AbstractNormalizer::GROUPS => ['docgen:read'], 'docgen:expects' => SocialIssue::class]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$activity['socialIssues'] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$results[] = $activity;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $results;
|
||||||
|
}
|
||||||
|
}
|
@ -232,3 +232,5 @@ This is the minimal activity data: Activité n°
|
|||||||
docgen:
|
docgen:
|
||||||
Activity basic: Echange
|
Activity basic: Echange
|
||||||
A basic context for activity: Contexte pour les échanges
|
A basic context for activity: Contexte pour les échanges
|
||||||
|
Accompanying period with a list of activities: Parcours d'accompagnement avec liste des échanges
|
||||||
|
Accompanying period with a list of activities description: Ce contexte reprend les informations du parcours, et tous les échanges pour un parcours. Les échanges ne sont pas filtrés.
|
||||||
|
@ -23,15 +23,15 @@ use function count;
|
|||||||
/**
|
/**
|
||||||
* Helps to find a summary of the budget: the sum of resources and charges.
|
* Helps to find a summary of the budget: the sum of resources and charges.
|
||||||
*/
|
*/
|
||||||
class SummaryBudget
|
class SummaryBudget implements SummaryBudgetInterface
|
||||||
{
|
{
|
||||||
private const QUERY_CHARGE_BY_HOUSEHOLD = 'select SUM(amount) AS sum, type FROM chill_budget.charge WHERE (person_id IN (_ids_) OR household_id = ?) AND NOW() BETWEEN startdate AND COALESCE(enddate, \'infinity\'::timestamp) GROUP BY type';
|
private const QUERY_CHARGE_BY_HOUSEHOLD = 'select SUM(amount) AS sum, string_agg(comment, \'|\') AS comment, type FROM chill_budget.charge WHERE (person_id IN (_ids_) OR household_id = ?) AND NOW() BETWEEN startdate AND COALESCE(enddate, \'infinity\'::timestamp) GROUP BY type';
|
||||||
|
|
||||||
private const QUERY_CHARGE_BY_PERSON = 'select SUM(amount) AS sum, type FROM chill_budget.charge WHERE person_id = ? AND NOW() BETWEEN startdate AND COALESCE(enddate, \'infinity\'::timestamp) GROUP BY type';
|
private const QUERY_CHARGE_BY_PERSON = 'select SUM(amount) AS sum, string_agg(comment, \'|\') AS comment, type FROM chill_budget.charge WHERE person_id = ? AND NOW() BETWEEN startdate AND COALESCE(enddate, \'infinity\'::timestamp) GROUP BY type';
|
||||||
|
|
||||||
private const QUERY_RESOURCE_BY_HOUSEHOLD = 'select SUM(amount) AS sum, type FROM chill_budget.resource WHERE (person_id IN (_ids_) OR household_id = ?) AND NOW() BETWEEN startdate AND COALESCE(enddate, \'infinity\'::timestamp) GROUP BY type';
|
private const QUERY_RESOURCE_BY_HOUSEHOLD = 'select SUM(amount) AS sum, string_agg(comment, \'|\') AS comment, type FROM chill_budget.resource WHERE (person_id IN (_ids_) OR household_id = ?) AND NOW() BETWEEN startdate AND COALESCE(enddate, \'infinity\'::timestamp) GROUP BY type';
|
||||||
|
|
||||||
private const QUERY_RESOURCE_BY_PERSON = 'select SUM(amount) AS sum, type FROM chill_budget.resource WHERE person_id = ? AND NOW() BETWEEN startdate AND COALESCE(enddate, \'infinity\'::timestamp) GROUP BY type';
|
private const QUERY_RESOURCE_BY_PERSON = 'select SUM(amount) AS sum, string_agg(comment, \'|\') AS comment, type FROM chill_budget.resource WHERE person_id = ? AND NOW() BETWEEN startdate AND COALESCE(enddate, \'infinity\'::timestamp) GROUP BY type';
|
||||||
|
|
||||||
private array $chargeLabels;
|
private array $chargeLabels;
|
||||||
|
|
||||||
@ -52,26 +52,6 @@ class SummaryBudget
|
|||||||
$this->translatableStringHelper = $translatableStringHelper;
|
$this->translatableStringHelper = $translatableStringHelper;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getEmptyChargeArray(): array
|
|
||||||
{
|
|
||||||
$keys = $this->configRepository->getChargesKeys();
|
|
||||||
$labels = $this->chargeLabels;
|
|
||||||
|
|
||||||
return array_combine($keys, array_map(function ($i) use ($labels) {
|
|
||||||
return ['sum' => 0.0, 'label' => $this->translatableStringHelper->localize($labels[$i])];
|
|
||||||
}, $keys));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getEmptyResourceArray(): array
|
|
||||||
{
|
|
||||||
$keys = $this->configRepository->getResourcesKeys();
|
|
||||||
$labels = $this->resourcesLabels;
|
|
||||||
|
|
||||||
return array_combine($keys, array_map(function ($i) use ($labels) {
|
|
||||||
return ['sum' => 0.0, 'label' => $this->translatableStringHelper->localize($labels[$i])];
|
|
||||||
}, $keys));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getSummaryForHousehold(?Household $household): array
|
public function getSummaryForHousehold(?Household $household): array
|
||||||
{
|
{
|
||||||
if (null === $household) {
|
if (null === $household) {
|
||||||
@ -101,8 +81,15 @@ class SummaryBudget
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getSummaryForPerson(Person $person): array
|
public function getSummaryForPerson(?Person $person): array
|
||||||
{
|
{
|
||||||
|
if (null === $person) {
|
||||||
|
return [
|
||||||
|
'resources' => $this->getEmptyResourceArray(),
|
||||||
|
'charges' => $this->getEmptyChargeArray(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
$rsm = $this->buildRsm();
|
$rsm = $this->buildRsm();
|
||||||
|
|
||||||
$resources = $this->em->createNativeQuery(self::QUERY_RESOURCE_BY_PERSON, $rsm)
|
$resources = $this->em->createNativeQuery(self::QUERY_RESOURCE_BY_PERSON, $rsm)
|
||||||
@ -123,11 +110,32 @@ class SummaryBudget
|
|||||||
$rsm = new ResultSetMapping();
|
$rsm = new ResultSetMapping();
|
||||||
$rsm
|
$rsm
|
||||||
->addScalarResult('sum', 'sum')
|
->addScalarResult('sum', 'sum')
|
||||||
->addScalarResult('type', 'type');
|
->addScalarResult('type', 'type')
|
||||||
|
->addScalarResult('comment', 'comment');
|
||||||
|
|
||||||
return $rsm;
|
return $rsm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function getEmptyChargeArray(): array
|
||||||
|
{
|
||||||
|
$keys = $this->configRepository->getChargesKeys();
|
||||||
|
$labels = $this->chargeLabels;
|
||||||
|
|
||||||
|
return array_combine($keys, array_map(function ($i) use ($labels) {
|
||||||
|
return ['sum' => 0.0, 'label' => $this->translatableStringHelper->localize($labels[$i]), 'comment' => ''];
|
||||||
|
}, $keys));
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getEmptyResourceArray(): array
|
||||||
|
{
|
||||||
|
$keys = $this->configRepository->getResourcesKeys();
|
||||||
|
$labels = $this->resourcesLabels;
|
||||||
|
|
||||||
|
return array_combine($keys, array_map(function ($i) use ($labels) {
|
||||||
|
return ['sum' => 0.0, 'label' => $this->translatableStringHelper->localize($labels[$i]), 'comment' => ''];
|
||||||
|
}, $keys));
|
||||||
|
}
|
||||||
|
|
||||||
private function rowToArray(array $rows, string $kind): array
|
private function rowToArray(array $rows, string $kind): array
|
||||||
{
|
{
|
||||||
switch ($kind) {
|
switch ($kind) {
|
||||||
@ -151,6 +159,7 @@ class SummaryBudget
|
|||||||
$result[$row['type']] = [
|
$result[$row['type']] = [
|
||||||
'sum' => (float) $row['sum'],
|
'sum' => (float) $row['sum'],
|
||||||
'label' => $this->translatableStringHelper->localize($label[$row['type']]),
|
'label' => $this->translatableStringHelper->localize($label[$row['type']]),
|
||||||
|
'comment' => (string) $row['comment'],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,25 @@
|
|||||||
|
<?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\BudgetBundle\Service\Summary;
|
||||||
|
|
||||||
|
use Chill\PersonBundle\Entity\Household\Household;
|
||||||
|
use Chill\PersonBundle\Entity\Person;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helps to find a summary of the budget: the sum of resources and charges.
|
||||||
|
*/
|
||||||
|
interface SummaryBudgetInterface
|
||||||
|
{
|
||||||
|
public function getSummaryForHousehold(?Household $household): array;
|
||||||
|
|
||||||
|
public function getSummaryForPerson(?Person $person): array;
|
||||||
|
}
|
@ -16,10 +16,102 @@ use Chill\DocStoreBundle\Entity\StoredObject;
|
|||||||
use Twig\Environment;
|
use Twig\Environment;
|
||||||
use Twig\Extension\RuntimeExtensionInterface;
|
use Twig\Extension\RuntimeExtensionInterface;
|
||||||
|
|
||||||
use function array_key_exists;
|
use function in_array;
|
||||||
|
|
||||||
final class WopiEditTwigExtensionRuntime implements RuntimeExtensionInterface
|
final class WopiEditTwigExtensionRuntime implements RuntimeExtensionInterface
|
||||||
{
|
{
|
||||||
|
public const SUPPORTED_MIMES = [
|
||||||
|
'image/svg+xml',
|
||||||
|
'application/vnd.ms-powerpoint',
|
||||||
|
'application/vnd.ms-excel',
|
||||||
|
'application/vnd.sun.xml.writer',
|
||||||
|
'application/vnd.oasis.opendocument.text',
|
||||||
|
'application/vnd.oasis.opendocument.text-flat-xml',
|
||||||
|
'application/vnd.sun.xml.calc',
|
||||||
|
'application/vnd.oasis.opendocument.spreadsheet',
|
||||||
|
'application/vnd.oasis.opendocument.spreadsheet-flat-xml',
|
||||||
|
'application/vnd.sun.xml.impress',
|
||||||
|
'application/vnd.oasis.opendocument.presentation',
|
||||||
|
'application/vnd.oasis.opendocument.presentation-flat-xml',
|
||||||
|
'application/vnd.sun.xml.draw',
|
||||||
|
'application/vnd.oasis.opendocument.graphics',
|
||||||
|
'application/vnd.oasis.opendocument.graphics-flat-xml',
|
||||||
|
'application/vnd.oasis.opendocument.chart',
|
||||||
|
'application/vnd.sun.xml.writer.global',
|
||||||
|
'application/vnd.oasis.opendocument.text-master',
|
||||||
|
'application/vnd.sun.xml.writer.template',
|
||||||
|
'application/vnd.oasis.opendocument.text-template',
|
||||||
|
'application/vnd.oasis.opendocument.text-master-template',
|
||||||
|
'application/vnd.sun.xml.calc.template',
|
||||||
|
'application/vnd.oasis.opendocument.spreadsheet-template',
|
||||||
|
'application/vnd.sun.xml.impress.template',
|
||||||
|
'application/vnd.oasis.opendocument.presentation-template',
|
||||||
|
'application/vnd.sun.xml.draw.template',
|
||||||
|
'application/vnd.oasis.opendocument.graphics-template',
|
||||||
|
'application/msword',
|
||||||
|
'application/msword',
|
||||||
|
'application/vnd.ms-excel',
|
||||||
|
'application/vnd.ms-powerpoint',
|
||||||
|
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
||||||
|
'application/vnd.ms-word.document.macroEnabled.12',
|
||||||
|
'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
|
||||||
|
'application/vnd.ms-word.template.macroEnabled.12',
|
||||||
|
'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
|
||||||
|
'application/vnd.ms-excel.template.macroEnabled.12',
|
||||||
|
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||||
|
'application/vnd.ms-excel.sheet.binary.macroEnabled.12',
|
||||||
|
'application/vnd.ms-excel.sheet.macroEnabled.12',
|
||||||
|
'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
||||||
|
'application/vnd.ms-powerpoint.presentation.macroEnabled.12',
|
||||||
|
'application/vnd.openxmlformats-officedocument.presentationml.template',
|
||||||
|
'application/vnd.ms-powerpoint.template.macroEnabled.12',
|
||||||
|
'application/vnd.wordperfect',
|
||||||
|
'application/x-aportisdoc',
|
||||||
|
'application/x-hwp',
|
||||||
|
'application/vnd.ms-works',
|
||||||
|
'application/x-mswrite',
|
||||||
|
'application/x-dif-document',
|
||||||
|
'text/spreadsheet',
|
||||||
|
'text/csv',
|
||||||
|
'application/x-dbase',
|
||||||
|
'application/vnd.lotus-1-2-3',
|
||||||
|
'image/cgm',
|
||||||
|
'image/vnd.dxf',
|
||||||
|
'image/x-emf',
|
||||||
|
'image/x-wmf',
|
||||||
|
'application/coreldraw',
|
||||||
|
'application/vnd.visio2013',
|
||||||
|
'application/vnd.visio',
|
||||||
|
'application/vnd.ms-visio.drawing',
|
||||||
|
'application/x-mspublisher',
|
||||||
|
'application/x-sony-bbeb',
|
||||||
|
'application/x-gnumeric',
|
||||||
|
'application/macwriteii',
|
||||||
|
'application/x-iwork-numbers-sffnumbers',
|
||||||
|
'application/vnd.oasis.opendocument.text-web',
|
||||||
|
'application/x-pagemaker',
|
||||||
|
'text/rtf',
|
||||||
|
'text/plain',
|
||||||
|
'application/x-fictionbook+xml',
|
||||||
|
'application/clarisworks',
|
||||||
|
'image/x-wpg',
|
||||||
|
'application/x-iwork-pages-sffpages',
|
||||||
|
'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
|
||||||
|
'application/x-iwork-keynote-sffkey',
|
||||||
|
|
||||||
|
'application/x-abiword',
|
||||||
|
'image/x-freehand',
|
||||||
|
'application/vnd.sun.xml.chart',
|
||||||
|
'application/x-t602',
|
||||||
|
'image/bmp',
|
||||||
|
'image/png',
|
||||||
|
'image/gif',
|
||||||
|
'image/tiff',
|
||||||
|
'image/jpg',
|
||||||
|
'image/jpeg',
|
||||||
|
'application/pdf',
|
||||||
|
];
|
||||||
|
|
||||||
private const TEMPLATE = '@ChillDocStore/Button/wopi_edit_document.html.twig';
|
private const TEMPLATE = '@ChillDocStore/Button/wopi_edit_document.html.twig';
|
||||||
|
|
||||||
private DiscoveryInterface $discovery;
|
private DiscoveryInterface $discovery;
|
||||||
@ -31,15 +123,7 @@ final class WopiEditTwigExtensionRuntime implements RuntimeExtensionInterface
|
|||||||
|
|
||||||
public function isEditable(StoredObject $document): bool
|
public function isEditable(StoredObject $document): bool
|
||||||
{
|
{
|
||||||
$mime_type = $this->discovery->discoverMimeType($document->getType());
|
return in_array($document->getType(), self::SUPPORTED_MIMES, true);
|
||||||
|
|
||||||
foreach ($mime_type as $item) {
|
|
||||||
if (array_key_exists('default', $item) && 'true' === $item['default']) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function renderEditButton(Environment $environment, StoredObject $document, ?array $options = null): string
|
public function renderEditButton(Environment $environment, StoredObject $document, ?array $options = null): string
|
||||||
|
@ -116,6 +116,6 @@ class AccompanyingCourseDocumentWorkflowHandler implements EntityWorkflowHandler
|
|||||||
|
|
||||||
public function supportsFreeze(EntityWorkflow $entityWorkflow, array $options = []): bool
|
public function supportsFreeze(EntityWorkflow $entityWorkflow, array $options = []): bool
|
||||||
{
|
{
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,11 +33,11 @@ class ApiController extends AbstractCRUDController
|
|||||||
* Base method for handling api action.
|
* Base method for handling api action.
|
||||||
*
|
*
|
||||||
* @param mixed $id
|
* @param mixed $id
|
||||||
* @param mixed $_format
|
* @param string $_format
|
||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function entityApi(Request $request, $id, $_format): Response
|
public function entityApi(Request $request, $id, ?string $_format = 'json'): Response
|
||||||
{
|
{
|
||||||
switch ($request->getMethod()) {
|
switch ($request->getMethod()) {
|
||||||
case Request::METHOD_GET:
|
case Request::METHOD_GET:
|
||||||
|
@ -12,6 +12,7 @@ declare(strict_types=1);
|
|||||||
namespace Chill\MainBundle\Controller;
|
namespace Chill\MainBundle\Controller;
|
||||||
|
|
||||||
use Chill\MainBundle\CRUD\Controller\CRUDController;
|
use Chill\MainBundle\CRUD\Controller\CRUDController;
|
||||||
|
use Chill\MainBundle\Pagination\PaginatorInterface;
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
|
||||||
class LocationController extends CRUDController
|
class LocationController extends CRUDController
|
||||||
@ -29,4 +30,9 @@ class LocationController extends CRUDController
|
|||||||
{
|
{
|
||||||
$query->where('e.availableForUsers = true'); //TODO not working
|
$query->where('e.availableForUsers = true'); //TODO not working
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function orderQuery(string $action, $query, Request $request, PaginatorInterface $paginator)
|
||||||
|
{
|
||||||
|
return $query->addOrderBy('e.name', 'DESC');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@ use Chill\MainBundle\Pagination\PaginatorFactory;
|
|||||||
use Chill\MainBundle\Repository\NotificationRepository;
|
use Chill\MainBundle\Repository\NotificationRepository;
|
||||||
use Chill\MainBundle\Security\Authorization\NotificationVoter;
|
use Chill\MainBundle\Security\Authorization\NotificationVoter;
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
@ -31,14 +32,19 @@ use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
|
|||||||
use Symfony\Component\Routing\Annotation\Route;
|
use Symfony\Component\Routing\Annotation\Route;
|
||||||
use Symfony\Component\Security\Core\Security;
|
use Symfony\Component\Security\Core\Security;
|
||||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||||
|
use function in_array;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Route("/{_locale}/notification")
|
* @Route("/{_locale}/notification")
|
||||||
*/
|
*/
|
||||||
class NotificationController extends AbstractController
|
class NotificationController extends AbstractController
|
||||||
{
|
{
|
||||||
|
private LoggerInterface $chillLogger;
|
||||||
|
|
||||||
private EntityManagerInterface $em;
|
private EntityManagerInterface $em;
|
||||||
|
|
||||||
|
private LoggerInterface $logger;
|
||||||
|
|
||||||
private NotificationHandlerManager $notificationHandlerManager;
|
private NotificationHandlerManager $notificationHandlerManager;
|
||||||
|
|
||||||
private NotificationRepository $notificationRepository;
|
private NotificationRepository $notificationRepository;
|
||||||
@ -51,6 +57,8 @@ class NotificationController extends AbstractController
|
|||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
EntityManagerInterface $em,
|
EntityManagerInterface $em,
|
||||||
|
LoggerInterface $chillLogger,
|
||||||
|
LoggerInterface $logger,
|
||||||
Security $security,
|
Security $security,
|
||||||
NotificationRepository $notificationRepository,
|
NotificationRepository $notificationRepository,
|
||||||
NotificationHandlerManager $notificationHandlerManager,
|
NotificationHandlerManager $notificationHandlerManager,
|
||||||
@ -58,6 +66,8 @@ class NotificationController extends AbstractController
|
|||||||
TranslatorInterface $translator
|
TranslatorInterface $translator
|
||||||
) {
|
) {
|
||||||
$this->em = $em;
|
$this->em = $em;
|
||||||
|
$this->logger = $logger;
|
||||||
|
$this->chillLogger = $chillLogger;
|
||||||
$this->security = $security;
|
$this->security = $security;
|
||||||
$this->notificationRepository = $notificationRepository;
|
$this->notificationRepository = $notificationRepository;
|
||||||
$this->notificationHandlerManager = $notificationHandlerManager;
|
$this->notificationHandlerManager = $notificationHandlerManager;
|
||||||
@ -150,6 +160,49 @@ class NotificationController extends AbstractController
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Route("/{id}/access_key", name="chill_main_notification_grant_access_by_access_key")
|
||||||
|
*/
|
||||||
|
public function getAccessByAccessKey(Notification $notification, Request $request): Response
|
||||||
|
{
|
||||||
|
$this->denyAccessUnlessGranted('IS_AUTHENTICATED_REMEMBERED');
|
||||||
|
|
||||||
|
if (!$this->security->getUser() instanceof User) {
|
||||||
|
throw new AccessDeniedHttpException('You must be authenticated and a user to create a notification');
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (['accessKey', 'email'] as $param) {
|
||||||
|
if (!$request->query->has($param)) {
|
||||||
|
throw new BadRequestHttpException("Missing {$param} parameter");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($notification->getAccessKey() !== $request->query->getAlnum('accessKey')) {
|
||||||
|
throw new AccessDeniedHttpException('access key is invalid');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!in_array($request->query->get('email'), $notification->getAddressesEmails(), true)) {
|
||||||
|
return (new Response('The email address is no more associated with this notification'))
|
||||||
|
->setStatusCode(Response::HTTP_FORBIDDEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
$notification->addAddressee($this->security->getUser());
|
||||||
|
|
||||||
|
$this->getDoctrine()->getManager()->flush();
|
||||||
|
|
||||||
|
$logMsg = '[Notification] a user is granted access to notification trough an access key';
|
||||||
|
$context = [
|
||||||
|
'notificationId' => $notification->getId(),
|
||||||
|
'email' => $request->query->get('email'),
|
||||||
|
'user' => $this->security->getUser()->getId(),
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->logger->info($logMsg, $context);
|
||||||
|
$this->chillLogger->info($logMsg, $context);
|
||||||
|
|
||||||
|
return $this->redirectToRoute('chill_main_notification_show', ['id' => $notification->getId()]);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Route("/inbox", name="chill_main_notification_my")
|
* @Route("/inbox", name="chill_main_notification_my")
|
||||||
*/
|
*/
|
||||||
|
@ -122,7 +122,7 @@ class SearchController extends AbstractController
|
|||||||
|
|
||||||
public function searchAction(Request $request, $_format)
|
public function searchAction(Request $request, $_format)
|
||||||
{
|
{
|
||||||
$pattern = $request->query->get('q', '');
|
$pattern = trim($request->query->get('q', ''));
|
||||||
|
|
||||||
if ('' === $pattern) {
|
if ('' === $pattern) {
|
||||||
switch ($_format) {
|
switch ($_format) {
|
||||||
|
@ -18,6 +18,9 @@ use Doctrine\Common\Collections\ArrayCollection;
|
|||||||
use Doctrine\Common\Collections\Collection;
|
use Doctrine\Common\Collections\Collection;
|
||||||
use Doctrine\ORM\Mapping as ORM;
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
use Symfony\Component\Validator\Constraints as Assert;
|
use Symfony\Component\Validator\Constraints as Assert;
|
||||||
|
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
||||||
|
use function count;
|
||||||
|
use function in_array;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ORM\Entity
|
* @ORM\Entity
|
||||||
@ -31,15 +34,34 @@ use Symfony\Component\Validator\Constraints as Assert;
|
|||||||
*/
|
*/
|
||||||
class Notification implements TrackUpdateInterface
|
class Notification implements TrackUpdateInterface
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* @ORM\Column(type="text", nullable=false)
|
||||||
|
*/
|
||||||
|
private string $accessKey;
|
||||||
|
|
||||||
private array $addedAddresses = [];
|
private array $addedAddresses = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ORM\ManyToMany(targetEntity=User::class)
|
* @ORM\ManyToMany(targetEntity=User::class)
|
||||||
* @ORM\JoinTable(name="chill_main_notification_addresses_user")
|
* @ORM\JoinTable(name="chill_main_notification_addresses_user")
|
||||||
* @Assert\Count(min="1", minMessage="notification.At least one addressee")
|
|
||||||
*/
|
*/
|
||||||
private Collection $addressees;
|
private Collection $addressees;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* a list of destinee which will receive notifications.
|
||||||
|
*
|
||||||
|
* @var array|string[]
|
||||||
|
* @ORM\Column(type="json")
|
||||||
|
*/
|
||||||
|
private array $addressesEmails = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* a list of emails adresses which were added to the notification.
|
||||||
|
*
|
||||||
|
* @var array|string[]
|
||||||
|
*/
|
||||||
|
private array $addressesEmailsAdded = [];
|
||||||
|
|
||||||
private ?ArrayCollection $addressesOnLoad = null;
|
private ?ArrayCollection $addressesOnLoad = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -111,6 +133,7 @@ class Notification implements TrackUpdateInterface
|
|||||||
$this->unreadBy = new ArrayCollection();
|
$this->unreadBy = new ArrayCollection();
|
||||||
$this->comments = new ArrayCollection();
|
$this->comments = new ArrayCollection();
|
||||||
$this->setDate(new DateTimeImmutable());
|
$this->setDate(new DateTimeImmutable());
|
||||||
|
$this->accessKey = bin2hex(openssl_random_pseudo_bytes(24));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function addAddressee(User $addressee): self
|
public function addAddressee(User $addressee): self
|
||||||
@ -123,6 +146,14 @@ class Notification implements TrackUpdateInterface
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function addAddressesEmail(string $email)
|
||||||
|
{
|
||||||
|
if (!in_array($email, $this->addressesEmails, true)) {
|
||||||
|
$this->addressesEmails[] = $email;
|
||||||
|
$this->addressesEmailsAdded[] = $email;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function addComment(NotificationComment $comment): self
|
public function addComment(NotificationComment $comment): self
|
||||||
{
|
{
|
||||||
if (!$this->comments->contains($comment)) {
|
if (!$this->comments->contains($comment)) {
|
||||||
@ -142,6 +173,30 @@ class Notification implements TrackUpdateInterface
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Assert\Callback
|
||||||
|
*
|
||||||
|
* @param array $payload
|
||||||
|
*/
|
||||||
|
public function assertCountAddresses(ExecutionContextInterface $context, $payload): void
|
||||||
|
{
|
||||||
|
if (0 === (count($this->getAddressesEmails()) + count($this->getAddressees()))) {
|
||||||
|
$context->buildViolation('notification.At least one addressee')
|
||||||
|
->atPath('addressees')
|
||||||
|
->addViolation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAccessKey(): string
|
||||||
|
{
|
||||||
|
return $this->accessKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAddedAddresses(): array
|
||||||
|
{
|
||||||
|
return $this->addedAddresses;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Collection|User[]
|
* @return Collection|User[]
|
||||||
*/
|
*/
|
||||||
@ -155,6 +210,22 @@ class Notification implements TrackUpdateInterface
|
|||||||
return $this->addressees;
|
return $this->addressees;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array|string[]
|
||||||
|
*/
|
||||||
|
public function getAddressesEmails(): array
|
||||||
|
{
|
||||||
|
return $this->addressesEmails;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array|string[]
|
||||||
|
*/
|
||||||
|
public function getAddressesEmailsAdded(): array
|
||||||
|
{
|
||||||
|
return $this->addressesEmailsAdded;
|
||||||
|
}
|
||||||
|
|
||||||
public function getComments(): Collection
|
public function getComments(): Collection
|
||||||
{
|
{
|
||||||
return $this->comments;
|
return $this->comments;
|
||||||
@ -271,6 +342,14 @@ class Notification implements TrackUpdateInterface
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function removeAddressesEmail(string $email)
|
||||||
|
{
|
||||||
|
if (in_array($email, $this->addressesEmails, true)) {
|
||||||
|
$this->addressesEmails = array_filter($this->addressesEmails, static fn ($e) => $e !== $email);
|
||||||
|
$this->addressesEmailsAdded = array_filter($this->addressesEmailsAdded, static fn ($e) => $e !== $email);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function removeComment(NotificationComment $comment): self
|
public function removeComment(NotificationComment $comment): self
|
||||||
{
|
{
|
||||||
$this->comments->removeElement($comment);
|
$this->comments->removeElement($comment);
|
||||||
|
@ -12,12 +12,17 @@ declare(strict_types=1);
|
|||||||
namespace Chill\MainBundle\Form;
|
namespace Chill\MainBundle\Form;
|
||||||
|
|
||||||
use Chill\MainBundle\Entity\Notification;
|
use Chill\MainBundle\Entity\Notification;
|
||||||
|
use Chill\MainBundle\Form\Type\ChillCollectionType;
|
||||||
use Chill\MainBundle\Form\Type\ChillTextareaType;
|
use Chill\MainBundle\Form\Type\ChillTextareaType;
|
||||||
use Chill\MainBundle\Form\Type\PickUserDynamicType;
|
use Chill\MainBundle\Form\Type\PickUserDynamicType;
|
||||||
use Symfony\Component\Form\AbstractType;
|
use Symfony\Component\Form\AbstractType;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\EmailType;
|
||||||
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\Validator\Constraints\Email;
|
||||||
|
use Symfony\Component\Validator\Constraints\NotBlank;
|
||||||
|
use Symfony\Component\Validator\Constraints\NotNull;
|
||||||
|
|
||||||
class NotificationType extends AbstractType
|
class NotificationType extends AbstractType
|
||||||
{
|
{
|
||||||
@ -30,9 +35,27 @@ class NotificationType extends AbstractType
|
|||||||
])
|
])
|
||||||
->add('addressees', PickUserDynamicType::class, [
|
->add('addressees', PickUserDynamicType::class, [
|
||||||
'multiple' => true,
|
'multiple' => true,
|
||||||
|
'required' => false,
|
||||||
])
|
])
|
||||||
->add('message', ChillTextareaType::class, [
|
->add('message', ChillTextareaType::class, [
|
||||||
'required' => false,
|
'required' => false,
|
||||||
|
])
|
||||||
|
->add('addressesEmails', ChillCollectionType::class, [
|
||||||
|
'label' => 'notification.dest by email',
|
||||||
|
'help' => 'notification.dest by email help',
|
||||||
|
'by_reference' => false,
|
||||||
|
'allow_add' => true,
|
||||||
|
'allow_delete' => true,
|
||||||
|
'entry_type' => EmailType::class,
|
||||||
|
'button_add_label' => 'notification.Add an email',
|
||||||
|
'button_remove_label' => 'notification.Remove an email',
|
||||||
|
'empty_collection_explain' => 'notification.Any email',
|
||||||
|
'entry_options' => [
|
||||||
|
'constraints' => [
|
||||||
|
new NotNull(), new NotBlank(), new Email(['checkMX' => true]),
|
||||||
|
],
|
||||||
|
'label' => 'Email',
|
||||||
|
],
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,6 +73,17 @@ class NotificationMailer
|
|||||||
* Send a email after a notification is persisted.
|
* Send a email after a notification is persisted.
|
||||||
*/
|
*/
|
||||||
public function postPersistNotification(Notification $notification, LifecycleEventArgs $eventArgs): void
|
public function postPersistNotification(Notification $notification, LifecycleEventArgs $eventArgs): void
|
||||||
|
{
|
||||||
|
$this->sendNotificationEmailsToAddresses($notification);
|
||||||
|
$this->sendNotificationEmailsToAddressesEmails($notification);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function postUpdateNotification(Notification $notification, LifecycleEventArgs $eventArgs): void
|
||||||
|
{
|
||||||
|
$this->sendNotificationEmailsToAddressesEmails($notification);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function sendNotificationEmailsToAddresses(Notification $notification): void
|
||||||
{
|
{
|
||||||
foreach ($notification->getAddressees() as $addressee) {
|
foreach ($notification->getAddressees() as $addressee) {
|
||||||
if (null === $addressee->getEmail()) {
|
if (null === $addressee->getEmail()) {
|
||||||
@ -108,4 +119,31 @@ class NotificationMailer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function sendNotificationEmailsToAddressesEmails(Notification $notification): void
|
||||||
|
{
|
||||||
|
foreach ($notification->getAddressesEmailsAdded() as $emailAddress) {
|
||||||
|
$email = new TemplatedEmail();
|
||||||
|
$email
|
||||||
|
->textTemplate('@ChillMain/Notification/email_non_system_notification_content_to_email.fr.md.twig')
|
||||||
|
->context([
|
||||||
|
'notification' => $notification,
|
||||||
|
'dest' => $emailAddress,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$email
|
||||||
|
->subject($notification->getTitle())
|
||||||
|
->to($emailAddress);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$this->mailer->send($email);
|
||||||
|
} catch (TransportExceptionInterface $e) {
|
||||||
|
$this->logger->warning('[NotificationMailer] could not send an email notification', [
|
||||||
|
'to' => $emailAddress,
|
||||||
|
'error_message' => $e->getMessage(),
|
||||||
|
'error_trace' => $e->getTraceAsString(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -56,17 +56,18 @@ final class NotificationRepository implements ObjectRepository
|
|||||||
if (null === $this->notificationByRelatedEntityAndUserAssociatedStatement) {
|
if (null === $this->notificationByRelatedEntityAndUserAssociatedStatement) {
|
||||||
$sql =
|
$sql =
|
||||||
'SELECT
|
'SELECT
|
||||||
SUM((EXISTS (SELECT 1 AS c FROM chill_main_notification_addresses_unread cmnau WHERE user_id = 1812 and cmnau.notification_id = cmn.id))::int) AS unread,
|
SUM((EXISTS (SELECT 1 AS c FROM chill_main_notification_addresses_unread cmnau JOIN chill_main_notification cmn ON cmnau.notification_id = cmn.id WHERE user_id = :userid and cmnau.notification_id = cmn.id and cmn.sender_id IS NOT NULL))::int) AS unread,
|
||||||
SUM((cmn.sender_id = 1812)::int) AS sent,
|
SUM((cmn.sender_id = :userid)::int) AS sent,
|
||||||
COUNT(cmn.*) AS total
|
COUNT(cmn.*) AS total
|
||||||
FROM chill_main_notification cmn
|
FROM chill_main_notification cmn
|
||||||
WHERE relatedentityclass = :relatedEntityClass AND relatedentityid = :relatedEntityId AND sender_id IS NOT NULL';
|
WHERE relatedentityclass = :relatedEntityClass AND relatedentityid = :relatedEntityId AND sender_id IS NOT NULL';
|
||||||
|
|
||||||
$this->notificationByRelatedEntityAndUserAssociatedStatement =
|
$this->notificationByRelatedEntityAndUserAssociatedStatement =
|
||||||
$this->em->getConnection()->prepare($sql);
|
$this->em->getConnection()->prepare($sql);
|
||||||
}
|
}
|
||||||
|
|
||||||
$results = $this->notificationByRelatedEntityAndUserAssociatedStatement
|
$results = $this->notificationByRelatedEntityAndUserAssociatedStatement
|
||||||
->executeQuery(['relatedEntityClass' => $relatedEntityClass, 'relatedEntityId' => $relatedEntityId]);
|
->executeQuery(['relatedEntityClass' => $relatedEntityClass, 'relatedEntityId' => $relatedEntityId, 'userid' => $user->getId()]);
|
||||||
|
|
||||||
$result = $results->fetchAssociative();
|
$result = $results->fetchAssociative();
|
||||||
|
|
||||||
|
@ -8,30 +8,31 @@ window.addEventListener('DOMContentLoaded', function (e) {
|
|||||||
document.querySelectorAll('.notification_toggle_read_status')
|
document.querySelectorAll('.notification_toggle_read_status')
|
||||||
.forEach(function (el, i) {
|
.forEach(function (el, i) {
|
||||||
createApp({
|
createApp({
|
||||||
template: '<notification-read-toggle ' +
|
template: `<notification-read-toggle
|
||||||
':notificationId="notificationId" ' +
|
:notificationId="notificationId"
|
||||||
':buttonClass="buttonClass" ' +
|
:buttonClass="buttonClass"
|
||||||
':buttonNoText="buttonNoText" ' +
|
:buttonNoText="buttonNoText"
|
||||||
':showUrl="showUrl" ' +
|
:showUrl="showUrl"
|
||||||
':isRead="isRead"' +
|
:isRead="isRead"
|
||||||
'@markRead="onMarkRead" @markUnread="onMarkUnread"' +
|
@markRead="onMarkRead"
|
||||||
'></notification-read-toggle>',
|
@markUnread="onMarkUnread">
|
||||||
|
</notification-read-toggle>`,
|
||||||
components: {
|
components: {
|
||||||
NotificationReadToggle,
|
NotificationReadToggle,
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
notificationId: +el.dataset.notificationId,
|
notificationId: el.dataset.notificationId,
|
||||||
buttonClass: el.dataset.buttonClass,
|
buttonClass: el.dataset.buttonClass,
|
||||||
buttonNoText: 'false' === el.dataset.buttonText,
|
buttonNoText: 'false' === el.dataset.buttonText,
|
||||||
showUrl: el.dataset.showButtonUrl,
|
showUrl: el.dataset.showButtonUrl,
|
||||||
isRead: 1 === +el.dataset.notificationCurrentIsRead,
|
isRead: 1 === Number.parseInt(el.dataset.notificationCurrentIsRead),
|
||||||
container: el.dataset.container
|
container: el.dataset.container
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
getContainer() {
|
getContainer() {
|
||||||
return document.querySelectorAll('div.' + this.container);
|
return document.querySelectorAll(`div.${this.container}`);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -81,7 +81,7 @@ export default {
|
|||||||
/// [Option] showUrl is href for show page second button.
|
/// [Option] showUrl is href for show page second button.
|
||||||
// When passed, the component return a button-group with 2 buttons.
|
// When passed, the component return a button-group with 2 buttons.
|
||||||
isButtonGroup() {
|
isButtonGroup() {
|
||||||
return !!this.showUrl
|
return this.showUrl;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -47,6 +47,8 @@
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
{{ chill_pagination(paginator) }}
|
||||||
|
|
||||||
<ul class="record_actions">
|
<ul class="record_actions">
|
||||||
<li>
|
<li>
|
||||||
<a href="{{ path('chill_crud_main_location_new') }}" class="btn btn-create">
|
<a href="{{ path('chill_crud_main_location_new') }}" class="btn btn-create">
|
||||||
|
@ -40,6 +40,11 @@
|
|||||||
{{ a|chill_entity_render_string }}
|
{{ a|chill_entity_render_string }}
|
||||||
</span>
|
</span>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
{% for a in c.notification.addressesEmails %}
|
||||||
|
<span class="badge-user" title="{{ 'notification.Email with access link'|trans|e('html_attr') }}">
|
||||||
|
{{ a }}
|
||||||
|
</span>
|
||||||
|
{% endfor %}
|
||||||
</li>
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</ul>
|
</ul>
|
||||||
@ -61,13 +66,24 @@
|
|||||||
<div class="item-row">
|
<div class="item-row">
|
||||||
<div class="notification-content">
|
<div class="notification-content">
|
||||||
{% if c.full_content is defined and c.full_content == true %}
|
{% if c.full_content is defined and c.full_content == true %}
|
||||||
|
{% if c.notification.message is not empty %}
|
||||||
{{ c.notification.message|chill_markdown_to_html }}
|
{{ c.notification.message|chill_markdown_to_html }}
|
||||||
{% else %}
|
{% else %}
|
||||||
|
<p class="chill-no-data-statement">{{ 'Any comment'|trans }}</p>
|
||||||
|
{% endif %}
|
||||||
|
{% else %}
|
||||||
|
{% if c.notification.message is not empty %}
|
||||||
{{ c.notification.message|u.truncate(250, '…', false)|chill_markdown_to_html }}
|
{{ c.notification.message|u.truncate(250, '…', false)|chill_markdown_to_html }}
|
||||||
<p class="read-more"><a href="{{ chill_path_add_return_path('chill_main_notification_show', {'id': c.notification.id}) }}">{{ 'Read more'|trans }}</a></p>
|
<p class="read-more"><a href="{{ chill_path_add_return_path('chill_main_notification_show', {'id': c.notification.id}) }}">{{ 'Read more'|trans }}</a></p>
|
||||||
|
{% else %}
|
||||||
|
<p class="chill-no-data-statement">{{ 'Any comment'|trans }}</p>
|
||||||
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{% endmacro %}
|
||||||
|
|
||||||
|
{% macro actions(c) %}
|
||||||
{% if c.action_button is not defined or c.action_button != false %}
|
{% if c.action_button is not defined or c.action_button != false %}
|
||||||
<div class="item-row separator">
|
<div class="item-row separator">
|
||||||
<div class="item-col item-meta">
|
<div class="item-col item-meta">
|
||||||
@ -134,12 +150,13 @@
|
|||||||
data-bs-parent="#notification-fold">
|
data-bs-parent="#notification-fold">
|
||||||
|
|
||||||
{{ _self.content(_context) }}
|
{{ _self.content(_context) }}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
{{ _self.actions(_context) }}
|
||||||
{% else %}
|
{% else %}
|
||||||
{{ _self.title(_context) }}
|
{{ _self.title(_context) }}
|
||||||
{{ _self.header(_context) }}
|
{{ _self.header(_context) }}
|
||||||
{{ _self.content(_context) }}
|
{{ _self.content(_context) }}
|
||||||
|
{{ _self.actions(_context) }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -21,6 +21,8 @@
|
|||||||
{{ form_row(form.title, { 'label': 'notification.subject'|trans }) }}
|
{{ form_row(form.title, { 'label': 'notification.subject'|trans }) }}
|
||||||
{{ form_row(form.addressees, { 'label': 'notification.sent_to'|trans }) }}
|
{{ form_row(form.addressees, { 'label': 'notification.sent_to'|trans }) }}
|
||||||
|
|
||||||
|
{{ form_row(form.addressesEmails) }}
|
||||||
|
|
||||||
{% include handler.template(notification) with handler.templateData(notification) %}
|
{% include handler.template(notification) with handler.templateData(notification) %}
|
||||||
|
|
||||||
<div class="mb-3 row">
|
<div class="mb-3 row">
|
||||||
|
@ -21,6 +21,8 @@
|
|||||||
{{ form_row(form.title, { 'label': 'notification.subject'|trans }) }}
|
{{ form_row(form.title, { 'label': 'notification.subject'|trans }) }}
|
||||||
{{ form_row(form.addressees, { 'label': 'notification.sent_to'|trans }) }}
|
{{ form_row(form.addressees, { 'label': 'notification.sent_to'|trans }) }}
|
||||||
|
|
||||||
|
{{ form_row(form.addressesEmails) }}
|
||||||
|
|
||||||
{% include handler.template(notification) with handler.templateData(notification) %}
|
{% include handler.template(notification) with handler.templateData(notification) %}
|
||||||
|
|
||||||
<div class="mb-3 row">
|
<div class="mb-3 row">
|
||||||
|
@ -0,0 +1,20 @@
|
|||||||
|
{{ dest }},
|
||||||
|
|
||||||
|
{{ notification.sender.label }} a créé une notification pour vous:
|
||||||
|
|
||||||
|
> {{ notification.title }}
|
||||||
|
>
|
||||||
|
>
|
||||||
|
{%- for line in notification.message|split("\n") %}
|
||||||
|
> {{ line }}
|
||||||
|
{%- if not loop.last %}
|
||||||
|
>
|
||||||
|
{%- endif %}
|
||||||
|
{%- endfor %}
|
||||||
|
|
||||||
|
Vous pouvez cliquer sur ce lien pour obtenir un accès permanent à la notification:
|
||||||
|
|
||||||
|
{{ absolute_url(path('chill_main_notification_grant_access_by_access_key', {'_locale': 'fr', 'id': notification.id, 'accessKey': notification.accessKey, 'email': dest})) }}
|
||||||
|
|
||||||
|
--
|
||||||
|
Le logiciel Chill
|
@ -3,6 +3,7 @@
|
|||||||
{% block title 'workflow.Delete workflow ?'|trans %}
|
{% block title 'workflow.Delete workflow ?'|trans %}
|
||||||
|
|
||||||
{% block display_content %}
|
{% block display_content %}
|
||||||
|
<div class="col-10 workflow">
|
||||||
<h2>
|
<h2>
|
||||||
{{ handler.entityTitle(entityWorkflow) }}
|
{{ handler.entityTitle(entityWorkflow) }}
|
||||||
</h2>
|
</h2>
|
||||||
@ -10,6 +11,7 @@
|
|||||||
{% include handler.template(entityWorkflow) with handler.templateData(entityWorkflow)|merge({
|
{% include handler.template(entityWorkflow) with handler.templateData(entityWorkflow)|merge({
|
||||||
'display_action': false
|
'display_action': false
|
||||||
}) %}
|
}) %}
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
@ -25,5 +27,5 @@
|
|||||||
'form' : delete_form
|
'form' : delete_form
|
||||||
} ) }}
|
} ) }}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -49,11 +49,12 @@ class SearchUserApiProvider implements SearchApiInterface
|
|||||||
SIMILARITY(LOWER(UNACCENT(?)), u.usernamecanonical))', [$pattern, $pattern])
|
SIMILARITY(LOWER(UNACCENT(?)), u.usernamecanonical))', [$pattern, $pattern])
|
||||||
->setFromClause('users AS u')
|
->setFromClause('users AS u')
|
||||||
->setWhereClauses('
|
->setWhereClauses('
|
||||||
|
u.enabled IS TRUE and (
|
||||||
SIMILARITY(LOWER(UNACCENT(?)), u.usernamecanonical) > 0.15
|
SIMILARITY(LOWER(UNACCENT(?)), u.usernamecanonical) > 0.15
|
||||||
OR u.usernamecanonical LIKE \'%\' || LOWER(UNACCENT(?)) || \'%\'
|
OR u.usernamecanonical LIKE \'%\' || LOWER(UNACCENT(?)) || \'%\'
|
||||||
OR SIMILARITY(LOWER(UNACCENT(?)), LOWER(UNACCENT(u.label))) > 0.15
|
OR SIMILARITY(LOWER(UNACCENT(?)), LOWER(UNACCENT(u.label))) > 0.15
|
||||||
OR u.label LIKE \'%\' || LOWER(UNACCENT(?)) || \'%\'
|
OR u.label LIKE \'%\' || LOWER(UNACCENT(?)) || \'%\'
|
||||||
', [$pattern, $pattern, $pattern, $pattern]);
|
)', [$pattern, $pattern, $pattern, $pattern]);
|
||||||
|
|
||||||
return $query;
|
return $query;
|
||||||
}
|
}
|
||||||
|
@ -59,7 +59,23 @@ class EntityWorkflowVoter extends Voter
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->security->isGranted($entityAttribute, $handler->getRelatedEntity($subject));
|
$relatedEntity = $handler->getRelatedEntity($subject);
|
||||||
|
|
||||||
|
if (null === $relatedEntity) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->security->isGranted($entityAttribute, $relatedEntity)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($subject->getSteps() as $step) {
|
||||||
|
if ($step->getAllDestUser()->contains($token->getUser())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
|
||||||
case self::DELETE:
|
case self::DELETE:
|
||||||
return $subject->getStep() === 'initial';
|
return $subject->getStep() === 'initial';
|
||||||
|
@ -44,14 +44,25 @@ class DateNormalizer implements ContextAwareNormalizerInterface, DenormalizerInt
|
|||||||
|
|
||||||
switch ($type) {
|
switch ($type) {
|
||||||
case DateTime::class:
|
case DateTime::class:
|
||||||
return DateTime::createFromFormat(DateTimeInterface::ISO8601, $data['datetime']);
|
$result = DateTime::createFromFormat(DateTimeInterface::ISO8601, $data['datetime']);
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
case DateTimeInterface::class:
|
case DateTimeInterface::class:
|
||||||
case DateTimeImmutable::class:
|
case DateTimeImmutable::class:
|
||||||
return DateTimeImmutable::createFromFormat(DateTimeInterface::ISO8601, $data['datetime']);
|
$result = DateTimeImmutable::createFromFormat(DateTimeInterface::ISO8601, $data['datetime']);
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new UnexpectedValueException();
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new UnexpectedValueException();
|
if (false === $result) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function normalize($date, $format = null, array $context = [])
|
public function normalize($date, $format = null, array $context = [])
|
||||||
|
@ -32,7 +32,7 @@ class PhonenumberNormalizer implements ContextAwareNormalizerInterface, Denormal
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param mixed $data
|
* @param string|null $data
|
||||||
* @param mixed $type
|
* @param mixed $type
|
||||||
* @param null|mixed $format
|
* @param null|mixed $format
|
||||||
*
|
*
|
||||||
@ -40,7 +40,7 @@ class PhonenumberNormalizer implements ContextAwareNormalizerInterface, Denormal
|
|||||||
*/
|
*/
|
||||||
public function denormalize($data, $type, $format = null, array $context = [])
|
public function denormalize($data, $type, $format = null, array $context = [])
|
||||||
{
|
{
|
||||||
if ('' === trim($data)) {
|
if ('' === trim((string) $data)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,6 +88,24 @@ final class NotificationTest extends KernelTestCase
|
|||||||
$this->assertNotContains($user1, $notification->getUnreadBy()->toArray());
|
$this->assertNotContains($user1, $notification->getUnreadBy()->toArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testAddressesEmail(): void
|
||||||
|
{
|
||||||
|
$notification = new Notification();
|
||||||
|
|
||||||
|
$notification->addAddressesEmail('test');
|
||||||
|
$notification->addAddressesEmail('other');
|
||||||
|
|
||||||
|
$this->assertContains('test', $notification->getAddressesEmails());
|
||||||
|
$this->assertContains('other', $notification->getAddressesEmails());
|
||||||
|
$this->assertContains('test', $notification->getAddressesEmailsAdded());
|
||||||
|
$this->assertContains('other', $notification->getAddressesEmailsAdded());
|
||||||
|
|
||||||
|
$notification->removeAddressesEmail('other');
|
||||||
|
|
||||||
|
$this->assertNotContains('other', $notification->getAddressesEmails());
|
||||||
|
$this->assertNotContains('other', $notification->getAddressesEmailsAdded());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @dataProvider generateNotificationData
|
* @dataProvider generateNotificationData
|
||||||
*/
|
*/
|
||||||
|
@ -0,0 +1,54 @@
|
|||||||
|
<?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 Serializer\Normalizer;
|
||||||
|
|
||||||
|
use Chill\MainBundle\Serializer\Normalizer\PhonenumberNormalizer;
|
||||||
|
use libphonenumber\PhoneNumber;
|
||||||
|
use libphonenumber\PhoneNumberUtil;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use Prophecy\Argument;
|
||||||
|
use Prophecy\PhpUnit\ProphecyTrait;
|
||||||
|
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
* @coversNothing
|
||||||
|
*/
|
||||||
|
final class PhonenumberNormalizerTest extends TestCase
|
||||||
|
{
|
||||||
|
use ProphecyTrait;
|
||||||
|
|
||||||
|
public function dataProviderNormalizePhonenumber()
|
||||||
|
{
|
||||||
|
$phonenumberUtil = PhoneNumberUtil::getInstance();
|
||||||
|
|
||||||
|
yield [$phonenumberUtil->parse('+32486123465'), 'docgen', ['docgen:expects' => PhoneNumber::class], '0486 12 34 65'];
|
||||||
|
|
||||||
|
yield [null, 'docgen', ['docgen:expects' => PhoneNumber::class], ''];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider dataProviderNormalizePhonenumber
|
||||||
|
*
|
||||||
|
* @param mixed $format
|
||||||
|
* @param mixed $context
|
||||||
|
* @param mixed $expected
|
||||||
|
*/
|
||||||
|
public function testNormalize(?Phonenumber $phonenumber, $format, $context, $expected)
|
||||||
|
{
|
||||||
|
$parameterBag = $this->prophesize(ParameterBagInterface::class);
|
||||||
|
$parameterBag->get(Argument::exact('chill_main'))->willReturn(['phone_helper' => ['default_carrier_code' => 'BE']]);
|
||||||
|
$normalizer = new PhonenumberNormalizer($parameterBag->reveal());
|
||||||
|
|
||||||
|
$this->assertEquals($expected, $normalizer->normalize($phonenumber, $format, $context));
|
||||||
|
}
|
||||||
|
}
|
@ -61,6 +61,15 @@ services:
|
|||||||
# set the 'lazy' option to TRUE to only instantiate listeners when they are used
|
# set the 'lazy' option to TRUE to only instantiate listeners when they are used
|
||||||
lazy: true
|
lazy: true
|
||||||
method: 'postPersistNotification'
|
method: 'postPersistNotification'
|
||||||
|
|
||||||
|
-
|
||||||
|
name: 'doctrine.orm.entity_listener'
|
||||||
|
event: 'postUpdate'
|
||||||
|
entity: 'Chill\MainBundle\Entity\Notification'
|
||||||
|
# set the 'lazy' option to TRUE to only instantiate listeners when they are used
|
||||||
|
lazy: true
|
||||||
|
method: 'postUpdateNotification'
|
||||||
|
|
||||||
-
|
-
|
||||||
name: 'doctrine.orm.entity_listener'
|
name: 'doctrine.orm.entity_listener'
|
||||||
event: 'postPersist'
|
event: 'postPersist'
|
||||||
|
@ -0,0 +1,46 @@
|
|||||||
|
<?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\Migrations\Main;
|
||||||
|
|
||||||
|
use Doctrine\DBAL\Schema\Schema;
|
||||||
|
use Doctrine\Migrations\AbstractMigration;
|
||||||
|
|
||||||
|
final class Version20220413154743 extends AbstractMigration
|
||||||
|
{
|
||||||
|
public function down(Schema $schema): void
|
||||||
|
{
|
||||||
|
$this->addSql('ALTER TABLE chill_main_notification DROP adressesEmails');
|
||||||
|
$this->addSql('ALTER TABLE chill_main_notification DROP accessKey');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDescription(): string
|
||||||
|
{
|
||||||
|
return 'add access keys and emails dest to notifications';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function up(Schema $schema): void
|
||||||
|
{
|
||||||
|
$this->addSql('ALTER TABLE chill_main_notification ADD addressesEmails JSON NOT NULL DEFAULT \'[]\';');
|
||||||
|
$this->addSql('ALTER TABLE chill_main_notification ADD accessKey TEXT DEFAULT NULL');
|
||||||
|
$this->addSql('WITH randoms AS (select
|
||||||
|
n.id,
|
||||||
|
string_agg(substr(characters, (random() * length(characters) + 0.5)::integer, 1), \'\') as random_word
|
||||||
|
from (values(\'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\')) as symbols(characters)
|
||||||
|
-- length of word
|
||||||
|
join generate_series(1, 16) on 1 = 1
|
||||||
|
JOIN chill_main_notification n ON true
|
||||||
|
GROUP BY n.id)
|
||||||
|
UPDATE chill_main_notification SET accessKey = randoms.random_word FROM randoms WHERE chill_main_notification.id = randoms.id');
|
||||||
|
$this->addSql('ALTER TABLE chill_main_notification ALTER accessKey DROP DEFAULT');
|
||||||
|
$this->addSql('ALTER TABLE chill_main_notification ALTER accessKey SET NOT NULL');
|
||||||
|
}
|
||||||
|
}
|
@ -450,4 +450,10 @@ notification:
|
|||||||
subject: Objet
|
subject: Objet
|
||||||
see_comments_thread: Voir le fil de commentaires associé
|
see_comments_thread: Voir le fil de commentaires associé
|
||||||
object_prefix: "[CHILL] notification - "
|
object_prefix: "[CHILL] notification - "
|
||||||
|
dest by email: Lien d'accès par email
|
||||||
|
Any email: Aucun email
|
||||||
|
Add an email: Ajouter un email
|
||||||
|
dest by email help: Les adresses email mentionnées ici recevront un lien d'accès. Un compte utilisateur sera toujours nécessaire.
|
||||||
|
Remove an email: Supprimer l'adresse email
|
||||||
|
Email with access link: Adresse email ayant reçu un lien d'accès
|
||||||
|
|
||||||
|
@ -514,6 +514,8 @@ class Household
|
|||||||
|
|
||||||
if ($iterator->valid()) {
|
if ($iterator->valid()) {
|
||||||
$current->setValidTo($iterator->current()->getValidFrom());
|
$current->setValidTo($iterator->current()->getValidFrom());
|
||||||
|
} else {
|
||||||
|
$current->setValidTo(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -276,25 +276,21 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
|
|||||||
/**
|
/**
|
||||||
* The person's first name.
|
* The person's first name.
|
||||||
*
|
*
|
||||||
* @var string
|
|
||||||
*
|
|
||||||
* @ORM\Column(type="string", length=255)
|
* @ORM\Column(type="string", length=255)
|
||||||
* @Assert\NotBlank(message="The firstname cannot be empty")
|
* @Assert\NotBlank(message="The firstname cannot be empty")
|
||||||
* @Assert\Length(
|
* @Assert\Length(
|
||||||
* max=255,
|
* max=255,
|
||||||
* )
|
* )
|
||||||
*/
|
*/
|
||||||
private $firstName;
|
private string $firstName = '';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* fullname canonical. Read-only field, which is calculated by
|
* fullname canonical. Read-only field, which is calculated by
|
||||||
* the database.
|
* the database.
|
||||||
*
|
*
|
||||||
* @var string
|
|
||||||
*
|
|
||||||
* @ORM\Column(type="text", nullable=true)
|
* @ORM\Column(type="text", nullable=true)
|
||||||
*/
|
*/
|
||||||
private $fullnameCanonical;
|
private string $fullnameCanonical = '';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The person's gender.
|
* The person's gender.
|
||||||
@ -328,6 +324,8 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
|
|||||||
* targetEntity=HouseholdMember::class,
|
* targetEntity=HouseholdMember::class,
|
||||||
* mappedBy="person"
|
* mappedBy="person"
|
||||||
* )
|
* )
|
||||||
|
*
|
||||||
|
* @var Collection|HouseholdMember[]
|
||||||
*/
|
*/
|
||||||
private Collection $householdParticipations;
|
private Collection $householdParticipations;
|
||||||
|
|
||||||
@ -343,15 +341,13 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
|
|||||||
/**
|
/**
|
||||||
* The person's last name.
|
* The person's last name.
|
||||||
*
|
*
|
||||||
* @var string
|
|
||||||
*
|
|
||||||
* @ORM\Column(type="string", length=255)
|
* @ORM\Column(type="string", length=255)
|
||||||
* @Assert\NotBlank(message="The lastname cannot be empty")
|
* @Assert\NotBlank(message="The lastname cannot be empty")
|
||||||
* @Assert\Length(
|
* @Assert\Length(
|
||||||
* max=255,
|
* max=255,
|
||||||
* )
|
* )
|
||||||
*/
|
*/
|
||||||
private $lastName;
|
private string $lastName = '';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The marital status of the person.
|
* The marital status of the person.
|
||||||
@ -1117,6 +1113,9 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
|
|||||||
return $this->householdAddresses;
|
return $this->householdAddresses;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Collection|HouseholdMember[]
|
||||||
|
*/
|
||||||
public function getHouseholdParticipations(): Collection
|
public function getHouseholdParticipations(): Collection
|
||||||
{
|
{
|
||||||
return $this->householdParticipations;
|
return $this->householdParticipations;
|
||||||
@ -1126,6 +1125,8 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
|
|||||||
* Get participation where the person does not share the household.
|
* Get participation where the person does not share the household.
|
||||||
*
|
*
|
||||||
* Order by startDate, desc
|
* Order by startDate, desc
|
||||||
|
*
|
||||||
|
* @return HouseholdMember[]
|
||||||
*/
|
*/
|
||||||
public function getHouseholdParticipationsNotShareHousehold(): Collection
|
public function getHouseholdParticipationsNotShareHousehold(): Collection
|
||||||
{
|
{
|
||||||
@ -1146,6 +1147,8 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
|
|||||||
* Get participation where the person does share the household.
|
* Get participation where the person does share the household.
|
||||||
*
|
*
|
||||||
* Order by startDate, desc
|
* Order by startDate, desc
|
||||||
|
*
|
||||||
|
* @return Collection|HouseholdMember[]
|
||||||
*/
|
*/
|
||||||
public function getHouseholdParticipationsShareHousehold(): Collection
|
public function getHouseholdParticipationsShareHousehold(): Collection
|
||||||
{
|
{
|
||||||
@ -1565,18 +1568,14 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
|
|||||||
|
|
||||||
public function setEmail(?string $email): self
|
public function setEmail(?string $email): self
|
||||||
{
|
{
|
||||||
if (null === $email) {
|
$this->email = trim((string) $email);
|
||||||
$email = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->email = $email;
|
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setFirstName(string $firstName): self
|
public function setFirstName(?string $firstName): self
|
||||||
{
|
{
|
||||||
$this->firstName = $firstName;
|
$this->firstName = (string) $firstName;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
@ -1602,9 +1601,9 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setLastName(string $lastName): self
|
public function setLastName(?string $lastName): self
|
||||||
{
|
{
|
||||||
$this->lastName = $lastName;
|
$this->lastName = (string) $lastName;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
@ -29,8 +29,12 @@ use Symfony\Contracts\Translation\TranslatorInterface;
|
|||||||
|
|
||||||
final class PersonResourceType extends AbstractType
|
final class PersonResourceType extends AbstractType
|
||||||
{
|
{
|
||||||
|
private PersonRenderInterface $personRender;
|
||||||
|
|
||||||
private ResourceKindRender $resourceKindRender;
|
private ResourceKindRender $resourceKindRender;
|
||||||
|
|
||||||
|
private ThirdPartyRender $thirdPartyRender;
|
||||||
|
|
||||||
private TranslatorInterface $translator;
|
private TranslatorInterface $translator;
|
||||||
|
|
||||||
public function __construct(ResourceKindRender $resourceKindRender, PersonRenderInterface $personRender, ThirdPartyRender $thirdPartyRender, TranslatorInterface $translator)
|
public function __construct(ResourceKindRender $resourceKindRender, PersonRenderInterface $personRender, ThirdPartyRender $thirdPartyRender, TranslatorInterface $translator)
|
||||||
|
@ -61,8 +61,6 @@ class MembersEditor
|
|||||||
throw new LogicException('You must define a household first');
|
throw new LogicException('You must define a household first');
|
||||||
}
|
}
|
||||||
|
|
||||||
$event = new PersonAddressMoveEvent($person);
|
|
||||||
|
|
||||||
$membership = (new HouseholdMember())
|
$membership = (new HouseholdMember())
|
||||||
->setStartDate($date)
|
->setStartDate($date)
|
||||||
->setPerson($person)
|
->setPerson($person)
|
||||||
@ -70,9 +68,15 @@ class MembersEditor
|
|||||||
->setHolder($holder)
|
->setHolder($holder)
|
||||||
->setComment($comment);
|
->setComment($comment);
|
||||||
$this->household->addMember($membership);
|
$this->household->addMember($membership);
|
||||||
$event->setNextMembership($membership);
|
|
||||||
|
|
||||||
if ($position->getShareHousehold()) {
|
if ($position->getShareHousehold()) {
|
||||||
|
// launch event only if moving to a "share household" position,
|
||||||
|
// and if the destination household is different than the previous one
|
||||||
|
$event = new PersonAddressMoveEvent($person);
|
||||||
|
$event->setNextMembership($membership);
|
||||||
|
|
||||||
|
$counter = 0;
|
||||||
|
|
||||||
foreach ($person->getHouseholdParticipationsShareHousehold() as $participation) {
|
foreach ($person->getHouseholdParticipationsShareHousehold() as $participation) {
|
||||||
if ($participation === $membership) {
|
if ($participation === $membership) {
|
||||||
continue;
|
continue;
|
||||||
@ -82,13 +86,24 @@ class MembersEditor
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
++$counter;
|
||||||
|
|
||||||
if ($participation->getEndDate() === null || $participation->getEndDate() > $date) {
|
if ($participation->getEndDate() === null || $participation->getEndDate() > $date) {
|
||||||
$event->setPreviousMembership($participation);
|
|
||||||
$participation->setEndDate($date);
|
$participation->setEndDate($date);
|
||||||
$this->membershipsAffected[] = $participation;
|
$this->membershipsAffected[] = $participation;
|
||||||
$this->oldMembershipsHashes[] = spl_object_hash($participation);
|
$this->oldMembershipsHashes[] = spl_object_hash($participation);
|
||||||
|
|
||||||
|
if ($participation->getHousehold() !== $this->household) {
|
||||||
|
$event->setPreviousMembership($participation);
|
||||||
|
$this->events[] = $event;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// send also the event if there was no participation before
|
||||||
|
if (0 === $counter) {
|
||||||
|
$this->events[] = $event;
|
||||||
|
}
|
||||||
|
|
||||||
foreach ($person->getHouseholdParticipationsNotShareHousehold() as $participation) {
|
foreach ($person->getHouseholdParticipationsNotShareHousehold() as $participation) {
|
||||||
if ($participation->getHousehold() === $this->household
|
if ($participation->getHousehold() === $this->household
|
||||||
@ -98,11 +113,30 @@ class MembersEditor
|
|||||||
$participation->setEndDate($membership->getStartDate());
|
$participation->setEndDate($membership->getStartDate());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// if a members is moved to the same household than the one he belongs to,
|
||||||
|
// we should make it leave the household
|
||||||
|
if ($person->getCurrentHousehold($date) === $this->household) {
|
||||||
|
$this->leaveMovement($date, $person);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if there are multiple belongings not sharing household, close the others
|
||||||
|
foreach ($person->getHouseholdParticipationsNotShareHousehold() as $participation) {
|
||||||
|
if ($participation === $membership) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($participation->getHousehold() === $this->household
|
||||||
|
&& ($participation->getEndDate() === null || $participation->getEndDate() > $membership->getStartDate())
|
||||||
|
&& $participation->getStartDate() <= $membership->getStartDate()
|
||||||
|
) {
|
||||||
|
$participation->setEndDate($membership->getStartDate());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->membershipsAffected[] = $membership;
|
$this->membershipsAffected[] = $membership;
|
||||||
$this->persistables[] = $membership;
|
$this->persistables[] = $membership;
|
||||||
$this->events[] = $event;
|
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
@ -88,7 +88,7 @@ class PersonMenuBuilder implements LocalMenuBuilderInterface
|
|||||||
->setExtras([
|
->setExtras([
|
||||||
'order' => 99999,
|
'order' => 99999,
|
||||||
]);
|
]);
|
||||||
|
/*
|
||||||
$menu->addChild($this->translator->trans('Person duplicate'), [
|
$menu->addChild($this->translator->trans('Person duplicate'), [
|
||||||
'route' => 'chill_person_duplicate_view',
|
'route' => 'chill_person_duplicate_view',
|
||||||
'routeParameters' => [
|
'routeParameters' => [
|
||||||
@ -98,7 +98,7 @@ class PersonMenuBuilder implements LocalMenuBuilderInterface
|
|||||||
->setExtras([
|
->setExtras([
|
||||||
'order' => 99999,
|
'order' => 99999,
|
||||||
]);
|
]);
|
||||||
|
*/
|
||||||
if (
|
if (
|
||||||
'visible' === $this->showAccompanyingPeriod
|
'visible' === $this->showAccompanyingPeriod
|
||||||
&& $this->security->isGranted(AccompanyingPeriodVoter::SEE, $parameters['person'])
|
&& $this->security->isGranted(AccompanyingPeriodVoter::SEE, $parameters['person'])
|
||||||
|
@ -136,8 +136,11 @@ final class PersonACLAwareRepository implements PersonACLAwareRepositoryInterfac
|
|||||||
$andWhereSearchClause = [];
|
$andWhereSearchClause = [];
|
||||||
$andWhereSearchClauseArgs = [];
|
$andWhereSearchClauseArgs = [];
|
||||||
|
|
||||||
if ('' !== $default) {
|
if ('' !== trim($default)) {
|
||||||
foreach (explode(' ', $default) as $str) {
|
foreach (explode(' ', $default) as $str) {
|
||||||
|
if ('' === trim($str)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
$pertinence[] =
|
$pertinence[] =
|
||||||
'STRICT_WORD_SIMILARITY(LOWER(UNACCENT(?)), person.fullnamecanonical) + ' .
|
'STRICT_WORD_SIMILARITY(LOWER(UNACCENT(?)), person.fullnamecanonical) + ' .
|
||||||
"(person.fullnamecanonical LIKE '%' || LOWER(UNACCENT(?)) || '%')::int + " .
|
"(person.fullnamecanonical LIKE '%' || LOWER(UNACCENT(?)) || '%')::int + " .
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
<label class="chill-no-data-statement">{{ $tc('resources.counter', counter) }}</label>
|
<label class="chill-no-data-statement">{{ $tc('resources.counter', counter) }}</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex-bloc mb-3">
|
<div class="flex-table mb-3">
|
||||||
<resource-item
|
<resource-item
|
||||||
v-for="resource in resources"
|
v-for="resource in resources"
|
||||||
v-bind:resource="resource"
|
v-bind:resource="resource"
|
||||||
|
@ -138,12 +138,12 @@
|
|||||||
@go-to-generate-document="submitBeforeGenerate"
|
@go-to-generate-document="submitBeforeGenerate"
|
||||||
>
|
>
|
||||||
<template v-slot:title>
|
<template v-slot:title>
|
||||||
<label class="col-sm-4 col-form-label">{{ $t('evaluation_generate_a_document') }}</label>
|
<label class="col-form-label">{{ $t('evaluation_generate_a_document') }}</label>
|
||||||
</template>
|
</template>
|
||||||
</pick-template>
|
</pick-template>
|
||||||
<div>
|
<div>
|
||||||
<label class="col-sm-4 col-form-label">{{ $t('document_upload') }}</label>
|
<label class="col-form-label">{{ $t('document_upload') }}</label>
|
||||||
<ul class="record_actions">
|
<ul class="record_actions document-upload">
|
||||||
<li>
|
<li>
|
||||||
<add-async-upload
|
<add-async-upload
|
||||||
:buttonTitle="$t('browse')"
|
:buttonTitle="$t('browse')"
|
||||||
@ -415,4 +415,7 @@ export default {
|
|||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
}
|
}
|
||||||
|
ul.document-upload {
|
||||||
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -45,7 +45,7 @@ const searchPersons = ({ query, options }, signal) => {
|
|||||||
const searchEntities = ({ query, options }, signal) => {
|
const searchEntities = ({ query, options }, signal) => {
|
||||||
let queryStr = parametersToString({ query, options });
|
let queryStr = parametersToString({ query, options });
|
||||||
let url = `/api/1.0/search.json?${queryStr}`;
|
let url = `/api/1.0/search.json?${queryStr}`;
|
||||||
return fetch(url)
|
return fetch(url, { signal })
|
||||||
.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');
|
||||||
|
@ -199,15 +199,26 @@ export default {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (query === this.search.query) {
|
if (query === this.search.query) {
|
||||||
if (this.currentSearchQueryController !== undefined) {
|
if (this.search.currentSearchQueryController !== null) {
|
||||||
this.currentSearchQueryController.abort()
|
this.search.currentSearchQueryController.abort();
|
||||||
}
|
}
|
||||||
this.currentSearchQueryController = new AbortController();
|
this.search.currentSearchQueryController = new AbortController();
|
||||||
searchEntities({ query, options: this.options }, this.currentSearchQueryController)
|
searchEntities({ query, options: this.options }, this.search.currentSearchQueryController.signal)
|
||||||
.then(suggested => new Promise((resolve, reject) => {
|
.then(suggested => new Promise((resolve, reject) => {
|
||||||
this.loadSuggestions(suggested.results);
|
this.loadSuggestions(suggested.results);
|
||||||
resolve();
|
resolve();
|
||||||
}));
|
}))
|
||||||
|
.catch(error => {
|
||||||
|
if (error instanceof DOMException) {
|
||||||
|
if (error.name === 'AbortError') {
|
||||||
|
console.log('request aborted due to user continue typing');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw error;
|
||||||
|
})
|
||||||
|
;
|
||||||
}
|
}
|
||||||
}.bind(this), query.length > 3 ? 300 : 700);
|
}.bind(this), query.length > 3 ? 300 : 700);
|
||||||
},
|
},
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
<div class="alert alert-warning alert-with-actions">
|
<div class="alert alert-warning alert-with-actions">
|
||||||
<div class="float-button bottom"><div class="box">
|
<div class="float-button bottom">
|
||||||
|
<div class="box">
|
||||||
|
|
||||||
<div class="action">
|
<div class="action">
|
||||||
<ul class="record_actions">
|
<ul class="record_actions">
|
||||||
<li>
|
<li>
|
||||||
@ -11,6 +13,7 @@
|
|||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
{{ 'Some peoples does not belong to any household currently. Add them to an household soon'|trans }}
|
<p>{{ 'Some peoples does not belong to any household currently. Add them to an household soon'|trans }}</p>
|
||||||
</div></div>
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -185,6 +185,7 @@
|
|||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
{% if accompanyingCourse.step != 'DRAFT' %}
|
||||||
<div class="mbloc col col-sm-6 col-lg-4">
|
<div class="mbloc col col-sm-6 col-lg-4">
|
||||||
<div class="notification-counter">
|
<div class="notification-counter">
|
||||||
<h4 class="item-key">{{ 'notification.Notifications'|trans }}</h4>
|
<h4 class="item-key">{{ 'notification.Notifications'|trans }}</h4>
|
||||||
@ -203,6 +204,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="social-actions my-4">
|
<div class="social-actions my-4">
|
||||||
|
@ -128,11 +128,13 @@
|
|||||||
'Chill\\PersonBundle\\Entity\\AccompanyingPeriod\\AccompanyingPeriodWork',
|
'Chill\\PersonBundle\\Entity\\AccompanyingPeriod\\AccompanyingPeriodWork',
|
||||||
w.id, [], suppEvaluations) }}
|
w.id, [], suppEvaluations) }}
|
||||||
</li>
|
</li>
|
||||||
|
{% if is_granted('CHILL_MAIN_ACCOMPANYING_PERIOD_WORK_UPDATE', w) %}
|
||||||
<li>
|
<li>
|
||||||
<a class="btn btn-edit" title="{{ 'Edit'|trans }}"
|
<a class="btn btn-edit" title="{{ 'Edit'|trans }}"
|
||||||
href="{{ chill_path_add_return_path('chill_person_accompanying_period_work_edit', { 'id': w.id }) }}"
|
href="{{ chill_path_add_return_path('chill_person_accompanying_period_work_edit', { 'id': w.id }) }}"
|
||||||
></a>
|
></a>
|
||||||
</li>
|
</li>
|
||||||
|
{% endif %}
|
||||||
{% if is_granted('CHILL_MAIN_ACCOMPANYING_PERIOD_WORK_DELETE', w) %}
|
{% if is_granted('CHILL_MAIN_ACCOMPANYING_PERIOD_WORK_DELETE', w) %}
|
||||||
<li>
|
<li>
|
||||||
<a class="btn btn-delete" title="{{ 'Delete'|trans }}"
|
<a class="btn btn-delete" title="{{ 'Delete'|trans }}"
|
||||||
|
@ -18,13 +18,7 @@
|
|||||||
<h1>{{ block('title') }}</h1>
|
<h1>{{ block('title') }}</h1>
|
||||||
|
|
||||||
{% if works|length == 0 %}
|
{% if works|length == 0 %}
|
||||||
<p class="chill-no-data-statement">{{ 'accompanying_course_work.Any work'|trans }}
|
<p class="chill-no-data-statement">{{ 'accompanying_course_work.Any work'|trans }}</p>
|
||||||
<a class="btn btn-sm btn-create"
|
|
||||||
title="{{ 'accompanying_course_work.create'|trans }}"
|
|
||||||
href="{{ chill_path_add_return_path('chill_person_accompanying_period_work_new', { 'id': accompanyingCourse.id }) }}"
|
|
||||||
></a>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
{% else %}
|
{% else %}
|
||||||
<div class="flex-table accompanying_course_work-list">
|
<div class="flex-table accompanying_course_work-list">
|
||||||
{% for w in works %}
|
{% for w in works %}
|
||||||
@ -35,6 +29,7 @@
|
|||||||
|
|
||||||
{{ chill_pagination(paginator) }}
|
{{ chill_pagination(paginator) }}
|
||||||
|
|
||||||
|
{% if is_granted('CHILL_MAIN_ACCOMPANYING_PERIOD_WORK_CREATE', accompanyingCourse) %}
|
||||||
<ul class="record_actions sticky-form-buttons">
|
<ul class="record_actions sticky-form-buttons">
|
||||||
<li>
|
<li>
|
||||||
<a href="{{ chill_path_add_return_path('chill_person_accompanying_period_work_new', { 'id': accompanyingCourse.id }) }}"
|
<a href="{{ chill_path_add_return_path('chill_person_accompanying_period_work_new', { 'id': accompanyingCourse.id }) }}"
|
||||||
@ -43,6 +38,7 @@
|
|||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -116,14 +116,40 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
{% if show_pinned_comment|default(false) and period.pinnedComment is not empty%}
|
||||||
|
<div class="item-row separator comment">
|
||||||
|
<div style="width: 100%">
|
||||||
|
<h4 class="item-key visually-hidden">{{ 'Pinned comment'|trans }}</h4>
|
||||||
|
<blockquote class="chill-user-quote">
|
||||||
|
{{ period.pinnedComment.content|u.truncate(750, '…', false)|chill_markdown_to_html }}
|
||||||
|
{% if period.pinnedComment.content|length > 750 %}
|
||||||
|
<a href="{{ chill_path_add_return_path('chill_person_accompanying_period_comment_list', {'accompanying_period_id': period.id}) }}">{{ 'Read more'|trans }}</a>
|
||||||
|
{% endif %}
|
||||||
|
<div class="metadata">
|
||||||
|
{{ 'Last updated by'| trans }}
|
||||||
|
<span class="user">
|
||||||
|
{{ period.pinnedComment.updatedBy|chill_entity_render_box }}
|
||||||
|
</span>
|
||||||
|
{{ 'on'|trans ~ ' ' }}
|
||||||
|
<span class="date">
|
||||||
|
{{ period.pinnedComment.updatedAt|format_datetime("medium", "short") }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</blockquote>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
<div class="item-row separator">
|
<div class="item-row separator">
|
||||||
|
{% if itemMeta is not defined %}
|
||||||
{% set notif_counter = chill_count_notifications('Chill\\PersonBundle\\Entity\\AccompanyingPeriod', period.id) %}
|
{% set notif_counter = chill_count_notifications('Chill\\PersonBundle\\Entity\\AccompanyingPeriod', period.id) %}
|
||||||
{% if notif_counter.total > 0 %}
|
{% if notif_counter.total > 0 %}
|
||||||
<div class="item-col item-meta">
|
<div class="item-col item-meta">
|
||||||
{{ chill_counter_notifications('Chill\\PersonBundle\\Entity\\AccompanyingPeriod', period.id) }}
|
{{ chill_counter_notifications('Chill\\PersonBundle\\Entity\\AccompanyingPeriod', period.id) }}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if itemMeta is defined %}
|
{% else %}
|
||||||
{{ itemMeta }}
|
{{ itemMeta }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<div class="item-col">
|
<div class="item-col">
|
||||||
|
@ -1,5 +1,10 @@
|
|||||||
{%- import "@ChillDocStore/Macro/macro.html.twig" as m -%}
|
{%- import "@ChillDocStore/Macro/macro.html.twig" as m -%}
|
||||||
|
|
||||||
|
{% if doc is null %}
|
||||||
|
<div class="alert alert-warning">
|
||||||
|
{{ 'workflow.doc for evaluation deleted'|trans }}
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
<div class="flex-table accompanying_course_work-list">
|
<div class="flex-table accompanying_course_work-list">
|
||||||
<div class="item-bloc evaluation-item bg-chill-llight-gray">
|
<div class="item-bloc evaluation-item bg-chill-llight-gray">
|
||||||
<div class="item-row mb-2">
|
<div class="item-row mb-2">
|
||||||
@ -131,3 +136,4 @@
|
|||||||
</ul>
|
</ul>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
@ -11,7 +11,7 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Chill\PersonBundle\Serializer\Normalizer;
|
namespace Chill\PersonBundle\Serializer\Normalizer;
|
||||||
|
|
||||||
use Chill\BudgetBundle\Service\Summary\SummaryBudget;
|
use Chill\BudgetBundle\Service\Summary\SummaryBudgetInterface;
|
||||||
use Chill\DocGeneratorBundle\Serializer\Helper\NormalizeNullValueHelper;
|
use Chill\DocGeneratorBundle\Serializer\Helper\NormalizeNullValueHelper;
|
||||||
use Chill\MainBundle\Entity\Address;
|
use Chill\MainBundle\Entity\Address;
|
||||||
use Chill\MainBundle\Entity\Civility;
|
use Chill\MainBundle\Entity\Civility;
|
||||||
@ -23,6 +23,7 @@ use Chill\PersonBundle\Repository\Relationships\RelationshipRepository;
|
|||||||
use Chill\PersonBundle\Templating\Entity\PersonRenderInterface;
|
use Chill\PersonBundle\Templating\Entity\PersonRenderInterface;
|
||||||
use DateTimeInterface;
|
use DateTimeInterface;
|
||||||
use Doctrine\Common\Collections\ArrayCollection;
|
use Doctrine\Common\Collections\ArrayCollection;
|
||||||
|
use libphonenumber\PhoneNumber;
|
||||||
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
|
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
|
||||||
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
|
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
|
||||||
use Symfony\Component\Serializer\Normalizer\ContextAwareNormalizerInterface;
|
use Symfony\Component\Serializer\Normalizer\ContextAwareNormalizerInterface;
|
||||||
@ -45,7 +46,7 @@ class PersonDocGenNormalizer implements
|
|||||||
|
|
||||||
private RelationshipRepository $relationshipRepository;
|
private RelationshipRepository $relationshipRepository;
|
||||||
|
|
||||||
private SummaryBudget $summaryBudget;
|
private SummaryBudgetInterface $summaryBudget;
|
||||||
|
|
||||||
private TranslatableStringHelper $translatableStringHelper;
|
private TranslatableStringHelper $translatableStringHelper;
|
||||||
|
|
||||||
@ -56,7 +57,7 @@ class PersonDocGenNormalizer implements
|
|||||||
RelationshipRepository $relationshipRepository,
|
RelationshipRepository $relationshipRepository,
|
||||||
TranslatorInterface $translator,
|
TranslatorInterface $translator,
|
||||||
TranslatableStringHelper $translatableStringHelper,
|
TranslatableStringHelper $translatableStringHelper,
|
||||||
SummaryBudget $summaryBudget
|
SummaryBudgetInterface $summaryBudget
|
||||||
) {
|
) {
|
||||||
$this->personRender = $personRender;
|
$this->personRender = $personRender;
|
||||||
$this->relationshipRepository = $relationshipRepository;
|
$this->relationshipRepository = $relationshipRepository;
|
||||||
@ -71,6 +72,7 @@ class PersonDocGenNormalizer implements
|
|||||||
$dateContext = $context;
|
$dateContext = $context;
|
||||||
$dateContext['docgen:expects'] = DateTimeInterface::class;
|
$dateContext['docgen:expects'] = DateTimeInterface::class;
|
||||||
$addressContext = array_merge($context, ['docgen:expects' => Address::class]);
|
$addressContext = array_merge($context, ['docgen:expects' => Address::class]);
|
||||||
|
$phonenumberContext = array_merge($context, ['docgen:expects' => PhoneNumber::class]);
|
||||||
$personResourceContext = array_merge($context, [
|
$personResourceContext = array_merge($context, [
|
||||||
'docgen:expects' => Person\PersonResource::class,
|
'docgen:expects' => Person\PersonResource::class,
|
||||||
// we simplify the list of attributes for the embedded persons
|
// we simplify the list of attributes for the embedded persons
|
||||||
@ -113,9 +115,9 @@ class PersonDocGenNormalizer implements
|
|||||||
'maritalStatus' => null !== ($ms = $person->getMaritalStatus()) ? $this->translatableStringHelper->localize($ms->getName()) : '',
|
'maritalStatus' => null !== ($ms = $person->getMaritalStatus()) ? $this->translatableStringHelper->localize($ms->getName()) : '',
|
||||||
'maritalStatusDate' => $this->normalizer->normalize($person->getMaritalStatusDate(), $format, $dateContext),
|
'maritalStatusDate' => $this->normalizer->normalize($person->getMaritalStatusDate(), $format, $dateContext),
|
||||||
'email' => $person->getEmail(),
|
'email' => $person->getEmail(),
|
||||||
'firstPhoneNumber' => $this->normalizer->normalize($person->getPhonenumber() ?? $person->getMobilenumber(), $format, $context),
|
'firstPhoneNumber' => $this->normalizer->normalize($person->getPhonenumber() ?? $person->getMobilenumber(), $format, $phonenumberContext),
|
||||||
'fixPhoneNumber' => $this->normalizer->normalize($person->getPhonenumber(), $format, $context),
|
'fixPhoneNumber' => $this->normalizer->normalize($person->getPhonenumber(), $format, $phonenumberContext),
|
||||||
'mobilePhoneNumber' => $this->normalizer->normalize($person->getMobilenumber(), $format, $context),
|
'mobilePhoneNumber' => $this->normalizer->normalize($person->getMobilenumber(), $format, $phonenumberContext),
|
||||||
'nationality' => null !== ($c = $person->getNationality()) ? $this->translatableStringHelper->localize($c->getName()) : '',
|
'nationality' => null !== ($c = $person->getNationality()) ? $this->translatableStringHelper->localize($c->getName()) : '',
|
||||||
'placeOfBirth' => $person->getPlaceOfBirth(),
|
'placeOfBirth' => $person->getPlaceOfBirth(),
|
||||||
'memo' => $person->getMemo(),
|
'memo' => $person->getMemo(),
|
||||||
@ -214,6 +216,14 @@ class PersonDocGenNormalizer implements
|
|||||||
$data['relations'] = [];
|
$data['relations'] = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($context['docgen:person:with-budget'] ?? false) {
|
||||||
|
$data['budget']['person'] = $this->summaryBudget->getSummaryForPerson(null);
|
||||||
|
|
||||||
|
if ($context['docgen:person:with-household'] ?? false) {
|
||||||
|
$data['budget']['household'] = $this->summaryBudget->getSummaryForHousehold(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -208,8 +208,8 @@ class PersonJsonNormalizer implements DenormalizerAwareInterface, NormalizerAwar
|
|||||||
'birthdate' => $this->normalizer->normalize($person->getBirthdate(), $format, $context),
|
'birthdate' => $this->normalizer->normalize($person->getBirthdate(), $format, $context),
|
||||||
'deathdate' => $this->normalizer->normalize($person->getDeathdate(), $format, $context),
|
'deathdate' => $this->normalizer->normalize($person->getDeathdate(), $format, $context),
|
||||||
'age' => $this->normalizer->normalize($person->getAge(), $format, $context),
|
'age' => $this->normalizer->normalize($person->getAge(), $format, $context),
|
||||||
'phonenumber' => $this->normalizer->normalize($person->getPhonenumber()),
|
'phonenumber' => $this->normalizer->normalize($person->getPhonenumber(), $format, $context),
|
||||||
'mobilenumber' => $this->normalizer->normalize($person->getMobilenumber()),
|
'mobilenumber' => $this->normalizer->normalize($person->getMobilenumber(), $format, $context),
|
||||||
'email' => $person->getEmail(),
|
'email' => $person->getEmail(),
|
||||||
'gender' => $person->getGender(),
|
'gender' => $person->getGender(),
|
||||||
];
|
];
|
||||||
|
@ -152,8 +152,25 @@ class AccompanyingPeriodContext implements
|
|||||||
$options = $template->getOptions();
|
$options = $template->getOptions();
|
||||||
$persons = $entity->getCurrentParticipations()->map(static function (AccompanyingPeriodParticipation $p) {
|
$persons = $entity->getCurrentParticipations()->map(static function (AccompanyingPeriodParticipation $p) {
|
||||||
return $p->getPerson();
|
return $p->getPerson();
|
||||||
})
|
});
|
||||||
->toArray();
|
|
||||||
|
foreach ($entity->getCurrentParticipations() as $p) {
|
||||||
|
foreach ($p->getPerson()->getResources() as $r) {
|
||||||
|
if (null !== $r->getPerson() && !$persons->contains($r->getPerson())) {
|
||||||
|
$persons->add($r->getPerson());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null !== $entity->getRequestorPerson() && !$persons->contains($entity->getRequestorPerson())) {
|
||||||
|
$persons->add($entity->getRequestorPerson());
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($entity->getResources() as $r) {
|
||||||
|
if (null !== $r->getPerson() && !$persons->contains($r->getPerson())) {
|
||||||
|
$persons->add($r->getPerson());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
foreach (['mainPerson', 'person1', 'person2'] as $key) {
|
foreach (['mainPerson', 'person1', 'person2'] as $key) {
|
||||||
if ($options[$key] ?? false) {
|
if ($options[$key] ?? false) {
|
||||||
|
@ -65,7 +65,7 @@ final class HouseholdApiControllerTest extends WebTestCase
|
|||||||
}
|
}
|
||||||
|
|
||||||
$reference = $em->createQueryBuilder()->select('ar')->from(AddressReference::class, 'ar')
|
$reference = $em->createQueryBuilder()->select('ar')->from(AddressReference::class, 'ar')
|
||||||
->setFirstResult(random_int(0, $nbReference))
|
->setFirstResult(random_int(0, $nbReference - 1))
|
||||||
->setMaxResults(1)
|
->setMaxResults(1)
|
||||||
->getQuery()->getSingleResult();
|
->getQuery()->getSingleResult();
|
||||||
|
|
||||||
|
@ -75,6 +75,7 @@ final class HouseholdTest extends TestCase
|
|||||||
|
|
||||||
$lastAddress = new Address();
|
$lastAddress = new Address();
|
||||||
$lastAddress->setValidFrom($yesterday = new DateTime('yesterday'));
|
$lastAddress->setValidFrom($yesterday = new DateTime('yesterday'));
|
||||||
|
$lastAddress->setValidTo(new DateTime('tomorrow'));
|
||||||
$household->addAddress($lastAddress);
|
$household->addAddress($lastAddress);
|
||||||
|
|
||||||
$this->assertNull($lastAddress->getValidTo());
|
$this->assertNull($lastAddress->getValidTo());
|
||||||
@ -82,6 +83,7 @@ final class HouseholdTest extends TestCase
|
|||||||
|
|
||||||
$previousAddress = new Address();
|
$previousAddress = new Address();
|
||||||
$previousAddress->setValidFrom($oneMonthAgo = new DateTime('1 month ago'));
|
$previousAddress->setValidFrom($oneMonthAgo = new DateTime('1 month ago'));
|
||||||
|
$previousAddress->setValidTo(new DateTime('now'));
|
||||||
$household->addAddress($previousAddress);
|
$household->addAddress($previousAddress);
|
||||||
|
|
||||||
$addresses = $household->getAddressesOrdered();
|
$addresses = $household->getAddressesOrdered();
|
||||||
@ -95,6 +97,7 @@ final class HouseholdTest extends TestCase
|
|||||||
|
|
||||||
$futureAddress = new Address();
|
$futureAddress = new Address();
|
||||||
$futureAddress->setValidFrom($tomorrow = new DateTime('tomorrow'));
|
$futureAddress->setValidFrom($tomorrow = new DateTime('tomorrow'));
|
||||||
|
$futureAddress->setValidTo(new DateTime('2150-01-01'));
|
||||||
$household->addAddress($futureAddress);
|
$household->addAddress($futureAddress);
|
||||||
|
|
||||||
$addresses = $household->getAddressesOrdered();
|
$addresses = $household->getAddressesOrdered();
|
||||||
|
@ -12,6 +12,7 @@ declare(strict_types=1);
|
|||||||
namespace Chill\PersonBundle\Tests\Household;
|
namespace Chill\PersonBundle\Tests\Household;
|
||||||
|
|
||||||
use Chill\PersonBundle\Entity\Household\Household;
|
use Chill\PersonBundle\Entity\Household\Household;
|
||||||
|
use Chill\PersonBundle\Entity\Household\HouseholdMember;
|
||||||
use Chill\PersonBundle\Entity\Household\Position;
|
use Chill\PersonBundle\Entity\Household\Position;
|
||||||
use Chill\PersonBundle\Entity\Person;
|
use Chill\PersonBundle\Entity\Person;
|
||||||
use Chill\PersonBundle\Event\Person\PersonAddressMoveEvent;
|
use Chill\PersonBundle\Event\Person\PersonAddressMoveEvent;
|
||||||
@ -39,6 +40,122 @@ final class MembersEditorTest extends TestCase
|
|||||||
$this->factory = $this->buildMembersEditorFactory();
|
$this->factory = $this->buildMembersEditorFactory();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testAddingParticipationNotSharingHouseholdCloseTheOldOnes()
|
||||||
|
{
|
||||||
|
$person = new Person();
|
||||||
|
$position = (new Position())->setShareHousehold(false);
|
||||||
|
$household = new Household();
|
||||||
|
|
||||||
|
// set a first time the person in position
|
||||||
|
$factory = $this->buildMembersEditorFactory();
|
||||||
|
$editor = $factory->createEditor($household);
|
||||||
|
|
||||||
|
$editor->addMovement($aMonthAgo = new DateTimeImmutable('1 month ago'), $person, $position);
|
||||||
|
|
||||||
|
// set a second time the person in position
|
||||||
|
$factory = $this->buildMembersEditorFactory();
|
||||||
|
$editor = $factory->createEditor($household);
|
||||||
|
|
||||||
|
$editor->addMovement($yesterday = new DateTimeImmutable('yesterday'), $person, $position);
|
||||||
|
|
||||||
|
$this->assertCount(2, $person->getHouseholdParticipationsNotShareHousehold());
|
||||||
|
|
||||||
|
$startDates = [];
|
||||||
|
$endDates = [];
|
||||||
|
|
||||||
|
foreach ($person->getHouseholdParticipationsNotShareHousehold() as $participation) {
|
||||||
|
$startDates[] = $participation->getStartDate();
|
||||||
|
$endDates[] = $participation->getEndDate();
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->assertContains($aMonthAgo, $startDates);
|
||||||
|
$this->assertContains($yesterday, $startDates);
|
||||||
|
$this->assertContains($yesterday, $endDates);
|
||||||
|
$this->assertContains(null, $endDates);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We test here a move for a person:.
|
||||||
|
*
|
||||||
|
* * which was in a position "sharing household"
|
||||||
|
* * which move to the another household, in a position "not sharing household"
|
||||||
|
*
|
||||||
|
* The person should stays in the two households
|
||||||
|
*/
|
||||||
|
public function testMoveFromSharingHouseholdToNotSharingHousehouldInDifferentHousehold()
|
||||||
|
{
|
||||||
|
$person = new Person();
|
||||||
|
$household = new Household();
|
||||||
|
$positionSharing = (new Position())->setShareHousehold(true);
|
||||||
|
$positionNotSharing = (new Position())->setShareHousehold(false);
|
||||||
|
$factory = $this->buildMembersEditorFactory();
|
||||||
|
$editor = $factory->createEditor($household);
|
||||||
|
|
||||||
|
// we add the member to the household
|
||||||
|
$editor->addMovement(new DateTimeImmutable('1 month ago'), $person, $positionSharing);
|
||||||
|
|
||||||
|
// double check that the person is in the household
|
||||||
|
$this->assertContains($person, $household->getCurrentPersons());
|
||||||
|
|
||||||
|
// we do the move to the position not sharing household
|
||||||
|
$editor = $factory->createEditor($household2 = new Household());
|
||||||
|
$editor->addMovement(new DateTimeImmutable('yesterday'), $person, $positionNotSharing);
|
||||||
|
|
||||||
|
$sharings = $household->getCurrentMembers()->filter(static function (HouseholdMember $m) {
|
||||||
|
return $m->getShareHousehold();
|
||||||
|
});
|
||||||
|
$notSharing = $household2->getCurrentMembers()->filter(static function (HouseholdMember $m) {
|
||||||
|
return !$m->getShareHousehold();
|
||||||
|
});
|
||||||
|
|
||||||
|
$this->assertCount(1, $notSharing);
|
||||||
|
$this->assertCount(1, $sharings);
|
||||||
|
|
||||||
|
$getPerson = static function (HouseholdMember $m) { return $m->getPerson(); };
|
||||||
|
|
||||||
|
$this->assertContains($person, $notSharing->map($getPerson));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We test here a move for a person:.
|
||||||
|
*
|
||||||
|
* * which was in a position "sharing household"
|
||||||
|
* * which move to the same household, in a position "not sharing household"
|
||||||
|
*/
|
||||||
|
public function testMoveFromSharingHouseholdToNotSharingHousehouldInSamehousehold()
|
||||||
|
{
|
||||||
|
$person = new Person();
|
||||||
|
$household = new Household();
|
||||||
|
$positionSharing = (new Position())->setShareHousehold(true);
|
||||||
|
$positionNotSharing = (new Position())->setShareHousehold(false);
|
||||||
|
$factory = $this->buildMembersEditorFactory();
|
||||||
|
$editor = $factory->createEditor($household);
|
||||||
|
|
||||||
|
// we add the member to the household
|
||||||
|
$editor->addMovement(new DateTimeImmutable('1 month ago'), $person, $positionSharing);
|
||||||
|
|
||||||
|
// double check that the person is in the household
|
||||||
|
$this->assertContains($person, $household->getCurrentPersons());
|
||||||
|
|
||||||
|
// we do the move to the position not sharing household
|
||||||
|
$editor = $factory->createEditor($household);
|
||||||
|
$editor->addMovement(new DateTimeImmutable('yesterday'), $person, $positionNotSharing);
|
||||||
|
|
||||||
|
$sharings = $household->getCurrentMembers()->filter(static function (HouseholdMember $m) {
|
||||||
|
return $m->getShareHousehold();
|
||||||
|
});
|
||||||
|
$notSharing = $household->getCurrentMembers()->filter(static function (HouseholdMember $m) {
|
||||||
|
return !$m->getShareHousehold();
|
||||||
|
});
|
||||||
|
|
||||||
|
$this->assertCount(1, $notSharing);
|
||||||
|
$this->assertCount(0, $sharings);
|
||||||
|
|
||||||
|
$getPerson = static function (HouseholdMember $m) { return $m->getPerson(); };
|
||||||
|
|
||||||
|
$this->assertContains($person, $notSharing->map($getPerson));
|
||||||
|
}
|
||||||
|
|
||||||
public function testMovePersonWithoutSharedHousehold()
|
public function testMovePersonWithoutSharedHousehold()
|
||||||
{
|
{
|
||||||
$person = new Person();
|
$person = new Person();
|
||||||
@ -126,7 +243,7 @@ final class MembersEditorTest extends TestCase
|
|||||||
$this->assertEquals($date, $membership1->getEndDate());
|
$this->assertEquals($date, $membership1->getEndDate());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testPostMove()
|
public function testPostMoveToAPositionNotSharingHousehold()
|
||||||
{
|
{
|
||||||
$person = new Person();
|
$person = new Person();
|
||||||
$position = (new Position())
|
$position = (new Position())
|
||||||
@ -134,6 +251,86 @@ final class MembersEditorTest extends TestCase
|
|||||||
$household1 = new Household();
|
$household1 = new Household();
|
||||||
$household2 = new Household();
|
$household2 = new Household();
|
||||||
$eventDispatcher = $this->prophesize(EventDispatcherInterface::class);
|
$eventDispatcher = $this->prophesize(EventDispatcherInterface::class);
|
||||||
|
$eventDispatcher
|
||||||
|
->dispatch(Argument::type(PersonAddressMoveEvent::class))
|
||||||
|
->shouldNotBeCalled();
|
||||||
|
$factory = $this->buildMembersEditorFactory(
|
||||||
|
$eventDispatcher->reveal(),
|
||||||
|
null
|
||||||
|
);
|
||||||
|
$editor = $factory->createEditor($household1);
|
||||||
|
|
||||||
|
$editor->addMovement(new DateTimeImmutable('now'), $person, $position);
|
||||||
|
|
||||||
|
$editor->postMove();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testPostMoveToAPositionSharingHouseholdAndSameHousehold()
|
||||||
|
{
|
||||||
|
$person = new Person();
|
||||||
|
$position = (new Position())
|
||||||
|
->setShareHousehold(true);
|
||||||
|
$position2 = (new Position())
|
||||||
|
->setShareHousehold(true);
|
||||||
|
$household1 = new Household();
|
||||||
|
|
||||||
|
// set into the first household
|
||||||
|
$editor = $this->buildMembersEditorFactory()
|
||||||
|
->createEditor($household1);
|
||||||
|
$editor->addMovement(new DateTimeImmutable('1 year ago'), $person, $position);
|
||||||
|
|
||||||
|
// prepare for next move
|
||||||
|
$eventDispatcher = $this->prophesize(EventDispatcherInterface::class);
|
||||||
|
$eventDispatcher
|
||||||
|
->dispatch(Argument::type(PersonAddressMoveEvent::class))
|
||||||
|
->shouldNotBeCalled();
|
||||||
|
$factory = $this->buildMembersEditorFactory(
|
||||||
|
$eventDispatcher->reveal(),
|
||||||
|
null
|
||||||
|
);
|
||||||
|
$editor = $factory->createEditor($household1);
|
||||||
|
|
||||||
|
$editor->addMovement(new DateTimeImmutable('now'), $person, $position2);
|
||||||
|
|
||||||
|
$editor->postMove();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testPostMoveToAPositionSharingHouseholdFromDifferentHousehold()
|
||||||
|
{
|
||||||
|
$person = new Person();
|
||||||
|
$position = (new Position())
|
||||||
|
->setShareHousehold(true);
|
||||||
|
$household1 = new Household();
|
||||||
|
$household2 = new Household();
|
||||||
|
|
||||||
|
// set into the first household
|
||||||
|
$editor = $this->buildMembersEditorFactory()
|
||||||
|
->createEditor($household1);
|
||||||
|
$editor->addMovement(new DateTimeImmutable('1 year ago'), $person, $position);
|
||||||
|
|
||||||
|
// perform now the movement
|
||||||
|
$eventDispatcher = $this->prophesize(EventDispatcherInterface::class);
|
||||||
|
$eventDispatcher
|
||||||
|
->dispatch(Argument::type(PersonAddressMoveEvent::class))
|
||||||
|
->shouldBeCalled();
|
||||||
|
$factory = $this->buildMembersEditorFactory(
|
||||||
|
$eventDispatcher->reveal(),
|
||||||
|
null
|
||||||
|
);
|
||||||
|
$editor = $factory->createEditor($household2);
|
||||||
|
|
||||||
|
$editor->addMovement(new DateTimeImmutable('now'), $person, $position);
|
||||||
|
|
||||||
|
$editor->postMove();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testPostMoveToAPositionSharingHouseholdFromNoHousehold()
|
||||||
|
{
|
||||||
|
$person = new Person();
|
||||||
|
$position = (new Position())
|
||||||
|
->setShareHousehold(true);
|
||||||
|
$household1 = new Household();
|
||||||
|
$eventDispatcher = $this->prophesize(EventDispatcherInterface::class);
|
||||||
$eventDispatcher
|
$eventDispatcher
|
||||||
->dispatch(Argument::type(PersonAddressMoveEvent::class))
|
->dispatch(Argument::type(PersonAddressMoveEvent::class))
|
||||||
->shouldBeCalled();
|
->shouldBeCalled();
|
||||||
|
@ -11,6 +11,7 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Serializer\Normalizer;
|
namespace Serializer\Normalizer;
|
||||||
|
|
||||||
|
use Chill\BudgetBundle\Service\Summary\SummaryBudgetInterface;
|
||||||
use Chill\MainBundle\Templating\TranslatableStringHelper;
|
use Chill\MainBundle\Templating\TranslatableStringHelper;
|
||||||
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||||
use Chill\PersonBundle\Entity\Household\Household;
|
use Chill\PersonBundle\Entity\Household\Household;
|
||||||
@ -72,6 +73,17 @@ final class PersonDocGenNormalizerTest extends KernelTestCase
|
|||||||
$this->normalizer = self::$container->get(NormalizerInterface::class);
|
$this->normalizer = self::$container->get(NormalizerInterface::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function dataGeneratorNormalizationNullOrNotNullHaveSameKeys(): iterable
|
||||||
|
{
|
||||||
|
yield [['docgen:expects' => Person::class, 'groups' => ['docgen:read']]];
|
||||||
|
|
||||||
|
yield [['docgen:expects' => Person::class, 'groups' => ['docgen:read'], 'docgen:person:with-household' => true]];
|
||||||
|
|
||||||
|
yield [['docgen:expects' => Person::class, 'groups' => ['docgen:read'], 'docgen:person:with-relations' => true]];
|
||||||
|
|
||||||
|
yield [['docgen:expects' => Person::class, 'groups' => ['docgen:read'], 'docgen:person:with-budget' => true]];
|
||||||
|
}
|
||||||
|
|
||||||
public function generateData()
|
public function generateData()
|
||||||
{
|
{
|
||||||
$person = new Person();
|
$person = new Person();
|
||||||
@ -90,12 +102,16 @@ final class PersonDocGenNormalizerTest extends KernelTestCase
|
|||||||
yield [null, self::BLANK, 'normalization for a null person'];
|
yield [null, self::BLANK, 'normalization for a null person'];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testNormalizationNullOrNotNullHaveSameKeys()
|
/**
|
||||||
|
* @dataProvider dataGeneratorNormalizationNullOrNotNullHaveSameKeys
|
||||||
|
*
|
||||||
|
* @param mixed $context
|
||||||
|
*/
|
||||||
|
public function testNormalizationNullOrNotNullHaveSameKeys($context)
|
||||||
{
|
{
|
||||||
$this->markTestSkipped();
|
|
||||||
$period = new Person();
|
$period = new Person();
|
||||||
$notNullData = $this->buildPersonNormalizer()->normalize($period, 'docgen', ['docgen:expects' => Person::class]);
|
$notNullData = $this->buildPersonNormalizer()->normalize($period, 'docgen', $context);
|
||||||
$nullData = $this->buildPersonNormalizer()->normalize(null, 'docgen', ['docgen:expects' => Person::class]);
|
$nullData = $this->buildPersonNormalizer()->normalize(null, 'docgen', $context);
|
||||||
|
|
||||||
$this->assertEqualsCanonicalizing(
|
$this->assertEqualsCanonicalizing(
|
||||||
array_keys($notNullData),
|
array_keys($notNullData),
|
||||||
@ -131,7 +147,6 @@ final class PersonDocGenNormalizerTest extends KernelTestCase
|
|||||||
|
|
||||||
public function testNormalizePersonWithHousehold()
|
public function testNormalizePersonWithHousehold()
|
||||||
{
|
{
|
||||||
$this->markTestSkipped();
|
|
||||||
$household = new Household();
|
$household = new Household();
|
||||||
$person = new Person();
|
$person = new Person();
|
||||||
$person
|
$person
|
||||||
@ -172,7 +187,6 @@ final class PersonDocGenNormalizerTest extends KernelTestCase
|
|||||||
|
|
||||||
public function testNormalizePersonWithRelationships()
|
public function testNormalizePersonWithRelationships()
|
||||||
{
|
{
|
||||||
$this->markTestSkipped();
|
|
||||||
$person = (new Person())->setFirstName('Renaud')->setLastName('megane');
|
$person = (new Person())->setFirstName('Renaud')->setLastName('megane');
|
||||||
$father = (new Person())->setFirstName('Clément')->setLastName('megane');
|
$father = (new Person())->setFirstName('Clément')->setLastName('megane');
|
||||||
$mother = (new Person())->setFirstName('Mireille')->setLastName('Mathieu');
|
$mother = (new Person())->setFirstName('Mireille')->setLastName('Mathieu');
|
||||||
@ -235,13 +249,25 @@ final class PersonDocGenNormalizerTest extends KernelTestCase
|
|||||||
?RelationshipRepository $relationshipRepository = null,
|
?RelationshipRepository $relationshipRepository = null,
|
||||||
?TranslatorInterface $translator = null,
|
?TranslatorInterface $translator = null,
|
||||||
?TranslatableStringHelper $translatableStringHelper = null,
|
?TranslatableStringHelper $translatableStringHelper = null,
|
||||||
?NormalizerInterface $normalizer = null
|
?NormalizerInterface $normalizer = null,
|
||||||
|
?SummaryBudgetInterface $summaryBudget = null
|
||||||
): PersonDocGenNormalizer {
|
): PersonDocGenNormalizer {
|
||||||
|
if (null === $summaryBudget) {
|
||||||
|
$summaryBudget = $this->prophesize(SummaryBudgetInterface::class);
|
||||||
|
$summaryBudget->getSummaryForHousehold(Argument::any())->willReturn(
|
||||||
|
['resources' => [], 'charges' => []]
|
||||||
|
);
|
||||||
|
$summaryBudget->getSummaryForPerson(Argument::any())->willReturn(
|
||||||
|
['resources' => [], 'charges' => []]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
$personDocGenNormalizer = new PersonDocGenNormalizer(
|
$personDocGenNormalizer = new PersonDocGenNormalizer(
|
||||||
$personRender ?? self::$container->get(PersonRender::class),
|
$personRender ?? self::$container->get(PersonRender::class),
|
||||||
$relationshipRepository ?? self::$container->get(RelationshipRepository::class),
|
$relationshipRepository ?? self::$container->get(RelationshipRepository::class),
|
||||||
$translator ?? self::$container->get(TranslatorInterface::class),
|
$translator ?? self::$container->get(TranslatorInterface::class),
|
||||||
$translatableStringHelper ?? self::$container->get(TranslatableStringHelperInterface::class)
|
$translatableStringHelper ?? self::$container->get(TranslatableStringHelperInterface::class),
|
||||||
|
$summaryBudget->reveal(),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (null === $normalizer) {
|
if (null === $normalizer) {
|
||||||
@ -259,13 +285,31 @@ final class PersonDocGenNormalizerTest extends KernelTestCase
|
|||||||
?PersonRender $personRender = null,
|
?PersonRender $personRender = null,
|
||||||
?RelationshipRepository $relationshipRepository = null,
|
?RelationshipRepository $relationshipRepository = null,
|
||||||
?TranslatorInterface $translator = null,
|
?TranslatorInterface $translator = null,
|
||||||
?TranslatableStringHelper $translatableStringHelper = null
|
?TranslatableStringHelper $translatableStringHelper = null,
|
||||||
|
?SummaryBudgetInterface $summaryBudget = null
|
||||||
): PersonDocGenNormalizer {
|
): PersonDocGenNormalizer {
|
||||||
|
if (null === $relationshipRepository) {
|
||||||
|
$relationshipRepository = $this->prophesize(RelationshipRepository::class);
|
||||||
|
$relationshipRepository->findByPerson(Argument::type(Person::class))->willReturn([]);
|
||||||
|
$relationshipRepository = $relationshipRepository->reveal();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null === $summaryBudget) {
|
||||||
|
$summaryBudget = $this->prophesize(SummaryBudgetInterface::class);
|
||||||
|
$summaryBudget->getSummaryForHousehold(Argument::any())->willReturn(
|
||||||
|
['resources' => [], 'charges' => []]
|
||||||
|
);
|
||||||
|
$summaryBudget->getSummaryForPerson(Argument::any())->willReturn(
|
||||||
|
['resources' => [], 'charges' => []]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
$normalizer = new PersonDocGenNormalizer(
|
$normalizer = new PersonDocGenNormalizer(
|
||||||
$personRender ?? self::$container->get(PersonRender::class),
|
$personRender ?? self::$container->get(PersonRender::class),
|
||||||
$relationshipRepository ?? self::$container->get(RelationshipRepository::class),
|
$relationshipRepository,
|
||||||
$translator ?? self::$container->get(TranslatorInterface::class),
|
$translator ?? self::$container->get(TranslatorInterface::class),
|
||||||
$translatableStringHelper ?? self::$container->get(TranslatableStringHelperInterface::class)
|
$translatableStringHelper ?? self::$container->get(TranslatableStringHelperInterface::class),
|
||||||
|
$summaryBudget->reveal()
|
||||||
);
|
);
|
||||||
$normalizerManager = $this->prophesize(NormalizerInterface::class);
|
$normalizerManager = $this->prophesize(NormalizerInterface::class);
|
||||||
$normalizerManager->supportsNormalization(Argument::any(), 'docgen', Argument::any())->willReturn(true);
|
$normalizerManager->supportsNormalization(Argument::any(), 'docgen', Argument::any())->willReturn(true);
|
||||||
|
@ -48,6 +48,12 @@ class AccompanyingPeriodWorkEvaluationDocumentWorkflowHandler implements EntityW
|
|||||||
{
|
{
|
||||||
$doc = $this->getRelatedEntity($entityWorkflow);
|
$doc = $this->getRelatedEntity($entityWorkflow);
|
||||||
|
|
||||||
|
if (null === $doc) {
|
||||||
|
return [
|
||||||
|
'persons' => [],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'persons' => $doc->getAccompanyingPeriodWorkEvaluation()
|
'persons' => $doc->getAccompanyingPeriodWorkEvaluation()
|
||||||
->getAccompanyingPeriodWork()->getPersons(),
|
->getAccompanyingPeriodWork()->getPersons(),
|
||||||
@ -58,6 +64,10 @@ class AccompanyingPeriodWorkEvaluationDocumentWorkflowHandler implements EntityW
|
|||||||
{
|
{
|
||||||
$doc = $this->getRelatedEntity($entityWorkflow);
|
$doc = $this->getRelatedEntity($entityWorkflow);
|
||||||
|
|
||||||
|
if (null === $doc) {
|
||||||
|
return $this->translator->trans('workflow.doc for evaluation deleted');
|
||||||
|
}
|
||||||
|
|
||||||
return $this->translator->trans(
|
return $this->translator->trans(
|
||||||
'workflow.Doc for evaluation (n°%eval%)',
|
'workflow.Doc for evaluation (n°%eval%)',
|
||||||
['%eval%' => $entityWorkflow->getRelatedEntityId()]
|
['%eval%' => $entityWorkflow->getRelatedEntityId()]
|
||||||
@ -98,7 +108,7 @@ class AccompanyingPeriodWorkEvaluationDocumentWorkflowHandler implements EntityW
|
|||||||
|
|
||||||
return [
|
return [
|
||||||
'entity_workflow' => $entityWorkflow,
|
'entity_workflow' => $entityWorkflow,
|
||||||
'evaluation' => $doc->getAccompanyingPeriodWorkEvaluation(),
|
'evaluation' => null !== $doc ? $doc->getAccompanyingPeriodWorkEvaluation() : $doc,
|
||||||
'doc' => $doc,
|
'doc' => $doc,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@ -584,6 +584,7 @@ My accompanying periods in draft: Mes parcours brouillons
|
|||||||
|
|
||||||
workflow:
|
workflow:
|
||||||
Doc for evaluation (n°%eval%): Document de l'évaluation n°%eval%
|
Doc for evaluation (n°%eval%): Document de l'évaluation n°%eval%
|
||||||
|
doc for evaluation deleted: Document supprimé dans une évaluation
|
||||||
|
|
||||||
period_by_user_list:
|
period_by_user_list:
|
||||||
Period by user: Parcours d'accompagnement par utilisateur
|
Period by user: Parcours d'accompagnement par utilisateur
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
<ul class="record_actions sticky-form-buttons">
|
<ul class="record_actions sticky-form-buttons">
|
||||||
<li class="cancel">
|
<li class="cancel">
|
||||||
<a class="btn btn-cancel" href={% if task.person is not null %}"{{ chill_return_path_or('chill_task_singletask_list', { 'person_id': task.person.id } ) }}"{% else %}"{{ chill_return_path_or('chill_task_singletask_courselist', {'course_id': task.course.id}) }}" {% endif %}>
|
<a class="btn btn-cancel" href={% if task.person is not null %}"{{ chill_return_path_or('chill_task_singletask_list', { 'person_id': task.person.id } ) }}"{% else %}"{{ chill_return_path_or('chill_task_singletask_by-course_list', {'id': task.course.id}) }}" {% endif %}>
|
||||||
{{ 'Cancel'|trans }}</a>
|
{{ 'Cancel'|trans }}</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
|
@ -762,7 +762,7 @@ class ThirdParty implements TrackCreationInterface, TrackUpdateInterface
|
|||||||
*/
|
*/
|
||||||
public function setEmail($email = null)
|
public function setEmail($email = null)
|
||||||
{
|
{
|
||||||
$this->email = $email;
|
$this->email = trim((string) $email);
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
@ -34,6 +34,12 @@ final class ThirdPartyACLAwareRepository implements ThirdPartyACLAwareRepository
|
|||||||
{
|
{
|
||||||
$qb = $this->thirdPartyRepository->createQueryBuilder('tp');
|
$qb = $this->thirdPartyRepository->createQueryBuilder('tp');
|
||||||
|
|
||||||
|
$qb->leftJoin('tp.parent', 'parent')
|
||||||
|
->andWhere($qb->expr()->andX(
|
||||||
|
'tp.active = \'TRUE\'',
|
||||||
|
$qb->expr()->orX($qb->expr()->isNull('parent'), 'parent.active = \'TRUE\'')
|
||||||
|
));
|
||||||
|
|
||||||
if (null !== $filterString) {
|
if (null !== $filterString) {
|
||||||
$qb->andWhere($qb->expr()->like('tp.canonicalized', 'LOWER(UNACCENT(:filterString))'))
|
$qb->andWhere($qb->expr()->like('tp.canonicalized', 'LOWER(UNACCENT(:filterString))'))
|
||||||
->setParameter('filterString', '%' . $filterString . '%');
|
->setParameter('filterString', '%' . $filterString . '%');
|
||||||
|
@ -85,12 +85,12 @@ class ThirdPartyApiSearch implements SearchApiInterface
|
|||||||
$pertinenceArgs = [];
|
$pertinenceArgs = [];
|
||||||
|
|
||||||
foreach ($strs as $str) {
|
foreach ($strs as $str) {
|
||||||
if (!empty($str)) {
|
if ('' !== trim($str)) {
|
||||||
$wheres[] = "(LOWER(UNACCENT(?)) <<% tparty.canonicalized OR
|
$wheres[] = "((LOWER(UNACCENT(?)) <<% tparty.canonicalized OR
|
||||||
tparty.canonicalized LIKE '%' || LOWER(UNACCENT(?)) || '%')
|
tparty.canonicalized LIKE '%' || LOWER(UNACCENT(?)) || '%')
|
||||||
OR
|
OR
|
||||||
(LOWER(UNACCENT(?)) <<% parent.canonicalized OR
|
(LOWER(UNACCENT(?)) <<% parent.canonicalized OR
|
||||||
parent.canonicalized LIKE '%' || LOWER(UNACCENT(?)) || '%')
|
parent.canonicalized LIKE '%' || LOWER(UNACCENT(?)) || '%'))
|
||||||
";
|
";
|
||||||
$whereArgs[] = [$str, $str, $str, $str];
|
$whereArgs[] = [$str, $str, $str, $str];
|
||||||
$pertinence[] = 'GREATEST(
|
$pertinence[] = 'GREATEST(
|
||||||
@ -113,7 +113,8 @@ class ThirdPartyApiSearch implements SearchApiInterface
|
|||||||
[],
|
[],
|
||||||
...$pertinenceArgs
|
...$pertinenceArgs
|
||||||
))
|
))
|
||||||
->andWhereClause(implode(' AND ', $wheres), array_merge(
|
->andWhereClause(implode(' AND ', $wheres)
|
||||||
|
. ' AND tparty.active IS TRUE and (parent.active IS TRUE OR parent IS NULL)', array_merge(
|
||||||
[],
|
[],
|
||||||
...$whereArgs
|
...$whereArgs
|
||||||
));
|
));
|
||||||
|
@ -63,7 +63,7 @@ class ThirdPartyNormalizer implements NormalizerAwareInterface, NormalizerInterf
|
|||||||
}, $thirdParty->getTypesAndCategories()),
|
}, $thirdParty->getTypesAndCategories()),
|
||||||
'profession' => $this->normalizer->normalize($thirdParty->getProfession(), $format, $context),
|
'profession' => $this->normalizer->normalize($thirdParty->getProfession(), $format, $context),
|
||||||
'address' => $this->normalizer->normalize($thirdParty->getAddress(), $format, ['address_rendering' => 'short']),
|
'address' => $this->normalizer->normalize($thirdParty->getAddress(), $format, ['address_rendering' => 'short']),
|
||||||
'telephone' => $this->normalizer->normalize($thirdParty->getTelephone()),
|
'telephone' => $this->normalizer->normalize($thirdParty->getTelephone(), $format, $context),
|
||||||
'email' => $thirdParty->getEmail(),
|
'email' => $thirdParty->getEmail(),
|
||||||
'isChild' => $thirdParty->isChild(),
|
'isChild' => $thirdParty->isChild(),
|
||||||
'parent' => $this->normalizer->normalize($thirdParty->getParent(), $format, $context),
|
'parent' => $this->normalizer->normalize($thirdParty->getParent(), $format, $context),
|
||||||
|
@ -71,13 +71,13 @@ class ThirdPartyRender extends AbstractChillEntityRender
|
|||||||
$civility = '';
|
$civility = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!empty($entity->getAcronym())) {
|
if ('' !== (string) $entity->getAcronym()) {
|
||||||
$acronym = ' (' . $entity->getAcronym() . ')';
|
$acronym = ' (' . $entity->getAcronym() . ')';
|
||||||
} else {
|
} else {
|
||||||
$acronym = '';
|
$acronym = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
$firstname = empty($entity->getFirstname()) ? '' : $entity->getFirstname();
|
$firstname = ('' === $entity->getFirstname()) ? '' : $entity->getFirstname();
|
||||||
|
|
||||||
return $civility . $firstname . ' ' . $entity->getName() . $acronym;
|
return $civility . $firstname . ' ' . $entity->getName() . $acronym;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user