From de841a4ec327645ba0b4c7db4b26e032731b63d4 Mon Sep 17 00:00:00 2001 From: Angel Fernando Quiroz Campos Date: Wed, 4 May 2022 22:50:39 -0500 Subject: [PATCH] Exercise: Add exercise_embeddable_extra_types configuration setting - refs BT#19898 --- main/inc/lib/exercise.lib.php | 84 +++++++++++-------- main/install/configuration.dist.php | 25 ++++++ .../CourseBundle/Entity/CQuizQuestion.php | 2 +- .../Repository/CQuizQuestionRepository.php | 53 ++++++++++++ 4 files changed, 130 insertions(+), 34 deletions(-) create mode 100644 src/Chamilo/CourseBundle/Entity/Repository/CQuizQuestionRepository.php diff --git a/main/inc/lib/exercise.lib.php b/main/inc/lib/exercise.lib.php index e24e01cf89..6d35b921d3 100644 --- a/main/inc/lib/exercise.lib.php +++ b/main/inc/lib/exercise.lib.php @@ -4,6 +4,7 @@ use Chamilo\CoreBundle\Component\Utils\ChamiloApi; use Chamilo\CoreBundle\Entity\TrackEExercises; +use Chamilo\CourseBundle\Entity\CQuizQuestion; use ChamiloSession as Session; /** @@ -5999,20 +6000,57 @@ EOT; return ($answeredQuestionsCount + $numberOfQuestions) > $questionsLimitPerDay; } + /** + * By default, allowed types are unique-answer (and image) or multiple-answer questions. + * Types can be extended by the configuration setting "exercise_embeddable_extra_types". + */ + public static function getEmbeddableTypes(): array + { + $allowedTypes = [ + UNIQUE_ANSWER, + MULTIPLE_ANSWER, + FILL_IN_BLANKS, + MATCHING, + FREE_ANSWER, + MULTIPLE_ANSWER_COMBINATION, + UNIQUE_ANSWER_NO_OPTION, + MULTIPLE_ANSWER_TRUE_FALSE, + MULTIPLE_ANSWER_COMBINATION_TRUE_FALSE, + ORAL_EXPRESSION, + GLOBAL_MULTIPLE_ANSWER, + CALCULATED_ANSWER, + UNIQUE_ANSWER_IMAGE, + READING_COMPREHENSION, + MULTIPLE_ANSWER_TRUE_FALSE_DEGREE_CERTAINTY, + UPLOAD_ANSWER, + ]; + $defaultTypes = [UNIQUE_ANSWER, MULTIPLE_ANSWER, UNIQUE_ANSWER_IMAGE]; + $types = $defaultTypes; + + $extraTypes = api_get_configuration_value('exercise_embeddable_extra_types'); + + if (false !== $extraTypes && !empty($extraTypes['types'])) { + $types = array_merge($defaultTypes, $extraTypes['types']); + } + + return array_filter( + array_unique($types), + function ($type) use ($allowedTypes) { + return in_array($type, $allowedTypes); + } + ); + } + /** * Check if an exercise complies with the requirements to be embedded in the mobile app or a video. - * By making sure it is set on one question per page and it only contains unique-answer or multiple-answer questions - * or unique-answer image. And that the exam does not have immediate feedback. + * By making sure it is set on one question per page, and that the exam does not have immediate feedback, + * and it only contains allowed types. * - * @param array $exercise Exercise info - * - * @throws \Doctrine\ORM\Query\QueryException - * - * @return bool + * @see Exercise::getEmbeddableTypes() */ - public static function isQuizEmbeddable(array $exercise) + public static function isQuizEmbeddable(array $exercise): bool { - $em = Database::getManager(); + $exercise['iid'] = isset($exercise['iid']) ? (int) $exercise['iid'] : 0; if (ONE_PER_PAGE != $exercise['type'] || in_array($exercise['feedback_type'], [EXERCISE_FEEDBACK_TYPE_DIRECT, EXERCISE_FEEDBACK_TYPE_POPUP]) @@ -6020,32 +6058,12 @@ EOT; return false; } - $countAll = $em - ->createQuery('SELECT COUNT(qq) - FROM ChamiloCourseBundle:CQuizQuestion qq - INNER JOIN ChamiloCourseBundle:CQuizRelQuestion qrq - WITH qq.iid = qrq.questionId - WHERE qrq.exerciceId = :id' - ) - ->setParameter('id', $exercise['iid']) - ->getSingleScalarResult(); + $questionRepository = Database::getManager()->getRepository(CQuizQuestion::class); - $countOfAllowed = $em - ->createQuery('SELECT COUNT(qq) - FROM ChamiloCourseBundle:CQuizQuestion qq - INNER JOIN ChamiloCourseBundle:CQuizRelQuestion qrq - WITH qq.iid = qrq.questionId - WHERE qrq.exerciceId = :id AND qq.type IN (:types)' - ) - ->setParameters( - [ - 'id' => $exercise['iid'], - 'types' => [UNIQUE_ANSWER, MULTIPLE_ANSWER, UNIQUE_ANSWER_IMAGE], - ] - ) - ->getSingleScalarResult(); + $countAll = $questionRepository->countQuestionsInExercise($exercise['iid']); + $countAllowed = $questionRepository->countEmbeddableQuestionsInExercise($exercise['iid']); - return $countAll === $countOfAllowed; + return $countAll === $countAllowed; } /** diff --git a/main/install/configuration.dist.php b/main/install/configuration.dist.php index 0ffdf1a913..aee6efacdc 100755 --- a/main/install/configuration.dist.php +++ b/main/install/configuration.dist.php @@ -728,6 +728,31 @@ $_configuration['send_all_emails_to'] = [ //$_configuration['hide_user_info_in_quiz_result'] = false; // Show the username field in exercise results report //$_configuration['exercise_attempts_report_show_username'] = false; +// Allow extends allowed question types for embeddable exercises. +/* By default, only the following question types are allowed: 1, 2, 17 +Add this types to allow them in embeddable exercises: + 1 = Multiple choice + 2 = Multiple answers + 3 = Fill blanks or form + 4 = Matching + 5 = Open question + 9 = Exact Selection +10 = Unique answer with unknown +11 = Multiple answer true/false/don't know +12 = Combination true/false/don't know +13 = Oral expression +14 = Global multiple answer +16 = Calculated question +17 = Unique answer image +21 = Reading comprehension +22 = Multiple answer true/false/degree of certainty +23 = Upload answer + */ +/* +$_configuration['exercise_embeddable_extra_types'] = [ + 'types' => [], +]; +*/ // Score model // Allow to convert a score into a text/color label diff --git a/src/Chamilo/CourseBundle/Entity/CQuizQuestion.php b/src/Chamilo/CourseBundle/Entity/CQuizQuestion.php index 36e7f2a312..34d6fdadc8 100644 --- a/src/Chamilo/CourseBundle/Entity/CQuizQuestion.php +++ b/src/Chamilo/CourseBundle/Entity/CQuizQuestion.php @@ -15,7 +15,7 @@ use Doctrine\ORM\Mapping as ORM; * @ORM\Index(name="position", columns={"position"}) * } * ) - * @ORM\Entity() + * @ORM\Entity(repositoryClass="Chamilo\CourseBundle\Entity\Repository\CQuizQuestionRepository") */ class CQuizQuestion { diff --git a/src/Chamilo/CourseBundle/Entity/Repository/CQuizQuestionRepository.php b/src/Chamilo/CourseBundle/Entity/Repository/CQuizQuestionRepository.php new file mode 100644 index 0000000000..ebeb8ff4a4 --- /dev/null +++ b/src/Chamilo/CourseBundle/Entity/Repository/CQuizQuestionRepository.php @@ -0,0 +1,53 @@ +createQueryBuilder('qq') + ->select('COUNT(qq)') + ->innerJoin(CQuizRelQuestion::class, 'qrq', Join::WITH, 'qq.iid = qrq.questionId') + ->where('qrq.exerciceId = :id') + ->setParameters(['id' => $exerciseId]) + ->getQuery() + ; + + return (int) $query->getSingleScalarResult(); + } + + /** + * @throws NonUniqueResultException + * @throws NoResultException + */ + public function countEmbeddableQuestionsInExercise(int $exerciseId): int + { + $query = $this->createQueryBuilder('qq') + ->select('COUNT(qq)') + ->innerJoin(CQuizRelQuestion::class, 'qrq', Join::WITH, 'qq.iid = qrq.questionId') + ->where('qrq.exerciceId = :id AND qq.type IN (:types)') + ->setParameters( + [ + 'id' => $exerciseId, + 'types' => \ExerciseLib::getEmbeddableTypes(), + ] + ) + ->getQuery() + ; + + return (int) $query->getSingleScalarResult(); + } +}