Add attachments to workflow

This commit is contained in:
2025-02-03 21:15:00 +00:00
parent 9e191f1b5b
commit 37227a3aeb
106 changed files with 3455 additions and 619 deletions

View File

@@ -12,6 +12,7 @@ declare(strict_types=1);
namespace Chill\CalendarBundle\Repository;
use Chill\CalendarBundle\Entity\CalendarDoc;
use Chill\DocStoreBundle\Entity\StoredObject;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use Doctrine\Persistence\ObjectRepository;
@@ -49,4 +50,21 @@ class CalendarDocRepository implements ObjectRepository, CalendarDocRepositoryIn
{
return CalendarDoc::class;
}
/**
* @param StoredObject|int $storedObject the StoredObject instance, or the id of the stored object
*/
public function findOneByStoredObject(StoredObject|int $storedObject): ?CalendarDoc
{
$storedObjectId = $storedObject instanceof StoredObject ? $storedObject->getId() : $storedObject;
$qb = $this->repository->createQueryBuilder('c');
$qb->where(
$qb->expr()->eq(':storedObject', 'c.storedObject')
);
$qb->setParameter('storedObject', $storedObjectId);
return $qb->getQuery()->getOneOrNullResult();
}
}

View File

@@ -12,6 +12,7 @@ declare(strict_types=1);
namespace Chill\CalendarBundle\Repository;
use Chill\CalendarBundle\Entity\CalendarDoc;
use Chill\DocStoreBundle\Entity\StoredObject;
interface CalendarDocRepositoryInterface
{
@@ -29,5 +30,7 @@ interface CalendarDocRepositoryInterface
public function findOneBy(array $criteria): ?CalendarDoc;
public function findOneByStoredObject(StoredObject|int $storedObject): ?CalendarDoc;
public function getClassName();
}

View File

@@ -106,7 +106,10 @@ export default {
});
state.key = state.key + toAdd.length;
},
addExternals(state, externalEvents: (EventInput & { id: string })[]) {
addExternals(
state: CalendarRangesState,
externalEvents: (EventInput & { id: string })[],
) {
const toAdd = externalEvents.filter(
(r) => !state.rangesIndex.has(r.id),
);
@@ -160,7 +163,7 @@ export default {
state.key = state.key + 1;
}
},
updateRange(state, range: CalendarRange) {
updateRange(state: CalendarRangesState, range: CalendarRange) {
const found = state.ranges.find(
(r) => r.calendarRangeId === range.id && r.is === "range",
);
@@ -207,7 +210,7 @@ export default {
});
},
createRange(
ctx,
ctx: Context,
{
start,
end,
@@ -253,10 +256,10 @@ export default {
throw error;
});
},
deleteRange(ctx, calendarRangeId: number) {
deleteRange(ctx: Context, calendarRangeId: number) {
const url = `/api/1.0/calendar/calendar-range/${calendarRangeId}.json`;
makeFetch<undefined, never>("DELETE", url).then((_) => {
makeFetch<undefined, never>("DELETE", url).then(() => {
ctx.commit("removeRange", calendarRangeId);
});
},
@@ -347,10 +350,10 @@ export default {
);
}
return Promise.all(promises).then((_) => Promise.resolve(null));
return Promise.all(promises).then(() => Promise.resolve(null));
},
copyFromWeekToAnotherWeek(
ctx,
ctx: Context,
{ fromMonday, toMonday }: { fromMonday: Date; toMonday: Date },
): Promise<null> {
const rangesToCopy: EventInputCalendarRange[] =
@@ -371,7 +374,7 @@ export default {
);
}
return Promise.all(promises).then((_) => Promise.resolve(null));
return Promise.all(promises).then(() => Promise.resolve(null));
},
},
} as Module<CalendarRangesState, State>;

View File

@@ -5,71 +5,5 @@
{% set c = document.calendar %}
<div class="item-bloc">
<div class="item-row">
<div class="item-col" style="width: unset">
{% if document.storedObject.isPending %}
<div class="badge text-bg-info" data-docgen-is-pending="{{ document.storedObject.id }}">{{ 'docgen.Doc generation is pending'|trans }}</div>
{% elseif document.storedObject.isFailure %}
<div class="badge text-bg-warning">{{ 'docgen.Doc generation failed'|trans }}</div>
{% endif %}
<div>
{% if c.accompanyingPeriod is not null and context == 'person' %}
<span class="badge bg-primary">
<i class="fa fa-random"></i> {{ c.accompanyingPeriod.id }}
</span>&nbsp;
{% endif %}
<span class="badge-calendar">
<span class="title_label"></span>
<span class="title_action">
{{ 'Calendar'|trans }}
{% if c.endDate.diff(c.startDate).days >= 1 %}
{{ c.startDate|format_datetime('short', 'short') }}
- {{ c.endDate|format_datetime('short', 'short') }}
{% else %}
{{ c.startDate|format_datetime('short', 'short') }}
- {{ c.endDate|format_datetime('none', 'short') }}
{% endif %}
</span>
</span>
</div>
<div class="denomination h2">
{{ document.storedObject.title|chill_print_or_message("No title") }}
</div>
{% if document.storedObject.hasTemplate %}
<div>
<p>{{ document.storedObject.template.name|localize_translatable_string }}</p>
</div>
{% endif %}
</div>
<div class="item-col">
<div class="container">
<div class="dates row text-end">
<span>{{ document.storedObject.createdAt|format_date('short') }}</span>
</div>
</div>
</div>
</div>
<div class="item-row separator">
<div class="item-col item-meta">
{{ mmm.createdBy(document) }}
</div>
<ul class="item-col record_actions flex-shrink-1">
{% if is_granted('CHILL_CALENDAR_DOC_SEE', document) %}
<li>
{{ document.storedObject|chill_document_button_group(document.storedObject.title, is_granted('CHILL_CALENDAR_CALENDAR_EDIT', c)) }}
</li>
{% endif %}
{% if is_granted('CHILL_CALENDAR_CALENDAR_EDIT', c) %}
<li>
<a href="{{ chill_path_add_return_path('chill_calendar_calendar_edit', {'id': c.id, 'docId': document.id}) }}" class="btn btn-edit"></a>
</li>
{% endif %}
</ul>
</div>
{{ include('@ChillCalendar/GenericDoc/calendar_document_row.html.twig') }}
</div>

View File

@@ -0,0 +1,75 @@
{% import "@ChillDocStore/Macro/macro.html.twig" as m %}
{% import "@ChillDocStore/Macro/macro_mimeicon.html.twig" as mm %}
{% import '@ChillPerson/Macro/updatedBy.html.twig' as mmm %}
{% set c = document.calendar %}
<div class="item-row">
<div class="item-col" style="width: unset">
{% if document.storedObject.isPending %}
<div class="badge text-bg-info" data-docgen-is-pending="{{ document.storedObject.id }}">{{ 'docgen.Doc generation is pending'|trans }}</div>
{% elseif document.storedObject.isFailure %}
<div class="badge text-bg-warning">{{ 'docgen.Doc generation failed'|trans }}</div>
{% endif %}
<div>
{% if c.accompanyingPeriod is not null and context == 'person' %}
<span class="badge bg-primary">
<i class="fa fa-random"></i> {{ c.accompanyingPeriod.id }}
</span>&nbsp;
{% endif %}
<span class="badge-calendar">
<span class="title_label"></span>
<span class="title_action">
{{ 'Calendar'|trans }}
{% if c.endDate.diff(c.startDate).days >= 1 %}
{{ c.startDate|format_datetime('short', 'short') }}
- {{ c.endDate|format_datetime('short', 'short') }}
{% else %}
{{ c.startDate|format_datetime('short', 'short') }}
- {{ c.endDate|format_datetime('none', 'short') }}
{% endif %}
</span>
</span>
</div>
<div class="denomination h2">
{{ document.storedObject.title|chill_print_or_message("No title") }}
</div>
{% if document.storedObject.hasTemplate %}
<div>
<p>{{ document.storedObject.template.name|localize_translatable_string }}</p>
</div>
{% endif %}
</div>
<div class="item-col">
<div class="container">
<div class="dates row text-end">
<span>{{ document.storedObject.createdAt|format_date('short') }}</span>
</div>
</div>
</div>
</div>
{% if show_actions %}
<div class="item-row separator">
<div class="item-col item-meta">
{{ mmm.createdBy(document) }}
</div>
<ul class="item-col record_actions flex-shrink-1">
{% if is_granted('CHILL_CALENDAR_DOC_SEE', document) %}
<li>
{{ document.storedObject|chill_document_button_group(document.storedObject.title, is_granted('CHILL_CALENDAR_CALENDAR_EDIT', c)) }}
</li>
{% endif %}
{% if is_granted('CHILL_CALENDAR_CALENDAR_EDIT', c) %}
<li>
<a href="{{ chill_path_add_return_path('chill_calendar_calendar_edit', {'id': c.id, 'docId': document.id}) }}" class="btn btn-edit"></a>
</li>
{% endif %}
</ul>
</div>
{% endif %}

View File

@@ -0,0 +1,51 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\CalendarBundle\Service\GenericDoc\Normalizer;
use Chill\CalendarBundle\Repository\CalendarDocRepositoryInterface;
use Chill\CalendarBundle\Service\GenericDoc\Providers\AccompanyingPeriodCalendarGenericDocProvider;
use Chill\CalendarBundle\Service\GenericDoc\Renderers\AccompanyingPeriodCalendarGenericDocRenderer;
use Chill\DocStoreBundle\GenericDoc\GenericDocDTO;
use Chill\DocStoreBundle\GenericDoc\GenericDocNormalizerInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
use Twig\Environment;
final readonly class AccompanyingPeriodCalendarGenericDocNormalizer implements GenericDocNormalizerInterface
{
public function __construct(
private AccompanyingPeriodCalendarGenericDocRenderer $renderer,
private CalendarDocRepositoryInterface $calendarDocRepository,
private Environment $twig,
private TranslatorInterface $translator,
) {}
public function supportsNormalization(GenericDocDTO $genericDocDTO, string $format, array $context = []): bool
{
return AccompanyingPeriodCalendarGenericDocProvider::KEY === $genericDocDTO->key && 'json' === $format;
}
public function normalize(GenericDocDTO $genericDocDTO, string $format, array $context = []): array
{
if (null === $calendarDoc = $this->calendarDocRepository->find($genericDocDTO->identifiers['id'])) {
return ['title' => $this->translator->trans('generic_doc.document removed'), 'isPresent' => false];
}
return [
'isPresent' => true,
'title' => $calendarDoc->getStoredObject()->getTitle(),
'html' => $this->twig->render(
$this->renderer->getTemplate($genericDocDTO, ['show-actions' => false, 'row-only' => true]),
$this->renderer->getTemplateData($genericDocDTO, ['show-actions' => false, 'row-only' => true])
),
];
}
}

View File

@@ -13,10 +13,12 @@ namespace Chill\CalendarBundle\Service\GenericDoc\Providers;
use Chill\CalendarBundle\Entity\Calendar;
use Chill\CalendarBundle\Entity\CalendarDoc;
use Chill\CalendarBundle\Repository\CalendarDocRepositoryInterface;
use Chill\CalendarBundle\Security\Voter\CalendarVoter;
use Chill\DocStoreBundle\Entity\StoredObject;
use Chill\DocStoreBundle\GenericDoc\FetchQuery;
use Chill\DocStoreBundle\GenericDoc\FetchQueryInterface;
use Chill\DocStoreBundle\GenericDoc\GenericDocDTO;
use Chill\DocStoreBundle\GenericDoc\GenericDocForAccompanyingPeriodProviderInterface;
use Chill\DocStoreBundle\GenericDoc\GenericDocForPersonProviderInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
@@ -38,8 +40,38 @@ final readonly class AccompanyingPeriodCalendarGenericDocProvider implements Gen
public function __construct(
private Security $security,
private EntityManagerInterface $em,
private CalendarDocRepositoryInterface $calendarRepository,
) {}
public function fetchAssociatedStoredObject(GenericDocDTO $genericDocDTO): ?StoredObject
{
return $this->calendarRepository->find($genericDocDTO->identifiers['id'])?->getStoredObject();
}
public function supportsGenericDoc(GenericDocDTO $genericDocDTO): bool
{
return $this->supportsKeyAndIdentifiers($genericDocDTO->key, $genericDocDTO->identifiers);
}
public function supportsKeyAndIdentifiers(string $key, array $identifiers): bool
{
return self::KEY === $key && array_key_exists('id', $identifiers);
}
public function buildOneGenericDoc(string $key, array $identifiers): ?GenericDocDTO
{
if (null === $calendarDoc = $this->calendarRepository->find($identifiers['id'])) {
return null;
}
return new GenericDocDTO(
self::KEY,
$identifiers,
\DateTimeImmutable::createFromInterface($calendarDoc->getCreatedAt() ?? new \DateTimeImmutable('now')),
$calendarDoc->getCalendar()->getAccompanyingPeriod() ?? $calendarDoc->getCalendar()->getPerson()
);
}
/**
* @throws MappingException
*/
@@ -82,7 +114,7 @@ final readonly class AccompanyingPeriodCalendarGenericDocProvider implements Gen
[Types::INTEGER]
);
return $query;
return $this->addWhereClausesToQuery($query, $startDate, $endDate, $content);
}
public function isAllowedForAccompanyingPeriod(AccompanyingPeriod $accompanyingPeriod): bool

View File

@@ -17,6 +17,9 @@ use Chill\CalendarBundle\Service\GenericDoc\Providers\PersonCalendarGenericDocPr
use Chill\DocStoreBundle\GenericDoc\GenericDocDTO;
use Chill\DocStoreBundle\GenericDoc\Twig\GenericDocRendererInterface;
/**
* @implements GenericDocRendererInterface<array{row-only?: bool, show-actions?: bool}>
*/
final readonly class AccompanyingPeriodCalendarGenericDocRenderer implements GenericDocRendererInterface
{
public function __construct(private CalendarDocRepository $repository) {}
@@ -28,7 +31,8 @@ final readonly class AccompanyingPeriodCalendarGenericDocRenderer implements Gen
public function getTemplate(GenericDocDTO $genericDocDTO, $options = []): string
{
return '@ChillCalendar/GenericDoc/calendar_document.html.twig';
return $options['row-only'] ?? false ? '@ChillCalendar/GenericDoc/calendar_document_row.html.twig'
: '@ChillCalendar/GenericDoc/calendar_document.html.twig';
}
public function getTemplateData(GenericDocDTO $genericDocDTO, $options = []): array
@@ -36,6 +40,7 @@ final readonly class AccompanyingPeriodCalendarGenericDocRenderer implements Gen
return [
'document' => $this->repository->find($genericDocDTO->identifiers['id']),
'context' => $genericDocDTO->getContext(),
'show_actions' => $options['show-actions'] ?? true,
];
}
}