Skip to content

Commit 3fd8617

Browse files
authored
Merge pull request #6287 from christianbeeznest/rna-22587-4
Course: Hide tools if initial test not completed with Positioning plugin - refs BT#22587
2 parents adf4702 + 1a682e8 commit 3fd8617

File tree

6 files changed

+227
-33
lines changed

6 files changed

+227
-33
lines changed

public/main/inc/lib/api.lib.php

Lines changed: 38 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
use Chamilo\CoreBundle\Framework\Container;
1414
use Chamilo\CoreBundle\ServiceHelper\MailHelper;
1515
use Chamilo\CoreBundle\ServiceHelper\PermissionServiceHelper;
16+
use Chamilo\CoreBundle\ServiceHelper\PluginServiceHelper;
1617
use Chamilo\CoreBundle\ServiceHelper\ThemeHelper;
1718
use Chamilo\CourseBundle\Entity\CGroup;
1819
use Chamilo\CourseBundle\Entity\CLp;
@@ -994,39 +995,46 @@ function api_protect_course_script($print_headers = false, $allow_session_admins
994995
return false;
995996
}
996997

997-
if ($is_visible && 'true' === api_get_plugin_setting('positioning', 'tool_enable')) {
998-
$plugin = Positioning::create();
999-
$block = $plugin->get('block_course_if_initial_exercise_not_attempted');
1000-
if ('true' === $block) {
1001-
$currentPath = $_SERVER['PHP_SELF'];
1002-
// Allowed only this course paths.
1003-
$paths = [
1004-
'/plugin/Positioning/start.php',
1005-
'/plugin/Positioning/start_student.php',
1006-
'/main/course_home/course_home.php',
1007-
'/main/exercise/overview.php',
998+
$pluginHelper = Container::$container->get(PluginServiceHelper::class);
999+
1000+
if ($pluginHelper->isPluginEnabled('Positioning')) {
1001+
$plugin = $pluginHelper->loadLegacyPlugin('Positioning');
1002+
1003+
if ($plugin && $plugin->get('block_course_if_initial_exercise_not_attempted') === 'true') {
1004+
$currentPath = $_SERVER['REQUEST_URI'];
1005+
1006+
$allowedPatterns = [
1007+
'#^/course/\d+/home#',
1008+
'#^/plugin/Positioning/#',
1009+
'#^/main/course_home/#',
1010+
'#^/main/exercise/#',
1011+
'#^/main/inc/ajax/exercise.ajax.php#',
10081012
];
10091013

1010-
if (!in_array($currentPath, $paths, true)) {
1011-
// Check if entering an exercise.
1012-
// @todo remove global $current_course_tool
1013-
/*global $current_course_tool;
1014-
if ('quiz' !== $current_course_tool) {
1015-
$initialData = $plugin->getInitialExercise($course_info['real_id'], $session_id);
1016-
if ($initialData && isset($initialData['exercise_id'])) {
1017-
$results = Event::getExerciseResultsByUser(
1018-
api_get_user_id(),
1019-
$initialData['exercise_id'],
1020-
$course_info['real_id'],
1021-
$session_id
1022-
);
1023-
if (empty($results)) {
1024-
api_not_allowed($print_headers);
1025-
1026-
return false;
1027-
}
1014+
$isWhitelisted = false;
1015+
foreach ($allowedPatterns as $pattern) {
1016+
if (preg_match($pattern, $currentPath)) {
1017+
$isWhitelisted = true;
1018+
break;
1019+
}
1020+
}
1021+
1022+
if (!$isWhitelisted) {
1023+
$initialData = $plugin->getInitialExercise($course_info['real_id'], $session_id);
1024+
1025+
if (!empty($initialData['exercise_id'])) {
1026+
$results = Event::getExerciseResultsByUser(
1027+
api_get_user_id(),
1028+
(int) $initialData['exercise_id'],
1029+
$course_info['real_id'],
1030+
$session_id
1031+
);
1032+
1033+
if (empty($results)) {
1034+
api_not_allowed($print_headers);
1035+
return false;
10281036
}
1029-
}*/
1037+
}
10301038
}
10311039
}
10321040
}

public/plugin/Positioning/src/Positioning.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,28 @@ public function blockFinalExercise($userId, $exerciseId, $courseId, $sessionId)
155155
return true;
156156
}
157157

158+
public function shouldBlockUser(int $userId, int $courseId, ?int $sessionId): bool
159+
{
160+
if ($this->get('block_course_if_initial_exercise_not_attempted') !== 'true') {
161+
return false;
162+
}
163+
164+
$initialData = $this->getInitialExercise($courseId, $sessionId);
165+
166+
if (empty($initialData['exercise_id'])) {
167+
return false;
168+
}
169+
170+
$results = \Event::getExerciseResultsByUser(
171+
$userId,
172+
(int) $initialData['exercise_id'],
173+
$courseId,
174+
$sessionId
175+
);
176+
177+
return empty($results);
178+
}
179+
158180
public function getInitialExercise($courseId, $sessionId)
159181
{
160182
return $this->getCourseExercise($courseId, $sessionId, true, false);

src/CoreBundle/Repository/AccessUrlRelPluginRepository.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,16 @@ public function __construct(ManagerRegistry $registry)
1919
{
2020
parent::__construct($registry, AccessUrlRelPlugin::class);
2121
}
22+
23+
public function findOneByPluginName(string $pluginTitle, int $accessUrlId): ?AccessUrlRelPlugin
24+
{
25+
return $this->createQueryBuilder('rel')
26+
->join('rel.plugin', 'p')
27+
->andWhere('p.title = :pluginTitle')
28+
->andWhere('rel.url = :accessUrlId')
29+
->setParameter('pluginTitle', $pluginTitle)
30+
->setParameter('accessUrlId', $accessUrlId)
31+
->getQuery()
32+
->getOneOrNullResult();
33+
}
2234
}

src/CoreBundle/Resources/config/services.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,3 +132,9 @@ services:
132132
arguments:
133133
$registry: '@doctrine'
134134
$em: '@doctrine.orm.entity_manager'
135+
136+
Chamilo\CoreBundle\ServiceHelper\PluginServiceHelper:
137+
arguments:
138+
$parameterBag: '@parameter_bag'
139+
$pluginRepo: '@Chamilo\CoreBundle\Repository\AccessUrlRelPluginRepository'
140+
$accessUrlHelper: '@Chamilo\CoreBundle\ServiceHelper\AccessUrlHelper'
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
<?php
2+
3+
/* For licensing terms, see /license.txt */
4+
5+
declare(strict_types=1);
6+
7+
namespace Chamilo\CoreBundle\ServiceHelper;
8+
9+
use Chamilo\CoreBundle\Repository\AccessUrlRelPluginRepository;
10+
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
11+
12+
class PluginServiceHelper
13+
{
14+
public function __construct(
15+
private ParameterBagInterface $parameterBag,
16+
private AccessUrlRelPluginRepository $pluginRepo,
17+
private AccessUrlHelper $accessUrlHelper,
18+
) {}
19+
20+
public function loadLegacyPlugin(string $pluginName): ?object
21+
{
22+
$projectDir = $this->parameterBag->get('kernel.project_dir');
23+
$pluginPath = $projectDir . '/public/plugin/' . $pluginName . '/src/' . $pluginName . '.php';
24+
$pluginClass = $pluginName;
25+
26+
if (!file_exists($pluginPath)) {
27+
return null;
28+
}
29+
30+
if (!class_exists($pluginClass)) {
31+
require_once $pluginPath;
32+
}
33+
34+
if (class_exists($pluginClass) && method_exists($pluginClass, 'create')) {
35+
return $pluginClass::create();
36+
}
37+
38+
return null;
39+
}
40+
41+
public function getPluginSetting(string $pluginName, string $settingKey): mixed
42+
{
43+
$plugin = $this->loadLegacyPlugin($pluginName);
44+
45+
if (!$plugin || !method_exists($plugin, 'get')) {
46+
return null;
47+
}
48+
49+
return $plugin->get($settingKey);
50+
}
51+
52+
public function isPluginEnabled(string $pluginName): bool
53+
{
54+
$accessUrl = $this->accessUrlHelper->getCurrent();
55+
if (null === $accessUrl) {
56+
return false;
57+
}
58+
59+
$pluginSetting = $this->pluginRepo->findOneByPluginName($pluginName, $accessUrl->getId());
60+
61+
return $pluginSetting && $pluginSetting->isActive();
62+
}
63+
64+
public function shouldBlockAccessByPositioning(?int $userId, int $courseId, ?int $sessionId): bool
65+
{
66+
if (!$this->isPluginEnabled('Positioning') || !$userId) {
67+
return false;
68+
}
69+
70+
$plugin = $this->loadLegacyPlugin('Positioning');
71+
72+
if (!$plugin || $plugin->get('block_course_if_initial_exercise_not_attempted') !== 'true') {
73+
return false;
74+
}
75+
76+
$initialData = $plugin->getInitialExercise($courseId, $sessionId);
77+
78+
if (empty($initialData['exercise_id'])) {
79+
return false;
80+
}
81+
82+
$results = \Event::getExerciseResultsByUser(
83+
$userId,
84+
(int) $initialData['exercise_id'],
85+
$courseId,
86+
$sessionId
87+
);
88+
89+
return empty($results);
90+
}
91+
}

src/CoreBundle/State/CToolStateProvider.php

Lines changed: 58 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@
1313
use Chamilo\CoreBundle\DataTransformer\CourseToolDataTranformer;
1414
use Chamilo\CoreBundle\Entity\ResourceLink;
1515
use Chamilo\CoreBundle\Entity\User;
16+
use Chamilo\CoreBundle\ServiceHelper\PluginServiceHelper;
1617
use Chamilo\CoreBundle\Settings\SettingsManager;
18+
use Chamilo\CoreBundle\Tool\AbstractPlugin;
1719
use Chamilo\CoreBundle\Tool\ToolChain;
1820
use Chamilo\CoreBundle\Traits\CourseFromRequestTrait;
1921
use Chamilo\CourseBundle\Entity\CTool;
@@ -37,6 +39,7 @@ public function __construct(
3739
private readonly Security $security,
3840
private readonly ToolChain $toolChain,
3941
protected RequestStack $requestStack,
42+
private readonly PluginServiceHelper $pluginServiceHelper,
4043
) {
4144
$this->transformer = new CourseToolDataTranformer(
4245
$this->requestStack,
@@ -51,23 +54,33 @@ public function provide(Operation $operation, array $uriVariables = [], array $c
5154
$result = $this->provider->provide($operation, $uriVariables, $context);
5255

5356
$request = $this->requestStack->getMainRequest();
54-
5557
$studentView = $request ? $request->getSession()->get('studentview') : 'studentview';
5658

5759
/** @var User|null $user */
5860
$user = $this->security->getUser();
5961

6062
$isAllowToEdit = $user && ($user->hasRole('ROLE_ADMIN') || $user->hasRole('ROLE_CURRENT_COURSE_TEACHER'));
61-
$isAllowToEditBack = $user && ($user->hasRole('ROLE_ADMIN') || $user->hasRole('ROLE_CURRENT_COURSE_TEACHER'));
62-
$isAllowToSessionEdit = $user && ($user->hasRole('ROLE_ADMIN') || $user->hasRole('ROLE_CURRENT_COURSE_TEACHER') || $user->hasRole('ROLE_CURRENT_COURSE_SESSION_TEACHER'));
63+
$isAllowToEditBack = $isAllowToEdit;
64+
$isAllowToSessionEdit = $user && (
65+
$user->hasRole('ROLE_ADMIN') ||
66+
$user->hasRole('ROLE_CURRENT_COURSE_TEACHER') ||
67+
$user->hasRole('ROLE_CURRENT_COURSE_SESSION_TEACHER')
68+
);
6369

6470
$allowVisibilityInSession = $this->settingsManager->getSetting('course.allow_edit_tool_visibility_in_session');
6571
$session = $this->getSession();
72+
$course = $this->getCourse();
73+
74+
[$restrictToPositioning, $allowedToolName] = $this->shouldRestrictToPositioningOnly($user, $course->getId(), $session?->getId());
6675

6776
$results = [];
6877

6978
/** @var CTool $cTool */
7079
foreach ($result as $cTool) {
80+
if ($restrictToPositioning && $cTool->getTool()->getTitle() !== $allowedToolName) {
81+
continue;
82+
}
83+
7184
$toolModel = $this->toolChain->getToolFromName(
7285
$cTool->getTool()->getTitle()
7386
);
@@ -107,4 +120,46 @@ public function provide(Operation $operation, array $uriVariables = [], array $c
107120

108121
return $results;
109122
}
123+
124+
private function shouldRestrictToPositioningOnly(?User $user, int $courseId, ?int $sessionId): array
125+
{
126+
if (!$user || !$user->hasRole('ROLE_STUDENT')) {
127+
return [false, null];
128+
}
129+
130+
$tool = $this->toolChain->getToolFromName('positioning');
131+
132+
if (!$tool instanceof AbstractPlugin) {
133+
return [false, null];
134+
}
135+
136+
if (!$this->pluginServiceHelper->isPluginEnabled('positioning')) {
137+
return [false, null];
138+
}
139+
140+
$pluginInstance = $this->pluginServiceHelper->loadLegacyPlugin('Positioning');
141+
142+
if (!$pluginInstance || 'true' !== $pluginInstance->get('block_course_if_initial_exercise_not_attempted')) {
143+
return [false, null];
144+
}
145+
146+
$initialData = $pluginInstance->getInitialExercise($courseId, $sessionId);
147+
148+
if (!isset($initialData['exercise_id'])) {
149+
return [false, null];
150+
}
151+
152+
$results = \Event::getExerciseResultsByUser(
153+
$user->getId(),
154+
(int) $initialData['exercise_id'],
155+
$courseId,
156+
$sessionId
157+
);
158+
159+
if (empty($results)) {
160+
return [true, 'positioning'];
161+
}
162+
163+
return [false, null];
164+
}
110165
}

0 commit comments

Comments
 (0)