diff --git a/public/main/inc/ajax/model.ajax.php b/public/main/inc/ajax/model.ajax.php
index 985fcd64db0..ed4b5c70aaa 100644
--- a/public/main/inc/ajax/model.ajax.php
+++ b/public/main/inc/ajax/model.ajax.php
@@ -677,7 +677,7 @@ function getWhereClause($col, $oper, $val)
$count = ExerciseLib::get_count_exam_results(
$exerciseId,
$whereCondition,
- '',
+ $courseId,
false,
true,
$status
@@ -839,6 +839,7 @@ function getWhereClause($col, $oper, $val)
['where' => $whereCondition, 'extra' => $extra_fields]
);
break;
+ case 'replication':
case 'custom':
case 'simple':
$count = SessionManager::getSessionsForAdmin(
@@ -1981,7 +1982,7 @@ function getWhereClause($col, $oper, $val)
break;
case 'custom':
case 'simple':
- case 'all':
+ case 'replication':
$result = SessionManager::getSessionsForAdmin(
api_get_user_id(),
[
@@ -2000,6 +2001,7 @@ function getWhereClause($col, $oper, $val)
break;
case 'active':
case 'close':
+ case 'all':
$result = SessionManager::formatSessionsAdminForGrid(
[
'where' => $whereCondition,
diff --git a/public/main/inc/lib/sessionmanager.lib.php b/public/main/inc/lib/sessionmanager.lib.php
index 851c384018a..68f23e8a0bb 100644
--- a/public/main/inc/lib/sessionmanager.lib.php
+++ b/public/main/inc/lib/sessionmanager.lib.php
@@ -159,7 +159,12 @@ public static function create_session(
$sendSubscriptionNotification = false,
$accessUrlId = 0,
$status = 0,
- $notifyBoss = false
+ $notifyBoss = false,
+ $parentId = null,
+ $daysBeforeFinishingForReinscription = null,
+ $lastRepetition = false,
+ $daysBeforeFinishingToCreateNewRepetition = null,
+ $validityInDays = null
) {
global $_configuration;
@@ -188,25 +193,17 @@ public static function create_session(
$endDate = Database::escape_string($endDate);
if (empty($name)) {
- $msg = get_lang('A title is required for the session');
-
- return $msg;
+ return get_lang('A title is required for the session');
} elseif (!empty($startDate) && !api_is_valid_date($startDate, 'Y-m-d H:i') &&
!api_is_valid_date($startDate, 'Y-m-d H:i:s')
) {
- $msg = get_lang('Invalid start date was given.');
-
- return $msg;
+ return get_lang('Invalid start date was given.');
} elseif (!empty($endDate) && !api_is_valid_date($endDate, 'Y-m-d H:i') &&
!api_is_valid_date($endDate, 'Y-m-d H:i:s')
) {
- $msg = get_lang('Invalid end date was given.');
-
- return $msg;
+ return get_lang('Invalid end date was given.');
} elseif (!empty($startDate) && !empty($endDate) && $startDate >= $endDate) {
- $msg = get_lang('The first date should be before the end date');
-
- return $msg;
+ return get_lang('The first date should be before the end date');
} else {
$ready_to_create = false;
if ($fixSessionNameIfExists) {
@@ -214,16 +211,12 @@ public static function create_session(
if ($name) {
$ready_to_create = true;
} else {
- $msg = get_lang('Session title already exists');
-
- return $msg;
+ return get_lang('Session title already exists');
}
} else {
$rs = Database::query("SELECT 1 FROM $tbl_session WHERE title='".$name."'");
if (Database::num_rows($rs)) {
- $msg = get_lang('Session title already exists');
-
- return $msg;
+ return get_lang('Session title already exists');
}
$ready_to_create = true;
}
@@ -239,7 +232,11 @@ public static function create_session(
->setShowDescription(1 === $showDescription)
->setSendSubscriptionNotification((bool) $sendSubscriptionNotification)
->setNotifyBoss((bool) $notifyBoss)
- ;
+ ->setParentId($parentId)
+ ->setDaysToReinscription((int) $daysBeforeFinishingForReinscription)
+ ->setLastRepetition($lastRepetition)
+ ->setDaysToNewRepetition((int) $daysBeforeFinishingToCreateNewRepetition)
+ ->setValidityInDays((int) $validityInDays);
foreach ($coachesId as $coachId) {
$session->addGeneralCoach(api_get_user_entity($coachId));
@@ -288,18 +285,6 @@ public static function create_session(
$extraFields['item_id'] = $session_id;
$sessionFieldValue = new ExtraFieldValue('session');
$sessionFieldValue->saveFieldValues($extraFields);
- /*
- Sends a message to the user_id = 1
-
- $user_info = api_get_user_info(1);
- $complete_name = $user_info['firstname'].' '.$user_info['lastname'];
- $subject = api_get_setting('siteName').' - '.get_lang('A new session has been created');
- $message = get_lang('A new session has been created')."
".get_lang('Session name').' : '.$name;
- api_mail_html($complete_name, $user_info['email'], $subject, $message);
- *
- */
- // Adding to the correct URL
- //UrlManager::add_session_to_url($session_id, $accessUrlId);
// add event to system log
$user_id = api_get_user_id();
@@ -523,6 +508,10 @@ public static function getSessionsForAdmin(
}
$select .= ', status';
+ if ('replication' === $listType) {
+ $select .= ', parent_id';
+ }
+
if (isset($options['order'])) {
$isMakingOrder = 0 === strpos($options['order'], 'category_name');
}
@@ -654,6 +643,11 @@ public static function getSessionsForAdmin(
)
)";
break;
+ case 'replication':
+ $formatted = false;
+ $query .= "AND s.days_to_new_repetition IS NOT NULL
+ AND (SELECT COUNT(id) FROM session AS child WHERE child.parent_id = s.id) <= 1";
+ break;
}
$query .= $order;
@@ -668,6 +662,23 @@ public static function getSessionsForAdmin(
$session['users'] = Database::fetch_assoc($result)['nbr'];
}
}
+
+ if ('replication' === $listType) {
+ $formattedSessions = [];
+ foreach ($sessions as $session) {
+ $formattedSessions[] = $session;
+ if (isset($session['id'])) {
+ $childSessions = array_filter($sessions, fn($s) => isset($s['parent_id']) && $s['parent_id'] === $session['id']);
+ foreach ($childSessions as $childSession) {
+ $childSession['title'] = '-- ' . $childSession['title'];
+ $formattedSessions[] = $childSession;
+ }
+ }
+ }
+
+ return $formattedSessions;
+ }
+
if ('all' === $listType) {
if ($getCount) {
return $sessions[0]['total_rows'];
@@ -1793,7 +1804,12 @@ public static function edit_session(
$sessionAdminId = 0,
$sendSubscriptionNotification = false,
$status = 0,
- $notifyBoss = 0
+ $notifyBoss = 0,
+ $parentId = 0,
+ $daysBeforeFinishingForReinscription = null,
+ $daysBeforeFinishingToCreateNewRepetition = null,
+ $lastRepetition = false,
+ $validityInDays = null
) {
$id = (int) $id;
$status = (int) $status;
@@ -1863,6 +1879,11 @@ public static function edit_session(
->setVisibility($visibility)
->setSendSubscriptionNotification((bool) $sendSubscriptionNotification)
->setNotifyBoss((bool) $notifyBoss)
+ ->setParentId($parentId)
+ ->setDaysToReinscription((int) $daysBeforeFinishingForReinscription)
+ ->setLastRepetition($lastRepetition)
+ ->setDaysToNewRepetition((int) $daysBeforeFinishingToCreateNewRepetition)
+ ->setValidityInDays((int) $validityInDays)
->setAccessStartDate(null)
->setAccessStartDate(null)
->setDisplayStartDate(null)
@@ -1871,6 +1892,16 @@ public static function edit_session(
->setCoachAccessEndDate(null)
;
+ if ($parentId) {
+ $sessionEntity->setParentId($parentId);
+ } else {
+ $sessionEntity->setParentId(null);
+ }
+
+ $sessionEntity->setDaysToReinscription($daysBeforeFinishingForReinscription);
+ $sessionEntity->setLastRepetition($lastRepetition);
+ $sessionEntity->setDaysToNewRepetition($daysBeforeFinishingToCreateNewRepetition);
+
$newGeneralCoaches = array_map(
fn($coachId) => api_get_user_entity($coachId),
$coachesId
@@ -8271,6 +8302,85 @@ public static function setForm(FormValidator $form, Session $session = null, $fr
$extra_field = new ExtraFieldModel('session');
$extra = $extra_field->addElements($form, $session ? $session->getId() : 0, ['image']);
+ if ('true' === api_get_setting('session.enable_auto_reinscription')) {
+ $form->addElement(
+ 'text',
+ 'days_before_finishing_for_reinscription',
+ get_lang('Days before finishing for reinscription'),
+ ['maxlength' => 5]
+ );
+ $form->addRule(
+ 'days_before_finishing_for_reinscription',
+ get_lang('Days must be a positive number or empty'),
+ 'regex',
+ '/^\d*$/'
+ );
+ }
+
+ if ('true' === api_get_setting('session.enable_session_replication')) {
+ $form->addElement(
+ 'text',
+ 'days_before_finishing_to_create_new_repetition',
+ get_lang('Days before finishing to create new repetition'),
+ ['maxlength' => 5]
+ );
+ $form->addRule(
+ 'days_before_finishing_to_create_new_repetition',
+ get_lang('Days must be a positive number or empty'),
+ 'regex',
+ '/^\d*$/'
+ );
+ }
+
+ if ('true' === api_get_setting('session.enable_auto_reinscription') || 'true' === api_get_setting('session.enable_session_replication')) {
+ $form->addElement(
+ 'checkbox',
+ 'last_repetition',
+ get_lang('Last repetition')
+ );
+
+ $form->addElement(
+ 'number',
+ 'validity_in_days',
+ get_lang('Validity in days'),
+ [
+ 'min' => 0,
+ 'max' => 365,
+ 'step' => 1,
+ 'placeholder' => get_lang('Enter the number of days'),
+ ]
+ );
+
+ $form->addRule(
+ 'validity_in_days',
+ get_lang('The field must be a positive number'),
+ 'numeric',
+ null,
+ 'client'
+ );
+ }
+
+ /** @var HTML_QuickForm_select $element */
+ $element = $form->createElement(
+ 'select',
+ 'parent_id',
+ get_lang('Parent session'),
+ [],
+ ['class' => 'form-control']
+ );
+
+ $element->addOption(get_lang('None'), 0, []);
+ $sessions = SessionManager::getListOfParentSessions();
+ $currentSessionId = $session?->getId();
+ foreach ($sessions as $id => $title) {
+ if ($id !== $currentSessionId) {
+ $attributes = [];
+ $element->addOption($title, $id, $attributes);
+ }
+ }
+
+ $form->addElement($element);
+
$form->addElement('html', '');
$js = $extra['jquery_ready_content'];
@@ -8791,7 +8901,7 @@ public static function getGridColumns(
];
break;
-
+ case 'replication':
case 'custom':
$columns = [
'#',
@@ -8810,6 +8920,7 @@ public static function getGridColumns(
[
'name' => 'title',
'index' => 's.title',
+ 'width' => '260px',
'width' => '300',
'align' => 'left',
'search' => 'true',
@@ -9805,9 +9916,9 @@ public static function getDefaultSessionTab()
}
/**
- * @return array
+ * @return string
*/
- public static function getSessionListTabs($listType)
+ public static function getSessionListTabs($listType): string
{
$tabs = [
[
@@ -9826,10 +9937,10 @@ public static function getSessionListTabs($listType)
'content' => get_lang('Custom list'),
'url' => api_get_path(WEB_CODE_PATH).'session/session_list.php?list_type=custom',
],
- /*[
- 'content' => get_lang('Complete'),
- 'url' => api_get_path(WEB_CODE_PATH).'session/session_list_simple.php?list_type=complete',
- ],*/
+ [
+ 'content' => get_lang('Replication'),
+ 'url' => api_get_path(WEB_CODE_PATH).'session/session_list.php?list_type=replication',
+ ],
];
$default = null;
switch ($listType) {
@@ -9845,6 +9956,9 @@ public static function getSessionListTabs($listType)
case 'custom':
$default = 4;
break;
+ case 'replication':
+ $default = 5;
+ break;
}
return Display::tabsOnlyLink($tabs, $default);
@@ -10227,6 +10341,24 @@ public static function getAllUserIdsInSession(int $sessionId): array
return $users;
}
+ /**
+ * Retrieves a list of parent sessions.
+ */
+ public static function getListOfParentSessions(): array
+ {
+ $sessions = [];
+ $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
+ $sql = "SELECT id, title FROM $tbl_session WHERE parent_id IS NULL ORDER BY title";
+ $result = Database::query($sql);
+
+ while ($row = Database::fetch_array($result)) {
+ $sessions[$row['id']] = $row['title'];
+ }
+
+ return $sessions;
+ }
+
+
/**
* Method to export sessions data as CSV
*/
@@ -10413,5 +10545,4 @@ private static function generateSessionCourseReportData($sessionId, $courseId, $
return [$csvHeaders, $csvContent];
}
-
}
diff --git a/public/main/session/session_add.php b/public/main/session/session_add.php
index eccd3bb61cc..309b94170c6 100644
--- a/public/main/session/session_add.php
+++ b/public/main/session/session_add.php
@@ -38,7 +38,7 @@ function search_coachs($needle)
if (!empty($needle)) {
$order_clause = api_sort_by_first_name() ? ' ORDER BY firstname, lastname, username' : ' ORDER BY lastname, firstname, username';
- // search users where username or firstname or lastname begins likes $needle
+ // search users where username or firstname or lastname begins like $needle
$sql = 'SELECT username, lastname, firstname
FROM '.$tbl_user.' user
WHERE (username LIKE "'.$needle.'%"
@@ -57,7 +57,7 @@ function search_coachs($needle)
INNER JOIN '.$tbl_user_rel_access_url.' url_user
ON (url_user.user_id=user.user_id)
WHERE
- access_url_id = '.$access_url_id.' AND
+ access_url_id = '.$access_url_id.' AND
(
username LIKE "'.$needle.'%" OR
firstname LIKE "'.$needle.'%" OR
@@ -219,6 +219,7 @@ function repopulateSelect2Values(selectId) {
";
$form->addButtonNext(get_lang('Next step'));
+$showValidityField = 'true' === api_get_setting('session.enable_auto_reinscription') || 'true' === api_get_setting('session.enable_session_replication');
$formDefaults = [];
if (!$formSent) {
@@ -245,10 +246,22 @@ function (User $user) {
$session->getGeneralCoaches()->getValues()
),
'session_template' => $session->getTitle(),
+ 'days_before_finishing_for_reinscription' => $session->getDaysToReinscription() ?? '',
+ 'days_before_finishing_to_create_new_repetition' => $session->getDaysToNewRepetition() ?? '',
+ 'last_repetition' => $session->getLastRepetition(),
+ 'parent_id' => $session->getParentId() ?? 0,
];
+
+ if ($showValidityField) {
+ $formDefaults['validity_in_days'] = $session->getValidityInDays();
+ }
+
} else {
$formDefaults['access_start_date'] = $formDefaults['display_start_date'] = api_get_local_time();
$formDefaults['coach_username'] = [api_get_user_id()];
+ if ($showValidityField) {
+ $formDefaults['validity_in_days'] = null;
+ }
}
}
@@ -261,15 +274,12 @@ function (User $user) {
$endDate = $params['access_end_date'];
$displayStartDate = $params['display_start_date'];
$displayEndDate = $params['display_end_date'];
- $coachStartDate = $params['coach_access_start_date'];
- if (empty($coachStartDate)) {
- $coachStartDate = $displayStartDate;
- }
+ $coachStartDate = $params['coach_access_start_date'] ?? $displayStartDate;
$coachEndDate = $params['coach_access_end_date'];
$coachUsername = $params['coach_username'];
$id_session_category = (int) $params['session_category'];
$id_visibility = $params['session_visibility'];
- $duration = isset($params['duration']) ? $params['duration'] : null;
+ $duration = $params['duration'] ?? null;
$description = $params['description'];
$showDescription = isset($params['show_description']) ? 1 : 0;
$sendSubscriptionNotification = isset($params['send_subscription_notification']);
@@ -311,6 +321,13 @@ function (User $user) {
}
}
}
+ $status = $params['status'] ?? 0;
+
+ $parentId = $params['parent_id'] ?? null;
+ $daysBeforeFinishingForReinscription = $params['days_before_finishing_for_reinscription'] ?? null;
+ $lastRepetition = isset($params['last_repetition']) ? true : false;
+ $daysBeforeFinishingToCreateNewRepetition = $params['days_before_finishing_to_create_new_repetition'] ?? null;
+ $validityInDays = $params['validity_in_days'] ?? null;
$return = SessionManager::create_session(
$title,
@@ -328,11 +345,16 @@ function (User $user) {
$description,
$showDescription,
$extraFields,
- null,
+ 0,
$sendSubscriptionNotification,
api_get_current_access_url_id(),
$status,
- $notifyBoss
+ $notifyBoss,
+ $parentId,
+ $daysBeforeFinishingForReinscription,
+ $lastRepetition,
+ $daysBeforeFinishingToCreateNewRepetition,
+ $validityInDays
);
if ($return == strval(intval($return))) {
diff --git a/public/main/session/session_edit.php b/public/main/session/session_edit.php
index 0c293604ebd..ff791c6ff31 100644
--- a/public/main/session/session_edit.php
+++ b/public/main/session/session_edit.php
@@ -49,6 +49,7 @@
';
$form->addButtonUpdate(get_lang('Edit this session'));
+$showValidityField = 'true' === api_get_setting('session.enable_auto_reinscription') || 'true' === api_get_setting('session.enable_session_replication');
$formDefaults = [
'id' => $session->getId(),
@@ -72,8 +73,16 @@ function (User $user) {
},
$session->getGeneralCoaches()->getValues()
),
+ 'days_before_finishing_for_reinscription' => $session->getDaysToReinscription() ?? '',
+ 'days_before_finishing_to_create_new_repetition' => $session->getDaysToNewRepetition() ?? '',
+ 'last_repetition' => $session->getLastRepetition(),
+ 'parent_id' => $session->getParentId() ?? 0,
];
+if ($showValidityField) {
+ $formDefaults['validity_in_days'] = $session->getValidityInDays();
+}
+
$form->setDefaults($formDefaults);
if ($form->validate()) {
@@ -113,6 +122,12 @@ function (User $user) {
$status = $params['status'] ?? 0;
$notifyBoss = isset($params['notify_boss']) ? 1 : 0;
+ $parentId = $params['parent_id'] ?? 0;
+ $daysBeforeFinishingForReinscription = $params['days_before_finishing_for_reinscription'] ?? null;
+ $daysBeforeFinishingToCreateNewRepetition = $params['days_before_finishing_to_create_new_repetition'] ?? null;
+ $lastRepetition = isset($params['last_repetition']);
+ $validityInDays = $params['validity_in_days'] ?? null;
+
$return = SessionManager::edit_session(
$id,
$name,
@@ -132,7 +147,12 @@ function (User $user) {
null,
$sendSubscriptionNotification,
$status,
- $notifyBoss
+ $notifyBoss,
+ $parentId,
+ $daysBeforeFinishingForReinscription,
+ $daysBeforeFinishingToCreateNewRepetition,
+ $lastRepetition,
+ $validityInDays
);
if ($return) {
diff --git a/public/main/session/session_list.php b/public/main/session/session_list.php
index 1ebe175b831..19312887af1 100644
--- a/public/main/session/session_list.php
+++ b/public/main/session/session_list.php
@@ -133,11 +133,17 @@
});
';
-// jqgrid will use this URL to do the selects
-if (!empty($courseId)) {
- $url = api_get_path(WEB_AJAX_PATH).'model.ajax.php?a=get_sessions&course_id='.$courseId;
-} else {
- $url = api_get_path(WEB_AJAX_PATH).'model.ajax.php?a=get_sessions';
+switch ($listType) {
+ case 'replication':
+ $url = api_get_path(WEB_AJAX_PATH).'model.ajax.php?a=get_sessions&list_type=replication';
+ break;
+ default:
+ if (!empty($courseId)) {
+ $url = api_get_path(WEB_AJAX_PATH).'model.ajax.php?a=get_sessions&course_id='.$courseId;
+ } else {
+ $url = api_get_path(WEB_AJAX_PATH).'model.ajax.php?a=get_sessions';
+ }
+ break;
}
if (isset($_REQUEST['keyword'])) {
diff --git a/src/CoreBundle/Command/ReinscriptionCheckCommand.php b/src/CoreBundle/Command/ReinscriptionCheckCommand.php
new file mode 100644
index 00000000000..7136a0ccbac
--- /dev/null
+++ b/src/CoreBundle/Command/ReinscriptionCheckCommand.php
@@ -0,0 +1,283 @@
+sessionRepository = $sessionRepository;
+ $this->certificateRepository = $certificateRepository;
+ $this->entityManager = $entityManager;
+ }
+
+ protected function configure(): void
+ {
+ $this
+ ->setDescription('Checks for users who have validated all gradebooks and reinscribe them into new sessions if needed.')
+ ->addOption(
+ 'debug',
+ null,
+ InputOption::VALUE_NONE,
+ 'If set, debug messages will be shown.'
+ );
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output): int
+ {
+ $debug = $input->getOption('debug');
+
+ $sessions = $this->sessionRepository->findAll();
+
+ foreach ($sessions as $session) {
+ if ($session->getValidityInDays() === null) {
+ continue;
+ }
+
+ $users = $this->getUsersForSession($session);
+
+ foreach ($users as $user) {
+ if ($debug) {
+ $output->writeln(sprintf('Processing user %d in session %d.', $user->getId(), $session->getId()));
+ }
+
+ if ($this->isUserReinscribed($user, $session)) {
+ continue;
+ }
+
+ if ($this->isUserAlreadyEnrolledInChildSession($user, $session)) {
+ if ($debug) {
+ $output->writeln(sprintf('User %d is already enrolled in a valid child session.', $user->getId()));
+ }
+ continue;
+ }
+
+ $certificates = $this->getUserCertificatesForSession($user, $session);
+
+ if ($this->hasUserValidatedAllGradebooks($session, $certificates)) {
+ $latestValidationDate = $this->getLatestCertificateDate($certificates);
+
+ if ($latestValidationDate !== null) {
+ $reinscriptionDate = (clone $latestValidationDate)->modify("+{$session->getValidityInDays()} days");
+
+ if ($debug) {
+ $output->writeln(sprintf(
+ 'User %d - Latest certificate date: %s, Reinscription date: %s',
+ $user->getId(),
+ $latestValidationDate->format('Y-m-d'),
+ $reinscriptionDate->format('Y-m-d')
+ ));
+ }
+
+ if (new \DateTime() >= $reinscriptionDate) {
+ $validSession = $this->findValidSessionInHierarchy($session);
+
+ if ($validSession) {
+ $this->enrollUserInSession($user, $validSession, $session);
+ if ($debug) {
+ $output->writeln(sprintf(
+ 'User %d re-enrolled into session %d.',
+ $user->getId(),
+ $validSession->getId()
+ ));
+ }
+ }
+ }
+ } else {
+ if ($debug) {
+ $output->writeln(sprintf(
+ 'User %d has no valid certificates for session %d.',
+ $user->getId(),
+ $session->getId()
+ ));
+ }
+ }
+ }
+ }
+ }
+
+ return Command::SUCCESS;
+ }
+
+ /**
+ * Retrieves all users associated with the session.
+ */
+ private function getUsersForSession(Session $session): array
+ {
+ $usersToNotify = [];
+ $sessionCourses = $this->entityManager->getRepository(SessionRelCourse::class)->findBy(['session' => $session]);
+
+ foreach ($sessionCourses as $courseRel) {
+ $course = $courseRel->getCourse();
+
+ $studentSubscriptions = $session->getSessionRelCourseRelUsersByStatus($course, Session::STUDENT);
+ foreach ($studentSubscriptions as $studentSubscription) {
+ $usersToNotify[$studentSubscription->getUser()->getId()] = $studentSubscription->getUser();
+ }
+
+ $coachSubscriptions = $session->getSessionRelCourseRelUsersByStatus($course, Session::COURSE_COACH);
+ foreach ($coachSubscriptions as $coachSubscription) {
+ $usersToNotify[$coachSubscription->getUser()->getId()] = $coachSubscription->getUser();
+ }
+ }
+
+ $generalCoaches = $session->getGeneralCoaches();
+ foreach ($generalCoaches as $generalCoach) {
+ $usersToNotify[$generalCoach->getId()] = $generalCoach;
+ }
+
+ return array_values($usersToNotify);
+ }
+
+ /**
+ * Checks if the user is already enrolled in a valid child session.
+ */
+ private function isUserAlreadyEnrolledInChildSession($user, $parentSession): bool
+ {
+ $childSessions = $this->sessionRepository->findChildSessions($parentSession);
+
+ foreach ($childSessions as $childSession) {
+ if ($this->findUserSubscriptionInSession($user, $childSession)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Gets the user's certificates for the courses in the session.
+ */
+ private function getUserCertificatesForSession($user, Session $session): array
+ {
+ $courses = $this->entityManager->getRepository(SessionRelCourse::class)
+ ->findBy(['session' => $session]);
+
+ $courseIds = array_map(fn($rel) => $rel->getCourse()->getId(), $courses);
+
+ return $this->certificateRepository->createQueryBuilder('gc')
+ ->join('gc.category', 'cat')
+ ->where('gc.user = :user')
+ ->andWhere('cat.course IN (:courses)')
+ ->setParameter('user', $user)
+ ->setParameter('courses', $courseIds)
+ ->getQuery()
+ ->getResult();
+ }
+
+ /**
+ * Checks if the user has validated all gradebooks in the session.
+ */
+ private function hasUserValidatedAllGradebooks(Session $session, array $certificates): bool
+ {
+ $courses = $this->entityManager->getRepository(SessionRelCourse::class)
+ ->findBy(['session' => $session]);
+
+ return count($certificates) === count($courses);
+ }
+
+ /**
+ * Returns the latest certificate creation date.
+ */
+ private function getLatestCertificateDate(array $certificates): ?\DateTime
+ {
+ $dates = array_map(fn($cert) => $cert->getCreatedAt(), $certificates);
+
+ if (empty($dates)) {
+ return null;
+ }
+
+ return max($dates);
+ }
+
+ /**
+ * Enrolls the user in a new session and updates the previous session subscription.
+ */
+ private function enrollUserInSession($user, $newSession, $oldSession): void
+ {
+ $existingSubscription = $this->findUserSubscriptionInSession($user, $newSession);
+
+ if (!$existingSubscription) {
+ $newSession->addUserInSession(Session::STUDENT, $user);
+
+ $subscription = $this->findUserSubscriptionInSession($user, $oldSession);
+ if ($subscription) {
+ $subscription->setNewSubscriptionSessionId($newSession->getId());
+ }
+
+ $this->entityManager->persist($newSession);
+ $this->entityManager->flush();
+ }
+ }
+
+ /**
+ * Determines if the user has already been reinscribed.
+ */
+ private function isUserReinscribed($user, Session $session): bool
+ {
+ $subscription = $this->findUserSubscriptionInSession($user, $session);
+ return $subscription && $subscription->getNewSubscriptionSessionId() !== null;
+ }
+
+ /**
+ * Finds the user's subscription in the specified session.
+ */
+ private function findUserSubscriptionInSession($user, $session)
+ {
+ return $this->entityManager->getRepository(SessionRelUser::class)
+ ->findOneBy([
+ 'user' => $user,
+ 'session' => $session,
+ ]);
+ }
+
+ /**
+ * Finds a valid session within the session hierarchy.
+ */
+ private function findValidSessionInHierarchy(Session $session): ?Session
+ {
+ $childSessions = $this->sessionRepository->findChildSessions($session);
+
+ /* @var Session $child */
+ foreach ($childSessions as $child) {
+ $validUntil = (clone $child->getAccessEndDate())->modify("-{$child->getDaysToReinscription()} days");
+ if (new \DateTime() <= $validUntil) {
+ return $child;
+ }
+ }
+
+ $parentSession = $this->sessionRepository->findParentSession($session);
+
+ if ($parentSession && new \DateTime() <= $parentSession->getAccessEndDate()) {
+ return $parentSession;
+ }
+
+ return null;
+ }
+}
diff --git a/src/CoreBundle/Command/SessionRepetitionCommand.php b/src/CoreBundle/Command/SessionRepetitionCommand.php
new file mode 100644
index 00000000000..aee12a1f0d6
--- /dev/null
+++ b/src/CoreBundle/Command/SessionRepetitionCommand.php
@@ -0,0 +1,209 @@
+sessionRepository = $sessionRepository;
+ $this->entityManager = $entityManager;
+ $this->mailer = $mailer;
+ $this->translator = $translator;
+ }
+
+ protected function configure(): void
+ {
+ $this
+ ->setDescription('Automatically duplicates sessions that meet the repetition criteria.')
+ ->addOption('debug', null, InputOption::VALUE_NONE, 'Enable debug mode');
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output): int
+ {
+ $debug = $input->getOption('debug');
+
+ // Find sessions that meet the repetition criteria
+ $sessions = $this->sessionRepository->findSessionsWithoutChildAndReadyForRepetition();
+
+ if ($debug) {
+ $output->writeln(sprintf('Found %d session(s) ready for repetition.', count($sessions)));
+ }
+
+ foreach ($sessions as $session) {
+ if ($debug) {
+ $output->writeln(sprintf('Processing session: %d', $session->getId()));
+ }
+
+ // Duplicate session
+ $newSession = $this->duplicateSession($session, $debug, $output);
+
+ // Notify general coach of the new session
+ $this->notifyGeneralCoach($newSession, $debug, $output);
+
+ $output->writeln('Created new session: ' . $newSession->getId() . ' from session: ' . $session->getId());
+ }
+
+ return Command::SUCCESS;
+ }
+
+ /**
+ * Duplicates a session and creates a new session with adjusted dates.
+ */
+ private function duplicateSession(Session $session, bool $debug, OutputInterface $output): Session
+ {
+ // Calculate new session dates based on the duration of the original session
+ $duration = $session->getAccessEndDate()->diff($session->getAccessStartDate())->days;
+ $newStartDate = (clone $session->getAccessEndDate())->modify('+1 day');
+ $newEndDate = (clone $newStartDate)->modify("+{$duration} days");
+
+ if ($debug) {
+ $output->writeln(sprintf(
+ 'Duplicating session %d. New start date: %s, New end date: %s',
+ $session->getId(),
+ $newStartDate->format('Y-m-d H:i:s'),
+ $newEndDate->format('Y-m-d H:i:s')
+ ));
+ }
+
+ // Create a new session with the same details as the original session
+ $newSession = new Session();
+ $newSession
+ ->setTitle($session->getTitle() . ' (Repetition ' . $session->getId() . ' - ' . time() . ')')
+ ->setAccessStartDate($newStartDate)
+ ->setAccessEndDate($newEndDate)
+ ->setDisplayStartDate($newStartDate)
+ ->setDisplayEndDate($newEndDate)
+ ->setCoachAccessStartDate($newStartDate)
+ ->setCoachAccessEndDate($newEndDate)
+ ->setVisibility($session->getVisibility())
+ ->setDuration(0)
+ ->setDescription($session->getDescription() ?? '')
+ ->setShowDescription($session->getShowDescription() ?? false)
+ ->setCategory($session->getCategory())
+ ->setPromotion($session->getPromotion())
+ ->setDaysToReinscription($session->getDaysToReinscription())
+ ->setDaysToNewRepetition($session->getDaysToNewRepetition())
+ ->setParentId($session->getId())
+ ->setLastRepetition(false);
+
+ // Copy the AccessUrls from the original session
+ foreach ($session->getUrls() as $accessUrl) {
+ $newSession->addAccessUrl($accessUrl->getUrl());
+ }
+
+ // Copy the courses from the original session
+ foreach ($session->getCourses() as $sessionRelCourse) {
+ $course = $sessionRelCourse->getCourse();
+ if ($course) {
+ $newSession->addCourse($course);
+ }
+ }
+
+ // Copy the general coaches from the original session
+ foreach ($session->getGeneralCoaches() as $coach) {
+ $newSession->addGeneralCoach($coach);
+ }
+
+ // Save the new session
+ $this->entityManager->persist($newSession);
+ $this->entityManager->flush();
+
+ if ($debug) {
+ $output->writeln(sprintf('New session %d created successfully.', $newSession->getId()));
+ }
+
+ return $newSession;
+ }
+
+ /**
+ * Retrieves or creates a default AccessUrl for sessions.
+ */
+ private function getDefaultAccessUrl()
+ {
+ return $this->entityManager->getRepository(AccessUrl::class)->findOneBy([]);
+ }
+
+
+ /**
+ * Notifies the general coach of the session about the new repetition.
+ */
+ private function notifyGeneralCoach(Session $newSession, bool $debug, OutputInterface $output): void
+ {
+ $generalCoach = $newSession->getGeneralCoaches()->first();
+ if ($generalCoach) {
+ $message = sprintf(
+ 'A new repetition of the session "%s" has been created. Please review the details: %s',
+ $newSession->getTitle(),
+ $this->generateSessionSummaryLink($newSession)
+ );
+
+ if ($debug) {
+ $output->writeln(sprintf('Notifying coach (ID: %d) for session %d', $generalCoach->getId(), $newSession->getId()));
+ }
+
+ // Send message to the general coach
+ $this->sendMessage($generalCoach->getEmail(), $message);
+
+ if ($debug) {
+ $output->writeln('Notification sent.');
+ }
+ } else {
+ if ($debug) {
+ $output->writeln('No general coach found for session ' . $newSession->getId());
+ }
+ }
+ }
+
+ /**
+ * Sends an email message to a user.
+ */
+ private function sendMessage(string $recipientEmail, string $message): void
+ {
+ $subject = $this->translator->trans('New Session Repetition Created');
+
+ $email = (new Email())
+ ->from('no-reply@yourdomain.com')
+ ->to($recipientEmail)
+ ->subject($subject)
+ ->html('
' . $message . '
'); + + $this->mailer->send($email); + } + + /** + * Generates a link to the session summary page. + */ + private function generateSessionSummaryLink(Session $session): string + { + return '/main/session/resume_session.php?id_session=' . $session->getId(); + } +} diff --git a/src/CoreBundle/DataFixtures/SettingsCurrentFixtures.php b/src/CoreBundle/DataFixtures/SettingsCurrentFixtures.php index 5735e9eae0d..01e0da4fb43 100644 --- a/src/CoreBundle/DataFixtures/SettingsCurrentFixtures.php +++ b/src/CoreBundle/DataFixtures/SettingsCurrentFixtures.php @@ -3291,6 +3291,16 @@ public static function getNewConfigurationSettings(): array 'title' => 'Sort session templates by id in session creation form', 'comment' => '', ], + [ + 'name' => 'enable_auto_reinscription', + 'title' => 'Enable Automatic Reinscription', + 'comment' => 'Enable or disable automatic reinscription when course validity expires. The related cron job must also be activated.', + ], + [ + 'name' => 'enable_session_replication', + 'title' => 'Enable Session Replication', + 'comment' => 'Enable or disable automatic session replication. The related cron job must also be activated.', + ], [ 'name' => 'session_multiple_subscription_students_list_avoid_emptying', 'title' => 'Prevent emptying the subscribed users in session subscription', diff --git a/src/CoreBundle/Entity/Session.php b/src/CoreBundle/Entity/Session.php index 0bda39bde58..4185ef89371 100644 --- a/src/CoreBundle/Entity/Session.php +++ b/src/CoreBundle/Entity/Session.php @@ -380,9 +380,25 @@ class Session implements ResourceWithAccessUrlInterface, Stringable #[Groups(['user_subscriptions:sessions', 'session:read', 'session:item:read'])] private int $accessVisibility = 0; + #[ORM\Column(name: 'parent_id', type: 'integer', nullable: true)] + protected ?int $parentId = null; + + #[ORM\Column(name: 'days_to_reinscription', type: 'integer', nullable: true)] + protected ?int $daysToReinscription = null; + + #[ORM\Column(name: 'last_repetition', type: 'boolean', nullable: false, options: ['default' => false])] + protected bool $lastRepetition = false; + + #[ORM\Column(name: 'days_to_new_repetition', type: 'integer', nullable: true)] + protected ?int $daysToNewRepetition = null; + #[ORM\Column(name: 'notify_boss', type: 'boolean', options: ['default' => false])] protected bool $notifyBoss = false; + #[Groups(['session:basic', 'session:read', 'session:write'])] + #[ORM\Column(name: 'validity_in_days', type: 'integer', nullable: true)] + protected ?int $validityInDays = null; + public function __construct() { $this->skills = new ArrayCollection(); @@ -446,7 +462,7 @@ public function setDuration(int $duration): self public function getShowDescription(): bool { - return $this->showDescription; + return $this->showDescription ?? false; } public function setShowDescription(bool $showDescription): self @@ -1474,6 +1490,54 @@ public function getClosedOrHiddenCourses(): Collection )); } + public function getParentId(): ?int + { + return $this->parentId; + } + + public function setParentId(?int $parentId): self + { + $this->parentId = $parentId; + + return $this; + } + + public function getDaysToReinscription(): ?int + { + return $this->daysToReinscription; + } + + public function setDaysToReinscription(?int $daysToReinscription): self + { + $this->daysToReinscription = $daysToReinscription; + + return $this; + } + + public function getLastRepetition(): bool + { + return $this->lastRepetition; + } + + public function setLastRepetition(bool $lastRepetition): self + { + $this->lastRepetition = $lastRepetition; + + return $this; + } + + public function getDaysToNewRepetition(): ?int + { + return $this->daysToNewRepetition; + } + + public function setDaysToNewRepetition(?int $daysToNewRepetition): self + { + $this->daysToNewRepetition = $daysToNewRepetition; + + return $this; + } + public function getNotifyBoss(): bool { return $this->notifyBoss; @@ -1485,4 +1549,15 @@ public function setNotifyBoss(bool $notifyBoss): self return $this; } + + public function getValidityInDays(): ?int + { + return $this->validityInDays; + } + + public function setValidityInDays(?int $validityInDays): self + { + $this->validityInDays = $validityInDays; + return $this; + } } diff --git a/src/CoreBundle/Entity/SessionRelUser.php b/src/CoreBundle/Entity/SessionRelUser.php index 25b474e5c34..5b4d104e726 100644 --- a/src/CoreBundle/Entity/SessionRelUser.php +++ b/src/CoreBundle/Entity/SessionRelUser.php @@ -108,6 +108,9 @@ class SessionRelUser #[ORM\Column(name: 'collapsed', type: 'boolean', nullable: true, options: ['default' => null])] protected ?bool $collapsed = null; + #[ORM\Column(name: 'new_subscription_session_id', type: 'integer', nullable: true)] + protected ?int $newSubscriptionSessionId = null; + /** * @throws Exception */ @@ -226,4 +229,16 @@ public function setDuration(int $duration): self return $this; } + + public function getNewSubscriptionSessionId(): ?int + { + return $this->newSubscriptionSessionId; + } + + public function setNewSubscriptionSessionId(?int $newSubscriptionSessionId): self + { + $this->newSubscriptionSessionId = $newSubscriptionSessionId; + + return $this; + } } diff --git a/src/CoreBundle/Migrations/Schema/V200/Version20240928003000.php b/src/CoreBundle/Migrations/Schema/V200/Version20240928003000.php new file mode 100644 index 00000000000..4faec2e633d --- /dev/null +++ b/src/CoreBundle/Migrations/Schema/V200/Version20240928003000.php @@ -0,0 +1,139 @@ +connection->createSchemaManager(); + + // Add 'new_subscription_session_id' to the 'session_rel_user' table + if ($schemaManager->tablesExist('session_rel_user')) { + $sessionRelUserTable = $schemaManager->listTableColumns('session_rel_user'); + + if (!isset($sessionRelUserTable['new_subscription_session_id'])) { + $this->addSql("ALTER TABLE session_rel_user ADD new_subscription_session_id INT DEFAULT NULL"); + } + } + + // Add fields to the 'session' table + if ($schemaManager->tablesExist('session')) { + $sessionTable = $schemaManager->listTableColumns('session'); + + if (!isset($sessionTable['parent_id'])) { + $this->addSql("ALTER TABLE session ADD parent_id INT DEFAULT NULL"); + } + if (!isset($sessionTable['days_to_reinscription'])) { + $this->addSql("ALTER TABLE session ADD days_to_reinscription INT DEFAULT NULL"); + } + if (!isset($sessionTable['last_repetition'])) { + $this->addSql("ALTER TABLE session ADD last_repetition TINYINT(1) DEFAULT 0 NOT NULL"); + } + if (!isset($sessionTable['days_to_new_repetition'])) { + $this->addSql("ALTER TABLE session ADD days_to_new_repetition INT DEFAULT NULL"); + } + } + + // Add 'validity_in_days' to the 'session' table + if ($schemaManager->tablesExist('session')) { + $sessionTable = $schemaManager->listTableColumns('session'); + + if (!isset($sessionTable['validity_in_days'])) { + $this->addSql("ALTER TABLE session ADD validity_in_days INT DEFAULT NULL"); + } + } + + // Remove 'validity_in_days' from the 'c_lp' table + if ($schemaManager->tablesExist('c_lp')) { + $clpTable = $schemaManager->listTableColumns('c_lp'); + + if (isset($clpTable['validity_in_days'])) { + $this->addSql("ALTER TABLE c_lp DROP COLUMN validity_in_days"); + } + } + + // Insert new settings if not exist + $this->addSql(" + INSERT INTO settings (variable, subkey, type, category, selected_value, title, comment, scope, subkeytext, access_url, access_url_changeable, access_url_locked) + SELECT 'enable_auto_reinscription', NULL, NULL, 'session', '0', 'Enable Auto Reinscription', 'Allow users to be automatically reinscribed in new sessions.', '', NULL, 1, 1, 1 + WHERE NOT EXISTS ( + SELECT 1 FROM settings WHERE variable = 'enable_auto_reinscription' + ) + "); + + $this->addSql(" + INSERT INTO settings (variable, subkey, type, category, selected_value, title, comment, scope, subkeytext, access_url, access_url_changeable, access_url_locked) + SELECT 'enable_session_replication', NULL, NULL, 'session', '0', 'Enable Session Replication', 'Allow replication of session data across instances.', '', NULL, 1, 1, 1 + WHERE NOT EXISTS ( + SELECT 1 FROM settings WHERE variable = 'enable_session_replication' + ) + "); + } + + public function down(Schema $schema): void + { + $schemaManager = $this->connection->createSchemaManager(); + + // Revert 'new_subscription_session_id' in the 'session_rel_user' table + if ($schemaManager->tablesExist('session_rel_user')) { + $sessionRelUserTable = $schemaManager->listTableColumns('session_rel_user'); + + if (isset($sessionRelUserTable['new_subscription_session_id'])) { + $this->addSql("ALTER TABLE session_rel_user DROP COLUMN new_subscription_session_id"); + } + } + + // Revert changes in the 'session' table + if ($schemaManager->tablesExist('session')) { + $sessionTable = $schemaManager->listTableColumns('session'); + + if (isset($sessionTable['parent_id'])) { + $this->addSql("ALTER TABLE session DROP COLUMN parent_id"); + } + if (isset($sessionTable['days_to_reinscription'])) { + $this->addSql("ALTER TABLE session DROP COLUMN days_to_reinscription"); + } + if (isset($sessionTable['last_repetition'])) { + $this->addSql("ALTER TABLE session DROP COLUMN last_repetition"); + } + if (isset($sessionTable['days_to_new_repetition'])) { + $this->addSql("ALTER TABLE session DROP COLUMN days_to_new_repetition"); + } + } + + // Revert 'validity_in_days' in the 'session' table + if ($schemaManager->tablesExist('session')) { + $sessionTable = $schemaManager->listTableColumns('session'); + + if (isset($sessionTable['validity_in_days'])) { + $this->addSql("ALTER TABLE session DROP COLUMN validity_in_days"); + } + } + + // Re-add 'validity_in_days' to the 'c_lp' table + if ($schemaManager->tablesExist('c_lp')) { + $clpTable = $schemaManager->listTableColumns('c_lp'); + + if (!isset($clpTable['validity_in_days'])) { + $this->addSql("ALTER TABLE c_lp ADD validity_in_days INT DEFAULT NULL"); + } + } + + // Remove settings + $this->addSql("DELETE FROM settings WHERE variable = 'enable_auto_reinscription'"); + $this->addSql("DELETE FROM settings WHERE variable = 'enable_session_replication'"); + } +} diff --git a/src/CoreBundle/Repository/SessionRepository.php b/src/CoreBundle/Repository/SessionRepository.php index 4efc2b93acd..da0ff1de3f9 100644 --- a/src/CoreBundle/Repository/SessionRepository.php +++ b/src/CoreBundle/Repository/SessionRepository.php @@ -464,6 +464,113 @@ public function getSubscribedSessionsOfUserInUrl( return array_filter($sessions, $filterSessions); } + /** + * Finds a valid child session based on access dates and reinscription days. + * + * @param Session $session + * @return Session|null + */ + public function findValidChildSession(Session $session): ?Session + { + $childSessions = $this->findChildSessions($session); + $now = new \DateTime(); + + foreach ($childSessions as $childSession) { + $startDate = $childSession->getAccessStartDate(); + $endDate = $childSession->getAccessEndDate(); + $daysToReinscription = $childSession->getDaysToReinscription(); + + if (empty($daysToReinscription) || $daysToReinscription <= 0) { + continue; + } + + $adjustedEndDate = (clone $endDate)->modify('-' . $daysToReinscription . ' days'); + + if ($startDate <= $now && $adjustedEndDate >= $now) { + return $childSession; + } + } + return null; + } + + /** + * Finds a valid parent session based on access dates and reinscription days. + */ + public function findValidParentSession(Session $session): ?Session + { + $parentSession = $this->findParentSession($session); + if ($parentSession) { + $now = new \DateTime(); + $startDate = $parentSession->getAccessStartDate(); + $endDate = $parentSession->getAccessEndDate(); + $daysToReinscription = $parentSession->getDaysToReinscription(); + + // Return null if days to reinscription is not set + if ($daysToReinscription === null || $daysToReinscription === '') { + return null; + } + + // Adjust the end date by days to reinscription + $endDate = $endDate->modify('-' . $daysToReinscription . ' days'); + + // Check if the current date falls within the session's validity period + if ($startDate <= $now && $endDate >= $now) { + return $parentSession; + } + } + return null; + } + + /** + * Finds child sessions based on the parent session. + */ + public function findChildSessions(Session $parentSession): array + { + return $this->createQueryBuilder('s') + ->where('s.parentId = :parentId') + ->setParameter('parentId', $parentSession->getId()) + ->getQuery() + ->getResult(); + } + + /** + * Finds the parent session for a given session. + */ + public function findParentSession(Session $session): ?Session + { + if ($session->getParentId()) { + return $this->find($session->getParentId()); + } + + return null; + } + + /** + * Find sessions without child and ready for repetition. + * + * @return Session[] + */ + public function findSessionsWithoutChildAndReadyForRepetition() + { + $currentDate = new \DateTime(); + + $qb = $this->createQueryBuilder('s') + ->where('s.parentId IS NULL') + ->andWhere('s.daysToNewRepetition IS NOT NULL') + ->andWhere('s.lastRepetition = :false') + ->andWhere(':currentDate BETWEEN DATE_SUB(s.accessEndDate, s.daysToNewRepetition, \'DAY\') AND s.accessEndDate') + ->andWhere('NOT EXISTS ( + SELECT 1 + FROM Chamilo\CoreBundle\Entity\Session child + WHERE child.parentId = s.id + AND child.accessEndDate >= :currentDate + )') + ->setParameter('false', false) + ->setParameter('currentDate', $currentDate); + + return $qb->getQuery()->getResult(); + } + public function countUsersBySession(int $sessionId, int $relationType = Session::STUDENT): int { $qb = $this->createQueryBuilder('s'); diff --git a/src/CoreBundle/Settings/SessionSettingsSchema.php b/src/CoreBundle/Settings/SessionSettingsSchema.php index d5f2da7dbcb..44072bc8ca7 100644 --- a/src/CoreBundle/Settings/SessionSettingsSchema.php +++ b/src/CoreBundle/Settings/SessionSettingsSchema.php @@ -79,6 +79,8 @@ public function buildSettings(AbstractSettingsBuilder $builder): void 'session_creation_user_course_extra_field_relation_to_prefill' => '', 'session_creation_form_set_extra_fields_mandatory' => '', 'session_model_list_field_ordered_by_id' => 'false', + 'enable_auto_reinscription' => 'false', + 'enable_session_replication' => 'false', ] ) ; @@ -217,6 +219,8 @@ public function buildForm(FormBuilderInterface $builder): void ] ) ->add('session_model_list_field_ordered_by_id', YesNoType::class) + ->add('enable_auto_reinscription', YesNoType::class) + ->add('enable_session_replication', YesNoType::class) ; $this->updateFormFieldsFromSettingsInfo($builder); diff --git a/src/CourseBundle/Repository/CLpRepository.php b/src/CourseBundle/Repository/CLpRepository.php index affb60f5666..525ed73e748 100644 --- a/src/CourseBundle/Repository/CLpRepository.php +++ b/src/CourseBundle/Repository/CLpRepository.php @@ -117,4 +117,25 @@ protected function addNotDeletedQueryBuilder(?QueryBuilder $qb = null): QueryBui return $qb; } + + public function getLpSessionId(int $lpId): ?int + { + $lp = $this->find($lpId); + + if (!$lp) { + return null; + } + + $resourceNode = $lp->getResourceNode(); + if ($resourceNode) { + $link = $resourceNode->getResourceLinks()->first(); + + if ($link && $link->getSession()) { + + return (int) $link->getSession()->getId(); + } + } + + return null; + } }