Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Session} cancellation #2855

Merged
merged 4 commits into from
Sep 19, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
45 changes: 45 additions & 0 deletions src/plugin/cursus/Controller/SessionController.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
use Claroline\CoreBundle\Component\Context\DesktopContext;
use Claroline\CoreBundle\Entity\Group;
use Claroline\CoreBundle\Entity\Organization\Organization;
use Claroline\CoreBundle\Entity\Template\Template;
use Claroline\CoreBundle\Entity\User;
use Claroline\CoreBundle\Library\Normalizer\TextNormalizer;
use Claroline\CoreBundle\Library\RoutingHelper;
Expand Down Expand Up @@ -106,6 +107,8 @@ protected function getDefaultHiddenFilters(): array
}
}

$filters['canceled'] = false;

return $filters;
}

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

/**
* @Route("/cancel", name="cancel", methods={"POST"})
*/
public function cancelAction(Request $request): JsonResponse
{
$processed = [];

$this->om->startFlushSuite();

$data = $this->decodeRequest($request);

/** @var Session[] $sessions */
$sessions = $this->om->getRepository(Session::class)->findBy([
'uuid' => $data['ids'],
]);

foreach ($sessions as $session) {
if ($this->authorization->isGranted('EDIT', $session) && !$session->isCanceled()) {
$session->setCanceled(true);
$session->setCancelReason($data['cancelReason'] ?? null);

$cancelTemplate = null;
$templateRepo = $this->om->getRepository(Template::class);
if (!empty($data['canceledTemplate']) && $data['canceledTemplate']['id']) {
$cancelTemplate = $templateRepo->findOneBy(['uuid' => $data['canceledTemplate']['id']]);
// if ($cancelTemplate) {
// $this->manager->sendSessionCancel($session);
// }
}
$session->setCanceledTemplate($cancelTemplate);

$processed[] = $session;
}
}

$this->om->endFlushSuite();

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

/**
* @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="string", 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 VARCHAR(255) 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
');
}
}
84 changes: 84 additions & 0 deletions src/plugin/cursus/Manager/SessionManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,90 @@ 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);
}
}

/**
* 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
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,12 @@ const CourseAbout = (props) => {
</AlertBlock>
}

{get(props.activeSession, 'meta.canceled') === true &&
<AlertBlock type="info" title={trans('cancel_session_info', {}, 'actions')}>
{get(props.activeSession, 'meta.cancelReason')}
</AlertBlock>
}

{!isEmpty(props.activeSession) &&
<div className="content-resume">
<div className="content-resume-info content-resume-primary">
Expand Down
6 changes: 6 additions & 0 deletions src/plugin/cursus/Resources/modules/data/sources/sessions.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,12 @@ export default {
displayed: false,
sortable: false,
filterable: true
}, {
name: 'meta.canceled',
label: trans('canceled'),
type: 'boolean',
filterable: true,
displayable: true
WolfyWin marked this conversation as resolved.
Show resolved Hide resolved
}
],
card: SessionCard
Expand Down
Loading
Loading