Compare commits

..

34 Commits

Author SHA1 Message Date
6f7bda9f0a indexAction adjusted so that users who are only admin will still go to admin homepage. 2022-03-10 15:15:14 +01:00
c34c8035ea csfixes 2022-03-10 14:54:03 +01:00
a3c5cabc9a entry added to section menu and redirection to normal chill homepage 2022-03-10 14:53:46 +01:00
0fc3c2eb1f fix display of checkbox field in user form 2022-03-10 10:38:01 +01:00
3ebeda9840 role field added to user form. Works with select or radio button, not with checkbox... 2022-03-09 15:49:50 +01:00
1453fdcd18 default value set in migration 2022-03-09 15:48:48 +01:00
0a4abe9b8d roles property added to user, migration does not want to execute due to null value not allowed 2022-03-08 15:48:22 +01:00
d18ab5cf49 fix display of email in person renderbox if null or empty string 2022-03-08 11:00:31 +01:00
efbd9b0185 do not allow to remove doc or evaluations if workflow is associated 2022-03-07 00:30:53 +01:00
b2a61071bf create voter which blocks deletion if a workflow exists 2022-03-07 00:30:53 +01:00
f0849eeef5 Merge branch 'issue484_link_household' into 'master'
add current household link to person banner

See merge request Chill-Projet/chill-bundles!369
2022-03-06 21:58:37 +00:00
c7f2eedd4b Merge remote-tracking branch 'origin/master' into issue484_link_household 2022-03-06 22:57:12 +01:00
ab74478afc Merge branch 'issue478_remove_localisation' into 'master'
fix parcours localisation

See merge request Chill-Projet/chill-bundles!368
2022-03-06 21:56:04 +00:00
f88d0ee19e Merge remote-tracking branch 'origin/master' into issue478_remove_localisation 2022-03-06 22:54:48 +01:00
727480d6d1 Merge branch 'issue472_filter_job_user_in_set_referrer' into 'master'
assign User to undispatched acc period: filter users by job type

See merge request Chill-Projet/chill-bundles!367
2022-03-06 21:52:43 +00:00
af79ffd544 Merge remote-tracking branch 'origin/master' into issue472_filter_job_user_in_set_referrer 2022-03-06 22:40:14 +01:00
80aee185a7 Merge branch 'url_in_accompanyingPeriodWork_evaluations' into 'master'
Url in accompanying period work evaluations

See merge request Chill-Projet/chill-bundles!364
2022-03-06 21:37:27 +00:00
7afada5dad fix cs 2022-03-06 22:36:38 +01:00
739c7e9a77 Merge remote-tracking branch 'origin/master' into url_in_accompanyingPeriodWork_evaluations 2022-03-06 22:33:16 +01:00
03471a74fe fix creating alt phonenumber 2022-03-06 22:28:33 +01:00
44ed2e44e0 another way to fix phonenumber and telephone in third party / OnTheFly 2022-03-06 22:16:48 +01:00
3352999862 rebase onto master 2022-03-04 17:23:37 +01:00
5605813826 Update changelog 2022-03-04 17:22:27 +01:00
72795240b9 Add current household link in person banner 2022-03-04 17:22:06 +01:00
d0772fc306 Add email to twig personRenderBox 2022-03-04 17:08:41 +01:00
9265a15427 Update changelog 2022-03-03 21:12:00 +01:00
6d3b88aee9 Add current household link in person banner 2022-03-03 21:09:50 +01:00
827c85ac56 fix parcours localisation 2022-03-03 20:37:12 +01:00
nobohan
7b52a10943 upd CHANGELOG 2022-03-03 17:21:54 +01:00
nobohan
187c9d82b6 assign User to undsipatched acc period: filter users by job type 2022-03-03 16:37:58 +01:00
nobohan
c5eac09478 php cs fix on migration 2022-03-03 15:17:05 +01:00
nobohan
a88e052eb6 upd CHANGELOG 2022-03-03 15:12:47 +01:00
nobohan
568a1d95f4 AccompanyingCourseWorkEdit: add url to vuejs form 2022-03-03 15:10:05 +01:00
nobohan
349db2142d person: add url field to SocialWork Evaluation entity + populate with http title values 2022-03-03 14:39:00 +01:00
36 changed files with 543 additions and 220 deletions

View File

@@ -11,6 +11,8 @@ and this project adheres to
## Unreleased
<!-- write down unreleased development here -->
* [main] filter user job in undispatch acc period to assign (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/472)
* [person] Add url in accompanying period work evaluations entity and form (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/476)
* [person] Add document generation in admin and in person/{id}/document (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/464)
* [activity] do not override location if already exist (when validating new activity) (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/470)
* [parcours] Toggle emergency/intensity only by referrer (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/442)
@@ -28,7 +30,9 @@ and this project adheres to
* [confidential] Fix position of toggle button so it does not cover text nor fall outside of box (no issue)
* [parcours] Fix edit of both thirdparty and contact name (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/474)
* [template] do not list inactive templates (for doc generator)
* [thirdparty] Bugfix phonenumber -> telephone for thirdparty (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/487)
* [parcours] location cannot be removed if linked to a user (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/478)
* [person] email added to twig personRenderbox (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/490)
* [person] Add link to current household in person banner (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/484)
## Test releases

View File

@@ -12,6 +12,7 @@ declare(strict_types=1);
namespace Chill\DocStoreBundle\Workflow;
use Chill\DocStoreBundle\Entity\AccompanyingCourseDocument;
use Chill\DocStoreBundle\Security\Authorization\AccompanyingCourseDocumentVoter;
use Chill\MainBundle\Entity\Workflow\EntityWorkflow;
use Chill\MainBundle\Workflow\EntityWorkflowHandlerInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation;
@@ -36,6 +37,13 @@ class AccompanyingCourseDocumentWorkflowHandler implements EntityWorkflowHandler
$this->translator = $translator;
}
public function getDeletionRoles(): array
{
return [
AccompanyingCourseDocumentVoter::DELETE,
];
}
public function getEntityData(EntityWorkflow $entityWorkflow, array $options = []): array
{
$course = $this->getRelatedEntity($entityWorkflow)
@@ -66,6 +74,18 @@ class AccompanyingCourseDocumentWorkflowHandler implements EntityWorkflowHandler
return $this->repository->find($entityWorkflow->getRelatedEntityId());
}
/**
* @param AccompanyingCourseDocument $object
*
* @return array[]
*/
public function getRelatedObjects(object $object): array
{
return [
['entityClass' => AccompanyingCourseDocument::class, 'entityId' => $object->getId()],
];
}
public function getRoleShow(EntityWorkflow $entityWorkflow): ?string
{
return null;
@@ -84,6 +104,11 @@ class AccompanyingCourseDocumentWorkflowHandler implements EntityWorkflowHandler
];
}
public function isObjectSupported(object $object): bool
{
return $object instanceof AccompanyingCourseDocument;
}
public function supports(EntityWorkflow $entityWorkflow, array $options = []): bool
{
return $entityWorkflow->getRelatedEntityClass() === AccompanyingCourseDocument::class;

View File

@@ -20,7 +20,7 @@ class DefaultController extends AbstractController
{
public function indexAction()
{
if ($this->isGranted('ROLE_ADMIN')) {
if ($this->isGranted('ROLE_ADMIN') && !in_array('ROLE_USER',$this->getUser()->getRoles())) {
return $this->redirectToRoute('chill_main_admin_central', [], 302);
}

View File

@@ -256,6 +256,13 @@ class ChillMainExtension extends Extension implements
'channels' => ['chill'],
]);
$container->prependExtensionConfig('security', [
'access_decision_manager' => [
'strategy' => 'unanimous',
'allow_if_all_abstain' => false,
],
]);
//add crud api
$this->prependCruds($container);
}

View File

@@ -112,6 +112,11 @@ class User implements AdvancedUserInterface
*/
private string $password = '';
/**
* @ORM\Column(type="json")
*/
private array $roles = ['ROLE_USER'];
/**
* @internal must be set to null if we use bcrypt
*
@@ -146,36 +151,24 @@ class User implements AdvancedUserInterface
$this->groupCenters = new ArrayCollection();
}
/**
* @return string
*/
public function __toString()
public function __toString(): string
{
return $this->getLabel();
}
/**
* @param \Chill\MainBundle\Entity\GroupCenter $groupCenter
*
* @return \Chill\MainBundle\Entity\User
*/
public function addGroupCenter(GroupCenter $groupCenter)
public function addGroupCenter(GroupCenter $groupCenter): self
{
$this->groupCenters->add($groupCenter);
return $this;
}
// empty function... remove?
public function eraseCredentials()
{
}
/**
* Get attributes.
*
* @return array
*/
public function getAttributes()
public function getAttributes(): ?array
{
if (null === $this->attributes) {
$this->attributes = [];
@@ -189,18 +182,12 @@ class User implements AdvancedUserInterface
return $this->currentLocation;
}
/**
* @return string
*/
public function getEmail(): ?string
{
return $this->email;
}
/**
* @return string
*/
public function getEmailCanonical()
public function getEmailCanonical(): ?string
{
return $this->emailCanonical;
}
@@ -213,12 +200,7 @@ class User implements AdvancedUserInterface
return $this->groupCenters;
}
/**
* Get id.
*
* @return int
*/
public function getId()
public function getId(): ?int
{
return $this->id;
}
@@ -243,23 +225,17 @@ class User implements AdvancedUserInterface
return $this->mainScope;
}
/**
* @return string
*/
public function getPassword()
public function getPassword(): string
{
return $this->password;
}
public function getRoles(): array
{
return ['ROLE_USER'];
return array_unique($this->roles);
}
/**
* @return string|null
*/
public function getSalt()
public function getSalt(): ?string
{
return $this->salt;
}
@@ -269,50 +245,32 @@ class User implements AdvancedUserInterface
return $this->userJob;
}
/**
* @return string
*/
public function getUsername()
public function getUsername(): string
{
return $this->username;
}
/**
* @return string
*/
public function getUsernameCanonical()
public function getUsernameCanonical(): ?string
{
return $this->usernameCanonical;
}
/**
* @return bool
*/
public function isAccountNonExpired()
public function isAccountNonExpired(): bool
{
return true;
}
/**
* @return bool
*/
public function isAccountNonLocked()
public function isAccountNonLocked(): bool
{
return $this->locked;
}
/**
* @return bool
*/
public function isCredentialsNonExpired()
public function isCredentialsNonExpired(): bool
{
return true;
}
/**
* @return bool
*/
public function isEnabled()
public function isEnabled(): bool
{
return $this->enabled;
}
@@ -363,97 +321,84 @@ class User implements AdvancedUserInterface
return $this;
}
public function setCurrentLocation(?Location $currentLocation): User
public function setCurrentLocation(?Location $currentLocation): self
{
$this->currentLocation = $currentLocation;
return $this;
}
/**
* @param $email
*
* @return $this
*/
public function setEmail($email)
public function setEmail($email): self
{
$this->email = $email;
return $this;
}
/**
* @param $emailCanonical
*
* @return $this
*/
public function setEmailCanonical($emailCanonical)
public function setEmailCanonical($emailCanonical): self
{
$this->emailCanonical = $emailCanonical;
return $this;
}
public function setEnabled(bool $enabled)
public function setEnabled(bool $enabled): self
{
$this->enabled = $enabled;
return $this;
}
public function setLabel(string $label): User
public function setLabel(string $label): self
{
$this->label = $label;
return $this;
}
public function setMainCenter(?Center $mainCenter): User
public function setMainCenter(?Center $mainCenter): self
{
$this->mainCenter = $mainCenter;
return $this;
}
public function setMainLocation(?Location $mainLocation): User
public function setMainLocation(?Location $mainLocation): self
{
$this->mainLocation = $mainLocation;
return $this;
}
public function setMainScope(?Scope $mainScope): User
public function setMainScope(?Scope $mainScope): self
{
$this->mainScope = $mainScope;
return $this;
}
/**
* @param $password
*
* @return $this
*/
public function setPassword($password)
public function setPassword($password): self
{
$this->password = $password;
return $this;
}
/**
* @param $salt
*
* @return $this
*/
public function setSalt($salt)
public function setRoles($roles): self
{
$this->roles = $roles;
return $this;
}
public function setSalt($salt): self
{
$this->salt = $salt;
return $this;
}
public function setUserJob(?UserJob $userJob): User
public function setUserJob(?UserJob $userJob): self
{
$this->userJob = $userJob;
@@ -478,12 +423,7 @@ class User implements AdvancedUserInterface
return $this;
}
/**
* @param $usernameCanonical
*
* @return $this
*/
public function setUsernameCanonical($usernameCanonical)
public function setUsernameCanonical($usernameCanonical): self
{
$this->usernameCanonical = $usernameCanonical;

View File

@@ -19,6 +19,7 @@ use Chill\MainBundle\Templating\TranslatableStringHelper;
use Doctrine\ORM\EntityRepository;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\CallbackTransformer;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
@@ -59,6 +60,16 @@ class UserType extends AbstractType
return $qb;
},
])
->add('roles', ChoiceType::class, [
'required' => true,
'multiple' => true,
'expanded' => true,
'label' => 'Roles',
'choices' => [
'Usager' => 'ROLE_USER',
'Administrateur' => 'ROLE_ADMIN',
],
])
->add('mainScope', EntityType::class, [
'label' => 'Main scope',
'required' => false,
@@ -94,6 +105,18 @@ class UserType extends AbstractType
},
]);
// $builder->get('roles')
// ->addModelTransformer(new CallbackTransformer(
// function ($rolesArray) {
// // transform the array to a string
// return count($rolesArray)? $rolesArray[0]: null;
// },
// function ($rolesString) {
// // transform the string back to an array
// return [$rolesString];
// }
// ));
if ($options['is_creation']) {
$builder->add('plainPassword', RepeatedType::class, [
'mapped' => false,

View File

@@ -27,7 +27,7 @@ interface PhoneNumberHelperInterface
/**
* Get type (mobile, landline, ...) for phone number.
*/
public function getType(string $phonenumber): string;
public function getType(PhoneNumber $phonenumber): string;
/**
* Return true if the validation is configured and available.

View File

@@ -17,6 +17,7 @@ use GuzzleHttp\Exception\ConnectException;
use GuzzleHttp\Exception\ServerException;
use libphonenumber\NumberParseException;
use libphonenumber\PhoneNumber;
use libphonenumber\PhoneNumberType;
use libphonenumber\PhoneNumberUtil;
use Psr\Cache\CacheItemPoolInterface;
use Psr\Log\LoggerInterface;
@@ -86,9 +87,19 @@ final class PhonenumberHelper implements PhoneNumberHelperInterface
/**
* Get type (mobile, landline, ...) for phone number.
*/
public function getType(string $phonenumber): string
public function getType(PhoneNumber $phonenumber): string
{
return $this->performTwilioLookup($phonenumber) ?? 'unknown';
switch ($this->phoneNumberUtil->getNumberType($phonenumber)) {
case PhoneNumberType::MOBILE:
return 'mobile';
case PhoneNumberType::FIXED_LINE:
case PhoneNumberType::VOIP:
return 'landline';
default:
return 'landline';
}
}
/**

View File

@@ -55,6 +55,30 @@ class EntityWorkflowRepository implements ObjectRepository
return (int) $qb->getQuery()->getSingleScalarResult();
}
public function countRelatedWorkflows(array $relateds): int
{
$qb = $this->repository->createQueryBuilder('w');
$orX = $qb->expr()->orX();
$i = 0;
foreach ($relateds as $related) {
$orX->add(
$qb->expr()->andX(
$qb->expr()->eq('w.relatedEntityClass', ':entity_class_' . $i),
$qb->expr()->eq('w.relatedEntityId', ':entity_id_' . $i)
)
);
$qb
->setParameter('entity_class_' . $i, $related['entityClass'])
->setParameter('entity_id_' . $i, $related['entityId']);
++$i;
}
$qb->where($orX);
return $qb->select('COUNT(w)')->getQuery()->getSingleScalarResult();
}
public function find($id): ?EntityWorkflow
{
return $this->repository->find($id);

View File

@@ -93,6 +93,7 @@ import Modal from 'ChillMainAssets/vuejs/_components/Modal.vue';
import OnTheFlyCreate from './Create.vue';
import OnTheFlyPerson from 'ChillPersonAssets/vuejs/_components/OnTheFly/Person.vue';
import OnTheFlyThirdparty from 'ChillThirdPartyAssets/vuejs/_components/OnTheFly/ThirdParty.vue';
import { postThirdparty } from "ChillThirdPartyAssets/vuejs/_api/OnTheFly";
export default {
name: 'OnTheFly',
@@ -217,24 +218,21 @@ export default {
case 'thirdparty':
data = this.$refs.castThirdparty.$data.thirdparty;
/* never executed ? Yes, when editing thirdparty... */
/* never executed ? */
break;
default:
if (typeof this.type === 'undefined') { // action=create or addContact
console.log('will rewrite data');
console.log('will rewrite data');
if (this.action === 'addContact') {
type = 'thirdparty'
data = this.$refs.castThirdparty.$data.thirdparty;
console.log('data original', data);
data.parent = {type: "thirdparty", id: this.parent.id};
// data.civility = {type: 'chill_main_civility', id: civility.id };
// data.profession = {type: 'third_party_profession', id: profession.id };
} else {
console.log('im here')
type = this.$refs.castNew.radioType;
data = this.$refs.castNew.castDataByType();
console.log('saveAction', data)
console.log(data)
}
} else {
throw 'error with object type';

View File

@@ -22,19 +22,10 @@ use Symfony\Contracts\Translation\TranslatorInterface;
*/
class SectionMenuBuilder implements LocalMenuBuilderInterface
{
/**
* @var AuthorizationCheckerInterface
*/
protected $authorizationChecker;
protected AuthorizationCheckerInterface $authorizationChecker;
/**
* @var TranslatorInterface
*/
protected $translator;
protected TranslatorInterface $translator;
/**
* SectionMenuBuilder constructor.
*/
public function __construct(AuthorizationCheckerInterface $authorizationChecker, TranslatorInterface $translator)
{
$this->authorizationChecker = $authorizationChecker;
@@ -54,6 +45,16 @@ class SectionMenuBuilder implements LocalMenuBuilderInterface
'order' => 0,
]);
if ($this->authorizationChecker->isGranted('ROLE_ADMIN')) {
$menu->addChild($this->translator->trans('Administration'), [
'route' => 'chill_main_admin_central',
])
->setExtras([
'icons' => ['home'],
'order' => 5,
]);
}
$menu->addChild($this->translator->trans('Global timeline'), [
'route' => 'chill_center_timeline',
])

View File

@@ -0,0 +1,65 @@
<?php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Chill\MainBundle\Security\Authorization;
use Chill\MainBundle\Repository\Workflow\EntityWorkflowRepository;
use Chill\MainBundle\Workflow\EntityWorkflowHandlerInterface;
use RuntimeException;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
use function in_array;
use function is_object;
class WorkflowEntityDeletionVoter extends Voter
{
private EntityWorkflowRepository $entityWorkflowRepository;
/**
* @var iterable|EntityWorkflowHandlerInterface[]
*/
private iterable $handlers;
public function __construct($handlers, EntityWorkflowRepository $entityWorkflowRepository)
{
$this->handlers = $handlers;
$this->entityWorkflowRepository = $entityWorkflowRepository;
}
protected function supports($attribute, $subject)
{
if (!is_object($subject)) {
return false;
}
foreach ($this->handlers as $handler) {
if ($handler->isObjectSupported($subject)
&& in_array($attribute, $handler->getDeletionRoles($subject), true)) {
return true;
}
}
return false;
}
protected function voteOnAttribute($attribute, $subject, TokenInterface $token)
{
foreach ($this->handlers as $handler) {
if ($handler->isObjectSupported($subject)) {
return 0 === $this->entityWorkflowRepository->countRelatedWorkflows(
$handler->getRelatedObjects($subject)
);
}
}
throw new RuntimeException('no handlers found');
}
}

View File

@@ -40,7 +40,7 @@ class PhonenumberNormalizer implements NormalizerInterface, DenormalizerInterfac
*/
public function denormalize($data, $type, $format = null, array $context = [])
{
if ("" == trim($data)) {
if ('' === trim($data)) {
return null;
}

View File

@@ -15,12 +15,19 @@ use Chill\MainBundle\Entity\Workflow\EntityWorkflow;
interface EntityWorkflowHandlerInterface
{
/**
* @return array|string[]
*/
public function getDeletionRoles(): array;
public function getEntityData(EntityWorkflow $entityWorkflow, array $options = []): array;
public function getEntityTitle(EntityWorkflow $entityWorkflow, array $options = []): string;
public function getRelatedEntity(EntityWorkflow $entityWorkflow): ?object;
public function getRelatedObjects(object $object): array;
/**
* Return a string representing the role required for seeing the workflow.
*
@@ -33,6 +40,8 @@ interface EntityWorkflowHandlerInterface
public function getTemplateData(EntityWorkflow $entityWorkflow, array $options = []): array;
public function isObjectSupported(object $object): bool;
public function supports(EntityWorkflow $entityWorkflow, array $options = []): bool;
public function supportsFreeze(EntityWorkflow $entityWorkflow, array $options = []): bool;

View File

@@ -75,3 +75,9 @@ services:
$locker: '@Chill\MainBundle\Security\PasswordRecover\PasswordRecoverLocker'
tags:
- { name: security.voter }
Chill\MainBundle\Security\Authorization\WorkflowEntityDeletionVoter:
autoconfigure: true
autowire: true
arguments:
$handlers: !tagged_iterator chill_main.workflow_handler

View File

@@ -0,0 +1,36 @@
<?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;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20220308104030 extends AbstractMigration
{
public function down(Schema $schema): void
{
$this->addSql('ALTER TABLE users DROP roles');
}
public function getDescription(): string
{
return 'Add roles property to user';
}
public function up(Schema $schema): void
{
$this->addSql('ALTER TABLE users ADD roles JSONB DEFAULT \'["ROLE_USER"]\' NOT NULL');
}
}

View File

@@ -98,7 +98,8 @@ class PersonPhone
public function isEmpty(): bool
{
return empty($this->getDescription()) && empty($this->getPhonenumber());
return ('' === $this->getDescription() || null === $this->getDescription())
&& null === $this->getPhonenumber();
}
public function setDate(DateTime $date): void

View File

@@ -62,6 +62,12 @@ class Evaluation
*/
private array $title = [];
/**
* @ORM\Column(type="text", nullable=true)
* @Serializer\Groups({"read", "docgen:read"})
*/
private ?string $url = null;
public function __construct()
{
$this->socialActions = new ArrayCollection();
@@ -101,6 +107,11 @@ class Evaluation
return $this->title;
}
public function getUrl(): ?string
{
return $this->url;
}
public function removeSocialAction(SocialAction $socialAction): self
{
if ($this->socialActions->contains($socialAction)) {
@@ -130,4 +141,11 @@ class Evaluation
return $this;
}
public function setUrl(?string $url): self
{
$this->url = $url;
return $this;
}
}

View File

@@ -27,6 +27,7 @@ use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Entity\PersonPhone;
use Chill\PersonBundle\Form\Type\GenderType;
use Chill\PersonBundle\Form\Type\PersonAltNameType;
use Chill\PersonBundle\Form\Type\PersonPhoneType;
use Chill\PersonBundle\Form\Type\Select2MaritalStatusType;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
use Symfony\Component\Form\AbstractType;
@@ -158,7 +159,7 @@ class PersonType extends AbstractType
}
$builder->add('otherPhoneNumbers', ChillCollectionType::class, [
'entry_type' => ChillPhoneNumberType::class,
'entry_type' => PersonPhoneType::class,
'button_add_label' => 'Add new phone',
'button_remove_label' => 'Remove phone',
'required' => false,

View File

@@ -11,11 +11,11 @@ declare(strict_types=1);
namespace Chill\PersonBundle\Form\Type;
use Chill\MainBundle\Form\Type\ChillPhoneNumberType;
use Chill\MainBundle\Phonenumber\PhonenumberHelper;
use Chill\PersonBundle\Entity\PersonPhone;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\TelType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormEvent;
@@ -36,7 +36,7 @@ class PersonPhoneType extends AbstractType
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('phonenumber', TelType::class, [
$builder->add('phonenumber', ChillPhoneNumberType::class, [
'label' => 'Other phonenumber',
'required' => true,
]);

View File

@@ -37,57 +37,68 @@ div.banner {
margin-right: 1em;
}
}
.household-link {
border: 1px solid white;
padding: .05rem .3rem;
border-radius: 5px;
color: white;
cursor: pointer;
&:hover {
background-color: white;
color: $chill-person-context
}
}
}
}
div.person-view {
figure.person-details {
h2 {
font-family: 'Open Sans';
font-weight: 600;
margin-bottom: 0.3em;
font-variant: small-caps;
}
dl {
margin-top: 0.3em;
}
dt {
font-family: 'Open Sans';
font-weight: 600;
}
dd {
margin-left: 0;
}
/*
a.sc-button { background-color: $black; padding-top: 0.2em; padding-bottom: 0.2em; }
*/
}
/* custom fields on the home page */
div.custom-fields {
figure.person-details {
display: flex;
flex-flow: row wrap;
div.cf_title_box:nth-child(4n+1) h2 {
@extend .chill-red !optional;
}
div.cf_title_box:nth-child(4n+2) h2 {
@extend .chill-green !optional;
}
div.cf_title_box:nth-child(4n+3) h2 {
@extend .chill-orange !optional;
}
div.cf_title_box:nth-child(4n+4) h2 {
@extend .chill-blue !optional;
}
div.cf_title_box:nth-child(2n+1) {
width: 50%;
margin-right: 40px;
}
div.cf_title_box:nth-child(2n+2) {
width: calc(50% - 40px);
}
}
}
figure.person-details {
h2 {
font-family: 'Open Sans';
font-weight: 600;
margin-bottom: 0.3em;
font-variant: small-caps;
}
dl {
margin-top: 0.3em;
}
dt {
font-family: 'Open Sans';
font-weight: 600;
}
dd {
margin-left: 0;
}
/*
a.sc-button { background-color: $black; padding-top: 0.2em; padding-bottom: 0.2em; }
*/
}
/* custom fields on the home page */
div.custom-fields {
figure.person-details {
display: flex;
flex-flow: row wrap;
div.cf_title_box:nth-child(4n+1) h2 {
@extend .chill-red !optional;
}
div.cf_title_box:nth-child(4n+2) h2 {
@extend .chill-green !optional;
}
div.cf_title_box:nth-child(4n+3) h2 {
@extend .chill-orange !optional;
}
div.cf_title_box:nth-child(4n+4) h2 {
@extend .chill-blue !optional;
}
div.cf_title_box:nth-child(2n+1) {
width: 50%;
margin-right: 40px;
}
div.cf_title_box:nth-child(2n+2) {
width: calc(50% - 40px);
}
}
}
}
/*

View File

@@ -19,22 +19,21 @@ import {fetchResults} from 'ChillMainAssets/lib/api/apiMethods.js';
*/
document.querySelectorAll('[data-set-referrer-app]').forEach(function (el) {
let
periodId = Number.parseInt(el.dataset.setReferrerAccompanyingPeriodId);
const periodId = Number.parseInt(el.dataset.setReferrerAccompanyingPeriodId);
const jobId = Number.parseInt(el.dataset.setReferrerJobId);
const url = `/api/1.0/person/accompanying-course/${periodId}/referrers-suggested.json`;
fetchResults(url).then(suggested => {
const filteredSuggested = suggested.filter((s) => s.user_job ? s.user_job.id === jobId : false);
const app = createApp({
components: {
SetReferrer,
},
template:
'<set-referrer :suggested="suggested" :periodId="periodId" @referrerSet="onReferrerSet"></set-referrer>',
'<set-referrer :suggested="filteredSuggested" :periodId="periodId" @referrerSet="onReferrerSet"></set-referrer>',
data() {
return {
periodId, suggested, original: suggested,
periodId, filteredSuggested, original: filteredSuggested,
}
},
methods: {
@@ -56,7 +55,7 @@ document.querySelectorAll('[data-set-referrer-app]').forEach(function (el) {
label.textContent = ref.text;
label.classList.remove('chill-no-data-statement');
this.suggested = this.original.filter(user => user.id !== ref.id);
this.filteredSuggested = this.original.filter(user => user.id !== ref.id);
}
}
});

View File

@@ -59,14 +59,6 @@
ref="addAddress">
</add-address>
</li>
<li v-if="isPersonLocation">
<button
class="btn btn-remove"
@click="removeAddress"
:title="$t('courselocation.remove_button')">
{{ $t('action.remove') }}
</button>
</li>
</ul>
</div>
@@ -180,22 +172,6 @@ export default {
}
this.$store.commit('setAddressContext', context);
},
removeAddress() {
let payload = {
target: this.context.target.name,
targetId: this.context.target.id,
locationStatusTo: 'none'
};
//console.log('remove address');
this.$store.dispatch('updateLocation', payload)
.catch(({name, violations}) => {
if (name === 'ValidationException' || name === 'AccessException') {
violations.forEach((violation) => this.$toast.open({message: violation}));
} else {
this.$toast.open({message: 'An error occurred'})
}
});
},
displayErrors() {
return this.$refs.addAddress.errorMsg;
},

View File

@@ -171,6 +171,8 @@ export default {
body.name = payload.data.name;
body.email = payload.data.email;
body.telephone = payload.data.telephone;
body.civility = payload.data.civility;
body.profession = payload.data.profession;
body.address = payload.data.address ? { id: payload.data.address.address_id } : null;
makeFetch('PATCH', `/api/1.0/thirdparty/thirdparty/${payload.data.id}.json`, body)

View File

@@ -5,6 +5,11 @@
<span>{{ evaluation.evaluation.title.fr }}</span>
</div>
<div class="item-url mt-3 mb-4" v-if="evaluation.evaluation.url">
<i class="fa fa-link fa-lg"></i>
<a :href="evaluation.evaluation.url" target="_blank">{{ evaluation.evaluation.url }}</a>
</div>
<div>
<form-evaluation ref="FormEvaluation" :key="evaluation.key" :evaluation="evaluation"></form-evaluation>
@@ -21,7 +26,7 @@
></list-workflow-modal>
</li>
<li>
<li v-if="canDelete">
<a class="btn btn-delete" @click="modal.showModal = true" :title="$t('action.delete')"></a>
</li>
</ul>
@@ -93,6 +98,19 @@ export default {
pickedEvaluations() {
return this.$store.state.evaluationsPicked;
},
canDelete() {
if (this.evaluation.workflows.length > 0) {
return false;
}
for (let doc of this.evaluation.documents) {
if (doc.workflows.length > 0) {
return false;
}
}
return true;
},
},
methods: {
removeEvaluation(e) {
@@ -128,4 +146,11 @@ export default {
}
}
}
div.item-url {
i {
color: unset!important;
margin-left: 1rem;
margin-right: 0.5rem;
}
}
</style>

View File

@@ -101,7 +101,7 @@
<li>
<a :href="buildEditLink(d.storedObject)" class="btn btn-wopilink"></a>
</li>
<li>
<li v-if="d.workflows.length === 0">
<a class="btn btn-delete" @click="removeDocument(d)">
</a>
</li>

View File

@@ -1,12 +1,12 @@
<template>
<ul class="list-suggest add-items" v-if="suggested.length > 0">
<li v-for="r in suggested" @click="setReferrer(r)"><span>{{ r.text }}</span></li>
<li v-for="(r, i) in suggested" @click="setReferrer(r)" :key="i"><span>{{ r.text }}</span></li>
</ul>
</template>
<script>
import {makeFetch} from 'ChillMainAssets/lib/api/apiMethods.js';
import { makeFetch } from 'ChillMainAssets/lib/api/apiMethods.js';
export default {
name: "SetReferrer",

View File

@@ -130,11 +130,13 @@
href="{{ chill_path_add_return_path('chill_person_accompanying_period_work_edit', { 'id': w.id }) }}"
></a>
</li>
<li>
<a class="btn btn-delete" title="{{ 'Delete'|trans }}"
href="{{ path('chill_person_accompanying_period_work_delete', { 'id': w.id } ) }}"
></a>
</li>
{% if is_granted('CHILL_MAIN_ACCOMPANYING_PERIOD_WORK_DELETE', w) %}
<li>
<a class="btn btn-delete" title="{{ 'Delete'|trans }}"
href="{{ path('chill_person_accompanying_period_work_delete', { 'id': w.id } ) }}"
></a>
</li>
{% endif %}
</ul>
{% endif %}
</div>

View File

@@ -167,6 +167,18 @@
<span class="chill-no-data-statement">{{ 'No data given'|trans }}</span>
</li>
{% endif %}
{% if person.email is not empty %}
<li>
<i class="fa fa-li fa-envelope-o"></i><a href="{{ 'mailto:' ~ person.email }}">
{{ person.email }}
</a>
</li>
{% else %}
<li>
<i class="fa fa-li fa-envelope-o"></i>
<span class="chill-no-data-statement">{{ 'No data given'|trans }}</span>
</li>
{% endif %}
{% if options['addCenter'] and person|chill_resolve_center|length > 0 %}
<li>
<i class="fa fa-li fa-long-arrow-right"></i>

View File

@@ -58,6 +58,13 @@
}) }}
</span>
{%- endif -%}
{% if person.getCurrentHousehold is not null %}
<span>
<a class="household-link" href="{{ chill_path_add_return_path('chill_person_household_summary', { 'household_id' : person.getCurrentHousehold.id } ) }}">
<i class="fa fa-home"></i>
</a>
</span>
{% endif %}
</div>
</div>

View File

@@ -24,6 +24,8 @@ class AccompanyingPeriodWorkVoter extends Voter
{
public const CREATE = 'CHILL_MAIN_ACCOMPANYING_PERIOD_WORK_CREATE';
public const DELETE = 'CHILL_MAIN_ACCOMPANYING_PERIOD_WORK_DELETE';
public const SEE = 'CHILL_MAIN_ACCOMPANYING_PERIOD_WORK_SEE';
public const UPDATE = 'CHILL_MAIN_ACCOMPANYING_PERIOD_WORK_UPDATE';
@@ -60,6 +62,7 @@ class AccompanyingPeriodWorkVoter extends Voter
case self::CREATE:
case self::UPDATE:
case self::DELETE:
return $this->security->isGranted(AccompanyingPeriodVoter::EDIT, $subject->getAccompanyingPeriod());
default:
@@ -86,6 +89,6 @@ class AccompanyingPeriodWorkVoter extends Voter
private function getRoles(): array
{
return [self::SEE, self::CREATE, self::UPDATE];
return [self::SEE, self::CREATE, self::UPDATE, self::DELETE];
}
}

View File

@@ -37,6 +37,13 @@ class AccompanyingPeriodWorkEvaluationDocumentWorkflowHandler implements EntityW
$this->translator = $translator;
}
public function getDeletionRoles(): array
{
return [
'_',
];
}
public function getEntityData(EntityWorkflow $entityWorkflow, array $options = []): array
{
$doc = $this->getRelatedEntity($entityWorkflow);
@@ -63,6 +70,18 @@ class AccompanyingPeriodWorkEvaluationDocumentWorkflowHandler implements EntityW
return $this->repository->find($entityWorkflow->getRelatedEntityId());
}
/**
* @param AccompanyingPeriodWorkEvaluationDocument $object
*
* @return array[]
*/
public function getRelatedObjects(object $object): array
{
return [
['entityClass' => AccompanyingPeriodWorkEvaluationDocument::class, $object->getId()],
];
}
public function getRoleShow(EntityWorkflow $entityWorkflow): ?string
{
return AccompanyingPeriodWorkEvaluationDocumentVoter::SEE;
@@ -84,6 +103,11 @@ class AccompanyingPeriodWorkEvaluationDocumentWorkflowHandler implements EntityW
];
}
public function isObjectSupported(object $object): bool
{
return $object instanceof AccompanyingPeriodWorkEvaluationDocument;
}
public function supports(EntityWorkflow $entityWorkflow, array $options = []): bool
{
return $entityWorkflow->getRelatedEntityClass() === AccompanyingPeriodWorkEvaluationDocument::class;

View File

@@ -15,6 +15,7 @@ use Chill\MainBundle\Entity\Workflow\EntityWorkflow;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Chill\MainBundle\Workflow\EntityWorkflowHandlerInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluation;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluationDocument;
use Chill\PersonBundle\Repository\AccompanyingPeriod\AccompanyingPeriodWorkEvaluationRepository;
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodWorkEvaluationVoter;
use Symfony\Contracts\Translation\TranslatorInterface;
@@ -37,6 +38,11 @@ class AccompanyingPeriodWorkEvaluationWorkflowHandler implements EntityWorkflowH
$this->translator = $translator;
}
public function getDeletionRoles(): array
{
return ['_'];
}
public function getEntityData(EntityWorkflow $entityWorkflow, array $options = []): array
{
$evaluation = $this->getRelatedEntity($entityWorkflow);
@@ -61,6 +67,20 @@ class AccompanyingPeriodWorkEvaluationWorkflowHandler implements EntityWorkflowH
return $this->repository->find($entityWorkflow->getRelatedEntityId());
}
/**
* @param AccompanyingPeriodWorkEvaluation $object
*/
public function getRelatedObjects(object $object): array
{
$relateds[] = ['entityClass' => AccompanyingPeriodWorkEvaluation::class, 'entityId' => $object->getId()];
foreach ($object->getDocuments() as $doc) {
$relateds[] = ['entityClass' => AccompanyingPeriodWorkEvaluationDocument::class, 'entityId' => $doc->getId()];
}
return $relateds;
}
public function getRoleShow(EntityWorkflow $entityWorkflow): ?string
{
return AccompanyingPeriodWorkEvaluationVoter::SEE;
@@ -79,6 +99,11 @@ class AccompanyingPeriodWorkEvaluationWorkflowHandler implements EntityWorkflowH
];
}
public function isObjectSupported(object $object): bool
{
return $object instanceof AccompanyingPeriodWorkEvaluation;
}
public function supports(EntityWorkflow $entityWorkflow, array $options = []): bool
{
return $entityWorkflow->getRelatedEntityClass() === AccompanyingPeriodWorkEvaluation::class;

View File

@@ -15,7 +15,10 @@ use Chill\MainBundle\Entity\Workflow\EntityWorkflow;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Chill\MainBundle\Workflow\EntityWorkflowHandlerInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluation;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluationDocument;
use Chill\PersonBundle\Repository\AccompanyingPeriod\AccompanyingPeriodWorkRepository;
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodWorkVoter;
use Symfony\Contracts\Translation\TranslatorInterface;
class AccompanyingPeriodWorkWorkflowHandler implements EntityWorkflowHandlerInterface
@@ -36,6 +39,11 @@ class AccompanyingPeriodWorkWorkflowHandler implements EntityWorkflowHandlerInte
$this->translator = $translator;
}
public function getDeletionRoles(): array
{
return [AccompanyingPeriodWorkVoter::DELETE];
}
public function getEntityData(EntityWorkflow $entityWorkflow, array $options = []): array
{
return [
@@ -58,6 +66,24 @@ class AccompanyingPeriodWorkWorkflowHandler implements EntityWorkflowHandlerInte
return $this->repository->find($entityWorkflow->getRelatedEntityId());
}
/**
* @param AccompanyingPeriodWork $object
*/
public function getRelatedObjects(object $object): array
{
$relateds[] = ['entityClass' => AccompanyingPeriodWork::class, 'entityId' => $object->getId()];
foreach ($object->getAccompanyingPeriodWorkEvaluations() as $evaluation) {
$relateds[] = ['entityClass' => AccompanyingPeriodWorkEvaluation::class, 'entityId' => $evaluation->getId()];
foreach ($evaluation->getDocuments() as $doc) {
$relateds[] = ['entityClass' => AccompanyingPeriodWorkEvaluationDocument::class, 'entityId' => $doc->getId()];
}
}
return $relateds;
}
public function getRoleShow(EntityWorkflow $entityWorkflow): ?string
{
return null;
@@ -76,6 +102,11 @@ class AccompanyingPeriodWorkWorkflowHandler implements EntityWorkflowHandlerInte
];
}
public function isObjectSupported(object $object): bool
{
return $object instanceof AccompanyingPeriodWork;
}
public function supports(EntityWorkflow $entityWorkflow, array $options = []): bool
{
return $entityWorkflow->getRelatedEntityClass() === AccompanyingPeriodWork::class;

View File

@@ -0,0 +1,37 @@
<?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\Person;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Add url to SocialWork Evaluation.
*/
final class Version20220303113855 extends AbstractMigration
{
public function down(Schema $schema): void
{
$this->addSql('ALTER TABLE chill_person_social_work_evaluation DROP url');
}
public function getDescription(): string
{
return 'Add url to SocialWork Evaluation';
}
public function up(Schema $schema): void
{
$this->addSql('ALTER TABLE chill_person_social_work_evaluation ADD url TEXT DEFAULT NULL');
$this->addSql("UPDATE chill_person_social_work_evaluation SET url = CASE WHEN title->>'fr' LIKE 'http%' THEN title->>'fr' ELSE null END;");
}
}

View File

@@ -116,12 +116,12 @@
</div>
<div class="input-group mb-3">
<span class="input-group-text" id="telephone"><i class="fa fa-fw fa-phone"></i></span>
<span class="input-group-text" id="phonenumber"><i class="fa fa-fw fa-phone"></i></span>
<input class="form-control form-control-lg"
v-model="thirdparty.telephone"
v-bind:placeholder="$t('thirdparty.phonenumber')"
v-bind:aria-label="$t('thirdparty.phonenumber')"
aria-describedby="telephone" />
aria-describedby="phonenumber" />
</div>
<div v-if="parent">
@@ -266,10 +266,10 @@ export default {
this.thirdparty.name = query;
},
},
mounted() {
let dependencies = [];
dependencies.push(this.loadProfessions());
dependencies.push(this.loadCivilities());
mounted() {
let dependencies = [];
dependencies.push(this.loadProfessions());
dependencies.push(this.loadCivilities());
if (this.action !== 'create') {
if (this.id) {
dependencies.push(this.loadData());