Merge branch 'MDL-61138-master-require-context-id' of git://github.com/ryanwyllie/moodle

This commit is contained in:
David Monllao
2018-04-19 11:18:47 +02:00
60 changed files with 4176 additions and 407 deletions
@@ -210,6 +210,29 @@ class random_question_loader {
}
}
/**
* Get the list of available question ids for the given criteria.
*
* @param int $categoryid The id of a category in the question bank.
* @param bool $includesubcategories Whether to pick a question from exactly
* that category, or that category and subcategories.
* @param array $tagids An array of tag ids. If an array is provided, then
* only the questions that are tagged with ALL the provided tagids will be loaded.
* @return int[] The list of question ids
*/
protected function get_question_ids($categoryid, $includesubcategories, $tagids = []) {
$this->ensure_questions_for_category_loaded($categoryid, $includesubcategories, $tagids);
$categorykey = $this->get_category_key($categoryid, $includesubcategories, $tagids);
$cachedvalues = $this->availablequestionscache[$categorykey];
$questionids = [];
foreach ($cachedvalues as $usecount => $ids) {
$questionids = array_merge($questionids, array_keys($ids));
}
return $questionids;
}
/**
* Check whether a given question is available in a given category. If so, mark it used.
* If an optional list of tag ids are provided, then the question must be tagged with
@@ -235,4 +258,65 @@ class random_question_loader {
return false;
}
/**
* Get the list of available questions for the given criteria.
*
* @param int $categoryid The id of a category in the question bank.
* @param bool $includesubcategories Whether to pick a question from exactly
* that category, or that category and subcategories.
* @param array $tagids An array of tag ids. If an array is provided, then
* only the questions that are tagged with ALL the provided tagids will be loaded.
* @param int $limit Maximum number of results to return.
* @param int $offset Number of items to skip from the begging of the result set.
* @param string[] $fields The fields to return for each question.
* @return \stdClass[] The list of question records
*/
public function get_questions(
$categoryid,
$includesubcategories,
$tagids = [],
$limit = 100,
$offset = 0,
$fields = []
) {
global $DB;
$questionids = $this->get_question_ids($categoryid, $includesubcategories, $tagids);
if (empty($questionids)) {
return [];
}
if (empty($fields)) {
// Return all fields.
$fieldsstring = '*';
} else {
$fieldsstring = implode(',', $fields);
}
return $DB->get_records_list(
'question',
'id',
$questionids,
'id',
$fieldsstring,
$offset,
$limit
);
}
/**
* Count the number of available questions for the given criteria.
*
* @param int $categoryid The id of a category in the question bank.
* @param bool $includesubcategories Whether to pick a question from exactly
* that category, or that category and subcategories.
* @param array $tagids An array of tag ids. If an array is provided, then
* only the questions that are tagged with ALL the provided tagids will be loaded.
* @return int The number of questions matching the criteria.
*/
public function count_questions($categoryid, $includesubcategories, $tagids = []) {
$questionids = $this->get_question_ids($categoryid, $includesubcategories, $tagids);
return count($questionids);
}
}
+103
View File
@@ -28,6 +28,7 @@ defined('MOODLE_INTERNAL') || die();
require_once("$CFG->libdir/externallib.php");
require_once($CFG->dirroot . '/question/engine/lib.php');
require_once($CFG->dirroot . '/question/engine/datalib.php');
require_once($CFG->libdir . '/questionlib.php');
/**
@@ -211,4 +212,106 @@ class core_question_external extends external_api {
'status' => new external_value(PARAM_BOOL, 'status: true if success')
]);
}
/**
* Returns description of method parameters.
*
* @return external_function_parameters.
*/
public static function get_random_question_summaries_parameters() {
return new external_function_parameters([
'categoryid' => new external_value(PARAM_INT, 'Category id to find random questions'),
'includesubcategories' => new external_value(PARAM_BOOL, 'Include the subcategories in the search'),
'tagids' => new external_multiple_structure(
new external_value(PARAM_INT, 'Tag id')
),
'contextid' => new external_value(PARAM_INT,
'Context id that the questions will be rendered in (used for exporting)'),
'limit' => new external_value(PARAM_INT, 'Maximum number of results to return',
VALUE_DEFAULT, 0),
'offset' => new external_value(PARAM_INT, 'Number of items to skip from the begging of the result set',
VALUE_DEFAULT, 0)
]);
}
/**
* Gets the list of random questions for the given criteria. The questions
* will be exported in a summaries format and won't include all of the
* question data.
*
* @param int $categoryid Category id to find random questions
* @param bool $includesubcategories Include the subcategories in the search
* @param int[] $tagids Only include questions with these tags
* @param int $contextid The context id where the questions will be rendered
* @param int $limit Maximum number of results to return
* @param int $offset Number of items to skip from the beginning of the result set.
* @return array The list of questions and total question count.
*/
public static function get_random_question_summaries(
$categoryid,
$includesubcategories,
$tagids,
$contextid,
$limit = 0,
$offset = 0
) {
global $DB, $PAGE;
// Parameter validation.
$params = self::validate_parameters(
self::get_random_question_summaries_parameters(),
[
'categoryid' => $categoryid,
'includesubcategories' => $includesubcategories,
'tagids' => $tagids,
'contextid' => $contextid,
'limit' => $limit,
'offset' => $offset
]
);
$categoryid = $params['categoryid'];
$includesubcategories = $params['includesubcategories'];
$tagids = $params['tagids'];
$contextid = $params['contextid'];
$limit = $params['limit'];
$offset = $params['offset'];
$context = \context::instance_by_id($contextid);
self::validate_context($context);
$categorycontextid = $DB->get_field('question_categories', 'contextid', ['id' => $categoryid], MUST_EXIST);
$categorycontext = \context::instance_by_id($categorycontextid);
$editcontexts = new \question_edit_contexts($categorycontext);
// The user must be able to view all questions in the category that they are requesting.
$editcontexts->require_cap('moodle/question:viewall');
$loader = new \core_question\bank\random_question_loader(new qubaid_list([]));
// Only load the properties we require from the DB.
$properties = \core_question\external\question_summary_exporter::get_mandatory_properties();
$questions = $loader->get_questions($categoryid, $includesubcategories, $tagids, $limit, $offset, $properties);
$totalcount = $loader->count_questions($categoryid, $includesubcategories, $tagids);
$renderer = $PAGE->get_renderer('core');
$formattedquestions = array_map(function($question) use ($context, $renderer) {
$exporter = new \core_question\external\question_summary_exporter($question, ['context' => $context]);
return $exporter->export($renderer);
}, $questions);
return [
'totalcount' => $totalcount,
'questions' => $formattedquestions
];
}
/**
* Returns description of method result value.
*/
public static function get_random_question_summaries_returns() {
return new external_single_structure([
'totalcount' => new external_value(PARAM_INT, 'total number of questions in result set'),
'questions' => new external_multiple_structure(
\core_question\external\question_summary_exporter::get_read_structure()
)
]);
}
}
+74
View File
@@ -0,0 +1,74 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Class for exporting a question icon from an stdClass.
*
* @package core_question
* @copyright 2018 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_question\external;
defined('MOODLE_INTERNAL') || die();
require_once($CFG->dirroot . '/question/engine/bank.php');
/**
* Class for exporting a question from an stdClass.
*
* @copyright 2018 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class question_icon_exporter extends \core\external\exporter {
/**
* Constructor.
*
* @param \stdClass $question
* @param array $related The related data.
*/
public function __construct(\stdClass $question, $related = []) {
$qtype = \question_bank::get_qtype($question->qtype, false);
parent::__construct((object) [
'key' => 'icon',
'component' => $qtype->plugin_name(),
'alttext' => $qtype->local_name()
], $related);
}
/**
* Set the moodle context as a required related object.
*
* @return array Required related objects.
*/
protected static function define_related() {
return ['context' => '\\context'];
}
/**
* Return the list of properties.
*
* @return array
*/
protected static function define_properties() {
return [
'key' => ['type' => PARAM_TEXT],
'component' => ['type' => PARAM_COMPONENT],
'alttext' => ['type' => PARAM_TEXT],
];
}
}
+128
View File
@@ -0,0 +1,128 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Class for exporting a question summary from an stdClass.
*
* @package core_question
* @copyright 2018 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_question\external;
defined('MOODLE_INTERNAL') || die();
use \renderer_base;
/**
* Class for exporting a question summary from an stdClass.
*
* @copyright 2018 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class question_summary_exporter extends \core\external\exporter {
/**
* @var \stdClass $question
*/
protected $question;
/**
* Constructor.
*
* @param \stdClass $question
* @param array $related The related data.
*/
public function __construct(\stdClass $question, $related = []) {
$this->question = $question;
return parent::__construct($question, $related);
}
/**
* Set the moodle context as a required related object.
*
* @return array Required related objects.
*/
protected static function define_related() {
return ['context' => '\\context'];
}
/**
* The list of mandatory properties required on the question object to
* export.
*
* @return string[] List of properties.
*/
public static function get_mandatory_properties() {
$properties = self::define_properties();
$mandatoryproperties = array_filter($properties, function($property) {
return empty($property['optional']);
});
return array_keys($mandatoryproperties);
}
/**
* The list of static properties returned.
*
* @return array List of properties.
*/
public static function define_properties() {
return [
'id' => [
'type' => PARAM_INT,
],
'category' => [
'type' => PARAM_INT,
],
'parent' => [
'type' => PARAM_INT,
],
'name' => [
'type' => PARAM_TEXT,
],
'qtype' => [
'type' => PARAM_COMPONENT,
]
];
}
/**
* Define the list of calculated properties.
*
* @return array The list of properties.
*/
protected static function define_other_properties() {
return [
'icon' => [
'type' => question_icon_exporter::read_properties_definition(),
]
];
}
/**
* Calculate the values for the properties defined in the define_other_properties
* function.
*
* @param renderer_base $output A renderer.
* @return array The list of properties.
*/
protected function get_other_values(\renderer_base $output) {
$iconexporter = new question_icon_exporter($this->question, $this->related);
return [
'icon' => $iconexporter->export($output),
];
}
}