Merge branch 'MDL-63806-310' of git://github.com/jleyva/moodle into MOODLE_310_STABLE
This commit is contained in:
@@ -162,7 +162,7 @@ class mod_glossary_external extends external_api {
|
||||
* @param int $id The glossary ID.
|
||||
* @return array Contains glossary, context, course and cm.
|
||||
*/
|
||||
protected static function validate_glossary($id) {
|
||||
public static function validate_glossary($id) {
|
||||
global $DB;
|
||||
$glossary = $DB->get_record('glossary', array('id' => $id), '*', MUST_EXIST);
|
||||
list($course, $cm) = get_course_and_cm_from_instance($glossary, 'glossary');
|
||||
@@ -1406,10 +1406,16 @@ class mod_glossary_external extends external_api {
|
||||
$entry = glossary_get_entry_by_id($id);
|
||||
self::fill_entry_details($entry, $context);
|
||||
|
||||
// Permissions (for entry edition).
|
||||
$permissions = [
|
||||
'candelete' => mod_glossary_can_delete_entry($entry, $glossary, $context),
|
||||
];
|
||||
|
||||
return array(
|
||||
'entry' => $entry,
|
||||
'ratinginfo' => \core_rating\external\util::get_rating_info($glossary, $context, 'mod_glossary', 'entry',
|
||||
array($entry)),
|
||||
'permissions' => $permissions,
|
||||
'warnings' => $warnings
|
||||
);
|
||||
}
|
||||
@@ -1424,6 +1430,12 @@ class mod_glossary_external extends external_api {
|
||||
return new external_single_structure(array(
|
||||
'entry' => self::get_entry_return_structure(),
|
||||
'ratinginfo' => \core_rating\external\util::external_ratings_structure(),
|
||||
'permissions' => new external_single_structure(
|
||||
[
|
||||
'candelete' => new external_value(PARAM_BOOL, 'Whether the user can delete the entry.'),
|
||||
],
|
||||
'User permissions for the managing the entry.', VALUE_OPTIONAL
|
||||
),
|
||||
'warnings' => new external_warnings()
|
||||
));
|
||||
}
|
||||
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* This is the external method for deleting a content.
|
||||
*
|
||||
* @package mod_glossary
|
||||
* @since Moodle 3.10
|
||||
* @copyright 2020 Juan Leyva <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace mod_glossary\external;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
global $CFG;
|
||||
require_once($CFG->libdir . '/externallib.php');
|
||||
require_once($CFG->dirroot . '/mod/glossary/lib.php');
|
||||
|
||||
use external_api;
|
||||
use external_function_parameters;
|
||||
use external_multiple_structure;
|
||||
use external_single_structure;
|
||||
use external_value;
|
||||
use external_warnings;
|
||||
|
||||
/**
|
||||
* This is the external method for deleting a content.
|
||||
*
|
||||
* @copyright 2020 Juan Leyva <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class delete_entry extends external_api {
|
||||
/**
|
||||
* Parameters.
|
||||
*
|
||||
* @return external_function_parameters
|
||||
*/
|
||||
public static function execute_parameters(): external_function_parameters {
|
||||
return new external_function_parameters([
|
||||
'entryid' => new external_value(PARAM_INT, 'Glossary entry id to delete'),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the indicated entry from the glossary.
|
||||
*
|
||||
* @param int $entryid The entry to delete
|
||||
* @return array with result and warnings
|
||||
* @throws moodle_exception
|
||||
*/
|
||||
public static function execute(int $entryid): array {
|
||||
global $DB;
|
||||
|
||||
$params = self::validate_parameters(self::execute_parameters(), compact('entryid'));
|
||||
$id = $params['entryid'];
|
||||
|
||||
// Get and validate the glossary.
|
||||
$entry = $DB->get_record('glossary_entries', ['id' => $id], '*', MUST_EXIST);
|
||||
list($glossary, $context, $course, $cm) = \mod_glossary_external::validate_glossary($entry->glossaryid);
|
||||
|
||||
// Check and delete.
|
||||
mod_glossary_can_delete_entry($entry, $glossary, $context, false);
|
||||
mod_glossary_delete_entry($entry, $glossary, $cm, $context, $course);
|
||||
|
||||
return [
|
||||
'result' => true,
|
||||
'warnings' => [],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return.
|
||||
*
|
||||
* @return external_single_structure
|
||||
*/
|
||||
public static function execute_returns(): external_single_structure {
|
||||
return new external_single_structure([
|
||||
'result' => new external_value(PARAM_BOOL, 'The processing result'),
|
||||
'warnings' => new external_warnings()
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -162,4 +162,12 @@ $functions = array(
|
||||
'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE)
|
||||
),
|
||||
|
||||
'mod_glossary_delete_entry' => [
|
||||
'classname' => 'mod_glossary\external\delete_entry',
|
||||
'methodname' => 'execute',
|
||||
'classpath' => '',
|
||||
'description' => 'Delete the given entry from the glossary.',
|
||||
'type' => 'write',
|
||||
'services' => [MOODLE_OFFICIAL_MOBILE_SERVICE]
|
||||
],
|
||||
);
|
||||
|
||||
@@ -46,107 +46,23 @@ if ($cm->instance != $entry->glossaryid) {
|
||||
|
||||
require_login($course, false, $cm);
|
||||
$context = context_module::instance($cm->id);
|
||||
$manageentries = has_capability('mod/glossary:manageentries', $context);
|
||||
|
||||
if (! $glossary = $DB->get_record("glossary", array("id"=>$cm->instance))) {
|
||||
print_error('invalidid', 'glossary');
|
||||
}
|
||||
|
||||
|
||||
$strareyousuredelete = get_string("areyousuredelete","glossary");
|
||||
|
||||
if (($entry->userid != $USER->id) and !$manageentries) { // guest id is never matched, no need for special check here
|
||||
print_error('nopermissiontodelentry');
|
||||
}
|
||||
$ineditperiod = ((time() - $entry->timecreated < $CFG->maxeditingtime) || $glossary->editalways);
|
||||
if (!$ineditperiod and !$manageentries) {
|
||||
print_error('errdeltimeexpired', 'glossary');
|
||||
}
|
||||
// Throws an exception if the user cannot delete the entry.
|
||||
mod_glossary_can_delete_entry($entry, $glossary, $context, false);
|
||||
|
||||
/// If data submitted, then process and store.
|
||||
|
||||
if ($confirm and confirm_sesskey()) { // the operation was confirmed.
|
||||
// if it is an imported entry, just delete the relation
|
||||
|
||||
$origentry = fullclone($entry);
|
||||
if ($entry->sourceglossaryid) {
|
||||
if (!$newcm = get_coursemodule_from_instance('glossary', $entry->sourceglossaryid)) {
|
||||
print_error('invalidcoursemodule');
|
||||
}
|
||||
$newcontext = context_module::instance($newcm->id);
|
||||
|
||||
$entry->glossaryid = $entry->sourceglossaryid;
|
||||
$entry->sourceglossaryid = 0;
|
||||
$DB->update_record('glossary_entries', $entry);
|
||||
|
||||
// move attachments too
|
||||
$fs = get_file_storage();
|
||||
|
||||
if ($oldfiles = $fs->get_area_files($context->id, 'mod_glossary', 'attachment', $entry->id)) {
|
||||
foreach ($oldfiles as $oldfile) {
|
||||
$file_record = new stdClass();
|
||||
$file_record->contextid = $newcontext->id;
|
||||
$fs->create_file_from_storedfile($file_record, $oldfile);
|
||||
}
|
||||
$fs->delete_area_files($context->id, 'mod_glossary', 'attachment', $entry->id);
|
||||
$entry->attachment = '1';
|
||||
} else {
|
||||
$entry->attachment = '0';
|
||||
}
|
||||
$DB->update_record('glossary_entries', $entry);
|
||||
|
||||
} else {
|
||||
$fs = get_file_storage();
|
||||
$fs->delete_area_files($context->id, 'mod_glossary', 'attachment', $entry->id);
|
||||
$DB->delete_records("comments", array('itemid'=>$entry->id, 'commentarea'=>'glossary_entry', 'contextid'=>$context->id));
|
||||
$DB->delete_records("glossary_alias", array("entryid"=>$entry->id));
|
||||
$DB->delete_records("glossary_entries", array("id"=>$entry->id));
|
||||
|
||||
// Update completion state
|
||||
$completion = new completion_info($course);
|
||||
if ($completion->is_enabled($cm) == COMPLETION_TRACKING_AUTOMATIC && $glossary->completionentries) {
|
||||
$completion->update_state($cm, COMPLETION_INCOMPLETE, $entry->userid);
|
||||
}
|
||||
|
||||
//delete glossary entry ratings
|
||||
require_once($CFG->dirroot.'/rating/lib.php');
|
||||
$delopt = new stdClass;
|
||||
$delopt->contextid = $context->id;
|
||||
$delopt->component = 'mod_glossary';
|
||||
$delopt->ratingarea = 'entry';
|
||||
$delopt->itemid = $entry->id;
|
||||
$rm = new rating_manager();
|
||||
$rm->delete_ratings($delopt);
|
||||
}
|
||||
|
||||
// Delete cached RSS feeds.
|
||||
if (!empty($CFG->enablerssfeeds)) {
|
||||
require_once($CFG->dirroot.'/mod/glossary/rsslib.php');
|
||||
glossary_rss_delete_file($glossary);
|
||||
}
|
||||
|
||||
core_tag_tag::remove_all_item_tags('mod_glossary', 'glossary_entries', $origentry->id);
|
||||
|
||||
$event = \mod_glossary\event\entry_deleted::create(array(
|
||||
'context' => $context,
|
||||
'objectid' => $origentry->id,
|
||||
'other' => array(
|
||||
'mode' => $prevmode,
|
||||
'hook' => $hook,
|
||||
'concept' => $origentry->concept
|
||||
)
|
||||
));
|
||||
$event->add_record_snapshot('glossary_entries', $origentry);
|
||||
$event->trigger();
|
||||
|
||||
// Reset caches.
|
||||
if ($entry->usedynalink and $entry->approved) {
|
||||
\mod_glossary\local\concept_cache::reset_glossary($glossary);
|
||||
}
|
||||
|
||||
mod_glossary_delete_entry($entry, $glossary, $cm, $context, $course, $hook, $prevmode);
|
||||
redirect("view.php?id=$cm->id&mode=$prevmode&hook=$hook");
|
||||
|
||||
} else { // the operation has not been confirmed yet so ask the user to do so
|
||||
$strareyousuredelete = get_string("areyousuredelete", "glossary");
|
||||
$PAGE->navbar->add(get_string('delete'));
|
||||
$PAGE->set_title($glossary->name);
|
||||
$PAGE->set_heading($course->fullname);
|
||||
|
||||
@@ -4318,3 +4318,140 @@ function mod_glossary_get_completion_active_rule_descriptions($cm) {
|
||||
}
|
||||
return $descriptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the current user can delete the given glossary entry.
|
||||
*
|
||||
* @since Moodle 3.10
|
||||
* @param stdClass $entry the entry database object
|
||||
* @param stdClass $glossary the glossary database object
|
||||
* @param stdClass $context the glossary context
|
||||
* @param bool $return Whether to return a boolean value or stop the execution (exception)
|
||||
* @return bool if the user can delete the entry
|
||||
* @throws moodle_exception
|
||||
*/
|
||||
function mod_glossary_can_delete_entry($entry, $glossary, $context, $return = true) {
|
||||
global $USER, $CFG;
|
||||
|
||||
$manageentries = has_capability('mod/glossary:manageentries', $context);
|
||||
|
||||
if ($manageentries) { // Users with the capability will always be able to delete entries.
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($entry->userid != $USER->id) { // Guest id is never matched, no need for special check here.
|
||||
if ($return) {
|
||||
return false;
|
||||
}
|
||||
throw new moodle_exception('nopermissiontodelentry');
|
||||
}
|
||||
|
||||
$ineditperiod = ((time() - $entry->timecreated < $CFG->maxeditingtime) || $glossary->editalways);
|
||||
|
||||
if (!$ineditperiod) {
|
||||
if ($return) {
|
||||
return false;
|
||||
}
|
||||
throw new moodle_exception('errdeltimeexpired', 'glossary');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the given entry, this function does not perform capabilities/permission checks.
|
||||
*
|
||||
* @since Moodle 3.10
|
||||
* @param stdClass $entry the entry database object
|
||||
* @param stdClass $glossary the glossary database object
|
||||
* @param stdClass $cm the glossary course moduule object
|
||||
* @param stdClass $context the glossary context
|
||||
* @param stdClass $course the glossary course
|
||||
* @param string $hook the hook, usually type of filtering, value
|
||||
* @param string $prevmode the previsualisation mode
|
||||
* @throws moodle_exception
|
||||
*/
|
||||
function mod_glossary_delete_entry($entry, $glossary, $cm, $context, $course, $hook = '', $prevmode = '') {
|
||||
global $CFG, $DB;
|
||||
|
||||
$origentry = fullclone($entry);
|
||||
|
||||
// If it is an imported entry, just delete the relation.
|
||||
if ($entry->sourceglossaryid) {
|
||||
if (!$newcm = get_coursemodule_from_instance('glossary', $entry->sourceglossaryid)) {
|
||||
print_error('invalidcoursemodule');
|
||||
}
|
||||
$newcontext = context_module::instance($newcm->id);
|
||||
|
||||
$entry->glossaryid = $entry->sourceglossaryid;
|
||||
$entry->sourceglossaryid = 0;
|
||||
$DB->update_record('glossary_entries', $entry);
|
||||
|
||||
// Move attachments too.
|
||||
$fs = get_file_storage();
|
||||
|
||||
if ($oldfiles = $fs->get_area_files($context->id, 'mod_glossary', 'attachment', $entry->id)) {
|
||||
foreach ($oldfiles as $oldfile) {
|
||||
$filerecord = new stdClass();
|
||||
$filerecord->contextid = $newcontext->id;
|
||||
$fs->create_file_from_storedfile($filerecord, $oldfile);
|
||||
}
|
||||
$fs->delete_area_files($context->id, 'mod_glossary', 'attachment', $entry->id);
|
||||
$entry->attachment = '1';
|
||||
} else {
|
||||
$entry->attachment = '0';
|
||||
}
|
||||
$DB->update_record('glossary_entries', $entry);
|
||||
|
||||
} else {
|
||||
$fs = get_file_storage();
|
||||
$fs->delete_area_files($context->id, 'mod_glossary', 'attachment', $entry->id);
|
||||
$DB->delete_records("comments",
|
||||
['itemid' => $entry->id, 'commentarea' => 'glossary_entry', 'contextid' => $context->id]);
|
||||
$DB->delete_records("glossary_alias", ["entryid" => $entry->id]);
|
||||
$DB->delete_records("glossary_entries", ["id" => $entry->id]);
|
||||
|
||||
// Update completion state.
|
||||
$completion = new completion_info($course);
|
||||
if ($completion->is_enabled($cm) == COMPLETION_TRACKING_AUTOMATIC && $glossary->completionentries) {
|
||||
$completion->update_state($cm, COMPLETION_INCOMPLETE, $entry->userid);
|
||||
}
|
||||
|
||||
// Delete glossary entry ratings.
|
||||
require_once($CFG->dirroot.'/rating/lib.php');
|
||||
$delopt = new stdClass;
|
||||
$delopt->contextid = $context->id;
|
||||
$delopt->component = 'mod_glossary';
|
||||
$delopt->ratingarea = 'entry';
|
||||
$delopt->itemid = $entry->id;
|
||||
$rm = new rating_manager();
|
||||
$rm->delete_ratings($delopt);
|
||||
}
|
||||
|
||||
// Delete cached RSS feeds.
|
||||
if (!empty($CFG->enablerssfeeds)) {
|
||||
require_once($CFG->dirroot . '/mod/glossary/rsslib.php');
|
||||
glossary_rss_delete_file($glossary);
|
||||
}
|
||||
|
||||
core_tag_tag::remove_all_item_tags('mod_glossary', 'glossary_entries', $origentry->id);
|
||||
|
||||
$event = \mod_glossary\event\entry_deleted::create(
|
||||
[
|
||||
'context' => $context,
|
||||
'objectid' => $origentry->id,
|
||||
'other' => [
|
||||
'mode' => $prevmode,
|
||||
'hook' => $hook,
|
||||
'concept' => $origentry->concept
|
||||
]
|
||||
]
|
||||
);
|
||||
$event->add_record_snapshot('glossary_entries', $origentry);
|
||||
$event->trigger();
|
||||
|
||||
// Reset caches.
|
||||
if ($entry->usedynalink and $entry->approved) {
|
||||
\mod_glossary\local\concept_cache::reset_glossary($glossary);
|
||||
}
|
||||
}
|
||||
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* External function test for delete_entry.
|
||||
*
|
||||
* @package mod_glossary
|
||||
* @category external
|
||||
* @since Moodle 3.10
|
||||
* @copyright 2020 Juan Leyva <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace mod_glossary\external;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
global $CFG;
|
||||
require_once($CFG->dirroot . '/webservice/tests/helpers.php');
|
||||
|
||||
use external_api;
|
||||
use externallib_advanced_testcase;
|
||||
|
||||
/**
|
||||
* External function test for delete_entry.
|
||||
*
|
||||
* @package mod_glossary
|
||||
* @copyright 2020 Juan Leyva <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class delete_entry_testcase extends externallib_advanced_testcase {
|
||||
|
||||
/**
|
||||
* Test the behaviour of delete_entry().
|
||||
*/
|
||||
public function test_delete_entry() {
|
||||
global $DB;
|
||||
$this->resetAfterTest();
|
||||
|
||||
// Create required data.
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$student = $this->getDataGenerator()->create_and_enrol($course, 'student');
|
||||
$anotherstudent = $this->getDataGenerator()->create_and_enrol($course, 'student');
|
||||
$glossary = $this->getDataGenerator()->create_module('glossary', ['course' => $course->id]);
|
||||
$gg = $this->getDataGenerator()->get_plugin_generator('mod_glossary');
|
||||
|
||||
$this->setUser($student);
|
||||
$entry = $gg->create_content($glossary);
|
||||
|
||||
// Test entry creator can delete.
|
||||
$result = delete_entry::execute($entry->id);
|
||||
$result = external_api::clean_returnvalue(delete_entry::execute_returns(), $result);
|
||||
$this->assertTrue($result['result']);
|
||||
$this->assertEquals(0, $DB->count_records('glossary_entries', ['id' => $entry->id]));
|
||||
|
||||
// Test admin can delete.
|
||||
$this->setAdminUser();
|
||||
$entry = $gg->create_content($glossary);
|
||||
$result = delete_entry::execute($entry->id);
|
||||
$result = external_api::clean_returnvalue(delete_entry::execute_returns(), $result);
|
||||
$this->assertTrue($result['result']);
|
||||
$this->assertEquals(0, $DB->count_records('glossary_entries', ['id' => $entry->id]));
|
||||
|
||||
$entry = $gg->create_content($glossary);
|
||||
// Test a different student is not able to delete.
|
||||
$this->setUser($anotherstudent);
|
||||
$this->expectExceptionMessage(get_string('nopermissiontodelentry', 'error'));
|
||||
delete_entry::execute($entry->id);
|
||||
}
|
||||
}
|
||||
@@ -1077,11 +1077,14 @@ class mod_glossary_external_testcase extends externallib_advanced_testcase {
|
||||
$c1 = $this->getDataGenerator()->create_course();
|
||||
$c2 = $this->getDataGenerator()->create_course();
|
||||
$g1 = $this->getDataGenerator()->create_module('glossary', array('course' => $c1->id));
|
||||
$g2 = $this->getDataGenerator()->create_module('glossary', array('course' => $c1->id, 'visible' => 0));
|
||||
$g2 = $this->getDataGenerator()->create_module('glossary', array('course' => $c2->id, 'visible' => 0));
|
||||
$u1 = $this->getDataGenerator()->create_user();
|
||||
$u2 = $this->getDataGenerator()->create_user();
|
||||
$u3 = $this->getDataGenerator()->create_user();
|
||||
$ctx = context_module::instance($g1->cmid);
|
||||
$this->getDataGenerator()->enrol_user($u1->id, $c1->id);
|
||||
$this->getDataGenerator()->enrol_user($u2->id, $c1->id);
|
||||
$this->getDataGenerator()->enrol_user($u3->id, $c1->id);
|
||||
|
||||
$e1 = $gg->create_content($g1, array('approved' => 1, 'userid' => $u1->id, 'tags' => array('Cats', 'Dogs')));
|
||||
// Add a fake inline image to the entry.
|
||||
@@ -1108,10 +1111,12 @@ class mod_glossary_external_testcase extends externallib_advanced_testcase {
|
||||
$this->assertEquals('Cats', $return['entry']['tags'][0]['rawname']);
|
||||
$this->assertEquals('Dogs', $return['entry']['tags'][1]['rawname']);
|
||||
$this->assertEquals($filename, $return['entry']['definitioninlinefiles'][0]['filename']);
|
||||
$this->assertTrue($return['permissions']['candelete']);
|
||||
|
||||
$return = mod_glossary_external::get_entry_by_id($e2->id);
|
||||
$return = external_api::clean_returnvalue(mod_glossary_external::get_entry_by_id_returns(), $return);
|
||||
$this->assertEquals($e2->id, $return['entry']['id']);
|
||||
$this->assertTrue($return['permissions']['candelete']);
|
||||
|
||||
try {
|
||||
$return = mod_glossary_external::get_entry_by_id($e3->id);
|
||||
@@ -1127,11 +1132,19 @@ class mod_glossary_external_testcase extends externallib_advanced_testcase {
|
||||
// All good.
|
||||
}
|
||||
|
||||
// An admin can be other's entries to be approved.
|
||||
// An admin can see other's entries to be approved.
|
||||
$this->setAdminUser();
|
||||
$return = mod_glossary_external::get_entry_by_id($e3->id);
|
||||
$return = external_api::clean_returnvalue(mod_glossary_external::get_entry_by_id_returns(), $return);
|
||||
$this->assertEquals($e3->id, $return['entry']['id']);
|
||||
$this->assertTrue($return['permissions']['candelete']);
|
||||
|
||||
// Students can see other students approved entries but they will not be able to delete them.
|
||||
$this->setUser($u3);
|
||||
$return = mod_glossary_external::get_entry_by_id($e1->id);
|
||||
$return = external_api::clean_returnvalue(mod_glossary_external::get_entry_by_id_returns(), $return);
|
||||
$this->assertEquals($e1->id, $return['entry']['id']);
|
||||
$this->assertFalse($return['permissions']['candelete']);
|
||||
}
|
||||
|
||||
public function test_add_entry_without_optional_settings() {
|
||||
|
||||
@@ -503,4 +503,171 @@ class mod_glossary_lib_testcase extends advanced_testcase {
|
||||
$search = glossary_get_entries_search($concept, $course->id);
|
||||
$this->assertCount(0, $search);
|
||||
}
|
||||
|
||||
public function test_mod_glossary_can_delete_entry_users() {
|
||||
$this->resetAfterTest();
|
||||
|
||||
// Create required data.
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$student = $this->getDataGenerator()->create_and_enrol($course, 'student');
|
||||
$anotherstudent = $this->getDataGenerator()->create_and_enrol($course, 'student');
|
||||
$teacher = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
|
||||
$glossary = $this->getDataGenerator()->create_module('glossary', ['course' => $course->id]);
|
||||
|
||||
$gg = $this->getDataGenerator()->get_plugin_generator('mod_glossary');
|
||||
$this->setUser($student);
|
||||
$entry = $gg->create_content($glossary);
|
||||
$context = context_module::instance($glossary->cmid);
|
||||
|
||||
// Test student can delete.
|
||||
$this->assertTrue(mod_glossary_can_delete_entry($entry, $glossary, $context));
|
||||
|
||||
// Test teacher can delete.
|
||||
$this->setUser($teacher);
|
||||
$this->assertTrue(mod_glossary_can_delete_entry($entry, $glossary, $context));
|
||||
|
||||
// Test admin can delete.
|
||||
$this->setAdminUser();
|
||||
$this->assertTrue(mod_glossary_can_delete_entry($entry, $glossary, $context));
|
||||
|
||||
// Test a different student is not able to delete.
|
||||
$this->setUser($anotherstudent);
|
||||
$this->assertFalse(mod_glossary_can_delete_entry($entry, $glossary, $context));
|
||||
|
||||
// Test exception.
|
||||
$this->expectExceptionMessage(get_string('nopermissiontodelentry', 'error'));
|
||||
mod_glossary_can_delete_entry($entry, $glossary, $context, false);
|
||||
}
|
||||
|
||||
public function test_mod_glossary_can_delete_entry_edit_period() {
|
||||
global $CFG;
|
||||
$this->resetAfterTest();
|
||||
|
||||
// Create required data.
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$student = $this->getDataGenerator()->create_and_enrol($course, 'student');
|
||||
$glossary = $this->getDataGenerator()->create_module('glossary', ['course' => $course->id, 'editalways' => 1]);
|
||||
|
||||
$gg = $this->getDataGenerator()->get_plugin_generator('mod_glossary');
|
||||
$this->setUser($student);
|
||||
$entry = $gg->create_content($glossary);
|
||||
$context = context_module::instance($glossary->cmid);
|
||||
|
||||
// Test student can always delete when edit always is set to 1.
|
||||
$entry->timecreated = time() - 2 * $CFG->maxeditingtime;
|
||||
$this->assertTrue(mod_glossary_can_delete_entry($entry, $glossary, $context));
|
||||
|
||||
// Test student cannot delete old entries when edit always is set to 0.
|
||||
$glossary->editalways = 0;
|
||||
$this->assertFalse(mod_glossary_can_delete_entry($entry, $glossary, $context));
|
||||
|
||||
// Test student can delete recent entries when edit always is set to 0.
|
||||
$entry->timecreated = time();
|
||||
$this->assertTrue(mod_glossary_can_delete_entry($entry, $glossary, $context));
|
||||
|
||||
// Check exception.
|
||||
$entry->timecreated = time() - 2 * $CFG->maxeditingtime;
|
||||
$this->expectExceptionMessage(get_string('errdeltimeexpired', 'glossary'));
|
||||
mod_glossary_can_delete_entry($entry, $glossary, $context, false);
|
||||
}
|
||||
|
||||
public function test_mod_glossary_delete_entry() {
|
||||
global $DB, $CFG;
|
||||
$this->resetAfterTest();
|
||||
require_once($CFG->dirroot . '/rating/lib.php');
|
||||
|
||||
// Create required data.
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$student1 = $this->getDataGenerator()->create_and_enrol($course, 'student');
|
||||
$student2 = $this->getDataGenerator()->create_and_enrol($course, 'student');
|
||||
|
||||
$record = new stdClass();
|
||||
$record->course = $course->id;
|
||||
$record->assessed = RATING_AGGREGATE_AVERAGE;
|
||||
$scale = $this->getDataGenerator()->create_scale(['scale' => 'A,B,C,D']);
|
||||
$record->scale = "-$scale->id";
|
||||
$glossary = $this->getDataGenerator()->create_module('glossary', $record);
|
||||
$context = context_module::instance($glossary->cmid);
|
||||
$cm = get_coursemodule_from_instance('glossary', $glossary->id);
|
||||
|
||||
$gg = $this->getDataGenerator()->get_plugin_generator('mod_glossary');
|
||||
$this->setUser($student1);
|
||||
|
||||
// Create entry with tags and rating.
|
||||
$entry = $gg->create_content(
|
||||
$glossary,
|
||||
['approved' => 1, 'userid' => $student1->id, 'tags' => ['Cats', 'Dogs']],
|
||||
['alias1', 'alias2']
|
||||
);
|
||||
|
||||
// Rate the entry as user2.
|
||||
$rating1 = new stdClass();
|
||||
$rating1->contextid = $context->id;
|
||||
$rating1->component = 'mod_glossary';
|
||||
$rating1->ratingarea = 'entry';
|
||||
$rating1->itemid = $entry->id;
|
||||
$rating1->rating = 1; // 1 is A.
|
||||
$rating1->scaleid = "-$scale->id";
|
||||
$rating1->userid = $student2->id;
|
||||
$rating1->timecreated = time();
|
||||
$rating1->timemodified = time();
|
||||
$rating1->id = $DB->insert_record('rating', $rating1);
|
||||
|
||||
$sink = $this->redirectEvents();
|
||||
mod_glossary_delete_entry(fullclone($entry), $glossary, $cm, $context, $course);
|
||||
$events = $sink->get_events();
|
||||
$event = array_pop($events);
|
||||
|
||||
// Check events.
|
||||
$this->assertEquals('\mod_glossary\event\entry_deleted', $event->eventname);
|
||||
$this->assertEquals($entry->id, $event->objectid);
|
||||
$sink->close();
|
||||
|
||||
// No entry, no alias, no ratings, no tags.
|
||||
$this->assertEquals(0, $DB->count_records('glossary_entries', ['id' => $entry->id]));
|
||||
$this->assertEquals(0, $DB->count_records('glossary_alias', ['entryid' => $entry->id]));
|
||||
$this->assertEquals(0, $DB->count_records('rating', ['component' => 'mod_glossary', 'itemid' => $entry->id]));
|
||||
$this->assertEmpty(core_tag_tag::get_by_name(0, 'Cats'));
|
||||
}
|
||||
|
||||
public function test_mod_glossary_delete_entry_imported() {
|
||||
global $DB;
|
||||
$this->resetAfterTest();
|
||||
|
||||
// Create required data.
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$student = $this->getDataGenerator()->create_and_enrol($course, 'student');
|
||||
|
||||
$glossary1 = $this->getDataGenerator()->create_module('glossary', ['course' => $course->id]);
|
||||
$glossary2 = $this->getDataGenerator()->create_module('glossary', ['course' => $course->id]);
|
||||
|
||||
$context = context_module::instance($glossary2->cmid);
|
||||
$cm = get_coursemodule_from_instance('glossary', $glossary2->id);
|
||||
|
||||
$gg = $this->getDataGenerator()->get_plugin_generator('mod_glossary');
|
||||
$this->setUser($student);
|
||||
|
||||
$entry1 = $gg->create_content($glossary1);
|
||||
$entry2 = $gg->create_content(
|
||||
$glossary2,
|
||||
['approved' => 1, 'userid' => $student->id, 'sourceglossaryid' => $glossary1->id, 'tags' => ['Cats', 'Dogs']]
|
||||
);
|
||||
|
||||
$sink = $this->redirectEvents();
|
||||
mod_glossary_delete_entry(fullclone($entry2), $glossary2, $cm, $context, $course);
|
||||
$events = $sink->get_events();
|
||||
$event = array_pop($events);
|
||||
|
||||
// Check events.
|
||||
$this->assertEquals('\mod_glossary\event\entry_deleted', $event->eventname);
|
||||
$this->assertEquals($entry2->id, $event->objectid);
|
||||
$sink->close();
|
||||
|
||||
// Check source.
|
||||
$this->assertEquals(0, $DB->get_field('glossary_entries', 'sourceglossaryid', ['id' => $entry2->id]));
|
||||
$this->assertEquals($glossary1->id, $DB->get_field('glossary_entries', 'glossaryid', ['id' => $entry2->id]));
|
||||
|
||||
// Tags.
|
||||
$this->assertEmpty(core_tag_tag::get_by_name(0, 'Cats'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
This files describes API changes in /mod/glossary/*,
|
||||
information provided here is intended especially for developers.
|
||||
|
||||
=== 3.10 ===
|
||||
* External function get_entries_by_id now returns and additional "permissions" field indicating the user permissions for managing
|
||||
the entry.
|
||||
|
||||
=== 3.8 ===
|
||||
* The following functions have been finally deprecated and can not be used anymore:
|
||||
* glossary_scale_used()
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$plugin->version = 2020061500; // The current module version (Date: YYYYMMDDXX)
|
||||
$plugin->version = 2020061501; // The current module version (Date: YYYYMMDDXX)
|
||||
$plugin->requires = 2020060900; // Requires this Moodle version
|
||||
$plugin->component = 'mod_glossary'; // Full name of the plugin (used for diagnostics)
|
||||
$plugin->cron = 0;
|
||||
|
||||
Reference in New Issue
Block a user