MDL-71642 qbank_comment: Add question comment plugin to core

This implementation will introduce a qbank plugin
"comment" which will allow users with the capability
to comment in a question. It also implements a
callback for the question preview page to view
and add comments from the preview page. Comment
plugin is implemented using the existing comments
API from the core.

Co-Authored-By: Guillermo Gomez Arias <guillermogomez@catalyst-au.net>
Co-Authored-By: Safat Shahin <safatshahin@catalyst-au.net>
Co-Authored-By: Matt Porritt <mattp@catalyst-au.net>
This commit is contained in:
Guillermo Gomez 2021-06-02 16:29:29 +10:00 committed by Safat Shahin
parent 70073fdc74
commit 3be2b123e5
35 changed files with 2165 additions and 18 deletions

View File

@ -36,6 +36,7 @@ require_once($CFG->dirroot . '/backup/moodle2/backup_block_task.class.php');
require_once($CFG->dirroot . '/backup/moodle2/backup_default_block_task.class.php');
require_once($CFG->dirroot . '/backup/moodle2/backup_xml_transformer.class.php');
require_once($CFG->dirroot . '/backup/moodle2/backup_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/backup_qbank_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/backup_qtype_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/backup_qtype_extrafields_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/backup_gradingform_plugin.class.php');

View File

@ -0,0 +1,38 @@
<?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/>.
/**
* Defines backup_qbank_plugin class.
*
* @package core_backup
* @subpackage moodle2
* @category backup
* @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
*/
/**
* Base class for qbank backup plugins.
*
* @package core_backup
* @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
*/
abstract class backup_qbank_plugin extends backup_plugin {
// Use default parent behaviour.
}

View File

@ -787,34 +787,28 @@ class backup_filters_structure_step extends backup_structure_step {
}
/**
* structure step in charge of constructing the comments.xml file for all the comments found
* in a given context
* Structure step in charge of constructing the comments.xml file for all the comments found in a given context.
*/
class backup_comments_structure_step extends backup_structure_step {
protected function define_structure() {
// Define each element separated
// Define each element separated.
$comments = new backup_nested_element('comments');
$comment = new backup_nested_element('comment', array('id'), array(
'commentarea', 'itemid', 'content', 'format',
'component', 'commentarea', 'itemid', 'content', 'format',
'userid', 'timecreated'));
// Build the tree
// Build the tree.
$comments->add_child($comment);
// Define sources
// Define sources.
$comment->set_source_table('comments', array('contextid' => backup::VAR_CONTEXTID));
// Define id annotations
// Define id annotations.
$comment->annotate_ids('user', 'userid');
// Return the root element (comments)
// Return the root element (comments).
return $comments;
}
}
@ -2356,6 +2350,9 @@ class backup_questions_structure_step extends backup_structure_step {
// attach qtype plugin structure to $question element, only one allowed
$this->add_plugin_structure('qtype', $question, false);
// Attach qbank plugin stucture to $question element, multiple allowed.
$this->add_plugin_structure('qbank', $question, true);
// attach local plugin stucture to $question element, multiple allowed
$this->add_plugin_structure('local', $question, true);

View File

@ -35,6 +35,7 @@ require_once($CFG->dirroot . '/backup/moodle2/restore_final_task.class.php');
require_once($CFG->dirroot . '/backup/moodle2/restore_block_task.class.php');
require_once($CFG->dirroot . '/backup/moodle2/restore_default_block_task.class.php');
require_once($CFG->dirroot . '/backup/moodle2/restore_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/restore_qbank_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/restore_qtype_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/restore_qtype_extrafields_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/restore_format_plugin.class.php');
@ -46,6 +47,7 @@ require_once($CFG->dirroot . '/backup/moodle2/restore_plagiarism_plugin.class.ph
require_once($CFG->dirroot . '/backup/moodle2/restore_gradingform_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/restore_enrol_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/backup_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/backup_qbank_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/backup_qtype_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/backup_qtype_extrafields_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/backup_format_plugin.class.php');

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/>.
/**
* Defines restore_qbank_plugin class.
*
* @package core_backup
* @subpackage moodle2
* @category backup
* @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
*/
/**
* Base class for qbank backup plugins.
*
* @package core_backup
* @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
*/
abstract class restore_qbank_plugin extends restore_plugin {
// Use default parent behaviour.
}

View File

@ -4750,10 +4750,13 @@ class restore_create_categories_and_questions extends restore_structure_step {
// Apply for 'qtype' plugins optional paths at question level
$this->add_plugin_structure('qtype', $question);
// Apply for 'qbank' plugins optional paths at question level.
$this->add_plugin_structure('qbank', $question);
// Apply for 'local' plugins optional paths at question level
$this->add_plugin_structure('local', $question);
return array($category, $question, $hint, $tag);
return [$category, $question, $hint, $tag];
}
protected function process_question_category($data) {

View File

@ -365,6 +365,8 @@ $string['question:viewall'] = 'View all questions';
$string['question:viewmine'] = 'View your own questions';
$string['question:tagall'] = 'Tag all questions';
$string['question:tagmine'] = 'Tag your own questions';
$string['question:commentall'] = 'Comment all questions';
$string['question:commentmine'] = 'Comment your own questions';
$string['rating:rate'] = 'Add ratings to items';
$string['rating:view'] = 'View the total rating you received';
$string['rating:viewany'] = 'View total ratings that anyone received';

View File

@ -1944,6 +1944,7 @@ class core_plugin_manager {
),
'qbank' => [
'comment',
'deletequestion',
'editquestion',
'exporttoxml',

View File

@ -387,6 +387,9 @@ function question_delete_question($questionid) {
}
}
// Delete question comments.
$DB->delete_records('comments', ['itemid' => $questionid, 'component' => 'qbank_comment',
'commentarea' => 'question']);
// Finally delete the question record itself
$DB->delete_records('question', array('id' => $questionid));
question_bank::notify_question_edited($questionid);
@ -1569,7 +1572,7 @@ function question_has_capability_on($questionorid, $cap, $notused = -1) {
// These are existing questions capabilities that are set per category.
// Each of these has a 'mine' and 'all' version that is appended to the capability name.
$capabilitieswithallandmine = ['edit' => 1, 'view' => 1, 'use' => 1, 'move' => 1, 'tag' => 1];
$capabilitieswithallandmine = ['edit' => 1, 'view' => 1, 'use' => 1, 'move' => 1, 'tag' => 1, 'comment' => 1];
if (!isset($capabilitieswithallandmine[$cap])) {
return has_capability('moodle/question:' . $cap, $context);
@ -1742,6 +1745,8 @@ function question_get_question_capabilities() {
'moodle/question:moveall',
'moodle/question:tagmine',
'moodle/question:tagall',
'moodle/question:commentmine',
'moodle/question:commentall',
);
}

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_comment/comment",["exports","core/fragment","core/str","core/modal_events","core/modal_factory","core/notification"],function(a,b,c,d,e,f){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.init=void 0;b=i(b);c=h(c);d=i(d);e=i(e);f=i(f);function g(){if("function"!=typeof WeakMap)return null;var a=new WeakMap;g=function(){return a};return a}function h(a){if(a&&a.__esModule){return a}if(null===a||"object"!==_typeof(a)&&"function"!=typeof a){return{default:a}}var b=g();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 f=d?Object.getOwnPropertyDescriptor(a,e):null;if(f&&(f.get||f.set)){Object.defineProperty(c,e,f)}else{c[e]=a[e]}}}c.default=a;if(b){b.set(a,c)}return c}function i(a){return a&&a.__esModule?a:{default:a}}var j=function(a,g,h){var i=b.default.loadFragment("qbank_comment","question_comment",h,{questionid:a,courseid:g});e.default.create({type:e.default.types.SAVE_CANCEL,title:c.get_string("commentheader","qbank_comment"),body:i,large:!0}).then(function(a){var b=a.getRoot();b.on(d.default.bodyRendered,function(){var a=document.querySelectorAll("div.comment-area a")[0];a.style.display="none"});c.get_strings([{key:"addcomment",component:"qbank_comment"},{key:"close",component:"qbank_comment"}]).then(function(b){a.setButtonText("save",b[0]);a.setButtonText("cancel",b[1])}).fail(f.default.exception);b.on(d.default.cancel,function(){location.reload();a.hide()});b.on(d.default.save,function(a){a.preventDefault();var b=document.querySelectorAll("div.comment-area a")[0],c=document.querySelectorAll("div.comment-area textarea")[0];if(c.value!=c.getAttribute("aria-label")&&""!=c.value){b.click()}});b.on("click","button[data-action=\"hide\"]",function(){location.reload();a.hide()});a.show();return a}).fail(f.default.exception)};a.init=function init(a){var b=document.querySelector(a),c=b.getAttribute("data-questionid"),d=b.getAttribute("data-courseid");b.addEventListener("click",function(){j(c,d,1)})}});
//# sourceMappingURL=comment.min.js.map

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,110 @@
// 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/>.
/**
* Column selector js.
*
* @package qbank_comment
* @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 ModalEvents from 'core/modal_events';
import ModalFactory from 'core/modal_factory';
import Notification from 'core/notification';
/**
* Event listeners for the module.
*
* @method clickEvent
* @param {int} questionId
* @param {int} courseID
* @param {int} contextId
*/
const commentEvent = (questionId, courseID, contextId) => {
let args = {
questionid: questionId,
courseid: courseID
};
let commentFragment = Fragment.loadFragment('qbank_comment', 'question_comment', contextId, args);
ModalFactory.create({
type: ModalFactory.types.SAVE_CANCEL,
title: Str.get_string('commentheader', 'qbank_comment'),
body: commentFragment,
large: true,
}).then((modal) => {
let root = modal.getRoot();
// Don't display the default add comment link in the modal.
root.on(ModalEvents.bodyRendered, function() {
const submitlink = document.querySelectorAll("div.comment-area a")[0];
submitlink.style.display = 'none';
});
// Get the required strings and updated the modal button text labels.
Str.get_strings([
{key: 'addcomment', component: 'qbank_comment'},
{key: 'close', component: 'qbank_comment'},
]).then((strings) => {
modal.setButtonText('save', strings[0]);
modal.setButtonText('cancel', strings[1]);
return;
}).fail(Notification.exception);
root.on(ModalEvents.cancel, function() {
location.reload();
modal.hide();
});
// Handle adding the comment when the button in the modal is clicked.
root.on(ModalEvents.save, function(e) {
e.preventDefault();
const submitlink = document.querySelectorAll("div.comment-area a")[0];
const textarea = document.querySelectorAll("div.comment-area textarea")[0];
// Check there is a valid comment to add, and trigger adding if there is.
if (textarea.value != textarea.getAttribute('aria-label') && textarea.value != '') {
submitlink.click();
}
});
root.on('click', 'button[data-action="hide"]', () => {
location.reload();
modal.hide();
});
modal.show();
return modal;
}).fail(Notification.exception);
};
/**
* Entrypoint of the js.
*
* @method init
* @param {string} questionSelector the question comment identifier.
*/
export const init = (questionSelector) => {
let target = document.querySelector(questionSelector);
let contextId = 1;
let questionId = target.getAttribute('data-questionid'),
courseID = target.getAttribute('data-courseid');
target.addEventListener('click', () => {
// Call for the event listener to listed for clicks in any comment count row.
commentEvent(questionId, courseID, contextId);
});
};

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/>.
/**
* Provides the information to backup question comments.
*
* @package qbank_comment
* @copyright 2021 Catalyst IT Australia Pty Ltd
* @author Matt Porritt <mattp@catalyst-au.net>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class backup_qbank_comment_plugin extends \backup_qbank_plugin {
/**
* Returns the comment information to attach to question element.
*/
protected function define_question_plugin_structure() {
// Define the virtual plugin element with the condition to fulfill.
$plugin = $this->get_plugin_element();
// Create one standard named plugin element (the visible container).
$pluginwrapper = new backup_nested_element($this->get_recommended_name());
// Connect the visible container ASAP.
$plugin->add_child($pluginwrapper);
$comments = new backup_nested_element('comments');
$comment = new backup_nested_element('comment', ['id'], ['component', 'commentarea', 'itemid', 'contextid',
'content', 'format', 'userid', 'timecreated']);
$pluginwrapper->add_child($comments);
$comments->add_child($comment);
$comment->set_source_sql("SELECT c.*
FROM {comments} c
WHERE c.commentarea = 'question'
AND c.component = 'qbank_comment'
AND c.itemid = ?", [backup::VAR_PARENTID]);
$comment->annotate_ids('user', 'userid');
return $plugin;
}
}

View File

@ -0,0 +1,58 @@
<?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/>.
/**
* Restore plugin class that provides the necessary information needed to restore comments for questions.
*
* @package qbank_comment
* @copyright 2021 Catalyst IT Australia Pty Ltd
* @author Matt Porritt <mattp@catalyst-au.net>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class restore_qbank_comment_plugin extends restore_qbank_plugin {
/**
* Returns the paths to be handled by the plugin at question level.
*/
protected function define_question_plugin_structure() {
return [
new restore_path_element('comment', $this->get_pathfor('/comments/comment'))
];
}
/**
* Process the question comments element.
*
* @param array $data The comment data to restore.
*/
public function process_comment($data) {
global $DB, $CFG;
$data = (object)$data;
$newquestionid = $this->get_new_parentid('question');
$questioncreated = (bool) $this->get_mappingid('question_created', $this->get_old_parentid('question'));
if (!$questioncreated) {
// This question already exists in the question bank. Nothing for us to do.
return;
}
if ($CFG->usecomments) {
$data->itemid = $newquestionid;
$DB->insert_record('comments', $data);
}
}
}

View File

@ -0,0 +1,80 @@
<?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_comment;
use core_question\local\bank\column_base;
use question_bank;
/**
* A column to show the number of comments.
*
* @package qbank_comment
* @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 comment_count_column extends column_base {
/**
* Get the name of the column, used internally.
*
* @return string
*/
public function get_name(): string {
return 'commentcount';
}
/**
* Get the title of the column that will be displayed.
*
* @return string
*/
protected function get_title(): string {
return get_string('commentplural', 'qbank_comment');
}
/**
* Generate the content to be displayed.
*
* @param object $question The question object.
* @param string $rowclasses Classes that can be added.
*/
protected function display_content($question, $rowclasses): void {
global $DB, $PAGE;
$args = [
'component' => 'qbank_comment',
'commentarea' => 'question',
'itemid' => $question->id,
'contextid' => 1
];
$commentcount = $DB->count_records('comments', $args);
$attributes = [];
if (question_has_capability_on($question, 'comment')) {
$target = 'questioncommentpreview_' . $question->id;
$datatarget = '[data-target="' . $target . '"]';
$PAGE->requires->js_call_amd('qbank_comment/comment', 'init', [$datatarget]);
$attributes = [
'data-target' => $target,
'data-questionid' => $question->id,
'data-courseid' => $this->qbank->course->id,
'class' => 'link-primary comment-pointer'
];
}
echo \html_writer::tag('a', $commentcount, $attributes);
}
}

View File

@ -0,0 +1,51 @@
<?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_comment\event;
/**
* qbank_comment comment created event.
*
* @package qbank_comment
* @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 comment_created extends \core\event\comment_created {
/**
* Get URL related to the action, null in this case.
*
* @return null
*/
public function get_url() {
return null;
}
/**
* Returns description of what happened.
*
* @return string
*/
public function get_description() {
$a = new \stdClass();
$a->userid = $this->userid;
$a->objectid = $this->objectid;
$a->component = $this->component;
$a->itemid = $this->other['itemid'];
return get_string('comment_added', 'qbank_comment', $a);
}
}

View File

@ -0,0 +1,51 @@
<?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_comment\event;
/**
* qbank_comment comment deleted event.
*
* @package qbank_comment
* @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 comment_deleted extends \core\event\comment_deleted {
/**
* Get URL related to the action, null in this case.
*
* @return null
*/
public function get_url() {
return null;
}
/**
* Returns description of what happened.
*
* @return string
*/
public function get_description() {
$a = new \stdClass();
$a->userid = $this->userid;
$a->objectid = $this->objectid;
$a->component = $this->component;
$a->itemid = $this->other['itemid'];
return get_string('comment_removed', 'qbank_comment', $a);
}
}

View File

@ -0,0 +1,38 @@
<?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_comment\output;
/**
* Class renderer for comment.
*
* @package qbank_comment
* @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 comment modal.
*
* @param array $displaydata
* @return string
*/
public function render_comment_fragment($displaydata): string {
return $this->render_from_template('qbank_comment/comment_modal', $displaydata);
}
}

View File

@ -0,0 +1,40 @@
<?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_comment;
/**
* Class plugin_features is the entrypoint for the columns.
*
* @package qbank_comment
* @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 {
/**
* Get the columns provided by this plugin.
*
* @param \core_question\local\bank\view $qbank
* @return comment_count_column[]
*/
public function get_question_columns(\core_question\local\bank\view $qbank): array {
return [
new comment_count_column($qbank)
];
}
}

View File

@ -0,0 +1,140 @@
<?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_comment\privacy;
use core_privacy\local\metadata\collection;
use core_privacy\local\request\approved_contextlist;
use core_privacy\local\request\contextlist;
use core_privacy\local\request\userlist;
use core_privacy\local\request\approved_userlist;
/**
* Privacy Subsystem for qbank_comment.
*
* @package qbank_comment
* @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
// The qbank_comment stores user provided data.
\core_privacy\local\metadata\provider,
\core_privacy\local\request\core_userlist_provider,
// The qbank_comment provides data directly to core.
\core_privacy\local\request\plugin\provider {
/**
* Returns meta data about this system.
*
* @param collection $collection
* @return collection
*/
public static function get_metadata(collection $collection): collection {
return $collection->add_subsystem_link('core_comment', [], 'privacy:metadata:core_comment');
}
/**
* Get the list of contexts that contain user information for the specified user.
*
* @param int $userid
* @return contextlist
*/
public static function get_contexts_for_userid(int $userid): contextlist {
$contextlist = new contextlist();
$sql = "SELECT contextid
FROM {comments}
WHERE component = :component
AND userid = :userid";
$params = [
'area' => 'question',
'component' => 'qbank_comment',
'userid' => $userid
];
$contextlist->add_from_sql($sql, $params);
return $contextlist;
}
/**
* Get the list of users within a specific context.
*
* @param userlist $userlist The userlist containing the list of users who have data in this context/plugin combination.
*/
public static function get_users_in_context(userlist $userlist) {
$context = $userlist->get_context();
$params = [
'contextid' => $context->id,
'area' => 'question',
'component' => 'qbank_comment'
];
$sql = "SELECT userid as userid
FROM {comments}
WHERE component = :component
AND contextid = :contextid";
$userlist->add_from_sql('userid', $sql, $params);
}
/**
* Export all user data for the specified user, in the specified contexts.
*
* @param approved_contextlist $contextlist
*/
public static function export_user_data(approved_contextlist $contextlist) {
$contexts = $contextlist->get_contexts();
foreach ($contexts as $context) {
\core_comment\privacy\provider::export_comments(
$context,
'qbank_comment',
'question',
0,
[]
);
}
}
/**
* Delete all data for all users in the specified context.
*
* @param \context $context
*/
public static function delete_data_for_all_users_in_context(\context $context) {
\core_comment\privacy\provider::delete_comments_for_all_users($context, 'qbank_comment');
}
/**
* Delete multiple users within a single context.
*
* @param approved_userlist $userlist The approved context and user information to delete information for.
*/
public static function delete_data_for_users(approved_userlist $userlist) {
\core_comment\privacy\provider::delete_comments_for_users($userlist, 'qbank_comment');
}
/**
* Delete all user data for the specified user, in the specified contexts.
*
* @param approved_contextlist $contextlist
*/
public static function delete_data_for_user(approved_contextlist $contextlist) {
\core_comment\privacy\provider::delete_comments_for_user($contextlist, 'qbank_comment');
}
}

View File

@ -0,0 +1,50 @@
<?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/>.
/**
* Capability definitions for this module.
*
* @package qbank_comment
* @copyright 2021 Catalyst IT Australia Pty Ltd
* @author Matt Porritt <mattp@catalyst-au.net>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$capabilities = [
// Controls whether users can comment their own questions.
'moodle/question:commentmine' => [
'captype' => 'write',
'contextlevel' => CONTEXT_COURSE,
'archetypes' => [
'editingteacher' => CAP_ALLOW,
'manager' => CAP_ALLOW
],
'clonepermissionsfrom' => 'moodle/question:editmine'
],
// Controls whether users can comment all questions.
'moodle/question:commentall' => [
'captype' => 'write',
'contextlevel' => CONTEXT_COURSE,
'archetypes' => [
'editingteacher' => CAP_ALLOW,
'manager' => CAP_ALLOW
],
'clonepermissionsfrom' => 'moodle/question:editall'
],
];

View File

@ -0,0 +1,41 @@
<?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_comment, language 'en'.
*
* @package qbank_comment
* @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 comment';
$string['privacy:metadata:core_comment'] = 'Question comment plugin helps users with permission to comment in a question.';
// Column.
$string['comment'] = 'Comment';
$string['commentplural'] = 'Comments';
// Modal.
$string['addcomment'] = 'Add comment';
$string['close'] = 'Close';
$string['commentheader'] = 'Question comments';
$string['commentdisabled'] = 'Comment feature is disabled "sitewide",
please ask your "Site administrator" to enable "usecomments" from "Advanced settings" in order to comment in this question.';
// Events.
$string['comment_added'] = 'The user with id \'{$a->userid}\' added the comment with id \'{$a->objectid}\'
to the \'{$a->component}\' for the question with id \'{$a->itemid}\'.';
$string['comment_removed'] = 'The user with id \'{$a->userid}\' deleted the comment with id \'{$a->objectid}\'
to the \'{$a->component}\' for the question with id \'{$a->itemid}\'.';

View File

@ -0,0 +1,138 @@
<?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_comment
* @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();
/**
* Validate comment parameter before perform other comments actions.
*
* @param stdClass $commentparam
* {
* context => context the context object
* courseid => int course id
* cm => stdClass course module object
* commentarea => string comment area
* itemid => int itemid
* }
* @return boolean
*/
function qbank_comment_comment_validate($commentparam): bool {
if ($commentparam->commentarea != 'question' && $commentparam->component != 'qbank_comment') {
throw new comment_exception('invalidcommentarea');
}
return true;
}
/**
* Running additional permission check on plugins.
*
* @param stdClass $args
* @return array
*/
function qbank_comment_comment_permissions($args): array {
return ['post' => true, 'view' => true];
}
/**
* Validate comment data before displaying comments.
*
* @param array $comments
* @param stdClass $args
* @return array $comments
*/
function qbank_comment_comment_display($comments, $args): array {
if ($args->commentarea != 'question' && $args->component != 'qbank_comment') {
throw new comment_exception('core_question');
}
return $comments;
}
/**
* Comment content for callbacks.
*
* @param question_definition $question
* @param int $courseid
* @return string
*/
function qbank_comment_preview_display($question, $courseid): string {
global $CFG, $PAGE;
if (question_has_capability_on($question, 'comment') && $CFG->usecomments
&& core\plugininfo\qbank::is_plugin_enabled('qbank_comment')) {
\comment::init($PAGE);
$args = new \stdClass;
$args->contextid = 1; // Static data to bypass comment sql as context is not needed.
$args->courseid = $courseid;
$args->area = 'question';
$args->itemid = $question->id;
$args->component = 'qbank_comment';
$args->notoggle = true;
$args->autostart = true;
$args->displaycancel = false;
$args->linktext = get_string('commentheader', 'qbank_comment');
$comment = new \comment($args);
$comment->set_view_permission(true);
$comment->set_fullwidth();
return $comment->output();
} else {
return '';
}
}
/**
* Question comment fragment callback.
*
* @param array $args
* @return string rendered output
* @todo cleanup after class renaming to remove check for previewlib.php MDL-71679
*/
function qbank_comment_output_fragment_question_comment($args): string {
global $USER, $PAGE, $CFG;
$displaydata = [];
require_once($CFG->dirroot . '/question/engine/bank.php');
$question = question_bank::load_question($args['questionid']);
$quba = question_engine::make_questions_usage_by_activity(
'core_question_preview', context_user::instance($USER->id));
if (class_exists('\\qbank_previewquestion\\question_preview_options')) {
$options = new \qbank_previewquestion\question_preview_options($question);
} else {
require_once($CFG->dirroot . '/question/previewlib.php');
$options = new 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');
$displaydata['comment'] = qbank_comment_preview_display($question, $args['courseid']);
$displaydata['commenstdisabled'] = false;
if (empty($displaydata['comment']) && !$CFG->usecomments) {
$displaydata['commenstdisabled'] = true;
}
return $PAGE->get_renderer('qbank_comment')->render_comment_fragment($displaydata);
}

View File

@ -0,0 +1,9 @@
/* Comment text area size to maximum */
.question-comment-view .comment-ctrl .comment-area {
max-width: none;
width: 100%;
}
.comment-pointer {
cursor: pointer;
}

View File

@ -0,0 +1,47 @@
{{!
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_comment/comment_modal
The template that renders the modal window for adding comments to questions.
* question - The html of the question.
* comment - The html of the comment.
* commenstdisabled - If true display the comment disabled notification.
Example context (json):
{
"commentdata": [
{
"question": "question html",
"comment": "comment html",
"commenstdisabled": "bool"
}
]
}
}}
<div class="question-comment-preview">
{{{question}}}
</div>
<div class="question-comment-view">
{{{comment}}}
{{#commenstdisabled}}
<a class="alert-danger">
{{#str}} commentdisabled, qbank_comment {{/str}}
</a>
{{/commenstdisabled}}
</div>

View File

@ -0,0 +1,209 @@
<?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/>.
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php');
/**
* Question comment backup and restore unit tests.
*
* @package qbank_comment
* @copyright 2021 Catalyst IT Australia Pty Ltd
* @author Matt Porritt <mattp@catalyst-au.net>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class qbank_comment_backup_restore_test extends \advanced_testcase {
/**
* @var array Data object for generating a question.
*/
protected $question1data;
/**
* @var array Data object for generating a question.
*/
protected $question2data;
/**
* @var component_generator_base Question Generator.
*/
protected $qgen;
/**
* @var core_course_category Course category.
*/
protected $category;
/**
* @var stdClass Course object.
*/
protected $course;
/**
* Set up
*/
protected function setUp(): void {
parent::setUp();
$this->resetAfterTest();
$this->setAdminUser();
// Set up custom fields.
$data = new \stdClass();
$data->component = 'qbank_comment';
$data->area = 'question';
// Question initial set up.
$this->category = $this->getDataGenerator()->create_category();
$this->course = $this->getDataGenerator()->create_course(['category' => $this->category->id]);
$context = context_coursecat::instance($this->category->id);
$this->qgen = $this->getDataGenerator()->get_plugin_generator('core_question');
$qcat = $this->qgen->create_question_category(['contextid' => $context->id]);
$this->question1data = ['category' => $qcat->id, 'idnumber' => 'q1'];
$this->question2data = ['category' => $qcat->id, 'idnumber' => 'q2'];
}
/**
* Makes a backup of the course.
*
* @param stdClass $course The course object.
* @return string Unique identifier for this backup.
*/
protected function backup_course(\stdClass $course): string {
global $CFG, $USER;
// Turn off file logging, otherwise it can't delete the file (Windows).
$CFG->backup_file_logger_level = backup::LOG_NONE;
// Do backup with default settings. MODE_IMPORT means it will just
// create the directory and not zip it.
$bc = new backup_controller(backup::TYPE_1COURSE, $course->id,
backup::FORMAT_MOODLE, backup::INTERACTIVE_NO, backup::MODE_IMPORT,
$USER->id);
$backupid = $bc->get_backupid();
$bc->execute_plan();
$bc->destroy();
return $backupid;
}
/**
* Restores a backup that has been made earlier.
*
* @param string $backupid The unique identifier of the backup.
* @param string $fullname Full name of the new course that is going to be created.
* @param string $shortname Short name of the new course that is going to be created.
* @param int $categoryid The course category the backup is going to be restored in.
* @return int The new course id.
*/
protected function restore_course(string $backupid, string $fullname, string $shortname, int $categoryid): int {
global $CFG, $USER;
// Turn off file logging, otherwise it can't delete the file (Windows).
$CFG->backup_file_logger_level = backup::LOG_NONE;
// Do restore to new course with default settings.
$newcourseid = restore_dbops::create_new_course($fullname, $shortname, $categoryid);
$rc = new restore_controller($backupid, $newcourseid,
backup::INTERACTIVE_NO, backup::MODE_GENERAL, $USER->id,
backup::TARGET_NEW_COURSE);
$rc->execute_precheck();
$rc->execute_plan();
$rc->destroy();
return $newcourseid;
}
/**
* Test comments attached to questions persist
* across the backup and restore process.
*/
public function test_backup_restore() {
global $DB;
$this->resetAfterTest();
$this->setAdminUser();
$courseshortname = $this->course->shortname;
$coursefullname = $this->course->fullname;
// Create 2 questions.
$question1 = $this->qgen->create_question('shortanswer', null, $this->question1data);
$question2 = $this->qgen->create_question('shortanswer', null, $this->question2data);
// Add comments to the questions.
$coursecontext = \context_course::instance($this->course->id);
$args = new stdClass;
$args->context = $coursecontext;
$args->course = $this->course;
$args->area = 'question';
$args->itemid = $question1->id;
$args->component = 'qbank_comment';
$args->linktext = get_string('commentheader', 'qbank_comment');
$args->notoggle = true;
$args->autostart = true;
$args->displaycancel = false;
// Two comments for question 1.
$commentobj1 = new \comment($args);
$commentobj1->add('new comment for question 1 _ 1');
$comment1 = $commentobj1->add('new comment for question 1 _ 2');
// One comment for question 2.
$args->itemid = $question2->id;
$commentobj2 = new \comment($args);
$comment2 = $commentobj2->add('new comment for question 2');
// Create a quiz and the questions to that.
$quiz = $this->getDataGenerator()->create_module(
'quiz', ['course' => $this->course->id, 'name' => 'restored_quiz']);
quiz_add_quiz_question($question1->id, $quiz);
quiz_add_quiz_question($question2->id, $quiz);
// Backup the course.
$backupid = $this->backup_course($this->course);
// Now delete everything.
delete_course($this->course, false);
question_delete_question($question1->id);
question_delete_question($question2->id);
// Check the comment data for the questions has also gone.
$DB->record_exists('comments', ['id' => $comment1->id]);
$this->assertFalse($DB->record_exists('comments', ['id' => $comment1->id]));
$this->assertFalse($DB->record_exists('comments', ['id' => $comment2->id]));
// Restore the backup we had made earlier into a new course.
$newcategory = $this->getDataGenerator()->create_category();
$this->restore_course($backupid, $coursefullname, $courseshortname . '_2', $newcategory->id);
// The questions and their associated comments should have been restored.
$newquestion1 = $DB->get_record('question', ['idnumber' => 'q1']);
$args->itemid = $newquestion1->id;
$commentobj = new \comment($args);
$this->assertEquals($commentobj->count(), 2);
$newquestion2 = $DB->get_record('question', ['idnumber' => 'q2']);
$args->itemid = $newquestion2->id;
$commentobj = new \comment($args);
$this->assertEquals($commentobj->count(), 1);
}
}

View File

@ -0,0 +1,143 @@
<?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/>.
/**
* Commenting system steps definitions for question.
*
* @package qbank_comment
* @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
*/
// 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 commenting system in question.
*
* @package qbank_comment
* @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_comment 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 row on the comments column
* @param string $linkname
* @param string $rowtext
*/
public function i_click_on_the_row_containing($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.commentcount a:contains("%s")', $linkname), $exception);
$row->click();
}
/**
* Looks for the appropriate comment count in the column.
*
* @Then I should see :arg1 on the comments column
* @param string $linkdata
*/
public function i_should_see_on_the_column($linkdata) {
$exception = new ElementNotFoundException($this->getSession(),
'Cannot find any row with the comment count of ' . $linkdata . ' on the column named Comments');
$this->find('css', sprintf('table tbody tr td.commentcount a:contains("%s")', $linkdata), $exception);
}
/**
* Adds the specified option to the question comments of the current modal.
*
* @Then I add :arg1 comment to question
* @param string $comment
*/
public function i_add_comment_to_question($comment) {
// Getting the textarea and setting the provided value.
$exception = new ElementNotFoundException($this->getSession(), 'Question ');
if ($this->running_javascript()) {
$commentstextarea = $this->find('css',
'.modal-dialog .question-comment-view .comment-area textarea', $exception);
$commentstextarea->setValue($comment);
// We delay 1 second which is all we need.
$this->getSession()->wait(1000);
} else {
throw new ExpectationException('JavaScript not running', $this->getSession());
}
}
/**
* Adds the specified option to the question comments of the question preview.
*
* @Then I add :arg1 comment to question preview
* @param string $comment
*/
public function i_add_comment_to_question_preview($comment) {
// Getting the textarea and setting the provided value.
$exception = new ElementNotFoundException($this->getSession(), 'Question ');
if ($this->running_javascript()) {
$commentstextarea = $this->find('css',
'.question-comment-view .comment-area textarea', $exception);
$commentstextarea->setValue($comment);
// We delay 1 second which is all we need.
$this->getSession()->wait(1000);
} else {
throw new ExpectationException('JavaScript not running', $this->getSession());
}
}
/**
* Deletes the specified comment from the current question comment modal.
*
* @Then I delete :arg comment from question
* @param string $comment
*/
public function i_delete_comment_from_question($comment) {
$exception = new ElementNotFoundException($this->getSession(), '"' . $comment . '" comment ');
// Using xpath liternal to avoid possible problems with comments containing quotes.
$commentliteral = behat_context_helper::escape($comment);
$commentxpath = "//*[contains(concat(' ', normalize-space(@class), ' '), ' question-comment-view ')]" .
"/descendant::div[@class='comment-message'][contains(., $commentliteral)]";
// Click on delete icon.
$this->execute('behat_general::i_click_on_in_the',
["Delete comment posted by", "icon", $this->escape($commentxpath), "xpath_element"]
);
// Wait for the animation to finish, in theory is just 1 sec, adding 4 just in case.
$this->getSession()->wait(4 * 1000);
}
}

View File

@ -0,0 +1,114 @@
@qbank @qbank_comment @javascript
Feature: A Teacher can comment in a question
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | T1 | Teacher1 | teacher1@example.com |
| teacher2 | T2 | Teacher2 | teacher2@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| teacher2 | C1 | editingteacher |
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 |
@javascript
Scenario: Add a comment in question
Given I log in as "teacher1"
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 "0" on the comments column
When I click "0" on the row on the comments column
And I add "Super test comment 01" comment to question
And I click on "Add comment" "button" in the ".modal-dialog" "css_element"
And I should see "Super test comment 01"
And I click on "Close" "button" in the ".modal-dialog" "css_element"
Then I should see "1" on the comments column
@javascript
Scenario: Delete a comment from question
Given I log in as "teacher1"
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 "0" on the comments column
When I click "0" on the row on the comments column
And I add "Super test comment 01 to be deleted" comment to question
And I click on "Add comment" "button" in the ".modal-dialog" "css_element"
And I should see "Super test comment 01 to be deleted"
And I click on "Close" "button" in the ".modal-dialog" "css_element"
Then I should see "1" on the comments column
And I click "1" on the row on the comments column
And I delete "Super test comment 01 to be deleted" comment from question
And I should not see "Super test comment 01 to be deleted"
And I click on "Close" "button" in the ".modal-dialog" "css_element"
But I should see "0" on the comments column
@javascript
Scenario: Preview question with comments
Given I log in as "teacher1"
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 choose "Preview" action for "First question" in the question bank
Then I should see "Save comment"
And I add "Super test comment 01" comment to question preview
And I click on "Save comment" "link"
And I wait "1" seconds
Then I should see "Super test comment 01"
And I click on "Close preview" "button"
Then I should see "1" on the comments column
And I choose "Preview" action for "First question" in the question bank
And I delete "Super test comment 01" comment from question
And I should not see "Super test comment 01"
And I click on "Close preview" "button"
Then I should see "0" on the comments column
@javascript
Scenario: Teacher with comment permissions for their own questions but not others questions
Given I log in as "admin"
And I set the following system permissions of "Teacher" role:
| capability | permission |
| moodle/question:commentmine | Allow |
| moodle/question:commentall | Prevent |
And I log out
Then I log in as "teacher1"
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 choose "Preview" action for "First question" in the question bank
Then I should not see "Save comment"
And I click on "Close preview" "button"
Then I click on "Create a new question ..." "button"
And I set the field "item_qtype_essay" to "1"
And I press "submitbutton"
Then I should see "Adding an Essay question"
And I set the field "Question name" to "Essay 01 new"
And I set the field "Question text" to "Please write 200 words about Essay 01"
And I press "id_submitbutton"
Then I should see "Essay 01 new"
And I choose "Preview" action for "Essay 01 new" in the question bank
Then I should see "Save comment"
And I log out
Then I log in as "teacher2"
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 choose "Preview" action for "First question" in the question bank
Then I should not see "Save comment"
And I click on "Close preview" "button"
And I choose "Preview" action for "Essay 01 new" in the question bank
Then I should not see "Save comment"
And I click on "Close preview" "button"

View File

@ -0,0 +1,32 @@
@qbank @qbank_comment
Feature: Use the qbank plugin manager page for comment
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 |
@javascript
Scenario: Enable/disable comment column from the base view
Given I log in as "admin"
When I navigate to "Plugins > Question bank plugins > Manage question bank plugins" in site administration
And I should see "Question comment"
And I click on "Disable" "link" in the "Question comment" "table_row"
And I am on the "Test quiz" "quiz activity" page
And I navigate to "Question bank > Questions" in current page administration
Then "#categoryquestions .header.commentcount" "css_element" should not be visible
And I navigate to "Plugins > Question bank plugins > Manage question bank plugins" in site administration
And I click on "Enable" "link" in the "Question comment" "table_row"
And I am on the "Test quiz" "quiz activity" page
And I navigate to "Question bank > Questions" in current page administration
And "#categoryquestions .header.commentcount" "css_element" should be visible

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/>.
namespace qbank_comment\event;
use advanced_testcase;
use cache;
use comment;
use context;
use context_course;
use core_question_generator;
use question_edit_contexts;
use stdClass;
/**
* Event tests for question comments.
*
* @package qbank_comment
* @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 qbank_comment_comment_created_deleted_test extends advanced_testcase {
/** @var stdClass Keeps course object */
private $course;
/** @var context Keeps context */
private $context;
/** @var stdClass Keeps question object */
private $questiondata;
/** @var stdClass Keeps comment object */
private $comment;
/**
* Setup test data.
*/
public function setUp(): void {
global $CFG;
require_once($CFG->dirroot . '/comment/lib.php');
$this->resetAfterTest();
$this->setAdminUser();
$generator = $this->getDataGenerator();
/** @var core_question_generator $questiongenerator */
$questiongenerator = $generator->get_plugin_generator('core_question');
// Create a course.
$this->course = $generator->create_course();
$this->context = context_course::instance($this->course->id);
// Create a question in the default category.
$contexts = new question_edit_contexts($this->context);
$cat = question_make_default_categories($contexts->all());
$this->questiondata = $questiongenerator->create_question('numerical', null,
['name' => 'Example question', 'category' => $cat->id]);
// Ensure the question is not in the cache.
$cache = cache::make('core', 'questiondata');
$cache->delete($this->questiondata->id);
// Comment on question.
$args = new stdClass;
$args->context = $this->context;
$args->course = $this->course;
$args->area = 'question';
$args->itemid = $this->questiondata->id;
$args->component = 'qbank_comment';
$args->linktext = get_string('commentheader', 'qbank_comment');
$args->notoggle = true;
$args->autostart = true;
$args->displaycancel = false;
$this->comment = new comment($args);
}
/**
* Test comment_created event.
*/
public function test_comment_created() {
// Triggering and capturing the event.
$sink = $this->redirectEvents();
$this->comment->add('New comment');
$events = $sink->get_events();
$this->assertCount(1, $events);
$event = reset($events);
// Checking that the event contains the expected values.
$this->assertInstanceOf('\qbank_comment\event\comment_created', $event);
$this->assertEquals($this->context, $event->get_context());
$this->assertStringContainsString('\'qbank_comment\' for the question with id \''.$this->questiondata->id.'\'',
$event->get_description());
}
/**
* Test comment_created event.
*/
public function test_comment_deleted() {
// Triggering and capturing the event.
$newcomment = $this->comment->add('New comment to delete');
$sink = $this->redirectEvents();
$this->comment->delete($newcomment->id);
$events = $sink->get_events();
$this->assertCount(1, $events);
$event = reset($events);
// Checking that the event contains the expected values.
$this->assertInstanceOf('\qbank_comment\event\comment_deleted', $event);
$this->assertEquals($this->context, $event->get_context());
$this->assertStringContainsString('\'qbank_comment\' for the question with id \''.$this->questiondata->id.'\'',
$event->get_description());
}
}

View File

@ -0,0 +1,126 @@
<?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/>.
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->dirroot . '/question/bank/comment/lib.php');
/**
* Comment lib unit tests.
*
* @package qbank_comment
* @copyright 2021 Catalyst IT Australia Pty Ltd
* @author Matt Porritt <mattp@catalyst-au.net>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class qbank_comment_lib_test extends \advanced_testcase {
/**
* Test the comment validation callback.
*/
public function test_qbank_comment_comment_validate() {
$commentparams = new \stdClass();
$commentparams->commentarea = 'question';
$commentparams->component = 'qbank_comment';
$isvalid = qbank_comment_comment_validate($commentparams);
$this->assertTrue($isvalid);
$this->expectException('comment_exception');
$commentparams->commentarea = 'core_comment';
$commentparams->component = 'blog_comment';
qbank_comment_comment_validate($commentparams);
}
/**
* Test the comment display callback.
*/
public function test_qbank_comment_comment_display() {
$comment = new \stdClass();
$comment->text = 'test';
$comments = [$comment];
$commentparams = new \stdClass();
$commentparams->commentarea = 'question';
$commentparams->component = 'qbank_comment';
$responses = qbank_comment_comment_display($comments, $commentparams);
$this->assertEquals($comment->text, $responses[0]->text);
$this->expectException('comment_exception');
$commentparams->commentarea = 'core_comment';
$commentparams->component = 'blog_comment';
qbank_comment_comment_display($comments, $commentparams);
}
/**
* Test the comment preview callback.
*/
public function test_qbank_comment_preview_display() {
$this->resetAfterTest();
global $PAGE;
$PAGE->set_url('/');
// Make a test question.
$category = $this->getDataGenerator()->create_category();
$course = $this->getDataGenerator()->create_course(['category' => $category->id]);
$qgen = $this->getDataGenerator()->get_plugin_generator('core_question');
$context = context_coursecat::instance($category->id);
$qcat = $qgen->create_question_category(['contextid' => $context->id]);
$question = $qgen->create_question('shortanswer', null, ['category' => $qcat->id, 'idnumber' => 'q1']);
$result = qbank_comment_preview_display($question, $course->id);
// User doesn't have perms so expecting no output.
$this->assertEmpty($result);
// Expect output.
$this->setAdminUser();
$result = qbank_comment_preview_display($question, $course->id);
$this->assertStringContainsString('comment-action-post', $result);
}
/**
* Test the comment preview callback.
*/
public function test_qbank_comment_output_fragment_question_comment() {
$this->resetAfterTest();
$this->setAdminUser();
global $PAGE;
$PAGE->set_url('/');
// Make a test question.
$category = $this->getDataGenerator()->create_category();
$course = $this->getDataGenerator()->create_course(['category' => $category->id]);
$qgen = $this->getDataGenerator()->get_plugin_generator('core_question');
$context = context_coursecat::instance($category->id);
$qcat = $qgen->create_question_category(['contextid' => $context->id]);
$question = $qgen->create_question('shortanswer', null, ['category' => $qcat->id, 'idnumber' => 'q1']);
$args = [
'questionid' => $question->id,
'courseid' => $course->id,
];
$result = qbank_comment_output_fragment_question_comment($args);
// Expect output.
$this->assertStringContainsString('comment-action-post', $result);
}
}

View File

@ -0,0 +1,358 @@
<?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_comment\privacy;
use comment;
use context;
use context_course;
use core_privacy\local\metadata\collection;
use qbank_comment\privacy\provider;
use core_privacy\local\request\approved_userlist;
use stdClass;
/**
* Privacy api tests.
*
* @package qbank_comment
* @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 qbank_comment_provider_test extends \core_privacy\tests\provider_testcase {
/** @var stdClass A teacher who is only enrolled in course1. */
protected $teacher1;
/** @var stdClass A teacher who is only enrolled in course2. */
protected $teacher2;
/** @var stdClass A teacher who is enrolled in both course1 and course2. */
protected $teacher3;
/** @var stdClass A test course. */
protected $course1;
/** @var stdClass A test course. */
protected $course2;
/**
* Set up function for tests in this class.
*/
protected function setUp(): void {
global $DB;
$this->resetAfterTest();
$this->setAdminUser();
// Create courses.
$generator = $this->getDataGenerator();
$this->course1 = $generator->create_course();
$this->course2 = $generator->create_course();
// Create and enrol teachers.
$this->teacher1 = $generator->create_user();
$this->teacher2 = $generator->create_user();
$this->teacher3 = $generator->create_user();
$studentrole = $DB->get_record('role', ['shortname' => 'editingteacher']);
$generator->enrol_user($this->teacher1->id, $this->course1->id, $studentrole->id);
$generator->enrol_user($this->teacher2->id, $this->course2->id, $studentrole->id);
$generator->enrol_user($this->teacher3->id, $this->course1->id, $studentrole->id);
$generator->enrol_user($this->teacher3->id, $this->course2->id, $studentrole->id);
}
/**
* Posts a comment on a given context.
*
* @param string $text The comment's text.
* @param context $context The context on which we want to put the comment.
*/
protected function add_comment($text, context $context) {
$args = new stdClass;
$args->context = $context;
$args->area = 'question';
$args->itemid = 0;
$args->component = 'qbank_comment';
$args->linktext = get_string('commentheader', 'qbank_comment');
$args->notoggle = true;
$args->autostart = true;
$args->displaycancel = false;
$comment = new comment($args);
$comment->add($text);
}
/**
* Test for provider::get_metadata().
*/
public function test_get_metadata() {
$collection = new collection('qbank_comment');
$newcollection = provider::get_metadata($collection);
$itemcollection = $newcollection->get_collection();
$this->assertCount(1, $itemcollection);
$link = reset($itemcollection);
$this->assertEquals('core_comment', $link->get_name());
$this->assertEmpty($link->get_privacy_fields());
$this->assertEquals('privacy:metadata:core_comment', $link->get_summary());
}
/**
* Test for provider::get_contexts_for_userid() when user had not posted any comments..
*/
public function test_get_contexts_for_userid_no_comment() {
$this->setUser($this->teacher1);
$coursecontext1 = context_course::instance($this->course1->id);
$this->add_comment('New comment', $coursecontext1);
$this->setUser($this->teacher2);
$contextlist = provider::get_contexts_for_userid($this->teacher2->id);
$this->assertCount(0, $contextlist);
}
/**
* Test for provider::get_contexts_for_userid().
*/
public function test_get_contexts_for_userid() {
$coursecontext1 = context_course::instance($this->course1->id);
$coursecontext2 = context_course::instance($this->course2->id);
$this->setUser($this->teacher3);
$this->add_comment('New comment', $coursecontext1);
$this->add_comment('New comment', $coursecontext1);
$this->add_comment('New comment', $coursecontext2);
$contextlist = provider::get_contexts_for_userid($this->teacher3->id);
$this->assertCount(2, $contextlist);
$contextids = $contextlist->get_contextids();
$this->assertEqualsCanonicalizing([$coursecontext1->id, $coursecontext2->id], $contextids);
}
/**
* Test for provider::export_user_data() when the user has not posted any comments.
*/
public function test_export_for_context_no_comment() {
$coursecontext1 = context_course::instance($this->course1->id);
$coursecontext2 = context_course::instance($this->course2->id);
$this->setUser($this->teacher1);
$this->add_comment('New comment', $coursecontext1);
$this->setUser($this->teacher2);
$this->setUser($this->teacher2);
$this->export_context_data_for_user($this->teacher1->id, $coursecontext2, 'qbank_comment');
$writer = \core_privacy\local\request\writer::with_context($coursecontext2);
$this->assertFalse($writer->has_any_data());
}
/**
* Test for provider::export_user_data().
*/
public function test_export_for_context() {
$coursecontext1 = context_course::instance($this->course1->id);
$coursecontext2 = context_course::instance($this->course2->id);
$this->setUser($this->teacher3);
$this->add_comment('New comment', $coursecontext1);
$this->add_comment('New comment', $coursecontext1);
$this->add_comment('New comment', $coursecontext2);
// Export all of the data for the context.
$this->export_context_data_for_user($this->teacher3->id, $coursecontext1, 'qbank_comment');
$writer = \core_privacy\local\request\writer::with_context($coursecontext1);
$this->assertTrue($writer->has_any_data());
}
/**
* Test for provider::delete_data_for_all_users_in_context().
*/
public function test_delete_data_for_all_users_in_context() {
global $DB;
$coursecontext1 = context_course::instance($this->course1->id);
$coursecontext2 = context_course::instance($this->course2->id);
$this->setUser($this->teacher1);
$this->add_comment('New comment', $coursecontext1);
$this->setUser($this->teacher2);
$this->add_comment('New comment', $coursecontext2);
$this->setUser($this->teacher3);
$this->add_comment('New comment', $coursecontext1);
$this->add_comment('New comment', $coursecontext1);
$this->add_comment('New comment', $coursecontext2);
// Before deletion, we should have 3 comments in $coursecontext1 and 2 comments in $coursecontext2.
$this->assertEquals(
3,
$DB->count_records('comments', ['component' => 'qbank_comment', 'contextid' => $coursecontext1->id])
);
$this->assertEquals(
2,
$DB->count_records('comments', ['component' => 'qbank_comment', 'contextid' => $coursecontext2->id])
);
// Delete data based on context.
provider::delete_data_for_all_users_in_context($coursecontext1);
// After deletion, the comments for $coursecontext1 should have been deleted.
$this->assertEquals(
0,
$DB->count_records('comments', ['component' => 'qbank_comment', 'contextid' => $coursecontext1->id])
);
$this->assertEquals(
2,
$DB->count_records('comments', ['component' => 'qbank_comment', 'contextid' => $coursecontext2->id])
);
}
/**
* Test for provider::delete_data_for_user().
*/
public function test_delete_data_for_user() {
global $DB;
$coursecontext1 = context_course::instance($this->course1->id);
$coursecontext2 = context_course::instance($this->course2->id);
$this->setUser($this->teacher1);
$this->add_comment('New comment', $coursecontext1);
$this->setUser($this->teacher2);
$this->add_comment('New comment', $coursecontext2);
$this->setUser($this->teacher3);
$this->add_comment('New comment', $coursecontext1);
$this->add_comment('New comment', $coursecontext1);
$this->add_comment('New comment', $coursecontext2);
// Before deletion, we should have 3 comments in $coursecontext1 and 2 comments in $coursecontext2,
// and 3 comments by student12 in $coursecontext1 and $coursecontext2 combined.
$this->assertEquals(
3,
$DB->count_records('comments', ['component' => 'qbank_comment', 'contextid' => $coursecontext1->id])
);
$this->assertEquals(
2,
$DB->count_records('comments', ['component' => 'qbank_comment', 'contextid' => $coursecontext2->id])
);
$this->assertEquals(
3,
$DB->count_records('comments', ['component' => 'qbank_comment', 'userid' => $this->teacher3->id])
);
$contextlist = new \core_privacy\local\request\approved_contextlist($this->teacher3, 'qbank_comment',
[$coursecontext1->id, $coursecontext2->id]);
provider::delete_data_for_user($contextlist);
// After deletion, the comments for the student12 should have been deleted.
$this->assertEquals(
1,
$DB->count_records('comments', ['component' => 'qbank_comment', 'contextid' => $coursecontext1->id])
);
$this->assertEquals(
1,
$DB->count_records('comments', ['component' => 'qbank_comment', 'contextid' => $coursecontext2->id])
);
$this->assertEquals(
0,
$DB->count_records('comments', ['component' => 'qbank_comment', 'userid' => $this->teacher3->id])
);
}
/**
* Test that only users within a course context are fetched.
*/
public function test_get_users_in_context() {
$component = 'qbank_comment';
$coursecontext1 = context_course::instance($this->course1->id);
$coursecontext2 = context_course::instance($this->course2->id);
$userlist1 = new \core_privacy\local\request\userlist($coursecontext1, $component);
provider::get_users_in_context($userlist1);
$this->assertCount(0, $userlist1);
$userlist2 = new \core_privacy\local\request\userlist($coursecontext2, $component);
provider::get_users_in_context($userlist2);
$this->assertCount(0, $userlist2);
$this->setUser($this->teacher3);
$this->add_comment('New comment', $coursecontext1);
$this->add_comment('New comment', $coursecontext2);
$this->setUser($this->teacher1);
$this->add_comment('New comment', $coursecontext1);
// The list of users should contain teacher3 and user1.
provider::get_users_in_context($userlist1);
$this->assertCount(2, $userlist1);
$this->assertTrue(in_array($this->teacher1->id, $userlist1->get_userids()));
$this->assertTrue(in_array($this->teacher3->id, $userlist1->get_userids()));
// The list of users should contain teacher3.
provider::get_users_in_context($userlist2);
$this->assertCount(1, $userlist2);
$expected = [$this->teacher3->id];
$actual = $userlist2->get_userids();
$this->assertEquals($expected, $actual);
}
/**
* Test that data for users in approved userlist is deleted.
*/
public function test_delete_data_for_users() {
$component = 'qbank_comment';
$coursecontext1 = context_course::instance($this->course1->id);
$coursecontext2 = context_course::instance($this->course2->id);
$this->setUser($this->teacher3);
$this->add_comment('New comment', $coursecontext1);
$this->add_comment('New comment', $coursecontext2);
$this->setUser($this->teacher1);
$this->add_comment('New comment', $coursecontext1);
$userlist1 = new \core_privacy\local\request\userlist($coursecontext1, $component);
provider::get_users_in_context($userlist1);
$this->assertCount(2, $userlist1);
$userlist2 = new \core_privacy\local\request\userlist($coursecontext2, $component);
provider::get_users_in_context($userlist2);
$this->assertCount(1, $userlist2);
// Convert $userlist1 into an approved_contextlist.
$approvedlist1 = new approved_userlist($coursecontext1, $component, $userlist1->get_userids());
// Delete using delete_data_for_user.
provider::delete_data_for_users($approvedlist1);
// Re-fetch users in coursecontext1.
$userlist1 = new \core_privacy\local\request\userlist($coursecontext1, $component);
provider::get_users_in_context($userlist1);
// The user data in coursecontext1 should be deleted.
$this->assertCount(0, $userlist1);
// Re-fetch users in coursecontext2.
$userlist2 = new \core_privacy\local\request\userlist($coursecontext2, $component);
provider::get_users_in_context($userlist2);
// The user data in coursecontext2 should be still present.
$this->assertCount(1, $userlist2);
}
}

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_comment.
*
* @package qbank_comment
* @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_comment';
$plugin->version = 2021090100;
$plugin->requires = 2021052500;
$plugin->maturity = MATURITY_STABLE;

View File

@ -27,7 +27,7 @@ use core\plugininfo\qbank;
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @coversDefaultClass \qbank_previewquestion\helper
*/
class helper_test extends \advanced_testcase {
class qbank_preview_helper_test extends \advanced_testcase {
/**
* @var bool|\context|\context_course $context
@ -148,10 +148,13 @@ class helper_test extends \advanced_testcase {
* @covers ::get_preview_extra_elements
*/
public function test_get_preview_extra_elements() {
global $PAGE;
$PAGE->set_url('/');
$question = \question_bank::load_question($this->questiondata->id);
list($comment, $extraelements) = helper::get_preview_extra_elements($question, $this->context->instanceid);
if (qbank::is_plugin_enabled('qbank_comment')) {
$this->assertEquals("<div class='comment-area'>", $comment);
$this->assertStringContainsString("comment-area", $comment);
} else {
$this->assertEquals('', $comment);
}

View File

@ -29,7 +29,7 @@
defined('MOODLE_INTERNAL') || die();
$version = 2021100600.00; // YYYYMMDD = weekly release date of this DEV branch.
$version = 2021100600.01; // YYYYMMDD = weekly release date of this DEV branch.
// RR = release increments - 00 in DEV branches.
// .XX = incremental changes.
$release = '4.0dev+ (Build: 20211006)'; // Human-friendly version name