Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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 public/main/admin/settings.lib.php
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,7 @@ function getStablePluginAllowList(): array
'ShowRegions',
'HelloWorld',
'ExtAuthChamiloLogoutButtonBehaviour',
'Resubscription',
];
}

Expand Down
50 changes: 50 additions & 0 deletions public/main/session/add_users_to_session.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
/* For licensing terms, see /license.txt */

use Chamilo\CoreBundle\Enums\ObjectIcon;
use Chamilo\CoreBundle\Event\AbstractEvent;
use Chamilo\CoreBundle\Event\Events;
use Chamilo\CoreBundle\Event\SessionResubscriptionEvent;
use Chamilo\CoreBundle\Framework\Container;

// resetting the course id
$cidReset = true;
Expand All @@ -29,6 +33,17 @@
$tbl_user = Database::get_main_table(TABLE_MAIN_USER);
$tbl_session_rel_user = Database::get_main_table(TABLE_MAIN_SESSION_USER);

$resubscriptionPlugin = null;
$resubscriptionPluginConfigPath = __DIR__.'/../../plugin/Resubscription/config.php';
if (is_file($resubscriptionPluginConfigPath)) {
require_once $resubscriptionPluginConfigPath;

if (class_exists('Resubscription', false)) {
$resubscriptionPlugin = Resubscription::create();
}
}


// setting the name of the tool
$tool_name = get_lang('Subscribe users to this session');
$add_type = 'unique';
Expand Down Expand Up @@ -64,6 +79,41 @@
$data = $form->getSubmitValues();
$users = $data['users'] ?? [];

$subscriptionAllowed = true;

foreach ($users as $userId) {
$userId = (int) $userId;

if (empty($userId)) {
continue;
}

try {
if ($resubscriptionPlugin instanceof Resubscription && $resubscriptionPlugin->isEnabled()) {
$resubscriptionPlugin->assertUserCanResubscribe($userId, $sessionId);
} else {
Container::getEventDispatcher()->dispatch(
new SessionResubscriptionEvent(
[
'session_id' => $sessionId,
'user_id' => $userId,
],
AbstractEvent::TYPE_PRE
),
Events::SESSION_RESUBSCRIPTION
);
}
} catch (Throwable $exception) {
$subscriptionAllowed = false;
Display::addFlash($exception->getMessage());
}
}

if (!$subscriptionAllowed) {
header('Location: '.api_get_self().'?id_session='.$sessionId);
exit;
}

SessionManager::subscribeUsersToSession(
$sessionId,
$users,
Expand Down
31 changes: 28 additions & 3 deletions public/plugin/Resubscription/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,29 @@
Resubscription
==============
# Resubscription for Chamilo 2

Limit session resubscriptions
This plugin limits repeated subscriptions to sessions when the target session contains a course that the learner already followed recently.

## Behavior

The plugin listens to the `SESSION_RESUBSCRIPTION` event before the subscription is completed.

When a learner tries to subscribe to a session, the plugin checks previous learner subscriptions and compares the courses of those sessions with the courses of the target session.

If a matching course is found inside the configured period, the subscription is blocked with an informational message.

## Settings

- `resubscription_limit = calendar_year`: blocks resubscription until the next calendar year.
- `resubscription_limit = natural_year`: blocks resubscription for one year after the previous access end date.

## Chamilo 2 notes

The plugin uses the existing Chamilo event and legacy plugin system. It does not create tables and does not modify courses, sessions or users.

The event subscriber is namespaced under `Chamilo\PluginBundle\Resubscription\EventSubscriber` so it can be registered by the Chamilo 2 plugin event subscriber compiler pass.

## Admin session subscription screen

Chamilo 2 also dispatches the same pre-subscription check from `public/main/session/add_users_to_session.php`.
This keeps the legacy administrator workflow aligned with the catalog workflow: adding a learner to a session is blocked when the target session contains a course already followed inside the configured period.

The `SessionResubscriptionEvent` accepts an optional `user_id` so admin-driven subscriptions can validate the selected learner instead of the current administrator account.
12 changes: 6 additions & 6 deletions public/plugin/Resubscription/config.php
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<?php

/* For licensing terms, see /license.txt */
/**
* Config the plugin.
*
* @author Imanol Losada Oriol <imanol.losada@beeznest.com>
*/
require_once api_get_path(SYS_PATH).'main/inc/global.inc.php';

require_once __DIR__.'/../../main/inc/global.inc.php';

if (!class_exists('Resubscription', false)) {
require_once __DIR__.'/src/Resubscription.php';
}
20 changes: 15 additions & 5 deletions public/plugin/Resubscription/index.php
Original file line number Diff line number Diff line change
@@ -1,9 +1,19 @@
<?php

/* For licensing terms, see /license.txt */
/**
* Config the plugin.
*
* @author Imanol Losada Oriol <imanol.losada@beeznest.com>
*/

require_once __DIR__.'/config.php';

api_protect_admin_script(true);

$plugin = Resubscription::create();

Display::display_header($plugin->get_title());

echo '<div class="max-w-3xl rounded-lg border border-gray-25 bg-white p-6 shadow-sm">';
echo '<h2 class="mb-2 text-xl font-semibold text-gray-90">'.htmlspecialchars($plugin->get_title(), ENT_QUOTES, api_get_system_encoding()).'</h2>';
echo '<p class="text-sm text-gray-50">'.htmlspecialchars($plugin->get_comment(), ENT_QUOTES, api_get_system_encoding()).'</p>';
echo '<p class="mt-4 text-sm text-gray-50">'.htmlspecialchars($plugin->get_lang('PluginUsageHelp'), ENT_QUOTES, api_get_system_encoding()).'</p>';
echo '</div>';

Display::display_footer();
6 changes: 1 addition & 5 deletions public/plugin/Resubscription/install.php
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
<?php

/* For licensing terms, see /license.txt */
/**
* Initialization install.
*
* @author Imanol Losada Oriol <imanol.losada@beeznest.com>
*/

require_once __DIR__.'/config.php';

Resubscription::create()->install();
16 changes: 8 additions & 8 deletions public/plugin/Resubscription/lang/en_US.php
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
<?php

/* For licensing terms, see /license.txt */
/**
* Strings to english L10n.
*
* @author Imanol Losada Oriol <imanol.losada@beeznest.com>
*/

$strings['plugin_title'] = 'Resubscription';
$strings['plugin_comment'] = 'This plugin limits session resubscription.';
$strings['plugin_comment'] = 'Limit session resubscriptions.';

$strings['resubscription_limit'] = 'Resubscription limit';
$strings['resubscription_limit_help'] = 'This limits how often a user can be resubscribed';
$strings['CanResubscribeFromX'] = 'Subscription available from %s';
$strings['resubscription_limit_help'] = 'Choose how long a learner must wait before subscribing again to another session that contains a course already followed recently.';
$strings['CalendarYear'] = 'Calendar year';
$strings['NaturalYear'] = 'Natural year';
$strings['CanResubscribeFromX'] = 'Subscription available from %s.';
$strings['UserCanResubscribeFromX'] = '%s: subscription available from %s.';
$strings['PluginUsageHelp'] = 'This plugin listens to the session resubscription event and blocks repeated subscription attempts when the target session contains a course already followed within the configured period.';
16 changes: 8 additions & 8 deletions public/plugin/Resubscription/lang/es.php
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
<?php

/* For licensing terms, see /license.txt */
/**
* Strings to spanish L10n.
*
* @author Imanol Losada Oriol <imanol.losada@beeznest.com>
*/

$strings['plugin_title'] = 'Reinscripción';
$strings['plugin_comment'] = 'Este plugin limita las reinscripiones a sesiones.';
$strings['plugin_comment'] = 'Limita la reinscripción a sesiones.';

$strings['resubscription_limit'] = 'Límite de reinscripción';
$strings['resubscription_limit_help'] = 'Esto limita cada cuánto puede reinscribirse un usuario';
$strings['CanResubscribeFromX'] = 'Inscripción posible a partir del %s';
$strings['resubscription_limit_help'] = 'Elige cuánto tiempo debe esperar un alumno antes de inscribirse nuevamente en otra sesión que contiene un curso seguido recientemente.';
$strings['CalendarYear'] = 'Año calendario';
$strings['NaturalYear'] = 'Año natural';
$strings['CanResubscribeFromX'] = 'Inscripción posible a partir del %s.';
$strings['UserCanResubscribeFromX'] = '%s: inscripción posible a partir del %s.';
$strings['PluginUsageHelp'] = 'Este plugin escucha el evento de reinscripción a sesiones y bloquea intentos repetidos cuando la sesión de destino contiene un curso ya seguido dentro del período configurado.';
6 changes: 1 addition & 5 deletions public/plugin/Resubscription/plugin.php
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
<?php

/* For licensing terms, see /license.txt */
/**
* Get the plugin info.
*
* @author Imanol Losada Oriol <imanol.losada@beeznest.com>
*/

require_once __DIR__.'/config.php';

$plugin_info = Resubscription::create()->get_info();
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,21 @@

declare(strict_types=1);

namespace Chamilo\PluginBundle\Resubscription\EventSubscriber;

use Chamilo\CoreBundle\Event\AbstractEvent;
use Chamilo\CoreBundle\Event\Events;
use Chamilo\CoreBundle\Event\SessionResubscriptionEvent;
use Exception;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

class ResubscriptionEventSubscriber implements EventSubscriberInterface
{
private Resubscription $plugin;
private \Resubscription $plugin;

public function __construct()
{
$this->plugin = Resubscription::create();
$this->plugin = \Resubscription::create();
}

public static function getSubscribedEvents(): array
Expand All @@ -26,93 +29,21 @@ public static function getSubscribedEvents(): array
}

/**
* @throws \Exception
* @throws Exception
*/
public function onResubscribe(SessionResubscriptionEvent $event): void
{
if (!$this->plugin->isEnabled()) {
if (AbstractEvent::TYPE_PRE !== $event->getType()) {
return;
}

if (AbstractEvent::TYPE_PRE === $event->getType()) {
$resubscriptionLimit = Resubscription::create()->get('resubscription_limit');

// Initialize variables as a calendar year by default
$limitDateFormat = 'Y-01-01';
$limitDate = gmdate($limitDateFormat);
$resubscriptionOffset = "1 year";

// No need to use a 'switch' with only two options so an 'if' is enough.
// However, this could change if the number of options increases
if ($resubscriptionLimit === 'natural_year') {
$limitDateFormat = 'Y-m-d';
$limitDate = gmdate($limitDateFormat);
$limitDate = gmdate($limitDateFormat, strtotime("$limitDate -$resubscriptionOffset"));
}

$join = " INNER JOIN ".Database::get_main_table(TABLE_MAIN_SESSION)." s ON s.id = su.session_id";

// User sessions and courses
$userSessions = Database::select(
'su.session_id, s.access_end_date',
Database::get_main_table(TABLE_MAIN_SESSION_USER).' su '.$join,
[
'where' => [
'su.user_id = ? AND s.access_end_date >= ?' => [
api_get_user_id(),
$limitDate,
],
],
'order' => 'access_end_date DESC',
]
);
$userSessionCourses = [];
foreach ($userSessions as $userSession) {
$userSessionCourseResult = Database::select(
'c_id',
Database::get_main_table(TABLE_MAIN_SESSION_COURSE),
[
'where' => [
'session_id = ?' => [
$userSession['session_id'],
],
],
]
);
foreach ($userSessionCourseResult as $userSessionCourse) {
if (!isset($userSessionCourses[$userSessionCourse['c_id']])) {
$userSessionCourses[$userSessionCourse['c_id']] = $userSession['access_end_date'];
}
}
}
$sessionId = $event->getSessionId();
$userId = $event->getUserId() ?? api_get_user_id();

// Current session and courses
$currentSessionCourseResult = Database::select(
'c_id',
Database::get_main_table(TABLE_MAIN_SESSION_COURSE),
[
'where' => [
'session_id = ?' => [
$event->getSessionId(),
],
],
]
);

// Check if current course code matches with one of the users
foreach ($currentSessionCourseResult as $currentSessionCourse) {
if (isset($userSessionCourses[$currentSessionCourse['c_id']])) {
$endDate = $userSessionCourses[$currentSessionCourse['c_id']];
$resubscriptionDate = gmdate($limitDateFormat, strtotime($endDate." +$resubscriptionOffset"));
$icon = Display::getMdiIcon(ObjectIcon::USER, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Learner'));
$canResubscribeFrom = sprintf(
get_plugin_lang('CanResubscribeFromX', 'resubscription'),
$resubscriptionDate
);

throw new Exception(Display::label($icon.' '.$canResubscribeFrom, "info"));
}
}
if (empty($sessionId) || empty($userId)) {
return;
}

$this->plugin->assertUserCanResubscribe((int) $userId, (int) $sessionId);
}
}
}
Loading
Loading