Commit 8f06aa01 authored by Safat Shahin's avatar Safat Shahin
Browse files

MDL-72074 qbank_usage: Add usage plugin to core



This implementation will introduce the usage plugin
which shows qhich quizes are using this question,
number of attmpts and other information.
Co-Authored-By: default avatarSafat Shahin <safatshahin@catalyst-au.net>
Co-Authored-By: default avatarMatt Porritt <mattp@catalyst-au.net>
parent b16fc541
......@@ -1959,6 +1959,7 @@ class core_plugin_manager {
'managecategories',
'previewquestion',
'tagquestion',
'usage',
'viewcreator',
'viewquestionname',
'viewquestiontext',
......
File suppressed by a .gitattributes entry or the file's encoding is unsupported.
File suppressed by a .gitattributes entry or the file's encoding is unsupported.
// 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);
});
};
<?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]);
}
}
<?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);
}
}
<?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)
];
}
}
<?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';
}
}
<?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);
}
}
<?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();
}
}
<?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}';
<?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);
}
.questionusage {
cursor: pointer;
}
#categoryquestions .questionusage {
width: 5em;
}
{{!
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