Skip to content

Commit

Permalink
[Training] adds session cancellation (#2855)
Browse files Browse the repository at this point in the history
* Session cancellation

* pr review

* PageListSection
  • Loading branch information
WolfyWin authored Sep 19, 2024
1 parent 3e936ad commit e5e884c
Show file tree
Hide file tree
Showing 34 changed files with 738 additions and 112 deletions.
5 changes: 3 additions & 2 deletions src/main/theme/Entity/Theme.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
* Theme.
*
* @ORM\Entity(repositoryClass="Claroline\ThemeBundle\Repository\ThemeRepository")
*
* @ORM\Table(name="claro_theme")
*/
class Theme
Expand Down Expand Up @@ -51,12 +52,12 @@ class Theme
/**
* @ORM\Column(nullable=true)
*/
private ?string $primaryColor;
private ?string $primaryColor = null;

/**
* @ORM\Column(nullable=true)
*/
private ?string $secondaryColor;
private ?string $secondaryColor = null;

public function __construct()
{
Expand Down
1 change: 1 addition & 0 deletions src/plugin/cursus/Controller/CourseController.php
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,7 @@ public function listSessionsAction(Course $course, Request $request): JsonRespon
$params['hiddenFilters'] = [];
}
$params['hiddenFilters']['course'] = $course->getUuid();
$params['hiddenFilters']['canceled'] = false;

// hide hidden sessions for non admin
if (!$this->checkToolAccess('EDIT')) {
Expand Down
42 changes: 42 additions & 0 deletions src/plugin/cursus/Controller/SessionController.php
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ protected function getDefaultHiddenFilters(): array
}
}

$filters['canceled'] = false;

return $filters;
}

Expand Down Expand Up @@ -160,6 +162,46 @@ public function copyAction(Request $request): JsonResponse
}, $processed));
}

/**
* @Route("/{id}/list/canceled", name="list_canceled", methods={"GET"})
*
* @EXT\ParamConverter("course", class="Claroline\CursusBundle\Entity\Course", options={"mapping": {"id": "uuid"}})
*/
public function listCanceledAction(Course $course, Request $request): JsonResponse
{
$this->checkPermission('EDIT', $course, [], true);

$filters = $request->query->all();
$filters['hiddenFilters'] = $filters['hiddenFilters'] ?? [];

$filters['hiddenFilters'] = array_merge($filters['hiddenFilters'], [
'course' => $course->getUuid(),
'canceled' => true,
]);

return new JsonResponse(
$this->crud->list(Session::class, $filters)
);
}

/**
* @Route("/cancel", name="cancel", methods={"POST"})
*/
public function cancelAction(Request $request): JsonResponse
{
$data = $this->decodeRequest($request);

$processedSessions = $this->manager->cancelSessions(
$data['ids'],
$data['cancelReason'] ?? null,
$data['canceledTemplate'] ?? null
);

return new JsonResponse(array_map(function (Session $session) {
return $this->serializer->serialize($session);
}, $processedSessions));
}

/**
* @Route("/{id}/pdf", name="download_pdf", methods={"GET"})
*
Expand Down
49 changes: 49 additions & 0 deletions src/plugin/cursus/Entity/Session.php
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,23 @@ class Session extends AbstractTraining implements IdentifiableInterface
*/
private ?Template $invitationTemplate = null;

/**
* @ORM\Column(name="canceled", type="boolean")
*/
private bool $canceled = false;

/**
* @ORM\Column(name="cancel_reason", type="text", nullable=true)
*/
private ?string $cancelReason = null;

/**
* @ORM\ManyToOne(targetEntity="Claroline\CoreBundle\Entity\Template\Template")
*
* @ORM\JoinColumn(name="canceled_template_id", nullable=true, onDelete="SET NULL")
*/
private ?Template $canceledTemplate = null;

public function __construct()
{
$this->refreshUuid();
Expand Down Expand Up @@ -211,4 +228,36 @@ public function setInvitationTemplate(Template $template = null): void
{
$this->invitationTemplate = $template;
}

public function isCanceled(): bool
{
return $this->canceled;
}

public function setCanceled(bool $canceled): void
{
$this->canceled = $canceled;
}

public function getCancelReason(): ?string
{
return $this->cancelReason;
}

public function setCancelReason(?string $cancelReason): self
{
$this->cancelReason = $cancelReason;

return $this;
}

public function getCanceledTemplate(): ?Template
{
return $this->canceledTemplate;
}

public function setCanceledTemplate(Template $template = null): void
{
$this->canceledTemplate = $template;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

namespace Claroline\CursusBundle\Installation\DataFixtures\Template;

use Claroline\CoreBundle\Installation\DataFixtures\AbstractTemplateFixture;

class TrainingSessionCanceledData extends AbstractTemplateFixture
{
protected static function getTemplateType(): string
{
return 'training_session_canceled';
}

protected function getSystemTemplates(): array
{
return [
'Claroline Connect' => [
'en' => [
'title' => 'Training session cancellation',
'content' => $this->twig->render('@ClarolineCursus/template/training_session_canceled.en.pdf.twig'),
],
'fr' => [
'title' => 'Annulation de session de formation',
'content' => $this->twig->render('@ClarolineCursus/template/training_session_canceled.fr.pdf.twig'),
],
],
];
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php

namespace Claroline\CursusBundle\Installation\Migrations;

use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;

/**
* Auto-generated migration based on mapping information: modify it with caution.
*
* Generation date: 2024/09/12 02:41:32
*/
final class Version20240912144131 extends AbstractMigration
{
public function up(Schema $schema): void
{
$this->addSql('
ALTER TABLE claro_cursusbundle_course_session
ADD canceled_template_id INT DEFAULT NULL,
ADD canceled TINYINT(1) NOT NULL,
ADD cancel_reason LONGTEXT DEFAULT NULL
');
$this->addSql('
ALTER TABLE claro_cursusbundle_course_session
ADD CONSTRAINT FK_C5F56FDE4FDA7C5E FOREIGN KEY (canceled_template_id)
REFERENCES claro_template (id)
ON DELETE SET NULL
');
$this->addSql('
CREATE INDEX IDX_C5F56FDE4FDA7C5E ON claro_cursusbundle_course_session (canceled_template_id)
');
}

public function down(Schema $schema): void
{
$this->addSql('
ALTER TABLE claro_cursusbundle_course_session
DROP FOREIGN KEY FK_C5F56FDE4FDA7C5E
');
$this->addSql('
DROP INDEX IDX_C5F56FDE4FDA7C5E ON claro_cursusbundle_course_session
');
$this->addSql('
ALTER TABLE claro_cursusbundle_course_session
DROP canceled_template_id,
DROP canceled,
DROP cancel_reason
');
}
}
119 changes: 119 additions & 0 deletions src/plugin/cursus/Manager/SessionManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
use Claroline\AppBundle\Manager\PlatformManager;
use Claroline\AppBundle\Persistence\ObjectManager;
use Claroline\CoreBundle\Entity\Role;
use Claroline\CoreBundle\Entity\Template\Template;
use Claroline\CoreBundle\Entity\Workspace\Workspace;
use Claroline\CoreBundle\Event\CatalogEvents\MessageEvents;
use Claroline\CoreBundle\Event\SendMessageEvent;
Expand Down Expand Up @@ -426,6 +427,124 @@ public function sendSessionInvitation(Session $session, array $users, bool $conf
}
}

/**
* Sends cancellation notice to session users.
*/
public function sendSessionCancel(Session $session): void
{
$templateName = 'training_session_canceled';

$workspace = $session->getWorkspace();
$course = $session->getCourse();
$trainersList = '';
/** @var SessionUser[] $sessionTrainers */
$sessionTrainers = $this->sessionUserRepo->findBy([
'session' => $session,
'type' => AbstractRegistration::TUTOR,
]);

if (0 < count($sessionTrainers)) {
$trainersList = '<ul>';

foreach ($sessionTrainers as $sessionTrainer) {
$user = $sessionTrainer->getUser();
$trainersList .= '<li>'.$user->getFirstName().' '.$user->getLastName().'</li>';
}
$trainersList .= '</ul>';
}

$basicPlaceholders = array_merge([
'course_name' => $course->getName(),
'course_code' => $course->getCode(),
'course_description' => $course->getDescription(),
'session_url' => $this->routingHelper->desktopUrl('trainings').'/course/'.$session->getCourse()->getSlug().'/'.$session->getUuid(),
'session_poster' => $session->getPoster() ? '<img src="'.$this->platformManager->getUrl().'/'.$session->getPoster().'" style="max-width: 100%;" />' : '',
'session_name' => $session->getName(),
'session_description' => $session->getDescription(),
'session_trainers' => $trainersList,
'cancel_reason' => $session->getCancelReason(),
'workspace_url' => $workspace ? $this->routingHelper->workspaceUrl($workspace) : '',
],
$this->templateManager->formatDatePlaceholder('session_start', $session->getStartDate()),
$this->templateManager->formatDatePlaceholder('session_end', $session->getEndDate()),
);

$sessionLearners = $this->sessionUserRepo->findBy(['session' => $session]);
$sessionGroups = $this->sessionGroupRepo->findBy(['session' => $session]);

$users = [];
foreach ($sessionLearners as $sessionLearner) {
$user = $sessionLearner->getUser();
$users[$user->getUuid()] = $user;
}
foreach ($sessionGroups as $sessionGroup) {
$group = $sessionGroup->getGroup();
$groupUsers = $group->getUsers();

foreach ($groupUsers as $user) {
$users[$user->getUuid()] = $user;
}
}

foreach ($users as $user) {
$locale = $user->getLocale();
$placeholders = array_merge($basicPlaceholders, [
'first_name' => $user->getFirstName(),
'last_name' => $user->getLastName(),
'username' => $user->getUsername(),
]);

if ($session->getCanceledTemplate()) {
$title = $this->templateManager->getTemplateContent($session->getCanceledTemplate(), $placeholders, $locale, 'title');
$content = $this->templateManager->getTemplateContent($session->getCanceledTemplate(), $placeholders, $locale);
} else {
$title = $this->templateManager->getTemplate($templateName, $placeholders, $locale, 'title');
$content = $this->templateManager->getTemplate($templateName, $placeholders, $locale);
}

$this->eventDispatcher->dispatch(new SendMessageEvent(
$content,
$title,
[$user],
$session->getCreator()
), MessageEvents::MESSAGE_SENDING);
}
}

public function cancelSessions(array $sessionIds, ?string $cancelReason = null, ?array $cancelTemplateData = null): array
{
$processed = [];

$this->om->startFlushSuite();

/** @var Session[] $sessions */
$sessions = $this->sessionRepo->findBy([
'uuid' => $sessionIds,
]);

foreach ($sessions as $session) {
if (!$session->isCanceled()) {
$session->setCanceled(true);
$session->setCancelReason($cancelReason);

$cancelTemplate = null;
if (!empty($cancelTemplateData['id'])) {
$cancelTemplate = $this->om->getRepository(Template::class)->findOneBy(['uuid' => $cancelTemplateData['id']]);
if ($cancelTemplate) {
$this->sendSessionCancel($session);
}
}

$session->setCanceledTemplate($cancelTemplate);
$processed[] = $session;
}
}

$this->om->endFlushSuite();

return $processed;
}

/**
* Register the user to the linked workspace and events if the registration is fully validated (confirmed and validated).
*/
Expand Down
26 changes: 26 additions & 0 deletions src/plugin/cursus/Resources/config/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,32 @@ plugin:
- registration_confirmation_url
- workspace_url

- name: training_session_canceled
type: email
placeholders:
- course_name
- course_code
- course_description
- session_url
- session_name
- session_description
- session_poster
- session_start_datetime_utc
- session_start_date_utc
- session_start_time_utc
- session_start_datetime
- session_start_date
- session_start_time
- session_end_datetime_utc
- session_end_date_utc
- session_end_time_utc
- session_end_datetime
- session_end_date
- session_end_time
- session_trainers
- workspace_url
- cancel_reason

- name: training_event
type: pdf
placeholders:
Expand Down
Loading

0 comments on commit e5e884c

Please sign in to comment.