diff --git a/main/inc/lib/api.lib.php b/main/inc/lib/api.lib.php index 5a7fa3a62f..d7607d86f7 100644 --- a/main/inc/lib/api.lib.php +++ b/main/inc/lib/api.lib.php @@ -10197,3 +10197,22 @@ function api_protect_webservices() exit; } } + +function api_filename_has_blacklisted_stream_wrapper(string $filename) { + if (strpos($filename, '://') > 0) { + $wrappers = stream_get_wrappers(); + $allowedWrappers = ['http', 'https', 'file']; + + foreach ($wrappers as $wrapper) { + if (in_array($wrapper, $allowedWrappers)) { + continue; + } + + if (stripos($filename, $wrapper . '://') === 0) { + return true; + } + } + } + + return false; +} diff --git a/main/inc/lib/pdf.lib.php b/main/inc/lib/pdf.lib.php index e06f5cc2be..cfee2e2876 100755 --- a/main/inc/lib/pdf.lib.php +++ b/main/inc/lib/pdf.lib.php @@ -973,11 +973,25 @@ class PDF $documentPath = $courseInfo ? $sysCoursePath.$courseInfo['path'].'/document/' : ''; + $notFoundImagePath = Display::return_icon( + 'closed-circle.png', + get_lang('FileNotFound'), + [], + ICON_SIZE_TINY, + false, + true + ); + /** @var \DOMElement $element */ foreach ($elements as $element) { $src = $element->getAttribute('src'); $src = trim($src); + if (api_filename_has_blacklisted_stream_wrapper($src)) { + $element->setAttribute('src', $notFoundImagePath); + continue; + } + if (strpos($src, $protocol) !== false) { continue; } diff --git a/main/install/configuration.dist.php b/main/install/configuration.dist.php index 7c8198035f..43a8013118 100755 --- a/main/install/configuration.dist.php +++ b/main/install/configuration.dist.php @@ -2109,6 +2109,10 @@ INSERT INTO `extra_field` (`extra_field_type`, `field_type`, `variable`, `displa 'INVITEE' => false ];*/ + +// Allow learnpath prerequisite on quiz to unblock if maximum attempt is reached +//$_configuration['lp_prerequisit_on_quiz_unblock_if_max_attempt_reached'] = false; + // Enables to hide user status when option is true visible only for admins from $_configuration['user_status_option_show_only_for_admin'] //$_configuration['user_status_option_only_for_admin_enabled'] = false; // The user status is hidden when is false, it requires $_configuration['user_status_option_only_for_admin_enabled'] = true diff --git a/main/lp/learnpathItem.class.php b/main/lp/learnpathItem.class.php index c459975279..12cea4ef72 100755 --- a/main/lp/learnpathItem.class.php +++ b/main/lp/learnpathItem.class.php @@ -2342,6 +2342,13 @@ class learnpathItem $status = $itemToCheck->get_status(true); $returnstatus = $status == $this->possible_status[2] || $status == $this->possible_status[3]; + // Allow learnpath prerequisite on quiz to unblock if maximum attempt is reached + if (true === api_get_configuration_value('lp_prerequisit_on_quiz_unblock_if_max_attempt_reached')) { + $isQuizMaxAttemptReached = $this->isQuizMaxAttemptReached($items[$refs_list[$prereqs_string]]->path, $user_id, $courseId, $this->lp_id, $prereqs_string); + if ($isQuizMaxAttemptReached) { + $returnstatus = true; + } + } if (!$returnstatus) { $explanation = sprintf( get_lang('ItemXBlocksThisElement'), @@ -2435,7 +2442,6 @@ class learnpathItem $minScore = $masteryScoreAsMin; } } - if (isset($minScore) && isset($minScore)) { // Taking min/max prerequisites values see BT#5776 if ($quiz['exe_result'] >= $minScore && @@ -2479,6 +2485,13 @@ class learnpathItem $refs_list ); } + // Allow learnpath prerequisite on quiz to unblock if maximum attempt is reached + if (true === api_get_configuration_value('lp_prerequisit_on_quiz_unblock_if_max_attempt_reached')) { + $isQuizMaxAttemptReached = $this->isQuizMaxAttemptReached($items[$refs_list[$prereqs_string]]->path, $user_id, $courseId, $this->lp_id, $prereqs_string); + if ($isQuizMaxAttemptReached) { + $returnstatus = true; + } + } } return $returnstatus; @@ -2636,6 +2649,35 @@ class learnpathItem return false; } + /** + * Check if max quiz attempt is reached. + * + * @param $exerciseId + * @param $userId + * @param $courseId + * @param $lpId + * @param $lpItemId + * + * @return bool + */ + public function isQuizMaxAttemptReached($exerciseId, $userId, $courseId, $lpId, $lpItemId) + { + $objExercise = new Exercise(); + $objExercise->read($exerciseId); + $nbAttempts = $objExercise->selectAttempts(); + $countAttempts = Tracking::count_student_exercise_attempts( + $userId, + $courseId, + $exerciseId, + $lpId, + $lpItemId, + api_get_session_id() + ); + + $isMaxAttemptReached = ($nbAttempts > 0 && $countAttempts >= $nbAttempts); + return $isMaxAttemptReached; + } + /** * Reinits all local values as the learnpath is restarted. * diff --git a/main/template/default/course_progress/pdf_general_thematic.tpl b/main/template/default/course_progress/pdf_general_thematic.tpl index 3dcde8bd69..6a8d28fe73 100644 --- a/main/template/default/course_progress/pdf_general_thematic.tpl +++ b/main/template/default/course_progress/pdf_general_thematic.tpl @@ -1,45 +1,40 @@ - - - - - - - - - - {% for item in data %} - - - - - - {% endfor %} - -
- {{ "Thematic"|get_lang }} - - {{ "ThematicPlan"|get_lang }} - - {{ "ThematicAdvance"|get_lang }} -
-

{{ item.title }}

-
- {{ item.content }} -
- {% for plan in item.thematic_plan %} -
-

{{ plan.title }}

-
- {{ plan.description }} - {% endfor %} -
- {% for advance in item.thematic_advance %} -
-

- {{ advance.duration }} {{ "MinHours" | get_lang }} -

- {{ advance.start_date | api_convert_and_format_date(2) }} -
- {{ advance.content }} - {% endfor %} -
\ No newline at end of file +
+
+ {{ "Thematic"|get_lang }} +
+
+ {{ "ThematicPlan"|get_lang }} +
+
+ {{ "ThematicAdvance"|get_lang }} +
+
+
+{% for item in data %} +
+
+

{{ item.title }}

+
+ {{ item.content }} +
+
+ {% for plan in item.thematic_plan %} +
+

{{ plan.title }}

+
+ {{ plan.description }} + {% endfor %} +
+
+ {% for advance in item.thematic_advance %} +
+

+ {{ advance.duration }} {{ "MinHours" | get_lang }} +

+ {{ advance.start_date | api_convert_and_format_date(2) }} +
+ {{ advance.content }} + {% endfor %} +
+
+{% endfor %} \ No newline at end of file