Merge branch 'MDL-61138-master-require-context-id' of git://github.com/ryanwyllie/moodle
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
)
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -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),
|
||||
];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user