diff --git a/.gitignore b/.gitignore
index ebdc16e56..26802dca0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,6 +5,7 @@ composer.lock
docs/build/
node_modules/*
.php_cs.cache
+.cache/*
###> symfony/framework-bundle ###
/.env.local
diff --git a/phpstan-types.neon b/phpstan-types.neon
index 1aae06880..b11cbd153 100644
--- a/phpstan-types.neon
+++ b/phpstan-types.neon
@@ -340,11 +340,6 @@ parameters:
count: 1
path: src/Bundle/ChillPersonBundle/Form/Type/PersonPhoneType.php
- -
- message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
- count: 3
- path: src/Bundle/ChillPersonBundle/Search/PersonSearch.php
-
-
message: "#^Method Chill\\\\PersonBundle\\\\Search\\\\PersonSearch\\:\\:renderResult\\(\\) should return string but return statement is missing\\.$#"
count: 1
diff --git a/src/Bundle/ChillActivityBundle/Entity/Activity.php b/src/Bundle/ChillActivityBundle/Entity/Activity.php
index 8fcae3e0b..2a5ae6acb 100644
--- a/src/Bundle/ChillActivityBundle/Entity/Activity.php
+++ b/src/Bundle/ChillActivityBundle/Entity/Activity.php
@@ -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;
}
diff --git a/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/LocationFilter.php b/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/LocationFilter.php
index ed591e957..5cdce5b31 100644
--- a/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/LocationFilter.php
+++ b/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/LocationFilter.php
@@ -50,7 +50,7 @@ class LocationFilter implements FilterInterface
{
$builder->add('accepted_location', PickUserLocationType::class, [
'multiple' => true,
- 'label' => 'pick location'
+ 'label' => 'pick location',
]);
}
diff --git a/src/Bundle/ChillActivityBundle/Resources/public/chill/chillactivity.scss b/src/Bundle/ChillActivityBundle/Resources/public/chill/chillactivity.scss
index 5c1c83d06..d70bd28c3 100644
--- a/src/Bundle/ChillActivityBundle/Resources/public/chill/chillactivity.scss
+++ b/src/Bundle/ChillActivityBundle/Resources/public/chill/chillactivity.scss
@@ -88,3 +88,11 @@ div.flex-bloc.concerned-groups {
font-size: 120%;
}
}
+
+/// DOCUMENT LIST IN ACTIVITY ITEM
+li.document-list-item {
+ display: flex;
+ width: 100%;
+ justify-content: space-between;
+ margin-bottom: 0.3rem;
+}
diff --git a/src/Bundle/ChillActivityBundle/Resources/views/Activity/_list_item.html.twig b/src/Bundle/ChillActivityBundle/Resources/views/Activity/_list_item.html.twig
index 3a4749f3c..885125aa6 100644
--- a/src/Bundle/ChillActivityBundle/Resources/views/Activity/_list_item.html.twig
+++ b/src/Bundle/ChillActivityBundle/Resources/views/Activity/_list_item.html.twig
@@ -68,7 +68,7 @@
{{ 'Referrer'|trans }}
- {{ activity.user|chill_entity_render_box }}
+ {{ activity.user|chill_entity_render_box }}
@@ -137,19 +137,42 @@
{{ activity.comment|chill_entity_render_box({
'disable_markdown': false,
'limit_lines': 3,
- 'metadata': false
+ 'metadata': false,
}) }}
{% endif %}
- {# Only if ACL SEE_DETAILS AND/OR only on template SHOW ??
- durationTime
- travelTime
- comment
- documents
- attendee
- #}
+ {% if is_granted('CHILL_ACTIVITY_SEE_DETAILS', activity) and activity.privateComment.hasCommentForUser(app.user) %}
+
+
+
{{ 'Private comment'|trans }}
+
+
+
+
+
+ {% endif %}
+
+ {% if is_granted('CHILL_ACTIVITY_SEE_DETAILS', activity) and activity.documents|length > 0 %}
+
+
+
{{ 'Documents'|trans }}
+
+
+
+ {% for d in activity.documents %}
+ - {{ d.title|chill_print_or_message('document.Any title') }} {{ d|chill_document_button_group(d.title, is_granted('CHILL_ACTIVITY_UPDATE', activity), {small: true}) }}
+ {% endfor %}
+
+
+
+ {% endif %}
+
diff --git a/src/Bundle/ChillActivityBundle/Resources/views/Activity/listAccompanyingCourse.html.twig b/src/Bundle/ChillActivityBundle/Resources/views/Activity/listAccompanyingCourse.html.twig
index a666f183d..bdf55d86f 100644
--- a/src/Bundle/ChillActivityBundle/Resources/views/Activity/listAccompanyingCourse.html.twig
+++ b/src/Bundle/ChillActivityBundle/Resources/views/Activity/listAccompanyingCourse.html.twig
@@ -8,11 +8,13 @@
{% block js %}
{{ parent() }}
{{ encore_entry_script_tags('mod_notification_toggle_read_status') }}
+ {{ encore_entry_script_tags('mod_document_action_buttons_group') }}
{% endblock %}
{% block css %}
{{ parent() }}
{{ encore_entry_link_tags('mod_notification_toggle_read_status') }}
+ {{ encore_entry_link_tags('mod_document_action_buttons_group') }}
{% endblock %}
{% block content %}
diff --git a/src/Bundle/ChillActivityBundle/Resources/views/Activity/listPerson.html.twig b/src/Bundle/ChillActivityBundle/Resources/views/Activity/listPerson.html.twig
index 0514284a2..0c1afdac9 100644
--- a/src/Bundle/ChillActivityBundle/Resources/views/Activity/listPerson.html.twig
+++ b/src/Bundle/ChillActivityBundle/Resources/views/Activity/listPerson.html.twig
@@ -23,11 +23,13 @@
{% block js %}
{{ parent() }}
{{ encore_entry_script_tags('mod_notification_toggle_read_status') }}
+ {{ encore_entry_script_tags('mod_document_action_buttons_group') }}
{% endblock %}
{% block css %}
{{ parent() }}
{{ encore_entry_link_tags('mod_notification_toggle_read_status') }}
+ {{ encore_entry_link_tags('mod_document_action_buttons_group') }}
{% endblock %}
{% block content %}
diff --git a/src/Bundle/ChillActivityBundle/Resources/views/Activity/list_recent.html.twig b/src/Bundle/ChillActivityBundle/Resources/views/Activity/list_recent.html.twig
index e4184da36..aa0bbea0c 100644
--- a/src/Bundle/ChillActivityBundle/Resources/views/Activity/list_recent.html.twig
+++ b/src/Bundle/ChillActivityBundle/Resources/views/Activity/list_recent.html.twig
@@ -41,7 +41,7 @@
{% if activity.user and t.userVisible %}
{{ 'Referrer'|trans ~ ': ' }}
- {{ activity.user|chill_entity_render_box}}
+ {{ activity.user|chill_entity_render_box }}
{% endif %}
diff --git a/src/Bundle/ChillActivityBundle/Resources/views/Activity/show.html.twig b/src/Bundle/ChillActivityBundle/Resources/views/Activity/show.html.twig
index 1ec0ab274..acda43b97 100644
--- a/src/Bundle/ChillActivityBundle/Resources/views/Activity/show.html.twig
+++ b/src/Bundle/ChillActivityBundle/Resources/views/Activity/show.html.twig
@@ -35,7 +35,9 @@
- {{ 'Referrer'|trans|capitalize }}
- - {{ entity.user|chill_entity_render_box }}
+ -
+ {{ entity.user|chill_entity_render_box }}
+
{%- if entity.scope -%}
- {{ 'Scope'|trans }}
@@ -168,7 +170,7 @@
{% if entity.documents|length > 0 %}
{% for d in entity.documents %}
- - {{ d.title }} {{ d|chill_document_button_group() }}
+ - {{ d.title|chill_print_or_message('document.Any title') }} {{ d|chill_document_button_group(d.title, is_granted('CHILL_ACTIVITY_UPDATE', entity), {small: true}) }}
{% endfor %}
{% else %}
diff --git a/src/Bundle/ChillActivityBundle/Service/DocGenerator/ActivityContext.php b/src/Bundle/ChillActivityBundle/Service/DocGenerator/ActivityContext.php
index 4e2970138..3df0d886d 100644
--- a/src/Bundle/ChillActivityBundle/Service/DocGenerator/ActivityContext.php
+++ b/src/Bundle/ChillActivityBundle/Service/DocGenerator/ActivityContext.php
@@ -22,6 +22,7 @@ use Chill\DocStoreBundle\Repository\DocumentCategoryRepository;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\Person;
+use Chill\PersonBundle\Repository\PersonRepository;
use Chill\PersonBundle\Templating\Entity\PersonRenderInterface;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
@@ -45,6 +46,8 @@ class ActivityContext implements
private PersonRenderInterface $personRender;
+ private PersonRepository $personRepository;
+
private TranslatableStringHelperInterface $translatableStringHelper;
private TranslatorInterface $translator;
@@ -55,6 +58,7 @@ class ActivityContext implements
TranslatableStringHelperInterface $translatableStringHelper,
EntityManagerInterface $em,
PersonRenderInterface $personRender,
+ PersonRepository $personRepository,
TranslatorInterface $translator,
BaseContextData $baseContextData
) {
@@ -63,6 +67,7 @@ class ActivityContext implements
$this->translatableStringHelper = $translatableStringHelper;
$this->em = $em;
$this->personRender = $personRender;
+ $this->personRepository = $personRepository;
$this->translator = $translator;
$this->baseContextData = $baseContextData;
}
@@ -147,7 +152,7 @@ class ActivityContext implements
$options = $template->getOptions();
$data = [];
- $data = array_merge($data, $this->baseContextData->getData());
+ $data = array_merge($data, $this->baseContextData->getData($contextGenerationData['creator'] ?? null));
$data['activity'] = $this->normalizer->normalize($entity, 'docgen', ['docgen:expects' => Activity::class, 'groups' => 'docgen:read']);
$data['course'] = $this->normalizer->normalize($entity->getAccompanyingPeriod(), 'docgen', ['docgen:expects' => AccompanyingPeriod::class, 'groups' => 'docgen:read']);
@@ -206,6 +211,32 @@ class ActivityContext implements
return $options['mainPerson'] || $options['person1'] || $options['person2'];
}
+ public function contextGenerationDataNormalize(DocGeneratorTemplate $template, $entity, array $data): array
+ {
+ $normalized = [];
+
+ foreach (['mainPerson', 'person1', 'person2'] as $k) {
+ $normalized[$k] = null === $data[$k] ? null : $data[$k]->getId();
+ }
+
+ return $normalized;
+ }
+
+ public function contextGenerationDataDenormalize(DocGeneratorTemplate $template, $entity, array $data): array
+ {
+ $denormalized = [];
+
+ foreach (['mainPerson', 'person1', 'person2'] as $k) {
+ if (null !== ($id = ($data[$k] ?? null))) {
+ $denormalized[$k] = $this->personRepository->find($id);
+ } else {
+ $denormalized[$k] = null;
+ }
+ }
+
+ return $denormalized;
+ }
+
/**
* @param Activity $entity
*/
diff --git a/src/Bundle/ChillActivityBundle/Service/DocGenerator/ListActivitiesByAccompanyingPeriodContext.php b/src/Bundle/ChillActivityBundle/Service/DocGenerator/ListActivitiesByAccompanyingPeriodContext.php
index 3189307f9..7e1873710 100644
--- a/src/Bundle/ChillActivityBundle/Service/DocGenerator/ListActivitiesByAccompanyingPeriodContext.php
+++ b/src/Bundle/ChillActivityBundle/Service/DocGenerator/ListActivitiesByAccompanyingPeriodContext.php
@@ -146,6 +146,16 @@ class ListActivitiesByAccompanyingPeriodContext implements
return $this->accompanyingPeriodContext->hasPublicForm($template, $entity);
}
+ public function contextGenerationDataNormalize(DocGeneratorTemplate $template, $entity, array $data): array
+ {
+ return $this->accompanyingPeriodContext->contextGenerationDataNormalize($template, $entity, $data);
+ }
+
+ public function contextGenerationDataDenormalize(DocGeneratorTemplate $template, $entity, array $data): array
+ {
+ return $this->accompanyingPeriodContext->contextGenerationDataDenormalize($template, $entity, $data);
+ }
+
public function storeGenerated(DocGeneratorTemplate $template, StoredObject $storedObject, object $entity, array $contextGenerationData): void
{
$this->accompanyingPeriodContext->storeGenerated($template, $storedObject, $entity, $contextGenerationData);
diff --git a/src/Bundle/ChillAsideActivityBundle/src/Export/Export/ListAsideActivity.php b/src/Bundle/ChillAsideActivityBundle/src/Export/Export/ListAsideActivity.php
index bf370f71b..976f4171c 100644
--- a/src/Bundle/ChillAsideActivityBundle/src/Export/Export/ListAsideActivity.php
+++ b/src/Bundle/ChillAsideActivityBundle/src/Export/Export/ListAsideActivity.php
@@ -1,10 +1,18 @@
format('H:i:s');
}
@@ -118,7 +122,7 @@ final class ListAsideActivity implements ListInterface, GroupedExportInterface
case 'createdAt':
case 'updatedAt':
case 'date':
- return $this->dateTimeHelper->getLabel('export.aside_activity.'.$key);
+ return $this->dateTimeHelper->getLabel('export.aside_activity.' . $key);
case 'agent_id':
case 'creator_id':
@@ -165,7 +169,7 @@ final class ListAsideActivity implements ListInterface, GroupedExportInterface
};
default:
- throw new \LogicException('this key is not supported : ' . $key);
+ throw new LogicException('this key is not supported : ' . $key);
}
}
@@ -182,7 +186,7 @@ final class ListAsideActivity implements ListInterface, GroupedExportInterface
'aside_activity_type',
'date',
'duration',
- 'note'
+ 'note',
];
}
@@ -195,6 +199,11 @@ final class ListAsideActivity implements ListInterface, GroupedExportInterface
return $query->getQuery()->getResult(AbstractQuery::HYDRATE_ARRAY);
}
+ public function getTitle()
+ {
+ return 'export.aside_activity.List of aside activities';
+ }
+
public function getType(): string
{
return Declarations::ASIDE_ACTIVITY_TYPE;
@@ -204,8 +213,7 @@ final class ListAsideActivity implements ListInterface, GroupedExportInterface
{
$qb = $this->em->createQueryBuilder()
->from(AsideActivity::class, 'aside')
- ->leftJoin('aside.agent', 'agent')
- ;
+ ->leftJoin('aside.agent', 'agent');
$qb
->addSelect('aside.id AS id')
@@ -218,8 +226,7 @@ final class ListAsideActivity implements ListInterface, GroupedExportInterface
->addSelect('IDENTITY(aside.type) AS aside_activity_type')
->addSelect('aside.date')
->addSelect('aside.duration')
- ->addSelect('aside.note')
- ;
+ ->addSelect('aside.note');
return $qb;
}
diff --git a/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByDateFilter.php b/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByDateFilter.php
index 2d49b3d57..7a1b6f4dc 100644
--- a/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByDateFilter.php
+++ b/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByDateFilter.php
@@ -17,7 +17,6 @@ use Chill\MainBundle\Form\Type\Export\FilterType;
use Chill\MainBundle\Form\Type\PickRollingDateType;
use Chill\MainBundle\Service\RollingDate\RollingDate;
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
-use Doctrine\ORM\Query\Expr\Andx;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormError;
diff --git a/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByUserFilter.php b/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByUserFilter.php
index c2a3b4c54..795c813cd 100644
--- a/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByUserFilter.php
+++ b/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByUserFilter.php
@@ -15,7 +15,6 @@ use Chill\AsideActivityBundle\Export\Declarations;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Form\Type\PickUserDynamicType;
use Chill\MainBundle\Templating\Entity\UserRender;
-use Doctrine\ORM\Query\Expr\Andx;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
diff --git a/src/Bundle/ChillBudgetBundle/Calculator/CalculatorManager.php b/src/Bundle/ChillBudgetBundle/Calculator/CalculatorManager.php
index a37ac68dc..934c22e36 100644
--- a/src/Bundle/ChillBudgetBundle/Calculator/CalculatorManager.php
+++ b/src/Bundle/ChillBudgetBundle/Calculator/CalculatorManager.php
@@ -29,10 +29,10 @@ class CalculatorManager
public function addCalculator(CalculatorInterface $calculator, bool $default)
{
- $this->calculators[$calculator::getAlias()] = $calculator;
+ $this->calculators[$calculator->getAlias()] = $calculator;
if ($default) {
- $this->defaultCalculator[] = $calculator::getAlias();
+ $this->defaultCalculator[] = $calculator->getAlias();
}
}
@@ -50,7 +50,7 @@ class CalculatorManager
$result = $calculator->calculate($elements);
if (null !== $result) {
- $results[$calculator::getAlias()] = $result;
+ $results[$calculator->getAlias()] = $result;
}
}
diff --git a/src/Bundle/ChillBudgetBundle/Form/Admin/ChargeKindType.php b/src/Bundle/ChillBudgetBundle/Form/Admin/ChargeKindType.php
index 59adf49af..c3c2a66bf 100644
--- a/src/Bundle/ChillBudgetBundle/Form/Admin/ChargeKindType.php
+++ b/src/Bundle/ChillBudgetBundle/Form/Admin/ChargeKindType.php
@@ -30,7 +30,7 @@ class ChargeKindType extends AbstractType
])
->add('kind', TextType::class, [
'label' => 'budget.admin.form.Charge_kind_key',
- 'help' => 'budget.admin.form.This kind must contains only alphabeticals characters, and dashes. This string is in use during document generation. Changes may have side effect on document'
+ 'help' => 'budget.admin.form.This kind must contains only alphabeticals characters, and dashes. This string is in use during document generation. Changes may have side effect on document',
])
->add('ordering', NumberType::class)
->add('isActive', CheckboxType::class, [
diff --git a/src/Bundle/ChillBudgetBundle/Form/Admin/ResourceKindType.php b/src/Bundle/ChillBudgetBundle/Form/Admin/ResourceKindType.php
index 0605b8731..41d3a8b53 100644
--- a/src/Bundle/ChillBudgetBundle/Form/Admin/ResourceKindType.php
+++ b/src/Bundle/ChillBudgetBundle/Form/Admin/ResourceKindType.php
@@ -30,7 +30,7 @@ class ResourceKindType extends AbstractType
])
->add('kind', TextType::class, [
'label' => 'budget.admin.form.Resource_kind_key',
- 'help' => 'budget.admin.form.This kind must contains only alphabeticals characters, and dashes. This string is in use during document generation. Changes may have side effect on document'
+ 'help' => 'budget.admin.form.This kind must contains only alphabeticals characters, and dashes. This string is in use during document generation. Changes may have side effect on document',
])
->add('ordering', NumberType::class)
->add('isActive', CheckboxType::class, [
diff --git a/src/Bundle/ChillBudgetBundle/Repository/ChargeKindRepository.php b/src/Bundle/ChillBudgetBundle/Repository/ChargeKindRepository.php
index 10d02749a..01f5cd737 100644
--- a/src/Bundle/ChillBudgetBundle/Repository/ChargeKindRepository.php
+++ b/src/Bundle/ChillBudgetBundle/Repository/ChargeKindRepository.php
@@ -49,8 +49,7 @@ final class ChargeKindRepository implements ChargeKindRepositoryInterface
->where($qb->expr()->eq('c.isActive', 'true'))
->orderBy('c.ordering', 'ASC')
->getQuery()
- ->getResult()
- ;
+ ->getResult();
}
/**
diff --git a/src/Bundle/ChillBudgetBundle/Repository/ChargeKindRepositoryInterface.php b/src/Bundle/ChillBudgetBundle/Repository/ChargeKindRepositoryInterface.php
index 5099a5674..fb8c9dc35 100644
--- a/src/Bundle/ChillBudgetBundle/Repository/ChargeKindRepositoryInterface.php
+++ b/src/Bundle/ChillBudgetBundle/Repository/ChargeKindRepositoryInterface.php
@@ -28,8 +28,6 @@ interface ChargeKindRepositoryInterface extends ObjectRepository
*/
public function findAllActive(): array;
- public function findOneByKind(string $kind): ?ChargeKind;
-
/**
* @return ChargeType[]
*/
@@ -45,5 +43,7 @@ interface ChargeKindRepositoryInterface extends ObjectRepository
public function findOneBy(array $criteria): ?ChargeKind;
+ public function findOneByKind(string $kind): ?ChargeKind;
+
public function getClassName(): string;
}
diff --git a/src/Bundle/ChillBudgetBundle/Repository/ResourceKindRepository.php b/src/Bundle/ChillBudgetBundle/Repository/ResourceKindRepository.php
index 140bb7cc7..f7935c5b7 100644
--- a/src/Bundle/ChillBudgetBundle/Repository/ResourceKindRepository.php
+++ b/src/Bundle/ChillBudgetBundle/Repository/ResourceKindRepository.php
@@ -49,8 +49,7 @@ final class ResourceKindRepository implements ResourceKindRepositoryInterface
->where($qb->expr()->eq('r.isActive', 'true'))
->orderBy('r.ordering', 'ASC')
->getQuery()
- ->getResult()
- ;
+ ->getResult();
}
/**
diff --git a/src/Bundle/ChillBudgetBundle/Repository/ResourceRepository.php b/src/Bundle/ChillBudgetBundle/Repository/ResourceRepository.php
index b3f0a41da..12f9fd52f 100644
--- a/src/Bundle/ChillBudgetBundle/Repository/ResourceRepository.php
+++ b/src/Bundle/ChillBudgetBundle/Repository/ResourceRepository.php
@@ -34,7 +34,7 @@ class ResourceRepository extends EntityRepository
//->andWhere('c.startDate < :date')
// TODO: there is a misconception here, the end date must be lower or null. startDate are never null
//->andWhere('c.startDate < :date OR c.startDate IS NULL');
- ;
+;
if (null !== $sort) {
$qb->orderBy($sort);
diff --git a/src/Bundle/ChillBudgetBundle/Service/Summary/SummaryBudget.php b/src/Bundle/ChillBudgetBundle/Service/Summary/SummaryBudget.php
index 2f401f1ec..ad2a014ed 100644
--- a/src/Bundle/ChillBudgetBundle/Service/Summary/SummaryBudget.php
+++ b/src/Bundle/ChillBudgetBundle/Service/Summary/SummaryBudget.php
@@ -13,9 +13,7 @@ namespace Chill\BudgetBundle\Service\Summary;
use Chill\BudgetBundle\Entity\ChargeKind;
use Chill\BudgetBundle\Entity\ResourceKind;
-use Chill\BudgetBundle\Repository\ChargeKindRepository;
use Chill\BudgetBundle\Repository\ChargeKindRepositoryInterface;
-use Chill\BudgetBundle\Repository\ResourceKindRepository;
use Chill\BudgetBundle\Repository\ResourceKindRepositoryInterface;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Chill\PersonBundle\Entity\Household\Household;
diff --git a/src/Bundle/ChillBudgetBundle/Tests/Service/Summary/SummaryBudgetTest.php b/src/Bundle/ChillBudgetBundle/Tests/Service/Summary/SummaryBudgetTest.php
index cf8c00efe..7fcda6b11 100644
--- a/src/Bundle/ChillBudgetBundle/Tests/Service/Summary/SummaryBudgetTest.php
+++ b/src/Bundle/ChillBudgetBundle/Tests/Service/Summary/SummaryBudgetTest.php
@@ -20,12 +20,15 @@ use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Chill\PersonBundle\Entity\Household\Household;
use Chill\PersonBundle\Entity\Household\HouseholdMember;
use Chill\PersonBundle\Entity\Person;
+use DateTimeImmutable;
use Doctrine\ORM\AbstractQuery;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Query;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait;
+use ReflectionClass;
+use RuntimeException;
/**
* @internal
@@ -47,10 +50,9 @@ final class SummaryBudgetTest extends TestCase
],
]);
$queryCharges->setParameters(Argument::type('array'))
- ->will(function ($args, $query) {
+ ->will(static function ($args, $query) {
return $query;
- })
- ;
+ });
$queryResources = $this->prophesize(AbstractQuery::class);
$queryResources->getResult()->willReturn([
@@ -61,23 +63,23 @@ final class SummaryBudgetTest extends TestCase
],
]);
$queryResources->setParameters(Argument::type('array'))
- ->will(function ($args, $query) {
+ ->will(static function ($args, $query) {
return $query;
- })
- ;
+ });
$em = $this->prophesize(EntityManagerInterface::class);
$em->createNativeQuery(Argument::type('string'), Argument::type(Query\ResultSetMapping::class))
- ->will(function ($args) use ($queryResources, $queryCharges) {
+ ->will(static function ($args) use ($queryResources, $queryCharges) {
if (false !== strpos($args[0], 'chill_budget.resource')) {
return $queryResources->reveal();
}
+
if (false !== strpos($args[0], 'chill_budget.charge')) {
return $queryCharges->reveal();
}
- throw new \RuntimeException('this query does not have a stub counterpart: '.$args[0]);
- })
- ;
+
+ throw new RuntimeException('this query does not have a stub counterpart: ' . $args[0]);
+ });
$chargeRepository = $this->prophesize(ChargeKindRepositoryInterface::class);
$chargeRepository->findAll()->willReturn([
@@ -98,24 +100,23 @@ final class SummaryBudgetTest extends TestCase
$resourceRepository->findOneByKind('misc')->willReturn($misc);
$translatableStringHelper = $this->prophesize(TranslatableStringHelperInterface::class);
- $translatableStringHelper->localize(Argument::type('array'))->will(function ($arg) {
+ $translatableStringHelper->localize(Argument::type('array'))->will(static function ($arg) {
return $arg[0]['fr'];
});
$person = new Person();
- $personReflection = new \ReflectionClass($person);
+ $personReflection = new ReflectionClass($person);
$personIdReflection = $personReflection->getProperty('id');
$personIdReflection->setAccessible(true);
$personIdReflection->setValue($person, 1);
$household = new Household();
- $householdReflection = new \ReflectionClass($household);
+ $householdReflection = new ReflectionClass($household);
$householdId = $householdReflection->getProperty('id');
$householdId->setAccessible(true);
$householdId->setValue($household, 1);
$householdMember = (new HouseholdMember())->setPerson($person)
- ->setStartDate(new \DateTimeImmutable('1 month ago'))
- ;
+ ->setStartDate(new DateTimeImmutable('1 month ago'));
$household->addMember($householdMember);
$summaryBudget = new SummaryBudget(
diff --git a/src/Bundle/ChillBudgetBundle/migrations/Version20230209161546.php b/src/Bundle/ChillBudgetBundle/migrations/Version20230209161546.php
index 618b0c982..4495db8a7 100644
--- a/src/Bundle/ChillBudgetBundle/migrations/Version20230209161546.php
+++ b/src/Bundle/ChillBudgetBundle/migrations/Version20230209161546.php
@@ -2,6 +2,13 @@
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\Budget;
use Doctrine\DBAL\Schema\Schema;
@@ -9,6 +16,12 @@ use Doctrine\Migrations\AbstractMigration;
final class Version20230209161546 extends AbstractMigration
{
+ public function down(Schema $schema): void
+ {
+ $this->addSql('DROP INDEX resource_kind_unique_type_idx');
+ $this->addSql('DROP INDEX charge_kind_unique_type_idx');
+ }
+
public function getDescription(): string
{
return 'Budget: add unique constraint on kind for charge_kind and resource_kind';
@@ -21,10 +34,4 @@ final class Version20230209161546 extends AbstractMigration
$this->addSql('CREATE UNIQUE INDEX resource_kind_unique_type_idx ON chill_budget.resource_type (kind);');
$this->addSql('CREATE UNIQUE INDEX charge_kind_unique_type_idx ON chill_budget.charge_type (kind);');
}
-
- public function down(Schema $schema): void
- {
- $this->addSql('DROP INDEX resource_kind_unique_type_idx');
- $this->addSql('DROP INDEX charge_kind_unique_type_idx');
- }
}
diff --git a/src/Bundle/ChillBudgetBundle/translations/messages.nl.yml b/src/Bundle/ChillBudgetBundle/translations/messages.nl.yml
index 5fd21520d..de334f79f 100644
--- a/src/Bundle/ChillBudgetBundle/translations/messages.nl.yml
+++ b/src/Bundle/ChillBudgetBundle/translations/messages.nl.yml
@@ -2,8 +2,8 @@ Budget: Budget
Resource: Inkomsten
Charge: Onkosten
Budget for %name%: Budget van %name%
-Budget for household %household%: Budget van gezin
-Current budget household members: Actuele budget van gezinsleden
+Budget for household %household%: Budget van huishouden
+Current budget household members: Actuele budget van leden huishouden
Show budget of %name%: Toon budget van %name%
See complete budget: Toon volledige budget
Hide budget: Verbergen
diff --git a/src/Bundle/ChillCalendarBundle/DataFixtures/ORM/LoadCalendarRange.php b/src/Bundle/ChillCalendarBundle/DataFixtures/ORM/LoadCalendarRange.php
index a1226ca6a..805386669 100644
--- a/src/Bundle/ChillCalendarBundle/DataFixtures/ORM/LoadCalendarRange.php
+++ b/src/Bundle/ChillCalendarBundle/DataFixtures/ORM/LoadCalendarRange.php
@@ -61,7 +61,7 @@ class LoadCalendarRange extends Fixture implements FixtureGroupInterface, Ordere
->setEmail('centreA@test.chill.social')
->setLocationType($type = new LocationType())
->setPhonenumber1(PhoneNumberUtil::getInstance()->parse('+3287653812'));
- $type->setTitle('Service');
+ $type->setTitle(['fr' => 'Service']);
$address->setStreet('Rue des Épaules')->setStreetNumber('14')
->setPostcode($postCode = new PostalCode());
$postCode->setCode('4145')->setName('Houte-Si-Plout')->setCountry(
diff --git a/src/Bundle/ChillCalendarBundle/DataFixtures/ORM/LoadInvite.php b/src/Bundle/ChillCalendarBundle/DataFixtures/ORM/LoadInvite.php
index 25b8ae8a8..ba325e296 100644
--- a/src/Bundle/ChillCalendarBundle/DataFixtures/ORM/LoadInvite.php
+++ b/src/Bundle/ChillCalendarBundle/DataFixtures/ORM/LoadInvite.php
@@ -12,6 +12,8 @@ declare(strict_types=1);
namespace Chill\CalendarBundle\DataFixtures\ORM;
use Chill\CalendarBundle\Entity\Invite;
+use Chill\MainBundle\DataFixtures\ORM\LoadUsers;
+use Chill\MainBundle\Entity\User;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Bundle\FixturesBundle\FixtureGroupInterface;
use Doctrine\Persistence\ObjectManager;
@@ -33,14 +35,21 @@ class LoadInvite extends Fixture implements FixtureGroupInterface
public function load(ObjectManager $manager): void
{
$arr = [
- ['name' => ['fr' => 'Rendez-vous décliné']],
- ['name' => ['fr' => 'Rendez-vous accepté']],
+ [
+ 'name' => ['fr' => 'Rendez-vous décliné'],
+ 'status' => Invite::DECLINED,
+ ],
+ [
+ 'name' => ['fr' => 'Rendez-vous accepté'],
+ 'status' => Invite::ACCEPTED,
+ ],
];
foreach ($arr as $a) {
echo 'Creating calendar invite : ' . $a['name']['fr'] . "\n";
$invite = (new Invite())
- ->setStatus($a['name']);
+ ->setStatus($a['status'])
+ ->setUser($this->getRandomUser());
$manager->persist($invite);
$reference = 'Invite_' . $a['name']['fr'];
$this->addReference($reference, $invite);
@@ -49,4 +58,11 @@ class LoadInvite extends Fixture implements FixtureGroupInterface
$manager->flush();
}
+
+ private function getRandomUser(): User
+ {
+ $userRef = array_rand(LoadUsers::$refs);
+
+ return $this->getReference($userRef);
+ }
}
diff --git a/src/Bundle/ChillCalendarBundle/RemoteCalendar/Connector/MSGraph/MapCalendarToUser.php b/src/Bundle/ChillCalendarBundle/RemoteCalendar/Connector/MSGraph/MapCalendarToUser.php
index 504d48ffc..563bd3a38 100644
--- a/src/Bundle/ChillCalendarBundle/RemoteCalendar/Connector/MSGraph/MapCalendarToUser.php
+++ b/src/Bundle/ChillCalendarBundle/RemoteCalendar/Connector/MSGraph/MapCalendarToUser.php
@@ -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;
}
diff --git a/src/Bundle/ChillCalendarBundle/RemoteCalendar/Connector/MSGraphRemoteCalendarConnector.php b/src/Bundle/ChillCalendarBundle/RemoteCalendar/Connector/MSGraphRemoteCalendarConnector.php
index c376f9680..d409e6f03 100644
--- a/src/Bundle/ChillCalendarBundle/RemoteCalendar/Connector/MSGraphRemoteCalendarConnector.php
+++ b/src/Bundle/ChillCalendarBundle/RemoteCalendar/Connector/MSGraphRemoteCalendarConnector.php
@@ -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();
}
diff --git a/src/Bundle/ChillCalendarBundle/Resources/views/Calendar/_documents.twig.html b/src/Bundle/ChillCalendarBundle/Resources/views/Calendar/_documents.twig.html
index 499fb0a83..09824b5cc 100644
--- a/src/Bundle/ChillCalendarBundle/Resources/views/Calendar/_documents.twig.html
+++ b/src/Bundle/ChillCalendarBundle/Resources/views/Calendar/_documents.twig.html
@@ -17,30 +17,20 @@
-
- {{ mm.mimeIcon(d.storedObject.type) }}
- {{ d.storedObject.title }}
- {% if d.dateTimeVersion < d.calendar.dateTimeVersion %}
- {{ 'chill_calendar.Document outdated'|trans }}
- {% endif %}
-
-
- {% if chill_document_is_editable(d.storedObject) and is_granted('CHILL_CALENDAR_DOC_EDIT', d) %}
- -
-
-
- -
- {{ d.storedObject|chill_document_edit_button }}
-
- {% endif %}
- {% if is_granted('CHILL_CALENDAR_DOC_EDIT', d) %}
- -
-
-
- {% endif %}
- -
- {{ m.download_button(d.storedObject, d.storedObject.title) }}
-
-
+
+
+ {{ d.storedObject.title }}
+ {% if d.dateTimeVersion < d.calendar.dateTimeVersion %}
+ {{ 'chill_calendar.Document outdated'|trans }}
+ {% endif %}
+
+
+ {{ mm.mimeIcon(d.storedObject.type) }}
+
+
+ {{ d.storedObject|chill_document_button_group(d.storedObject.title, is_granted('CHILL_CALENDAR_DOC_EDIT', d), {'small': true}) }}
+
+
|
diff --git a/src/Bundle/ChillCalendarBundle/Resources/views/Calendar/listByAccompanyingCourse.html.twig b/src/Bundle/ChillCalendarBundle/Resources/views/Calendar/listByAccompanyingCourse.html.twig
index d85c5237e..7ce1003bc 100644
--- a/src/Bundle/ChillCalendarBundle/Resources/views/Calendar/listByAccompanyingCourse.html.twig
+++ b/src/Bundle/ChillCalendarBundle/Resources/views/Calendar/listByAccompanyingCourse.html.twig
@@ -10,13 +10,13 @@
{% block js %}
{{ parent() }}
{{ encore_entry_script_tags('mod_answer') }}
- {{ encore_entry_script_tags('mod_async_upload') }}
+ {{ encore_entry_script_tags('mod_document_action_buttons_group') }}
{% endblock %}
{% block css %}
{{ parent() }}
{{ encore_entry_link_tags('mod_answer') }}
- {{ encore_entry_link_tags('mod_async_upload') }}
+ {{ encore_entry_link_tags('mod_document_action_buttons_group') }}
{% endblock %}
{% block content %}
diff --git a/src/Bundle/ChillCalendarBundle/Service/DocGenerator/CalendarContext.php b/src/Bundle/ChillCalendarBundle/Service/DocGenerator/CalendarContext.php
index 9ba9e36b4..cba7fc661 100644
--- a/src/Bundle/ChillCalendarBundle/Service/DocGenerator/CalendarContext.php
+++ b/src/Bundle/ChillCalendarBundle/Service/DocGenerator/CalendarContext.php
@@ -18,8 +18,10 @@ use Chill\DocGeneratorBundle\Service\Context\BaseContextData;
use Chill\DocStoreBundle\Entity\StoredObject;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Chill\PersonBundle\Entity\Person;
+use Chill\PersonBundle\Repository\PersonRepository;
use Chill\PersonBundle\Templating\Entity\PersonRender;
use Chill\ThirdPartyBundle\Entity\ThirdParty;
+use Chill\ThirdPartyBundle\Repository\ThirdPartyRepository;
use Chill\ThirdPartyBundle\Templating\Entity\ThirdPartyRender;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
@@ -39,6 +41,10 @@ final class CalendarContext implements CalendarContextInterface
private PersonRender $personRender;
+ private PersonRepository $personRepository;
+
+ private ThirdPartyRepository $thirdPartyRepository;
+
private ThirdPartyRender $thirdPartyRender;
private TranslatableStringHelperInterface $translatableStringHelper;
@@ -48,14 +54,18 @@ final class CalendarContext implements CalendarContextInterface
EntityManagerInterface $entityManager,
NormalizerInterface $normalizer,
PersonRender $personRender,
+ PersonRepository $personRepository,
ThirdPartyRender $thirdPartyRender,
+ ThirdPartyRepository $thirdPartyRepository,
TranslatableStringHelperInterface $translatableStringHelper
) {
$this->baseContextData = $baseContextData;
$this->entityManager = $entityManager;
$this->normalizer = $normalizer;
$this->personRender = $personRender;
+ $this->personRepository = $personRepository;
$this->thirdPartyRender = $thirdPartyRender;
+ $this->thirdPartyRepository = $thirdPartyRepository;
$this->translatableStringHelper = $translatableStringHelper;
}
@@ -146,7 +156,7 @@ final class CalendarContext implements CalendarContextInterface
$options = $this->getOptions($template);
$data = array_merge(
- $this->baseContextData->getData(),
+ $this->baseContextData->getData($contextGenerationData['creator'] ?? null),
[
'calendar' => $this->normalizer->normalize($entity, 'docgen', ['docgen:expects' => Calendar::class, 'groups' => ['docgen:read']]),
]
@@ -226,8 +236,44 @@ final class CalendarContext implements CalendarContextInterface
return true;
}
+ public function contextGenerationDataNormalize(DocGeneratorTemplate $template, $entity, array $data): array
+ {
+ $normalized = [];
+ $normalized['title'] = $data['title'] ?? '';
+
+ foreach (['mainPerson', 'thirdParty'] as $k) {
+ if (isset($data[$k])) {
+ $normalized[$k] = $data[$k]->getId();
+ }
+ }
+
+ return $normalized;
+ }
+
+ public function contextGenerationDataDenormalize(DocGeneratorTemplate $template, $entity, array $data): array
+ {
+ $denormalized = [];
+ $denormalized['title'] = $data['title'];
+
+ if (null !== ($data['mainPerson'] ?? null)) {
+ if (null === $person = $this->personRepository->find($data['mainPerson'])) {
+ throw new \RuntimeException('person not found');
+ }
+ $denormalized['mainPerson'] = $person;
+ }
+
+ if (null !== ($data['thirdParty'] ?? null)) {
+ if (null === $thirdParty = $this->thirdPartyRepository->find($data['thirdParty'])) {
+ throw new \RuntimeException('third party not found');
+ }
+ $denormalized['thirdParty'] = $thirdParty;
+ }
+
+ return $denormalized;
+ }
+
/**
- * @param array{mainPerson?: Person, thirdParty?: ThirdParty, title: string} $contextGenerationData
+ * param array{mainPerson?: Person, thirdParty?: ThirdParty, title: string} $contextGenerationData
*/
public function storeGenerated(DocGeneratorTemplate $template, StoredObject $storedObject, object $entity, array $contextGenerationData): void
{
diff --git a/src/Bundle/ChillCalendarBundle/Service/DocGenerator/CalendarContextInterface.php b/src/Bundle/ChillCalendarBundle/Service/DocGenerator/CalendarContextInterface.php
index d02cdc2c2..eeef3b417 100644
--- a/src/Bundle/ChillCalendarBundle/Service/DocGenerator/CalendarContextInterface.php
+++ b/src/Bundle/ChillCalendarBundle/Service/DocGenerator/CalendarContextInterface.php
@@ -56,6 +56,10 @@ interface CalendarContextInterface extends DocGeneratorContextWithPublicFormInte
*/
public function hasPublicForm(DocGeneratorTemplate $template, $entity): bool;
+ public function contextGenerationDataNormalize(DocGeneratorTemplate $template, $entity, array $data): array;
+
+ public function contextGenerationDataDenormalize(DocGeneratorTemplate $template, $entity, array $data): array;
+
/**
* @param Calendar $entity
*/
diff --git a/src/Bundle/ChillCalendarBundle/Tests/Service/DocGenerator/CalendarContextTest.php b/src/Bundle/ChillCalendarBundle/Tests/Service/DocGenerator/CalendarContextTest.php
index be31485d4..cbb4ea3af 100644
--- a/src/Bundle/ChillCalendarBundle/Tests/Service/DocGenerator/CalendarContextTest.php
+++ b/src/Bundle/ChillCalendarBundle/Tests/Service/DocGenerator/CalendarContextTest.php
@@ -205,7 +205,7 @@ final class CalendarContextTest extends TestCase
?NormalizerInterface $normalizer = null
): CalendarContext {
$baseContext = $this->prophesize(BaseContextData::class);
- $baseContext->getData()->willReturn(['base_context' => 'data']);
+ $baseContext->getData(null)->willReturn(['base_context' => 'data']);
$personRender = $this->prophesize(PersonRender::class);
$personRender->renderString(Argument::type(Person::class), [])->willReturn('person name');
diff --git a/src/Bundle/ChillDocGeneratorBundle/Context/DocGeneratorContextWithPublicFormInterface.php b/src/Bundle/ChillDocGeneratorBundle/Context/DocGeneratorContextWithPublicFormInterface.php
index f013c8435..4f58bd049 100644
--- a/src/Bundle/ChillDocGeneratorBundle/Context/DocGeneratorContextWithPublicFormInterface.php
+++ b/src/Bundle/ChillDocGeneratorBundle/Context/DocGeneratorContextWithPublicFormInterface.php
@@ -23,6 +23,9 @@ interface DocGeneratorContextWithPublicFormInterface extends DocGeneratorContext
*/
public function buildPublicForm(FormBuilderInterface $builder, DocGeneratorTemplate $template, $entity): void;
+ /**
+ * Fill the form with initial data
+ */
public function getFormData(DocGeneratorTemplate $template, $entity): array;
/**
@@ -31,4 +34,14 @@ interface DocGeneratorContextWithPublicFormInterface extends DocGeneratorContext
* @param mixed $entity
*/
public function hasPublicForm(DocGeneratorTemplate $template, $entity): bool;
+
+ /**
+ * Transform the data from the form into serializable data, storable into messenger's message
+ */
+ public function contextGenerationDataNormalize(DocGeneratorTemplate $template, $entity, array $data): array;
+
+ /**
+ * Reverse the data from the messenger's message into data usable for doc's generation
+ */
+ public function contextGenerationDataDenormalize(DocGeneratorTemplate $template, $entity, array $data): array;
}
diff --git a/src/Bundle/ChillDocGeneratorBundle/Controller/DocGeneratorTemplateController.php b/src/Bundle/ChillDocGeneratorBundle/Controller/DocGeneratorTemplateController.php
index d618f758a..eeab4932b 100644
--- a/src/Bundle/ChillDocGeneratorBundle/Controller/DocGeneratorTemplateController.php
+++ b/src/Bundle/ChillDocGeneratorBundle/Controller/DocGeneratorTemplateController.php
@@ -16,67 +16,58 @@ use Chill\DocGeneratorBundle\Context\DocGeneratorContextWithPublicFormInterface;
use Chill\DocGeneratorBundle\Context\Exception\ContextNotFoundException;
use Chill\DocGeneratorBundle\Entity\DocGeneratorTemplate;
use Chill\DocGeneratorBundle\GeneratorDriver\DriverInterface;
-use Chill\DocGeneratorBundle\GeneratorDriver\Exception\TemplateException;
use Chill\DocGeneratorBundle\Repository\DocGeneratorTemplateRepository;
+use Chill\DocGeneratorBundle\Service\Generator\GeneratorInterface;
+use Chill\DocGeneratorBundle\Service\Messenger\RequestGenerationMessage;
use Chill\DocStoreBundle\Entity\StoredObject;
use Chill\DocStoreBundle\Service\StoredObjectManagerInterface;
use Chill\MainBundle\Pagination\PaginatorFactory;
use Chill\MainBundle\Serializer\Model\Collection;
use Doctrine\ORM\EntityManagerInterface;
-use Exception;
use Psr\Log\LoggerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\FileType;
-use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
// TODO à mettre dans services
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
+use Symfony\Component\Messenger\MessageBusInterface;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
-use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
use Symfony\Contracts\HttpClient\HttpClientInterface;
-use Throwable;
use function strlen;
+use const JSON_PRETTY_PRINT;
final class DocGeneratorTemplateController extends AbstractController
{
- private HttpClientInterface $client;
-
private ContextManager $contextManager;
private DocGeneratorTemplateRepository $docGeneratorTemplateRepository;
- private DriverInterface $driver;
-
private EntityManagerInterface $entityManager;
- private LoggerInterface $logger;
+ private GeneratorInterface $generator;
+
+ private MessageBusInterface $messageBus;
private PaginatorFactory $paginatorFactory;
- private StoredObjectManagerInterface $storedObjectManager;
-
public function __construct(
ContextManager $contextManager,
DocGeneratorTemplateRepository $docGeneratorTemplateRepository,
- DriverInterface $driver,
- LoggerInterface $logger,
+ GeneratorInterface $generator,
+ MessageBusInterface $messageBus,
PaginatorFactory $paginatorFactory,
- HttpClientInterface $client,
- StoredObjectManagerInterface $storedObjectManager,
EntityManagerInterface $entityManager
) {
$this->contextManager = $contextManager;
$this->docGeneratorTemplateRepository = $docGeneratorTemplateRepository;
- $this->driver = $driver;
- $this->logger = $logger;
+ $this->generator = $generator;
+ $this->messageBus = $messageBus;
$this->paginatorFactory = $paginatorFactory;
- $this->client = $client;
- $this->storedObjectManager = $storedObjectManager;
$this->entityManager = $entityManager;
}
@@ -94,7 +85,6 @@ final class DocGeneratorTemplateController extends AbstractController
): Response {
return $this->generateDocFromTemplate(
$template,
- $entityClassName,
$entityId,
$request,
true
@@ -115,7 +105,6 @@ final class DocGeneratorTemplateController extends AbstractController
): Response {
return $this->generateDocFromTemplate(
$template,
- $entityClassName,
$entityId,
$request,
false
@@ -185,7 +174,6 @@ final class DocGeneratorTemplateController extends AbstractController
private function generateDocFromTemplate(
DocGeneratorTemplate $template,
- string $entityClassName,
int $entityId,
Request $request,
bool $isTest
@@ -206,7 +194,7 @@ final class DocGeneratorTemplateController extends AbstractController
if (null === $entity) {
throw new NotFoundHttpException(
- sprintf('Entity with classname %s and id %s is not found', $entityClassName, $entityId)
+ sprintf('Entity with classname %s and id %s is not found', $context->getEntityClass(), $entityId)
);
}
@@ -259,99 +247,68 @@ final class DocGeneratorTemplateController extends AbstractController
}
}
- $document = $template->getFile();
-
- if ($isTest && ($contextGenerationData['test_file'] instanceof File)) {
- $dataDecrypted = file_get_contents($contextGenerationData['test_file']->getPathname());
- } else {
- try {
- $dataDecrypted = $this->storedObjectManager->read($document);
- } catch (Throwable $exception) {
- throw $exception;
- }
- }
+ // transform context generation data
+ $contextGenerationDataSanitized =
+ $context instanceof DocGeneratorContextWithPublicFormInterface ?
+ $context->contextGenerationDataNormalize($template, $entity, $contextGenerationData)
+ : [];
+ // if is test, render the data or generate the doc
if ($isTest && isset($form) && $form['show_data']->getData()) {
return $this->render('@ChillDocGenerator/Generator/debug_value.html.twig', [
- 'datas' => json_encode($context->getData($template, $entity, $contextGenerationData), JSON_PRETTY_PRINT)
+ 'datas' => json_encode($context->getData($template, $entity, $contextGenerationData), JSON_PRETTY_PRINT),
]);
- }
-
- try {
- $generatedResource = $this
- ->driver
- ->generateFromString(
- $dataDecrypted,
- $template->getFile()->getType(),
- $context->getData($template, $entity, $contextGenerationData),
- $template->getFile()->getFilename()
- );
- } catch (TemplateException $e) {
- return new Response(
- implode("\n", $e->getErrors()),
- 400,
- [
- 'Content-Type' => 'text/plain',
- ]
+ } elseif ($isTest) {
+ $generated = $this->generator->generateDocFromTemplate(
+ $template,
+ $entityId,
+ $contextGenerationDataSanitized,
+ null,
+ true,
+ isset($form) ? $form['test_file']->getData() : null
);
- }
- if ($isTest) {
return new Response(
- $generatedResource,
+ $generated,
Response::HTTP_OK,
[
'Content-Transfer-Encoding', 'binary',
'Content-Type' => 'application/vnd.oasis.opendocument.text',
'Content-Disposition' => 'attachment; filename="generated.odt"',
- 'Content-Length' => strlen($generatedResource),
+ 'Content-Length' => strlen($generated),
],
);
}
- /** @var StoredObject $storedObject */
- $storedObject = (new ObjectNormalizer())
- ->denormalize(
- [
- 'type' => $template->getFile()->getType(),
- 'filename' => sprintf('%s_odt', uniqid('doc_', true)),
- ],
- StoredObject::class
- );
-
- try {
- $this->storedObjectManager->write($storedObject, $generatedResource);
- } catch (Throwable $exception) {
- throw $exception;
- }
+ // this is not a test
+ // we prepare the object to store the document
+ $storedObject = (new StoredObject())
+ ->setStatus(StoredObject::STATUS_PENDING)
+ ;
$this->entityManager->persist($storedObject);
- try {
- $context
- ->storeGenerated(
- $template,
- $storedObject,
- $entity,
- $contextGenerationData
- );
- } catch (Exception $e) {
- $this
- ->logger
- ->error(
- 'Unable to store the associated document to entity',
- [
- 'entityClassName' => $entityClassName,
- 'entityId' => $entityId,
- 'contextKey' => $context->getName(),
- ]
- );
-
- throw $e;
- }
+ // we store the generated document
+ $context
+ ->storeGenerated(
+ $template,
+ $storedObject,
+ $entity,
+ $contextGenerationData
+ );
$this->entityManager->flush();
+ $this->messageBus->dispatch(
+ new RequestGenerationMessage(
+ $this->getUser(),
+ $template,
+ $entityId,
+ $storedObject,
+ $contextGenerationDataSanitized,
+ )
+ );
+
return $this
->redirectToRoute(
'chill_wopi_file_edit',
diff --git a/src/Bundle/ChillDocGeneratorBundle/Resources/views/Email/on_generation_failed_email.txt.twig b/src/Bundle/ChillDocGeneratorBundle/Resources/views/Email/on_generation_failed_email.txt.twig
new file mode 100644
index 000000000..c4ca7079d
--- /dev/null
+++ b/src/Bundle/ChillDocGeneratorBundle/Resources/views/Email/on_generation_failed_email.txt.twig
@@ -0,0 +1,16 @@
+{{ creator.label }},
+
+{{ 'docgen.failure_email.The generation of the document {template_name} failed'|trans({'{template_name}': template.name|localize_translatable_string}) }}
+
+{{ 'docgen.failure_email.Forward this email to your administrator for solving'|trans }}
+
+{{ 'docgen.failure_email.References'|trans }}:
+{% if errors|length > 0 %}
+{{ 'docgen.failure_email.The following errors were encoutered'|trans }}:
+
+{% for error in errors %}
+- {{ error }}
+{% endfor %}
+{% endif %}
+- template_id: {{ template.id }}
+- stored_object_destination_id: {{ stored_object_id }}
diff --git a/src/Bundle/ChillDocGeneratorBundle/Service/Context/BaseContextData.php b/src/Bundle/ChillDocGeneratorBundle/Service/Context/BaseContextData.php
index e7b56ed88..5aed8554b 100644
--- a/src/Bundle/ChillDocGeneratorBundle/Service/Context/BaseContextData.php
+++ b/src/Bundle/ChillDocGeneratorBundle/Service/Context/BaseContextData.php
@@ -21,18 +21,14 @@ class BaseContextData
{
private NormalizerInterface $normalizer;
- private Security $security;
-
- public function __construct(Security $security, NormalizerInterface $normalizer)
+ public function __construct(NormalizerInterface $normalizer)
{
- $this->security = $security;
$this->normalizer = $normalizer;
}
- public function getData(): array
+ public function getData(?User $user = null): array
{
$data = [];
- $user = $this->security->getUser();
$data['creator'] = $this->normalizer->normalize(
$user instanceof User ? $user : null,
diff --git a/src/Bundle/ChillDocGeneratorBundle/Service/Generator/Generator.php b/src/Bundle/ChillDocGeneratorBundle/Service/Generator/Generator.php
index 755e608ba..4ddcb461c 100644
--- a/src/Bundle/ChillDocGeneratorBundle/Service/Generator/Generator.php
+++ b/src/Bundle/ChillDocGeneratorBundle/Service/Generator/Generator.php
@@ -3,16 +3,18 @@
namespace Chill\DocGeneratorBundle\Service\Generator;
use Chill\DocGeneratorBundle\Context\ContextManagerInterface;
+use Chill\DocGeneratorBundle\Context\DocGeneratorContextWithPublicFormInterface;
use Chill\DocGeneratorBundle\Entity\DocGeneratorTemplate;
use Chill\DocGeneratorBundle\GeneratorDriver\DriverInterface;
use Chill\DocGeneratorBundle\GeneratorDriver\Exception\TemplateException;
use Chill\DocStoreBundle\Entity\StoredObject;
use Chill\DocStoreBundle\Service\StoredObjectManagerInterface;
+use Chill\MainBundle\Entity\User;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpFoundation\File\File;
-class Generator
+class Generator implements GeneratorInterface
{
private ContextManagerInterface $contextManager;
@@ -24,6 +26,8 @@ class Generator
private StoredObjectManagerInterface $storedObjectManager;
+ private const LOG_PREFIX = '[docgen generator] ';
+
public function __construct(
ContextManagerInterface $contextManager,
DriverInterface $driver,
@@ -48,18 +52,24 @@ class Generator
*/
public function generateDocFromTemplate(
DocGeneratorTemplate $template,
- string $entityClassName,
int $entityId,
+ array $contextGenerationDataNormalized,
?StoredObject $destinationStoredObject = null,
bool $isTest = false,
- ?File $testFile = null
+ ?File $testFile = null,
+ ?User $creator = null
): ?string {
if ($destinationStoredObject instanceof StoredObject && StoredObject::STATUS_PENDING !== $destinationStoredObject->getStatus()) {
+ $this->logger->info(self::LOG_PREFIX.'Aborting generation of an already generated document');
throw new ObjectReadyException();
}
+ $this->logger->info(self::LOG_PREFIX.'Starting generation of a document', [
+ 'entity_id' => $entityId,
+ 'destination_stored_object' => $destinationStoredObject === null ? null : $destinationStoredObject->getId()
+ ]);
+
$context = $this->contextManager->getContextByDocGeneratorTemplate($template);
- $contextGenerationData = ['test_file' => $testFile];
$entity = $this
->entityManager
@@ -67,22 +77,39 @@ class Generator
;
if (null === $entity) {
- throw new RelatedEntityNotFoundException($entityClassName, $entityId);
+ throw new RelatedEntityNotFoundException($template->getEntity(), $entityId);
+ }
+
+ $contextGenerationDataNormalized = array_merge(
+ $contextGenerationDataNormalized,
+ ['creator' => $creator],
+ $context instanceof DocGeneratorContextWithPublicFormInterface ?
+ $context->contextGenerationDataDenormalize($template, $entity, $contextGenerationDataNormalized)
+ : []
+ );
+
+ $data = $context->getData($template, $entity, $contextGenerationDataNormalized);
+
+ $destinationStoredObjectId = $destinationStoredObject instanceof StoredObject ? $destinationStoredObject->getId() : null;
+ $this->entityManager->clear();
+ gc_collect_cycles();
+ if (null !== $destinationStoredObjectId) {
+ $destinationStoredObject = $this->entityManager->find(StoredObject::class, $destinationStoredObjectId);
}
if ($isTest && ($testFile instanceof File)) {
- $dataDecrypted = file_get_contents($testFile->getPathname());
+ $templateDecrypted = file_get_contents($testFile->getPathname());
} else {
- $dataDecrypted = $this->storedObjectManager->read($template->getFile());
+ $templateDecrypted = $this->storedObjectManager->read($template->getFile());
}
try {
$generatedResource = $this
->driver
->generateFromString(
- $dataDecrypted,
+ $templateDecrypted,
$template->getFile()->getType(),
- $context->getData($template, $entity, $contextGenerationData),
+ $data,
$template->getFile()->getFilename()
);
} catch (TemplateException $e) {
@@ -90,6 +117,11 @@ class Generator
}
if ($isTest) {
+ $this->logger->info(self::LOG_PREFIX.'Finished generation of a document', [
+ 'is_test' => true,
+ 'entity_id' => $entityId,
+ 'destination_stored_object' => $destinationStoredObject === null ? null : $destinationStoredObject->getId()
+ ]);
return $generatedResource;
}
@@ -102,31 +134,13 @@ class Generator
$this->storedObjectManager->write($destinationStoredObject, $generatedResource);
- try {
- $context
- ->storeGenerated(
- $template,
- $destinationStoredObject,
- $entity,
- $contextGenerationData
- );
- } catch (\Exception $e) {
- $this
- ->logger
- ->error(
- 'Unable to store the associated document to entity',
- [
- 'entityClassName' => $entityClassName,
- 'entityId' => $entityId,
- 'contextKey' => $context->getName(),
- ]
- );
-
- throw $e;
- }
-
$this->entityManager->flush();
+ $this->logger->info(self::LOG_PREFIX.'Finished generation of a document', [
+ 'entity_id' => $entityId,
+ 'destination_stored_object' => $destinationStoredObject->getId(),
+ ]);
+
return null;
}
}
diff --git a/src/Bundle/ChillDocGeneratorBundle/Service/Generator/GeneratorException.php b/src/Bundle/ChillDocGeneratorBundle/Service/Generator/GeneratorException.php
index 423d59dd4..4360413b4 100644
--- a/src/Bundle/ChillDocGeneratorBundle/Service/Generator/GeneratorException.php
+++ b/src/Bundle/ChillDocGeneratorBundle/Service/Generator/GeneratorException.php
@@ -1,18 +1,41 @@
*/
private array $errors;
- public function __construct(array $errors = [], \Throwable $previous = null)
+ public function __construct(array $errors = [], ?Throwable $previous = null)
{
$this->errors = $errors;
- parent::__construct("Could not generate the document", 15252,
- $previous);
+ parent::__construct(
+ 'Could not generate the document',
+ 15252,
+ $previous
+ );
+ }
+
+ /**
+ * @return array
+ */
+ public function getErrors(): array
+ {
+ return $this->errors;
}
}
diff --git a/src/Bundle/ChillDocGeneratorBundle/Service/Generator/GeneratorInterface.php b/src/Bundle/ChillDocGeneratorBundle/Service/Generator/GeneratorInterface.php
new file mode 100644
index 000000000..a8f01b97c
--- /dev/null
+++ b/src/Bundle/ChillDocGeneratorBundle/Service/Generator/GeneratorInterface.php
@@ -0,0 +1,29 @@
+docGeneratorTemplateRepository = $docGeneratorTemplateRepository;
+ $this->entityManager = $entityManager;
+ $this->logger = $logger;
+ $this->mailer = $mailer;
+ $this->storedObjectRepository = $storedObjectRepository;
+ $this->translator = $translator;
+ $this->userRepository = $userRepository;
+ }
+
+
+ public static function getSubscribedEvents()
+ {
+ return [
+ WorkerMessageFailedEvent::class => 'onMessageFailed'
+ ];
+ }
+
+ public function onMessageFailed(WorkerMessageFailedEvent $event): void
+ {
+ if ($event->willRetry()) {
+ return;
+ }
+
+ if (!$event->getEnvelope()->getMessage() instanceof RequestGenerationMessage) {
+ return;
+ }
+
+ /** @var \Chill\DocGeneratorBundle\Service\Messenger\RequestGenerationMessage $message */
+ $message = $event->getEnvelope()->getMessage();
+
+ $this->logger->error(self::LOG_PREFIX.'Docgen failed', [
+ 'stored_object_id' => $message->getDestinationStoredObjectId(),
+ 'entity_id' => $message->getEntityId(),
+ 'template_id' => $message->getTemplateId(),
+ 'creator_id' => $message->getCreatorId(),
+ 'throwable_class' => get_class($event->getThrowable()),
+ ]);
+
+ $this->markObjectAsFailed($message);
+ $this->warnCreator($message, $event);
+ }
+
+ private function markObjectAsFailed(RequestGenerationMessage $message): void
+ {
+ $object = $this->storedObjectRepository->find($message->getDestinationStoredObjectId());
+
+ if (null === $object) {
+ $this->logger->error(self::LOG_PREFIX.'Stored object not found', ['stored_object_id', $message->getDestinationStoredObjectId()]);
+ }
+
+ $object->setStatus(StoredObject::STATUS_FAILURE);
+
+ $this->entityManager->flush();
+ }
+
+ private function warnCreator(RequestGenerationMessage $message, WorkerMessageFailedEvent $event): void
+ {
+ if (null === $creatorId = $message->getCreatorId()) {
+ $this->logger->info(self::LOG_PREFIX.'creator id is null');
+ return;
+ }
+
+ if (null === $creator = $this->userRepository->find($creatorId)) {
+ $this->logger->error(self::LOG_PREFIX.'Creator not found with given id', ['creator_id', $creatorId]);
+ return;
+ }
+
+ if (null === $creator->getEmail() || '' === $creator->getEmail()) {
+ $this->logger->info(self::LOG_PREFIX.'Creator does not have any email', ['user' => $creator->getUsernameCanonical()]);
+ return;
+ }
+
+ // if the exception is not a GeneratorException, we try the previous one...
+ $throwable = $event->getThrowable();
+ if (!$throwable instanceof GeneratorException) {
+ $throwable = $throwable->getPrevious();
+ }
+
+ if ($throwable instanceof GeneratorException) {
+ $errors = $throwable->getErrors();
+ } else {
+ $errors = [$throwable->getTraceAsString()];
+ }
+
+ if (null === $template = $this->docGeneratorTemplateRepository->find($message->getTemplateId())) {
+ $this->logger->info(self::LOG_PREFIX.'Template not found', ['template_id' => $message->getTemplateId()]);
+ return;
+ }
+
+ $email = (new TemplatedEmail())
+ ->to($creator->getEmail())
+ ->subject($this->translator->trans('docgen.failure_email.The generation of a document failed'))
+ ->textTemplate('@ChillDocGenerator/Email/on_generation_failed_email.txt.twig')
+ ->context([
+ 'errors' => $errors,
+ 'template' => $template,
+ 'creator' => $creator,
+ 'stored_object_id' => $message->getDestinationStoredObjectId(),
+ ]);
+
+ $this->mailer->send($email);
+ }
+}
diff --git a/src/Bundle/ChillDocGeneratorBundle/Service/Messenger/RequestGenerationHandler.php b/src/Bundle/ChillDocGeneratorBundle/Service/Messenger/RequestGenerationHandler.php
new file mode 100644
index 000000000..8fef8cf41
--- /dev/null
+++ b/src/Bundle/ChillDocGeneratorBundle/Service/Messenger/RequestGenerationHandler.php
@@ -0,0 +1,89 @@
+docGeneratorTemplateRepository = $docGeneratorTemplateRepository;
+ $this->entityManager = $entityManager;
+ $this->generator = $generator;
+ $this->logger = $logger;
+ $this->storedObjectRepository = $storedObjectRepository;
+ $this->userRepository = $userRepository;
+ }
+
+ public function __invoke(RequestGenerationMessage $message)
+ {
+ if (null === $template = $this->docGeneratorTemplateRepository->find($message->getTemplateId())) {
+ throw new \RuntimeException('template not found: ' . $message->getTemplateId());
+ }
+
+ if (null === $destinationStoredObject = $this->storedObjectRepository->find($message->getDestinationStoredObjectId())) {
+ throw new \RuntimeException('destination stored object not found : ' . $message->getDestinationStoredObjectId());
+ }
+
+ if ($destinationStoredObject->getGenerationTrialsCounter() >= self::AUTHORIZED_TRIALS) {
+ throw new UnrecoverableMessageHandlingException('maximum number of retry reached');
+ }
+
+ $creator = $this->userRepository->find($message->getCreatorId());
+
+ $destinationStoredObject->addGenerationTrial();
+ $this->entityManager->createQuery('UPDATE '.StoredObject::class.' s SET s.generationTrialsCounter = s.generationTrialsCounter + 1 WHERE s.id = :id')
+ ->setParameter('id', $destinationStoredObject->getId())
+ ->execute();
+
+ $this->generator->generateDocFromTemplate(
+ $template,
+ $message->getEntityId(),
+ $message->getContextGenerationData(),
+ $destinationStoredObject,
+ false,
+ null,
+ $creator
+ );
+
+ $this->logger->info(self::LOG_PREFIX.'Request generation finished', [
+ 'template_id' => $message->getTemplateId(),
+ 'destination_stored_object' => $message->getDestinationStoredObjectId(),
+ 'duration_int' => (new \DateTimeImmutable('now'))->getTimestamp() - $message->getCreatedAt()->getTimestamp(),
+ ]);
+ }
+}
diff --git a/src/Bundle/ChillDocGeneratorBundle/Service/Messenger/RequestGenerationMessage.php b/src/Bundle/ChillDocGeneratorBundle/Service/Messenger/RequestGenerationMessage.php
index 1489e2686..8362e4ecc 100644
--- a/src/Bundle/ChillDocGeneratorBundle/Service/Messenger/RequestGenerationMessage.php
+++ b/src/Bundle/ChillDocGeneratorBundle/Service/Messenger/RequestGenerationMessage.php
@@ -3,6 +3,7 @@
namespace Chill\DocGeneratorBundle\Service\Messenger;
use Chill\DocGeneratorBundle\Entity\DocGeneratorTemplate;
+use Chill\DocStoreBundle\Entity\StoredObject;
use Chill\MainBundle\Entity\User;
class RequestGenerationMessage
@@ -13,14 +14,25 @@ class RequestGenerationMessage
private int $entityId;
- private string $entityClassName;
+ private int $destinationStoredObjectId;
- public function __construct(User $creator, DocGeneratorTemplate $template, int $entityId, string $entityClassName)
- {
+ private array $contextGenerationData;
+
+ private \DateTimeImmutable $createdAt;
+
+ public function __construct(
+ User $creator,
+ DocGeneratorTemplate $template,
+ int $entityId,
+ StoredObject $destinationStoredObject,
+ array $contextGenerationData
+ ) {
$this->creatorId = $creator->getId();
$this->templateId = $template->getId();
$this->entityId = $entityId;
- $this->entityClassName = $entityClassName;
+ $this->destinationStoredObjectId = $destinationStoredObject->getId();
+ $this->contextGenerationData = $contextGenerationData;
+ $this->createdAt = new \DateTimeImmutable('now');
}
public function getCreatorId(): int
@@ -28,6 +40,11 @@ class RequestGenerationMessage
return $this->creatorId;
}
+ public function getDestinationStoredObjectId(): int
+ {
+ return $this->destinationStoredObjectId;
+ }
+
public function getTemplateId(): int
{
return $this->templateId;
@@ -38,8 +55,13 @@ class RequestGenerationMessage
return $this->entityId;
}
- public function getEntityClassName(): string
+ public function getContextGenerationData(): array
{
- return $this->entityClassName;
+ return $this->contextGenerationData;
+ }
+
+ public function getCreatedAt(): \DateTimeImmutable
+ {
+ return $this->createdAt;
}
}
diff --git a/src/Bundle/ChillDocGeneratorBundle/config/services.yaml b/src/Bundle/ChillDocGeneratorBundle/config/services.yaml
index 5bdfe2a11..5fef6fb22 100644
--- a/src/Bundle/ChillDocGeneratorBundle/config/services.yaml
+++ b/src/Bundle/ChillDocGeneratorBundle/config/services.yaml
@@ -20,10 +20,14 @@ services:
resource: '../Serializer/Normalizer/'
tags:
- { name: 'serializer.normalizer', priority: -152 }
+
Chill\DocGeneratorBundle\Serializer\Normalizer\CollectionDocGenNormalizer:
tags:
- { name: 'serializer.normalizer', priority: -126 }
+ Chill\DocGeneratorBundle\Service\Context\:
+ resource: "../Service/Context"
+
Chill\DocGeneratorBundle\Controller\:
resource: "../Controller"
autowire: true
@@ -34,18 +38,20 @@ services:
autowire: true
autoconfigure: true
- Chill\DocGeneratorBundle\Service\Context\:
- resource: "../Service/Context/"
- autowire: true
- autoconfigure: true
-
Chill\DocGeneratorBundle\GeneratorDriver\:
resource: "../GeneratorDriver/"
autowire: true
autoconfigure: true
+ Chill\DocGeneratorBundle\Service\Messenger\:
+ resource: "../Service/Messenger/"
+
+ Chill\DocGeneratorBundle\Service\Generator\Generator: ~
+ Chill\DocGeneratorBundle\Service\Generator\GeneratorInterface: '@Chill\DocGeneratorBundle\Service\Generator\Generator'
+
Chill\DocGeneratorBundle\Driver\RelatorioDriver: '@Chill\DocGeneratorBundle\Driver\DriverInterface'
Chill\DocGeneratorBundle\Context\ContextManager:
arguments:
$contexts: !tagged_iterator { tag: chill_docgen.context, default_index_method: getKey }
+ Chill\DocGeneratorBundle\Context\ContextManagerInterface: '@Chill\DocGeneratorBundle\Context\ContextManager'
diff --git a/src/Bundle/ChillDocGeneratorBundle/migrations/Version20230214192558.php b/src/Bundle/ChillDocGeneratorBundle/migrations/Version20230214192558.php
new file mode 100644
index 000000000..de536ca84
--- /dev/null
+++ b/src/Bundle/ChillDocGeneratorBundle/migrations/Version20230214192558.php
@@ -0,0 +1,47 @@
+addSql('ALTER TABLE chill_doc.stored_object ADD template_id INT DEFAULT NULL');
+ $this->addSql('ALTER TABLE chill_doc.stored_object ADD status TEXT DEFAULT \'ready\' NOT NULL');
+ $this->addSql('ALTER TABLE chill_doc.stored_object ADD createdAt TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL');
+ $this->addSql('UPDATE chill_doc.stored_object SET createdAt = creation_date');
+ $this->addSql('ALTER TABLE chill_doc.stored_object ADD createdBy_id INT DEFAULT NULL');
+ $this->addSql('ALTER TABLE chill_doc.stored_object DROP creation_date;');
+ $this->addSql('ALTER TABLE chill_doc.stored_object ALTER type SET DEFAULT \'\'');
+ $this->addSql('ALTER TABLE chill_doc.stored_object ALTER title DROP DEFAULT');
+ $this->addSql('COMMENT ON COLUMN chill_doc.stored_object.createdAt IS \'(DC2Type:datetime_immutable)\'');
+ $this->addSql('ALTER TABLE chill_doc.stored_object ADD CONSTRAINT FK_49604E365DA0FB8 FOREIGN KEY (template_id) REFERENCES chill_docgen_template (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
+ $this->addSql('ALTER TABLE chill_doc.stored_object ADD CONSTRAINT FK_49604E363174800F FOREIGN KEY (createdBy_id) REFERENCES users (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
+ $this->addSql('CREATE INDEX IDX_49604E365DA0FB8 ON chill_doc.stored_object (template_id)');
+ $this->addSql('CREATE INDEX IDX_49604E363174800F ON chill_doc.stored_object (createdBy_id)');
+ }
+
+ public function down(Schema $schema): void
+ {
+ $this->addSql('ALTER TABLE chill_doc.stored_object DROP CONSTRAINT FK_49604E365DA0FB8');
+ $this->addSql('ALTER TABLE chill_doc.stored_object DROP CONSTRAINT FK_49604E363174800F');
+ $this->addSql('ALTER TABLE chill_doc.stored_object DROP template_id');
+ $this->addSql('ALTER TABLE chill_doc.stored_object DROP status');
+ $this->addSql('ALTER TABLE chill_doc.stored_object ADD creation_date TIMESTAMP(0) DEFAULT NOW()');
+ $this->addSql('UPDATE chill_doc.stored_object SET creation_date = createdAt');
+ $this->addSql('ALTER TABLE chill_doc.stored_object DROP createdAt');
+ $this->addSql('ALTER TABLE chill_doc.stored_object DROP createdBy_id');
+ $this->addSql('ALTER TABLE chill_doc.stored_object ALTER title SET DEFAULT \'\'');
+ $this->addSql('ALTER TABLE chill_doc.stored_object ALTER type DROP DEFAULT');
+ }
+}
diff --git a/src/Bundle/ChillDocGeneratorBundle/tests/Service/Context/Generator/GeneratorTest.php b/src/Bundle/ChillDocGeneratorBundle/tests/Service/Context/Generator/GeneratorTest.php
index 2e68b443c..066715b60 100644
--- a/src/Bundle/ChillDocGeneratorBundle/tests/Service/Context/Generator/GeneratorTest.php
+++ b/src/Bundle/ChillDocGeneratorBundle/tests/Service/Context/Generator/GeneratorTest.php
@@ -26,13 +26,14 @@ class GeneratorTest extends TestCase
$template = (new DocGeneratorTemplate())->setFile($templateStoredObject = (new StoredObject())
->setType('application/test'));
$destinationStoredObject = (new StoredObject())->setStatus(StoredObject::STATUS_PENDING);
+ $reflection = new \ReflectionClass($destinationStoredObject);
+ $reflection->getProperty('id')->setAccessible(true);
+ $reflection->getProperty('id')->setValue($destinationStoredObject, 1);
$entity = new class {};
$data = [];
$context = $this->prophesize(DocGeneratorContextInterface::class);
$context->getData($template, $entity, Argument::type('array'))->willReturn($data);
- $context->storeGenerated($template, $destinationStoredObject, $entity, Argument::type('array'))
- ->shouldBeCalled();
$context->getName()->willReturn('dummy_context');
$context->getEntityClass()->willReturn('DummyClass');
$context = $context->reveal();
@@ -46,8 +47,11 @@ class GeneratorTest extends TestCase
->willReturn('generated');
$entityManager = $this->prophesize(EntityManagerInterface::class);
- $entityManager->find(Argument::type('string'), Argument::type('int'))
+ $entityManager->find(StoredObject::class, 1)
+ ->willReturn($destinationStoredObject);
+ $entityManager->find('DummyClass', Argument::type('int'))
->willReturn($entity);
+ $entityManager->clear()->shouldBeCalled();
$entityManager->flush()->shouldBeCalled();
$storedObjectManager = $this->prophesize(StoredObjectManagerInterface::class);
@@ -65,8 +69,8 @@ class GeneratorTest extends TestCase
$generator->generateDocFromTemplate(
$template,
- 'DummyEntity',
1,
+ [],
$destinationStoredObject
);
}
@@ -89,8 +93,8 @@ class GeneratorTest extends TestCase
$generator->generateDocFromTemplate(
$template,
- 'DummyEntity',
1,
+ [],
$destinationStoredObject
);
}
@@ -102,6 +106,9 @@ class GeneratorTest extends TestCase
$template = (new DocGeneratorTemplate())->setFile($templateStoredObject = (new StoredObject())
->setType('application/test'));
$destinationStoredObject = (new StoredObject())->setStatus(StoredObject::STATUS_PENDING);
+ $reflection = new \ReflectionClass($destinationStoredObject);
+ $reflection->getProperty('id')->setAccessible(true);
+ $reflection->getProperty('id')->setValue($destinationStoredObject, 1);
$context = $this->prophesize(DocGeneratorContextInterface::class);
$context->getName()->willReturn('dummy_context');
@@ -126,8 +133,8 @@ class GeneratorTest extends TestCase
$generator->generateDocFromTemplate(
$template,
- 'DummyEntity',
1,
+ [],
$destinationStoredObject
);
}
diff --git a/src/Bundle/ChillDocGeneratorBundle/translations/messages.fr.yml b/src/Bundle/ChillDocGeneratorBundle/translations/messages.fr.yml
index bf37ff838..d0950482e 100644
--- a/src/Bundle/ChillDocGeneratorBundle/translations/messages.fr.yml
+++ b/src/Bundle/ChillDocGeneratorBundle/translations/messages.fr.yml
@@ -10,6 +10,16 @@ docgen:
test generate: Tester la génération
With context %name%: 'Avec le contexte "%name%"'
+ Doc generation failed: La génération de ce document a échoué
+ Doc generation is pending: La génération de ce document est en cours
+ Come back later: Revenir plus tard
+
+ failure_email:
+ The generation of a document failed: La génération d'un document a échoué
+ The generation of the document {template_name} failed: La génération d'un document à partir du modèle {{ template_name }} a échoué.
+ The following errors were encoutered: Les erreurs suivantes ont été rencontrées
+ Forward this email to your administrator for solving: Faites suivre ce message vers votre administrateur pour la résolution du problème.
+ References: Références
crud:
docgen_template:
@@ -19,4 +29,4 @@ crud:
Show data instead of generating: Montrer les données au lieu de générer le document
-Template file: Fichier modèle
\ No newline at end of file
+Template file: Fichier modèle
diff --git a/src/Bundle/ChillDocStoreBundle/Controller/StoredObjectApiController.php b/src/Bundle/ChillDocStoreBundle/Controller/StoredObjectApiController.php
new file mode 100644
index 000000000..29b978ffb
--- /dev/null
+++ b/src/Bundle/ChillDocStoreBundle/Controller/StoredObjectApiController.php
@@ -0,0 +1,39 @@
+security = $security;
+ }
+
+ /**
+ * @Route("/api/1.0/doc-store/stored-object/{uuid}/is-ready")
+ */
+ public function isDocumentReady(StoredObject $storedObject): Response
+ {
+ if (!$this->security->isGranted('ROLE_USER')) {
+ throw new AccessDeniedHttpException();
+ }
+
+ return new JsonResponse(
+ [
+ 'id' => $storedObject->getId(),
+ 'filename' => $storedObject->getFilename(),
+ 'status' => $storedObject->getStatus(),
+ 'type' => $storedObject->getType(),
+ ]
+ );
+ }
+}
diff --git a/src/Bundle/ChillDocStoreBundle/Entity/StoredObject.php b/src/Bundle/ChillDocStoreBundle/Entity/StoredObject.php
index 21abc1f56..8821ead7c 100644
--- a/src/Bundle/ChillDocStoreBundle/Entity/StoredObject.php
+++ b/src/Bundle/ChillDocStoreBundle/Entity/StoredObject.php
@@ -41,12 +41,6 @@ class StoredObject implements AsyncFileInterface, Document, TrackCreationInterfa
use TrackCreationTrait;
- /**
- * @ORM\Column(type="datetime", name="creation_date")
- * @Serializer\Groups({"read", "write"})
- */
- private DateTimeInterface $creationDate;
-
/**
* @ORM\Column(type="json", name="datas")
* @Serializer\Groups({"read", "write"})
@@ -87,7 +81,7 @@ class StoredObject implements AsyncFileInterface, Document, TrackCreationInterfa
private string $title = '';
/**
- * @ORM\Column(type="text", name="type")
+ * @ORM\Column(type="text", name="type", options={"default": ""})
* @Serializer\Groups({"read", "write"})
*/
private string $type = '';
@@ -105,9 +99,20 @@ class StoredObject implements AsyncFileInterface, Document, TrackCreationInterfa
/**
* @ORM\Column(type="text", options={"default": "ready"})
+ * @Serializer\Groups({"read"})
*/
private string $status;
+ /**
+ * Store the number of times a generation has been tryied for this StoredObject.
+ *
+ * This is a workaround, as generation consume lot of memory, and out-of-memory errors
+ * are not handled by messenger.
+ *
+ * @ORM\Column(type="integer", options={"default": 0})
+ */
+ private int $generationTrialsCounter = 0;
+
/**
* @param StoredObject::STATUS_* $status
*/
@@ -117,8 +122,16 @@ class StoredObject implements AsyncFileInterface, Document, TrackCreationInterfa
$this->status = $status;
}
+ public function addGenerationTrial(): self
+ {
+ $this->generationTrialsCounter++;
+
+ return $this;
+ }
+
/**
* @Serializer\Groups({"read", "write"})
+ * @deprecated
*/
public function getCreationDate(): DateTime
{
@@ -135,6 +148,11 @@ class StoredObject implements AsyncFileInterface, Document, TrackCreationInterfa
return $this->filename;
}
+ public function getGenerationTrialsCounter(): int
+ {
+ return $this->generationTrialsCounter;
+ }
+
public function getId(): ?int
{
return $this->id;
@@ -158,6 +176,9 @@ class StoredObject implements AsyncFileInterface, Document, TrackCreationInterfa
return $this->getFilename();
}
+ /**
+ * @return StoredObject::STATUS_*
+ */
public function getStatus(): string
{
return $this->status;
@@ -185,6 +206,7 @@ class StoredObject implements AsyncFileInterface, Document, TrackCreationInterfa
/**
* @Serializer\Groups({"write"})
+ * @deprecated
*/
public function setCreationDate(DateTime $creationDate): self
{
@@ -244,4 +266,30 @@ class StoredObject implements AsyncFileInterface, Document, TrackCreationInterfa
return $this;
}
+
+ public function getTemplate(): ?DocGeneratorTemplate
+ {
+ return $this->template;
+ }
+
+ public function hasTemplate(): bool
+ {
+ return null !== $this->template;
+ }
+
+ public function setTemplate(?DocGeneratorTemplate $template): StoredObject
+ {
+ $this->template = $template;
+ return $this;
+ }
+
+ public function isPending(): bool
+ {
+ return self::STATUS_PENDING === $this->getStatus();
+ }
+
+ public function isFailure(): bool
+ {
+ return self::STATUS_FAILURE === $this->getStatus();
+ }
}
diff --git a/src/Bundle/ChillDocStoreBundle/Resources/public/module/document_action_buttons_group/index.ts b/src/Bundle/ChillDocStoreBundle/Resources/public/module/document_action_buttons_group/index.ts
index ec1d50a86..4180808dd 100644
--- a/src/Bundle/ChillDocStoreBundle/Resources/public/module/document_action_buttons_group/index.ts
+++ b/src/Bundle/ChillDocStoreBundle/Resources/public/module/document_action_buttons_group/index.ts
@@ -1,7 +1,8 @@
import {_createI18n} from "../../../../../ChillMainBundle/Resources/public/vuejs/_js/i18n";
import DocumentActionButtonsGroup from "../../vuejs/DocumentActionButtonsGroup.vue";
import {createApp} from "vue";
-import {StoredObject} from "../../types";
+import {StoredObject, StoredObjectStatusChange} from "../../types";
+import {is_object_ready} from "../../vuejs/StoredObjectButton/helpers";
const i18n = _createI18n({});
@@ -15,19 +16,32 @@ window.addEventListener('DOMContentLoaded', function (e) {
filename: string,
canEdit: string,
storedObject: string,
- small: string,
+ buttonSmall: string,
};
const
- storedObject = JSON.parse(datasets.storedObject),
+ storedObject = JSON.parse(datasets.storedObject) as StoredObject,
filename = datasets.filename,
canEdit = datasets.canEdit === '1',
- small = datasets.small === '1'
+ small = datasets.buttonSmall === '1'
;
return { storedObject, filename, canEdit, small };
},
- template: '',
+ template: '',
+ methods: {
+ onStoredObjectStatusChange: function(newStatus: StoredObjectStatusChange): void {
+ this.$data.storedObject.status = newStatus.status;
+ this.$data.storedObject.filename = newStatus.filename;
+ this.$data.storedObject.type = newStatus.type;
+
+ // remove eventual div which inform pending status
+ document.querySelectorAll(`[data-docgen-is-pending="${this.$data.storedObject.id}"]`)
+ .forEach(function(el) {
+ el.remove();
+ });
+ }
+ }
});
app.use(i18n).mount(el);
diff --git a/src/Bundle/ChillDocStoreBundle/Resources/public/types.ts b/src/Bundle/ChillDocStoreBundle/Resources/public/types.ts
index 918526117..825055973 100644
--- a/src/Bundle/ChillDocStoreBundle/Resources/public/types.ts
+++ b/src/Bundle/ChillDocStoreBundle/Resources/public/types.ts
@@ -1,5 +1,7 @@
import {DateTime} from "../../../ChillMainBundle/Resources/public/types";
+export type StoredObjectStatus = "ready"|"failure"|"pending";
+
export interface StoredObject {
id: number,
@@ -13,7 +15,15 @@ export interface StoredObject {
keyInfos: object,
title: string,
type: string,
- uuid: string
+ uuid: string,
+ status: StoredObjectStatus,
+}
+
+export interface StoredObjectStatusChange {
+ id: number,
+ filename: string,
+ status: StoredObjectStatus,
+ type: string,
}
/**
diff --git a/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/DocumentActionButtonsGroup.vue b/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/DocumentActionButtonsGroup.vue
index 60b368cd3..88587e90f 100644
--- a/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/DocumentActionButtonsGroup.vue
+++ b/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/DocumentActionButtonsGroup.vue
@@ -1,6 +1,6 @@
-
-