Merge branch 'master_MDL-72074-usage' of https://github.com/catalyst/moodle-MDL-70329

This commit is contained in:
Ilya Tregubov 2021-11-04 16:12:35 +02:00
commit 73863ef3d9
19 changed files with 933 additions and 0 deletions

View File

@ -1960,6 +1960,7 @@ class core_plugin_manager {
'previewquestion',
'statistics',
'tagquestion',
'usage',
'viewcreator',
'viewquestionname',
'viewquestiontext',

View File

@ -0,0 +1,2 @@
function _typeof(a){"@babel/helpers - typeof";if("function"==typeof Symbol&&"symbol"==typeof Symbol.iterator){_typeof=function(a){return typeof a}}else{_typeof=function(a){return a&&"function"==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?"symbol":typeof a}}return _typeof(a)}define ("qbank_usage/usage",["exports","core/fragment","core/str","core/modal_factory","core/notification"],function(a,b,c,d,e){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.init=void 0;b=h(b);c=g(c);d=h(d);e=h(e);function f(){if("function"!=typeof WeakMap)return null;var a=new WeakMap;f=function(){return a};return a}function g(a){if(a&&a.__esModule){return a}if(null===a||"object"!==_typeof(a)&&"function"!=typeof a){return{default:a}}var b=f();if(b&&b.has(a)){return b.get(a)}var c={},d=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var e in a){if(Object.prototype.hasOwnProperty.call(a,e)){var g=d?Object.getOwnPropertyDescriptor(a,e):null;if(g&&(g.get||g.set)){Object.defineProperty(c,e,g)}else{c[e]=a[e]}}}c.default=a;if(b){b.set(a,c)}return c}function h(a){return a&&a.__esModule?a:{default:a}}var i=function(a,c){return b.default.loadFragment("qbank_usage","question_usage",c,a)},j=function(a,b){var f={questionid:a};d.default.create({type:d.default.types.CANCEL,title:c.get_string("usageheader","qbank_usage"),body:i(f,b),large:!0}).then(function(a){a.show();a.getRoot().on("click","a[href].page-link",function(c){c.preventDefault();var d=c.target.getAttribute("href");if("#"!==d){f.querystring=d;a.setBody(i(f,b))}});return a}).fail(e.default.exception)};a.init=function init(a,b){var c=document.querySelector(a),d=c.getAttribute("data-questionid");c.addEventListener("click",function(){j(d,b)})}});
//# sourceMappingURL=usage.min.js.map

View File

@ -0,0 +1 @@
{"version":3,"sources":["../src/usage.js"],"names":["getFragment","args","contextId","Fragment","loadFragment","usageEvent","questionId","questionid","ModalFactory","create","type","types","CANCEL","title","Str","get_string","body","large","then","modal","show","getRoot","on","e","preventDefault","attr","target","getAttribute","querystring","setBody","fail","Notification","exception","init","questionSelector","document","querySelector","addEventListener"],"mappings":"+eAwBA,OACA,OACA,OACA,O,4lBAUMA,CAAAA,CAAW,CAAG,SAACC,CAAD,CAAOC,CAAP,CAAqB,CACrC,MAAOC,WAASC,YAAT,CAAsB,aAAtB,CAAqC,gBAArC,CAAuDF,CAAvD,CAAkED,CAAlE,CACV,C,CASKI,CAAU,CAAG,SAACC,CAAD,CAAaJ,CAAb,CAA2B,CAC1C,GAAID,CAAAA,CAAI,CAAG,CACPM,UAAU,CAAED,CADL,CAAX,CAGAE,UAAaC,MAAb,CAAoB,CAChBC,IAAI,CAAEF,UAAaG,KAAb,CAAmBC,MADT,CAEhBC,KAAK,CAAEC,CAAG,CAACC,UAAJ,CAAe,aAAf,CAA8B,aAA9B,CAFS,CAGhBC,IAAI,CAAEhB,CAAW,CAACC,CAAD,CAAOC,CAAP,CAHD,CAIhBe,KAAK,GAJW,CAApB,EAKGC,IALH,CAKQ,SAACC,CAAD,CAAW,CACfA,CAAK,CAACC,IAAN,GACAD,CAAK,CAACE,OAAN,GAAgBC,EAAhB,CAAmB,OAAnB,CAA4B,mBAA5B,CAAiD,SAASC,CAAT,CAAY,CACzDA,CAAC,CAACC,cAAF,GACA,GAAIC,CAAAA,CAAI,CAAGF,CAAC,CAACG,MAAF,CAASC,YAAT,CAAsB,MAAtB,CAAX,CACA,GAAa,GAAT,GAAAF,CAAJ,CAAkB,CACdxB,CAAI,CAAC2B,WAAL,CAAmBH,CAAnB,CACAN,CAAK,CAACU,OAAN,CAAc7B,CAAW,CAACC,CAAD,CAAOC,CAAP,CAAzB,CACH,CACJ,CAPD,EAQA,MAAOiB,CAAAA,CACV,CAhBD,EAgBGW,IAhBH,CAgBQC,UAAaC,SAhBrB,CAiBH,C,QASmB,QAAPC,CAAAA,IAAO,CAACC,CAAD,CAAmBhC,CAAnB,CAAiC,IAC7CwB,CAAAA,CAAM,CAAGS,QAAQ,CAACC,aAAT,CAAuBF,CAAvB,CADoC,CAE7C5B,CAAU,CAAGoB,CAAM,CAACC,YAAP,CAAoB,iBAApB,CAFgC,CAGjDD,CAAM,CAACW,gBAAP,CAAwB,OAAxB,CAAiC,UAAM,CAEnChC,CAAU,CAACC,CAAD,CAAaJ,CAAb,CACb,CAHD,CAIH,C","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Usage column selector js.\n *\n * @module qbank_usage/usage\n * @copyright 2021 Catalyst IT Australia Pty Ltd\n * @author Safat Shahin <safatshahin@catalyst-au.net>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Fragment from 'core/fragment';\nimport * as Str from 'core/str';\nimport ModalFactory from 'core/modal_factory';\nimport Notification from 'core/notification';\n\n/**\n * Get the fragment.\n *\n * @method getFragment\n * @param {{questioned: int}} args\n * @param {int} contextId\n * @return {string}\n */\nconst getFragment = (args, contextId) => {\n return Fragment.loadFragment('qbank_usage', 'question_usage', contextId, args);\n};\n\n/**\n * Event listeners for the module.\n *\n * @method clickEvent\n * @param {int} questionId\n * @param {int} contextId\n */\nconst usageEvent = (questionId, contextId) => {\n let args = {\n questionid: questionId\n };\n ModalFactory.create({\n type: ModalFactory.types.CANCEL,\n title: Str.get_string('usageheader', 'qbank_usage'),\n body: getFragment(args, contextId),\n large: true,\n }).then((modal) => {\n modal.show();\n modal.getRoot().on('click', 'a[href].page-link', function(e) {\n e.preventDefault();\n let attr = e.target.getAttribute(\"href\");\n if (attr !== '#') {\n args.querystring = attr;\n modal.setBody(getFragment(args, contextId));\n }\n });\n return modal;\n }).fail(Notification.exception);\n};\n\n/**\n * Entrypoint of the js.\n *\n * @method init\n * @param {string} questionSelector the question usage identifier.\n * @param {int} contextId the question context id.\n */\nexport const init = (questionSelector, contextId) => {\n let target = document.querySelector(questionSelector);\n let questionId = target.getAttribute('data-questionid');\n target.addEventListener('click', () => {\n // Call for the event listener to listed for clicks in any usage count row.\n usageEvent(questionId, contextId);\n });\n};\n"],"file":"usage.min.js"}

View File

@ -0,0 +1,86 @@
// 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/>.
/**
* Usage column selector js.
*
* @module qbank_usage/usage
* @copyright 2021 Catalyst IT Australia Pty Ltd
* @author Safat Shahin <safatshahin@catalyst-au.net>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
import Fragment from 'core/fragment';
import * as Str from 'core/str';
import ModalFactory from 'core/modal_factory';
import Notification from 'core/notification';
/**
* Get the fragment.
*
* @method getFragment
* @param {{questioned: int}} args
* @param {int} contextId
* @return {string}
*/
const getFragment = (args, contextId) => {
return Fragment.loadFragment('qbank_usage', 'question_usage', contextId, args);
};
/**
* Event listeners for the module.
*
* @method clickEvent
* @param {int} questionId
* @param {int} contextId
*/
const usageEvent = (questionId, contextId) => {
let args = {
questionid: questionId
};
ModalFactory.create({
type: ModalFactory.types.CANCEL,
title: Str.get_string('usageheader', 'qbank_usage'),
body: getFragment(args, contextId),
large: true,
}).then((modal) => {
modal.show();
modal.getRoot().on('click', 'a[href].page-link', function(e) {
e.preventDefault();
let attr = e.target.getAttribute("href");
if (attr !== '#') {
args.querystring = attr;
modal.setBody(getFragment(args, contextId));
}
});
return modal;
}).fail(Notification.exception);
};
/**
* Entrypoint of the js.
*
* @method init
* @param {string} questionSelector the question usage identifier.
* @param {int} contextId the question context id.
*/
export const init = (questionSelector, contextId) => {
let target = document.querySelector(questionSelector);
let questionId = target.getAttribute('data-questionid');
target.addEventListener('click', () => {
// Call for the event listener to listed for clicks in any usage count row.
usageEvent(questionId, contextId);
});
};

View File

@ -0,0 +1,90 @@
<?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/>.
namespace qbank_usage;
/**
* Helper class for usage.
*
* @package qbank_usage
* @copyright 2021 Catalyst IT Australia Pty Ltd
* @author Safat Shahin <safatshahin@catalyst-au.net>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class helper {
/**
* Get the usage count for a question.
*
* @param \question_definition $question
* @return int
*/
public static function get_question_entry_usage_count($question) {
global $DB;
$sql = 'SELECT COUNT(quizid) FROM (' . self::question_usage_sql() . ') AS quizid';
return $DB->count_records_sql($sql, [$question->id, $question->id]);
}
/**
* Get the sql for usage data.
*
* @return string
*/
public static function question_usage_sql(): string {
$sqlset = "(SELECT qz.id as quizid,
qz.name as modulename,
qz.course as courseid
FROM {quiz} as qz
JOIN {quiz_attempts} qa ON qa.quiz = qz.id
JOIN {question_usages} qu ON qu.id = qa.uniqueid
JOIN {question_attempts} qatt ON qatt.questionusageid = qu.id
JOIN {question} q ON q.id = qatt.questionid
WHERE qa.preview = 0
AND q.id = ?)
UNION
(SELECT qz.id as quizid,
qz.name as modulename,
qz.course as courseid
FROM {quiz_slots} slot
JOIN {quiz} qz ON qz.id = slot.quizid
WHERE slot.questionid = ?)";
return $sqlset;
}
/**
* Get question attempt count for the question.
*
* @param int $questionid
* @param int $quizid
* @return int
*/
public static function get_question_attempts_count_in_quiz(int $questionid, int $quizid): int {
global $DB;
$sql = 'SELECT COUNT(qatt.id)
FROM {quiz} as qz
JOIN {quiz_attempts} qa ON qa.quiz = qz.id
JOIN {question_usages} qu ON qu.id = qa.uniqueid
JOIN {question_attempts} qatt ON qatt.questionusageid = qu.id
JOIN {question} q ON q.id = qatt.questionid
WHERE qatt.questionid = :questionid
AND qa.preview = 0
AND qz.id = :quizid';
return $DB->count_records_sql($sql, [ 'questionid' => $questionid, 'quizid' => $quizid]);
}
}

View File

@ -0,0 +1,39 @@
<?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/>.
namespace qbank_usage\output;
/**
* Class renderer
*
* @package qbank_usage
* @copyright 2021 Catalyst IT Australia Pty Ltd
* @author Safat Shahin <safatshahin@catalyst-au.net>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class renderer extends \plugin_renderer_base {
/**
* Render the html fragment for usage modal.
*
* @param array $displaydata
* @return string
*/
public function render_usage_fragment(array $displaydata): string {
return $this->render_from_template('qbank_usage/usage_modal', $displaydata);
}
}

View File

@ -0,0 +1,34 @@
<?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/>.
namespace qbank_usage;
/**
* Class plugin_feature is the entrypoint for the columns.
*
* @package qbank_usage
* @copyright 2021 Catalyst IT Australia Pty Ltd
* @author Safat Shahin <safatshahin@catalyst-au.net>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class plugin_feature extends \core_question\local\bank\plugin_features_base {
public function get_question_columns($qbank): array {
return [
new question_usage_column($qbank)
];
}
}

View File

@ -0,0 +1,31 @@
<?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/>.
namespace qbank_usage\privacy;
/**
* Privacy Subsystem for qbank_usage implementing null_provider.
*
* @copyright 2021 Catalyst IT Australia Pty Ltd
* @author Safat Shahin <safatshahin@catalyst-au.net>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider implements \core_privacy\local\metadata\null_provider {
public static function get_reason(): string {
return 'privacy:metadata';
}
}

View File

@ -0,0 +1,57 @@
<?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/>.
namespace qbank_usage;
use core_question\local\bank\column_base;
/**
* A column type for the name of the question type.
*
* @package qbank_usage
* @copyright 2021 Catalyst IT Australia Pty Ltd
* @author Safat Shahin <safatshahin@catalyst-au.net>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class question_usage_column extends column_base {
public function get_name(): string {
return 'questionusage';
}
protected function get_title(): string {
return get_string('questionusage', 'qbank_usage');
}
protected function display_content($question, $rowclasses): void {
global $PAGE;
$usagecount = helper::get_question_entry_usage_count($question);
$attributes = [];
if (question_has_capability_on($question, 'view')) {
$target = 'questionusagepreview_' . $question->id;
$datatarget = '[data-target="' . $target . '"]';
$PAGE->requires->js_call_amd('qbank_usage/usage', 'init', [$datatarget, $question->contextid]);
$attributes = [
'data-target' => $target,
'data-questionid' => $question->id,
'data-courseid' => $this->qbank->course->id,
'class' => 'link-primary comment-pointer'
];
}
echo \html_writer::tag('a', $usagecount, $attributes);
}
}

View File

@ -0,0 +1,126 @@
<?php
// This file is part of Moodle - https://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 <https://www.gnu.org/licenses/>.
namespace qbank_usage\tables;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->libdir.'/tablelib.php');
use moodle_url;
use qbank_usage\helper;
use table_sql;
/**
* Class question_usage_table.
* An extension of regular Moodle table.
*
* @package qbank_usage
* @copyright 2021 Catalyst IT Australia Pty Ltd
* @author Safat Shahin <safatshahin@catalyst-au.net>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class question_usage_table extends table_sql {
/**
* Search string.
*
* @var string $search
*/
public $search = '';
/**
* Question id.
*
* @var \question_definition $question
*/
public $question;
/**
* constructor.
* Sets the SQL for the table and the pagination.
*
* @param string $uniqueid
* @param \question_definition $question
*/
public function __construct(string $uniqueid, \question_definition $question) {
global $PAGE;
parent::__construct($uniqueid);
$this->question = $question;
$columns = ['modulename', 'coursename', 'attempts'];
$headers = [
get_string('modulename', 'qbank_usage'),
get_string('coursename', 'qbank_usage'),
get_string('attempts', 'qbank_usage')
];
$this->is_collapsible = false;
$this->no_sorting('modulename');
$this->no_sorting('coursename');
$this->no_sorting('attempts');
$this->define_columns($columns);
$this->define_headers($headers);
$this->define_baseurl($PAGE->url);
}
public function query_db($pagesize, $useinitialsbar = true) {
global $DB;
if (!$this->is_downloading()) {
$total = helper::get_question_entry_usage_count($this->question);
$this->pagesize($pagesize, $total);
}
$sql = helper::question_usage_sql();
$params = [$this->question->id, $this->question->id];
if (!$this->is_downloading()) {
$this->rawdata = $DB->get_records_sql($sql, $params, $this->get_page_start(), $this->get_page_size());
} else {
$this->rawdata = $DB->get_records_sql($sql, $params);
}
}
public function col_modulename(\stdClass $values): string {
$params = [
'href' => new moodle_url('/mod/quiz/view.php', ['q' => $values->quizid])
];
return \html_writer::tag('a', $values->modulename, $params);
}
public function col_coursename(\stdClass $values): string {
$course = get_course($values->courseid);
$params = [
'href' => new moodle_url('/course/view.php', ['id' => $values->courseid])
];
return \html_writer::tag('a', $course->fullname, $params);
}
public function col_attempts(\stdClass $values): string {
return helper::get_question_attempts_count_in_quiz($this->question->id, $values->quizid);
}
/**
* Export this data so it can be used as the context for a mustache template/fragment.
*
* @return string
*/
public function export_for_fragment(): string {
ob_start();
$this->out(10, true);
return ob_get_clean();
}
}

View File

@ -0,0 +1,37 @@
<?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/>.
/**
* Strings for component qbank_usage, language 'en'
*
* @package qbank_usage
* @copyright 2021 Catalyst IT Australia Pty Ltd
* @author Safat Shahin <safatshahin@catalyst-au.net>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
$string['pluginname'] = 'Question usage';
$string['privacy:metadata'] = 'Question usage plugin does not store any user data.';
$string['questionusage'] = 'Usage';
$string['usageheader'] = 'Question usage';
// Table.
$string['modulename'] = 'Activity name';
$string['coursename'] = 'Course name';
$string['versions'] = 'Version';
$string['state'] = 'State';
$string['attempts'] = 'Attempts';
$string['questionusageversion'] = 'v{$a}';

View File

@ -0,0 +1,63 @@
<?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/>.
/**
* Helper functions and callbacks.
*
* @package qbank_usage
* @copyright 2021 Catalyst IT Australia Pty Ltd
* @author Safat Shahin <safatshahin@catalyst-au.net>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Question usage fragment callback.
*
* @param array $args
* @return string rendered output
*/
function qbank_usage_output_fragment_question_usage(array $args): string {
global $USER, $PAGE, $CFG;
require_once($CFG->dirroot . '/question/engine/bank.php');
$displaydata = [];
$question = question_bank::load_question($args['questionid']);
$quba = question_engine::make_questions_usage_by_activity('core_question_preview', context_user::instance($USER->id));
$options = new \qbank_previewquestion\question_preview_options($question);
$options->load_user_defaults();
$options->set_from_request();
$quba->set_preferred_behaviour($options->behaviour);
$slot = $quba->add_question($question, $options->maxmark);
$quba->start_question($slot, $options->variant);
$displaydata['question'] = $quba->render_question($slot, $options, '1');
$questionusagetable = new \qbank_usage\tables\question_usage_table('question_usage_table', $question);
$questionusagetable->baseurl = new moodle_url('');
if (isset($args['querystring'])) {
$querystring = preg_replace('/^\?/', '', $args['querystring']);
$params = [];
parse_str($querystring, $params);
if (isset($params['page'])) {
$questionusagetable->currpage = $params['page'];
}
}
$displaydata['tablesql'] = $questionusagetable->export_for_fragment();
return $PAGE->get_renderer('qbank_usage')->render_usage_fragment($displaydata);
}

View File

@ -0,0 +1,6 @@
.questionusage {
cursor: pointer;
}
#categoryquestions .questionusage {
width: 5em;
}

View File

@ -0,0 +1,36 @@
{{!
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/>.
}}
{{!
@template qbank_usage/usage_modal
Example context (json):
{
"usagedata": [
{
"question": "question html"
}
]
}
}}
<div class="question-usage-preview">
{{{question}}}
</div>
<div id="question-usage_table">
{{{tablesql}}}
</div>

View File

@ -0,0 +1,59 @@
<?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/>.
// NOTE: no MOODLE_INTERNAL test here, this file may be required by behat before including /config.php.
require_once(__DIR__ . '/../../../../../lib/behat/behat_base.php');
require_once(__DIR__ . '/../../../../tests/behat/behat_question_base.php');
use Behat\Mink\Exception\ExpectationException as ExpectationException,
Behat\Mink\Exception\ElementNotFoundException as ElementNotFoundException;
/**
* Steps definitions to deal with the usage in question.
*
* @package qbank_usage
* @copyright 2021 Catalyst IT Australia Pty Ltd
* @author Safat Shahin <safatshahin@catalyst-au.net>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class behat_qbank_usage extends behat_question_base {
/**
* Looks for a table, then looks for a row that contains the given text.
* Once it finds the right row, it clicks a link in that row.
*
* @When I click :arg1 on the usage column
* @param string $linkname
*/
public function i_click_on_the_usage_column($linkname) {
$exception = new ElementNotFoundException($this->getSession(),
'Cannot find any row on the page containing the text ' . $linkname);
$row = $this->find('css', sprintf('table tbody tr td.questionusage a:contains("%s")', $linkname), $exception);
$row->click();
}
/**
* Looks for the appropriate usage count in the column.
*
* @Then I should see :arg1 on the usage column
* @param string $linkdata
*/
public function i_should_see_on_the_usage_column($linkdata) {
$exception = new ElementNotFoundException($this->getSession(),
'Cannot find any row with the usage count of ' . $linkdata . ' on the column named Usage');
$this->find('css', sprintf('table tbody tr td.questionusage a:contains("%s")', $linkdata), $exception);
}
}

View File

@ -0,0 +1,44 @@
@qbank @qbank_usage
Feature: Use the qbank plugin manager page for question usage
In order to check the plugin behaviour with enable and disable
Background:
Given the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "activities" exist:
| activity | name | course | idnumber |
| quiz | Test quiz | C1 | quiz1 |
And the following "question categories" exist:
| contextlevel | reference | name |
| Course | C1 | Test questions |
And the following "questions" exist:
| questioncategory | qtype | name | questiontext |
| Test questions | truefalse | First question | Answer the first question |
Scenario: Enable/disable question usage column from the base view
Given I log in as "admin"
And I navigate to "Plugins > Question bank plugins > Manage question bank plugins" in site administration
And I should see "Question usage"
When I click on "Disable" "link" in the "Question usage" "table_row"
And I am on the "Test quiz" "quiz activity" page
And I navigate to "Question bank > Questions" in current page administration
Then I should not see "Usage"
And I navigate to "Plugins > Question bank plugins > Manage question bank plugins" in site administration
And I click on "Enable" "link" in the "Question usage" "table_row"
And I am on the "Test quiz" "quiz activity" page
And I navigate to "Question bank > Questions" in current page administration
And I should see "Usage"
@javascript
Scenario: Question usage modal should work without any usage data
Given I log in as "admin"
And I am on the "Test quiz" "quiz activity" page
And I navigate to "Question bank > Questions" in current page administration
And I set the field "Select a category" to "Test questions"
And I should see "Test questions"
And I should see "0" on the usage column
When I click "0" on the usage column
Then I should see "Question usage"
And I click on "Close" "button" in the ".modal-dialog" "css_element"
And I should see "0" on the usage column

View File

@ -0,0 +1,106 @@
<?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/>.
namespace qbank_usage;
/**
* Helper test.
*
* @package qbank_usage
* @copyright 2021 Catalyst IT Australia Pty Ltd
* @author Safat Shahin <safatshahin@catalyst-au.net>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @coversDefaultClass \qbank_usage\helper
*/
class qbank_usage_helper_test extends \advanced_testcase {
/**
* @var \stdClass $quiz
*/
protected $quiz;
/**
* @var array $questions
*/
protected $questions = [];
/**
* Test setup.
*/
public function setup(): void {
$this->resetAfterTest();
$layout = '1,2,0';
// Make a user to do the quiz.
$user = $this->getDataGenerator()->create_user();
$course = $this->getDataGenerator()->create_course();
// Make a quiz.
$quizgenerator = $this->getDataGenerator()->get_plugin_generator('mod_quiz');
$this->quiz = $quizgenerator->create_instance(['course' => $course->id,
'grade' => 100.0, 'sumgrades' => 2, 'layout' => $layout]);
$quizobj = \quiz::create($this->quiz->id, $user->id);
$quba = \question_engine::make_questions_usage_by_activity('mod_quiz', $quizobj->get_context());
$quba->set_preferred_behaviour($quizobj->get_quiz()->preferredbehaviour);
$questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question');
$cat = $questiongenerator->create_question_category();
$page = 1;
foreach (explode(',', $layout) as $slot) {
if ($slot == 0) {
$page += 1;
continue;
}
$question = $questiongenerator->create_question('shortanswer', null, ['category' => $cat->id]);
quiz_add_quiz_question($question->id, $this->quiz, $page);
$this->questions [] = $question;
}
$timenow = time();
$attempt = quiz_create_attempt($quizobj, 1, false, $timenow, false, $user->id);
quiz_start_new_attempt($quizobj, $quba, $attempt, 1, $timenow);
quiz_attempt_save_started($quizobj, $quba, $attempt);
\quiz_attempt::create($attempt->id);
}
/**
* Test question attempt count.
*
* @covers ::get_question_attempts_count_in_quiz
*/
public function test_get_question_attempts_count_in_quiz() {
foreach ($this->questions as $question) {
$questionattemptcount = helper::get_question_attempts_count_in_quiz($question->id, $this->quiz->id);
// Test the attempt count matches the usage count, each question should have one count.
$this->assertEquals(1, $questionattemptcount);
}
}
/**
* Test test usage data.
*
* @covers ::get_question_entry_usage_count
*/
public function test_get_question_entry_usage_count() {
foreach ($this->questions as $question) {
$count = helper::get_question_entry_usage_count($question);
// Test that the attempt data matches the usage data for the count.
$this->assertEquals(1, $count);
}
}
}

View File

@ -0,0 +1,84 @@
<?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/>.
namespace qbank_usage;
/**
* Tests for the data of question usage from differnet areas like helper or usage table.
*
* @package qbank_usage
* @copyright 2021 Catalyst IT Australia Pty Ltd
* @author Safat Shahin <safatshahin@catalyst-au.net>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @coversDefaultClass \qbank_usage\tables\question_usage_table
* @covers qbank_usage_output_fragment_question_usage
*/
class question_usage_test extends \advanced_testcase {
/**
* Test question usage data.
*/
public function test_question_usage() {
global $PAGE;
$this->resetAfterTest(true);
$layout = '1,2,0';
// Make a user to do the quiz.
$user = $this->getDataGenerator()->create_user();
$course = $this->getDataGenerator()->create_course();
// Make a quiz.
$quizgenerator = $this->getDataGenerator()->get_plugin_generator('mod_quiz');
$quiz = $quizgenerator->create_instance(['course' => $course->id,
'grade' => 100.0, 'sumgrades' => 2, 'layout' => $layout]);
$quizobj = \quiz::create($quiz->id, $user->id);
$quba = \question_engine::make_questions_usage_by_activity('mod_quiz', $quizobj->get_context());
$quba->set_preferred_behaviour($quizobj->get_quiz()->preferredbehaviour);
$questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question');
$cat = $questiongenerator->create_question_category();
$questions = [];
$page = 1;
foreach (explode(',', $layout) as $slot) {
if ($slot == 0) {
$page += 1;
continue;
}
$question = $questiongenerator->create_question('shortanswer', null, ['category' => $cat->id]);
quiz_add_quiz_question($question->id, $quiz, $page);
$questions [] = $question;
}
$timenow = time();
$attempt = quiz_create_attempt($quizobj, 1, false, $timenow, false, $user->id);
quiz_start_new_attempt($quizobj, $quba, $attempt, 1, $timenow);
quiz_attempt_save_started($quizobj, $quba, $attempt);
$attemptdata = \quiz_attempt::create($attempt->id);
$this->setAdminUser();
$PAGE->set_url(new \moodle_url('/'));
foreach ($questions as $question) {
$questionusagetable = qbank_usage_output_fragment_question_usage(['questionid' => $question->id]);
// Test usage table contains the quiz data which was attempted.
$this->assertStringContainsString($quiz->name, $questionusagetable);
// Test usage table contains the course data where the quiz was attempted.
$this->assertStringContainsString($course->fullname, $questionusagetable);
}
}
}

View File

@ -0,0 +1,31 @@
<?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/>.
/**
* Version information for qbank_usage.
*
* @package qbank_usage
* @copyright 2021 Catalyst IT Australia Pty Ltd
* @author Safat Shahin <safatshahin@catalyst-au.net>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$plugin->component = 'qbank_usage';
$plugin->version = 2021092400;
$plugin->requires = 2021052500;
$plugin->maturity = MATURITY_STABLE;