Merge branch 'master' into ofaj-20927-2

pull/4962/head
christianbeeznest 1 year ago committed by GitHub
commit 0c971060ff
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 7
      assets/js/legacy/vendor.js
  2. 68
      public/main/template/default/tracking/tracking_course_log.html.twig
  3. 17
      public/main/tracking/courseLog.php
  4. 2
      src/CoreBundle/Controller/CourseController.php
  5. 2
      src/CoreBundle/Entity/Session.php
  6. 1
      src/CoreBundle/Entity/SessionRelCourse.php
  7. 2
      src/CoreBundle/Entity/SessionRelUser.php
  8. 9
      src/CoreBundle/Entity/TrackEAttemptRecording.php
  9. 3
      src/CoreBundle/Entity/User.php
  10. 4
      src/CoreBundle/Migrations/Schema/V200/Version20231004220000.php
  11. 20
      src/CoreBundle/Migrations/Schema/V200/Version20231022124700.php
  12. 7
      src/CoreBundle/Security/Authorization/Voter/CourseVoter.php
  13. 12
      src/CoreBundle/State/CategorizedExerciseResultStateProvider.php
  14. 2
      src/CoreBundle/Traits/UserExtraFieldFilterTrait.php
  15. 1
      src/CourseBundle/Entity/CQuizAnswer.php

@ -19,7 +19,12 @@ global.hljs = hljs;
var textcomplete = require('textcomplete'); var textcomplete = require('textcomplete');
global.textcomplete = textcomplete; global.textcomplete = textcomplete;
require('chart.js'); //global.Chart = require("chart.js/dist/chart").Chart
import { Chart, registerables } from 'chart.js';
Chart.register(...registerables);
global.Chart = Chart;
require('./annotation.js'); require('./annotation.js');
require('../editor.js'); require('../editor.js');
import translateHtml from '../translatehtml.js'; import translateHtml from '../translatehtml.js';

@ -1,9 +1,9 @@
<!-- tracking course log --> <!-- tracking course log -->
<script> <script>
window.onload = function() { $(() => {
var scoreStudent = document.getElementById("chart-score").getContext('2d'); var scoreStudent = document.getElementById("chart-score").getContext('2d');
var lastAccess = document.getElementById("chart-access").getContext('2d'); var lastAccess = document.getElementById("chart-access").getContext('2d');
var jsonfile = {{ json_time_student }}; var jsonfile = {{ json_time_student|raw }};
var labels = []; var labels = [];
var times = []; var times = [];
@ -32,23 +32,23 @@ window.onload = function() {
display: false display: false
}, },
scales: { scales: {
xAxes:[{ x: {
position: "bottom", position: "bottom",
scaleLabel: { title: {
display: true, display: true,
labelString: '{{ "Students"|get_lang|e('js') }}', text: '{{ "Students"|get_lang|e('js') }}'
}, },
ticks: { ticks: { display: false },
display: false min: 0
} },
}], y: {
yAxes: [{ title: {
position: "left",
scaleLabel: {
display: true, display: true,
labelString: '{{ "Minutes"|get_lang|e('js') }}', text: '{{ "Minutes"|get_lang|e('js') }}'
} },
}] position: "left",
min: 0
}
} }
} }
}); });
@ -59,9 +59,9 @@ window.onload = function() {
labels: ["0-9%", "10-19%", "20-29%", "30-39%", "40-49%", "50-59%", "60-69%", "70-79%", "80-89%", "90-100%"], labels: ["0-9%", "10-19%", "20-29%", "30-39%", "40-49%", "50-59%", "60-69%", "70-79%", "80-89%", "90-100%"],
datasets: [{ datasets: [{
label: '{{ "NumberOfUsers"|get_lang|e('js') }}', label: '{{ "NumberOfUsers"|get_lang|e('js') }}',
data: {{ score_distribution }}, data: {{ score_distribution|raw }},
backgroundColor: {{ chart_colors }}, backgroundColor: {{ chart_colors|raw }},
borderColor: {{ chart_colors }}, borderColor: {{ chart_colors|raw }},
borderWidth: 1, borderWidth: 1,
fill: false fill: false
}] }]
@ -71,35 +71,33 @@ window.onload = function() {
display: false display: false
}, },
scales: { scales: {
yAxes: [{ y: {
position: "left", position: "left",
scaleLabel: { title: {
display: true, text: '{{ "NumberOfUsers"|get_lang|e('js') }}',
labelString: '{{ "NumberOfUsers"|get_lang|e('js') }}', display: true
}, },
ticks: { ticks: {
display: true, display: true,
min: 0, min: 0,
stepSize: 1 stepSize: 1
}
}],
xAxes:[{
position: "bottom",
scaleLabel: {
display: true,
labelString: "{{ 'PercentileScoresDistribution'|get_lang|e('js') }}",
}, },
gridLines: { min: 0
},
x: {
position: "bottom",
title: {
text: "{{ 'PercentileScoresDistribution'|get_lang|e('js') }}",
display: true display: true
}, },
ticks: { gridLines: { display: true },
display: false, ticks: { display: true, },
} min: 0
}], },
} }
} }
}); });
}; })
</script> </script>
<div class="tracking-course-summary"> <div class="tracking-course-summary">

@ -95,9 +95,12 @@ $csv_content = [];
$js = "<script> $js = "<script>
// hide column and display the button to unhide it // hide column and display the button to unhide it
function foldup(id) { function foldup(id) {
$('#reporting_table .data_table tr td:nth-child(' + (id + 1) + ')').toggleClass('hide'); var show = function () {\$(this).css('outline', '1px solid red')};
$('#reporting_table .data_table tr th:nth-child(' + (id + 1) + ')').toggleClass('hide'); var hide = function () {\$(this).css('outline', '1px solid gree')};
$('div#unhideButtons a:nth-child(' + (id + 1) + ')').toggleClass('hide');
$('#reporting_table .data_table tr td:nth-child(' + (id + 1) + ')').toggle();
$('#reporting_table .data_table tr th:nth-child(' + (id + 1) + ')').toggle();
$('div#unhideButtons a:nth-child(' + (id + 1) + ')').toggle();
} }
// add the red cross on top of each column // add the red cross on top of each column
@ -763,20 +766,20 @@ if ($nbStudents > 0) {
$html .= '<div id="unhideButtons" class="btn-toolbar">'; $html .= '<div id="unhideButtons" class="btn-toolbar">';
$index = 0; $index = 0;
$getLangDisplayColumn = get_lang('Show column'); $getLangDisplayColumn = get_lang('Show column');
/*foreach ($headers as $header) { foreach ($headers as $header) {
$html .= Display::toolbarButton( $html .= Display::toolbarButton(
$header, $header,
'#', '#',
'arrow-right', 'arrow-right',
'default', 'plain-outline',
[ [
'title' => htmlentities("$getLangDisplayColumn \"$header\"", ENT_QUOTES), 'title' => htmlentities("$getLangDisplayColumn \"$header\"", ENT_QUOTES),
'class' => 'hide', 'style' => 'display: none;',
'onclick' => "foldup($index); return false;", 'onclick' => "foldup($index); return false;",
] ]
); );
$index++; $index++;
}*/ }
$html .= '</div>'; $html .= '</div>';
$html .= '<div id="reporting_table">'; $html .= '<div id="reporting_table">';

@ -653,7 +653,7 @@ class CourseController extends ToolBaseController
} }
$responseData['c_tool'] = [ $responseData['c_tool'] = [
'iid' => $ctool->getIid(), 'iid' => $ctool->getIid(),
'name' => $ctool->getName() 'name' => $ctool->getName(),
]; ];
} }

@ -40,7 +40,7 @@ use Symfony\Component\Validator\Constraints as Assert;
new Put(security: "is_granted('ROLE_ADMIN')"), new Put(security: "is_granted('ROLE_ADMIN')"),
new GetCollection(security: "is_granted('ROLE_ADMIN')"), new GetCollection(security: "is_granted('ROLE_ADMIN')"),
new Post(security: "is_granted('ROLE_ADMIN')"), new Post(security: "is_granted('ROLE_ADMIN')"),
new Delete(security: "is_granted('DELETE', object)") new Delete(security: "is_granted('DELETE', object)"),
], ],
normalizationContext: ['groups' => ['session:read']], normalizationContext: ['groups' => ['session:read']],
denormalizationContext: ['groups' => ['session:write']], denormalizationContext: ['groups' => ['session:write']],

@ -68,7 +68,6 @@ class SessionRelCourse
$this->position = 0; $this->position = 0;
} }
public function getId(): ?int public function getId(): ?int
{ {
return $this->id; return $this->id;

@ -28,7 +28,7 @@ use Symfony\Component\Validator\Constraints as Assert;
#[ApiResource( #[ApiResource(
operations: [ operations: [
new Get(security: "is_granted('ROLE_ADMIN') or object.user == user"), new Get(security: "is_granted('ROLE_ADMIN') or object.user == user"),
new GetCollection(security: "is_granted('ROLE_USER')"), new GetCollection(security: "is_granted('ROLE_USER')"),
new Post(security: "is_granted('ROLE_ADMIN')"), new Post(security: "is_granted('ROLE_ADMIN')"),
], ],
normalizationContext: [ normalizationContext: [

@ -8,7 +8,6 @@ namespace Chamilo\CoreBundle\Entity;
use ApiPlatform\Metadata\ApiResource; use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Get; use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\GetCollection;
use DateTime; use DateTime;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation as Gedmo; use Gedmo\Mapping\Annotation as Gedmo;
@ -31,10 +30,6 @@ class TrackEAttemptRecording
#[ORM\GeneratedValue] #[ORM\GeneratedValue]
protected ?int $id = null; protected ?int $id = null;
#[ORM\ManyToOne(inversedBy: 'revisedAttempts')]
#[ORM\JoinColumn(name: 'exe_id', referencedColumnName: 'exe_id', nullable: false)]
private ?TrackEExercise $trackExercise = null;
#[ORM\Column(name: 'question_id', type: 'integer', nullable: false)] #[ORM\Column(name: 'question_id', type: 'integer', nullable: false)]
protected int $questionId; protected int $questionId;
@ -57,6 +52,10 @@ class TrackEAttemptRecording
#[ORM\Column(name: 'answer', type: 'text', nullable: true)] #[ORM\Column(name: 'answer', type: 'text', nullable: true)]
protected ?string $answer; protected ?string $answer;
#[ORM\ManyToOne(inversedBy: 'revisedAttempts')]
#[ORM\JoinColumn(name: 'exe_id', referencedColumnName: 'exe_id', nullable: false)]
private ?TrackEExercise $trackExercise = null;
public function __construct() public function __construct()
{ {
$this->teacherComment = ''; $this->teacherComment = '';

@ -2361,7 +2361,8 @@ class User implements UserInterface, EquatableInterface, ResourceInterface, Reso
{ {
return $this return $this
->getFriendsWithMeByRelationType($relationType) ->getFriendsWithMeByRelationType($relationType)
->exists(fn (int $index, UserRelUser $userRelUser) => $userRelUser->getUser() === $friend); ->exists(fn (int $index, UserRelUser $userRelUser) => $userRelUser->getUser() === $friend)
;
} }
/** /**

@ -6,11 +6,7 @@ declare(strict_types=1);
namespace Chamilo\CoreBundle\Migrations\Schema\V200; namespace Chamilo\CoreBundle\Migrations\Schema\V200;
use Chamilo\CoreBundle\Entity\User;
use Chamilo\CoreBundle\Migrations\AbstractMigrationChamilo; use Chamilo\CoreBundle\Migrations\AbstractMigrationChamilo;
use Chamilo\CourseBundle\Entity\CCalendarEvent;
use DateTime;
use DateTimeZone;
use Doctrine\DBAL\Schema\Schema; use Doctrine\DBAL\Schema\Schema;
use Doctrine\ORM\Exception\ORMException; use Doctrine\ORM\Exception\ORMException;
use Exception; use Exception;

@ -416,11 +416,9 @@ final class Version20231022124700 extends AbstractMigrationChamilo
} }
} }
} }
} }
} }
private function replaceURLParametersInContent($htmlContent, $connection) private function replaceURLParametersInContent($htmlContent, $connection)
{ {
$pattern = '/((https?:\/\/[^\/\s]*|)\/[^?\s]+?)\?cidReq=([a-zA-Z0-9_]+)(&(?:amp;)?id_session=([0-9]+))(&(?:amp;)?gidReq=([0-9]+))([^"\s]*)/i'; $pattern = '/((https?:\/\/[^\/\s]*|)\/[^?\s]+?)\?cidReq=([a-zA-Z0-9_]+)(&(?:amp;)?id_session=([0-9]+))(&(?:amp;)?gidReq=([0-9]+))([^"\s]*)/i';
@ -432,7 +430,7 @@ final class Version20231022124700 extends AbstractMigrationChamilo
$code = $matches[3]; // The 'code' is extracted from the captured URL. $code = $matches[3]; // The 'code' is extracted from the captured URL.
$courseId = null; $courseId = null;
$sql = "SELECT id FROM course WHERE code = :code ORDER BY id DESC LIMIT 1"; $sql = 'SELECT id FROM course WHERE code = :code ORDER BY id DESC LIMIT 1';
$stmt = $connection->executeQuery($sql, ['code' => $code]); $stmt = $connection->executeQuery($sql, ['code' => $code]);
$course = $stmt->fetch(); $course = $stmt->fetch();
@ -440,7 +438,7 @@ final class Version20231022124700 extends AbstractMigrationChamilo
$courseId = $course['id']; $courseId = $course['id'];
} }
if ($courseId === null) { if (null === $courseId) {
return $matches[0]; // Complete original URL. return $matches[0]; // Complete original URL.
} }
@ -448,24 +446,22 @@ final class Version20231022124700 extends AbstractMigrationChamilo
$remainingParams = ''; $remainingParams = '';
if (!empty($matches[8])) { if (!empty($matches[8])) {
$remainingParams = $matches[8]; $remainingParams = $matches[8];
if ($remainingParams[0] !== '&') { if ('&' !== $remainingParams[0]) {
$remainingParams = '&' . $remainingParams; $remainingParams = '&'.$remainingParams;
} }
} }
// Reconstructing the URL with the new courseId and adjusted parameters. // Reconstructing the URL with the new courseId and adjusted parameters.
$newUrl = $matches[1] . '?cid=' . $courseId . '&sid=' . $matches[5] . '&gid=' . $matches[7] . $remainingParams; return $matches[1].'?cid='.$courseId.'&sid='.$matches[5].'&gid='.$matches[7].$remainingParams;
// Return the new URL.
return $newUrl; // Return the new URL.
}, },
$htmlContent $htmlContent
); );
if (preg_last_error() !== PREG_NO_ERROR) { if (PREG_NO_ERROR !== preg_last_error()) {
error_log('Error encountered in preg_replace_callback: ' . preg_last_error()); error_log('Error encountered in preg_replace_callback: '.preg_last_error());
} }
return $newContent; return $newContent;
} }
} }

@ -6,12 +6,11 @@ declare(strict_types=1);
namespace Chamilo\CoreBundle\Security\Authorization\Voter; namespace Chamilo\CoreBundle\Security\Authorization\Voter;
use Doctrine\ORM\EntityManagerInterface;
use Chamilo\CoreBundle\Entity\Session;
use Chamilo\CoreBundle\Entity\Course; use Chamilo\CoreBundle\Entity\Course;
use Chamilo\CoreBundle\Entity\Session;
use Chamilo\CoreBundle\Entity\User; use Chamilo\CoreBundle\Entity\User;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\Voter\Voter; use Symfony\Component\Security\Core\Authorization\Voter\Voter;
use Symfony\Component\Security\Core\Security; use Symfony\Component\Security\Core\Security;
@ -151,8 +150,8 @@ class CourseVoter extends Voter
return true; return true;
} }
} }
break; break;
case self::EDIT: case self::EDIT:
case self::DELETE: case self::DELETE:

@ -11,6 +11,7 @@ use ApiPlatform\State\ProviderInterface;
use Chamilo\CoreBundle\ApiResource\CategorizedExerciseResult; use Chamilo\CoreBundle\ApiResource\CategorizedExerciseResult;
use Chamilo\CoreBundle\Entity\TrackEExercise; use Chamilo\CoreBundle\Entity\TrackEExercise;
use Chamilo\CoreBundle\Security\Authorization\Voter\TrackEExerciseVoter; use Chamilo\CoreBundle\Security\Authorization\Voter\TrackEExerciseVoter;
use function count;
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityManagerInterface;
use Event; use Event;
use Exception; use Exception;
@ -22,9 +23,6 @@ use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface; use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
use TestCategory; use TestCategory;
use function count;
use function in_array;
class CategorizedExerciseResultStateProvider implements ProviderInterface class CategorizedExerciseResultStateProvider implements ProviderInterface
{ {
public function __construct( public function __construct(
@ -98,7 +96,7 @@ class CategorizedExerciseResultStateProvider implements ProviderInterface
$show_results = false; $show_results = false;
$show_only_score = false; $show_only_score = false;
if (in_array( if (\in_array(
$objExercise->results_disabled, $objExercise->results_disabled,
[ [
RESULT_DISABLE_SHOW_ONLY_IN_CORRECT_ANSWER, RESULT_DISABLE_SHOW_ONLY_IN_CORRECT_ANSWER,
@ -110,7 +108,7 @@ class CategorizedExerciseResultStateProvider implements ProviderInterface
$show_results = true; $show_results = true;
} }
if (in_array( if (\in_array(
$objExercise->results_disabled, $objExercise->results_disabled,
[ [
RESULT_DISABLE_SHOW_SCORE_ONLY, RESULT_DISABLE_SHOW_SCORE_ONLY,
@ -133,7 +131,7 @@ class CategorizedExerciseResultStateProvider implements ProviderInterface
$showTotalScoreAndUserChoicesInLastAttempt = true; $showTotalScoreAndUserChoicesInLastAttempt = true;
$showTotalScore = true; $showTotalScore = true;
if (in_array( if (\in_array(
$objExercise->results_disabled, $objExercise->results_disabled,
[ [
RESULT_DISABLE_SHOW_SCORE_ATTEMPT_SHOW_ANSWERS_LAST_ATTEMPT, RESULT_DISABLE_SHOW_SCORE_ATTEMPT_SHOW_ANSWERS_LAST_ATTEMPT,
@ -158,7 +156,7 @@ class CategorizedExerciseResultStateProvider implements ProviderInterface
); );
if ($attempts) { if ($attempts) {
$numberAttempts = count($attempts); $numberAttempts = \count($attempts);
} }
$showTotalScore = false; $showTotalScore = false;

@ -11,7 +11,7 @@ use Chamilo\CoreBundle\Filter\UserExtraFieldFilter;
/** /**
* Properties to use as filters. To search by a user extra field. * Properties to use as filters. To search by a user extra field.
* The API Resource must have a relation with User * The API Resource must have a relation with User.
*/ */
trait UserExtraFieldFilterTrait trait UserExtraFieldFilterTrait
{ {

@ -110,7 +110,6 @@ class CQuizAnswer
return $this; return $this;
} }
public function getPonderation(): float public function getPonderation(): float
{ {
return $this->ponderation; return $this->ponderation;

Loading…
Cancel
Save