Merge branch 'MDL-67548-master' of https://github.com/kabalin/moodle
This commit is contained in:
+56
-18
@@ -171,6 +171,24 @@ class core_course_category implements renderable, cacheable_object, IteratorAggr
|
||||
debugging('Can not unset core_course_category instance properties!', DEBUG_DEVELOPER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get list of plugin callback functions.
|
||||
*
|
||||
* @param string $name Callback function name.
|
||||
* @return [callable] $pluginfunctions
|
||||
*/
|
||||
public function get_plugins_callback_function(string $name) : array {
|
||||
$pluginfunctions = [];
|
||||
if ($pluginsfunction = get_plugins_with_function($name)) {
|
||||
foreach ($pluginsfunction as $plugintype => $plugins) {
|
||||
foreach ($plugins as $pluginfunction) {
|
||||
$pluginfunctions[] = $pluginfunction;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $pluginfunctions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an iterator because magic vars can't be seen by 'foreach'.
|
||||
*
|
||||
@@ -1900,13 +1918,12 @@ class core_course_category implements renderable, cacheable_object, IteratorAggr
|
||||
return false;
|
||||
}
|
||||
|
||||
$context = $this->get_context();
|
||||
if (!$this->is_uservisible() ||
|
||||
!has_capability('moodle/category:manage', $context)) {
|
||||
if (!$this->has_manage_capability()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check all child categories (not only direct children).
|
||||
$context = $this->get_context();
|
||||
$sql = context_helper::get_preload_record_columns_sql('ctx');
|
||||
$childcategories = $DB->get_records_sql('SELECT c.id, c.visible, '. $sql.
|
||||
' FROM {context} ctx '.
|
||||
@@ -1936,6 +1953,15 @@ class core_course_category implements renderable, cacheable_object, IteratorAggr
|
||||
}
|
||||
}
|
||||
|
||||
// Check if plugins permit deletion of category content.
|
||||
$pluginfunctions = $this->get_plugins_callback_function('can_course_category_delete');
|
||||
foreach ($pluginfunctions as $pluginfunction) {
|
||||
// If at least one plugin does not permit deletion, stop and return false.
|
||||
if (!$pluginfunction($this)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1961,13 +1987,9 @@ class core_course_category implements renderable, cacheable_object, IteratorAggr
|
||||
$settimeout = core_php_time_limit::raise();
|
||||
|
||||
// Allow plugins to use this category before we completely delete it.
|
||||
if ($pluginsfunction = get_plugins_with_function('pre_course_category_delete')) {
|
||||
$category = $this->get_db_record();
|
||||
foreach ($pluginsfunction as $plugintype => $plugins) {
|
||||
foreach ($plugins as $pluginfunction) {
|
||||
$pluginfunction($category);
|
||||
}
|
||||
}
|
||||
$pluginfunctions = $this->get_plugins_callback_function('pre_course_category_delete');
|
||||
foreach ($pluginfunctions as $pluginfunction) {
|
||||
$pluginfunction($this->get_db_record());
|
||||
}
|
||||
|
||||
$deletedcourses = array();
|
||||
@@ -2076,25 +2098,35 @@ class core_course_category implements renderable, cacheable_object, IteratorAggr
|
||||
public function can_move_content_to($newcatid) {
|
||||
global $CFG;
|
||||
require_once($CFG->libdir . '/questionlib.php');
|
||||
$context = $this->get_context();
|
||||
if (!$this->is_uservisible() ||
|
||||
!has_capability('moodle/category:manage', $context)) {
|
||||
|
||||
if (!$this->has_manage_capability()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$testcaps = array();
|
||||
// If this category has courses in it, user must have 'course:create' capability in target category.
|
||||
if ($this->has_courses()) {
|
||||
$testcaps[] = 'moodle/course:create';
|
||||
}
|
||||
// If this category has subcategories or questions, user must have 'category:manage' capability in target category.
|
||||
if ($this->has_children() || question_context_has_any_questions($context)) {
|
||||
if ($this->has_children() || question_context_has_any_questions($this->get_context())) {
|
||||
$testcaps[] = 'moodle/category:manage';
|
||||
}
|
||||
if (!empty($testcaps)) {
|
||||
return has_all_capabilities($testcaps, context_coursecat::instance($newcatid));
|
||||
if (!empty($testcaps) && !has_all_capabilities($testcaps, context_coursecat::instance($newcatid))) {
|
||||
// No sufficient capabilities to perform this task.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if plugins permit moving category content.
|
||||
$pluginfunctions = $this->get_plugins_callback_function('can_course_category_delete_move');
|
||||
$newparentcat = self::get($newcatid, MUST_EXIST, true);
|
||||
foreach ($pluginfunctions as $pluginfunction) {
|
||||
// If at least one plugin does not permit move on deletion, stop and return false.
|
||||
if (!$pluginfunction($this, $newparentcat)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// There is no content but still return true.
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -2124,6 +2156,12 @@ class core_course_category implements renderable, cacheable_object, IteratorAggr
|
||||
$coursesids = $DB->get_fieldset_select('course', 'id', 'category = :category ORDER BY sortorder ASC', $params);
|
||||
$context = $this->get_context();
|
||||
|
||||
// Allow plugins to make necessary changes before we move the category content.
|
||||
$pluginfunctions = $this->get_plugins_callback_function('pre_course_category_delete_move');
|
||||
foreach ($pluginfunctions as $pluginfunction) {
|
||||
$pluginfunction($this, $newparentcat);
|
||||
}
|
||||
|
||||
if ($children) {
|
||||
foreach ($children as $childcat) {
|
||||
$childcat->change_parent_raw($newparentcat);
|
||||
@@ -2186,7 +2224,7 @@ class core_course_category implements renderable, cacheable_object, IteratorAggr
|
||||
$event = \core\event\course_category_deleted::create(array(
|
||||
'objectid' => $this->id,
|
||||
'context' => $context,
|
||||
'other' => array('name' => $this->name)
|
||||
'other' => array('name' => $this->name, 'contentmovedcategoryid' => $newparentid)
|
||||
));
|
||||
$event->set_coursecat($this);
|
||||
$event->trigger();
|
||||
|
||||
@@ -76,14 +76,23 @@ class core_course_deletecategory_form extends moodleform {
|
||||
// Describe the contents of this category.
|
||||
$contents = '';
|
||||
if ($this->coursecat->has_children()) {
|
||||
$contents .= '<li>' . get_string('subcategories') . '</li>';
|
||||
$contents .= html_writer::tag('li', get_string('subcategories'));
|
||||
}
|
||||
if ($this->coursecat->has_courses()) {
|
||||
$contents .= '<li>' . get_string('courses') . '</li>';
|
||||
$contents .= html_writer::tag('li', get_string('courses'));
|
||||
}
|
||||
if (question_context_has_any_questions($categorycontext)) {
|
||||
$contents .= '<li>' . get_string('questionsinthequestionbank') . '</li>';
|
||||
$contents .= html_writer::tag('li', get_string('questionsinthequestionbank'));
|
||||
}
|
||||
|
||||
// Check if plugins can provide more info.
|
||||
$pluginfunctions = $this->coursecat->get_plugins_callback_function('get_course_category_contents');
|
||||
foreach ($pluginfunctions as $pluginfunction) {
|
||||
if ($plugincontents = $pluginfunction($this->coursecat)) {
|
||||
$contents .= html_writer::tag('li', $plugincontents);
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($contents)) {
|
||||
$mform->addElement('static', 'emptymessage', get_string('thiscategorycontains'), html_writer::tag('ul', $contents));
|
||||
} else {
|
||||
@@ -92,7 +101,9 @@ class core_course_deletecategory_form extends moodleform {
|
||||
|
||||
// Give the options for what to do.
|
||||
$mform->addElement('select', 'fulldelete', get_string('whattodo'), $options);
|
||||
|
||||
if (count($options) == 1) {
|
||||
// Freeze selector if only one option available.
|
||||
$optionkeys = array_keys($options);
|
||||
$option = reset($optionkeys);
|
||||
$mform->hardFreeze('fulldelete');
|
||||
@@ -111,10 +122,6 @@ class core_course_deletecategory_form extends moodleform {
|
||||
$mform->setType('categoryid', PARAM_ALPHANUM);
|
||||
$mform->addElement('hidden', 'action', 'deletecategory');
|
||||
$mform->setType('action', PARAM_ALPHANUM);
|
||||
$mform->addElement('hidden', 'sure');
|
||||
// This gets set by default to ensure that if the user changes it manually we can detect it.
|
||||
$mform->setDefault('sure', md5(serialize($this->coursecat)));
|
||||
$mform->setType('sure', PARAM_ALPHANUM);
|
||||
|
||||
$this->add_action_buttons(true, get_string('delete'));
|
||||
}
|
||||
@@ -131,10 +138,11 @@ class core_course_deletecategory_form extends moodleform {
|
||||
if (empty($data['fulldelete']) && empty($data['newparent'])) {
|
||||
// When they have chosen the move option, they must specify a destination.
|
||||
$errors['newparent'] = get_string('required');
|
||||
return $errors;
|
||||
}
|
||||
|
||||
if ($data['sure'] !== md5(serialize($this->coursecat))) {
|
||||
$errors['categorylabel'] = get_string('categorymodifiedcancel');
|
||||
if (!empty($data['newparent']) && !$this->coursecat->can_move_content_to($data['newparent'])) {
|
||||
$errors['newparent'] = get_string('movecatcontentstoselected', 'error');
|
||||
}
|
||||
|
||||
return $errors;
|
||||
|
||||
@@ -0,0 +1,215 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Tests for class core_course_category methods invoking hooks.
|
||||
*
|
||||
* @package core_course
|
||||
* @category test
|
||||
* @copyright 2020 Ruslan Kabalin
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace tests\core_course;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
global $CFG;
|
||||
require_once($CFG->dirroot . '/course/tests/fixtures/mock_hooks.php');
|
||||
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
|
||||
/**
|
||||
* Functional test for class core_course_category methods invoking hooks.
|
||||
*/
|
||||
class core_course_category_hooks_testcase extends \advanced_testcase {
|
||||
|
||||
protected function setUp() {
|
||||
$this->resetAfterTest();
|
||||
$this->setAdminUser();
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides mocked category configured for named callback function.
|
||||
*
|
||||
* get_plugins_callback_function will return callable prefixed with `tool_unittest_`,
|
||||
* the actual callbacks are defined in mock_hooks.php fixture file.
|
||||
*
|
||||
* @param core_course_category $category Category to mock
|
||||
* @param string $callback Callback function used in method we test.
|
||||
* @return MockObject
|
||||
*/
|
||||
public function get_mock_category(\core_course_category $category, string $callback = '') : MockObject {
|
||||
// Setup mock object for \core_course_category.
|
||||
// Disable original constructor, since we can't use it directly since it is private.
|
||||
$mockcategory = $this->getMockBuilder(\core_course_category::class)
|
||||
->setMethods(['get_plugins_callback_function'])
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
|
||||
// Define get_plugins_callback_function use and return value.
|
||||
if (!empty($callback)) {
|
||||
$mockcategory->method('get_plugins_callback_function')
|
||||
->with($this->equalTo($callback))
|
||||
->willReturn(['tool_unittest_' . $callback]);
|
||||
}
|
||||
|
||||
// Modify constructor visibility and invoke mock object with real object.
|
||||
// This is used to overcome private constructor.
|
||||
$reflected = new \ReflectionClass(\core_course_category::class);
|
||||
$constructor = $reflected->getConstructor();
|
||||
$constructor->setAccessible(true);
|
||||
$constructor->invoke($mockcategory, $category->get_db_record());
|
||||
|
||||
return $mockcategory;
|
||||
}
|
||||
|
||||
public function test_can_course_category_delete_hook() {
|
||||
$category1 = \core_course_category::create(array('name' => 'Cat1'));
|
||||
$category2 = \core_course_category::create(array('name' => 'Cat2', 'parent' => $category1->id));
|
||||
$category3 = \core_course_category::create(array('name' => 'Cat3'));
|
||||
|
||||
$mockcategory2 = $this->get_mock_category($category2, 'can_course_category_delete');
|
||||
|
||||
// Add course to mocked clone of category2.
|
||||
$course1 = $this->getDataGenerator()->create_course(array('category' => $mockcategory2->id));
|
||||
|
||||
// Now configure fixture to return false for the callback.
|
||||
mock_hooks::set_can_course_category_delete_return(false);
|
||||
$this->assertFalse($mockcategory2->can_delete_full($category3->id));
|
||||
|
||||
// Now configure fixture to return true for the callback.
|
||||
mock_hooks::set_can_course_category_delete_return(true);
|
||||
$this->assertTrue($mockcategory2->can_delete_full($category3->id));
|
||||
|
||||
// Verify passed arguments.
|
||||
$arguments = mock_hooks::get_calling_arguments();
|
||||
$this->assertCount(1, $arguments);
|
||||
|
||||
// Argument 1 is the same core_course_category instance.
|
||||
$argument = array_shift($arguments);
|
||||
$this->assertSame($mockcategory2, $argument);
|
||||
}
|
||||
|
||||
public function test_can_course_category_delete_move_hook() {
|
||||
$category1 = \core_course_category::create(array('name' => 'Cat1'));
|
||||
$category2 = \core_course_category::create(array('name' => 'Cat2', 'parent' => $category1->id));
|
||||
$category3 = \core_course_category::create(array('name' => 'Cat3'));
|
||||
|
||||
$mockcategory2 = $this->get_mock_category($category2, 'can_course_category_delete_move');
|
||||
|
||||
// Add course to mocked clone of category2.
|
||||
$course1 = $this->getDataGenerator()->create_course(array('category' => $mockcategory2->id));
|
||||
|
||||
// Now configure fixture to return false for the callback.
|
||||
mock_hooks::set_can_course_category_delete_move_return(false);
|
||||
$this->assertFalse($mockcategory2->can_move_content_to($category3->id));
|
||||
|
||||
// Now configure fixture to return true for the callback.
|
||||
mock_hooks::set_can_course_category_delete_move_return(true);
|
||||
$this->assertTrue($mockcategory2->can_move_content_to($category3->id));
|
||||
|
||||
// Verify passed arguments.
|
||||
$arguments = mock_hooks::get_calling_arguments();
|
||||
$this->assertCount(2, $arguments);
|
||||
|
||||
// Argument 1 is the same core_course_category instance.
|
||||
$argument = array_shift($arguments);
|
||||
$this->assertSame($mockcategory2, $argument);
|
||||
|
||||
// Argument 2 is referring to category 3.
|
||||
$argument = array_shift($arguments);
|
||||
$this->assertInstanceOf(\core_course_category::class, $argument);
|
||||
$this->assertEquals($category3->id, $argument->id);
|
||||
}
|
||||
|
||||
public function test_pre_course_category_delete_hook() {
|
||||
$category1 = \core_course_category::create(array('name' => 'Cat1'));
|
||||
$category2 = \core_course_category::create(array('name' => 'Cat2', 'parent' => $category1->id));
|
||||
|
||||
$mockcategory2 = $this->get_mock_category($category2, 'pre_course_category_delete');
|
||||
$mockcategory2->delete_full();
|
||||
|
||||
// Verify passed arguments.
|
||||
$arguments = mock_hooks::get_calling_arguments();
|
||||
$this->assertCount(1, $arguments);
|
||||
|
||||
// Argument 1 is the category object.
|
||||
$argument = array_shift($arguments);
|
||||
$this->assertEquals($mockcategory2->get_db_record(), $argument);
|
||||
}
|
||||
|
||||
public function test_pre_course_category_delete_move_hook() {
|
||||
$category1 = \core_course_category::create(array('name' => 'Cat1'));
|
||||
$category2 = \core_course_category::create(array('name' => 'Cat2', 'parent' => $category1->id));
|
||||
$category3 = \core_course_category::create(array('name' => 'Cat3'));
|
||||
|
||||
$mockcategory2 = $this->get_mock_category($category2, 'pre_course_category_delete_move');
|
||||
|
||||
// Add course to mocked clone of category2.
|
||||
$course1 = $this->getDataGenerator()->create_course(array('category' => $mockcategory2->id));
|
||||
|
||||
$mockcategory2->delete_move($category3->id);
|
||||
|
||||
// Verify passed arguments.
|
||||
$arguments = mock_hooks::get_calling_arguments();
|
||||
$this->assertCount(2, $arguments);
|
||||
|
||||
// Argument 1 is the same core_course_category instance.
|
||||
$argument = array_shift($arguments);
|
||||
$this->assertSame($mockcategory2, $argument);
|
||||
|
||||
// Argument 2 is referring to category 3.
|
||||
$argument = array_shift($arguments);
|
||||
$this->assertInstanceOf(\core_course_category::class, $argument);
|
||||
$this->assertEquals($category3->id, $argument->id);
|
||||
}
|
||||
|
||||
public function test_get_course_category_contents_hook() {
|
||||
$category1 = \core_course_category::create(array('name' => 'Cat1'));
|
||||
$category2 = \core_course_category::create(array('name' => 'Cat2', 'parent' => $category1->id));
|
||||
|
||||
$mockcategory2 = $this->get_mock_category($category2);
|
||||
|
||||
// Define get_plugins_callback_function use in the mock, it is called twice for different callback in the form.
|
||||
$mockcategory2->expects($this->exactly(2))
|
||||
->method('get_plugins_callback_function')
|
||||
->withConsecutive(
|
||||
[$this->equalTo('can_course_category_delete')],
|
||||
[$this->equalTo('get_course_category_contents')]
|
||||
)
|
||||
->willReturn(
|
||||
['tool_unittest_can_course_category_delete'],
|
||||
['tool_unittest_get_course_category_contents']
|
||||
);
|
||||
|
||||
// Now configure fixture to return string for the callback.
|
||||
$content = 'Bunch of test artefacts';
|
||||
mock_hooks::set_get_course_category_contents_return($content);
|
||||
|
||||
$mform = new \core_course_deletecategory_form(null, $mockcategory2);
|
||||
$this->expectOutputRegex("/<li>$content<\/li>/");
|
||||
$mform->display();
|
||||
|
||||
// Verify passed arguments.
|
||||
$arguments = mock_hooks::get_calling_arguments();
|
||||
$this->assertCount(1, $arguments);
|
||||
|
||||
// Argument 1 is the same core_course_category instance.
|
||||
$argument = array_shift($arguments);
|
||||
$this->assertSame($mockcategory2, $argument);
|
||||
}
|
||||
}
|
||||
Vendored
+184
@@ -0,0 +1,184 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Fixture for mocking callbacks used in \core_course_category
|
||||
*
|
||||
* @package core_course
|
||||
* @category test
|
||||
* @copyright 2020 Ruslan Kabalin
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace tests\core_course {
|
||||
|
||||
/**
|
||||
* Class mock_hooks
|
||||
*
|
||||
* @package core_course
|
||||
* @category test
|
||||
* @copyright 2020 Ruslan Kabalin
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class mock_hooks {
|
||||
/** @var bool $cancoursecategorydelete */
|
||||
private static $cancoursecategorydelete = true;
|
||||
|
||||
/** @var bool $cancoursecategorydeletemove */
|
||||
private static $cancoursecategorydeletemove = true;
|
||||
|
||||
/** @var string $getcoursecategorycontents */
|
||||
private static $getcoursecategorycontents = '';
|
||||
|
||||
/** @var array $callingarguments */
|
||||
private static $callingarguments = [];
|
||||
|
||||
/**
|
||||
* Set calling arguments.
|
||||
*
|
||||
* This is supposed to be used in the callbacks to store arguments passed to callback.
|
||||
*
|
||||
* @param array $callingarguments
|
||||
*/
|
||||
public static function set_calling_arguments($callingarguments) {
|
||||
self::$callingarguments = $callingarguments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get calling arguments.
|
||||
*
|
||||
* This is supposed to be used in the test to verify arguments passed to callback.
|
||||
* This method also reset stored calling arguments.
|
||||
*
|
||||
* @return array $callingarguments
|
||||
*/
|
||||
public static function get_calling_arguments() {
|
||||
$callingarguments = self::$callingarguments;
|
||||
self::$callingarguments = [];
|
||||
return $callingarguments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get can_course_category_delete callback return.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function get_can_course_category_delete_return() : bool {
|
||||
return self::$cancoursecategorydelete;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets can_course_category_delete callback return.
|
||||
*
|
||||
* @param bool $return
|
||||
*/
|
||||
public static function set_can_course_category_delete_return(bool $return) {
|
||||
self::$cancoursecategorydelete = $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get can_course_category_delete_move callback return.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function get_can_course_category_delete_move_return() : bool {
|
||||
return self::$cancoursecategorydeletemove;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets can_course_category_delete_move callback return.
|
||||
*
|
||||
* @param bool $return
|
||||
*/
|
||||
public static function set_can_course_category_delete_move_return(bool $return) {
|
||||
self::$cancoursecategorydeletemove = $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get get_course_category_contents callback return.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_get_course_category_contents_return() : string {
|
||||
return self::$getcoursecategorycontents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets get_course_category_contents callback return.
|
||||
*
|
||||
* @param string $return
|
||||
*/
|
||||
public static function set_get_course_category_contents_return(string $return) {
|
||||
self::$getcoursecategorycontents = $return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
/**
|
||||
* Test pre_course_category_delete callback.
|
||||
*
|
||||
* @param object $category
|
||||
*/
|
||||
function tool_unittest_pre_course_category_delete(object $category) {
|
||||
\tests\core_course\mock_hooks::set_calling_arguments(func_get_args());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test pre_course_category_delete_move callback.
|
||||
*
|
||||
* @param core_course_category $category
|
||||
* @param core_course_category $newcategory
|
||||
*/
|
||||
function tool_unittest_pre_course_category_delete_move(core_course_category $category, core_course_category $newcategory) {
|
||||
\tests\core_course\mock_hooks::set_calling_arguments(func_get_args());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test can_course_category_delete callback.
|
||||
*
|
||||
* @param core_course_category $category
|
||||
* @return bool
|
||||
*/
|
||||
function tool_unittest_can_course_category_delete(core_course_category $category) {
|
||||
\tests\core_course\mock_hooks::set_calling_arguments(func_get_args());
|
||||
return \tests\core_course\mock_hooks::get_can_course_category_delete_return();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test can_course_category_delete_move callback.
|
||||
*
|
||||
* @param core_course_category $category
|
||||
* @param core_course_category $newcategory
|
||||
* @return bool
|
||||
*/
|
||||
function tool_unittest_can_course_category_delete_move(core_course_category $category, core_course_category $newcategory) {
|
||||
\tests\core_course\mock_hooks::set_calling_arguments(func_get_args());
|
||||
return \tests\core_course\mock_hooks::get_can_course_category_delete_move_return();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test get_course_category_contents callback.
|
||||
*
|
||||
* @param core_course_category $category
|
||||
* @return string
|
||||
*/
|
||||
function tool_unittest_get_course_category_contents(core_course_category $category) {
|
||||
\tests\core_course\mock_hooks::set_calling_arguments(func_get_args());
|
||||
return \tests\core_course\mock_hooks::get_get_course_category_contents_return();
|
||||
}
|
||||
}
|
||||
@@ -405,6 +405,7 @@ $string['moduledoesnotexist'] = 'This module does not exist';
|
||||
$string['moduleinstancedoesnotexist'] = 'The instance of this module does not exist';
|
||||
$string['modulemissingcode'] = 'Module {$a} is missing the code needed to perform this function';
|
||||
$string['movecatcontentstoroot'] = 'Moving the category content to root is not allowed. You must move the contents to an existing category!';
|
||||
$string['movecatcontentstoselected'] = 'Some of the category content can not be moved into selected category.';
|
||||
$string['movecategorynotpossible'] = 'You cannot move category \'{$a}\' into the selected category.';
|
||||
$string['movecategoryownparent'] = 'You cannot make category \'{$a}\' a parent of itself.';
|
||||
$string['movecategoryparentconflict'] = 'You cannot make category \'{$a}\' a subcategory of one of its own subcategories.';
|
||||
|
||||
@@ -33,6 +33,7 @@ defined('MOODLE_INTERNAL') || die();
|
||||
* Extra information about event.
|
||||
*
|
||||
* - string name: category name.
|
||||
* - string contentmovedcategoryid: (optional) category id where content was moved on deletion
|
||||
* }
|
||||
*
|
||||
* @package core
|
||||
@@ -71,7 +72,11 @@ class course_category_deleted extends base {
|
||||
* @return string
|
||||
*/
|
||||
public function get_description() {
|
||||
return "The user with id '$this->userid' deleted the course category with id '$this->objectid'.";
|
||||
$descr = "The user with id '$this->userid' deleted the course category with id '$this->objectid'.";
|
||||
if (!empty($this->other['contentmovedcategoryid'])) {
|
||||
$descr .= " Its content has been moved to category with id '{$this->other['contentmovedcategoryid']}'.";
|
||||
}
|
||||
return $descr;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -71,6 +71,20 @@ information provided here is intended especially for developers.
|
||||
- cron_execute_plugin_type()
|
||||
- cron_bc_hack_plugin_functions()
|
||||
Please, use the Task API instead: https://docs.moodle.org/dev/Task_API
|
||||
* Introduce new hooks for plugin developers:
|
||||
- <component>_can_course_category_delete($category)
|
||||
- <component>_can_course_category_delete_move($category, $newcategory)
|
||||
These hooks allow plugin developers greater control over category deletion. Plugin can return false in those
|
||||
functions if category deletion or deletion with content move to the new parent category is not permitted.
|
||||
Both $category and $newcategory params are instances of core_course_category class.
|
||||
- <component>_pre_course_category_delete_move($category, $newcategory)
|
||||
This hook is expanding functionality of existing <component>_pre_course_category_delete hook and allow plugin developers
|
||||
to execute code prior to category deletion when its content is moved to another category.
|
||||
Both $category and $newcategory params are instances of core_course_category class.
|
||||
- <component>_get_course_category_contents($category)
|
||||
This hook allow plugin developers to add information that is displayed on category deletion form. Function should
|
||||
return string, which will be added to the list of category contents shown on the form. $category param is an instance
|
||||
of core_course_category class.
|
||||
|
||||
=== 3.8 ===
|
||||
* Add CLI option to notify all cron tasks to stop: admin/cli/cron.php --stop
|
||||
|
||||
Reference in New Issue
Block a user