Compare commits

..

13 Commits

Author SHA1 Message Date
2dcc65d172 unresolved! temporary disable deprecated appendScopeChoices function in reportType form 2023-03-29 13:49:10 +02:00
2d6a0d14eb Fix syntax in ReportController 2023-03-29 13:48:24 +02:00
fcb057c55b resolve center in Report Controller 2023-03-29 13:47:34 +02:00
96ddc73e45 Feature: [calendar sync msgraph] Allow to show the calendar details if the user does not have msgraph mapping
We first check that there are "msgraph" attributes before forcing redirection to MSGraph authorization. If not, we let's the user see the calendar edit/create page.
2023-03-23 12:48:37 +01:00
4eb7d10e45 Fixed: [calendar ms synchro] do not throw an error if we are not allowed
to get the default calendar
2023-03-23 11:23:14 +01:00
efa475df0f Merge branch 'master' of gitlab.com:Chill-Projet/chill-bundles 2023-03-22 09:06:12 +01:00
a8977729fe Fixed: [similar person matcher] properly takes person center into account
Center comes from the table person_center_history, not person.center_id
2023-03-21 16:39:31 +01:00
ecac409586 fixed: fix 3party syntax 2023-03-20 21:38:42 +01:00
df2480c47c Fixed: transform null value into emtpy string into ThirdParty::setProfession 2023-03-20 21:36:46 +01:00
c729a14304 FIX [minor] remove dump, use statement, ... 2023-03-20 07:54:16 +01:00
06b7e84270 FIX [thirdparty][profession] set default value for profession in symfony form 2023-03-16 13:48:01 +01:00
1cc80c8e6a Fixed: takes all activity into account to check social issue consistency 2023-03-16 00:12:41 +01:00
c3558beee1 Fixed: add a social issue on an activity to an accompanying period 2023-03-16 00:12:09 +01:00
37 changed files with 191 additions and 451 deletions

View File

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

View File

@@ -22,6 +22,7 @@ use Chill\MainBundle\Entity\User;
use DateTimeImmutable;
use LogicException;
use Psr\Log\LoggerInterface;
use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
use Symfony\Contracts\HttpClient\HttpClientInterface;
use function array_key_exists;
@@ -74,9 +75,18 @@ class MapCalendarToUser
public function getDefaultUserCalendar(string $idOrUserPrincipalName): ?array
{
$value = $this->machineHttpClient->request('GET', "users/{$idOrUserPrincipalName}/calendars", [
'query' => ['$filter' => 'isDefaultCalendar eq true'],
])->toArray()['value'];
try {
$value = $this->machineHttpClient->request('GET', "users/{$idOrUserPrincipalName}/calendars", [
'query' => ['$filter' => 'isDefaultCalendar eq true'],
])->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;
}

View File

@@ -34,6 +34,7 @@ use Psr\Log\LoggerInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Core\Security;
use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
use Symfony\Contracts\HttpClient\HttpClientInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
@@ -64,6 +65,8 @@ class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface
private OnBehalfOfUserHttpClient $userHttpClient;
private Security $security;
public function __construct(
CalendarRepository $calendarRepository,
CalendarRangeRepository $calendarRangeRepository,
@@ -74,7 +77,8 @@ class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface
OnBehalfOfUserHttpClient $userHttpClient,
RemoteEventConverter $remoteEventConverter,
TranslatorInterface $translator,
UrlGeneratorInterface $urlGenerator
UrlGeneratorInterface $urlGenerator,
Security $security
) {
$this->calendarRepository = $calendarRepository;
$this->calendarRangeRepository = $calendarRangeRepository;
@@ -86,6 +90,7 @@ class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface
$this->translator = $translator;
$this->urlGenerator = $urlGenerator;
$this->userHttpClient = $userHttpClient;
$this->security = $security;
}
public function countEventsForUser(User $user, DateTimeImmutable $startDate, DateTimeImmutable $endDate): int
@@ -133,6 +138,24 @@ class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface
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();
}

View File

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

View File

@@ -93,45 +93,6 @@ 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"})
*/

View File

@@ -224,33 +224,6 @@ 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")
*/
@@ -345,7 +318,6 @@ class WorkflowController extends AbstractController
}
// 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->futureDestEmails = $transitionForm['future_dest_emails']->getData();

View File

@@ -41,13 +41,6 @@ class EntityWorkflow implements TrackCreationInterface, TrackUpdateInterface
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.
*

View File

@@ -32,12 +32,6 @@ class EntityWorkflowStep
*/
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": ""})
*/
@@ -120,21 +114,11 @@ class EntityWorkflowStep
public function __construct()
{
$this->ccUser = new ArrayCollection();
$this->destUser = new ArrayCollection();
$this->destUserByAccessKey = new ArrayCollection();
$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
{
if (!in_array($email, $this->destEmail, true)) {
@@ -183,11 +167,6 @@ class EntityWorkflowStep
);
}
public function getCcUser(): Collection
{
return $this->ccUser;
}
public function getComment(): string
{
return $this->comment;
@@ -282,13 +261,6 @@ class EntityWorkflowStep
return true;
}
public function removeCcUser(User $user): self
{
$this->ccUser->removeElement($user);
return $this;
}
public function removeDestEmail(string $email): self
{
$this->destEmail = array_filter($this->destEmail, static function (string $existing) use ($email) {

View File

@@ -155,12 +155,6 @@ class WorkflowStepType extends AbstractType
'multiple' => true,
'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, [
'label' => 'workflow.dest by email',
'help' => 'workflow.dest by email help',

View File

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

View File

@@ -27,13 +27,6 @@ class EntityWorkflowRepository implements ObjectRepository
$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
{
$qb = $this->buildQueryByDest($user)->select('count(ew)');
@@ -110,19 +103,6 @@ class EntityWorkflowRepository implements ObjectRepository
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
{
$qb = $this->buildQueryByDest($user)->select('ew');
@@ -185,25 +165,6 @@ class EntityWorkflowRepository implements ObjectRepository
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
{
$qb = $this->repository->createQueryBuilder('ew');

View File

@@ -1,25 +1,88 @@
<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_cc') }}</div>
<my-workflows-table :workflows="workflowsCc" />
<div class="alert alert-light">{{ $t('my_workflows.description') }}</div>
<span v-if="noResults" 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 { mapState } from "vuex";
import MyWorkflowsTable from './MyWorkflowsTable.vue';
import { mapState, mapGetters } from "vuex";
import TabTable from "./TabTable";
import OnTheFly from 'ChillMainAssets/vuejs/OnTheFly/components/OnTheFly';
export default {
name: "MyWorkflows",
components: {
MyWorkflowsTable
TabTable,
OnTheFly
},
computed: {
...mapState([
'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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -81,15 +81,6 @@
</ul>
{% 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 %}
<p><b>{{ 'workflow.An access key was also sent to those addresses'|trans }}&nbsp;:</b></p>
<ul>

View File

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

View File

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

View File

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

View File

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

View File

@@ -826,20 +826,4 @@ paths:
$ref: '#/components/schemas/Workflow'
403:
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

@@ -1,39 +0,0 @@
<?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,7 +447,6 @@ workflow:
Created by: Créé par
My decision: Ma décision
Next step: Prochaine étape
cc for next steps: Utilisateurs en copie
dest for next steps: Utilisateurs qui valideront la prochaine étape
Freeze: Geler
Freezed: Gelé
@@ -466,14 +465,12 @@ workflow:
Document (n°%doc%): "Document (n°%doc%)"
Work (n°%w%): "Action d'accompagnement (n°%w%)"
subscribed: Workflows suivis
cc: Workflows dont je suis en copie
dest: Workflows en attente d'action
you subscribed to all steps: Vous recevrez une notification à chaque étape
you subscribed to final step: Vous recevrez une notification à l'étape finale
Current step: Étape actuelle
Comment on last change: Commentaire à la transition précédente
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é
Delete workflow ?: Supprimer le workflow ?
Are you sure you want to delete this workflow ?: Êtes-vous sûr·e de vouloir supprimer ce workflow ?
@@ -490,7 +487,6 @@ workflow:
Previous transitionned: Anciens workflows
Previous workflow transitionned help: Workflows où vous avez exécuté une action.
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.
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
@@ -525,9 +521,7 @@ notification:
list: Notifications
Sent: Envoyé
to: À
cc: Cc
sent_to: Destinataire(s)
sent_cc: En copie
from: De
received_from: Expéditeur
you were notified by %sender%: Vous avez été notifié par %sender%

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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