Merge branch 'master' into upgrade-php82

This commit is contained in:
Julien Fastré 2023-03-28 11:11:51 +02:00
commit 27dbdbcd96
Signed by: julienfastre
GPG Key ID: BDE2190974723FCB
30 changed files with 419 additions and 139 deletions

View File

@ -34,6 +34,7 @@ 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;
@ -64,6 +65,8 @@ 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,
@ -74,7 +77,8 @@ 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;
@ -86,6 +90,7 @@ 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
@ -133,6 +138,24 @@ 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

@ -95,6 +95,16 @@ class AccompanyingCourseDocumentWorkflowHandler implements EntityWorkflowHandler
return null; return null;
} }
public function getSuggestedUsers(EntityWorkflow $entityWorkflow): array
{
$suggestedUsers = $entityWorkflow->getUsersInvolved();
$referrer = $this->getRelatedEntity($entityWorkflow)->getCourse()->getUser();
$suggestedUsers[spl_object_hash($referrer)] = $referrer;
return $suggestedUsers;
}
public function getTemplate(EntityWorkflow $entityWorkflow, array $options = []): string public function getTemplate(EntityWorkflow $entityWorkflow, array $options = []): string
{ {
return '@ChillDocStore/AccompanyingCourseDocument/_workflow.html.twig'; return '@ChillDocStore/AccompanyingCourseDocument/_workflow.html.twig';

View File

@ -30,6 +30,7 @@ use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Core\Exception\AccessDeniedException; use Symfony\Component\Security\Core\Exception\AccessDeniedException;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Validator\Validator\ValidatorInterface; use Symfony\Component\Validator\Validator\ValidatorInterface;
use Symfony\Component\Workflow\Registry; use Symfony\Component\Workflow\Registry;
use Symfony\Component\Workflow\TransitionBlocker; use Symfony\Component\Workflow\TransitionBlocker;
@ -48,11 +49,13 @@ class WorkflowController extends AbstractController
private Registry $registry; private Registry $registry;
private Security $security;
private TranslatorInterface $translator; private TranslatorInterface $translator;
private ValidatorInterface $validator; private ValidatorInterface $validator;
public function __construct(EntityWorkflowManager $entityWorkflowManager, EntityWorkflowRepository $entityWorkflowRepository, ValidatorInterface $validator, PaginatorFactory $paginatorFactory, Registry $registry, EntityManagerInterface $entityManager, TranslatorInterface $translator) public function __construct(EntityWorkflowManager $entityWorkflowManager, EntityWorkflowRepository $entityWorkflowRepository, ValidatorInterface $validator, PaginatorFactory $paginatorFactory, Registry $registry, EntityManagerInterface $entityManager, TranslatorInterface $translator, Security $security)
{ {
$this->entityWorkflowManager = $entityWorkflowManager; $this->entityWorkflowManager = $entityWorkflowManager;
$this->entityWorkflowRepository = $entityWorkflowRepository; $this->entityWorkflowRepository = $entityWorkflowRepository;
@ -61,6 +64,7 @@ class WorkflowController extends AbstractController
$this->registry = $registry; $this->registry = $registry;
$this->entityManager = $entityManager; $this->entityManager = $entityManager;
$this->translator = $translator; $this->translator = $translator;
$this->security = $security;
} }
/** /**
@ -291,10 +295,18 @@ class WorkflowController extends AbstractController
if (count($workflow->getEnabledTransitions($entityWorkflow)) > 0) { if (count($workflow->getEnabledTransitions($entityWorkflow)) > 0) {
// possible transition // possible transition
$usersInvolved = $entityWorkflow->getUsersInvolved();
$currentUserFound = array_search($this->security->getUser(), $usersInvolved, true);
if (false !== $currentUserFound) {
unset($usersInvolved[$currentUserFound]);
}
$transitionForm = $this->createForm( $transitionForm = $this->createForm(
WorkflowStepType::class, WorkflowStepType::class,
$entityWorkflow->getCurrentStep(), $entityWorkflow->getCurrentStep(),
['transition' => true, 'entity_workflow' => $entityWorkflow] ['transition' => true, 'entity_workflow' => $entityWorkflow, 'suggested_users' => $usersInvolved]
); );
$transitionForm->handleRequest($request); $transitionForm->handleRequest($request);

View File

@ -348,6 +348,23 @@ class EntityWorkflow implements TrackCreationInterface, TrackUpdateInterface
return $this->transitionningStep; return $this->transitionningStep;
} }
/**
* @return User[]
*/
public function getUsersInvolved(): array
{
$usersInvolved = [];
$usersInvolved[spl_object_hash($this->getCreatedBy())] = $this->getCreatedBy();
foreach ($this->steps as $step) {
foreach ($step->getDestUser() as $u) {
$usersInvolved[spl_object_hash($u)] = $u;
}
}
return $usersInvolved;
}
public function getWorkflowName(): string public function getWorkflowName(): string
{ {
return $this->workflowName; return $this->workflowName;

View File

@ -56,12 +56,7 @@ class CommentType extends AbstractType
public function buildView(FormView $view, FormInterface $form, array $options) public function buildView(FormView $view, FormInterface $form, array $options)
{ {
$view->vars = array_replace( $view->vars['fullWidth'] = true;
$view->vars,
[
'fullWidth' => true,
]
);
} }
public function configureOptions(OptionsResolver $resolver) public function configureOptions(OptionsResolver $resolver)

View File

@ -19,6 +19,7 @@ use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormView; use Symfony\Component\Form\FormView;
use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
use Symfony\Component\Serializer\SerializerInterface; use Symfony\Component\Serializer\SerializerInterface;
/** /**
@ -28,12 +29,15 @@ class PickUserDynamicType extends AbstractType
{ {
private DenormalizerInterface $denormalizer; private DenormalizerInterface $denormalizer;
private NormalizerInterface $normalizer;
private SerializerInterface $serializer; private SerializerInterface $serializer;
public function __construct(DenormalizerInterface $denormalizer, SerializerInterface $serializer) public function __construct(DenormalizerInterface $denormalizer, SerializerInterface $serializer, NormalizerInterface $normalizer)
{ {
$this->denormalizer = $denormalizer; $this->denormalizer = $denormalizer;
$this->serializer = $serializer; $this->serializer = $serializer;
$this->normalizer = $normalizer;
} }
public function buildForm(FormBuilderInterface $builder, array $options) public function buildForm(FormBuilderInterface $builder, array $options)
@ -46,6 +50,11 @@ class PickUserDynamicType extends AbstractType
$view->vars['multiple'] = $options['multiple']; $view->vars['multiple'] = $options['multiple'];
$view->vars['types'] = ['user']; $view->vars['types'] = ['user'];
$view->vars['uniqid'] = uniqid('pick_user_dyn'); $view->vars['uniqid'] = uniqid('pick_user_dyn');
$view->vars['suggested'] = [];
foreach ($options['suggested'] as $user) {
$view->vars['suggested'][] = $this->normalizer->normalize($user, 'json', ['groups' => 'read']);
}
} }
public function configureOptions(OptionsResolver $resolver) public function configureOptions(OptionsResolver $resolver)
@ -53,7 +62,8 @@ class PickUserDynamicType extends AbstractType
$resolver $resolver
->setDefault('multiple', false) ->setDefault('multiple', false)
->setAllowedTypes('multiple', ['bool']) ->setAllowedTypes('multiple', ['bool'])
->setDefault('compound', false); ->setDefault('compound', false)
->setDefault('suggested', []);
} }
public function getBlockPrefix() public function getBlockPrefix()

View File

@ -39,7 +39,7 @@ class PrivateCommentType extends AbstractType
$builder $builder
->add('comments', ChillTextareaType::class, [ ->add('comments', ChillTextareaType::class, [
'disable_editor' => $options['disable_editor'], 'disable_editor' => $options['disable_editor'],
'label' => false, 'label' => $options['label'],
]) ])
->setDataMapper($this->dataMapper); ->setDataMapper($this->dataMapper);
} }

View File

@ -95,12 +95,7 @@ class ScopePickerType extends AbstractType
public function buildView(FormView $view, FormInterface $form, array $options) public function buildView(FormView $view, FormInterface $form, array $options)
{ {
$view->vars = array_replace( $view->vars['fullWidth'] = true;
$view->vars,
[
'fullWidth' => true,
]
);
} }
public function configureOptions(OptionsResolver $resolver) public function configureOptions(OptionsResolver $resolver)

View File

@ -154,6 +154,7 @@ class WorkflowStepType extends AbstractType
'label' => 'workflow.dest for next steps', 'label' => 'workflow.dest for next steps',
'multiple' => true, 'multiple' => true,
'mapped' => false, 'mapped' => false,
'suggested' => $options['suggested_users'],
]) ])
->add('future_dest_emails', ChillCollectionType::class, [ ->add('future_dest_emails', ChillCollectionType::class, [
'label' => 'workflow.dest by email', 'label' => 'workflow.dest by email',
@ -200,6 +201,7 @@ class WorkflowStepType extends AbstractType
->setAllowedTypes('transition', 'bool') ->setAllowedTypes('transition', 'bool')
->setRequired('entity_workflow') ->setRequired('entity_workflow')
->setAllowedTypes('entity_workflow', EntityWorkflow::class) ->setAllowedTypes('entity_workflow', EntityWorkflow::class)
->setDefault('suggested_users', [])
->setDefault('constraints', [ ->setDefault('constraints', [
new Callback( new Callback(
function ($step, ExecutionContextInterface $context, $payload) { function ($step, ExecutionContextInterface $context, $payload) {

View File

@ -23,7 +23,7 @@ function loadDynamicPicker(element) {
(input.value === '[]' || input.value === '') ? (input.value === '[]' || input.value === '') ?
null : [ JSON.parse(input.value) ] null : [ JSON.parse(input.value) ]
) )
; suggested = JSON.parse(el.dataset.suggested)
if (!isMultiple) { if (!isMultiple) {
if (input.value === '[]'){ if (input.value === '[]'){
@ -37,6 +37,7 @@ function loadDynamicPicker(element) {
':types="types" ' + ':types="types" ' +
':picked="picked" ' + ':picked="picked" ' +
':uniqid="uniqid" ' + ':uniqid="uniqid" ' +
':suggested="notPickedSuggested" ' +
'@addNewEntity="addNewEntity" ' + '@addNewEntity="addNewEntity" ' +
'@removeEntity="removeEntity"></pick-entity>', '@removeEntity="removeEntity"></pick-entity>',
components: { components: {
@ -48,16 +49,31 @@ function loadDynamicPicker(element) {
types: JSON.parse(el.dataset.types), types: JSON.parse(el.dataset.types),
picked: picked === null ? [] : picked, picked: picked === null ? [] : picked,
uniqid: el.dataset.uniqid, uniqid: el.dataset.uniqid,
suggested: suggested
} }
}, },
computed: {
notPickedSuggested() {
if (this.multiple) {
const pickedIds = new Set();
for (const p of this.picked) {
pickedIds.add(`${p.type}${p.id}`);
}
return this.suggested.filter(e => !pickedIds.has(`${e.type}${e.id}`))
}
return this.suggested.filter(e => e.type !== this.picked.type && e.id !== e.picked.id);
}
},
methods: { methods: {
addNewEntity(entity) { addNewEntity({entity}) {
if (this.multiple) { if (this.multiple) {
if (!this.picked.some(el => { if (!this.picked.some(el => {
return el.type === entity.type && el.id === entity.id; return el.type === entity.type && el.id === entity.id;
})) { })) {
this.picked.push(entity); this.picked.push(entity);
input.value = JSON.stringify(this.picked); input.value = JSON.stringify(this.picked);
console.log(entity)
} }
} else { } else {
if (!this.picked.some(el => { if (!this.picked.some(el => {
@ -69,7 +85,10 @@ function loadDynamicPicker(element) {
} }
} }
}, },
removeEntity(entity) { removeEntity({entity}) {
if (-1 === this.suggested.findIndex(e => e.type === entity.type && e.id === entity.id)) {
this.suggested.push(entity);
}
this.picked = this.picked.filter(e => !(e.type === entity.type && e.id === entity.id)); this.picked = this.picked.filter(e => !(e.type === entity.type && e.id === entity.id));
input.value = JSON.stringify(this.picked); input.value = JSON.stringify(this.picked);
}, },

View File

@ -17,6 +17,9 @@
</add-persons> </add-persons>
</li> </li>
</ul> </ul>
<ul class="list-suggest add-items inline">
<li v-for="s in suggested" :key="s.id" @click="addNewSuggested(s)"><span>{{ s.text }}</span></li>
</ul>
</template> </template>
<script> <script>
@ -49,6 +52,10 @@ export default {
// display picked entities. // display picked entities.
type: Boolean, type: Boolean,
default: true, default: true,
},
suggested: {
type: Array,
default: []
} }
}, },
emits: ['addNewEntity', 'removeEntity'], emits: ['addNewEntity', 'removeEntity'],
@ -61,55 +68,58 @@ export default {
}; };
}, },
computed: { computed: {
addPersonsOptions() { addPersonsOptions() {
return { return {
uniq: !this.multiple, uniq: !this.multiple,
type: this.types, type: this.types,
priority: null, priority: null,
button: { button: {
size: 'btn-sm', size: 'btn-sm',
class: 'btn-submit', class: 'btn-submit',
}, },
}; };
}, },
translatedListOfTypes() { translatedListOfTypes() {
let trans = []; let trans = [];
this.types.forEach(t => { this.types.forEach(t => {
if (this.$props.multiple) { if (this.$props.multiple) {
trans.push(appMessages.fr.pick_entity[t].toLowerCase()); trans.push(appMessages.fr.pick_entity[t].toLowerCase());
} else { } else {
trans.push(appMessages.fr.pick_entity[t + '_one'].toLowerCase()); trans.push(appMessages.fr.pick_entity[t + '_one'].toLowerCase());
} }
}) })
if (this.$props.multiple) { if (this.$props.multiple) {
return appMessages.fr.pick_entity.modal_title + trans.join(', '); return appMessages.fr.pick_entity.modal_title + trans.join(', ');
} else { } else {
return appMessages.fr.pick_entity.modal_title_one + trans.join(', '); return appMessages.fr.pick_entity.modal_title_one + trans.join(', ');
} }
}, },
listClasses() { listClasses() {
return { return {
'list-suggest': true, 'list-suggest': true,
'remove-items': this.$props.removableIfSet, 'remove-items': this.$props.removableIfSet,
}; };
}, },
}, },
methods: { methods: {
addNewEntity({ selected, modal }) { addNewSuggested(entity) {
selected.forEach((item) => { this.$emit('addNewEntity', {entity: entity});
this.$emit('addNewEntity', item.result); },
}, this addNewEntity({ selected, modal }) {
); selected.forEach((item) => {
this.$refs.addPersons.resetSearch(); // to cast child method this.$emit('addNewEntity', { entity: item.result});
modal.showModal = false; }, this
}, );
removeEntity(entity) { this.$refs.addPersons.resetSearch(); // to cast child method
if (!this.$props.removableIfSet) { modal.showModal = false;
return; },
} removeEntity(entity) {
this.$emit('removeEntity', entity); if (!this.$props.removableIfSet) {
} return;
}
this.$emit('removeEntity',{ entity: entity });
}
}, },
} }
</script> </script>

View File

@ -68,7 +68,8 @@
{{- form_errors(form) -}} {{- form_errors(form) -}}
</div> </div>
{% else %} {% else %}
<div class="col-sm"> <div class="col-12 clear">{{- form_label(form) -}}</div>
<div class="col-sm-12">
{{- form_widget(form, widget_attr) -}} {{- form_widget(form, widget_attr) -}}
{{- form_help(form) -}} {{- form_help(form) -}}
{{- form_errors(form) -}} {{- form_errors(form) -}}

View File

@ -18,43 +18,48 @@
{% block form_row %} {% block form_row %}
{% apply spaceless %} {% apply spaceless %}
{% if form.vars.fullWidth is not defined or form.vars.fullWidth == false %}
<div class="mb-2"> <div class="mb-2">
<div class="row"> <div class="row">
<div class="{% apply spaceless %} {% if form.vars.fullWidth is not defined or form.vars.fullWidth == false %}
{% if attr.class is defined and ('cf-title' in attr.class or 'cf-fields' in attr.class ) %} <div class="{% apply spaceless %}
col-sm-12 {% if attr.class is defined and ('cf-title' in attr.class or 'cf-fields' in attr.class ) %}
{% elseif attr.class is defined and 'multiple-cf-inline' in attr.class %} col-sm-12
col-sm-2 col-md-4 clear {% elseif attr.class is defined and 'multiple-cf-inline' in attr.class %}
{% else %} col-sm-2 col-md-4 clear
col-sm-4 clear {% else %}
col-sm-4 clear
{% endif %}
{% endapply %}">
{% if attr.class is not defined or ('cf-title' not in attr.class and 'cf-fields' not in attr.class ) %}
{{ form_label(form) }}
{% endif %} {% endif %}
{% endapply %}"> </div>
{% if attr.class is not defined or ('cf-title' not in attr.class and 'cf-fields' not in attr.class ) %} <div class="{% apply spaceless %}
{{ form_label(form) }} {% if attr.class is defined and 'cf-title' in attr.class %}
col-sm-12
{% elseif attr.class is defined and 'cf-fields' in attr.class %}
col-sm-12 parent
{% elseif attr.class is defined and 'multiple-cf-inline' in attr.class %}
col-sm-2 col-md-8 multiple-cf-inline
{% else %}
col-sm-8
{% endif %}
{% endapply %}">
{{ form_widget(form) }}
{{ form_errors(form) }}
</div>
{% else %}
<div class="col-12 clear">{{ form_label(form) }}</div>
<div class="col-12">{{ form_widget(form) }}</div>
{% endif %} {% endif %}
</div>
<div class="{% apply spaceless %}
{% if attr.class is defined and 'cf-title' in attr.class %}
col-sm-12
{% elseif attr.class is defined and 'cf-fields' in attr.class %}
col-sm-12 parent
{% elseif attr.class is defined and 'multiple-cf-inline' in attr.class %}
col-sm-2 col-md-8 multiple-cf-inline
{% else %}
col-sm-8
{% endif %}
{% endapply %}">
{{ form_widget(form) }}
{{ form_errors(form) }}
</div>
</div> </div>
</div> </div>
{% else %}
{{ form_widget(form) }}
{% endif %}
{% endapply %} {% endapply %}
{% endblock form_row %} {% endblock form_row %}
{#
The block 'form_row' above may be removed !
Read this note: https://gitlab.com/Chill-Projet/chill-bundles/-/merge_requests/502#note_1311993084
#}
{% block choice_widget_expanded %} {% block choice_widget_expanded %}
{% apply spaceless %} {% apply spaceless %}
@ -200,7 +205,6 @@
{% block private_comment_row %} {% block private_comment_row %}
{{ form_label(form) }}
{{ form_row(form) }} {{ form_row(form) }}
{% endblock %} {% endblock %}
@ -211,7 +215,6 @@
{% endblock %} {% endblock %}
{% block comment_row %} {% block comment_row %}
{{ form_label(form) }}
{{ form_row(form) }} {{ form_row(form) }}
{% endblock %} {% endblock %}
@ -249,7 +252,11 @@
{% block pick_entity_dynamic_widget %} {% block pick_entity_dynamic_widget %}
<input type="hidden" {{ block('widget_attributes') }} {% if value is not empty %}value="{{ value|escape('html_attr') }}" {% endif %} data-input-uniqid="{{ form.vars['uniqid'] }}"/> <input type="hidden" {{ block('widget_attributes') }} {% if value is not empty %}value="{{ value|escape('html_attr') }}" {% endif %} data-input-uniqid="{{ form.vars['uniqid'] }}"/>
<div data-module="pick-dynamic" data-types="{{ form.vars['types']|json_encode }}" data-multiple="{{ form.vars['multiple'] }}" data-uniqid="{{ form.vars['uniqid'] }}"></div> <div data-module="pick-dynamic"
data-types="{{ form.vars['types']|json_encode }}"
data-multiple="{{ form.vars['multiple'] }}"
data-uniqid="{{ form.vars['uniqid'] }}"
data-suggested="{{ form.vars['suggested']|json_encode|escape('html_attr') }}"></div>
{% endblock %} {% endblock %}
{% block pick_postal_code_widget %} {% block pick_postal_code_widget %}
@ -269,4 +276,4 @@
{{ form_errors(form.fixedDate) }} {{ form_errors(form.fixedDate) }}
</div> </div>
</div> </div>
{% endblock %} {% endblock %}

View File

@ -36,6 +36,11 @@ interface EntityWorkflowHandlerInterface
*/ */
public function getRoleShow(EntityWorkflow $entityWorkflow): ?string; public function getRoleShow(EntityWorkflow $entityWorkflow): ?string;
/**
* @return User[]
*/
public function getSuggestedUsers(EntityWorkflow $entityWorkflow): array;
public function getTemplate(EntityWorkflow $entityWorkflow, array $options = []): string; public function getTemplate(EntityWorkflow $entityWorkflow, array $options = []): string;
public function getTemplateData(EntityWorkflow $entityWorkflow, array $options = []): array; public function getTemplateData(EntityWorkflow $entityWorkflow, array $options = []): array;

View File

@ -45,3 +45,12 @@ workflow:
few {# workflows} few {# workflows}
other {# workflows} other {# workflows}
} }
duration:
minute: >-
{m, plural,
=0 {Aucune durée}
one {# minute}
few {# minutes}
other {# minutes}
}

View File

@ -160,6 +160,14 @@ class AccompanyingPeriodWorkEvaluation implements TrackCreationInterface, TrackU
*/ */
private ?DateInterval $warningInterval = null; private ?DateInterval $warningInterval = null;
/**
* @ORM\Column(type="integer", nullable=true)
* @Serializer\Groups({"read", "docgen:read"})
* @Serializer\Groups({"write"})
* @Serializer\Groups({"accompanying_period_work_evaluation:create"})
*/
private ?int $timeSpent = null;
public function __construct() public function __construct()
{ {
$this->documents = new ArrayCollection(); $this->documents = new ArrayCollection();
@ -265,6 +273,11 @@ class AccompanyingPeriodWorkEvaluation implements TrackCreationInterface, TrackU
return $this->warningInterval; return $this->warningInterval;
} }
public function getTimeSpent(): ?int
{
return $this->timeSpent;
}
public function removeDocument(AccompanyingPeriodWorkEvaluationDocument $document): self public function removeDocument(AccompanyingPeriodWorkEvaluationDocument $document): self
{ {
$this->documents->removeElement($document); $this->documents->removeElement($document);
@ -322,6 +335,13 @@ class AccompanyingPeriodWorkEvaluation implements TrackCreationInterface, TrackU
return $this; return $this;
} }
public function setTimeSpent(?int $timeSpent): self
{
$this->timeSpent = $timeSpent;
return $this;
}
public function setEvaluation(?Evaluation $evaluation): AccompanyingPeriodWorkEvaluation public function setEvaluation(?Evaluation $evaluation): AccompanyingPeriodWorkEvaluation
{ {
if ( if (

View File

@ -47,6 +47,7 @@ class ListEvaluation implements ListInterface, GroupedExportInterface
'endDate', 'endDate',
'maxDate', 'maxDate',
'warningInterval', 'warningInterval',
'timeSpent',
'acpw_id', 'acpw_id',
'acpw_startDate', 'acpw_startDate',
'acpw_endDate', 'acpw_endDate',
@ -295,6 +296,9 @@ class ListEvaluation implements ListInterface, GroupedExportInterface
$qb->addSelect(sprintf('workeval.%s AS %s', $field, $field)); $qb->addSelect(sprintf('workeval.%s AS %s', $field, $field));
} }
// add the time spent field
$qb->addSelect('(workeval.timeSpent / 60) AS timeSpent');
// those with identity // those with identity
foreach (['createdBy', 'updatedBy'] as $field) { foreach (['createdBy', 'updatedBy'] as $field) {
$qb->addSelect(sprintf('IDENTITY(workeval.%s) AS %s', $field, $field)); $qb->addSelect(sprintf('IDENTITY(workeval.%s) AS %s', $field, $field));

View File

@ -19,7 +19,6 @@ use Chill\PersonBundle\Entity\SocialWork\SocialAction;
use Chill\PersonBundle\Export\Declarations; use Chill\PersonBundle\Export\Declarations;
use Chill\PersonBundle\Templating\Entity\SocialActionRender; use Chill\PersonBundle\Templating\Entity\SocialActionRender;
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Query\Expr\Andx;
use Doctrine\ORM\QueryBuilder; use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\CallbackTransformer; use Symfony\Component\Form\CallbackTransformer;
use Symfony\Component\Form\Extension\Core\Type\HiddenType; use Symfony\Component\Form\Extension\Core\Type\HiddenType;
@ -52,45 +51,39 @@ class SocialWorkTypeFilter implements FilterInterface
public function alterQuery(QueryBuilder $qb, $data) public function alterQuery(QueryBuilder $qb, $data)
{ {
$where = $qb->getDQLPart('where');
if (count($data['actionType']) > 0) { if (count($data['actionType']) > 0) {
$clause = $qb->expr()->in('acpw.socialAction', ':actionType'); $qb
->andWhere($qb->expr()->in('acpw.socialAction', ':actionType'))
if ($where instanceof Andx) { ->setParameter('actionType', $data['actionType']);
$where->add($clause);
} else {
$where = $qb->expr()->andX($clause);
}
$qb->setParameter('actionType', $data['actionType']);
} }
if (count($data['goal']) > 0) { if (count($data['goal']) > 0) {
if (!in_array('goal', $qb->getAllAliases(), true)) { if (!in_array('acpw_goal', $qb->getAllAliases(), true)) {
$qb->join('acpw.goals', 'goal'); $qb->join('acpw.goals', 'acpw_goal');
} }
$where->add( $orX = $qb->expr()->orX();
$qb->expr()->in('goal.id', ':goals') foreach ($data['goal'] as $goal) {
);
$qb->setParameter('goals', $data['goal']); /** @var Goal $goal */
} $andX = $qb->expr()->andX();
$andX->add($qb->expr()->eq('acpw_goal.goal', $goalId = ':goal_'.uniqid()));
$qb->setParameter($goalId, $goal);
if (count($data['result']) > 0) { if (count($data['result']) > 0) {
if (!in_array('result', $qb->getAllAliases(), true)) { $orXResult = $qb->expr()->orX();
$qb->join('acpw.results', 'result'); foreach ($data['result'] as $result) {
/** @var Result $result */
$orXResult->add($qb->expr()->isMemberOf($resultId = ':result_'.uniqid(), 'acpw_goal.results'));
$qb->setParameter($resultId, $result);
}
$andX->add($orXResult);
}
$orX->add($andX);
} }
$qb->andWhere($orX);
$where->add(
$qb->expr()->in('result.id', ':results')
);
$qb->setParameter('results', $data['result']);
} }
$qb->add('where', $where);
} }
public function applyOn(): string public function applyOn(): string

View File

@ -18,21 +18,25 @@ use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormView; use Symfony\Component\Form\FormView;
use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
use Symfony\Component\Serializer\SerializerInterface; use Symfony\Component\Serializer\SerializerInterface;
/** /**
* Pick user dymically, using vuejs module "AddPerson". * m* Pick user dymically, using vuejs module "AddPerson".
*/ */
class PickPersonDynamicType extends AbstractType class PickPersonDynamicType extends AbstractType
{ {
private DenormalizerInterface $denormalizer; private DenormalizerInterface $denormalizer;
private DenormalizerInterface $normalizer;
private SerializerInterface $serializer; private SerializerInterface $serializer;
public function __construct(DenormalizerInterface $denormalizer, SerializerInterface $serializer) public function __construct(DenormalizerInterface $denormalizer, SerializerInterface $serializer, NormalizerInterface $normalizer)
{ {
$this->denormalizer = $denormalizer; $this->denormalizer = $denormalizer;
$this->serializer = $serializer; $this->serializer = $serializer;
$this->normalizer = $normalizer;
} }
public function buildForm(FormBuilderInterface $builder, array $options) public function buildForm(FormBuilderInterface $builder, array $options)
@ -45,6 +49,11 @@ class PickPersonDynamicType extends AbstractType
$view->vars['multiple'] = $options['multiple']; $view->vars['multiple'] = $options['multiple'];
$view->vars['types'] = ['person']; $view->vars['types'] = ['person'];
$view->vars['uniqid'] = uniqid('pick_user_dyn'); $view->vars['uniqid'] = uniqid('pick_user_dyn');
$view->vars['suggested'] = [];
foreach ($options['suggested'] as $person) {
$view->vars['suggested'][] = $this->normalizer->normalize($person, 'json', ['groups' => 'read']);
}
} }
public function configureOptions(OptionsResolver $resolver) public function configureOptions(OptionsResolver $resolver)
@ -52,7 +61,8 @@ class PickPersonDynamicType extends AbstractType
$resolver $resolver
->setDefault('multiple', false) ->setDefault('multiple', false)
->setAllowedTypes('multiple', ['bool']) ->setAllowedTypes('multiple', ['bool'])
->setDefault('compound', false); ->setDefault('compound', false)
->setDefault('suggested', []);
} }
public function getBlockPrefix() public function getBlockPrefix()

View File

@ -49,6 +49,20 @@
</div> </div>
</div> </div>
<div class="row mb-3">
<label class="col-4 col-sm-2 col-md-4 col-lg-2 col-form-label">
{{ $t('evaluation_time_spent') }}
</label>
<div class="col-8 col-sm-4 col-md-8 col-lg-4">
<select class="form-control form-control-sm" type="time" v-model="timeSpent">
<option disabled value="">{{ $t('select_time_spent') }}</option>
<option v-for="time in timeSpentChoices" :value="time.value">
{{ time.text }}
</option>
</select>
</div>
</div>
<div class="row mb-3"> <div class="row mb-3">
<label class="col-sm-4 col-form-label visually-hidden">{{ $t('evaluation_public_comment') }}</label> <label class="col-sm-4 col-form-label visually-hidden">{{ $t('evaluation_public_comment') }}</label>
<div class="col-sm-12"> <div class="col-sm-12">
@ -191,6 +205,8 @@ const i18n = {
evaluation_choose_a_template: "Choisir un modèle", evaluation_choose_a_template: "Choisir un modèle",
evaluation_add_a_document: "Ajouter un document", evaluation_add_a_document: "Ajouter un document",
evaluation_add: "Ajouter une évaluation", evaluation_add: "Ajouter une évaluation",
evaluation_time_spent: "Temps de rédaction",
select_time_spent: "Indiquez le temps de rédaction",
Documents: "Documents", Documents: "Documents",
document_add: "Générer ou téléverser un document", document_add: "Générer ou téléverser un document",
document_upload: "Téléverser un document", document_upload: "Téléverser un document",
@ -224,6 +240,22 @@ export default {
maxPostSize: 15000000, maxPostSize: 15000000,
required: false, required: false,
}, },
timeSpentChoices: [
{ text: '1 minute', value: 60 }, { text: '2 minutes', value: 120 },
{ text: '3 minutes', value: 180 }, { text: '4 minutes', value: 240 },
{ text: '5 minutes', value: 300 }, { text: '10 minutes', value: 600 },
{ text: '15 minutes', value: 900 },{ text: '20 minutes', value: 1200 },
{ text: '25 minutes', value: 1500 }, { text: '30 minutes', value: 1800 },
{ text: '45 minutes', value: 2700 },{ text: '1 hour', value: 3600 },
{ text: '1 hour 15 minutes', value: 4500 }, { text: '1 hour 30 minutes', value: 5400 },
{ text: '1 hour 45 minutes', value: 6300 }, { text: '2 hours', value: 7200 },
{ text: '2 hours 30 minutes', value: 9000 }, { text: '3 hours', value: 10800 },
{ text: '3 hours 30 minutes', value: 12600 },{ text: '4 hours', value: 14400 },
{ text: '4 hours 30 minutes', value: 16200 },{ text: '5 hours', value: 18000 },
{ text: '5 hours 30 minutes', value: 19800 },{ text: '6 hours', value: 21600 },
{ text: '6 hours 30 minutes', value: 23400 },{ text: '7 hours', value: 25200 },
{ text: '7 hours 30 minutes', value: 27000 },{ text: '8 hours', value: 28800 },
]
} }
}, },
computed: { computed: {
@ -265,6 +297,10 @@ export default {
get() { return this.evaluation.warningInterval; }, get() { return this.evaluation.warningInterval; },
set(v) { this.$store.commit('setEvaluationWarningInterval', { key: this.evaluation.key, days: v }); } set(v) { this.$store.commit('setEvaluationWarningInterval', { key: this.evaluation.key, days: v }); }
}, },
timeSpent: {
get() { return this.evaluation.timeSpent },
set(v) { this.$store.commit('setEvaluationTimeSpent', { key: this.evaluation.key, time: v}) }
},
comment: { comment: {
get() { return this.evaluation.comment; }, get() { return this.evaluation.comment; },
set(v) { this.$store.commit('setEvaluationComment', { key: this.evaluation.key, comment: v }); } set(v) { this.$store.commit('setEvaluationComment', { key: this.evaluation.key, comment: v }); }

View File

@ -116,6 +116,7 @@ const store = createStore({
endDate: e.endDate === null || e.endDate === '' ? null : { datetime: datetimeToISO(ISOToDate(e.endDate)) }, endDate: e.endDate === null || e.endDate === '' ? null : { datetime: datetimeToISO(ISOToDate(e.endDate)) },
maxDate: e.maxDate === null || e.maxDate === '' ? null : { datetime: datetimeToISO(ISOToDate(e.maxDate)) }, maxDate: e.maxDate === null || e.maxDate === '' ? null : { datetime: datetimeToISO(ISOToDate(e.maxDate)) },
warningInterval: intervalDaysToISO(e.warningInterval), warningInterval: intervalDaysToISO(e.warningInterval),
timeSpent: e.timeSpent,
comment: e.comment, comment: e.comment,
documents: e.documents documents: e.documents
}; };
@ -138,6 +139,7 @@ const store = createStore({
endDate: e.endDate !== null ? dateToISO(new Date(e.endDate.datetime)) : null, endDate: e.endDate !== null ? dateToISO(new Date(e.endDate.datetime)) : null,
maxDate: e.maxDate !== null ? dateToISO(new Date(e.maxDate.datetime)) : null, maxDate: e.maxDate !== null ? dateToISO(new Date(e.maxDate.datetime)) : null,
warningInterval: e.warningInterval !== null ? intervalISOToDays(e.warningInterval) : null, warningInterval: e.warningInterval !== null ? intervalISOToDays(e.warningInterval) : null,
timeSpent: e.timeSpent !== null ? e.timeSpent : null,
documents: e.documents.map((d, docIndex) => { documents: e.documents.map((d, docIndex) => {
return Object.assign(d, { return Object.assign(d, {
key: docIndex key: docIndex
@ -258,6 +260,7 @@ const store = createStore({
endDate: null, endDate: null,
maxDate: null, maxDate: null,
warningInterval: null, warningInterval: null,
timeSpent: null,
comment: "", comment: "",
editEvaluation: true, editEvaluation: true,
workflows_availables: state.work.workflows_availables_evaluation, workflows_availables: state.work.workflows_availables_evaluation,
@ -286,6 +289,10 @@ const store = createStore({
state.evaluationsPicked.find(e => e.key === key) state.evaluationsPicked.find(e => e.key === key)
.warningInterval = days; .warningInterval = days;
}, },
setEvaluationTimeSpent(state, {key, time}) {
state.evaluationsPicked.find(e => e.key === key)
.timeSpent = time;
},
setEvaluationComment(state, {key, comment}) { setEvaluationComment(state, {key, comment}) {
state.evaluationsPicked.find(e => e.key === key) state.evaluationsPicked.find(e => e.key === key)
.comment = comment; .comment = comment;
@ -482,7 +489,7 @@ const store = createStore({
; ;
commit('setIsPosting', true); commit('setIsPosting', true);
console.log('payload', payload); console.log('the social action', payload);
return makeFetch('PUT', url, payload) return makeFetch('PUT', url, payload)
.then(data => { .then(data => {

View File

@ -118,6 +118,18 @@
</li> </li>
{% endif %} {% endif %}
{% endif %} {% endif %}
{% if e.timeSpent is not null and e.timeSpent > 0 %}
<li>
{% set minutes = (e.timeSpent / 60) %}
<span class="item-key">{{ 'accompanying_course_work.timeSpent'|trans ~ ' : ' }}</span> {{ 'duration.minute'|trans({ '{m}' : minutes }) }}
</li>
{% elseif displayContent is defined and displayContent == 'long' %}
<li>
<span class="item-key">{{ 'accompanying_course_work.timeSpent'|trans ~ ' : ' }}</span>
<span class="chill-no-data-statement">{{ 'Not given'|trans }}</span>
</li>
{% endif %}
</ul> </ul>
</li> </li>
</ul> </ul>
@ -143,7 +155,7 @@
{% else %} {% else %}
<span class="chill-no-data-statement">{{ 'No document found'|trans }}</span> <span class="chill-no-data-statement">{{ 'No document found'|trans }}</span>
{% endif %} {% endif %}
{% endif %} {% endif %}
</td> </td>

View File

@ -97,6 +97,21 @@ class AccompanyingPeriodWorkEvaluationDocumentWorkflowHandler implements EntityW
return AccompanyingPeriodWorkEvaluationDocumentVoter::SEE; return AccompanyingPeriodWorkEvaluationDocumentVoter::SEE;
} }
public function getSuggestedUsers(EntityWorkflow $entityWorkflow): array
{
$suggestedUsers = $entityWorkflow->getUsersInvolved();
$referrer = $this->getRelatedEntity($entityWorkflow)
->getAccompanyingPeriodWorkEvaluation()
->getAccompanyingPeriodWork()
->getAccompanyingPeriod()
->getUser();
$suggestedUsers[spl_object_hash($referrer)] = $referrer;
return $suggestedUsers;
}
public function getTemplate(EntityWorkflow $entityWorkflow, array $options = []): string public function getTemplate(EntityWorkflow $entityWorkflow, array $options = []): string
{ {
return '@ChillPerson/Workflow/_evaluation_document.html.twig'; return '@ChillPerson/Workflow/_evaluation_document.html.twig';

View File

@ -87,6 +87,20 @@ class AccompanyingPeriodWorkEvaluationWorkflowHandler implements EntityWorkflowH
return AccompanyingPeriodWorkEvaluationVoter::SEE; return AccompanyingPeriodWorkEvaluationVoter::SEE;
} }
public function getSuggestedUsers(EntityWorkflow $entityWorkflow): array
{
$suggestedUsers = $entityWorkflow->getUsersInvolved();
$referrer = $this->getRelatedEntity($entityWorkflow)
->getAccompanyingPeriodWork()
->getAccompanyingPeriod()
->getUser();
$suggestedUsers[spl_object_hash($referrer)] = $referrer;
return $suggestedUsers;
}
public function getTemplate(EntityWorkflow $entityWorkflow, array $options = []): string public function getTemplate(EntityWorkflow $entityWorkflow, array $options = []): string
{ {
return '@ChillPerson/Workflow/_evaluation.html.twig'; return '@ChillPerson/Workflow/_evaluation.html.twig';

View File

@ -94,6 +94,19 @@ class AccompanyingPeriodWorkWorkflowHandler implements EntityWorkflowHandlerInte
return null; return null;
} }
public function getSuggestedUsers(EntityWorkflow $entityWorkflow): array
{
$suggestedUsers = $entityWorkflow->getUsersInvolved();
$referrer = $this->getRelatedEntity($entityWorkflow)
->getAccompanyingPeriod()
->getUser();
$suggestedUsers[spl_object_hash($referrer)] = $referrer;
return $suggestedUsers;
}
public function getTemplate(EntityWorkflow $entityWorkflow, array $options = []): string public function getTemplate(EntityWorkflow $entityWorkflow, array $options = []): string
{ {
return '@ChillPerson/Workflow/_accompanying_period_work.html.twig'; return '@ChillPerson/Workflow/_accompanying_period_work.html.twig';

View File

@ -0,0 +1,26 @@
<?php
declare(strict_types=1);
namespace Chill\Migrations\Person;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
final class Version20230210104424 extends AbstractMigration
{
public function getDescription(): string
{
return 'Add a duration field to accompanyingPeriodWorkEvaluation';
}
public function up(Schema $schema): void
{
$this->addSql('ALTER TABLE chill_person_accompanying_period_work_evaluation ADD timeSpent INT DEFAULT NULL');
}
public function down(Schema $schema): void
{
$this->addSql('ALTER TABLE chill_person_accompanying_period_work_evaluation DROP timeSpent');
}
}

View File

@ -908,6 +908,7 @@ accompanying_course_work:
remove: Supprimer une action d'accompagnement remove: Supprimer une action d'accompagnement
social_evaluation: Évaluation social_evaluation: Évaluation
private_comment: Commentaire privé private_comment: Commentaire privé
timeSpent: Temps de rédaction
# #
@ -1139,6 +1140,7 @@ export:
updatedAt: Date de modification updatedAt: Date de modification
createdBy: Créé par createdBy: Créé par
updatedBy: Modifié par updatedBy: Modifié par
timeSpent: Temps de rédaction (minutes)
acpw: acpw:
List of accompanying period works: Liste des actions List of accompanying period works: Liste des actions
List description: Génère une liste des actions d'accompagnement, filtrée sur différents paramètres. List description: Génère une liste des actions d'accompagnement, filtrée sur différents paramètres.

View File

@ -18,6 +18,7 @@ use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormView; use Symfony\Component\Form\FormView;
use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
use Symfony\Component\Serializer\SerializerInterface; use Symfony\Component\Serializer\SerializerInterface;
/** /**
@ -27,12 +28,15 @@ class PickThirdpartyDynamicType extends AbstractType
{ {
private DenormalizerInterface $denormalizer; private DenormalizerInterface $denormalizer;
private NormalizerInterface $normalizer;
private SerializerInterface $serializer; private SerializerInterface $serializer;
public function __construct(DenormalizerInterface $denormalizer, SerializerInterface $serializer) public function __construct(DenormalizerInterface $denormalizer, SerializerInterface $serializer, NormalizerInterface $normalizer)
{ {
$this->denormalizer = $denormalizer; $this->denormalizer = $denormalizer;
$this->serializer = $serializer; $this->serializer = $serializer;
$this->normalizer = $normalizer;
} }
public function buildForm(FormBuilderInterface $builder, array $options) public function buildForm(FormBuilderInterface $builder, array $options)
@ -45,6 +49,11 @@ class PickThirdpartyDynamicType extends AbstractType
$view->vars['multiple'] = $options['multiple']; $view->vars['multiple'] = $options['multiple'];
$view->vars['types'] = ['thirdparty']; $view->vars['types'] = ['thirdparty'];
$view->vars['uniqid'] = uniqid('pick_user_dyn'); $view->vars['uniqid'] = uniqid('pick_user_dyn');
$view->vars['suggested'] = [];
foreach ($options['suggested'] as $tp) {
$view->vars['suggested'][] = $this->normalizer->normalize($tp, 'json', ['groups' => 'read']);
}
} }
public function configureOptions(OptionsResolver $resolver) public function configureOptions(OptionsResolver $resolver)
@ -52,7 +61,8 @@ class PickThirdpartyDynamicType extends AbstractType
$resolver $resolver
->setDefault('multiple', false) ->setDefault('multiple', false)
->setAllowedTypes('multiple', ['bool']) ->setAllowedTypes('multiple', ['bool'])
->setDefault('compound', false); ->setDefault('compound', false)
->setDefault('suggested', []);
} }
public function getBlockPrefix() public function getBlockPrefix()

View File

@ -20,6 +20,7 @@ use Chill\WopiBundle\Service\Controller\ResponderInterface;
use Exception; use Exception;
use Lexik\Bundle\JWTAuthenticationBundle\Services\JWTTokenManagerInterface; use Lexik\Bundle\JWTAuthenticationBundle\Services\JWTTokenManagerInterface;
use loophp\psr17\Psr17Interface; use loophp\psr17\Psr17Interface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
@ -81,7 +82,7 @@ final class Editor
$this->router = $router; $this->router = $router;
} }
public function __invoke(string $fileId): Response public function __invoke(string $fileId, Request $request): Response
{ {
if (null === $user = $this->security->getUser()) { if (null === $user = $this->security->getUser()) {
throw new AccessDeniedHttpException('Please authenticate to access this feature'); throw new AccessDeniedHttpException('Please authenticate to access this feature');
@ -151,6 +152,7 @@ final class Editor
UrlGeneratorInterface::ABSOLUTE_URL UrlGeneratorInterface::ABSOLUTE_URL
), ),
'closebutton' => 1, 'closebutton' => 1,
'lang' => $request->getLocale(),
] ]
) )
); );

View File

@ -25,6 +25,7 @@
<form id="office_form" name="office_form" target="office_frame" action="{{ server }}" method="post"> <form id="office_form" name="office_form" target="office_frame" action="{{ server }}" method="post">
<input name="access_token" value="{{ access_token }}" type="hidden" /> <input name="access_token" value="{{ access_token }}" type="hidden" />
<input name="access_token_ttl" value="{{ access_token_ttl }}" type="hidden" /> <input name="access_token_ttl" value="{{ access_token_ttl }}" type="hidden" />
<input name="ui_defaults" value="TextRuler=fase;TextSidebar=false;TextStatusbar=true;PresentationSidebar=false;PresentationStatusbar=false;SpreadsheetSidebar=false;SpreadsheetStatusbar=false;UIMode=tabbed;SaveAsMode=group;" type="hidden" />
</form> </form>
<span id="frameholder"></span> <span id="frameholder"></span>