merge last AddPerson branch

This commit is contained in:
nobohan
2022-02-16 11:23:21 +01:00
105 changed files with 1554 additions and 882 deletions

View File

@@ -12,6 +12,8 @@ declare(strict_types=1);
namespace Chill\MainBundle\Controller;
use Chill\MainBundle\CRUD\Controller\ApiController;
use Chill\MainBundle\Pagination\PaginatorInterface;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\HttpFoundation\Request;
/**
@@ -19,7 +21,7 @@ use Symfony\Component\HttpFoundation\Request;
*/
class LocationApiController extends ApiController
{
public function customizeQuery(string $action, Request $request, $query): void
protected function customizeQuery(string $action, Request $request, $query): void
{
$query
->leftJoin('e.locationType', 'lt')
@@ -31,4 +33,14 @@ class LocationApiController extends ApiController
)
);
}
/**
* @param QueryBuilder $query
* @param mixed $_format
*/
protected function orderQuery(string $action, $query, Request $request, PaginatorInterface $paginator, $_format)
{
return $query
->addOrderBy('e.name', 'ASC');
}
}

View File

@@ -236,6 +236,10 @@ class NotificationController extends AbstractController
'_fragment' => 'comment-' . $commentId,
]);
}
if ($editedCommentForm->isSubmitted() && !$editedCommentForm->isValid()) {
$this->addFlash('error', $this->translator->trans('This form contains errors'));
}
}
}
@@ -257,6 +261,10 @@ class NotificationController extends AbstractController
'id' => $notification->getId(),
]);
}
if ($appendCommentForm->isSubmitted() && !$appendCommentForm->isValid()) {
$this->addFlash('error', $this->translator->trans('This form contains errors'));
}
}
}

View File

@@ -167,6 +167,7 @@ class WorkflowController extends AbstractController
$handler = $this->entityWorkflowManager->getHandler($entityWorkflow);
$workflow = $this->registry->get($entityWorkflow, $entityWorkflow->getWorkflowName());
$errors = [];
if (count($workflow->getEnabledTransitions($entityWorkflow)) > 0) {
// possible transition
@@ -245,7 +246,7 @@ class WorkflowController extends AbstractController
'handler_template_data' => $handler->getTemplateData($entityWorkflow),
'transition_form' => isset($transitionForm) ? $transitionForm->createView() : null,
'entity_workflow' => $entityWorkflow,
'transition_form_errors' => $errors ?? [],
'transition_form_errors' => $errors,
//'comment_form' => $commentForm->createView(),
]
);

View File

@@ -18,6 +18,7 @@ use DateTimeInterface;
use Doctrine\ORM\Event\LifecycleEventArgs;
use Doctrine\ORM\Event\PreFlushEventArgs;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* @ORM\Entity
@@ -28,6 +29,7 @@ class NotificationComment implements TrackCreationInterface, TrackUpdateInterfac
{
/**
* @ORM\Column(type="text")
* @Assert\NotBlank(message="notification.Comment content might not be blank")
*/
private string $content = '';
@@ -136,9 +138,9 @@ class NotificationComment implements TrackCreationInterface, TrackUpdateInterfac
$this->recentlyPersisted = true;
}
public function setContent(string $content): self
public function setContent(?string $content): self
{
$this->content = $content;
$this->content = (string) $content;
return $this;
}

View File

@@ -135,6 +135,7 @@ class EntityWorkflow implements TrackCreationInterface, TrackUpdateInterface
if (!$this->steps->contains($step)) {
$this->steps[] = $step;
$step->setEntityWorkflow($this);
$this->stepsChainedCache = null;
}
return $this;
@@ -332,32 +333,26 @@ class EntityWorkflow implements TrackCreationInterface, TrackUpdateInterface
public function isFinal(): bool
{
$steps = $this->getStepsChained();
if (1 === count($steps)) {
// the initial step cannot be finalized
return false;
foreach ($this->getStepsChained() as $step) {
if ($step->isFinal()) {
return true;
}
}
/** @var EntityWorkflowStep $last */
$last = end($steps);
return $last->isFinal();
return false;
}
public function isFreeze(): bool
{
$steps = $this->getStepsChained();
if (1 === count($steps)) {
// the initial step cannot be finalized
return false;
foreach ($this->getStepsChained() as $step) {
if ($step->isFreezeAfter()) {
return true;
}
}
/** @var EntityWorkflowStep $last */
$last = end($steps);
return $last->getPrevious()->isFreezeAfter();
return false;
}
public function isUserSubscribedToFinal(User $user): bool
@@ -434,7 +429,7 @@ class EntityWorkflow implements TrackCreationInterface, TrackUpdateInterface
$newStep->setCurrentStep($step);
// copy the freeze
if ($this->getCurrentStep()->isFreezeAfter()) {
if ($this->isFreeze()) {
$newStep->setFreezeAfter(true);
}

View File

@@ -79,11 +79,8 @@ class NotificationMailer
continue;
}
$email = new Email();
$email
->subject($notification->getTitle());
if ($notification->isSystem()) {
$email = new Email();
$email
->text($notification->getMessage());
} else {
@@ -96,7 +93,9 @@ class NotificationMailer
]);
}
$email->to($addressee->getEmail());
$email
->subject($notification->getTitle())
->to($addressee->getEmail());
try {
$this->mailer->send($email);

View File

@@ -56,7 +56,7 @@ class EntityWorkflowStepRepository implements ObjectRepository
public function getClassName()
{
return EntityWorkflow::class;
return EntityWorkflowStep::class;
}
private function buildQueryByUser(User $user): QueryBuilder

View File

@@ -15,6 +15,7 @@ $chill-theme-buttons: (
"action": $chill-orange,
"edit": $chill-orange,
"update": $chill-orange,
"wopilink": $chill-orange,
"show": $chill-blue,
"view": $chill-blue,
"misc": $gray-300,
@@ -54,6 +55,7 @@ $chill-theme-buttons: (
&.btn-action,
&.btn-edit,
&.btn-tpchild,
&.btn-wopilink,
&.btn-update {
&, &:hover {
color: $light;
@@ -66,6 +68,7 @@ $chill-theme-buttons: (
&.btn-create::before,
&.btn-edit::before,
&.btn-update::before,
&.btn-wopilink::before,
&.btn-show::before,
&.btn-view::before,
&.btn-save::before,
@@ -98,6 +101,7 @@ $chill-theme-buttons: (
&.btn-create::before { content: "\f067"; } // fa-plus
&.btn-edit::before,
&.btn-update::before { content: "\f040"; } // fa-pencil
&.btn-wopilink::before { content: "\f1dd"; } // fa-paragraph
&.btn-show::before,
&.btn-view::before { content: "\f06e"; } // fa-eye
&.btn-save::before { content: "\f0c7"; } // fa-floppy-o

View File

@@ -17,7 +17,12 @@ function loadDynamicPicker(element) {
isMultiple = parseInt(el.dataset.multiple) === 1,
uniqId = el.dataset.uniqid,
input = element.querySelector('[data-input-uniqid="'+ el.dataset.uniqid +'"]'),
picked = (isMultiple) ? (JSON.parse(input.value)) : ((input.value === '[]') ? (null) : ([JSON.parse(input.value)]));
picked = isMultiple ?
JSON.parse(input.value) : (
(input.value === '[]' || input.value === '') ?
null : [ JSON.parse(input.value) ]
)
;
if (!isMultiple) {
if (input.value === '[]'){

View File

@@ -4,20 +4,34 @@ import {_createI18n} from "ChillMainAssets/vuejs/_js/i18n";
const i18n = _createI18n({});
//TODO move to chillDocStore or ChillWopi
/*
tags to load module:
<span data-module="wopi-link"
data-wopi-url="{{ path('chill_wopi_file_edit', {'fileId': document.uuid}) }}"
data-doc-type="{{ document.type|e('html_attr') }}"
data-options="{{ options|json_encode }}"
></span>
*/
window.addEventListener('DOMContentLoaded', function (e) {
document.querySelectorAll('span[data-module="wopi-link"]')
.forEach(function (el) {
createApp({
template: '<open-wopi-link :wopiUrl="wopiUrl" :title="title" :type="type" :button="button"></open-wopi-link>',
template: '<open-wopi-link :wopiUrl="wopiUrl" :type="type" :options="options"></open-wopi-link>',
components: {
OpenWopiLink
},
data() {
return {
wopiUrl: el.dataset.wopiUrl,
title: el.dataset.docTitle,
type: el.dataset.docType,
button: el.dataset.button ? JSON.parse(el.dataset.button) : {}
options: el.dataset.options !== 'null' ? JSON.parse(el.dataset.options) : {}
}
}
})
@@ -26,4 +40,4 @@ window.addEventListener('DOMContentLoaded', function (e) {
;
})
;
});
});

View File

@@ -260,8 +260,7 @@ export default {
editPane: false,
datePane: false,
loading: false,
success: false,
dirty: false
success: false
},
errors: [],
defaultz: {
@@ -537,17 +536,19 @@ export default {
checkErrors() {
this.errors = [];
if (this.flag.dirty) {
if (this.entity.selected.country === null) {
this.errors.push("Un pays doit être sélectionné.");
}
if (this.entity.selected.country === null) {
this.errors.push("Un pays doit être sélectionné.");
}
if (this.entity.selected.city === null) {
this.errors.push("Une ville doit être sélectionnée.");
} else {
if (Object.keys(this.entity.selected.city).length === 0) {
this.errors.push("Une ville doit être sélectionnée.");
}
if (!this.entity.selected.isNoAddress) {
if (this.entity.selected.address.street === null || this.entity.selected.address.streetNumber === null) {
this.errors.push("Une adresse doit être sélectionnée.");
}
}
if (!this.entity.selected.isNoAddress) {
if (this.entity.selected.address.street === null || this.entity.selected.address.street === '' || this.entity.selected.address.streetNumber === null || this.entity.selected.address.streetNumber === '') {
this.errors.push("Une adresse doit être sélectionnée.");
}
}
},
@@ -567,7 +568,7 @@ export default {
this.entity.selected.country = this.context.edit ? this.entity.address.country : {};
this.entity.selected.postcode = this.context.edit ? this.entity.address.postcode : {};
this.entity.selected.city = {};
this.entity.selected.city = this.context.edit ? this.entity.address.postcode : {};
this.entity.selected.address = {};
this.entity.selected.address.street = this.context.edit ? this.entity.address.street: null;
@@ -583,6 +584,8 @@ export default {
this.entity.selected.writeNew.address = this.context.edit && this.entity.address.addressReference === null && this.entity.address.street.length > 0
this.entity.selected.writeNew.postcode = false // NB: this used to be this.context.edit, but think it was erroneous;
console.log('!! just set writeNew.postcode to', this.entity.selected.writeNew.postcode);
this.checkErrors();
},
/*

View File

@@ -111,11 +111,9 @@ export default {
this.entity.selected.address.streetNumber = value.streetNumber;
this.entity.selected.writeNew.address = false;
this.updateMapCenter(value.point);
this.flag.dirty = true;
this.checkErrors();
},
remove() {
this.flag.dirty = true;
this.entity.selected.address = {};
this.checkErrors();
},
@@ -158,7 +156,6 @@ export default {
this.entity.selected.address.street = addr.street;
this.entity.selected.address.streetNumber = addr.number;
this.entity.selected.writeNew.address = true;
this.flag.dirty = true;
this.checkErrors();
}
},

View File

@@ -125,11 +125,9 @@ export default {
if (value.center) {
this.updateMapCenter(value.center);
}
this.flag.dirty = true;
this.checkErrors();
},
remove() {
this.flag.dirty = true;
this.entity.selected.city = {};
this.checkErrors();
},

View File

@@ -51,7 +51,6 @@ export default {
init() {
if (this.value !== undefined) {
this.selectCountry(this.value);
this.flag.dirty = false;
}
},
selectCountryByCode(countryCode) {
@@ -67,7 +66,6 @@ export default {
this.checkErrors();
},
remove() {
this.flag.dirty = true;
this.entity.selected.country = null;
this.checkErrors();
},

View File

@@ -157,6 +157,7 @@ export default {
set(value) {
console.log('isNoAddress value', value);
this.entity.selected.isNoAddress = value;
this.checkErrors();
},
get() {
return this.entity.selected.isNoAddress;

View File

@@ -32,7 +32,7 @@
</span>
</td>
<td>
<span v-if="c.emergency" class="badge rounded-pill bg-danger">{{ $t('emergency') }}</span>
<span v-if="c.emergency" class="badge rounded-pill bg-danger me-1">{{ $t('emergency') }}</span>
<span v-if="c.confidential" class="badge rounded-pill bg-danger">{{ $t('confidential') }}</span>
</td>
<td>
@@ -80,5 +80,7 @@ export default {
</script>
<style scoped>
span.badge.rounded-pill.bg-danger {
text-transform: uppercase;
}
</style>

View File

@@ -50,7 +50,9 @@ const appMessages = {
assignated_evaluations: "{n} évaluation assignée | {n} évaluations assignées",
alert_tasks: "{n} tâche en rappel | {n} tâches en rappel",
warning_tasks: "{n} tâche à échéance | {n} tâches à échéance",
}
},
emergency: "Urgent",
confidential: "Confidentiel",
}
};

View File

@@ -1,6 +1,6 @@
<template>
<ul class="nav nav-tabs">
<li class="nav-item">
<li v-if="allowedTypes.includes('person')" class="nav-item">
<a class="nav-link" :class="{ active: isActive('person') }">
<label for="person">
<input type="radio" name="person" id="person" v-model="radioType" value="person">
@@ -8,7 +8,7 @@
</label>
</a>
</li>
<li class="nav-item">
<li v-if="allowedTypes.includes('thirdparty')" class="nav-item">
<a class="nav-link" :class="{ active: isActive('thirdparty') }">
<label for="thirdparty">
<input type="radio" name="thirdparty" id="thirdparty" v-model="radioType" value="thirdparty">
@@ -21,14 +21,16 @@
<div class="my-4">
<on-the-fly-person
v-if="type === 'person'"
v-bind:action="action"
:action="action"
:query="query"
ref="castPerson"
>
</on-the-fly-person>
<on-the-fly-thirdparty
v-if="type === 'thirdparty'"
v-bind:action="action"
:action="action"
:query="query"
ref="castThirdparty"
>
</on-the-fly-thirdparty>
@@ -38,18 +40,17 @@
<script>
import OnTheFlyPerson from 'ChillPersonAssets/vuejs/_components/OnTheFly/Person.vue';
import OnTheFlyThirdparty from 'ChillThirdPartyAssets/vuejs/_components/OnTheFly/ThirdParty.vue';
import { postPerson } from "ChillPersonAssets/vuejs/_api/OnTheFly";
export default {
name: "OnTheFlyCreate",
props: ['action'],
props: ['action', 'allowedTypes', 'query'],
components: {
OnTheFlyPerson,
OnTheFlyThirdparty
},
data() {
return {
type: 'person' //by default
type: null
}
},
computed: {
@@ -61,7 +62,10 @@ export default {
get() {
return this.type;
}
}
},
},
mounted() {
this.type = (this.allowedTypes.length === 1 && this.allowedTypes.includes('thirdparty')) ? 'thirdparty' : 'person'
},
methods: {
isActive(tab) {

View File

@@ -64,6 +64,8 @@
<template v-slot:body v-else>
<on-the-fly-create
:action="action"
:allowedTypes="allowedTypes"
:query="query"
ref="castNew">
</on-the-fly-create>
</template>
@@ -101,7 +103,7 @@ export default {
OnTheFlyThirdparty,
OnTheFlyCreate
},
props: ['type', 'id', 'action', 'buttonText', 'displayBadge', 'isDead', 'parent'],
props: ['type', 'id', 'action', 'buttonText', 'displayBadge', 'isDead', 'parent', 'allowedTypes', 'query'],
emits: ['saveFormOnTheFly'],
data() {
return {
@@ -144,6 +146,13 @@ export default {
return 'action.addContact';
}
},
titleCreate() {
return this.allowedTypes.every(t => t === 'person')
? 'onthefly.create.title.person'
: this.allowedTypes.every(t => t === 'thirdparty')
? 'onthefly.create.title.thirdparty'
: 'onthefly.create.title.default'
},
titleModal() {
switch (this.action) {
case 'show':
@@ -151,7 +160,7 @@ export default {
case 'edit':
return 'onthefly.edit.' + this.type;
case 'create':
return 'onthefly.create.title';
return this.titleCreate;
case 'addContact':
return 'onthefly.addContact.title';
}
@@ -177,7 +186,10 @@ export default {
},
badgeType() {
return 'entity-' + this.type + ' badge-' + this.type;
}
},
getReturnPath() {
return `?returnPath=${window.location.pathname}${window.location.search}${window.location.hash}`;
},
},
methods: {
closeModal() {
@@ -243,9 +255,9 @@ export default {
buildLocation(id, type) {
if (type === 'person') {
// TODO i18n
return `/fr/person/${id}/general`;
return encodeURI(`/fr/person/${id}/general${this.getReturnPath}`);
} else if (type === 'thirdparty') {
return `/fr/3party/3party/${id}/view`;
return encodeURI(`/fr/3party/3party/${id}/view${this.getReturnPath}`);
}
}
}

View File

@@ -9,11 +9,15 @@ const ontheflyMessages = {
},
edit: {
person: "Modifier un usager",
thirdparty: "Modifier un tiers"
thirdparty: "Modifier un tiers",
},
create: {
button: "Créer \"{q}\"",
title: "Création d'un nouvel usager ou d'un tiers professionnel",
title: {
default: "Création d'un nouvel usager ou d'un tiers professionnel",
person: "Création d'un nouvel usager",
thirdparty: "Création d'un nouveau tiers professionnel",
},
person: "un nouvel usager",
thirdparty: "un nouveau tiers professionnel"
},

View File

@@ -5,8 +5,8 @@
</li>
</ul>
<ul class="record_actions">
<li>
<AddPersons
<li class="add-persons">
<add-persons
:options="addPersonsOptions"
:key="uniqid"
:buttonTitle="translatedListOfTypes"
@@ -14,7 +14,7 @@
ref="addPersons"
@addNewPersons="addNewEntity"
>
</AddPersons>
</add-persons>
</li>
</ul>
</template>

View File

@@ -1,4 +1,7 @@
import { personMessages } from 'ChillPersonAssets/vuejs/_js/i18n';
import { thirdpartyMessages } from 'ChillThirdPartyAssets/vuejs/_js/i18n';
import { addressMessages } from 'ChillMainAssets/vuejs/Address/i18n';
import { ontheflyMessages } from 'ChillMainAssets/vuejs/OnTheFly/i18n';
const appMessages = {
fr: {
@@ -12,6 +15,6 @@ const appMessages = {
}
}
Object.assign(appMessages.fr, personMessages.fr);
Object.assign(appMessages.fr, personMessages.fr, thirdpartyMessages.fr, addressMessages.fr, ontheflyMessages.fr );
export { appMessages };

View File

@@ -42,57 +42,11 @@
class="street">
{{ address.text }}
</p>
<p v-if="address.postcode"
class="postcode">
{{ address.postcode.code }} {{ address.postcode.name }}
</p>
<p v-if="address.country"
class="country">
{{ address.country.name.fr }}
</p>
</div>
</div>
</component>
<!-- <div v-if="isMultiline === true" class="address-more">
<div v-if="address.floor">
<span class="floor">
<b>{{ $t('floor') }}</b>: {{ address.floor }}
</span>
</div>
<div v-if="address.corridor">
<span class="corridor">
<b>{{ $t('corridor') }}</b>: {{ address.corridor }}
</span>
</div>
<div v-if="address.steps">
<span class="steps">
<b>{{ $t('steps') }}</b>: {{ address.steps }}
</span>
</div>
<div v-if="address.flat">
<span class="flat">
<b>{{ $t('flat') }}</b>: {{ address.flat }}
</span>
</div>
<div v-if="address.buildingName">
<span class="buildingName">
<b>{{ $t('buildingName') }}</b>: {{ address.buildingName }}
</span>
</div>
<div v-if="address.extra">
<span class="extra">
<b>{{ $t('extra') }}</b>: {{ address.extra }}
</span>
</div>
<div v-if="address.distribution">
<span class="distribution">
<b>{{ $t('distribution') }}</b>: {{ address.distribution }}
</span>
</div>
</div> -->
<div v-if="useDatePane === true" class="address-more">
<div v-if="address.validFrom">
<span class="validFrom">

View File

@@ -1,12 +1,14 @@
<template>
<a v-if="isOpenDocument"
class="btn change-icon" :class="[isChangeClass ? button.changeClass : 'btn-edit']"
class="btn" :class="[
isChangeIcon ? 'change-icon' : '',
isChangeClass ? options.changeClass : 'btn-wopilink' ]"
@click="openModal">
<i class="fa me-2" :class="[isChangeIcon ? button.changeIcon : 'fa-pencil']"></i>
<i v-if="isChangeIcon" class="fa me-2" :class="options.changeIcon"></i>
<span v-if="!noText">
{{ $t('Update_document') }}
{{ $t('online_edit_document') }}
</span>
</a>
@@ -20,12 +22,14 @@
<template v-slot:header>
<img class="logo" :src="logo" height="45"/>
<span class="ms-auto me-3">
{{ this.title }}
<span v-if="options.title">{{ options.title }}</span>
</span>
<!--
<a class="btn btn-outline-light">
<i class="fa fa-save fa-fw"></i>
{{ $t('save_and_quit') }}
</a>
-->
</template>
<template v-slot:body>
@@ -71,15 +75,11 @@ export default {
type: String,
required: true
},
title: {
type: String,
required: true
},
type: {
type: String,
required: true
},
button: {
options: {
type: Object,
required: false
}
@@ -175,20 +175,20 @@ export default {
return false;
},
noText() {
if (typeof this.button.noText !== 'undefined') {
return this.button.noText === true;
if (typeof this.options.noText !== 'undefined') {
return this.options.noText === true;
}
return false;
},
isChangeIcon() {
if (typeof this.button.changeIcon !== 'undefined') {
return (!(this.button.changeIcon === null || this.button.changeIcon === ''))
if (typeof this.options.changeIcon !== 'undefined') {
return (!(this.options.changeIcon === null || this.options.changeIcon === ''))
}
return false;
},
isChangeClass() {
if (typeof this.button.changeClass !== 'undefined') {
return (!(this.button.changeClass === null || this.button.changeClass === ''))
if (typeof this.options.changeClass !== 'undefined') {
return (!(this.options.changeClass === null || this.options.changeClass === ''))
}
return false;
}
@@ -205,7 +205,7 @@ export default {
i18n: {
messages: {
fr: {
Update_document: "Modifier le document",
online_edit_document: "Éditer en ligne",
save_and_quit: "Enregistrer et quitter",
loading: "Chargement de l'éditeur en ligne",
invalid_title: "Format incompatible",

View File

@@ -92,11 +92,11 @@ export const multiSelectMessages = {
multiselect: {
placeholder: 'Choisir',
tag_placeholder: 'Créer un nouvel élément',
select_label: 'Appuyer sur "Entrée" pour sélectionner',
deselect_label: 'Appuyer sur "Entrée" pour désélectionner',
select_label: '"Entrée" ou cliquez pour sélectionner',
deselect_label: '"Entrée" ou cliquez pour désélectionner',
select_group_label: 'Appuyer sur "Entrée" pour sélectionner ce groupe',
deselect_group_label: 'Appuyer sur "Entrée" pour désélectionner ce groupe',
selected_label: 'Sélectionné'
}
}
};
};

View File

@@ -15,11 +15,11 @@
<div class="notification-comment-list my-5">
<h2 class="chill-blue">{{ 'notification.comments_list'|trans }}</h2>
{% if notification.comments|length > 0 %}
<div class="flex-table">
{% for comment in notification.comments %}
{% if editedCommentForm is null or editedCommentId != comment.id %}
{{ m.show_comment(comment, {
'recordAction': _self.recordAction(comment)
@@ -28,10 +28,11 @@
<div class="item-bloc">
<div class="item-row row">
<a id="comment-{{ comment.id }}"></a>
{{ form_start(editedCommentForm) }}
{{ form_errors(editedCommentForm) }}
{{ form_widget(editedCommentForm.content) }}
{{ form_errors(editedCommentForm.content) }}
<input type="hidden" name="form" value="edit" />
<ul class="record_actions">
<li class="cancel">
@@ -46,24 +47,25 @@
</li>
</ul>
{{ form_end(editedCommentForm) }}
</div>
</div>
{% endif %}
{% endfor %}
</div>
{% else %}
<span class="chill-no-data-statement">{{ 'No comments'|trans }}</span>
{% endif %}
{% if appendCommentForm is not null %}
<div class="new-comment my-5">
<h2 class="chill-blue mb-4">{{ 'Write a new comment'|trans }}</h2>
{{ form_start(appendCommentForm) }}
{{ form_errors(appendCommentForm) }}
{{ form_widget(appendCommentForm.content) }}
{{ form_errors(appendCommentForm.content) }}
<input type="hidden" name="form" value="append" />
<ul class="record_actions">
<li>
@@ -71,7 +73,7 @@
</li>
</ul>
{{ form_end(appendCommentForm) }}
</div>
{% endif %}
</div>
</div>

View File

@@ -6,7 +6,6 @@
{% block js %}
{{ parent() }}
{{ encore_entry_script_tags('mod_async_upload') }}
{{ encore_entry_script_tags('mod_pickentity_type') }}
{{ encore_entry_script_tags('mod_entity_workflow_subscribe') }}

View File

@@ -64,7 +64,7 @@ class AddressNormalizer implements ContextAwareNormalizerInterface, NormalizerAw
if ($address instanceof Address) {
$data = [
'address_id' => $address->getId(),
'text' => $address->isNoAddress() ? null : $this->addressRender->renderStreetLine($address, []),
'text' => $this->addressRender->renderString($address, []),
'street' => $address->getStreet(),
'streetNumber' => $address->getStreetNumber(),
'postcode' => [

View File

@@ -12,6 +12,7 @@ declare(strict_types=1);
namespace Chill\MainBundle\Serializer\Normalizer;
use LogicException;
use Symfony\Component\Serializer\Exception\NotNormalizableValueException;
use Symfony\Component\Serializer\Exception\RuntimeException;
use Symfony\Component\Serializer\Normalizer\ContextAwareDenormalizerInterface;
use Symfony\Component\Serializer\Normalizer\DenormalizerAwareInterface;
@@ -44,7 +45,7 @@ class DiscriminatedObjectDenormalizer implements ContextAwareDenormalizerInterfa
if ($this->denormalizer->supportsDenormalization($data, $localType, $format)) {
try {
return $this->denormalizer->denormalize($data, $localType, $format, $context);
} catch (RuntimeException $e) {
} catch (RuntimeException|NotNormalizableValueException $e) {
$lastException = $e;
}
}

View File

@@ -124,7 +124,7 @@ class AddressRender implements ChillEntityRenderInterface
*/
public function renderString($addr, array $options): string
{
return implode(' - ', $this->renderLines($addr));
return implode(' ', $this->renderLines($addr));
}
public function supports($entity, array $options): bool

View File

@@ -24,30 +24,30 @@ final class EntityWorkflowTest extends TestCase
{
$entityWorkflow = new EntityWorkflow();
$entityWorkflow->getCurrentStep()->setFinalizeAfter(true);
$entityWorkflow->setStep('final');
$entityWorkflow->getCurrentStep()->setIsFinal(true);
$this->assertTrue($entityWorkflow->isFinalize());
$this->assertTrue($entityWorkflow->isFinal());
}
public function testIsFinalizeWith4Steps()
{
$entityWorkflow = new EntityWorkflow();
$this->assertFalse($entityWorkflow->isFinalize());
$this->assertFalse($entityWorkflow->isFinal());
$entityWorkflow->setStep('two');
$this->assertFalse($entityWorkflow->isFinalize());
$this->assertFalse($entityWorkflow->isFinal());
$entityWorkflow->setStep('previous_final');
$this->assertFalse($entityWorkflow->isFinalize());
$this->assertFalse($entityWorkflow->isFinal());
$entityWorkflow->getCurrentStep()->setFinalizeAfter(true);
$entityWorkflow->getCurrentStep()->setIsFinal(true);
$entityWorkflow->setStep('final');
$this->assertTrue($entityWorkflow->isFinalize());
$this->assertTrue($entityWorkflow->isFinal());
}
public function testIsFreeze()
@@ -64,11 +64,8 @@ final class EntityWorkflowTest extends TestCase
$this->assertFalse($entityWorkflow->isFreeze());
$entityWorkflow->getCurrentStep()->setFreezeAfter(true);
$this->assertFalse($entityWorkflow->isFreeze());
$entityWorkflow->setStep('freezed');
$entityWorkflow->getCurrentStep()->setFreezeAfter(true);
$this->assertTrue($entityWorkflow->isFreeze());

View File

@@ -48,7 +48,7 @@ final class AddressRenderTest extends KernelTestCase
$addr->setBuildingName('Résidence "Les Bleuets"');
yield [$addr, 'Résidence "Les Bleuets" - Rue ABC, 5 - 012345 Locality - Belgium'];
yield [$addr, 'Résidence "Les Bleuets" Rue ABC, 5 012345 Locality Belgium'];
}
public function addressDataProviderBEWithSteps(): Iterator
@@ -68,7 +68,7 @@ final class AddressRenderTest extends KernelTestCase
$addr->setSteps('4');
yield [$addr, 'esc 4 - Rue ABC, 5 - 012345 Locality - Belgium'];
yield [$addr, 'esc 4 Rue ABC, 5 012345 Locality Belgium'];
}
public function addressDataProviderFRWithBuilding(): Iterator
@@ -88,7 +88,7 @@ final class AddressRenderTest extends KernelTestCase
$addr->setBuildingName('Résidence "Les Bleuets"');
yield [$addr, 'Résidence "Les Bleuets" - 5, Rue ABC - 012345 Locality - France'];
yield [$addr, 'Résidence "Les Bleuets" 5, Rue ABC 012345 Locality France'];
}
public function addressDataProviderFRWithSteps(): Iterator
@@ -108,7 +108,7 @@ final class AddressRenderTest extends KernelTestCase
$addr->setSteps('4');
yield [$addr, 'esc 4 - 5, Rue ABC - 012345 Locality - France'];
yield [$addr, 'esc 4 5, Rue ABC 012345 Locality France'];
}
public function complexAddressDataProviderBE(): Iterator
@@ -132,7 +132,7 @@ final class AddressRenderTest extends KernelTestCase
$addr->setCorridor('3');
$addr->setSteps('4');
yield [$addr, 'Résidence "Les Bleuets" - appart 1 - ét 2 - coul 3 - esc 4 - Rue ABC, 5 - 012345 Locality - Belgium'];
yield [$addr, 'Résidence "Les Bleuets" - appart 1 - ét 2 - coul 3 - esc 4 Rue ABC, 5 012345 Locality Belgium'];
}
public function complexAddressDataProviderFR(): Iterator
@@ -158,7 +158,7 @@ final class AddressRenderTest extends KernelTestCase
$addr->setExtra('A droite de la porte');
$addr->setDistribution('CEDEX');
yield [$addr, 'appart 1 - ét 2 - coul 3 - esc 4 - Résidence "Les Bleuets" - 5, Rue ABC - A droite de la porte - 012345 Locality CEDEX - France'];
yield [$addr, 'appart 1 - ét 2 - coul 3 - esc 4 Résidence "Les Bleuets" 5, Rue ABC A droite de la porte 012345 Locality CEDEX France'];
}
public function noFullAddressDataProviderBE(): Iterator
@@ -175,7 +175,7 @@ final class AddressRenderTest extends KernelTestCase
$addr->setPostcode($postCode)
->setIsNoAddress(true);
yield [$addr, '012345 Locality - Belgium'];
yield [$addr, '012345 Locality Belgium'];
}
public function simpleAddressDataProviderBE(): Iterator
@@ -193,7 +193,7 @@ final class AddressRenderTest extends KernelTestCase
->setStreetNumber('5')
->setPostcode($postCode);
yield [$addr, 'Rue ABC, 5 - 012345 Locality - Belgium'];
yield [$addr, 'Rue ABC, 5 012345 Locality Belgium'];
}
public function simpleAddressDataProviderFR(): Iterator
@@ -211,7 +211,7 @@ final class AddressRenderTest extends KernelTestCase
->setStreetNumber('5')
->setPostcode($postCode);
yield [$addr, '5, Rue ABC - 012345 Locality - France'];
yield [$addr, '5, Rue ABC 012345 Locality France'];
}
/**

View File

@@ -16,36 +16,36 @@ use Chill\MainBundle\Entity\Workflow\EntityWorkflow;
use Chill\MainBundle\Repository\Workflow\EntityWorkflowStepRepository;
use Chill\MainBundle\Templating\UI\NotificationCounterInterface;
use Psr\Cache\CacheItemPoolInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Workflow\Event\Event;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
final class WorkflowByUserCounter implements NotificationCounterInterface, EventSubscriberInterface
{
private EntityWorkflowStepRepository $workflowStepRepository;
private CacheItemPoolInterface $cacheItemPool;
private EntityWorkflowStepRepository $workflowStepRepository;
public function __construct(EntityWorkflowStepRepository $workflowStepRepository, CacheItemPoolInterface $cacheItemPool)
{
$this->workflowStepRepository = $workflowStepRepository;
$this->cacheItemPool = $cacheItemPool;
}
public function addNotification(UserInterface $user): int
public function addNotification(UserInterface $u): int
{
if (!$user instanceof User) {
if (!$u instanceof User) {
return 0;
}
$key = self::generateCacheKeyWorkflowByUser($user);
$key = self::generateCacheKeyWorkflowByUser($u);
$item = $this->cacheItemPool->getItem($key);
if ($item->isHit()) {
return $item->get();
}
$nb = $this->getCountUnreadByUser($user);
$nb = $this->getCountUnreadByUser($u);
$item->set($nb)
->expiresAfter(60 * 15);
@@ -54,14 +54,21 @@ final class WorkflowByUserCounter implements NotificationCounterInterface, Event
return $nb;
}
public static function generateCacheKeyWorkflowByUser(User $user): string
{
return 'chill_main_workflow_by_u_' . $user->getId();
}
public function getCountUnreadByUser(User $user): int
{
return $this->workflowStepRepository->countUnreadByUser($user);
}
public static function generateCacheKeyWorkflowByUser(User $user): string
public static function getSubscribedEvents()
{
return 'chill_main_workflow_by_u_'.$user->getId();
return [
'workflow.leave' => 'resetWorkflowCache',
];
}
public function resetWorkflowCache(Event $event): void
@@ -74,11 +81,12 @@ final class WorkflowByUserCounter implements NotificationCounterInterface, Event
$entityWorkflow = $event->getSubject();
$step = $entityWorkflow->getCurrentStep();
if ($step === null) {
if (null === $step) {
return;
}
$keys = [];
foreach ($step->getDestUser() as $user) {
$keys[] = self::generateCacheKeyWorkflowByUser($user);
}
@@ -86,15 +94,5 @@ final class WorkflowByUserCounter implements NotificationCounterInterface, Event
if ([] !== $keys) {
$this->cacheItemPool->deleteItems($keys);
}
}
public static function getSubscribedEvents()
{
return [
'workflow.leave' => 'resetWorkflowCache',
];
}
}

View File

@@ -72,17 +72,18 @@ class MetadataExtractor
foreach ($transitions as $transition) {
if ($transition->getName() === $transitionName) {
break;
$metadata = $workflow->getMetadataStore()->getTransitionMetadata($transition);
return [
'name' => $transition->getName(),
'text' => array_key_exists('label', $metadata) ?
$this->translatableStringHelper->localize($metadata['label']) : $transition->getName(),
'isForward' => $metadata['isForward'] ?? null,
];
}
}
$metadata = $workflow->getMetadataStore()->getTransitionMetadata($transition);
return [
'name' => $transition->getName(),
'text' => array_key_exists('label', $metadata) ?
$this->translatableStringHelper->localize($metadata['label']) : $transition->getName(),
'isForward' => $metadata['isForward'] ?? null,
];
return [];
}
public function buildArrayPresentationForWorkflow(WorkflowInterface $workflow): array

View File

@@ -27,4 +27,4 @@ address:
notification:
At least one addressee: Indiquez au moins un destinataire
Title must be defined: Un titre doit être indiqué
Comment content might not be blank: Le commentaire ne peut pas être vide