Compare commits

..

10 Commits

37 changed files with 451 additions and 191 deletions

View File

@@ -257,10 +257,12 @@ class Activity implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterfac
/** /**
* Add a social issue. * Add a social issue.
* *
* Note: the social issue consistency (the fact that only youngest social issues * Note: the social issue consistency (the fact that only yougest social issues
* are kept) is processed by an entity listener: * are kept) is processed by an entity listener:
* *
* @see{\Chill\PersonBundle\AccompanyingPeriod\SocialIssueConsistency\AccompanyingPeriodSocialIssueConsistencyEntityListener} * @see{\Chill\PersonBundle\AccompanyingPeriod\SocialIssueConsistency\AccompanyingPeriodSocialIssueConsistencyEntityListener}
*
* @return $this
*/ */
public function addSocialIssue(SocialIssue $socialIssue): self public function addSocialIssue(SocialIssue $socialIssue): self
{ {
@@ -268,10 +270,6 @@ class Activity implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterfac
$this->socialIssues[] = $socialIssue; $this->socialIssues[] = $socialIssue;
} }
if ($this->getAccompanyingPeriod() !== null) {
$this->getAccompanyingPeriod()->addSocialIssue($socialIssue);
}
return $this; return $this;
} }
@@ -552,10 +550,6 @@ class Activity implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterfac
{ {
$this->accompanyingPeriod = $accompanyingPeriod; $this->accompanyingPeriod = $accompanyingPeriod;
foreach ($this->getSocialIssues() as $issue) {
$this->accompanyingPeriod->addSocialIssue($issue);
}
return $this; return $this;
} }

View File

@@ -22,7 +22,6 @@ use Chill\MainBundle\Entity\User;
use DateTimeImmutable; use DateTimeImmutable;
use LogicException; use LogicException;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
use Symfony\Contracts\HttpClient\HttpClientInterface; use Symfony\Contracts\HttpClient\HttpClientInterface;
use function array_key_exists; use function array_key_exists;
@@ -75,18 +74,9 @@ class MapCalendarToUser
public function getDefaultUserCalendar(string $idOrUserPrincipalName): ?array public function getDefaultUserCalendar(string $idOrUserPrincipalName): ?array
{ {
try { $value = $this->machineHttpClient->request('GET', "users/{$idOrUserPrincipalName}/calendars", [
$value = $this->machineHttpClient->request('GET', "users/{$idOrUserPrincipalName}/calendars", [ 'query' => ['$filter' => 'isDefaultCalendar eq true'],
'query' => ['$filter' => 'isDefaultCalendar eq true'], ])->toArray()['value'];
])->toArray()['value'];
} catch (ClientExceptionInterface $e) {
$this->logger->error('[MapCalendarToUser] Error while listing calendars for a user', [
'http_status_code' => $e->getResponse()->getStatusCode(),
'id_user' => $idOrUserPrincipalName,
]);
return null;
}
return $value[0] ?? null; return $value[0] ?? null;
} }

View File

@@ -34,7 +34,6 @@ use Psr\Log\LoggerInterface;
use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Core\Security;
use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface; use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
use Symfony\Contracts\HttpClient\HttpClientInterface; use Symfony\Contracts\HttpClient\HttpClientInterface;
use Symfony\Contracts\Translation\TranslatorInterface; use Symfony\Contracts\Translation\TranslatorInterface;
@@ -65,8 +64,6 @@ class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface
private OnBehalfOfUserHttpClient $userHttpClient; private OnBehalfOfUserHttpClient $userHttpClient;
private Security $security;
public function __construct( public function __construct(
CalendarRepository $calendarRepository, CalendarRepository $calendarRepository,
CalendarRangeRepository $calendarRangeRepository, CalendarRangeRepository $calendarRangeRepository,
@@ -77,8 +74,7 @@ class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface
OnBehalfOfUserHttpClient $userHttpClient, OnBehalfOfUserHttpClient $userHttpClient,
RemoteEventConverter $remoteEventConverter, RemoteEventConverter $remoteEventConverter,
TranslatorInterface $translator, TranslatorInterface $translator,
UrlGeneratorInterface $urlGenerator, UrlGeneratorInterface $urlGenerator
Security $security
) { ) {
$this->calendarRepository = $calendarRepository; $this->calendarRepository = $calendarRepository;
$this->calendarRangeRepository = $calendarRangeRepository; $this->calendarRangeRepository = $calendarRangeRepository;
@@ -90,7 +86,6 @@ class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface
$this->translator = $translator; $this->translator = $translator;
$this->urlGenerator = $urlGenerator; $this->urlGenerator = $urlGenerator;
$this->userHttpClient = $userHttpClient; $this->userHttpClient = $userHttpClient;
$this->security = $security;
} }
public function countEventsForUser(User $user, DateTimeImmutable $startDate, DateTimeImmutable $endDate): int public function countEventsForUser(User $user, DateTimeImmutable $startDate, DateTimeImmutable $endDate): int
@@ -138,24 +133,6 @@ class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface
public function isReady(): bool public function isReady(): bool
{ {
$user = $this->security->getUser();
if (!$user instanceof User) {
// this is not a user from chill. This is not the role of this class to
// restrict access, so we will just say that we do not have to do anything more
// here...
return true;
}
if (null === $this->mapCalendarToUser->getUserId($user)) {
// this user is not mapped with remote calendar. The user will have to wait for
// the next calendar subscription iteration
$this->logger->debug('mark user ready for msgraph calendar as he does not have any mapping', [
'userId' => $user->getId(),
]);
return true;
}
return $this->tokenStorage->hasToken(); return $this->tokenStorage->hasToken();
} }

View File

@@ -14,6 +14,7 @@ namespace Chill\MainBundle\Controller;
use Chill\MainBundle\Entity\Notification; use Chill\MainBundle\Entity\Notification;
use Chill\MainBundle\Entity\NotificationComment; use Chill\MainBundle\Entity\NotificationComment;
use Chill\MainBundle\Entity\User; use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Entity\Workflow\EntityWorkflow;
use Chill\MainBundle\Form\NotificationCommentType; use Chill\MainBundle\Form\NotificationCommentType;
use Chill\MainBundle\Form\NotificationType; use Chill\MainBundle\Form\NotificationType;
use Chill\MainBundle\Notification\Exception\NotificationHandlerNotFound; use Chill\MainBundle\Notification\Exception\NotificationHandlerNotFound;
@@ -21,6 +22,7 @@ use Chill\MainBundle\Notification\NotificationHandlerManager;
use Chill\MainBundle\Pagination\PaginatorFactory; use Chill\MainBundle\Pagination\PaginatorFactory;
use Chill\MainBundle\Repository\NotificationRepository; use Chill\MainBundle\Repository\NotificationRepository;
use Chill\MainBundle\Repository\UserRepository; use Chill\MainBundle\Repository\UserRepository;
use Chill\MainBundle\Repository\Workflow\EntityWorkflowRepository;
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 Psr\Log\LoggerInterface;
@@ -68,7 +70,8 @@ class NotificationController extends AbstractController
NotificationHandlerManager $notificationHandlerManager, NotificationHandlerManager $notificationHandlerManager,
PaginatorFactory $paginatorFactory, PaginatorFactory $paginatorFactory,
TranslatorInterface $translator, TranslatorInterface $translator,
UserRepository $userRepository UserRepository $userRepository,
EntityWorkflowRepository $entityWorkflowRepository
) { ) {
$this->em = $em; $this->em = $em;
$this->logger = $logger; $this->logger = $logger;
@@ -79,6 +82,7 @@ class NotificationController extends AbstractController
$this->paginatorFactory = $paginatorFactory; $this->paginatorFactory = $paginatorFactory;
$this->translator = $translator; $this->translator = $translator;
$this->userRepository = $userRepository; $this->userRepository = $userRepository;
$this->entityWorkflowRepository = $entityWorkflowRepository;
} }
/** /**
@@ -345,6 +349,7 @@ class NotificationController extends AbstractController
'appendCommentForm' => isset($appendCommentForm) ? $appendCommentForm->createView() : null, 'appendCommentForm' => isset($appendCommentForm) ? $appendCommentForm->createView() : null,
'editedCommentForm' => isset($editedCommentForm) ? $editedCommentForm->createView() : null, 'editedCommentForm' => isset($editedCommentForm) ? $editedCommentForm->createView() : null,
'editedCommentId' => $commentId ?? null, 'editedCommentId' => $commentId ?? null,
'notificationCc' => $this->isNotificationCc($notification),
]); ]);
// we mark the notification as read after having computed the response // we mark the notification as read after having computed the response
@@ -364,6 +369,21 @@ class NotificationController extends AbstractController
]; ];
} }
private function isNotificationCc(Notification $notification): bool
{
$notificationCc = false;
if ($notification->getRelatedEntityClass() === EntityWorkflow::class) {
$relatedEntity = $this->entityWorkflowRepository->findOneBy(['id' => $notification->getRelatedEntityId()]);
if ($relatedEntity->getCurrentStepCreatedBy() !== $this->security->getUser()) {
$notificationCc = true;
}
}
return $notificationCc;
}
private function itemsForTemplate(array $notifications): array private function itemsForTemplate(array $notifications): array
{ {
$templateData = []; $templateData = [];
@@ -373,6 +393,7 @@ class NotificationController extends AbstractController
'template' => $this->notificationHandlerManager->getTemplate($notification), 'template' => $this->notificationHandlerManager->getTemplate($notification),
'template_data' => $this->notificationHandlerManager->getTemplateData($notification), 'template_data' => $this->notificationHandlerManager->getTemplateData($notification),
'notification' => $notification, 'notification' => $notification,
'isNotificationCc' => $this->isNotificationCc($notification),
]; ];
} }

View File

@@ -93,6 +93,45 @@ class WorkflowApiController
); );
} }
/**
* Return a list of workflow which are waiting an action for the user.
*
* @Route("/api/1.0/main/workflow/my-cc", methods={"GET"})
*/
public function myWorkflowCc(Request $request): JsonResponse
{
if (!$this->security->isGranted('ROLE_USER') || !$this->security->getUser() instanceof User) {
throw new AccessDeniedException();
}
$total = $this->entityWorkflowRepository->countByCc($this->security->getUser());
if ($request->query->getBoolean('countOnly', false)) {
return new JsonResponse(
$this->serializer->serialize(new Counter($total), 'json'),
JsonResponse::HTTP_OK,
[],
true
);
}
$paginator = $this->paginatorFactory->create($total);
$workflows = $this->entityWorkflowRepository->findByCc(
$this->security->getUser(),
['id' => 'DESC'],
$paginator->getItemsPerPage(),
$paginator->getCurrentPageFirstItemNumber()
);
return new JsonResponse(
$this->serializer->serialize(new Collection($workflows, $paginator), 'json', ['groups' => ['read']]),
JsonResponse::HTTP_OK,
[],
true
);
}
/** /**
* @Route("/api/1.0/main/workflow/{id}/subscribe", methods={"POST"}) * @Route("/api/1.0/main/workflow/{id}/subscribe", methods={"POST"})
*/ */

View File

@@ -224,6 +224,33 @@ class WorkflowController extends AbstractController
); );
} }
/**
* @Route("/{_locale}/main/workflow/list/cc", name="chill_main_workflow_list_cc")
*/
public function myWorkflowsCc(Request $request): Response
{
$this->denyAccessUnlessGranted('IS_AUTHENTICATED_REMEMBERED');
$total = $this->entityWorkflowRepository->countByDest($this->getUser());
$paginator = $this->paginatorFactory->create($total);
$workflows = $this->entityWorkflowRepository->findByCc(
$this->getUser(),
['createdAt' => 'DESC'],
$paginator->getItemsPerPage(),
$paginator->getCurrentPageFirstItemNumber()
);
return $this->render(
'@ChillMain/Workflow/list.html.twig',
[
'workflows' => $this->buildHandler($workflows),
'paginator' => $paginator,
'step' => 'cc',
]
);
}
/** /**
* @Route("/{_locale}/main/workflow/list/dest", name="chill_main_workflow_list_dest") * @Route("/{_locale}/main/workflow/list/dest", name="chill_main_workflow_list_dest")
*/ */
@@ -318,6 +345,7 @@ class WorkflowController extends AbstractController
} }
// TODO symfony 5: add those "future" on context ($workflow->apply($entityWorkflow, $transition, $context) // TODO symfony 5: add those "future" on context ($workflow->apply($entityWorkflow, $transition, $context)
$entityWorkflow->futureCcUsers = $transitionForm['future_cc_users']->getData();
$entityWorkflow->futureDestUsers = $transitionForm['future_dest_users']->getData(); $entityWorkflow->futureDestUsers = $transitionForm['future_dest_users']->getData();
$entityWorkflow->futureDestEmails = $transitionForm['future_dest_emails']->getData(); $entityWorkflow->futureDestEmails = $transitionForm['future_dest_emails']->getData();

View File

@@ -41,6 +41,13 @@ class EntityWorkflow implements TrackCreationInterface, TrackUpdateInterface
use TrackUpdateTrait; use TrackUpdateTrait;
/**
* a list of future cc users for the next steps.
*
* @var array|User[]
*/
public array $futureCcUsers = [];
/** /**
* a list of future dest emails for the next steps. * a list of future dest emails for the next steps.
* *

View File

@@ -32,6 +32,12 @@ class EntityWorkflowStep
*/ */
private string $accessKey; private string $accessKey;
/**
* @ORM\ManyToMany(targetEntity=User::class)
* @ORM\JoinTable(name="chill_main_workflow_entity_step_cc_user")
*/
private Collection $ccUser;
/** /**
* @ORM\Column(type="text", options={"default": ""}) * @ORM\Column(type="text", options={"default": ""})
*/ */
@@ -114,11 +120,21 @@ class EntityWorkflowStep
public function __construct() public function __construct()
{ {
$this->ccUser = new ArrayCollection();
$this->destUser = new ArrayCollection(); $this->destUser = new ArrayCollection();
$this->destUserByAccessKey = new ArrayCollection(); $this->destUserByAccessKey = new ArrayCollection();
$this->accessKey = bin2hex(openssl_random_pseudo_bytes(32)); $this->accessKey = bin2hex(openssl_random_pseudo_bytes(32));
} }
public function addCcUser(User $user): self
{
if (!$this->ccUser->contains($user)) {
$this->ccUser[] = $user;
}
return $this;
}
public function addDestEmail(string $email): self public function addDestEmail(string $email): self
{ {
if (!in_array($email, $this->destEmail, true)) { if (!in_array($email, $this->destEmail, true)) {
@@ -167,6 +183,11 @@ class EntityWorkflowStep
); );
} }
public function getCcUser(): Collection
{
return $this->ccUser;
}
public function getComment(): string public function getComment(): string
{ {
return $this->comment; return $this->comment;
@@ -261,6 +282,13 @@ class EntityWorkflowStep
return true; return true;
} }
public function removeCcUser(User $user): self
{
$this->ccUser->removeElement($user);
return $this;
}
public function removeDestEmail(string $email): self public function removeDestEmail(string $email): self
{ {
$this->destEmail = array_filter($this->destEmail, static function (string $existing) use ($email) { $this->destEmail = array_filter($this->destEmail, static function (string $existing) use ($email) {

View File

@@ -155,6 +155,12 @@ class WorkflowStepType extends AbstractType
'multiple' => true, 'multiple' => true,
'mapped' => false, 'mapped' => false,
]) ])
->add('future_cc_users', PickUserDynamicType::class, [
'label' => 'workflow.cc for next steps',
'multiple' => true,
'mapped' => false,
'required' => false,
])
->add('future_dest_emails', ChillCollectionType::class, [ ->add('future_dest_emails', ChillCollectionType::class, [
'label' => 'workflow.dest by email', 'label' => 'workflow.dest by email',
'help' => 'workflow.dest by email help', 'help' => 'workflow.dest by email help',

View File

@@ -74,7 +74,9 @@ class NotificationTwigExtensionRuntime implements RuntimeExtensionInterface
} }
return $environment->render('@ChillMain/Notification/extension_list_notifications_for.html.twig', [ return $environment->render('@ChillMain/Notification/extension_list_notifications_for.html.twig', [
'notifications' => $notifications, 'appendCommentForms' => $appendCommentForms, 'notifications' => $notifications,
'appendCommentForms' => $appendCommentForms,
'notificationCc' => false,
]); ]);
} }
} }

View File

@@ -27,6 +27,13 @@ class EntityWorkflowRepository implements ObjectRepository
$this->repository = $entityManager->getRepository(EntityWorkflow::class); $this->repository = $entityManager->getRepository(EntityWorkflow::class);
} }
public function countByCc(User $user): int
{
$qb = $this->buildQueryByCc($user)->select('count(ew)');
return (int) $qb->getQuery()->getSingleScalarResult();
}
public function countByDest(User $user): int public function countByDest(User $user): int
{ {
$qb = $this->buildQueryByDest($user)->select('count(ew)'); $qb = $this->buildQueryByDest($user)->select('count(ew)');
@@ -103,6 +110,19 @@ class EntityWorkflowRepository implements ObjectRepository
return $this->repository->findBy($criteria, $orderBy, $limit, $offset); return $this->repository->findBy($criteria, $orderBy, $limit, $offset);
} }
public function findByCc(User $user, ?array $orderBy = null, $limit = null, $offset = null): array
{
$qb = $this->buildQueryByCc($user)->select('ew');
foreach ($orderBy as $key => $sort) {
$qb->addOrderBy('ew.' . $key, $sort);
}
$qb->setMaxResults($limit)->setFirstResult($offset);
return $qb->getQuery()->getResult();
}
public function findByDest(User $user, ?array $orderBy = null, $limit = null, $offset = null): array public function findByDest(User $user, ?array $orderBy = null, $limit = null, $offset = null): array
{ {
$qb = $this->buildQueryByDest($user)->select('ew'); $qb = $this->buildQueryByDest($user)->select('ew');
@@ -165,6 +185,25 @@ class EntityWorkflowRepository implements ObjectRepository
return EntityWorkflow::class; return EntityWorkflow::class;
} }
private function buildQueryByCc(User $user): QueryBuilder
{
$qb = $this->repository->createQueryBuilder('ew');
$qb->join('ew.steps', 'step');
$qb->where(
$qb->expr()->andX(
$qb->expr()->isMemberOf(':user', 'step.ccUser'),
$qb->expr()->isNull('step.transitionAfter'),
$qb->expr()->eq('step.isFinal', "'FALSE'")
)
);
$qb->setParameter('user', $user);
return $qb;
}
private function buildQueryByDest(User $user): QueryBuilder private function buildQueryByDest(User $user): QueryBuilder
{ {
$qb = $this->repository->createQueryBuilder('ew'); $qb = $this->repository->createQueryBuilder('ew');

View File

@@ -1,88 +1,25 @@
<template> <template>
<div class="alert alert-light">{{ $t('my_workflows.description') }}</div>
<my-workflows-table :workflows="workflows" />
<div class="alert alert-light">{{ $t('my_workflows.description') }}</div> <div class="alert alert-light">{{ $t('my_workflows.description_cc') }}</div>
<span v-if="noResults" class="chill-no-data-statement">{{ $t('no_data') }}</span> <my-workflows-table :workflows="workflowsCc" />
<tab-table v-else>
<template v-slot:thead>
<th scope="col">{{ $t('Object_workflow') }}</th>
<th scope="col">{{ $t('Step') }}</th>
<th scope="col">{{ $t('concerned_users') }}</th>
<th scope="col"></th>
</template>
<template v-slot:tbody>
<tr v-for="(w, i) in workflows.results" :key="`workflow-${i}`">
<td>{{ w.title }}</td>
<td>
<div class="workflow">
<div class="breadcrumb">
<i class="fa fa-circle me-1 text-chill-yellow mx-2"></i>
<span class="mx-2">{{ getStep(w) }}</span>
</div>
</div>
</td>
<td v-if="w.datas.persons !== null">
<span v-for="p in w.datas.persons" class="me-1" :key="p.id">
<on-the-fly
:type="p.type"
:id="p.id"
:buttonText="p.textAge"
:displayBadge="'true' === 'true'"
action="show">
</on-the-fly>
</span>
</td>
<td>
<a class="btn btn-sm btn-show" :href="getUrl(w)">
{{ $t('show_entity', { entity: $t('the_workflow') }) }}
</a>
</td>
</tr>
</template>
</tab-table>
</template> </template>
<script> <script>
import { mapState, mapGetters } from "vuex"; import { mapState } from "vuex";
import TabTable from "./TabTable"; import MyWorkflowsTable from './MyWorkflowsTable.vue';
import OnTheFly from 'ChillMainAssets/vuejs/OnTheFly/components/OnTheFly';
export default { export default {
name: "MyWorkflows", name: "MyWorkflows",
components: { components: {
TabTable, MyWorkflowsTable
OnTheFly
}, },
computed: { computed: {
...mapState([ ...mapState([
'workflows', 'workflows',
'workflowsCc',
]), ]),
...mapGetters([
'isWorkflowsLoaded',
]),
noResults() {
if (!this.isWorkflowsLoaded) {
return false;
} else {
return this.workflows.count === 0;
}
},
},
methods: {
getUrl(w) {
return `/fr/main/workflow/${w.id}/show`;
},
getStep(w) {
const lastStep = w.steps.length - 1
return w.steps[lastStep].currentStep.text;
}
}, },
} }
</script> </script>
<style scoped>
span.outdated {
font-weight: bold;
color: var(--bs-warning);
}
</style>

View File

@@ -0,0 +1,83 @@
<template>
<span v-if="hasNoResults(workflows)" class="chill-no-data-statement">{{ $t('no_data') }}</span>
<tab-table v-else>
<template v-slot:thead>
<th scope="col">{{ $t('Object_workflow') }}</th>
<th scope="col">{{ $t('Step') }}</th>
<th scope="col">{{ $t('concerned_users') }}</th>
<th scope="col"></th>
</template>
<template v-slot:tbody>
<tr v-for="(w, i) in workflows.results" :key="`workflow-${i}`">
<td>{{ w.title }}</td>
<td>
<div class="workflow">
<div class="breadcrumb">
<i class="fa fa-circle me-1 text-chill-yellow mx-2"></i>
<span class="mx-2">{{ getStep(w) }}</span>
</div>
</div>
</td>
<td v-if="w.datas.persons !== null">
<span v-for="p in w.datas.persons" class="me-1" :key="p.id">
<on-the-fly
:type="p.type"
:id="p.id"
:buttonText="p.textAge"
:displayBadge="'true' === 'true'"
action="show">
</on-the-fly>
</span>
</td>
<td>
<a class="btn btn-sm btn-show" :href="getUrl(w)">
{{ $t('show_entity', { entity: $t('the_workflow') }) }}
</a>
</td>
</tr>
</template>
</tab-table>
</template>
<script>
import { mapGetters } from "vuex";
import TabTable from "./TabTable";
import OnTheFly from 'ChillMainAssets/vuejs/OnTheFly/components/OnTheFly';
export default {
name: "MyWorkflows",
components: {
TabTable,
OnTheFly
},
props: ['workflows'],
computed: {
...mapGetters([
'isWorkflowsLoaded',
]),
},
methods: {
hasNoResults(workflows) {
if (!this.isWorkflowsLoaded) {
return false;
} else {
return workflows.count === 0;
}
},
getUrl(w) {
return `/fr/main/workflow/${w.id}/show`;
},
getStep(w) {
const lastStep = w.steps.length - 1
return w.steps[lastStep].currentStep.text;
}
},
}
</script>
<style scoped>
span.outdated {
font-weight: bold;
color: var(--bs-warning);
}
</style>

View File

@@ -24,7 +24,8 @@ const appMessages = {
}, },
my_workflows: { my_workflows: {
tab: "Mes workflows", tab: "Mes workflows",
description: "Liste des workflows en attente d'une action." description: "Liste des workflows en attente d'une action.",
description_cc: "Liste des workflows dont je suis en copie."
}, },
opening_date: "Date d'ouverture", opening_date: "Date d'ouverture",
social_issues: "Problématiques sociales", social_issues: "Problématiques sociales",

View File

@@ -22,6 +22,7 @@ const store = createStore({
accompanyingCourses: {}, accompanyingCourses: {},
notifications: {}, notifications: {},
workflows: {}, workflows: {},
workflowsCc: {},
errorMsg: [], errorMsg: [],
loading: false loading: false
}, },
@@ -87,6 +88,9 @@ const store = createStore({
addWorkflows(state, workflows) { addWorkflows(state, workflows) {
state.workflows = workflows; state.workflows = workflows;
}, },
addWorkflowsCc(state, workflows) {
state.workflowsCc = workflows;
},
setLoading(state, bool) { setLoading(state, bool) {
state.loading = bool; state.loading = bool;
}, },
@@ -195,17 +199,23 @@ const store = createStore({
case 'MyWorkflows': case 'MyWorkflows':
if (!getters.isWorflowsLoaded) { if (!getters.isWorflowsLoaded) {
commit('setLoading', true); commit('setLoading', true);
const url = '/api/1.0/main/workflow/my'; makeFetch('GET', '/api/1.0/main/workflow/my')
makeFetch('GET', url) .then((response) => {
.then((response) => { commit('addWorkflows', response);
console.log('workflows', response) makeFetch('GET', '/api/1.0/main/workflow/my-cc')
commit('addWorkflows', response); .then((response) => {
commit('setLoading', false); commit('addWorkflowsCc', response);
}) commit('setLoading', false);
.catch((error) => { })
commit('catchError', error); .catch((error) => {
throw error; commit('catchError', error);
}); throw error;
});
})
.catch((error) => {
commit('catchError', error);
throw error;
});
} }
break; break;
default: default:

View File

@@ -30,11 +30,27 @@
{% endif %} {% endif %}
{% if c.notification.addressees|length > 0 %} {% if c.notification.addressees|length > 0 %}
<li class="notification-to"> <li class="notification-to">
<span class="item-key"> {% if c.notification_cc is defined %}
{% if c.notification_cc %}
<span class="item-key">
<abbr title="{{ 'notification.sent_cc'|trans }}">
{{ 'notification.cc'|trans }} :
</abbr>
</span>
{% else %}
<span class="item-key">
<abbr title="{{ 'notification.sent_to'|trans }}">
{{ 'notification.to'|trans }} :
</abbr>
</span>
{% endif %}
{% else %}
<span class="item-key">
<abbr title="{{ 'notification.sent_to'|trans }}"> <abbr title="{{ 'notification.sent_to'|trans }}">
{{ 'notification.to'|trans }} : {{ 'notification.to'|trans }} :
</abbr> </abbr>
</span> </span>
{% endif %}
{% for a in c.notification.addressees %} {% for a in c.notification.addressees %}
<span class="badge-user"> <span class="badge-user">
{{ a|chill_entity_render_string }} {{ a|chill_entity_render_string }}

View File

@@ -6,6 +6,7 @@
'full_content': true, 'full_content': true,
'fold_item': true, 'fold_item': true,
'action_button': true, 'action_button': true,
'notification_cc': notificationCc,
} %}{# } %}{#
#} #}
{% endfor %} {% endfor %}

View File

@@ -50,7 +50,8 @@
{% for data in datas %} {% for data in datas %}
{% set notification = data.notification %} {% set notification = data.notification %}
{% include 'ChillMainBundle:Notification:_list_item.html.twig' with { {% include 'ChillMainBundle:Notification:_list_item.html.twig' with {
'fold_item': true 'fold_item': true,
'notification_cc': data.isNotificationCc
} %} } %}
{% endfor %} {% endfor %}
</div> </div>

View File

@@ -27,7 +27,8 @@
}, },
'action_button': false, 'action_button': false,
'full_content': true, 'full_content': true,
'fold_item': false 'fold_item': false,
'notification_cc': notificationCc
} %} } %}
</div> </div>

View File

@@ -65,6 +65,8 @@
<div id="futureDests"> <div id="futureDests">
{{ form_row(transition_form.future_dest_users) }} {{ form_row(transition_form.future_dest_users) }}
{{ form_row(transition_form.future_cc_users) }}
{{ form_row(transition_form.future_dest_emails) }} {{ form_row(transition_form.future_dest_emails) }}
</div> </div>

View File

@@ -81,6 +81,15 @@
</ul> </ul>
{% endif %} {% endif %}
{% if step.ccUser|length > 0 %}
<p><b>{{ 'workflow.Users put in Cc'|trans }}&nbsp;: </b></p>
<ul>
{% for u in step.ccUser %}
<li>{{ u|chill_entity_render_box }}</li>
{% endfor %}
</ul>
{% endif %}
{% if entity_workflow.currentStep.destEmail|length > 0 %} {% if entity_workflow.currentStep.destEmail|length > 0 %}
<p><b>{{ 'workflow.An access key was also sent to those addresses'|trans }}&nbsp;:</b></p> <p><b>{{ 'workflow.An access key was also sent to those addresses'|trans }}&nbsp;:</b></p>
<ul> <ul>

View File

@@ -7,7 +7,7 @@
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<div class="col-10 workflow"> <div class="col-12 workflow">
<h1 class="mb-5">{{ block('title') }}</h1> <h1 class="mb-5">{{ block('title') }}</h1>
@@ -25,6 +25,12 @@
{{ 'workflow.dest'|trans }} {{ 'workflow.dest'|trans }}
</a> </a>
</li> </li>
<li class="nav-item">
<a href="{{ path('chill_main_workflow_list_cc') }}"
class="nav-link {% if step == 'cc' %}active{% endif %}">
{{ 'workflow.cc'|trans }}
</a>
</li>
<li class="nav-item"> <li class="nav-item">
<a href="{{ path('chill_main_workflow_list_previous_without_reaction') }}" <a href="{{ path('chill_main_workflow_list_previous_without_reaction') }}"
class="nav-link {% if step == 'previous_without_reaction' %}active{% endif %}"> class="nav-link {% if step == 'previous_without_reaction' %}active{% endif %}">

View File

@@ -15,6 +15,12 @@
{% for d in step.destUser %}{{ d|chill_entity_render_string }}{% if not loop.last %}, {% endif %}{% endfor %} {% for d in step.destUser %}{{ d|chill_entity_render_string }}{% if not loop.last %}, {% endif %}{% endfor %}
</b> </b>
</li> </li>
<li>
<span class="item-key">{{ 'workflow.Cc'|trans ~ ' : ' }}</span>
<b>
{% for u in step.ccUser %}{{ u|chill_entity_render_string }}{% if not loop.last %}, {% endif %}{% endfor %}
</b>
</li>
{% else %} {% else %}
<li> <li>
<span class="item-key">{{ 'workflow.Created by'|trans ~ ' : ' }}</span> <span class="item-key">{{ 'workflow.Created by'|trans ~ ' : ' }}</span>

View File

@@ -50,6 +50,10 @@ class EntityWorkflowTransitionEventSubscriber implements EventSubscriberInterfac
/** @var EntityWorkflow $entityWorkflow */ /** @var EntityWorkflow $entityWorkflow */
$entityWorkflow = $event->getSubject(); $entityWorkflow = $event->getSubject();
foreach ($entityWorkflow->futureCcUsers as $user) {
$entityWorkflow->getCurrentStep()->addCcUser($user);
}
foreach ($entityWorkflow->futureDestUsers as $user) { foreach ($entityWorkflow->futureDestUsers as $user) {
$entityWorkflow->getCurrentStep()->addDestUser($user); $entityWorkflow->getCurrentStep()->addDestUser($user);
} }

View File

@@ -85,7 +85,9 @@ class NotificationOnTransition implements EventSubscriberInterface
// the subscriber to final, only if final // the subscriber to final, only if final
$entityWorkflow->isFinal() ? $entityWorkflow->getSubscriberToFinal()->toArray() : [], $entityWorkflow->isFinal() ? $entityWorkflow->getSubscriberToFinal()->toArray() : [],
// the dests for the current step // the dests for the current step
$entityWorkflow->getCurrentStep()->getDestUser()->toArray() $entityWorkflow->getCurrentStep()->getDestUser()->toArray(),
// the cc users for the current step
$entityWorkflow->getCurrentStep()->getCcUser()->toArray()
) as $dest) { ) as $dest) {
$dests[spl_object_hash($dest)] = $dest; $dests[spl_object_hash($dest)] = $dest;
} }

View File

@@ -826,4 +826,20 @@ paths:
$ref: '#/components/schemas/Workflow' $ref: '#/components/schemas/Workflow'
403: 403:
description: "Unauthorized" description: "Unauthorized"
/1.0/main/workflow/my-cc:
get:
tags:
- workflow
summary: Return a list of workflows for which user was notified in Cc
responses:
200:
description: "ok"
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Workflow'
403:
description: "Unauthorized"

View File

@@ -0,0 +1,39 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\Migrations\Main;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
final class Version20230321134155 extends AbstractMigration
{
public function down(Schema $schema): void
{
$this->addSql('ALTER TABLE chill_main_workflow_entity_step_cc_user DROP CONSTRAINT FK_9FC79037E6AF9D4');
$this->addSql('ALTER TABLE chill_main_workflow_entity_step_cc_user DROP CONSTRAINT FK_9FC7903A76ED395');
$this->addSql('DROP TABLE chill_main_workflow_entity_step_cc_user');
}
public function getDescription(): string
{
return 'Add cc User to workflow step';
}
public function up(Schema $schema): void
{
$this->addSql('CREATE TABLE chill_main_workflow_entity_step_cc_user (entityworkflowstep_id INT NOT NULL, user_id INT NOT NULL, PRIMARY KEY(entityworkflowstep_id, user_id))');
$this->addSql('CREATE INDEX IDX_9FC79037E6AF9D4 ON chill_main_workflow_entity_step_cc_user (entityworkflowstep_id)');
$this->addSql('CREATE INDEX IDX_9FC7903A76ED395 ON chill_main_workflow_entity_step_cc_user (user_id)');
$this->addSql('ALTER TABLE chill_main_workflow_entity_step_cc_user ADD CONSTRAINT FK_9FC79037E6AF9D4 FOREIGN KEY (entityworkflowstep_id) REFERENCES chill_main_workflow_entity_step (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE');
$this->addSql('ALTER TABLE chill_main_workflow_entity_step_cc_user ADD CONSTRAINT FK_9FC7903A76ED395 FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE');
}
}

View File

@@ -447,6 +447,7 @@ workflow:
Created by: Créé par Created by: Créé par
My decision: Ma décision My decision: Ma décision
Next step: Prochaine étape Next step: Prochaine étape
cc for next steps: Utilisateurs en copie
dest for next steps: Utilisateurs qui valideront la prochaine étape dest for next steps: Utilisateurs qui valideront la prochaine étape
Freeze: Geler Freeze: Geler
Freezed: Gelé Freezed: Gelé
@@ -465,12 +466,14 @@ workflow:
Document (n°%doc%): "Document (n°%doc%)" Document (n°%doc%): "Document (n°%doc%)"
Work (n°%w%): "Action d'accompagnement (n°%w%)" Work (n°%w%): "Action d'accompagnement (n°%w%)"
subscribed: Workflows suivis subscribed: Workflows suivis
cc: Workflows dont je suis en copie
dest: Workflows en attente d'action dest: Workflows en attente d'action
you subscribed to all steps: Vous recevrez une notification à chaque étape you subscribed to all steps: Vous recevrez une notification à chaque étape
you subscribed to final step: Vous recevrez une notification à l'étape finale you subscribed to final step: Vous recevrez une notification à l'étape finale
Current step: Étape actuelle Current step: Étape actuelle
Comment on last change: Commentaire à la transition précédente Comment on last change: Commentaire à la transition précédente
Users allowed to apply transition: Utilisateurs pouvant valider cette étape Users allowed to apply transition: Utilisateurs pouvant valider cette étape
Users put in Cc: Utilisateurs mis en copie
Workflow deleted with success: Le workflow a été supprimé Workflow deleted with success: Le workflow a été supprimé
Delete workflow ?: Supprimer le workflow ? Delete workflow ?: Supprimer le workflow ?
Are you sure you want to delete this workflow ?: Êtes-vous sûr·e de vouloir supprimer ce workflow ? Are you sure you want to delete this workflow ?: Êtes-vous sûr·e de vouloir supprimer ce workflow ?
@@ -487,6 +490,7 @@ workflow:
Previous transitionned: Anciens workflows Previous transitionned: Anciens workflows
Previous workflow transitionned help: Workflows où vous avez exécuté une action. Previous workflow transitionned help: Workflows où vous avez exécuté une action.
For: Pour For: Pour
Cc: Cc
You must select a next step, pick another decision if no next steps are available: Il faut une prochaine étape. Choissisez une autre décision si nécessaire. You must select a next step, pick another decision if no next steps are available: Il faut une prochaine étape. Choissisez une autre décision si nécessaire.
An access key was also sent to those addresses: Un lien d'accès a été envoyé à ces adresses An access key was also sent to those addresses: Un lien d'accès a été envoyé à ces adresses
Those users are also granted to apply a transition by using an access key: Ces utilisateurs ont obtenu l'accès grâce au lien reçu par email Those users are also granted to apply a transition by using an access key: Ces utilisateurs ont obtenu l'accès grâce au lien reçu par email
@@ -521,7 +525,9 @@ notification:
list: Notifications list: Notifications
Sent: Envoyé Sent: Envoyé
to: À to: À
cc: Cc
sent_to: Destinataire(s) sent_to: Destinataire(s)
sent_cc: En copie
from: De from: De
received_from: Expéditeur received_from: Expéditeur
you were notified by %sender%: Vous avez été notifié par %sender% you were notified by %sender%: Vous avez été notifié par %sender%

View File

@@ -373,6 +373,7 @@ workflow:
Created by: Créé par Created by: Créé par
My decision: Ma décision My decision: Ma décision
Next step: Prochaine étape Next step: Prochaine étape
cc for next steps: Utilisateurs en copie
dest for next steps: Utilisateurs qui valideront la prochaine étape dest for next steps: Utilisateurs qui valideront la prochaine étape
Freeze: Geler Freeze: Geler
Freezed: Gelé Freezed: Gelé
@@ -392,11 +393,13 @@ workflow:
Work (n°%w%): "Action d'accompagnement (n°%w%)" Work (n°%w%): "Action d'accompagnement (n°%w%)"
subscribed: Workflows suivis subscribed: Workflows suivis
dest: Workflows en attente d'action dest: Workflows en attente d'action
cc: Workflows dont je suis en copie
you subscribed to all steps: Vous recevrez une notification à chaque étape you subscribed to all steps: Vous recevrez une notification à chaque étape
you subscribed to final step: Vous recevrez une notification à l'étape finale you subscribed to final step: Vous recevrez une notification à l'étape finale
Current step: Étape actuelle Current step: Étape actuelle
Comment on last change: Commentaire à la transition précédente Comment on last change: Commentaire à la transition précédente
Users allowed to apply transition: Utilisateurs pouvant valider cette étape Users allowed to apply transition: Utilisateurs pouvant valider cette étape
Users put in Cc: Utilisateurs mis en copie
Workflow deleted with success: Le workflow a été supprimé Workflow deleted with success: Le workflow a été supprimé
Delete workflow ?: Supprimer le workflow ? Delete workflow ?: Supprimer le workflow ?
Are you sure you want to delete this workflow ?: Êtes-vous sûr·e de vouloir supprimer ce workflow ? Are you sure you want to delete this workflow ?: Êtes-vous sûr·e de vouloir supprimer ce workflow ?
@@ -414,6 +417,7 @@ workflow:
Previous transitionned: Anciens workflows Previous transitionned: Anciens workflows
Previous workflow transitionned help: Workflows où vous avez exécuté une action. Previous workflow transitionned help: Workflows où vous avez exécuté une action.
For: Pour For: Pour
Cc: Cc
Subscribe final: Recevoir une notification à l'étape finale Subscribe final: Recevoir une notification à l'étape finale
@@ -442,7 +446,9 @@ notification:
list: Notifications list: Notifications
Sent: Envoyé Sent: Envoyé
to: À to: À
cc: Cc
sent_to: Destinataire(s) sent_to: Destinataire(s)
sent_cc: En copie
from: De from: De
received_from: Expéditeur received_from: Expéditeur
you were notified by %sender%: Vous avez été notifié par %sender% you were notified by %sender%: Vous avez été notifié par %sender%

View File

@@ -76,17 +76,8 @@ class SimilarPersonMatcher
$qb->select('p') $qb->select('p')
->from(Person::class, 'p') ->from(Person::class, 'p')
->join('p.centerHistory', 'center_history')
->where('SIMILARITY(p.fullnameCanonical, UNACCENT(LOWER(:fullName))) >= :precision') ->where('SIMILARITY(p.fullnameCanonical, UNACCENT(LOWER(:fullName))) >= :precision')
->andWhere($qb->expr()->in('center_history.center', ':centers')) ->andWhere($qb->expr()->in('p.center', ':centers'));
->andWhere($qb->expr()->andX(
$qb->expr()->lte('center_history.startDate', 'CURRENT_DATE()'),
$qb->expr()->orX(
$qb->expr()->isNull('center_history.endDate'),
$qb->expr()->gt('center_history.endDate', 'CURRENT_DATE()')
)
))
;
$qb $qb
->setParameter('fullName', $this->personRender->renderString($person, [])) ->setParameter('fullName', $this->personRender->renderString($person, []))
@@ -126,6 +117,7 @@ class SimilarPersonMatcher
$qb->setParameter('fullName', $this->personRender->renderString($person, [])); $qb->setParameter('fullName', $this->personRender->renderString($person, []));
} }
dump($qb->getQuery());
return $qb->getQuery()->getResult(); return $qb->getQuery()->getResult();
} }
} }

View File

@@ -54,7 +54,7 @@ class AccompanyingPeriodValidityValidator extends ConstraintValidator
$activities = $this->activityRepository->findBy(['accompanyingPeriod' => $period]); $activities = $this->activityRepository->findBy(['accompanyingPeriod' => $period]);
foreach ($activities as $activity) { foreach ($activities as $activity) {
$socialIssues = array_merge($socialIssues, $activity->getSocialIssues()->toArray()); $socialIssues = $activity->getSocialIssues()->toArray();
} }
foreach ($period->getWorks() as $work) { foreach ($period->getWorks() as $work) {
@@ -64,7 +64,7 @@ class AccompanyingPeriodValidityValidator extends ConstraintValidator
$socialIssuesByKey = []; $socialIssuesByKey = [];
foreach ($socialIssues as $si) { foreach ($socialIssues as $si) {
$socialIssuesByKey[spl_object_hash($si)] = $si; $socialIssuesByKey[$si->getId()] = $si;
} }
$periodIssuesWithAncestors = []; $periodIssuesWithAncestors = [];
@@ -75,7 +75,7 @@ class AccompanyingPeriodValidityValidator extends ConstraintValidator
$periodIssuesWithAncestors, $periodIssuesWithAncestors,
array_map( array_map(
static function (SocialIssue $si) { static function (SocialIssue $si) {
return spl_object_hash($si); return $si->getId();
}, },
$si->getAncestors(true) $si->getAncestors(true)
) )

View File

@@ -11,15 +11,12 @@ declare(strict_types=1);
namespace Chill\ReportBundle\Controller; namespace Chill\ReportBundle\Controller;
use Chill\DocStoreBundle\Security\Authorization\PersonDocumentVoter;
use Chill\MainBundle\Pagination\PaginatorFactory; use Chill\MainBundle\Pagination\PaginatorFactory;
use Chill\MainBundle\Security\Authorization\AuthorizationHelper; use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
use Chill\MainBundle\Security\Resolver\CenterResolverManagerInterface;
use Chill\PersonBundle\Entity\Person; use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Privacy\PrivacyEvent; use Chill\PersonBundle\Privacy\PrivacyEvent;
use Chill\ReportBundle\Entity\Report; use Chill\ReportBundle\Entity\Report;
use Chill\ReportBundle\Form\ReportType; use Chill\ReportBundle\Form\ReportType;
use Chill\ReportBundle\Security\Authorization\ReportVoter;
use DateTime; use DateTime;
use RuntimeException; use RuntimeException;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
@@ -28,7 +25,7 @@ use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\FormType; use Symfony\Component\Form\Extension\Core\Type\FormType;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\Role\Role; use Symfony\Component\Security\Core\Role\Role;
use Symfony\Component\Security\Core\Security;
use function count; use function count;
/** /**
@@ -51,25 +48,17 @@ class ReportController extends AbstractController
*/ */
protected $paginator; protected $paginator;
private CenterResolverManagerInterface $centerResolverManager;
private Security $security;
/** /**
* ReportController constructor. * ReportController constructor.
*/ */
public function __construct( public function __construct(
EventDispatcherInterface $eventDispatcher, EventDispatcherInterface $eventDispatcher,
AuthorizationHelper $authorizationHelper, AuthorizationHelper $authorizationHelper,
PaginatorFactory $paginator, PaginatorFactory $paginator
CenterResolverManagerInterface $centerResolverManager,
Security $security
) { ) {
$this->eventDispatcher = $eventDispatcher; $this->eventDispatcher = $eventDispatcher;
$this->authorizationHelper = $authorizationHelper; $this->authorizationHelper = $authorizationHelper;
$this->paginator = $paginator; $this->paginator = $paginator;
$this->centerResolverManager = $centerResolverManager;
$this->security = $security;
} }
/** /**
@@ -131,7 +120,7 @@ class ReportController extends AbstractController
->trans('The form is not valid. The report has not been created !') ->trans('The form is not valid. The report has not been created !')
); );
return $this->render('@ChillReport/Report/new.html.twig', [ return $this->render('ChillReportBundle:Report:new.html.twig', [
'entity' => $entity, 'entity' => $entity,
'form' => $form->createView(), 'form' => $form->createView(),
'person' => $person, 'person' => $person,
@@ -151,7 +140,7 @@ class ReportController extends AbstractController
$em = $this->getDoctrine()->getManager(); $em = $this->getDoctrine()->getManager();
/** @var Report $report */ /** @var Report $report */
$report = $em->getRepository(Report::class)->find($report_id); $report = $em->getRepository('ChillReportBundle:Report')->find($report_id);
if (!$report) { if (!$report) {
throw $this->createNotFoundException( throw $this->createNotFoundException(
@@ -200,7 +189,7 @@ class ReportController extends AbstractController
$cFGroup = $em->getRepository(\Chill\CustomFieldsBundle\Entity\CustomFieldsGroup::class)->find($cf_group_id); $cFGroup = $em->getRepository(\Chill\CustomFieldsBundle\Entity\CustomFieldsGroup::class)->find($cf_group_id);
$reports = $em->getRepository('ChillReportBundle:Report')->findByCFGroup($cFGroup); $reports = $em->getRepository('ChillReportBundle:Report')->findByCFGroup($cFGroup);
$response = $this->render('@ChillReport/Report/export.csv.twig', [ $response = $this->render('ChillReportBundle:Report:export.csv.twig', [
'reports' => $reports, 'reports' => $reports,
'cf_group' => $cFGroup, 'cf_group' => $cFGroup,
]); ]);
@@ -229,9 +218,9 @@ class ReportController extends AbstractController
$reachableScopes = $this->authorizationHelper $reachableScopes = $this->authorizationHelper
->getReachableScopes( ->getReachableScopes(
$this->security->getUser(), $this->getUser(),
ReportVoter::SEE, new Role('CHILL_REPORT_SEE'),
$this->centerResolverManager->resolveCenters($person) $person->getCenter()
); );
$total = $em $total = $em
@@ -309,7 +298,7 @@ class ReportController extends AbstractController
$form = $this->createCreateForm($entity, $person, $cFGroup); $form = $this->createCreateForm($entity, $person, $cFGroup);
return $this->render('@ChillReport/Report/new.html.twig', [ return $this->render('ChillReportBundle:Report:new.html.twig', [
'entity' => $entity, 'entity' => $entity,
'form' => $form->createView(), 'form' => $form->createView(),
'person' => $person, 'person' => $person,
@@ -526,7 +515,7 @@ class ReportController extends AbstractController
$person = $em->getRepository(\Chill\PersonBundle\Entity\Person::class)->find($person_id); $person = $em->getRepository(\Chill\PersonBundle\Entity\Person::class)->find($person_id);
$entity = $em->getRepository(Report::class)->find($report_id); $entity = $em->getRepository('ChillReportBundle:Report')->find($report_id);
if (!$entity || !$person) { if (!$entity || !$person) {
throw $this->createNotFoundException( throw $this->createNotFoundException(
@@ -543,7 +532,7 @@ class ReportController extends AbstractController
]); ]);
$this->eventDispatcher->dispatch(PrivacyEvent::PERSON_PRIVACY_EVENT, $event); $this->eventDispatcher->dispatch(PrivacyEvent::PERSON_PRIVACY_EVENT, $event);
return $this->render('@ChillReport/Report/view.html.twig', [ return $this->render('ChillReportBundle:Report:view.html.twig', [
'entity' => $entity, 'entity' => $entity,
'person' => $person, 'person' => $person,
]); ]);
@@ -589,8 +578,8 @@ class ReportController extends AbstractController
), ),
'method' => 'PUT', 'method' => 'PUT',
'cFGroup' => $entity->getCFGroup(), 'cFGroup' => $entity->getCFGroup(),
//'role' => ReportVoter::UPDATE, 'role' => new Role('CHILL_REPORT_UPDATE'),
//'center' => $this->centerResolverManager->resolveCenters($entity->getPerson()), 'center' => $entity->getPerson()->getCenter(),
]); ]);
} }
} }

View File

@@ -74,15 +74,15 @@ class ReportType extends AbstractType
'group' => $options['cFGroup'], ] 'group' => $options['cFGroup'], ]
); );
//$this->appendScopeChoices( $this->appendScopeChoices(
// $builder, $builder,
// $options['role'], $options['role'],
// $options['center'], $options['center'],
// $this->user, $this->user,
// $this->authorizationHelper, $this->authorizationHelper,
// $this->translatableStringHelper, $this->translatableStringHelper,
// $this->om $this->om
//); );
} }
public function configureOptions(OptionsResolver $resolver) public function configureOptions(OptionsResolver $resolver)
@@ -97,7 +97,7 @@ class ReportType extends AbstractType
$resolver->setAllowedTypes('cFGroup', 'Chill\CustomFieldsBundle\Entity\CustomFieldsGroup'); $resolver->setAllowedTypes('cFGroup', 'Chill\CustomFieldsBundle\Entity\CustomFieldsGroup');
//$this->appendScopeChoicesOptions($resolver); $this->appendScopeChoicesOptions($resolver);
} }
/** /**

View File

@@ -25,9 +25,7 @@
{{ form_row(edit_form.user) }} {{ form_row(edit_form.user) }}
{{ form_row(edit_form.date) }} {{ form_row(edit_form.date) }}
{#
{{ form_row(edit_form.scope) }} {{ form_row(edit_form.scope) }}
#}
{{ form_row(edit_form.cFData) }} {{ form_row(edit_form.cFData) }}
{{ form_widget(edit_form) }} {{ form_widget(edit_form) }}

View File

@@ -1,5 +1,7 @@
services: services:
Chill\ReportBundle\Controller\ReportController: Chill\ReportBundle\Controller\ReportController:
autowire: true arguments:
autoconfigure: true $eventDispatcher: '@Symfony\Component\EventDispatcher\EventDispatcherInterface'
$authorizationHelper: '@Chill\MainBundle\Security\Authorization\AuthorizationHelper'
$paginator: '@Chill\MainBundle\Pagination\PaginatorFactory'
tags: ['controller.service_arguments'] tags: ['controller.service_arguments']

View File

@@ -805,9 +805,12 @@ class ThirdParty implements TrackCreationInterface, TrackUpdateInterface
return $this; return $this;
} }
public function setProfession(?string $profession): self /**
* @return $this
*/
public function setProfession(string $profession): self
{ {
$this->profession = (string) $profession; $this->profession = $profession;
return $this; return $this;
} }

View File

@@ -110,7 +110,6 @@ class ThirdPartyType extends AbstractType
->add('profession', TextType::class, [ ->add('profession', TextType::class, [
'label' => 'thirdparty.Profession', 'label' => 'thirdparty.Profession',
'required' => false, 'required' => false,
'empty_data' => '',
]) ])
->add('contactDataAnonymous', CheckboxType::class, [ ->add('contactDataAnonymous', CheckboxType::class, [
'required' => false, 'required' => false,