Merge branch 'MDL-74936' of https://github.com/skodak/moodle
This commit is contained in:
@@ -80,8 +80,15 @@ class core_role_preset {
|
||||
$top->appendChild($contextlevels);
|
||||
foreach (get_role_contextlevels($roleid) as $level) {
|
||||
$name = context_helper::get_class_for_level($level);
|
||||
$name = preg_replace('/^context_/', '', $name);
|
||||
$contextlevels->appendChild($dom->createElement('level', $name));
|
||||
if (strpos($name, 'core\\context\\') === 0) {
|
||||
// Use short names of standard contexts for backwards compatibility.
|
||||
$value = preg_replace('/^core\\\\context\\\\/', '', $name);
|
||||
} else {
|
||||
// Must be a custom plugin level, use numbers to work around
|
||||
// potential duplicate short names of contexts in add-ons.
|
||||
$value = $level;
|
||||
}
|
||||
$contextlevels->appendChild($dom->createElement('level', $value));
|
||||
}
|
||||
|
||||
foreach (array('assign', 'override', 'switch', 'view') as $type) {
|
||||
@@ -197,9 +204,10 @@ class core_role_preset {
|
||||
$info['contextlevels'] = array();
|
||||
$levelmap = array_flip(context_helper::get_all_levels());
|
||||
foreach ($values as $value) {
|
||||
$level = 'context_'.$value;
|
||||
if (isset($levelmap[$level])) {
|
||||
$cl = $levelmap[$level];
|
||||
// Numbers and short names are supported since Moodle 4.2.
|
||||
$classname = \core\context_helper::parse_external_level($value);
|
||||
if ($classname) {
|
||||
$cl = $classname::LEVEL;
|
||||
$info['contextlevels'][$cl] = $cl;
|
||||
}
|
||||
}
|
||||
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<role>
|
||||
<shortname>courseviewer</shortname>
|
||||
<name/>
|
||||
<description/>
|
||||
<archetype/>
|
||||
<contextlevels>
|
||||
<level>system</level>
|
||||
<level>40</level>
|
||||
<level>50</level>
|
||||
</contextlevels>
|
||||
<allowassign/>
|
||||
<allowoverride/>
|
||||
<allowswitch/>
|
||||
<allowview>
|
||||
<shortname>student</shortname>
|
||||
</allowview>
|
||||
<permissions>
|
||||
<allow>moodle/category:viewhiddencategories</allow>
|
||||
<allow>moodle/course:ignoreavailabilityrestrictions</allow>
|
||||
<allow>moodle/course:viewhiddencourses</allow>
|
||||
<allow>moodle/site:configview</allow>
|
||||
</permissions>
|
||||
</role>
|
||||
@@ -76,4 +76,20 @@ class preset_test extends \advanced_testcase {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers \core_role_preset::parse_preset
|
||||
*/
|
||||
public function test_mixed_levels() {
|
||||
// The problem here is that we cannot guarantee plugin contexts
|
||||
// have unique short names, so we have to also support level numbers.
|
||||
$xml = file_get_contents(__DIR__ . '/fixtures/mixed_levels.xml');
|
||||
$this->assertTrue(\core_role_preset::is_valid_preset($xml));
|
||||
|
||||
$preset = \core_role_preset::parse_preset($xml);
|
||||
$expected = [\core\context\system::LEVEL, \core\context\coursecat::LEVEL, \core\context\course::LEVEL];
|
||||
$expected = array_combine($expected, $expected);
|
||||
$this->assertSame($expected, $preset['contextlevels']);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,6 +47,12 @@ class data_registry {
|
||||
* @return string[]
|
||||
*/
|
||||
public static function var_names_from_context($classname, $pluginname = '') {
|
||||
// Unfortunately authors of privacy API did not expect that we would be
|
||||
// on day fixing auto-loading of context classes.
|
||||
// The best way would have been probably level numbers at the end of vars,
|
||||
// but it is probably too late to fix it.
|
||||
$classname = preg_replace('/^[a-z0-9_]+\\\\context\\\\/', 'context_', $classname);
|
||||
|
||||
$pluginname = trim($pluginname ?? '');
|
||||
if (!empty($pluginname)) {
|
||||
$categoryvar = $classname . '_' . $pluginname . '_category';
|
||||
|
||||
+14
-2796
File diff suppressed because it is too large
Load Diff
@@ -1148,53 +1148,12 @@ EOF;
|
||||
* @return context
|
||||
*/
|
||||
public static function get_context(string $levelname, string $contextref): context {
|
||||
global $DB;
|
||||
|
||||
// Getting context levels and names (we will be using the English ones as it is the test site language).
|
||||
$contextlevels = context_helper::get_all_levels();
|
||||
$contextnames = array();
|
||||
foreach ($contextlevels as $level => $classname) {
|
||||
$contextnames[context_helper::get_level_name($level)] = $level;
|
||||
$context = \core\context_helper::resolve_behat_reference($levelname, $contextref);
|
||||
if ($context) {
|
||||
return $context;
|
||||
}
|
||||
|
||||
if (empty($contextnames[$levelname])) {
|
||||
throw new Exception('The specified "' . $levelname . '" context level does not exist');
|
||||
}
|
||||
$contextlevel = $contextnames[$levelname];
|
||||
|
||||
// Return it, we don't need to look for other internal ids.
|
||||
if ($contextlevel == CONTEXT_SYSTEM) {
|
||||
return context_system::instance();
|
||||
}
|
||||
|
||||
switch ($contextlevel) {
|
||||
|
||||
case CONTEXT_USER:
|
||||
$instanceid = $DB->get_field('user', 'id', array('username' => $contextref));
|
||||
break;
|
||||
|
||||
case CONTEXT_COURSECAT:
|
||||
$instanceid = $DB->get_field('course_categories', 'id', array('idnumber' => $contextref));
|
||||
break;
|
||||
|
||||
case CONTEXT_COURSE:
|
||||
$instanceid = $DB->get_field('course', 'id', array('shortname' => $contextref));
|
||||
break;
|
||||
|
||||
case CONTEXT_MODULE:
|
||||
$instanceid = $DB->get_field('course_modules', 'id', array('idnumber' => $contextref));
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
$contextclass = $contextlevels[$contextlevel];
|
||||
if (!$context = $contextclass::instance($instanceid, IGNORE_MISSING)) {
|
||||
throw new Exception('The specified "' . $contextref . '" context reference does not exist');
|
||||
}
|
||||
|
||||
return $context;
|
||||
throw new Exception("The specified context \"$levelname, $contextref\" does not exist");
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,268 @@
|
||||
<?php
|
||||
// This file is part of Moodle - https://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
namespace core\context;
|
||||
|
||||
use core\context;
|
||||
use stdClass;
|
||||
use coding_exception, moodle_url;
|
||||
|
||||
/**
|
||||
* Block context class
|
||||
*
|
||||
* @package core_access
|
||||
* @category access
|
||||
* @copyright Petr Skoda
|
||||
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @since Moodle 4.2
|
||||
*/
|
||||
class block extends context {
|
||||
/** @var int numeric context level value matching legacy CONTEXT_BLOCK */
|
||||
public const LEVEL = 80;
|
||||
|
||||
/**
|
||||
* Please use \core\context\block::instance($blockinstanceid) if you need the instance of context.
|
||||
* Alternatively if you know only the context id use \core\context::instance_by_id($contextid)
|
||||
*
|
||||
* @param stdClass $record
|
||||
*/
|
||||
protected function __construct(stdClass $record) {
|
||||
parent::__construct($record);
|
||||
if ($record->contextlevel != self::LEVEL) {
|
||||
throw new coding_exception('Invalid $record->contextlevel in core\context\block constructor');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns short context name.
|
||||
*
|
||||
* @since Moodle 4.2
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_short_name(): string {
|
||||
return 'block';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns human readable context level name.
|
||||
*
|
||||
* @return string the human readable context level name.
|
||||
*/
|
||||
public static function get_level_name() {
|
||||
return get_string('block');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns human readable context identifier.
|
||||
*
|
||||
* @param boolean $withprefix whether to prefix the name of the context with Block
|
||||
* @param boolean $short does not apply to block context
|
||||
* @param boolean $escape does not apply to block context
|
||||
* @return string the human readable context name.
|
||||
*/
|
||||
public function get_context_name($withprefix = true, $short = false, $escape = true) {
|
||||
global $DB, $CFG;
|
||||
|
||||
$name = '';
|
||||
if ($blockinstance = $DB->get_record('block_instances', array('id' => $this->_instanceid))) {
|
||||
global $CFG;
|
||||
require_once("$CFG->dirroot/blocks/moodleblock.class.php");
|
||||
require_once("$CFG->dirroot/blocks/$blockinstance->blockname/block_$blockinstance->blockname.php");
|
||||
$blockname = "block_$blockinstance->blockname";
|
||||
if ($blockobject = new $blockname()) {
|
||||
if ($withprefix) {
|
||||
$name = get_string('block').': ';
|
||||
}
|
||||
$name .= $blockobject->title;
|
||||
}
|
||||
}
|
||||
|
||||
return $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the most relevant URL for this context.
|
||||
*
|
||||
* @return moodle_url
|
||||
*/
|
||||
public function get_url() {
|
||||
$parentcontexts = $this->get_parent_context();
|
||||
return $parentcontexts->get_url();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of all possible parent context levels.
|
||||
* @since Moodle 4.2
|
||||
*
|
||||
* @return int[]
|
||||
*/
|
||||
public static function get_possible_parent_levels(): array {
|
||||
// Blocks may be added to any other context instance.
|
||||
$alllevels = \core\context_helper::get_all_levels();
|
||||
unset($alllevels[self::LEVEL]);
|
||||
return array_keys($alllevels);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns array of relevant context capability records.
|
||||
*
|
||||
* @param string $sort
|
||||
* @return array
|
||||
*/
|
||||
public function get_capabilities(string $sort = self::DEFAULT_CAPABILITY_SORT) {
|
||||
global $DB;
|
||||
|
||||
$bi = $DB->get_record('block_instances', array('id' => $this->_instanceid));
|
||||
|
||||
$select = '(contextlevel = :level AND component = :component)';
|
||||
$params = [
|
||||
'level' => self::LEVEL,
|
||||
'component' => 'block_' . $bi->blockname,
|
||||
];
|
||||
|
||||
$extracaps = block_method_result($bi->blockname, 'get_extra_capabilities');
|
||||
if ($extracaps) {
|
||||
list($extra, $extraparams) = $DB->get_in_or_equal($extracaps, SQL_PARAMS_NAMED, 'cap');
|
||||
$select .= " OR name $extra";
|
||||
$params = array_merge($params, $extraparams);
|
||||
}
|
||||
|
||||
return $DB->get_records_select('capabilities', $select, $params, $sort);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this context part of any course? If yes return course context.
|
||||
*
|
||||
* @param bool $strict true means throw exception if not found, false means return false if not found
|
||||
* @return course context of the enclosing course, null if not found or exception
|
||||
*/
|
||||
public function get_course_context($strict = true) {
|
||||
$parentcontext = $this->get_parent_context();
|
||||
return $parentcontext->get_course_context($strict);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns block context instance.
|
||||
*
|
||||
* @param int $blockinstanceid id from {block_instances} table.
|
||||
* @param int $strictness
|
||||
* @return block|false context instance
|
||||
*/
|
||||
public static function instance($blockinstanceid, $strictness = MUST_EXIST) {
|
||||
global $DB;
|
||||
|
||||
if ($context = context::cache_get(self::LEVEL, $blockinstanceid)) {
|
||||
return $context;
|
||||
}
|
||||
|
||||
if (!$record = $DB->get_record('context', array('contextlevel' => self::LEVEL, 'instanceid' => $blockinstanceid))) {
|
||||
if ($bi = $DB->get_record('block_instances', array('id' => $blockinstanceid), 'id,parentcontextid', $strictness)) {
|
||||
$parentcontext = context::instance_by_id($bi->parentcontextid);
|
||||
$record = context::insert_context_record(self::LEVEL, $bi->id, $parentcontext->path);
|
||||
}
|
||||
}
|
||||
|
||||
if ($record) {
|
||||
$context = new block($record);
|
||||
context::cache_add($context);
|
||||
return $context;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Block do not have child contexts...
|
||||
* @return array
|
||||
*/
|
||||
public function get_child_contexts() {
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create missing context instances at block context level
|
||||
*/
|
||||
protected static function create_level_instances() {
|
||||
global $DB;
|
||||
|
||||
$sql = <<<EOF
|
||||
INSERT INTO {context} (
|
||||
contextlevel,
|
||||
instanceid
|
||||
) SELECT
|
||||
:contextlevel,
|
||||
bi.id as instanceid
|
||||
FROM {block_instances} bi
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 'x' FROM {context} cx WHERE bi.id = cx.instanceid AND cx.contextlevel = :existingcontextlevel
|
||||
)
|
||||
EOF;
|
||||
|
||||
$DB->execute($sql, [
|
||||
'contextlevel' => self::LEVEL,
|
||||
'existingcontextlevel' => self::LEVEL,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns sql necessary for purging of stale context instances.
|
||||
*
|
||||
* @return string cleanup SQL
|
||||
*/
|
||||
protected static function get_cleanup_sql() {
|
||||
$sql = "
|
||||
SELECT c.*
|
||||
FROM {context} c
|
||||
LEFT OUTER JOIN {block_instances} bi ON c.instanceid = bi.id
|
||||
WHERE bi.id IS NULL AND c.contextlevel = ".self::LEVEL."
|
||||
";
|
||||
|
||||
return $sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rebuild context paths and depths at block context level.
|
||||
*
|
||||
* @param bool $force
|
||||
*/
|
||||
protected static function build_paths($force) {
|
||||
global $DB;
|
||||
|
||||
if ($force || $DB->record_exists_select('context', "contextlevel = ".self::LEVEL." AND (depth = 0 OR path IS NULL)")) {
|
||||
if ($force) {
|
||||
$ctxemptyclause = '';
|
||||
} else {
|
||||
$ctxemptyclause = "AND (ctx.path IS NULL OR ctx.depth = 0)";
|
||||
}
|
||||
|
||||
// The pctx.path IS NOT NULL prevents fatal problems with broken block instances that point to invalid context parent.
|
||||
$sql = "INSERT INTO {context_temp} (id, path, depth, locked)
|
||||
SELECT ctx.id, ".$DB->sql_concat('pctx.path', "'/'", 'ctx.id').", pctx.depth+1, ctx.locked
|
||||
FROM {context} ctx
|
||||
JOIN {block_instances} bi ON (bi.id = ctx.instanceid AND ctx.contextlevel = " . self::LEVEL . ")
|
||||
JOIN {context} pctx ON (pctx.id = bi.parentcontextid)
|
||||
WHERE (pctx.path IS NOT NULL AND pctx.depth > 0)
|
||||
$ctxemptyclause";
|
||||
$trans = $DB->start_delegated_transaction();
|
||||
$DB->delete_records('context_temp');
|
||||
$DB->execute($sql);
|
||||
context::merge_context_temp_table();
|
||||
$DB->delete_records('context_temp');
|
||||
$trans->allow_commit();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,298 @@
|
||||
<?php
|
||||
// This file is part of Moodle - https://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
namespace core\context;
|
||||
|
||||
use core\context;
|
||||
use stdClass;
|
||||
use coding_exception, moodle_url;
|
||||
|
||||
/**
|
||||
* Course context class
|
||||
*
|
||||
* @package core_access
|
||||
* @category access
|
||||
* @copyright Petr Skoda
|
||||
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @since Moodle 4.2
|
||||
*/
|
||||
class course extends context {
|
||||
/** @var int numeric context level value matching legacy CONTEXT_COURSE */
|
||||
public const LEVEL = 50;
|
||||
|
||||
/**
|
||||
* Please use \core\context\course::instance($courseid) if you need the instance of context.
|
||||
* Alternatively if you know only the context id use \core\context::instance_by_id($contextid)
|
||||
*
|
||||
* @param stdClass $record
|
||||
*/
|
||||
protected function __construct(stdClass $record) {
|
||||
parent::__construct($record);
|
||||
if ($record->contextlevel != self::LEVEL) {
|
||||
throw new coding_exception('Invalid $record->contextlevel in core\context\course constructor.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns short context name.
|
||||
*
|
||||
* @since Moodle 4.2
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_short_name(): string {
|
||||
return 'course';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns human readable context level name.
|
||||
*
|
||||
* @return string the human readable context level name.
|
||||
*/
|
||||
public static function get_level_name() {
|
||||
return get_string('course');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns human readable context identifier.
|
||||
*
|
||||
* @param boolean $withprefix whether to prefix the name of the context with Course
|
||||
* @param boolean $short whether to use the short name of the thing.
|
||||
* @param bool $escape Whether the returned category name is to be HTML escaped or not.
|
||||
* @return string the human readable context name.
|
||||
*/
|
||||
public function get_context_name($withprefix = true, $short = false, $escape = true) {
|
||||
global $DB;
|
||||
|
||||
$name = '';
|
||||
if ($this->_instanceid == SITEID) {
|
||||
$name = get_string('frontpage', 'admin');
|
||||
} else {
|
||||
if ($course = $DB->get_record('course', array('id' => $this->_instanceid))) {
|
||||
if ($withprefix) {
|
||||
$name = get_string('course').': ';
|
||||
}
|
||||
if ($short) {
|
||||
if (!$escape) {
|
||||
$name .= format_string($course->shortname, true, array('context' => $this, 'escape' => false));
|
||||
} else {
|
||||
$name .= format_string($course->shortname, true, array('context' => $this));
|
||||
}
|
||||
} else {
|
||||
if (!$escape) {
|
||||
$name .= format_string(get_course_display_name_for_list($course), true, array('context' => $this,
|
||||
'escape' => false));
|
||||
} else {
|
||||
$name .= format_string(get_course_display_name_for_list($course), true, array('context' => $this));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the most relevant URL for this context.
|
||||
*
|
||||
* @return moodle_url
|
||||
*/
|
||||
public function get_url() {
|
||||
if ($this->_instanceid != SITEID) {
|
||||
return new moodle_url('/course/view.php', array('id' => $this->_instanceid));
|
||||
}
|
||||
|
||||
return new moodle_url('/');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns context instance database name.
|
||||
*
|
||||
* @return string|null table name for all levels except system.
|
||||
*/
|
||||
protected static function get_instance_table(): ?string {
|
||||
return 'course';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of columns that can be used from behat
|
||||
* to look up context by reference.
|
||||
*
|
||||
* @return array list of column names from instance table
|
||||
*/
|
||||
protected static function get_behat_reference_columns(): array {
|
||||
return ['shortname'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of all role archetypes that are compatible
|
||||
* with role assignments in context level.
|
||||
* @since Moodle 4.2
|
||||
*
|
||||
* @return int[]
|
||||
*/
|
||||
protected static function get_compatible_role_archetypes(): array {
|
||||
return ['manager', 'editingteacher', 'teacher', 'student'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of all possible parent context levels.
|
||||
* @since Moodle 4.2
|
||||
*
|
||||
* @return int[]
|
||||
*/
|
||||
public static function get_possible_parent_levels(): array {
|
||||
return [coursecat::LEVEL];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns array of relevant context capability records.
|
||||
*
|
||||
* @param string $sort
|
||||
* @return array
|
||||
*/
|
||||
public function get_capabilities(string $sort = self::DEFAULT_CAPABILITY_SORT) {
|
||||
global $DB;
|
||||
|
||||
$levels = \core\context_helper::get_child_levels(self::LEVEL);
|
||||
$levels[] = self::LEVEL;
|
||||
|
||||
return $DB->get_records_list('capabilities', 'contextlevel', $levels, $sort);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this context part of any course? If yes return course context.
|
||||
*
|
||||
* @param bool $strict true means throw exception if not found, false means return false if not found
|
||||
* @return course context of the enclosing course, null if not found or exception
|
||||
*/
|
||||
public function get_course_context($strict = true) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns course context instance.
|
||||
*
|
||||
* @param int $courseid id from {course} table
|
||||
* @param int $strictness
|
||||
* @return course|false context instance
|
||||
*/
|
||||
public static function instance($courseid, $strictness = MUST_EXIST) {
|
||||
global $DB;
|
||||
|
||||
if ($context = context::cache_get(self::LEVEL, $courseid)) {
|
||||
return $context;
|
||||
}
|
||||
|
||||
if (!$record = $DB->get_record('context', array('contextlevel' => self::LEVEL, 'instanceid' => $courseid))) {
|
||||
if ($course = $DB->get_record('course', array('id' => $courseid), 'id,category', $strictness)) {
|
||||
if ($course->category) {
|
||||
$parentcontext = coursecat::instance($course->category);
|
||||
$record = context::insert_context_record(self::LEVEL, $course->id, $parentcontext->path);
|
||||
} else {
|
||||
$record = context::insert_context_record(self::LEVEL, $course->id, '/'.SYSCONTEXTID, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($record) {
|
||||
$context = new course($record);
|
||||
context::cache_add($context);
|
||||
return $context;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create missing context instances at course context level
|
||||
*/
|
||||
protected static function create_level_instances() {
|
||||
global $DB;
|
||||
|
||||
$sql = "SELECT ".self::LEVEL.", c.id
|
||||
FROM {course} c
|
||||
WHERE NOT EXISTS (SELECT 'x'
|
||||
FROM {context} cx
|
||||
WHERE c.id = cx.instanceid AND cx.contextlevel=".self::LEVEL.")";
|
||||
$contextdata = $DB->get_recordset_sql($sql);
|
||||
foreach ($contextdata as $context) {
|
||||
context::insert_context_record(self::LEVEL, $context->id, null);
|
||||
}
|
||||
$contextdata->close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns sql necessary for purging of stale context instances.
|
||||
*
|
||||
* @return string cleanup SQL
|
||||
*/
|
||||
protected static function get_cleanup_sql() {
|
||||
$sql = "
|
||||
SELECT c.*
|
||||
FROM {context} c
|
||||
LEFT OUTER JOIN {course} co ON c.instanceid = co.id
|
||||
WHERE co.id IS NULL AND c.contextlevel = ".self::LEVEL."
|
||||
";
|
||||
|
||||
return $sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rebuild context paths and depths at course context level.
|
||||
*
|
||||
* @param bool $force
|
||||
*/
|
||||
protected static function build_paths($force) {
|
||||
global $DB;
|
||||
|
||||
if ($force || $DB->record_exists_select('context', "contextlevel = ".self::LEVEL." AND (depth = 0 OR path IS NULL)")) {
|
||||
if ($force) {
|
||||
$ctxemptyclause = $emptyclause = '';
|
||||
} else {
|
||||
$ctxemptyclause = "AND (ctx.path IS NULL OR ctx.depth = 0)";
|
||||
$emptyclause = "AND ({context}.path IS NULL OR {context}.depth = 0)";
|
||||
}
|
||||
|
||||
$base = '/'.SYSCONTEXTID;
|
||||
|
||||
// Standard frontpage.
|
||||
$sql = "UPDATE {context}
|
||||
SET depth = 2,
|
||||
path = ".$DB->sql_concat("'$base/'", 'id')."
|
||||
WHERE contextlevel = ".self::LEVEL."
|
||||
AND EXISTS (SELECT 'x'
|
||||
FROM {course} c
|
||||
WHERE c.id = {context}.instanceid AND c.category = 0)
|
||||
$emptyclause";
|
||||
$DB->execute($sql);
|
||||
|
||||
// Standard courses.
|
||||
$sql = "INSERT INTO {context_temp} (id, path, depth, locked)
|
||||
SELECT ctx.id, ".$DB->sql_concat('pctx.path', "'/'", 'ctx.id').", pctx.depth+1, ctx.locked
|
||||
FROM {context} ctx
|
||||
JOIN {course} c ON (c.id = ctx.instanceid AND ctx.contextlevel = ".self::LEVEL." AND c.category <> 0)
|
||||
JOIN {context} pctx ON (pctx.instanceid = c.category AND pctx.contextlevel = ".coursecat::LEVEL.")
|
||||
WHERE pctx.path IS NOT NULL AND pctx.depth > 0
|
||||
$ctxemptyclause";
|
||||
$trans = $DB->start_delegated_transaction();
|
||||
$DB->delete_records('context_temp');
|
||||
$DB->execute($sql);
|
||||
context::merge_context_temp_table();
|
||||
$DB->delete_records('context_temp');
|
||||
$trans->allow_commit();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,304 @@
|
||||
<?php
|
||||
// This file is part of Moodle - https://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
namespace core\context;
|
||||
|
||||
use core\context;
|
||||
use stdClass;
|
||||
use coding_exception, moodle_url;
|
||||
|
||||
/**
|
||||
* Course category context class
|
||||
*
|
||||
* @package core_access
|
||||
* @category access
|
||||
* @copyright Petr Skoda
|
||||
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @since Moodle 4.2
|
||||
*/
|
||||
class coursecat extends context {
|
||||
/** @var int numeric context level value matching legacy CONTEXT_COURSECAT */
|
||||
public const LEVEL = 40;
|
||||
|
||||
/**
|
||||
* Please use \core\context\coursecat::instance($coursecatid) if you need the instance of context.
|
||||
* Alternatively if you know only the context id use \core\context::instance_by_id($contextid)
|
||||
*
|
||||
* @param stdClass $record
|
||||
*/
|
||||
protected function __construct(stdClass $record) {
|
||||
parent::__construct($record);
|
||||
if ($record->contextlevel != self::LEVEL) {
|
||||
throw new coding_exception('Invalid $record->contextlevel in core\context\coursecat constructor.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns short context name.
|
||||
*
|
||||
* @since Moodle 4.2
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_short_name(): string {
|
||||
return 'coursecat';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns human readable context level name.
|
||||
*
|
||||
* @return string the human readable context level name.
|
||||
*/
|
||||
public static function get_level_name() {
|
||||
return get_string('category');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns human readable context identifier.
|
||||
*
|
||||
* @param boolean $withprefix whether to prefix the name of the context with Category
|
||||
* @param boolean $short does not apply to course categories
|
||||
* @param boolean $escape Whether the returned name of the context is to be HTML escaped or not.
|
||||
* @return string the human readable context name.
|
||||
*/
|
||||
public function get_context_name($withprefix = true, $short = false, $escape = true) {
|
||||
global $DB;
|
||||
|
||||
$name = '';
|
||||
if ($category = $DB->get_record('course_categories', array('id' => $this->_instanceid))) {
|
||||
if ($withprefix) {
|
||||
$name = get_string('category').': ';
|
||||
}
|
||||
if (!$escape) {
|
||||
$name .= format_string($category->name, true, array('context' => $this, 'escape' => false));
|
||||
} else {
|
||||
$name .= format_string($category->name, true, array('context' => $this));
|
||||
}
|
||||
}
|
||||
return $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the most relevant URL for this context.
|
||||
*
|
||||
* @return moodle_url
|
||||
*/
|
||||
public function get_url() {
|
||||
return new moodle_url('/course/index.php', array('categoryid' => $this->_instanceid));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns context instance database name.
|
||||
*
|
||||
* @return string|null table name for all levels except system.
|
||||
*/
|
||||
protected static function get_instance_table(): ?string {
|
||||
return 'course_categories';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of columns that can be used from behat
|
||||
* to look up context by reference.
|
||||
*
|
||||
* @return array list of column names from instance table
|
||||
*/
|
||||
protected static function get_behat_reference_columns(): array {
|
||||
return ['idnumber'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of all role archetypes that are compatible
|
||||
* with role assignments in context level.
|
||||
* @since Moodle 4.2
|
||||
*
|
||||
* @return int[]
|
||||
*/
|
||||
protected static function get_compatible_role_archetypes(): array {
|
||||
return ['manager', 'coursecreator'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of all possible parent context levels.
|
||||
* @since Moodle 4.2
|
||||
*
|
||||
* @return int[]
|
||||
*/
|
||||
public static function get_possible_parent_levels(): array {
|
||||
return [system::LEVEL, self::LEVEL];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns array of relevant context capability records.
|
||||
*
|
||||
* @param string $sort
|
||||
* @return array
|
||||
*/
|
||||
public function get_capabilities(string $sort = self::DEFAULT_CAPABILITY_SORT) {
|
||||
global $DB;
|
||||
|
||||
$levels = \core\context_helper::get_child_levels(self::LEVEL);
|
||||
$levels[] = self::LEVEL;
|
||||
|
||||
return $DB->get_records_list('capabilities', 'contextlevel', $levels, $sort);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns course category context instance.
|
||||
*
|
||||
* @param int $categoryid id from {course_categories} table
|
||||
* @param int $strictness
|
||||
* @return coursecat|false context instance
|
||||
*/
|
||||
public static function instance($categoryid, $strictness = MUST_EXIST) {
|
||||
global $DB;
|
||||
|
||||
if ($context = context::cache_get(self::LEVEL, $categoryid)) {
|
||||
return $context;
|
||||
}
|
||||
|
||||
if (!$record = $DB->get_record('context', array('contextlevel' => self::LEVEL, 'instanceid' => $categoryid))) {
|
||||
if ($category = $DB->get_record('course_categories', array('id' => $categoryid), 'id,parent', $strictness)) {
|
||||
if ($category->parent) {
|
||||
$parentcontext = self::instance($category->parent);
|
||||
$record = context::insert_context_record(self::LEVEL, $category->id, $parentcontext->path);
|
||||
} else {
|
||||
$record = context::insert_context_record(self::LEVEL, $category->id, '/'.SYSCONTEXTID, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($record) {
|
||||
$context = new coursecat($record);
|
||||
context::cache_add($context);
|
||||
return $context;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns immediate child contexts of category and all subcategories,
|
||||
* children of subcategories and courses are not returned.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_child_contexts() {
|
||||
global $DB;
|
||||
|
||||
if (empty($this->_path) || empty($this->_depth)) {
|
||||
debugging('Can not find child contexts of context '.$this->_id.' try rebuilding of context paths');
|
||||
return array();
|
||||
}
|
||||
|
||||
$sql = "SELECT ctx.*
|
||||
FROM {context} ctx
|
||||
WHERE ctx.path LIKE ? AND (ctx.depth = ? OR ctx.contextlevel = ?)";
|
||||
$params = array($this->_path.'/%', $this->depth + 1, self::LEVEL);
|
||||
$records = $DB->get_records_sql($sql, $params);
|
||||
|
||||
$result = array();
|
||||
foreach ($records as $record) {
|
||||
$result[$record->id] = context::create_instance_from_record($record);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create missing context instances at course category context level
|
||||
*/
|
||||
protected static function create_level_instances() {
|
||||
global $DB;
|
||||
|
||||
$sql = "SELECT ".self::LEVEL.", cc.id
|
||||
FROM {course_categories} cc
|
||||
WHERE NOT EXISTS (SELECT 'x'
|
||||
FROM {context} cx
|
||||
WHERE cc.id = cx.instanceid AND cx.contextlevel=".self::LEVEL.")";
|
||||
$contextdata = $DB->get_recordset_sql($sql);
|
||||
foreach ($contextdata as $context) {
|
||||
context::insert_context_record(self::LEVEL, $context->id, null);
|
||||
}
|
||||
$contextdata->close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns sql necessary for purging of stale context instances.
|
||||
*
|
||||
* @return string cleanup SQL
|
||||
*/
|
||||
protected static function get_cleanup_sql() {
|
||||
$sql = "
|
||||
SELECT c.*
|
||||
FROM {context} c
|
||||
LEFT OUTER JOIN {course_categories} cc ON c.instanceid = cc.id
|
||||
WHERE cc.id IS NULL AND c.contextlevel = ".self::LEVEL."
|
||||
";
|
||||
|
||||
return $sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rebuild context paths and depths at course category context level.
|
||||
*
|
||||
* @param bool $force
|
||||
*/
|
||||
protected static function build_paths($force) {
|
||||
global $DB;
|
||||
|
||||
if ($force || $DB->record_exists_select('context', "contextlevel = ".self::LEVEL." AND (depth = 0 OR path IS NULL)")) {
|
||||
if ($force) {
|
||||
$ctxemptyclause = $emptyclause = '';
|
||||
} else {
|
||||
$ctxemptyclause = "AND (ctx.path IS NULL OR ctx.depth = 0)";
|
||||
$emptyclause = "AND ({context}.path IS NULL OR {context}.depth = 0)";
|
||||
}
|
||||
|
||||
$base = '/'.SYSCONTEXTID;
|
||||
|
||||
// Normal top level categories.
|
||||
$sql = "UPDATE {context}
|
||||
SET depth=2,
|
||||
path=".$DB->sql_concat("'$base/'", 'id')."
|
||||
WHERE contextlevel=".self::LEVEL."
|
||||
AND EXISTS (SELECT 'x'
|
||||
FROM {course_categories} cc
|
||||
WHERE cc.id = {context}.instanceid AND cc.depth=1)
|
||||
$emptyclause";
|
||||
$DB->execute($sql);
|
||||
|
||||
// Deeper categories - one query per depthlevel.
|
||||
$maxdepth = $DB->get_field_sql("SELECT MAX(depth) FROM {course_categories}");
|
||||
for ($n = 2; $n <= $maxdepth; $n++) {
|
||||
$sql = "INSERT INTO {context_temp} (id, path, depth, locked)
|
||||
SELECT ctx.id, ".$DB->sql_concat('pctx.path', "'/'", 'ctx.id').", pctx.depth+1, ctx.locked
|
||||
FROM {context} ctx
|
||||
JOIN {course_categories} cc ON (cc.id = ctx.instanceid AND ctx.contextlevel = ".self::LEVEL."
|
||||
AND cc.depth = $n)
|
||||
JOIN {context} pctx ON (pctx.instanceid = cc.parent AND pctx.contextlevel = ".self::LEVEL.")
|
||||
WHERE pctx.path IS NOT NULL AND pctx.depth > 0
|
||||
$ctxemptyclause";
|
||||
$trans = $DB->start_delegated_transaction();
|
||||
$DB->delete_records('context_temp');
|
||||
$DB->execute($sql);
|
||||
context::merge_context_temp_table();
|
||||
$DB->delete_records('context_temp');
|
||||
$trans->allow_commit();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,349 @@
|
||||
<?php
|
||||
// This file is part of Moodle - https://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
namespace core\context;
|
||||
|
||||
use core\context;
|
||||
use stdClass;
|
||||
use coding_exception, moodle_url;
|
||||
|
||||
/**
|
||||
* Course module context class
|
||||
*
|
||||
* @package core_access
|
||||
* @category access
|
||||
* @copyright Petr Skoda
|
||||
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @since Moodle 4.2
|
||||
*/
|
||||
class module extends context {
|
||||
/** @var int numeric context level value matching legacy CONTEXT_MODULE */
|
||||
public const LEVEL = 70;
|
||||
|
||||
/**
|
||||
* Please use \core\context\module::instance($cmid) if you need the instance of context.
|
||||
* Alternatively if you know only the context id use \core\context::instance_by_id($contextid)
|
||||
*
|
||||
* @param stdClass $record
|
||||
*/
|
||||
protected function __construct(stdClass $record) {
|
||||
parent::__construct($record);
|
||||
if ($record->contextlevel != self::LEVEL) {
|
||||
throw new coding_exception('Invalid $record->contextlevel in core\context\module constructor.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns short context name.
|
||||
*
|
||||
* @since Moodle 4.2
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_short_name(): string {
|
||||
return 'module';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns human readable context level name.
|
||||
*
|
||||
* @return string the human readable context level name.
|
||||
*/
|
||||
public static function get_level_name() {
|
||||
return get_string('activitymodule');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns human readable context identifier.
|
||||
*
|
||||
* @param boolean $withprefix whether to prefix the name of the context with the
|
||||
* module name, e.g. Forum, Glossary, etc.
|
||||
* @param boolean $short does not apply to module context
|
||||
* @param boolean $escape Whether the returned name of the context is to be HTML escaped or not.
|
||||
* @return string the human readable context name.
|
||||
*/
|
||||
public function get_context_name($withprefix = true, $short = false, $escape = true) {
|
||||
global $DB;
|
||||
|
||||
$name = '';
|
||||
if ($cm = $DB->get_record_sql("SELECT cm.*, md.name AS modname
|
||||
FROM {course_modules} cm
|
||||
JOIN {modules} md ON md.id = cm.module
|
||||
WHERE cm.id = ?", array($this->_instanceid))) {
|
||||
if ($mod = $DB->get_record($cm->modname, array('id' => $cm->instance))) {
|
||||
if ($withprefix) {
|
||||
$name = get_string('modulename', $cm->modname).': ';
|
||||
}
|
||||
if (!$escape) {
|
||||
$name .= format_string($mod->name, true, array('context' => $this, 'escape' => false));
|
||||
} else {
|
||||
$name .= format_string($mod->name, true, array('context' => $this));
|
||||
}
|
||||
}
|
||||
}
|
||||
return $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the most relevant URL for this context.
|
||||
*
|
||||
* @return moodle_url
|
||||
*/
|
||||
public function get_url() {
|
||||
global $DB;
|
||||
|
||||
if ($modname = $DB->get_field_sql("SELECT md.name AS modname
|
||||
FROM {course_modules} cm
|
||||
JOIN {modules} md ON md.id = cm.module
|
||||
WHERE cm.id = ?", array($this->_instanceid))) {
|
||||
return new moodle_url('/mod/' . $modname . '/view.php', array('id' => $this->_instanceid));
|
||||
}
|
||||
|
||||
return new moodle_url('/');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns context instance database name.
|
||||
*
|
||||
* @return string|null table name for all levels except system.
|
||||
*/
|
||||
protected static function get_instance_table(): ?string {
|
||||
return 'course_modules';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of columns that can be used from behat
|
||||
* to look up context by reference.
|
||||
*
|
||||
* @return array list of column names from instance table
|
||||
*/
|
||||
protected static function get_behat_reference_columns(): array {
|
||||
return ['idnumber'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of all role archetypes that are compatible
|
||||
* with role assignments in context level.
|
||||
* @since Moodle 4.2
|
||||
*
|
||||
* @return int[]
|
||||
*/
|
||||
protected static function get_compatible_role_archetypes(): array {
|
||||
return ['editingteacher', 'teacher', 'student'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of all possible parent context levels.
|
||||
* @since Moodle 4.2
|
||||
*
|
||||
* @return int[]
|
||||
*/
|
||||
public static function get_possible_parent_levels(): array {
|
||||
return [course::LEVEL];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns array of relevant context capability records.
|
||||
*
|
||||
* @param string $sort
|
||||
* @return array
|
||||
*/
|
||||
public function get_capabilities(string $sort = self::DEFAULT_CAPABILITY_SORT) {
|
||||
global $DB, $CFG;
|
||||
|
||||
$cm = $DB->get_record('course_modules', array('id' => $this->_instanceid));
|
||||
$module = $DB->get_record('modules', array('id' => $cm->module));
|
||||
|
||||
$subcaps = array();
|
||||
|
||||
$modulepath = "{$CFG->dirroot}/mod/{$module->name}";
|
||||
if (file_exists("{$modulepath}/db/subplugins.json")) {
|
||||
$subplugins = (array) json_decode(file_get_contents("{$modulepath}/db/subplugins.json"))->plugintypes;
|
||||
} else if (file_exists("{$modulepath}/db/subplugins.php")) {
|
||||
debugging('Use of subplugins.php has been deprecated. ' .
|
||||
'Please update your plugin to provide a subplugins.json file instead.',
|
||||
DEBUG_DEVELOPER);
|
||||
$subplugins = array(); // Should be redefined in the file.
|
||||
include("{$modulepath}/db/subplugins.php");
|
||||
}
|
||||
|
||||
if (!empty($subplugins)) {
|
||||
foreach (array_keys($subplugins) as $subplugintype) {
|
||||
foreach (array_keys(\core_component::get_plugin_list($subplugintype)) as $subpluginname) {
|
||||
$subcaps = array_merge($subcaps, array_keys(load_capability_def($subplugintype.'_'.$subpluginname)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$modfile = "{$modulepath}/lib.php";
|
||||
$extracaps = array();
|
||||
if (file_exists($modfile)) {
|
||||
include_once($modfile);
|
||||
$modfunction = $module->name.'_get_extra_capabilities';
|
||||
if (function_exists($modfunction)) {
|
||||
$extracaps = $modfunction();
|
||||
}
|
||||
}
|
||||
|
||||
$extracaps = array_merge($subcaps, $extracaps);
|
||||
$extra = '';
|
||||
list($extra, $params) = $DB->get_in_or_equal(
|
||||
$extracaps, SQL_PARAMS_NAMED, 'cap0', true, '');
|
||||
if (!empty($extra)) {
|
||||
$extra = "OR name $extra";
|
||||
}
|
||||
|
||||
// Fetch the list of modules, and remove this one.
|
||||
$components = \core_component::get_component_list();
|
||||
$componentnames = $components['mod'];
|
||||
unset($componentnames["mod_{$module->name}"]);
|
||||
$componentnames = array_keys($componentnames);
|
||||
|
||||
// Exclude all other modules.
|
||||
list($notcompsql, $notcompparams) = $DB->get_in_or_equal($componentnames, SQL_PARAMS_NAMED, 'notcomp', false);
|
||||
$params = array_merge($params, $notcompparams);
|
||||
|
||||
// Exclude other component submodules.
|
||||
$i = 0;
|
||||
$ignorecomponents = [];
|
||||
foreach ($componentnames as $mod) {
|
||||
if ($subplugins = \core_component::get_subplugins($mod)) {
|
||||
foreach (array_keys($subplugins) as $subplugintype) {
|
||||
$paramname = "notlike{$i}";
|
||||
$ignorecomponents[] = $DB->sql_like('component', ":{$paramname}", true, true, true);
|
||||
$params[$paramname] = "{$subplugintype}_%";
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
$notlikesql = "(" . implode(' AND ', $ignorecomponents) . ")";
|
||||
|
||||
$sql = "SELECT *
|
||||
FROM {capabilities}
|
||||
WHERE (contextlevel = ".self::LEVEL."
|
||||
AND component {$notcompsql}
|
||||
AND {$notlikesql})
|
||||
$extra
|
||||
ORDER BY $sort";
|
||||
|
||||
return $DB->get_records_sql($sql, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this context part of any course? If yes return course context.
|
||||
*
|
||||
* @param bool $strict true means throw exception if not found, false means return false if not found
|
||||
* @return course|false context of the enclosing course, null if not found or exception
|
||||
*/
|
||||
public function get_course_context($strict = true) {
|
||||
return $this->get_parent_context();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns module context instance.
|
||||
*
|
||||
* @param int $cmid id of the record from {course_modules} table; pass cmid there, NOT id in the instance column
|
||||
* @param int $strictness
|
||||
* @return module|false context instance
|
||||
*/
|
||||
public static function instance($cmid, $strictness = MUST_EXIST) {
|
||||
global $DB;
|
||||
|
||||
if ($context = context::cache_get(self::LEVEL, $cmid)) {
|
||||
return $context;
|
||||
}
|
||||
|
||||
if (!$record = $DB->get_record('context', array('contextlevel' => self::LEVEL, 'instanceid' => $cmid))) {
|
||||
if ($cm = $DB->get_record('course_modules', array('id' => $cmid), 'id,course', $strictness)) {
|
||||
$parentcontext = course::instance($cm->course);
|
||||
$record = context::insert_context_record(self::LEVEL, $cm->id, $parentcontext->path);
|
||||
}
|
||||
}
|
||||
|
||||
if ($record) {
|
||||
$context = new module($record);
|
||||
context::cache_add($context);
|
||||
return $context;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create missing context instances at module context level
|
||||
*/
|
||||
protected static function create_level_instances() {
|
||||
global $DB;
|
||||
|
||||
$sql = "SELECT " . self::LEVEL . ", cm.id
|
||||
FROM {course_modules} cm
|
||||
WHERE NOT EXISTS (SELECT 'x'
|
||||
FROM {context} cx
|
||||
WHERE cm.id = cx.instanceid AND cx.contextlevel=" . self::LEVEL . ")";
|
||||
$contextdata = $DB->get_recordset_sql($sql);
|
||||
foreach ($contextdata as $context) {
|
||||
context::insert_context_record(self::LEVEL, $context->id, null);
|
||||
}
|
||||
$contextdata->close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns sql necessary for purging of stale context instances.
|
||||
*
|
||||
* @return string cleanup SQL
|
||||
*/
|
||||
protected static function get_cleanup_sql() {
|
||||
$sql = "
|
||||
SELECT c.*
|
||||
FROM {context} c
|
||||
LEFT OUTER JOIN {course_modules} cm ON c.instanceid = cm.id
|
||||
WHERE cm.id IS NULL AND c.contextlevel = " . self::LEVEL . "
|
||||
";
|
||||
|
||||
return $sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rebuild context paths and depths at module context level.
|
||||
*
|
||||
* @param bool $force
|
||||
*/
|
||||
protected static function build_paths($force) {
|
||||
global $DB;
|
||||
|
||||
if ($force || $DB->record_exists_select('context', "contextlevel = " . self::LEVEL . " AND (depth = 0 OR path IS NULL)")) {
|
||||
if ($force) {
|
||||
$ctxemptyclause = '';
|
||||
} else {
|
||||
$ctxemptyclause = "AND (ctx.path IS NULL OR ctx.depth = 0)";
|
||||
}
|
||||
|
||||
$sql = "INSERT INTO {context_temp} (id, path, depth, locked)
|
||||
SELECT ctx.id, ".$DB->sql_concat('pctx.path', "'/'", 'ctx.id').", pctx.depth+1, ctx.locked
|
||||
FROM {context} ctx
|
||||
JOIN {course_modules} cm ON (cm.id = ctx.instanceid AND ctx.contextlevel = " . self::LEVEL . ")
|
||||
JOIN {context} pctx ON (pctx.instanceid = cm.course AND pctx.contextlevel = " . course::LEVEL . ")
|
||||
WHERE pctx.path IS NOT NULL AND pctx.depth > 0
|
||||
$ctxemptyclause";
|
||||
$trans = $DB->start_delegated_transaction();
|
||||
$DB->delete_records('context_temp');
|
||||
$DB->execute($sql);
|
||||
context::merge_context_temp_table();
|
||||
$DB->delete_records('context_temp');
|
||||
$trans->allow_commit();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,307 @@
|
||||
<?php
|
||||
// This file is part of Moodle - https://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
namespace core\context;
|
||||
|
||||
use core\context;
|
||||
use stdClass;
|
||||
use coding_exception, moodle_url;
|
||||
|
||||
/**
|
||||
* System context class
|
||||
*
|
||||
* @package core_access
|
||||
* @category access
|
||||
* @copyright Petr Skoda
|
||||
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @since Moodle 4.2
|
||||
*/
|
||||
class system extends context {
|
||||
/** @var int numeric context level value matching legacy CONTEXT_SYSTEM */
|
||||
public const LEVEL = 10;
|
||||
|
||||
/**
|
||||
* Please use \core\context\system::instance() if you need the instance of context.
|
||||
*
|
||||
* @param stdClass $record
|
||||
*/
|
||||
protected function __construct(stdClass $record) {
|
||||
parent::__construct($record);
|
||||
if ($record->contextlevel != self::LEVEL) {
|
||||
throw new coding_exception('Invalid $record->contextlevel in core\context\system constructor.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns short context name.
|
||||
*
|
||||
* @since Moodle 4.2
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_short_name(): string {
|
||||
return 'system';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns human readable context level name.
|
||||
*
|
||||
* @return string the human readable context level name.
|
||||
*/
|
||||
public static function get_level_name() {
|
||||
return get_string('coresystem');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns human readable context identifier.
|
||||
*
|
||||
* @param boolean $withprefix does not apply to system context
|
||||
* @param boolean $short does not apply to system context
|
||||
* @param boolean $escape does not apply to system context
|
||||
* @return string the human readable context name.
|
||||
*/
|
||||
public function get_context_name($withprefix = true, $short = false, $escape = true) {
|
||||
return self::get_level_name();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the most relevant URL for this context.
|
||||
*
|
||||
* @return moodle_url
|
||||
*/
|
||||
public function get_url() {
|
||||
return new moodle_url('/');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of all role archetypes that are compatible
|
||||
* with role assignments in context level.
|
||||
* @since Moodle 4.2
|
||||
*
|
||||
* @return int[]
|
||||
*/
|
||||
protected static function get_compatible_role_archetypes(): array {
|
||||
return ['manager', 'coursecreator'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of all possible parent context levels.
|
||||
* @since Moodle 4.2
|
||||
*
|
||||
* @return int[]
|
||||
*/
|
||||
public static function get_possible_parent_levels(): array {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns array of relevant context capability records.
|
||||
*
|
||||
* @param string $sort
|
||||
* @return array
|
||||
*/
|
||||
public function get_capabilities(string $sort = self::DEFAULT_CAPABILITY_SORT) {
|
||||
global $DB;
|
||||
|
||||
return $DB->get_records('capabilities', [], $sort);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create missing context instances at system context
|
||||
*/
|
||||
protected static function create_level_instances() {
|
||||
// Nothing to do here, the system context is created automatically in installer.
|
||||
self::instance(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns system context instance.
|
||||
*
|
||||
* @param int $instanceid should be 0
|
||||
* @param int $strictness
|
||||
* @param bool $cache
|
||||
* @return system context instance
|
||||
*/
|
||||
public static function instance($instanceid = 0, $strictness = MUST_EXIST, $cache = true) {
|
||||
global $DB;
|
||||
|
||||
if ($instanceid != 0) {
|
||||
debugging('context_system::instance(): invalid $id parameter detected, should be 0');
|
||||
}
|
||||
|
||||
// SYSCONTEXTID is cached in local cache to eliminate 1 query per page.
|
||||
if (defined('SYSCONTEXTID') && $cache) {
|
||||
if (!isset(context::$systemcontext)) {
|
||||
$record = new stdClass();
|
||||
$record->id = SYSCONTEXTID;
|
||||
$record->contextlevel = self::LEVEL;
|
||||
$record->instanceid = 0;
|
||||
$record->path = '/'.SYSCONTEXTID;
|
||||
$record->depth = 1;
|
||||
$record->locked = 0;
|
||||
context::$systemcontext = new system($record);
|
||||
}
|
||||
return context::$systemcontext;
|
||||
}
|
||||
|
||||
try {
|
||||
// We ignore the strictness completely because system context must exist except during install.
|
||||
$record = $DB->get_record('context', array('contextlevel' => self::LEVEL), '*', MUST_EXIST);
|
||||
} catch (\dml_exception $e) {
|
||||
// Table or record does not exist.
|
||||
if (!during_initial_install()) {
|
||||
// Do not mess with system context after install, it simply must exist.
|
||||
throw $e;
|
||||
}
|
||||
$record = null;
|
||||
}
|
||||
|
||||
if (!$record) {
|
||||
$record = new stdClass();
|
||||
$record->contextlevel = self::LEVEL;
|
||||
$record->instanceid = 0;
|
||||
$record->depth = 1;
|
||||
$record->path = null; // Not known before insert.
|
||||
$record->locked = 0;
|
||||
|
||||
try {
|
||||
if ($DB->count_records('context')) {
|
||||
// Contexts already exist, this is very weird, system must be first!!!
|
||||
return null;
|
||||
}
|
||||
if (defined('SYSCONTEXTID')) {
|
||||
// This would happen only in unittest on sites that went through weird 1.7 upgrade.
|
||||
$record->id = SYSCONTEXTID;
|
||||
$DB->import_record('context', $record);
|
||||
$DB->get_manager()->reset_sequence('context');
|
||||
} else {
|
||||
$record->id = $DB->insert_record('context', $record);
|
||||
}
|
||||
} catch (\dml_exception $e) {
|
||||
// Can not create context - table does not exist yet, sorry.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if ($record->instanceid != 0) {
|
||||
// This is very weird, somebody must be messing with context table.
|
||||
debugging('Invalid system context detected');
|
||||
}
|
||||
|
||||
if ($record->depth != 1 || $record->path != '/'.$record->id) {
|
||||
// Fix path if necessary, initial install or path reset.
|
||||
$record->depth = 1;
|
||||
$record->path = '/'.$record->id;
|
||||
$DB->update_record('context', $record);
|
||||
}
|
||||
|
||||
if (empty($record->locked)) {
|
||||
$record->locked = 0;
|
||||
}
|
||||
|
||||
if (!defined('SYSCONTEXTID')) {
|
||||
define('SYSCONTEXTID', $record->id);
|
||||
}
|
||||
|
||||
context::$systemcontext = new system($record);
|
||||
return context::$systemcontext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all site contexts except the system context, DO NOT call on production servers!!
|
||||
*
|
||||
* Contexts are not cached.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_child_contexts() {
|
||||
global $DB;
|
||||
|
||||
debugging('Fetching of system context child courses is strongly discouraged'
|
||||
. ' on production servers (it may eat all available memory)!');
|
||||
|
||||
// Just get all the contexts except for system level
|
||||
// and hope we don't OOM in the process - don't cache.
|
||||
$sql = "SELECT c.*
|
||||
FROM {context} c
|
||||
WHERE contextlevel > " . self::LEVEL;
|
||||
$records = $DB->get_records_sql($sql);
|
||||
|
||||
$result = array();
|
||||
foreach ($records as $record) {
|
||||
$result[$record->id] = context::create_instance_from_record($record);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns sql necessary for purging of stale context instances.
|
||||
*
|
||||
* @return string cleanup SQL
|
||||
*/
|
||||
protected static function get_cleanup_sql() {
|
||||
$sql = "
|
||||
SELECT c.*
|
||||
FROM {context} c
|
||||
WHERE 1=2
|
||||
";
|
||||
|
||||
return $sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rebuild context paths and depths at system context level.
|
||||
*
|
||||
* @param bool $force
|
||||
*/
|
||||
protected static function build_paths($force) {
|
||||
global $DB;
|
||||
|
||||
/* note: ignore $force here, we always do full test of system context */
|
||||
|
||||
// Exactly one record must exist.
|
||||
$record = $DB->get_record('context', array('contextlevel' => self::LEVEL), '*', MUST_EXIST);
|
||||
|
||||
if ($record->instanceid != 0) {
|
||||
debugging('Invalid system context detected');
|
||||
}
|
||||
|
||||
if (defined('SYSCONTEXTID') && $record->id != SYSCONTEXTID) {
|
||||
debugging('Invalid SYSCONTEXTID detected');
|
||||
}
|
||||
|
||||
if ($record->depth != 1 || $record->path != '/'.$record->id) {
|
||||
// Fix path if necessary, initial install or path reset.
|
||||
$record->depth = 1;
|
||||
$record->path = '/'.$record->id;
|
||||
$DB->update_record('context', $record);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether this context has been locked or not.
|
||||
*
|
||||
* @param bool $locked
|
||||
* @return $this
|
||||
*/
|
||||
public function set_locked(bool $locked) {
|
||||
if ($locked) {
|
||||
throw new \coding_exception('It is not possible to lock the system context');
|
||||
}
|
||||
return parent::set_locked($locked);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,242 @@
|
||||
<?php
|
||||
// This file is part of Moodle - https://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
namespace core\context;
|
||||
|
||||
use core\context;
|
||||
use stdClass;
|
||||
use coding_exception, moodle_url;
|
||||
|
||||
/**
|
||||
* User context class
|
||||
*
|
||||
* @package core_access
|
||||
* @category access
|
||||
* @copyright Petr Skoda
|
||||
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @since Moodle 4.2
|
||||
*/
|
||||
class user extends context {
|
||||
/** @var int numeric context level value matching legacy CONTEXT_USER */
|
||||
public const LEVEL = 30;
|
||||
|
||||
/**
|
||||
* Please use \core\context\user::instance($userid) if you need the instance of context.
|
||||
* Alternatively if you know only the context id use \core\context::instance_by_id($contextid)
|
||||
*
|
||||
* @param stdClass $record
|
||||
*/
|
||||
protected function __construct(stdClass $record) {
|
||||
parent::__construct($record);
|
||||
if ($record->contextlevel != self::LEVEL) {
|
||||
throw new coding_exception('Invalid $record->contextlevel in core\context\user constructor.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns short context name.
|
||||
*
|
||||
* @since Moodle 4.2
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_short_name(): string {
|
||||
return 'user';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns human readable context level name.
|
||||
*
|
||||
* @return string the human readable context level name.
|
||||
*/
|
||||
public static function get_level_name() {
|
||||
return get_string('user');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns human readable context identifier.
|
||||
*
|
||||
* @param boolean $withprefix whether to prefix the name of the context with User
|
||||
* @param boolean $short does not apply to user context
|
||||
* @param boolean $escape does not apply to user context
|
||||
* @return string the human readable context name.
|
||||
*/
|
||||
public function get_context_name($withprefix = true, $short = false, $escape = true) {
|
||||
global $DB;
|
||||
|
||||
$name = '';
|
||||
if ($user = $DB->get_record('user', array('id' => $this->_instanceid, 'deleted' => 0))) {
|
||||
if ($withprefix) {
|
||||
$name = get_string('user').': ';
|
||||
}
|
||||
$name .= fullname($user);
|
||||
}
|
||||
return $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the most relevant URL for this context.
|
||||
*
|
||||
* @return moodle_url
|
||||
*/
|
||||
public function get_url() {
|
||||
global $COURSE;
|
||||
|
||||
if ($COURSE->id == SITEID) {
|
||||
$url = new moodle_url('/user/profile.php', array('id' => $this->_instanceid));
|
||||
} else {
|
||||
$url = new moodle_url('/user/view.php', array('id' => $this->_instanceid, 'courseid' => $COURSE->id));
|
||||
}
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of all possible parent context levels.
|
||||
* @since Moodle 4.2
|
||||
*
|
||||
* @return int[]
|
||||
*/
|
||||
public static function get_possible_parent_levels(): array {
|
||||
return [system::LEVEL];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns context instance database name.
|
||||
*
|
||||
* @return string|null table name for all levels except system.
|
||||
*/
|
||||
protected static function get_instance_table(): ?string {
|
||||
return 'user';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of columns that can be used from behat
|
||||
* to look up context by reference.
|
||||
*
|
||||
* @return array list of column names from instance table
|
||||
*/
|
||||
protected static function get_behat_reference_columns(): array {
|
||||
return ['username'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns array of relevant context capability records.
|
||||
*
|
||||
* @param string $sort
|
||||
* @return array
|
||||
*/
|
||||
public function get_capabilities(string $sort = self::DEFAULT_CAPABILITY_SORT) {
|
||||
global $DB;
|
||||
|
||||
$extracaps = array('moodle/grade:viewall');
|
||||
list($extra, $params) = $DB->get_in_or_equal($extracaps, SQL_PARAMS_NAMED, 'cap');
|
||||
|
||||
return $DB->get_records_select('capabilities', "contextlevel = :level OR name {$extra}",
|
||||
$params + ['level' => self::LEVEL], $sort);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns user context instance.
|
||||
*
|
||||
* @param int $userid id from {user} table
|
||||
* @param int $strictness
|
||||
* @return user|false context instance
|
||||
*/
|
||||
public static function instance($userid, $strictness = MUST_EXIST) {
|
||||
global $DB;
|
||||
|
||||
if ($context = context::cache_get(self::LEVEL, $userid)) {
|
||||
return $context;
|
||||
}
|
||||
|
||||
if (!$record = $DB->get_record('context', array('contextlevel' => self::LEVEL, 'instanceid' => $userid))) {
|
||||
if ($user = $DB->get_record('user', array('id' => $userid, 'deleted' => 0), 'id', $strictness)) {
|
||||
$record = context::insert_context_record(self::LEVEL, $user->id, '/'.SYSCONTEXTID, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if ($record) {
|
||||
$context = new user($record);
|
||||
context::cache_add($context);
|
||||
return $context;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create missing context instances at user context level
|
||||
*/
|
||||
protected static function create_level_instances() {
|
||||
global $DB;
|
||||
|
||||
$sql = "SELECT " . self::LEVEL . ", u.id
|
||||
FROM {user} u
|
||||
WHERE u.deleted = 0
|
||||
AND NOT EXISTS (SELECT 'x'
|
||||
FROM {context} cx
|
||||
WHERE u.id = cx.instanceid AND cx.contextlevel=" . self::LEVEL . ")";
|
||||
$contextdata = $DB->get_recordset_sql($sql);
|
||||
foreach ($contextdata as $context) {
|
||||
context::insert_context_record(self::LEVEL, $context->id, null);
|
||||
}
|
||||
$contextdata->close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns sql necessary for purging of stale context instances.
|
||||
*
|
||||
* @return string cleanup SQL
|
||||
*/
|
||||
protected static function get_cleanup_sql() {
|
||||
$sql = "
|
||||
SELECT c.*
|
||||
FROM {context} c
|
||||
LEFT OUTER JOIN {user} u ON (c.instanceid = u.id AND u.deleted = 0)
|
||||
WHERE u.id IS NULL AND c.contextlevel = " . self::LEVEL . "
|
||||
";
|
||||
|
||||
return $sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rebuild context paths and depths at user context level.
|
||||
*
|
||||
* @param bool $force
|
||||
*/
|
||||
protected static function build_paths($force) {
|
||||
global $DB;
|
||||
|
||||
// First update normal users.
|
||||
$path = $DB->sql_concat('?', 'id');
|
||||
$pathstart = '/' . SYSCONTEXTID . '/';
|
||||
$params = array($pathstart);
|
||||
|
||||
if ($force) {
|
||||
$where = "depth <> 2 OR path IS NULL OR path <> ({$path})";
|
||||
$params[] = $pathstart;
|
||||
} else {
|
||||
$where = "depth = 0 OR path IS NULL";
|
||||
}
|
||||
|
||||
$sql = "UPDATE {context}
|
||||
SET depth = 2,
|
||||
path = {$path}
|
||||
WHERE contextlevel = " . self::LEVEL . "
|
||||
AND ($where)";
|
||||
$DB->execute($sql, $params);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,484 @@
|
||||
<?php
|
||||
// This file is part of Moodle - https://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
namespace core;
|
||||
|
||||
use stdClass;
|
||||
use coding_exception;
|
||||
|
||||
/**
|
||||
* Context maintenance and helper methods.
|
||||
*
|
||||
* This is "extends context" is a bloody hack that tires to work around the deficiencies
|
||||
* in the "protected" keyword in PHP, this helps us to hide all the internals of context
|
||||
* level implementation from the rest of code, the code completion returns what developers need.
|
||||
*
|
||||
* Thank you Tim Hunt for helping me with this nasty trick.
|
||||
*
|
||||
* @package core_access
|
||||
* @category access
|
||||
* @copyright Petr Skoda
|
||||
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @since Moodle 4.2
|
||||
*/
|
||||
abstract class context_helper extends context {
|
||||
|
||||
/**
|
||||
* @var array An array definitions of all context levels
|
||||
*/
|
||||
private static $alllevels;
|
||||
|
||||
/**
|
||||
* Reset internal context levels array.
|
||||
*/
|
||||
public static function reset_levels() {
|
||||
self::$alllevels = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise context levels, call before using self::$alllevels.
|
||||
*/
|
||||
private static function init_levels():void {
|
||||
global $CFG;
|
||||
|
||||
if (isset(self::$alllevels)) {
|
||||
return;
|
||||
}
|
||||
self::$alllevels = array(
|
||||
CONTEXT_SYSTEM => \core\context\system::class,
|
||||
CONTEXT_USER => \core\context\user::class,
|
||||
CONTEXT_COURSECAT => \core\context\coursecat::class,
|
||||
CONTEXT_COURSE => \core\context\course::class,
|
||||
CONTEXT_MODULE => \core\context\module::class,
|
||||
CONTEXT_BLOCK => \core\context\block::class,
|
||||
);
|
||||
|
||||
if (empty($CFG->custom_context_classes)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$levels = $CFG->custom_context_classes;
|
||||
if (!is_array($levels)) {
|
||||
$levels = @unserialize($levels);
|
||||
}
|
||||
if (!is_array($levels)) {
|
||||
debugging('Invalid $CFG->custom_context_classes detected, value ignored.', DEBUG_DEVELOPER);
|
||||
return;
|
||||
}
|
||||
|
||||
// Unsupported custom levels, use with care!!!
|
||||
foreach ($levels as $level => $classname) {
|
||||
self::$alllevels[$level] = $classname;
|
||||
}
|
||||
ksort(self::$alllevels);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts legacy context_* class name to new class name.
|
||||
*
|
||||
* NOTE: this is needed for external API which uses short context names.
|
||||
* @since Moodle 4.2
|
||||
*
|
||||
* @param int|string $extlevel
|
||||
* @return string|null context class name or null if not found
|
||||
*/
|
||||
public static function parse_external_level($extlevel): ?string {
|
||||
self::init_levels();
|
||||
if (is_number($extlevel)) {
|
||||
if (isset(self::$alllevels[$extlevel])) {
|
||||
return self::$alllevels[$extlevel];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
if ($extlevel && is_string($extlevel)) {
|
||||
$found = null;
|
||||
foreach (self::$alllevels as $classname) {
|
||||
if ($classname::get_short_name() === $extlevel) {
|
||||
if ($found) {
|
||||
debugging("Duplicate short context level name found '$extlevel', use numeric value instead",
|
||||
DEBUG_DEVELOPER);
|
||||
} else {
|
||||
$found = $classname;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $found;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve reference to context used in behat feature files.
|
||||
*
|
||||
* @param string $level
|
||||
* @param string $reference
|
||||
* @return context|null
|
||||
*/
|
||||
public static function resolve_behat_reference(string $level, string $reference): ?context {
|
||||
global $DB;
|
||||
|
||||
if (!PHPUNIT_TEST && !defined('BEHAT_SITE_RUNNING')) {
|
||||
throw new coding_exception('resolve_behat_reference() cannot be used outside of tests');
|
||||
}
|
||||
self::init_levels();
|
||||
|
||||
$classname = null;
|
||||
if (is_number($level)) {
|
||||
if (isset(self::$alllevels[$level])) {
|
||||
$classname = self::$alllevels[$level];
|
||||
}
|
||||
} else {
|
||||
foreach (self::$alllevels as $levelclassname) {
|
||||
if ($level === $levelclassname::get_level_name()) {
|
||||
$classname = $levelclassname;
|
||||
break;
|
||||
}
|
||||
if ($level === $levelclassname::get_short_name()) {
|
||||
$classname = $levelclassname;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!$classname) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($classname::LEVEL === context\system::LEVEL) {
|
||||
return context\system::instance();
|
||||
}
|
||||
|
||||
if (trim($reference) === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
$table = $classname::get_instance_table();
|
||||
if (!$table) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$columns = $classname::get_behat_reference_columns();
|
||||
foreach ($columns as $column) {
|
||||
$instance = $DB->get_record($table, [$column => $reference]);
|
||||
if ($instance) {
|
||||
$context = $classname::instance($instance->id, IGNORE_MISSING);
|
||||
if ($context) {
|
||||
return $context;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a class name of the context level class
|
||||
*
|
||||
* @param int $contextlevel (CONTEXT_SYSTEM, etc.)
|
||||
* @return string class name of the context class
|
||||
* @throws coding_exception if level does not exist
|
||||
*/
|
||||
public static function get_class_for_level(int $contextlevel): string {
|
||||
self::init_levels();
|
||||
if (isset(self::$alllevels[$contextlevel])) {
|
||||
return self::$alllevels[$contextlevel];
|
||||
} else {
|
||||
throw new coding_exception('Invalid context level specified');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of all context levels
|
||||
*
|
||||
* @return array int=>string (level=>level class name)
|
||||
*/
|
||||
public static function get_all_levels(): array {
|
||||
self::init_levels();
|
||||
return self::$alllevels;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get list of possible child levels for given level.
|
||||
* @since Moodle 4.2
|
||||
*
|
||||
* @param int $parentlevel
|
||||
* @return int[] list of context levels that my be children of given context level.
|
||||
*/
|
||||
public static function get_child_levels(int $parentlevel): array {
|
||||
self::init_levels();
|
||||
$result = [];
|
||||
$definitions = self::$alllevels;
|
||||
|
||||
$recursion = function(int $pl) use (&$result, $definitions, &$recursion): void {
|
||||
foreach ($definitions as $contextlevel => $classname) {
|
||||
$parentlevels = $classname::get_possible_parent_levels();
|
||||
if (in_array($pl, $parentlevels)) {
|
||||
if (isset($result[$contextlevel])) {
|
||||
continue;
|
||||
}
|
||||
$result[$contextlevel] = $contextlevel;
|
||||
$recursion($contextlevel);
|
||||
}
|
||||
}
|
||||
};
|
||||
$recursion($parentlevel);
|
||||
|
||||
$classname = self::get_class_for_level($parentlevel);
|
||||
$parentlevels = $classname::get_possible_parent_levels();
|
||||
if (!in_array($parentlevel, $parentlevels)) {
|
||||
unset($result[$parentlevel]);
|
||||
}
|
||||
|
||||
return array_values($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns context levels that compatible with role archetype assignments.
|
||||
* @since Moodle 4.2
|
||||
*
|
||||
* @param string $archetype
|
||||
* @return array
|
||||
*/
|
||||
public static function get_compatible_levels(string $archetype): array {
|
||||
self::init_levels();
|
||||
$result = [];
|
||||
|
||||
foreach (self::$alllevels as $contextlevel => $classname) {
|
||||
$compatiblearchetypes = $classname::get_compatible_role_archetypes();
|
||||
foreach ($compatiblearchetypes as $at) {
|
||||
if ($at === $archetype) {
|
||||
$result[] = $contextlevel;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove stale contexts that belonged to deleted instances.
|
||||
* Ideally all code should cleanup contexts properly, unfortunately accidents happen...
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function cleanup_instances() {
|
||||
global $DB;
|
||||
self::init_levels();
|
||||
|
||||
$sqls = array();
|
||||
foreach (self::$alllevels as $classname) {
|
||||
$sqls[] = $classname::get_cleanup_sql();
|
||||
}
|
||||
|
||||
$sql = implode(" UNION ", $sqls);
|
||||
|
||||
// It is probably better to use transactions, it might be faster too.
|
||||
$transaction = $DB->start_delegated_transaction();
|
||||
|
||||
$rs = $DB->get_recordset_sql($sql);
|
||||
foreach ($rs as $record) {
|
||||
$context = context::create_instance_from_record($record);
|
||||
$context->delete();
|
||||
}
|
||||
$rs->close();
|
||||
|
||||
$transaction->allow_commit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create all context instances at the given level and above.
|
||||
*
|
||||
* @param int $contextlevel null means all levels
|
||||
* @param bool $buildpaths
|
||||
* @return void
|
||||
*/
|
||||
public static function create_instances($contextlevel = null, $buildpaths = true) {
|
||||
self::init_levels();
|
||||
foreach (self::$alllevels as $level => $classname) {
|
||||
if ($contextlevel && $contextlevel != context\block::LEVEL && $level > $contextlevel) {
|
||||
// Skip potential sub-contexts,
|
||||
// in case of blocks build all contexts because plugin contexts may have higher levels.
|
||||
continue;
|
||||
}
|
||||
$classname::create_level_instances();
|
||||
if ($buildpaths) {
|
||||
$classname::build_paths(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Rebuild paths and depths in all context levels.
|
||||
*
|
||||
* @param bool $force false means add missing only
|
||||
* @return void
|
||||
*/
|
||||
public static function build_all_paths($force = false) {
|
||||
self::init_levels();
|
||||
foreach (self::$alllevels as $classname) {
|
||||
$classname::build_paths($force);
|
||||
}
|
||||
|
||||
// Reset static course cache - it might have incorrect cached data.
|
||||
accesslib_clear_all_caches(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the cache to remove all data.
|
||||
*/
|
||||
public static function reset_caches() {
|
||||
context::reset_caches();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all fields necessary for context preloading from user $rec.
|
||||
*
|
||||
* This helps with performance when dealing with hundreds of contexts.
|
||||
*
|
||||
* @param string $tablealias context table alias in the query
|
||||
* @return array (table.column=>alias, ...)
|
||||
*/
|
||||
public static function get_preload_record_columns($tablealias) {
|
||||
return [
|
||||
"$tablealias.id" => "ctxid",
|
||||
"$tablealias.path" => "ctxpath",
|
||||
"$tablealias.depth" => "ctxdepth",
|
||||
"$tablealias.contextlevel" => "ctxlevel",
|
||||
"$tablealias.instanceid" => "ctxinstance",
|
||||
"$tablealias.locked" => "ctxlocked",
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all fields necessary for context preloading from user $rec.
|
||||
*
|
||||
* This helps with performance when dealing with hundreds of contexts.
|
||||
*
|
||||
* @param string $tablealias context table alias in the query
|
||||
* @return string
|
||||
*/
|
||||
public static function get_preload_record_columns_sql($tablealias) {
|
||||
return "$tablealias.id AS ctxid, " .
|
||||
"$tablealias.path AS ctxpath, " .
|
||||
"$tablealias.depth AS ctxdepth, " .
|
||||
"$tablealias.contextlevel AS ctxlevel, " .
|
||||
"$tablealias.instanceid AS ctxinstance, " .
|
||||
"$tablealias.locked AS ctxlocked";
|
||||
}
|
||||
|
||||
/**
|
||||
* Preloads context cache with information from db record and strips the cached info.
|
||||
*
|
||||
* The db request has to contain all columns from context_helper::get_preload_record_columns().
|
||||
*
|
||||
* @param stdClass $rec
|
||||
* @return void This is intentional. See MDL-37115. You will need to get the context
|
||||
* in the normal way, but it is now cached, so that will be fast.
|
||||
*/
|
||||
public static function preload_from_record(stdClass $rec): void {
|
||||
context::preload_from_record($rec);
|
||||
}
|
||||
|
||||
/**
|
||||
* Preload a set of contexts using their contextid.
|
||||
*
|
||||
* @param array $contextids
|
||||
*/
|
||||
public static function preload_contexts_by_id(array $contextids): void {
|
||||
global $DB;
|
||||
|
||||
// Determine which contexts are not already cached.
|
||||
$tofetch = [];
|
||||
foreach ($contextids as $contextid) {
|
||||
if (!self::cache_get_by_id($contextid)) {
|
||||
$tofetch[] = $contextid;
|
||||
}
|
||||
}
|
||||
|
||||
if (count($tofetch) > 1) {
|
||||
// There are at least two to fetch.
|
||||
// There is no point only fetching a single context as this would be no more efficient than calling the existing code.
|
||||
list($insql, $inparams) = $DB->get_in_or_equal($tofetch, SQL_PARAMS_NAMED);
|
||||
$ctxs = $DB->get_records_select('context', "id {$insql}", $inparams, '',
|
||||
self::get_preload_record_columns_sql('{context}'));
|
||||
foreach ($ctxs as $ctx) {
|
||||
self::preload_from_record($ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Preload all contexts instances from course.
|
||||
*
|
||||
* To be used if you expect multiple queries for course activities...
|
||||
*
|
||||
* @param int $courseid
|
||||
*/
|
||||
public static function preload_course($courseid) {
|
||||
// Users can call this multiple times without doing any harm.
|
||||
if (isset(context::$cache_preloaded[$courseid])) {
|
||||
return;
|
||||
}
|
||||
$coursecontext = context\course::instance($courseid);
|
||||
$coursecontext->get_child_contexts();
|
||||
|
||||
context::$cache_preloaded[$courseid] = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete context instance
|
||||
*
|
||||
* @param int $contextlevel
|
||||
* @param int $instanceid
|
||||
* @return void
|
||||
*/
|
||||
public static function delete_instance($contextlevel, $instanceid) {
|
||||
global $DB;
|
||||
|
||||
// Double check the context still exists.
|
||||
if ($record = $DB->get_record('context', array('contextlevel' => $contextlevel, 'instanceid' => $instanceid))) {
|
||||
$context = context::create_instance_from_record($record);
|
||||
$context->delete();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of specified context level
|
||||
*
|
||||
* @param int $contextlevel
|
||||
* @return string name of the context level
|
||||
*/
|
||||
public static function get_level_name($contextlevel) {
|
||||
$classname = self::get_class_for_level($contextlevel);
|
||||
return $classname::get_level_name();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current context to be used for navigation tree filtering.
|
||||
*
|
||||
* @param context|null $context The current context to be checked against.
|
||||
* @return context|null the context that navigation tree filtering should use.
|
||||
*/
|
||||
public static function get_navigation_filter_context(?context $context): ?context {
|
||||
global $CFG;
|
||||
if (!empty($CFG->filternavigationwithsystemcontext)) {
|
||||
return context\system::instance();
|
||||
} else {
|
||||
return $context;
|
||||
}
|
||||
}
|
||||
}
|
||||
+9
-7
@@ -519,7 +519,9 @@ class external_api {
|
||||
* The passed array must either contain a contextid or a combination of context level and instance id to fetch the context.
|
||||
* For example, the context level can be "course" and instanceid can be courseid.
|
||||
*
|
||||
* See context_helper::get_all_levels() for a list of valid context levels.
|
||||
* See context_helper::get_all_levels() for a list of valid numeric context levels,
|
||||
* legacy short names such as 'system', 'user', 'course' are not supported in new
|
||||
* plugin capabilities.
|
||||
*
|
||||
* @param array $param
|
||||
* @since Moodle 2.6
|
||||
@@ -527,15 +529,15 @@ class external_api {
|
||||
* @return context
|
||||
*/
|
||||
protected static function get_context_from_params($param) {
|
||||
$levels = context_helper::get_all_levels();
|
||||
if (!empty($param['contextid'])) {
|
||||
return context::instance_by_id($param['contextid'], IGNORE_MISSING);
|
||||
} else if (!empty($param['contextlevel']) && isset($param['instanceid'])) {
|
||||
$contextlevel = "context_{$param['contextlevel']}";
|
||||
if (!array_search($contextlevel, $levels)) {
|
||||
throw new invalid_parameter_exception("Invalid context level = {$param['contextlevel']}");
|
||||
// Numbers and short names are supported since Moodle 4.2.
|
||||
$classname = \core\context_helper::parse_external_level($param['contextlevel']);
|
||||
if (!$classname) {
|
||||
throw new invalid_parameter_exception('Invalid context level = '.$param['contextlevel']);
|
||||
}
|
||||
return $contextlevel::instance($param['instanceid'], IGNORE_MISSING);
|
||||
return $classname::instance($param['instanceid'], IGNORE_MISSING);
|
||||
} else {
|
||||
// No valid context info was found.
|
||||
throw new invalid_parameter_exception(
|
||||
@@ -556,7 +558,7 @@ class external_api {
|
||||
0
|
||||
);
|
||||
$level = new external_value(
|
||||
PARAM_ALPHA,
|
||||
PARAM_ALPHANUM, // Since Moodle 4.2 numeric context level values are supported too.
|
||||
'Context level. To be used with instanceid.',
|
||||
VALUE_DEFAULT,
|
||||
''
|
||||
|
||||
+22
-3
@@ -184,6 +184,11 @@ class external_api_test extends \advanced_testcase {
|
||||
$fetchedcontext = $this->get_context_from_params(["contextlevel" => "course", "instanceid" => $course->id]);
|
||||
$this->assertEquals($realcontext, $fetchedcontext);
|
||||
|
||||
// Use context level numbers instead of legacy short level names.
|
||||
$fetchedcontext = $this->get_context_from_params(
|
||||
["contextlevel" => \core\context\course::LEVEL, "instanceid" => $course->id]);
|
||||
$this->assertEquals($realcontext, $fetchedcontext);
|
||||
|
||||
// Passing empty values.
|
||||
try {
|
||||
$fetchedcontext = $this->get_context_from_params(["contextid" => 0]);
|
||||
@@ -211,9 +216,23 @@ class external_api_test extends \advanced_testcase {
|
||||
$fetchedcontext = $this->get_context_from_params(["contextlevel" => "system", "instanceid" => 0]);
|
||||
$this->assertEquals($realcontext, $fetchedcontext);
|
||||
|
||||
// Passing wrong level.
|
||||
$this->expectException('invalid_parameter_exception');
|
||||
$fetchedcontext = $this->get_context_from_params(["contextlevel" => "random", "instanceid" => $course->id]);
|
||||
// Passing wrong level name.
|
||||
try {
|
||||
$fetchedcontext = $this->get_context_from_params(["contextlevel" => "random", "instanceid" => $course->id]);
|
||||
$this->fail('exception expected when level name is invalid');
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('invalid_parameter_exception', $e);
|
||||
$this->assertSame('Invalid parameter value detected (Invalid context level = random)', $e->getMessage());
|
||||
}
|
||||
|
||||
// Passing wrong level number.
|
||||
try {
|
||||
$fetchedcontext = $this->get_context_from_params(["contextlevel" => -10, "instanceid" => $course->id]);
|
||||
$this->fail('exception expected when level name is invalid');
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('invalid_parameter_exception', $e);
|
||||
$this->assertSame('Invalid parameter value detected (Invalid context level = -10)', $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -5190,7 +5190,7 @@ class accesslib_test extends advanced_testcase {
|
||||
/**
|
||||
* Context caching fixture
|
||||
*/
|
||||
class context_inspection extends context_helper {
|
||||
abstract class context_inspection extends \core\context_helper {
|
||||
public static function test_context_cache_size() {
|
||||
return self::$cache_count;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,269 @@
|
||||
<?php
|
||||
// This file is part of Moodle - https://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
namespace core\context;
|
||||
|
||||
use core\context, core\context_helper;
|
||||
|
||||
/**
|
||||
* Unit tests for block context class.
|
||||
*
|
||||
* NOTE: more tests are in lib/tests/accesslib_test.php
|
||||
*
|
||||
* @package core
|
||||
* @copyright Petr Skoda
|
||||
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @coversDefaultClass \core\context\block
|
||||
*/
|
||||
class block_test extends \advanced_testcase {
|
||||
/**
|
||||
* Tests legacy class name.
|
||||
* @covers \context_block
|
||||
*/
|
||||
public function test_legacy_classname() {
|
||||
$this->resetAfterTest();
|
||||
|
||||
$block = $this->getDataGenerator()->create_block('online_users');
|
||||
$context = block::instance($block->id);
|
||||
|
||||
$this->assertInstanceOf(block::class, $context);
|
||||
$this->assertInstanceOf(\context_block::class, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered methods.
|
||||
* @covers ::instance
|
||||
* @covers \core\context::instance_by_id
|
||||
*/
|
||||
public function test_factory_methods() {
|
||||
$this->resetAfterTest();
|
||||
|
||||
$block = $this->getDataGenerator()->create_block('online_users');
|
||||
$context = block::instance($block->id);
|
||||
|
||||
$this->assertInstanceOf(block::class, $context);
|
||||
$this->assertSame((string)$block->id, $context->instanceid);
|
||||
|
||||
$context = context::instance_by_id($context->id);
|
||||
$this->assertInstanceOf(block::class, $context);
|
||||
$this->assertSame((string)$block->id, $context->instanceid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::get_short_name
|
||||
*/
|
||||
public function test_get_short_name() {
|
||||
$this->assertSame('block', block::get_short_name());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests levels.
|
||||
* @coversNothing
|
||||
*/
|
||||
public function test_level() {
|
||||
$this->assertSame(80, block::LEVEL);
|
||||
$this->assertSame(CONTEXT_BLOCK, block::LEVEL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::get_level_name
|
||||
*/
|
||||
public function test_get_level_name() {
|
||||
$this->assertSame('Block', block::get_level_name());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::get_context_name
|
||||
*/
|
||||
public function test_get_context_name() {
|
||||
$this->resetAfterTest();
|
||||
|
||||
$block = $this->getDataGenerator()->create_block('online_users');
|
||||
$context = block::instance($block->id);
|
||||
|
||||
$this->assertSame('Block: Online users', $context->get_context_name());
|
||||
$this->assertSame('Block: Online users', $context->get_context_name(true));
|
||||
$this->assertSame('Online users', $context->get_context_name(false));
|
||||
$this->assertSame('Online users', $context->get_context_name(false, true));
|
||||
$this->assertSame('Block: Online users', $context->get_context_name(true, true, false));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::get_url
|
||||
*/
|
||||
public function test_get_url() {
|
||||
$this->resetAfterTest();
|
||||
|
||||
$block = $this->getDataGenerator()->create_block('online_users');
|
||||
$context = block::instance($block->id);
|
||||
|
||||
$expected = new \moodle_url('/');
|
||||
$url = $context->get_url();
|
||||
$this->assertInstanceOf(\moodle_url::class, $url);
|
||||
$this->assertSame($expected->out(), $url->out());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::get_compatible_role_archetypes
|
||||
*/
|
||||
public function test_get_compatible_role_archetypes() {
|
||||
global $DB;
|
||||
|
||||
$allarchetypes = $DB->get_fieldset_select('role', 'DISTINCT archetype', 'archetype IS NOT NULL');
|
||||
foreach ($allarchetypes as $allarchetype) {
|
||||
$levels = context_helper::get_compatible_levels($allarchetype);
|
||||
$this->assertNotContains(block::LEVEL, $levels, "$allarchetype is not expected to be compatible with context");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::get_possible_parent_levels
|
||||
*/
|
||||
public function test_get_possible_parent_levels() {
|
||||
$result = block::get_possible_parent_levels();
|
||||
// All except itself.
|
||||
$this->assertContains(system::LEVEL, $result);
|
||||
$this->assertContains(user::LEVEL, $result);
|
||||
$this->assertContains(coursecat::LEVEL, $result);
|
||||
$this->assertContains(course::LEVEL, $result);
|
||||
$this->assertContains(module::LEVEL, $result);
|
||||
$this->assertNotContains(block::LEVEL, $result);
|
||||
|
||||
// Make sure plugin contexts are covered too.
|
||||
$all = \core\context_helper::get_all_levels();
|
||||
$this->assertCount(count($all) - 1, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::get_capabilities
|
||||
*/
|
||||
public function test_get_capabilities() {
|
||||
$this->resetAfterTest();
|
||||
|
||||
$block = $this->getDataGenerator()->create_block('online_users');
|
||||
$context = block::instance($block->id);
|
||||
|
||||
$capabilities = $context->get_capabilities();
|
||||
$capabilities = convert_to_array($capabilities);
|
||||
$capabilities = array_column($capabilities, 'name');
|
||||
$this->assertNotContains('moodle/site:config', $capabilities);
|
||||
$this->assertNotContains('moodle/course:view', $capabilities);
|
||||
$this->assertNotContains('moodle/category:manage', $capabilities);
|
||||
$this->assertNotContains('moodle/user:viewalldetails', $capabilities);
|
||||
$this->assertNotContains('mod/page:view', $capabilities);
|
||||
$this->assertNotContains('mod/url:view', $capabilities);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::create_level_instances
|
||||
*/
|
||||
public function test_create_level_instances() {
|
||||
global $DB;
|
||||
$this->resetAfterTest();
|
||||
|
||||
$block = $this->getDataGenerator()->create_block('online_users');
|
||||
$context = block::instance($block->id);
|
||||
|
||||
$DB->delete_records('context', ['id' => $context->id]);
|
||||
context_helper::create_instances(block::LEVEL);
|
||||
$record = $DB->get_record('context', ['contextlevel' => block::LEVEL, 'instanceid' => $block->id], '*', MUST_EXIST);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::get_child_contexts
|
||||
*/
|
||||
public function test_get_child_contexts() {
|
||||
$this->resetAfterTest();
|
||||
|
||||
$block = $this->getDataGenerator()->create_block('online_users');
|
||||
$context = block::instance($block->id);
|
||||
|
||||
$children = $context->get_child_contexts();
|
||||
$this->assertCount(0, $children);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::get_cleanup_sql
|
||||
*/
|
||||
public function test_get_cleanup_sql() {
|
||||
global $DB;
|
||||
$this->resetAfterTest();
|
||||
|
||||
$block = $this->getDataGenerator()->create_block('online_users');
|
||||
$context = block::instance($block->id);
|
||||
|
||||
$DB->delete_records('block_instances', ['id' => $block->id]);
|
||||
|
||||
context_helper::cleanup_instances();
|
||||
$this->assertFalse($DB->record_exists('context', ['contextlevel' => block::LEVEL, 'instanceid' => $block->id]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::build_paths
|
||||
*/
|
||||
public function test_build_paths() {
|
||||
global $DB;
|
||||
$this->resetAfterTest();
|
||||
|
||||
$syscontext = system::instance();
|
||||
$block = $this->getDataGenerator()->create_block('online_users');
|
||||
$context = block::instance($block->id);
|
||||
|
||||
$DB->set_field('context', 'depth', 1, ['id' => $context->id]);
|
||||
$DB->set_field('context', 'path', '/0', ['id' => $context->id]);
|
||||
|
||||
context_helper::build_all_paths(true);
|
||||
|
||||
$record = $DB->get_record('context', ['id' => $context->id]);
|
||||
$this->assertSame('2', $record->depth);
|
||||
$this->assertSame('/' . $syscontext->id . '/' . $record->id, $record->path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::set_locked
|
||||
*/
|
||||
public function test_set_locked() {
|
||||
global $DB;
|
||||
$this->resetAfterTest();
|
||||
|
||||
$block = $this->getDataGenerator()->create_block('online_users');
|
||||
$context = block::instance($block->id);
|
||||
|
||||
$context->set_locked(true);
|
||||
$context = block::instance($block->id);
|
||||
$this->assertTrue($context->locked);
|
||||
$record = $DB->get_record('context', ['id' => $context->id]);
|
||||
$this->assertSame('1', $record->locked);
|
||||
|
||||
$context->set_locked(false);
|
||||
$context = block::instance($block->id);
|
||||
$this->assertFalse($context->locked);
|
||||
$record = $DB->get_record('context', ['id' => $context->id]);
|
||||
$this->assertSame('0', $record->locked);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,291 @@
|
||||
<?php
|
||||
// This file is part of Moodle - https://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
namespace core\context;
|
||||
|
||||
use core\context, core\context_helper;
|
||||
|
||||
/**
|
||||
* Unit tests for course context class.
|
||||
*
|
||||
* NOTE: more tests are in lib/tests/accesslib_test.php
|
||||
*
|
||||
* @package core
|
||||
* @copyright Petr Skoda
|
||||
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @coversDefaultClass \core\context\course
|
||||
*/
|
||||
class course_test extends \advanced_testcase {
|
||||
/**
|
||||
* Tests legacy class.
|
||||
* @coversNothing
|
||||
*/
|
||||
public function test_legacy_classname() {
|
||||
global $SITE;
|
||||
$course = $SITE;
|
||||
$context = \context_course::instance($course->id);
|
||||
$this->assertInstanceOf(course::class, $context);
|
||||
$this->assertInstanceOf(\context_course::class, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered methods.
|
||||
* @covers ::instance
|
||||
* @covers \core\context::instance_by_id
|
||||
*/
|
||||
public function test_factory_methods() {
|
||||
global $SITE;
|
||||
$course = $SITE;
|
||||
$context = course::instance($course->id);
|
||||
$this->assertInstanceOf(course::class, $context);
|
||||
$this->assertSame($course->id, $context->instanceid);
|
||||
|
||||
$context = context::instance_by_id($context->id);
|
||||
$this->assertInstanceOf(course::class, $context);
|
||||
$this->assertSame($course->id, $context->instanceid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::get_short_name
|
||||
*/
|
||||
public function test_get_short_name() {
|
||||
$this->assertSame('course', course::get_short_name());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests levels.
|
||||
* @coversNothing
|
||||
*/
|
||||
public function test_level() {
|
||||
$this->assertSame(50, course::LEVEL);
|
||||
$this->assertSame(CONTEXT_COURSE, course::LEVEL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::get_level_name
|
||||
*/
|
||||
public function test_get_level_name() {
|
||||
$this->assertSame('Course', course::get_level_name());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::get_context_name
|
||||
*/
|
||||
public function test_get_context_name() {
|
||||
$this->resetAfterTest();
|
||||
|
||||
$course = $this->getDataGenerator()->create_course(['fullname' => 'Test course', 'shortname' => 'TST']);
|
||||
$context = course::instance($course->id);
|
||||
|
||||
$this->assertSame('Course: Test course', $context->get_context_name());
|
||||
$this->assertSame('Course: Test course', $context->get_context_name(true));
|
||||
$this->assertSame('Test course', $context->get_context_name(false));
|
||||
$this->assertSame('TST', $context->get_context_name(false, true));
|
||||
$this->assertSame('Course: TST', $context->get_context_name(true, true, false));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::get_url
|
||||
*/
|
||||
public function test_get_url() {
|
||||
$this->resetAfterTest();
|
||||
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$context = course::instance($course->id);
|
||||
|
||||
$expected = new \moodle_url('/course/view.php', ['id' => $course->id]);
|
||||
$url = $context->get_url();
|
||||
$this->assertInstanceOf(\moodle_url::class, $url);
|
||||
$this->assertSame($expected->out(), $url->out());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered methods.
|
||||
* @covers ::get_instance_table()
|
||||
* @covers ::get_behat_reference_columns()
|
||||
* @covers \core\context_helper::resolve_behat_reference
|
||||
*/
|
||||
public function test_resolve_behat_reference() {
|
||||
$this->resetAfterTest();
|
||||
|
||||
$instance = $this->getDataGenerator()->create_course(['shortname' => 'xyz']);
|
||||
$context = context\course::instance($instance->id);
|
||||
|
||||
$result = context_helper::resolve_behat_reference('Course', $instance->shortname);
|
||||
$this->assertSame($context->id, $result->id);
|
||||
|
||||
$result = context_helper::resolve_behat_reference('course', $instance->shortname);
|
||||
$this->assertSame($context->id, $result->id);
|
||||
|
||||
$result = context_helper::resolve_behat_reference('50', $instance->shortname);
|
||||
$this->assertSame($context->id, $result->id);
|
||||
|
||||
$result = context_helper::resolve_behat_reference('Course', 'dshjkdshjkhjsadjhdsa');
|
||||
$this->assertNull($result);
|
||||
|
||||
$result = context_helper::resolve_behat_reference('Course', '');
|
||||
$this->assertNull($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::get_compatible_role_archetypes
|
||||
*/
|
||||
public function test_get_compatible_role_archetypes() {
|
||||
global $DB;
|
||||
|
||||
$allarchetypes = $DB->get_fieldset_select('role', 'DISTINCT archetype', 'archetype IS NOT NULL');
|
||||
foreach ($allarchetypes as $allarchetype) {
|
||||
$levels = context_helper::get_compatible_levels($allarchetype);
|
||||
if ($allarchetype === 'editingteacher' || $allarchetype === 'teacher'
|
||||
|| $allarchetype === 'student' || $allarchetype === 'manager') {
|
||||
$this->assertContains(course::LEVEL, $levels, "$allarchetype is expected to be compatible with context");
|
||||
} else {
|
||||
$this->assertNotContains(course::LEVEL, $levels, "$allarchetype is not expected to be compatible with context");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::get_possible_parent_levels
|
||||
*/
|
||||
public function test_get_possible_parent_levels() {
|
||||
$this->assertSame([coursecat::LEVEL], course::get_possible_parent_levels());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::get_capabilities
|
||||
*/
|
||||
public function test_get_capabilities() {
|
||||
$this->resetAfterTest();
|
||||
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$context = course::instance($course->id);
|
||||
|
||||
$capabilities = $context->get_capabilities();
|
||||
$capabilities = convert_to_array($capabilities);
|
||||
$capabilities = array_column($capabilities, 'name');
|
||||
$this->assertContains('moodle/course:view', $capabilities);
|
||||
$this->assertContains('mod/page:view', $capabilities);
|
||||
$this->assertContains('mod/url:view', $capabilities);
|
||||
$this->assertNotContains('moodle/category:manage', $capabilities);
|
||||
$this->assertNotContains('moodle/user:viewalldetails', $capabilities);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::create_level_instances
|
||||
*/
|
||||
public function test_create_level_instances() {
|
||||
global $DB;
|
||||
$this->resetAfterTest();
|
||||
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$coursecontext = course::instance($course->id);
|
||||
|
||||
$DB->delete_records('context', ['id' => $coursecontext->id]);
|
||||
context_helper::create_instances(course::LEVEL);
|
||||
$record = $DB->get_record('context', ['contextlevel' => course::LEVEL, 'instanceid' => $course->id], '*', MUST_EXIST);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::get_child_contexts
|
||||
*/
|
||||
public function test_get_child_contexts() {
|
||||
$this->resetAfterTest();
|
||||
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$page = $this->getDataGenerator()->create_module('page', ['course' => $course->id, 'name' => 'Pokus']);
|
||||
|
||||
$context = course::instance($course->id);
|
||||
$children = $context->get_child_contexts();
|
||||
$this->assertCount(1, $children);
|
||||
$childcontext = reset($children);
|
||||
$this->assertInstanceOf(module::class, $childcontext);
|
||||
$this->assertEquals($page->cmid, $childcontext->instanceid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::get_cleanup_sql
|
||||
*/
|
||||
public function test_get_cleanup_sql() {
|
||||
global $DB;
|
||||
$this->resetAfterTest();
|
||||
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$coursecontext = course::instance($course->id);
|
||||
|
||||
$DB->delete_records('course', ['id' => $course->id]);
|
||||
|
||||
context_helper::cleanup_instances();
|
||||
$this->assertFalse($DB->record_exists('context', ['contextlevel' => course::LEVEL, 'instanceid' => $course->id]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::build_paths
|
||||
*/
|
||||
public function test_build_paths() {
|
||||
global $DB;
|
||||
$this->resetAfterTest();
|
||||
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$coursecontext = course::instance($course->id);
|
||||
$syscontext = system::instance();
|
||||
|
||||
$DB->set_field('context', 'depth', 1, ['id' => $coursecontext->id]);
|
||||
$DB->set_field('context', 'path', '/0', ['id' => $coursecontext->id]);
|
||||
|
||||
context_helper::build_all_paths(true);
|
||||
|
||||
$record = $DB->get_record('context', ['id' => $coursecontext->id]);
|
||||
$categorycontext = coursecat::instance($course->category);
|
||||
$this->assertSame('3', $record->depth);
|
||||
$this->assertSame('/' . $syscontext->id . '/' . $categorycontext->id . '/' . $record->id, $record->path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::set_locked
|
||||
*/
|
||||
public function test_set_locked() {
|
||||
global $DB;
|
||||
$this->resetAfterTest();
|
||||
|
||||
$course1 = $this->getDataGenerator()->create_course();
|
||||
$context1 = course::instance($course1->id);
|
||||
|
||||
$context1->set_locked(true);
|
||||
$context1 = course::instance($course1->id);
|
||||
$this->assertTrue($context1->locked);
|
||||
$record = $DB->get_record('context', ['id' => $context1->id]);
|
||||
$this->assertSame('1', $record->locked);
|
||||
|
||||
$context1->set_locked(false);
|
||||
$context1 = course::instance($course1->id);
|
||||
$this->assertFalse($context1->locked);
|
||||
$record = $DB->get_record('context', ['id' => $context1->id]);
|
||||
$this->assertSame('0', $record->locked);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,289 @@
|
||||
<?php
|
||||
// This file is part of Moodle - https://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
namespace core\context;
|
||||
|
||||
use core\context, core\context_helper;
|
||||
|
||||
/**
|
||||
* Unit tests for coursecat context class.
|
||||
*
|
||||
* NOTE: more tests are in lib/tests/accesslib_test.php
|
||||
*
|
||||
* @package core
|
||||
* @copyright Petr Skoda
|
||||
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @coversDefaultClass \core\context\coursecat
|
||||
*/
|
||||
class coursecat_test extends \advanced_testcase {
|
||||
/**
|
||||
* Tests legacy class name.
|
||||
* @coversNothing
|
||||
*/
|
||||
public function test_legacy_classname() {
|
||||
$category = \core_course_category::get_default();
|
||||
$context = \context_coursecat::instance($category->id);
|
||||
$this->assertInstanceOf(coursecat::class, $context);
|
||||
$this->assertInstanceOf(\context_coursecat::class, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered methods.
|
||||
* @covers ::instance
|
||||
* @covers \core\context::instance_by_id
|
||||
*/
|
||||
public function test_factory_methods() {
|
||||
$category = \core_course_category::get_default();
|
||||
$context = coursecat::instance($category->id);
|
||||
$this->assertInstanceOf(coursecat::class, $context);
|
||||
$this->assertSame($category->id, $context->instanceid);
|
||||
|
||||
$context = context::instance_by_id($context->id);
|
||||
$this->assertInstanceOf(coursecat::class, $context);
|
||||
$this->assertSame($category->id, $context->instanceid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::get_short_name
|
||||
*/
|
||||
public function test_get_short_name() {
|
||||
$this->assertSame('coursecat', coursecat::get_short_name());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests levels.
|
||||
* @coversNothing
|
||||
*/
|
||||
public function test_level() {
|
||||
$this->assertSame(40, coursecat::LEVEL);
|
||||
$this->assertSame(CONTEXT_COURSECAT, coursecat::LEVEL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::get_level_name
|
||||
*/
|
||||
public function test_get_level_name() {
|
||||
$this->assertSame('Category', coursecat::get_level_name());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::get_context_name
|
||||
*/
|
||||
public function test_get_context_name() {
|
||||
$category = \core_course_category::get_default();
|
||||
$context = coursecat::instance($category->id);
|
||||
$this->assertSame('Category: Category 1', $context->get_context_name());
|
||||
$this->assertSame('Category: Category 1', $context->get_context_name(true));
|
||||
$this->assertSame('Category 1', $context->get_context_name(false));
|
||||
$this->assertSame('Category 1', $context->get_context_name(false, true));
|
||||
$this->assertSame('Category: Category 1', $context->get_context_name(true, true, false));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::get_url
|
||||
*/
|
||||
public function test_get_url() {
|
||||
$category = \core_course_category::get_default();
|
||||
$context = coursecat::instance($category->id);
|
||||
$expected = new \moodle_url('/course/index.php', ['categoryid' => $category->id]);
|
||||
$url = $context->get_url();
|
||||
$this->assertInstanceOf(\moodle_url::class, $url);
|
||||
$this->assertSame($expected->out(), $url->out());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered methods.
|
||||
* @covers ::get_instance_table()
|
||||
* @covers ::get_behat_reference_columns()
|
||||
* @covers \core\context_helper::resolve_behat_reference
|
||||
*/
|
||||
public function test_resolve_behat_reference() {
|
||||
$this->resetAfterTest();
|
||||
|
||||
$instance = $this->getDataGenerator()->create_category(['idnumber' => 'xyz']);
|
||||
$context = context\coursecat::instance($instance->id);
|
||||
|
||||
$result = context_helper::resolve_behat_reference('Category', $instance->idnumber);
|
||||
$this->assertSame($context->id, $result->id);
|
||||
|
||||
$result = context_helper::resolve_behat_reference('coursecat', $instance->idnumber);
|
||||
$this->assertSame($context->id, $result->id);
|
||||
|
||||
$result = context_helper::resolve_behat_reference('40', $instance->idnumber);
|
||||
$this->assertSame($context->id, $result->id);
|
||||
|
||||
$result = context_helper::resolve_behat_reference('Category', 'dshjkdshjkhjsadjhdsa');
|
||||
$this->assertNull($result);
|
||||
|
||||
$result = context_helper::resolve_behat_reference('Category', '');
|
||||
$this->assertNull($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::get_compatible_role_archetypes
|
||||
*/
|
||||
public function test_get_compatible_role_archetypes() {
|
||||
global $DB;
|
||||
|
||||
$allarchetypes = $DB->get_fieldset_select('role', 'DISTINCT archetype', 'archetype IS NOT NULL');
|
||||
foreach ($allarchetypes as $allarchetype) {
|
||||
$levels = context_helper::get_compatible_levels($allarchetype);
|
||||
if ($allarchetype === 'manager' || $allarchetype === 'coursecreator') {
|
||||
$this->assertContains(coursecat::LEVEL, $levels, "$allarchetype is expected to be compatible with context");
|
||||
} else {
|
||||
$this->assertNotContains(coursecat::LEVEL, $levels, "$allarchetype is not expected to be compatible with context");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::get_possible_parent_levels
|
||||
*/
|
||||
public function test_get_possible_parent_levels() {
|
||||
$this->assertSame([system::LEVEL, coursecat::LEVEL], coursecat::get_possible_parent_levels());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::get_capabilities
|
||||
*/
|
||||
public function test_get_capabilities() {
|
||||
$category = \core_course_category::get_default();
|
||||
|
||||
$context = coursecat::instance($category->id);
|
||||
$capabilities = $context->get_capabilities();
|
||||
$capabilities = convert_to_array($capabilities);
|
||||
$capabilities = array_column($capabilities, 'name');
|
||||
$this->assertContains('moodle/category:manage', $capabilities);
|
||||
$this->assertContains('moodle/course:view', $capabilities);
|
||||
$this->assertNotContains('moodle/user:viewalldetails', $capabilities);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::create_level_instances
|
||||
*/
|
||||
public function test_create_level_instances() {
|
||||
global $DB;
|
||||
$this->resetAfterTest();
|
||||
|
||||
$coursecat = $this->getDataGenerator()->create_category();
|
||||
$coursecatcontext = coursecat::instance($coursecat->id);
|
||||
|
||||
$DB->delete_records('context', ['id' => $coursecatcontext->id]);
|
||||
context_helper::create_instances(coursecat::LEVEL);
|
||||
$record = $DB->get_record('context', ['contextlevel' => coursecat::LEVEL, 'instanceid' => $coursecat->id], '*', MUST_EXIST);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::get_child_contexts
|
||||
*/
|
||||
public function test_get_child_contexts() {
|
||||
$this->resetAfterTest();
|
||||
|
||||
$category = \core_course_category::get_default();
|
||||
|
||||
$context = coursecat::instance($category->id);
|
||||
$children = $context->get_child_contexts();
|
||||
$this->assertCount(0, $children);
|
||||
|
||||
$course = $this->getDataGenerator()->create_course(['categoryid' => $category->id]);
|
||||
|
||||
// This may fail if some plugin auto-creates activities.
|
||||
$children = $context->get_child_contexts();
|
||||
$this->assertCount(1, $children);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::get_cleanup_sql
|
||||
*/
|
||||
public function test_get_cleanup_sql() {
|
||||
global $DB;
|
||||
$this->resetAfterTest();
|
||||
|
||||
$coursecat = $this->getDataGenerator()->create_category();
|
||||
$coursecatcontext = coursecat::instance($coursecat->id);
|
||||
|
||||
$DB->delete_records('course_categories', ['id' => $coursecat->id]);
|
||||
|
||||
context_helper::cleanup_instances();
|
||||
$this->assertFalse($DB->record_exists('context', ['contextlevel' => coursecat::LEVEL, 'instanceid' => $coursecat->id]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::build_paths
|
||||
*/
|
||||
public function test_build_paths() {
|
||||
global $DB;
|
||||
$this->resetAfterTest();
|
||||
|
||||
$coursecat = $this->getDataGenerator()->create_category();
|
||||
$coursecatcontext = coursecat::instance($coursecat->id);
|
||||
$syscontext = system::instance();
|
||||
|
||||
$DB->set_field('context', 'depth', 1, ['id' => $coursecatcontext->id]);
|
||||
$DB->set_field('context', 'path', '/0', ['id' => $coursecatcontext->id]);
|
||||
|
||||
context_helper::build_all_paths(true);
|
||||
|
||||
$record = $DB->get_record('context', ['id' => $coursecatcontext->id]);
|
||||
$this->assertSame('2', $record->depth);
|
||||
$this->assertSame('/' . $syscontext->id . '/' . $record->id, $record->path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::set_locked
|
||||
*/
|
||||
public function test_set_locked() {
|
||||
global $DB;
|
||||
$this->resetAfterTest();
|
||||
|
||||
$category1 = $this->getDataGenerator()->create_category();
|
||||
$category2 = $this->getDataGenerator()->create_category(['parent' => $category1->id]);
|
||||
$context1 = coursecat::instance($category1->id);
|
||||
$context2 = coursecat::instance($category2->id);
|
||||
|
||||
$context1->set_locked(true);
|
||||
$context1 = coursecat::instance($category1->id);
|
||||
$context2 = coursecat::instance($category2->id);
|
||||
$this->assertTrue($context1->locked);
|
||||
$this->assertTrue($context2->locked);
|
||||
$record = $DB->get_record('context', ['id' => $context1->id]);
|
||||
$this->assertSame('1', $record->locked);
|
||||
$record = $DB->get_record('context', ['id' => $context2->id]);
|
||||
$this->assertSame('0', $record->locked);
|
||||
|
||||
$context1->set_locked(false);
|
||||
$context1 = coursecat::instance($category1->id);
|
||||
$context2 = coursecat::instance($category2->id);
|
||||
$this->assertFalse($context1->locked);
|
||||
$this->assertFalse($context2->locked);
|
||||
$record = $DB->get_record('context', ['id' => $context1->id]);
|
||||
$this->assertSame('0', $record->locked);
|
||||
$record = $DB->get_record('context', ['id' => $context2->id]);
|
||||
$this->assertSame('0', $record->locked);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,306 @@
|
||||
<?php
|
||||
// This file is part of Moodle - https://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
namespace core\context;
|
||||
|
||||
use core\context, core\context_helper;
|
||||
|
||||
/**
|
||||
* Unit tests for module context class.
|
||||
*
|
||||
* NOTE: more tests are in lib/tests/accesslib_test.php
|
||||
*
|
||||
* @package core
|
||||
* @copyright Petr Skoda
|
||||
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @coversDefaultClass \core\context\module
|
||||
*/
|
||||
class module_test extends \advanced_testcase {
|
||||
/**
|
||||
* Tests legacy class.
|
||||
* @coversNothing
|
||||
*/
|
||||
public function test_legacy_classname() {
|
||||
$this->resetAfterTest();
|
||||
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$page = $this->getDataGenerator()->create_module('page', ['course' => $course->id]);
|
||||
$context = module::instance($page->cmid);
|
||||
|
||||
$this->assertInstanceOf(module::class, $context);
|
||||
$this->assertInstanceOf(\context_module::class, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered methods.
|
||||
* @covers ::instance
|
||||
* @covers \core\context::instance_by_id
|
||||
*/
|
||||
public function test_factory_methods() {
|
||||
$this->resetAfterTest();
|
||||
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$page = $this->getDataGenerator()->create_module('page', ['course' => $course->id]);
|
||||
$context = module::instance($page->cmid);
|
||||
|
||||
$this->assertInstanceOf(module::class, $context);
|
||||
$this->assertSame((string)$page->cmid, $context->instanceid);
|
||||
|
||||
$context = context::instance_by_id($context->id);
|
||||
$this->assertInstanceOf(module::class, $context);
|
||||
$this->assertSame((string)$page->cmid, $context->instanceid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::get_short_name
|
||||
*/
|
||||
public function test_get_short_name() {
|
||||
$this->assertSame('module', module::get_short_name());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests context level.
|
||||
* @coversNothing
|
||||
*/
|
||||
public function test_level() {
|
||||
$this->assertSame(70, module::LEVEL);
|
||||
$this->assertSame(CONTEXT_MODULE, module::LEVEL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::get_level_name
|
||||
*/
|
||||
public function test_get_level_name() {
|
||||
$this->assertSame('Activity module', module::get_level_name());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::get_context_name
|
||||
*/
|
||||
public function test_get_context_name() {
|
||||
$this->resetAfterTest();
|
||||
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$page = $this->getDataGenerator()->create_module('page', ['course' => $course->id, 'name' => 'Pokus']);
|
||||
$context = module::instance($page->cmid);
|
||||
|
||||
$this->assertSame('Page: Pokus', $context->get_context_name());
|
||||
$this->assertSame('Page: Pokus', $context->get_context_name(true));
|
||||
$this->assertSame('Pokus', $context->get_context_name(false));
|
||||
$this->assertSame('Pokus', $context->get_context_name(false, true));
|
||||
$this->assertSame('Page: Pokus', $context->get_context_name(true, true, false));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::get_url
|
||||
*/
|
||||
public function test_get_url() {
|
||||
$this->resetAfterTest();
|
||||
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$page = $this->getDataGenerator()->create_module('page', ['course' => $course->id, 'name' => 'Pokus']);
|
||||
$context = module::instance($page->cmid);
|
||||
|
||||
$expected = new \moodle_url('/mod/page/view.php', ['id' => $page->cmid]);
|
||||
$url = $context->get_url();
|
||||
$this->assertInstanceOf(\moodle_url::class, $url);
|
||||
$this->assertSame($expected->out(), $url->out());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered methods.
|
||||
* @covers ::get_instance_table()
|
||||
* @covers ::get_behat_reference_columns()
|
||||
* @covers \core\context_helper::resolve_behat_reference
|
||||
*/
|
||||
public function test_resolve_behat_reference() {
|
||||
global $DB;
|
||||
$this->resetAfterTest();
|
||||
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$page = $this->getDataGenerator()->create_module('page', ['course' => $course->id, 'idnumber' => 'xyz']);
|
||||
$instance = $DB->get_record('course_modules', ['id' => $page->cmid], '*', MUST_EXIST);
|
||||
$context = module::instance($instance->id);
|
||||
|
||||
$result = context_helper::resolve_behat_reference('Activity module', $instance->idnumber);
|
||||
$this->assertSame($context->id, $result->id);
|
||||
|
||||
$result = context_helper::resolve_behat_reference('module', $instance->idnumber);
|
||||
$this->assertSame($context->id, $result->id);
|
||||
|
||||
$result = context_helper::resolve_behat_reference('70', $instance->idnumber);
|
||||
$this->assertSame($context->id, $result->id);
|
||||
|
||||
$result = context_helper::resolve_behat_reference('Activity module', 'dshjkdshjkhjsadjhdsa');
|
||||
$this->assertNull($result);
|
||||
|
||||
$result = context_helper::resolve_behat_reference('Activity module', '');
|
||||
$this->assertNull($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::get_compatible_role_archetypes
|
||||
*/
|
||||
public function test_get_compatible_role_archetypes() {
|
||||
global $DB;
|
||||
|
||||
$allarchetypes = $DB->get_fieldset_select('role', 'DISTINCT archetype', 'archetype IS NOT NULL');
|
||||
foreach ($allarchetypes as $allarchetype) {
|
||||
$levels = context_helper::get_compatible_levels($allarchetype);
|
||||
if ($allarchetype === 'editingteacher' || $allarchetype === 'teacher' || $allarchetype === 'student') {
|
||||
$this->assertContains(module::LEVEL, $levels, "$allarchetype is expected to be compatible with context");
|
||||
} else {
|
||||
$this->assertNotContains(module::LEVEL, $levels, "$allarchetype is not expected to be compatible with context");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::get_possible_parent_levels
|
||||
*/
|
||||
public function test_get_possible_parent_levels() {
|
||||
$this->assertSame([course::LEVEL], module::get_possible_parent_levels());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::get_capabilities
|
||||
*/
|
||||
public function test_get_capabilities() {
|
||||
$this->resetAfterTest();
|
||||
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$page = $this->getDataGenerator()->create_module('page', ['course' => $course->id, 'name' => 'Pokus']);
|
||||
$context = module::instance($page->cmid);
|
||||
|
||||
$capabilities = $context->get_capabilities();
|
||||
$capabilities = convert_to_array($capabilities);
|
||||
$capabilities = array_column($capabilities, 'name');
|
||||
$this->assertContains('mod/page:view', $capabilities);
|
||||
$this->assertNotContains('mod/url:view', $capabilities);
|
||||
$this->assertNotContains('moodle/course:view', $capabilities);
|
||||
$this->assertNotContains('moodle/category:manage', $capabilities);
|
||||
$this->assertNotContains('moodle/user:viewalldetails', $capabilities);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::create_level_instances
|
||||
*/
|
||||
public function test_create_level_instances() {
|
||||
global $DB;
|
||||
$this->resetAfterTest();
|
||||
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$page = $this->getDataGenerator()->create_module('page', ['course' => $course->id, 'name' => 'Pokus']);
|
||||
$context = module::instance($page->cmid);
|
||||
|
||||
$DB->delete_records('context', ['id' => $context->id]);
|
||||
context_helper::create_instances(module::LEVEL);
|
||||
$record = $DB->get_record('context', ['contextlevel' => module::LEVEL, 'instanceid' => $page->cmid], '*', MUST_EXIST);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::get_child_contexts
|
||||
*/
|
||||
public function test_get_child_contexts() {
|
||||
$this->resetAfterTest();
|
||||
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$page = $this->getDataGenerator()->create_module('page', ['course' => $course->id, 'name' => 'Pokus']);
|
||||
$context = module::instance($page->cmid);
|
||||
|
||||
$children = $context->get_child_contexts();
|
||||
$this->assertCount(0, $children);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::get_cleanup_sql
|
||||
*/
|
||||
public function test_get_cleanup_sql() {
|
||||
global $DB;
|
||||
$this->resetAfterTest();
|
||||
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$page = $this->getDataGenerator()->create_module('page', ['course' => $course->id, 'name' => 'Pokus']);
|
||||
$context = module::instance($page->cmid);
|
||||
|
||||
$DB->delete_records('course_modules', ['id' => $page->cmid]);
|
||||
$DB->delete_records('page', ['id' => $page->id]);
|
||||
|
||||
context_helper::cleanup_instances();
|
||||
$this->assertFalse($DB->record_exists('context', ['contextlevel' => module::LEVEL, 'instanceid' => $page->cmid]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::build_paths
|
||||
*/
|
||||
public function test_build_paths() {
|
||||
global $DB;
|
||||
$this->resetAfterTest();
|
||||
|
||||
$syscontext = system::instance();
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$coursecontext = course::instance($course->id);
|
||||
$page = $this->getDataGenerator()->create_module('page', ['course' => $course->id, 'name' => 'Pokus']);
|
||||
$pagecontext = module::instance($page->cmid);
|
||||
|
||||
$DB->set_field('context', 'depth', 1, ['id' => $pagecontext->id]);
|
||||
$DB->set_field('context', 'path', '/0', ['id' => $pagecontext->id]);
|
||||
|
||||
context_helper::build_all_paths(true);
|
||||
|
||||
$record = $DB->get_record('context', ['id' => $pagecontext->id]);
|
||||
$categorycontext = coursecat::instance($course->category);
|
||||
$this->assertSame('4', $record->depth);
|
||||
$this->assertSame('/' . $syscontext->id . '/' . $categorycontext->id . '/'
|
||||
. $coursecontext->id . '/' . $record->id, $record->path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::set_locked
|
||||
*/
|
||||
public function test_set_locked() {
|
||||
global $DB;
|
||||
$this->resetAfterTest();
|
||||
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$page = $this->getDataGenerator()->create_module('page', ['course' => $course->id, 'name' => 'Pokus']);
|
||||
$context = module::instance($page->cmid);
|
||||
|
||||
$context->set_locked(true);
|
||||
$context = module::instance($page->cmid);
|
||||
$this->assertTrue($context->locked);
|
||||
$record = $DB->get_record('context', ['id' => $context->id]);
|
||||
$this->assertSame('1', $record->locked);
|
||||
|
||||
$context->set_locked(false);
|
||||
$context = module::instance($page->cmid);
|
||||
$this->assertFalse($context->locked);
|
||||
$record = $DB->get_record('context', ['id' => $context->id]);
|
||||
$this->assertSame('0', $record->locked);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,233 @@
|
||||
<?php
|
||||
// This file is part of Moodle - https://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
namespace core\context;
|
||||
|
||||
use core\context, core\context_helper;
|
||||
|
||||
/**
|
||||
* Unit tests for system context class.
|
||||
*
|
||||
* NOTE: more tests are in lib/tests/accesslib_test.php
|
||||
*
|
||||
* @package core
|
||||
* @copyright Petr Skoda
|
||||
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @coversDefaultClass \core\context\system
|
||||
*/
|
||||
class system_test extends \advanced_testcase {
|
||||
/**
|
||||
* Tests legacy class.
|
||||
* @coversNothing
|
||||
*/
|
||||
public function test_legacy_classname() {
|
||||
$context = \context_system::instance();
|
||||
$this->assertInstanceOf(system::class, $context);
|
||||
$this->assertInstanceOf(\context_system::class, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered methods.
|
||||
* @covers ::instance
|
||||
* @covers \core\context::instance_by_id
|
||||
*/
|
||||
public function test_factory_methods() {
|
||||
$context = system::instance();
|
||||
$this->assertInstanceOf(system::class, $context);
|
||||
$this->assertEquals(SYSCONTEXTID, $context->id);
|
||||
|
||||
$context = context::instance_by_id($context->id);
|
||||
$this->assertInstanceOf(system::class, $context);
|
||||
$this->assertEquals(SYSCONTEXTID, $context->id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::get_short_name
|
||||
*/
|
||||
public function test_get_short_name() {
|
||||
$this->assertSame('system', system::get_short_name());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests context level.
|
||||
* @coversNothing
|
||||
*/
|
||||
public function test_level() {
|
||||
$this->assertSame(10, system::LEVEL);
|
||||
$this->assertSame(CONTEXT_SYSTEM, system::LEVEL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::get_level_name
|
||||
*/
|
||||
public function test_get_level_name() {
|
||||
$this->assertSame('System', system::get_level_name());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::get_context_name
|
||||
*/
|
||||
public function test_get_context_name() {
|
||||
$context = system::instance();
|
||||
$this->assertSame('System', $context->get_context_name());
|
||||
$this->assertSame('System', $context->get_context_name(true));
|
||||
$this->assertSame('System', $context->get_context_name(false));
|
||||
$this->assertSame('System', $context->get_context_name(false, true));
|
||||
$this->assertSame('System', $context->get_context_name(true, true, false));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::get_url
|
||||
*/
|
||||
public function test_get_url() {
|
||||
$context = system::instance();
|
||||
$expected = new \moodle_url('/');
|
||||
$url = $context->get_url();
|
||||
$this->assertInstanceOf(\moodle_url::class, $url);
|
||||
$this->assertSame($expected->out(), $url->out());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers \core\context_helper::resolve_behat_reference
|
||||
*/
|
||||
public function test_resolve_behat_reference() {
|
||||
$syscontext = context\system::instance();
|
||||
|
||||
$result = context_helper::resolve_behat_reference('System', '');
|
||||
$this->assertSame($syscontext->id, $result->id);
|
||||
|
||||
$result = context_helper::resolve_behat_reference('System', '44');
|
||||
$this->assertSame($syscontext->id, $result->id);
|
||||
|
||||
$result = context_helper::resolve_behat_reference('system', '');
|
||||
$this->assertSame($syscontext->id, $result->id);
|
||||
|
||||
$result = context_helper::resolve_behat_reference('10', '');
|
||||
$this->assertSame($syscontext->id, $result->id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::get_compatible_role_archetypes
|
||||
*/
|
||||
public function test_get_compatible_role_archetypes() {
|
||||
global $DB;
|
||||
|
||||
$allarchetypes = $DB->get_fieldset_select('role', 'DISTINCT archetype', 'archetype IS NOT NULL');
|
||||
foreach ($allarchetypes as $allarchetype) {
|
||||
$levels = context_helper::get_compatible_levels($allarchetype);
|
||||
if ($allarchetype === 'manager' || $allarchetype === 'coursecreator') {
|
||||
$this->assertContains(system::LEVEL, $levels, "$allarchetype is expected to be compatible with context");
|
||||
} else {
|
||||
$this->assertNotContains(system::LEVEL, $levels, "$allarchetype is not expected to be compatible with context");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::get_possible_parent_levels
|
||||
*/
|
||||
public function test_get_possible_parent_levels() {
|
||||
$this->assertSame([], system::get_possible_parent_levels());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::get_capabilities
|
||||
*/
|
||||
public function test_get_capabilities() {
|
||||
global $DB;
|
||||
|
||||
$context = system::instance();
|
||||
$capabilities = $context->get_capabilities();
|
||||
$expected = $DB->count_records('capabilities', []);
|
||||
$this->assertCount($expected, $capabilities);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::create_level_instances
|
||||
*/
|
||||
public function test_create_level_instances() {
|
||||
context_helper::create_instances(system::LEVEL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::get_child_contexts
|
||||
*/
|
||||
public function test_get_child_contexts() {
|
||||
global $DB;
|
||||
|
||||
$context = system::instance();
|
||||
$children = $context->get_child_contexts();
|
||||
$expected = $DB->count_records('context', []) - 1;
|
||||
$this->assertCount($expected, $children);
|
||||
$this->assertDebuggingCalled('Fetching of system context child courses is strongly '
|
||||
. 'discouraged on production servers (it may eat all available memory)!');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::get_cleanup_sql
|
||||
*/
|
||||
public function test_get_cleanup_sql() {
|
||||
// Nothing to clean up actually.
|
||||
context_helper::cleanup_instances();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::build_paths
|
||||
*/
|
||||
public function test_build_paths() {
|
||||
global $DB;
|
||||
$this->resetAfterTest();
|
||||
|
||||
$DB->set_field('context', 'depth', 2, ['id' => SYSCONTEXTID]);
|
||||
$DB->set_field('context', 'path', '/0', ['id' => SYSCONTEXTID]);
|
||||
|
||||
context_helper::build_all_paths(true);
|
||||
|
||||
$record = $DB->get_record('context', ['id' => SYSCONTEXTID]);
|
||||
$this->assertSame('1', $record->depth);
|
||||
$this->assertSame('/' . $record->id, $record->path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::set_locked
|
||||
*/
|
||||
public function test_set_locked() {
|
||||
$context = system::instance();
|
||||
|
||||
$context->set_locked(false);
|
||||
|
||||
try {
|
||||
$context->set_locked(true);
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf(\coding_exception::class, $e);
|
||||
$this->assertSame('Coding error detected, it must be fixed by a programmer: '
|
||||
. 'It is not possible to lock the system context', $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,268 @@
|
||||
<?php
|
||||
// This file is part of Moodle - https://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
namespace core\context;
|
||||
|
||||
use core\context, core\context_helper;
|
||||
|
||||
/**
|
||||
* Unit tests for user context class.
|
||||
*
|
||||
* NOTE: more tests are in lib/tests/accesslib_test.php
|
||||
*
|
||||
* @package core
|
||||
* @copyright Petr Skoda
|
||||
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @coversDefaultClass \core\context\user
|
||||
*/
|
||||
class user_test extends \advanced_testcase {
|
||||
/**
|
||||
* Tests legacy class.
|
||||
* @coversNothing
|
||||
*/
|
||||
public function test_legacy_classname() {
|
||||
$admin = get_admin();
|
||||
$context = \context_user::instance($admin->id);
|
||||
$this->assertInstanceOf(user::class, $context);
|
||||
$this->assertInstanceOf(\context_user::class, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered methods.
|
||||
* @covers ::instance
|
||||
* @covers \core\context::instance_by_id
|
||||
*/
|
||||
public function test_factory_methods() {
|
||||
$admin = get_admin();
|
||||
$context = user::instance($admin->id);
|
||||
$this->assertInstanceOf(user::class, $context);
|
||||
$this->assertSame($admin->id, $context->instanceid);
|
||||
|
||||
$context = context::instance_by_id($context->id);
|
||||
$this->assertInstanceOf(user::class, $context);
|
||||
$this->assertSame($admin->id, $context->instanceid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::get_short_name
|
||||
*/
|
||||
public function test_get_short_name() {
|
||||
$this->assertSame('user', user::get_short_name());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests context level.
|
||||
* @coversNothing
|
||||
*/
|
||||
public function test_level() {
|
||||
$this->assertSame(30, user::LEVEL);
|
||||
$this->assertSame(CONTEXT_USER, user::LEVEL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::get_level_name
|
||||
*/
|
||||
public function test_get_level_name() {
|
||||
$this->assertSame('User', user::get_level_name());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::get_context_name
|
||||
*/
|
||||
public function test_get_context_name() {
|
||||
$admin = get_admin();
|
||||
$context = user::instance($admin->id);
|
||||
$this->assertSame('User: Admin User', $context->get_context_name());
|
||||
$this->assertSame('User: Admin User', $context->get_context_name(true));
|
||||
$this->assertSame('Admin User', $context->get_context_name(false));
|
||||
$this->assertSame('Admin User', $context->get_context_name(false, true));
|
||||
$this->assertSame('User: Admin User', $context->get_context_name(true, true, false));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::get_url
|
||||
*/
|
||||
public function test_get_url() {
|
||||
$admin = get_admin();
|
||||
$context = user::instance($admin->id);
|
||||
$expected = new \moodle_url('/user/profile.php', ['id' => $admin->id]);
|
||||
$url = $context->get_url();
|
||||
$this->assertInstanceOf(\moodle_url::class, $url);
|
||||
$this->assertSame($expected->out(), $url->out());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered methods.
|
||||
* @covers ::get_instance_table()
|
||||
* @covers ::get_behat_reference_columns()
|
||||
* @covers \core\context_helper::resolve_behat_reference
|
||||
*/
|
||||
public function test_resolve_behat_reference() {
|
||||
$this->resetAfterTest();
|
||||
|
||||
$instance = $this->getDataGenerator()->create_user();
|
||||
$context = context\user::instance($instance->id);
|
||||
|
||||
$result = context_helper::resolve_behat_reference('User', $instance->username);
|
||||
$this->assertSame($context->id, $result->id);
|
||||
|
||||
$result = context_helper::resolve_behat_reference('user', $instance->username);
|
||||
$this->assertSame($context->id, $result->id);
|
||||
|
||||
$result = context_helper::resolve_behat_reference('30', $instance->username);
|
||||
$this->assertSame($context->id, $result->id);
|
||||
|
||||
$result = context_helper::resolve_behat_reference('User', 'dshjkdshjkhjsadjhdsa');
|
||||
$this->assertNull($result);
|
||||
|
||||
$result = context_helper::resolve_behat_reference('User', '');
|
||||
$this->assertNull($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::get_compatible_role_archetypes
|
||||
*/
|
||||
public function test_get_compatible_role_archetypes() {
|
||||
global $DB;
|
||||
|
||||
$allarchetypes = $DB->get_fieldset_select('role', 'DISTINCT archetype', 'archetype IS NOT NULL');
|
||||
foreach ($allarchetypes as $allarchetype) {
|
||||
$levels = context_helper::get_compatible_levels($allarchetype);
|
||||
$this->assertNotContains(user::LEVEL, $levels, "$allarchetype is not expected to be compatible with context");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::get_possible_parent_levels
|
||||
*/
|
||||
public function test_get_possible_parent_levels() {
|
||||
$this->assertSame([system::LEVEL], user::get_possible_parent_levels());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::get_capabilities
|
||||
*/
|
||||
public function test_get_capabilities() {
|
||||
$admin = get_admin();
|
||||
|
||||
$context = user::instance($admin->id);
|
||||
$capabilities = $context->get_capabilities();
|
||||
$capabilities = convert_to_array($capabilities);
|
||||
$capabilities = array_column($capabilities, 'name');
|
||||
|
||||
$this->assertContains('moodle/user:viewalldetails', $capabilities);
|
||||
$this->assertContains('moodle/grade:viewall', $capabilities);
|
||||
$this->assertNotContains('moodle/course:view', $capabilities);
|
||||
$this->assertNotContains('moodle/site:config', $capabilities);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::create_level_instances
|
||||
*/
|
||||
public function test_create_level_instances() {
|
||||
global $DB;
|
||||
$this->resetAfterTest();
|
||||
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
$usercontext = user::instance($user->id);
|
||||
|
||||
$DB->delete_records('context', ['id' => $usercontext->id]);
|
||||
context_helper::create_instances(user::LEVEL);
|
||||
$record = $DB->get_record('context', ['contextlevel' => user::LEVEL, 'instanceid' => $user->id], '*', MUST_EXIST);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::get_child_contexts
|
||||
*/
|
||||
public function test_get_child_contexts() {
|
||||
$admin = get_admin();
|
||||
|
||||
$context = user::instance($admin->id);
|
||||
$children = $context->get_child_contexts();
|
||||
$this->assertCount(0, $children);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::get_cleanup_sql
|
||||
*/
|
||||
public function test_get_cleanup_sql() {
|
||||
global $DB;
|
||||
$this->resetAfterTest();
|
||||
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
$usercontext = user::instance($user->id);
|
||||
|
||||
$DB->set_field('user', 'deleted', 1, ['id' => $user->id]);
|
||||
|
||||
context_helper::cleanup_instances();
|
||||
$this->assertFalse($DB->record_exists('context', ['contextlevel' => user::LEVEL, 'instanceid' => $user->id]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::build_paths
|
||||
*/
|
||||
public function test_build_paths() {
|
||||
global $DB;
|
||||
$this->resetAfterTest();
|
||||
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
$usercontext = user::instance($user->id);
|
||||
$syscontext = system::instance();
|
||||
|
||||
$DB->set_field('context', 'depth', 1, ['id' => $usercontext->id]);
|
||||
$DB->set_field('context', 'path', '/0', ['id' => $usercontext->id]);
|
||||
|
||||
context_helper::build_all_paths(true);
|
||||
|
||||
$record = $DB->get_record('context', ['id' => $usercontext->id]);
|
||||
$this->assertSame('2', $record->depth);
|
||||
$this->assertSame('/' . $syscontext->id . '/' . $record->id, $record->path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::set_locked
|
||||
*/
|
||||
public function test_set_locked() {
|
||||
global $DB;
|
||||
$this->resetAfterTest();
|
||||
|
||||
$admin = get_admin();
|
||||
$context = user::instance($admin->id);
|
||||
$this->assertFalse($context->locked);
|
||||
|
||||
$context->set_locked(true);
|
||||
$this->assertTrue($context->locked);
|
||||
$record = $DB->get_record('context', ['id' => $context->id]);
|
||||
$this->assertSame('1', $record->locked);
|
||||
|
||||
$context->set_locked(false);
|
||||
$this->assertFalse($context->locked);
|
||||
$record = $DB->get_record('context', ['id' => $context->id]);
|
||||
$this->assertSame('0', $record->locked);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,530 @@
|
||||
<?php
|
||||
// This file is part of Moodle - https://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
namespace core;
|
||||
|
||||
/**
|
||||
* Unit tests for context helper class.
|
||||
*
|
||||
* NOTE: more tests are in lib/tests/accesslib_test.php
|
||||
*
|
||||
* @package core
|
||||
* @copyright Petr Skoda
|
||||
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @coversDefaultClass \core\context_helper
|
||||
*/
|
||||
class context_helper_test extends \advanced_testcase {
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::parse_external_level
|
||||
*/
|
||||
public function test_parse_external_level() {
|
||||
$this->assertSame(context\system::class, context_helper::parse_external_level('system'));
|
||||
$this->assertSame(context\system::class, context_helper::parse_external_level(CONTEXT_SYSTEM));
|
||||
$this->assertSame(context\system::class, context_helper::parse_external_level((string)CONTEXT_SYSTEM));
|
||||
|
||||
$this->assertSame(context\user::class, context_helper::parse_external_level('user'));
|
||||
$this->assertSame(context\user::class, context_helper::parse_external_level(CONTEXT_USER));
|
||||
$this->assertSame(context\user::class, context_helper::parse_external_level((string)CONTEXT_USER));
|
||||
|
||||
$this->assertSame(context\coursecat::class, context_helper::parse_external_level('coursecat'));
|
||||
$this->assertSame(context\coursecat::class, context_helper::parse_external_level(CONTEXT_COURSECAT));
|
||||
$this->assertSame(context\coursecat::class, context_helper::parse_external_level((string)CONTEXT_COURSECAT));
|
||||
|
||||
$this->assertSame(context\course::class, context_helper::parse_external_level('course'));
|
||||
$this->assertSame(context\course::class, context_helper::parse_external_level(CONTEXT_COURSE));
|
||||
$this->assertSame(context\course::class, context_helper::parse_external_level((string)CONTEXT_COURSE));
|
||||
|
||||
$this->assertSame(context\module::class, context_helper::parse_external_level('module'));
|
||||
$this->assertSame(context\module::class, context_helper::parse_external_level(CONTEXT_MODULE));
|
||||
$this->assertSame(context\module::class, context_helper::parse_external_level((string)CONTEXT_MODULE));
|
||||
|
||||
$this->assertSame(context\block::class, context_helper::parse_external_level('block'));
|
||||
$this->assertSame(context\block::class, context_helper::parse_external_level(CONTEXT_BLOCK));
|
||||
$this->assertSame(context\block::class, context_helper::parse_external_level((string)CONTEXT_BLOCK));
|
||||
|
||||
$this->assertNull(context_helper::parse_external_level('core_system'));
|
||||
$this->assertNull(context_helper::parse_external_level('xsystem'));
|
||||
$this->assertNull(context_helper::parse_external_level(1));
|
||||
$this->assertNull(context_helper::parse_external_level(''));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::resolve_behat_reference
|
||||
*/
|
||||
public function test_resolve_behat_reference() {
|
||||
$this->assertNull(context_helper::resolve_behat_reference('blahbla', 'blahbla'));
|
||||
$this->assertNull(context_helper::resolve_behat_reference('', ''));
|
||||
$this->assertNull(context_helper::resolve_behat_reference('0', ''));
|
||||
|
||||
$syscontext = context\system::instance();
|
||||
$result = context_helper::resolve_behat_reference('System', '');
|
||||
$this->assertSame($syscontext->id, $result->id);
|
||||
|
||||
$syscontext = context\system::instance();
|
||||
$result = context_helper::resolve_behat_reference('10', '');
|
||||
$this->assertSame($syscontext->id, $result->id);
|
||||
|
||||
// The rest is tested in each context class test.
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::get_class_for_level
|
||||
*/
|
||||
public function test_get_class_for_level() {
|
||||
$this->assertSame(context\system::class, context_helper::get_class_for_level(CONTEXT_SYSTEM));
|
||||
$this->assertSame(context\system::class, context_helper::get_class_for_level((string)CONTEXT_SYSTEM));
|
||||
|
||||
$this->assertSame(context\user::class, context_helper::get_class_for_level(CONTEXT_USER));
|
||||
$this->assertSame(context\user::class, context_helper::get_class_for_level((string)CONTEXT_USER));
|
||||
|
||||
$this->assertSame(context\coursecat::class, context_helper::get_class_for_level(CONTEXT_COURSECAT));
|
||||
$this->assertSame(context\coursecat::class, context_helper::get_class_for_level((string)CONTEXT_COURSECAT));
|
||||
|
||||
$this->assertSame(context\course::class, context_helper::get_class_for_level(CONTEXT_COURSE));
|
||||
$this->assertSame(context\course::class, context_helper::get_class_for_level((string)CONTEXT_COURSE));
|
||||
|
||||
$this->assertSame(context\module::class, context_helper::get_class_for_level(CONTEXT_MODULE));
|
||||
$this->assertSame(context\module::class, context_helper::get_class_for_level((string)CONTEXT_MODULE));
|
||||
|
||||
$this->assertSame(context\block::class, context_helper::get_class_for_level(CONTEXT_BLOCK));
|
||||
$this->assertSame(context\block::class, context_helper::get_class_for_level((string)CONTEXT_BLOCK));
|
||||
|
||||
try {
|
||||
context_helper::get_class_for_level(1);
|
||||
$this->fail('Exception expected if level does not exist');
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf(\coding_exception::class, $e);
|
||||
$this->assertSame('Coding error detected, it must be fixed by a programmer: Invalid context level specified',
|
||||
$e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::get_all_levels
|
||||
*/
|
||||
public function test_get_all_levels() {
|
||||
$levels = context_helper::get_all_levels();
|
||||
|
||||
$this->assertArrayHasKey(CONTEXT_SYSTEM, $levels);
|
||||
$this->assertSame(context\system::class, $levels[CONTEXT_SYSTEM]);
|
||||
|
||||
$this->assertArrayHasKey(CONTEXT_USER, $levels);
|
||||
$this->assertSame(context\user::class, $levels[CONTEXT_USER]);
|
||||
|
||||
$this->assertArrayHasKey(CONTEXT_COURSECAT, $levels);
|
||||
$this->assertSame(context\coursecat::class, $levels[CONTEXT_COURSECAT]);
|
||||
|
||||
$this->assertArrayHasKey(CONTEXT_COURSE, $levels);
|
||||
$this->assertSame(context\course::class, $levels[CONTEXT_COURSE]);
|
||||
|
||||
$this->assertArrayHasKey(CONTEXT_MODULE, $levels);
|
||||
$this->assertSame(context\module::class, $levels[CONTEXT_MODULE]);
|
||||
|
||||
$this->assertArrayHasKey(CONTEXT_BLOCK, $levels);
|
||||
$this->assertSame(context\block::class, $levels[CONTEXT_BLOCK]);
|
||||
|
||||
$sorted = $levels;
|
||||
ksort($sorted, SORT_NUMERIC);
|
||||
$block = $sorted[CONTEXT_BLOCK];
|
||||
unset($sorted[CONTEXT_BLOCK]);
|
||||
$sorted[CONTEXT_BLOCK] = $block;
|
||||
$this->assertSame(array_keys($sorted), array_keys($levels));
|
||||
|
||||
// Make sure level is set properly.
|
||||
foreach ($levels as $level => $classname) {
|
||||
$this->assertEquals($level, $classname::LEVEL);
|
||||
if ($level != CONTEXT_SYSTEM) {
|
||||
$this->assertGreaterThan(CONTEXT_SYSTEM, $level);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::get_child_levels
|
||||
*/
|
||||
public function test_get_child_levels() {
|
||||
$alllevels = context_helper::get_all_levels();
|
||||
|
||||
$childlevels = context_helper::get_child_levels(CONTEXT_SYSTEM);
|
||||
$this->assertSame(count($alllevels) - 1, count($childlevels));
|
||||
|
||||
$childlevels = context_helper::get_child_levels(CONTEXT_USER);
|
||||
$this->assertNotContains(CONTEXT_SYSTEM, $childlevels);
|
||||
$this->assertNotContains(CONTEXT_USER, $childlevels);
|
||||
$this->assertNotContains(CONTEXT_COURSECAT, $childlevels);
|
||||
$this->assertNotContains(CONTEXT_COURSE, $childlevels);
|
||||
$this->assertNotContains(CONTEXT_MODULE, $childlevels);
|
||||
$this->assertContains(CONTEXT_BLOCK, $childlevels);
|
||||
|
||||
$childlevels = context_helper::get_child_levels(CONTEXT_COURSECAT);
|
||||
$this->assertNotContains(CONTEXT_SYSTEM, $childlevels);
|
||||
$this->assertNotContains(CONTEXT_USER, $childlevels);
|
||||
$this->assertContains(CONTEXT_COURSECAT, $childlevels);
|
||||
$this->assertContains(CONTEXT_COURSE, $childlevels);
|
||||
$this->assertContains(CONTEXT_MODULE, $childlevels);
|
||||
$this->assertContains(CONTEXT_BLOCK, $childlevels);
|
||||
|
||||
$childlevels = context_helper::get_child_levels(CONTEXT_COURSE);
|
||||
$this->assertNotContains(CONTEXT_SYSTEM, $childlevels);
|
||||
$this->assertNotContains(CONTEXT_USER, $childlevels);
|
||||
$this->assertNotContains(CONTEXT_COURSECAT, $childlevels);
|
||||
$this->assertNotContains(CONTEXT_COURSE, $childlevels);
|
||||
$this->assertContains(CONTEXT_MODULE, $childlevels);
|
||||
$this->assertContains(CONTEXT_BLOCK, $childlevels);
|
||||
|
||||
$childlevels = context_helper::get_child_levels(CONTEXT_MODULE);
|
||||
$this->assertNotContains(CONTEXT_SYSTEM, $childlevels);
|
||||
$this->assertNotContains(CONTEXT_USER, $childlevels);
|
||||
$this->assertNotContains(CONTEXT_COURSECAT, $childlevels);
|
||||
$this->assertNotContains(CONTEXT_COURSE, $childlevels);
|
||||
$this->assertNotContains(CONTEXT_MODULE, $childlevels);
|
||||
$this->assertContains(CONTEXT_BLOCK, $childlevels);
|
||||
|
||||
$childlevels = context_helper::get_child_levels(CONTEXT_BLOCK);
|
||||
$this->assertCount(0, $childlevels);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::get_compatible_levels
|
||||
*/
|
||||
public function test_get_compatible_levels() {
|
||||
$levels = context_helper::get_compatible_levels('manager');
|
||||
$this->assertContains(CONTEXT_SYSTEM, $levels);
|
||||
$this->assertNotContains(CONTEXT_USER, $levels);
|
||||
$this->assertContains(CONTEXT_COURSECAT, $levels);
|
||||
$this->assertContains(CONTEXT_COURSE, $levels);
|
||||
$this->assertNotContains(CONTEXT_MODULE, $levels);
|
||||
$this->assertNotContains(CONTEXT_BLOCK, $levels);
|
||||
|
||||
$levels = context_helper::get_compatible_levels('coursecreator');
|
||||
$this->assertContains(CONTEXT_SYSTEM, $levels);
|
||||
$this->assertNotContains(CONTEXT_USER, $levels);
|
||||
$this->assertContains(CONTEXT_COURSECAT, $levels);
|
||||
$this->assertNotContains(CONTEXT_COURSE, $levels);
|
||||
$this->assertNotContains(CONTEXT_MODULE, $levels);
|
||||
$this->assertNotContains(CONTEXT_BLOCK, $levels);
|
||||
|
||||
$levels = context_helper::get_compatible_levels('editingteacher');
|
||||
$this->assertNotContains(CONTEXT_SYSTEM, $levels);
|
||||
$this->assertNotContains(CONTEXT_USER, $levels);
|
||||
$this->assertNotContains(CONTEXT_COURSECAT, $levels);
|
||||
$this->assertContains(CONTEXT_COURSE, $levels);
|
||||
$this->assertContains(CONTEXT_MODULE, $levels);
|
||||
$this->assertNotContains(CONTEXT_BLOCK, $levels);
|
||||
|
||||
$levels = context_helper::get_compatible_levels('teacher');
|
||||
$this->assertNotContains(CONTEXT_SYSTEM, $levels);
|
||||
$this->assertNotContains(CONTEXT_USER, $levels);
|
||||
$this->assertNotContains(CONTEXT_COURSECAT, $levels);
|
||||
$this->assertContains(CONTEXT_COURSE, $levels);
|
||||
$this->assertContains(CONTEXT_MODULE, $levels);
|
||||
$this->assertNotContains(CONTEXT_BLOCK, $levels);
|
||||
|
||||
$levels = context_helper::get_compatible_levels('student');
|
||||
$this->assertNotContains(CONTEXT_SYSTEM, $levels);
|
||||
$this->assertNotContains(CONTEXT_USER, $levels);
|
||||
$this->assertNotContains(CONTEXT_COURSECAT, $levels);
|
||||
$this->assertContains(CONTEXT_COURSE, $levels);
|
||||
$this->assertContains(CONTEXT_MODULE, $levels);
|
||||
$this->assertNotContains(CONTEXT_BLOCK, $levels);
|
||||
|
||||
$levels = context_helper::get_compatible_levels('user');
|
||||
$this->assertCount(0, $levels);
|
||||
|
||||
$levels = context_helper::get_compatible_levels('guest');
|
||||
$this->assertCount(0, $levels);
|
||||
|
||||
$levels = context_helper::get_compatible_levels('frontpage');
|
||||
$this->assertCount(0, $levels);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::cleanup_instances
|
||||
*/
|
||||
public function test_cleanup_instances() {
|
||||
global $DB;
|
||||
|
||||
$this->resetAfterTest();
|
||||
$this->preventResetByRollback();
|
||||
|
||||
$prevcount = $DB->count_records('context', []);
|
||||
context_helper::cleanup_instances();
|
||||
$count = $DB->count_records('context', []);
|
||||
$this->assertSame($prevcount, $count);
|
||||
|
||||
// Insert bogus records for each level and see if they get removed,
|
||||
// more test should be in tests for each context level.
|
||||
$alllevels = context_helper::get_all_levels();
|
||||
foreach ($alllevels as $classname) {
|
||||
if ($classname::LEVEL == CONTEXT_SYSTEM) {
|
||||
continue;
|
||||
}
|
||||
$record = new \stdClass();
|
||||
$record->contextlevel = $classname::LEVEL;
|
||||
$record->instanceid = 9999999999;
|
||||
$record->path = null;
|
||||
$record->depth = '2';
|
||||
$record->id = $DB->insert_record('context', $record);
|
||||
$DB->set_field('context', 'path', SYSCONTEXTID . '/' . $record->id, ['id' => $record->id]);
|
||||
}
|
||||
context_helper::cleanup_instances();
|
||||
$count = $DB->count_records('context', []);
|
||||
$this->assertSame($prevcount, $count);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::create_instances
|
||||
*/
|
||||
public function test_create_instances() {
|
||||
global $DB;
|
||||
|
||||
$this->resetAfterTest();
|
||||
$this->preventResetByRollback();
|
||||
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
$usercontext = context\user::instance($user->id);
|
||||
$category = $this->getDataGenerator()->create_category();
|
||||
$categorycontext = context\coursecat::instance($category->id);
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$coursecontext = context\course::instance($course->id);
|
||||
$page = $this->getDataGenerator()->create_module('page', ['course' => $course->id]);
|
||||
$pagecontext = context\module::instance($page->cmid);
|
||||
|
||||
$prevcount = $DB->count_records('context', []);
|
||||
$DB->delete_records('context', ['id' => $usercontext->id]);
|
||||
$DB->delete_records('context', ['id' => $categorycontext->id]);
|
||||
$DB->delete_records('context', ['id' => $coursecontext->id]);
|
||||
$DB->delete_records('context', ['id' => $pagecontext->id]);
|
||||
|
||||
context_helper::create_instances();
|
||||
$count = $DB->count_records('context', []);
|
||||
$this->assertSame($prevcount, $count);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::build_all_paths
|
||||
*/
|
||||
public function test_build_all_paths() {
|
||||
$this->resetAfterTest();
|
||||
$this->preventResetByRollback();
|
||||
|
||||
// Just make sure there are no fatal errors for now.
|
||||
context_helper::build_all_paths(true);
|
||||
context_helper::build_all_paths();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::reset_caches
|
||||
*/
|
||||
public function test_reset_caches() {
|
||||
$this->resetAfterTest();
|
||||
$this->preventResetByRollback();
|
||||
|
||||
// Just make sure there are no fatal errors for now.
|
||||
context_helper::reset_caches();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::get_preload_record_columns
|
||||
*/
|
||||
public function test_get_preload_record_columns() {
|
||||
$expected = array (
|
||||
'testalias.id' => 'ctxid',
|
||||
'testalias.path' => 'ctxpath',
|
||||
'testalias.depth' => 'ctxdepth',
|
||||
'testalias.contextlevel' => 'ctxlevel',
|
||||
'testalias.instanceid' => 'ctxinstance',
|
||||
'testalias.locked' => 'ctxlocked',
|
||||
);
|
||||
$result = context_helper::get_preload_record_columns('testalias');
|
||||
$this->assertSame($expected, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::get_preload_record_columns_sql
|
||||
*/
|
||||
public function test_get_preload_record_columns_sql() {
|
||||
global $DB;
|
||||
|
||||
$result = context_helper::get_preload_record_columns_sql('testalias');
|
||||
$expected = 'testalias.id AS ctxid, testalias.path AS ctxpath, testalias.depth AS ctxdepth,'
|
||||
.' testalias.contextlevel AS ctxlevel, testalias.instanceid AS ctxinstance, testalias.locked AS ctxlocked';
|
||||
$this->assertSame($expected, $result);
|
||||
|
||||
$sql = "SELECT id, $result
|
||||
FROM {context} testalias";
|
||||
$DB->get_records_sql($sql, []);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::preload_from_record
|
||||
*/
|
||||
public function test_preload_from_record() {
|
||||
global $DB;
|
||||
|
||||
$select = context_helper::get_preload_record_columns_sql('testalias');
|
||||
$sql = "SELECT id, $select
|
||||
FROM {context} testalias";
|
||||
$records = $DB->get_records_sql($sql, []);
|
||||
foreach ($records as $record) {
|
||||
$this->assertNull(context_helper::preload_from_record($record));
|
||||
}
|
||||
|
||||
$this->assertDebuggingNotCalled();
|
||||
$record = reset($records);
|
||||
unset($record->ctxlevel);
|
||||
$this->assertNull(context_helper::preload_from_record($record));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::preload_contexts_by_id
|
||||
*/
|
||||
public function test_preload_contexts_by_id() {
|
||||
global $DB;
|
||||
|
||||
$contextids = $DB->get_fieldset_sql("SELECT id FROM {context}", []);
|
||||
context_helper::preload_contexts_by_id($contextids);
|
||||
context_helper::preload_contexts_by_id($contextids);
|
||||
|
||||
context_helper::reset_caches();
|
||||
context_helper::preload_contexts_by_id($contextids);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::preload_course
|
||||
*/
|
||||
public function test_preload_course() {
|
||||
global $SITE;
|
||||
context_helper::preload_course($SITE->id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::delete_instance
|
||||
*/
|
||||
public function test_delete_instance() {
|
||||
$this->resetAfterTest();
|
||||
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
$category = $this->getDataGenerator()->create_category();
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$page = $this->getDataGenerator()->create_module('page', array('course' => $course->id));
|
||||
|
||||
// This is a bit silly test, it might start failing in the future
|
||||
// because the instances are not deleted before deleting the contexts.
|
||||
context_helper::delete_instance(CONTEXT_USER, $user->id);
|
||||
context_helper::delete_instance(CONTEXT_COURSECAT, $category->id);
|
||||
context_helper::delete_instance(CONTEXT_COURSE, $course->id);
|
||||
context_helper::delete_instance(CONTEXT_MODULE, $page->cmid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::get_level_name
|
||||
*/
|
||||
public function test_get_level_name() {
|
||||
$allevels = context_helper::get_all_levels();
|
||||
foreach ($allevels as $level => $classname) {
|
||||
$name = context_helper::get_level_name($level);
|
||||
$this->assertIsString($name);
|
||||
}
|
||||
|
||||
$this->assertSame('System', context_helper::get_level_name(CONTEXT_SYSTEM));
|
||||
$this->assertSame('User', context_helper::get_level_name(CONTEXT_USER));
|
||||
$this->assertSame('Category', context_helper::get_level_name(CONTEXT_COURSECAT));
|
||||
$this->assertSame('Course', context_helper::get_level_name(CONTEXT_COURSE));
|
||||
$this->assertSame('Activity module', context_helper::get_level_name(CONTEXT_MODULE));
|
||||
$this->assertSame('Block', context_helper::get_level_name(CONTEXT_BLOCK));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::get_navigation_filter_context
|
||||
*/
|
||||
public function test_get_navigation_filter_context() {
|
||||
global $CFG;
|
||||
$this->resetAfterTest();
|
||||
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
$usercontext = context\user::instance($user->id);
|
||||
$category = $this->getDataGenerator()->create_category();
|
||||
$categorycontext = context\coursecat::instance($category->id);
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$coursecontext = context\course::instance($course->id);
|
||||
$page = $this->getDataGenerator()->create_module('page', array('course' => $course->id));
|
||||
$pagecontext = context\module::instance($page->cmid);
|
||||
$systemcontext = context\system::instance();
|
||||
|
||||
// Default is OFF.
|
||||
$this->assertSame('0', $CFG->filternavigationwithsystemcontext);
|
||||
|
||||
// First test passed values are returned if disabled.
|
||||
set_config('filternavigationwithsystemcontext', '0');
|
||||
|
||||
$this->assertNull(context_helper::get_navigation_filter_context(null));
|
||||
|
||||
$filtercontext = context_helper::get_navigation_filter_context($systemcontext);
|
||||
$this->assertSame($systemcontext, $filtercontext);
|
||||
|
||||
$filtercontext = context_helper::get_navigation_filter_context($usercontext);
|
||||
$this->assertSame($usercontext, $filtercontext);
|
||||
|
||||
$filtercontext = context_helper::get_navigation_filter_context($categorycontext);
|
||||
$this->assertSame($categorycontext, $filtercontext);
|
||||
|
||||
$filtercontext = context_helper::get_navigation_filter_context($coursecontext);
|
||||
$this->assertSame($coursecontext, $filtercontext);
|
||||
|
||||
$filtercontext = context_helper::get_navigation_filter_context($pagecontext);
|
||||
$this->assertSame($pagecontext, $filtercontext);
|
||||
|
||||
// Now test that any input returns system context if enabled.
|
||||
set_config('filternavigationwithsystemcontext', '1');
|
||||
|
||||
$filtercontext = context_helper::get_navigation_filter_context(null);
|
||||
$this->assertSame($systemcontext->id, $filtercontext->id);
|
||||
|
||||
$filtercontext = context_helper::get_navigation_filter_context($systemcontext);
|
||||
$this->assertSame($systemcontext->id, $filtercontext->id);
|
||||
|
||||
$filtercontext = context_helper::get_navigation_filter_context($usercontext);
|
||||
$this->assertSame($systemcontext->id, $filtercontext->id);
|
||||
|
||||
$filtercontext = context_helper::get_navigation_filter_context($categorycontext);
|
||||
$this->assertSame($systemcontext->id, $filtercontext->id);
|
||||
|
||||
$filtercontext = context_helper::get_navigation_filter_context($coursecontext);
|
||||
$this->assertSame($systemcontext->id, $filtercontext->id);
|
||||
|
||||
$filtercontext = context_helper::get_navigation_filter_context($pagecontext);
|
||||
$this->assertSame($systemcontext->id, $filtercontext->id);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
<?php
|
||||
// This file is part of Moodle - https://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
namespace core;
|
||||
|
||||
/**
|
||||
* Unit tests for base context class.
|
||||
*
|
||||
* NOTE: more tests are in lib/tests/accesslib_test.php
|
||||
*
|
||||
* @package core
|
||||
* @copyright Petr Skoda
|
||||
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @coversDefaultClass \core\context
|
||||
*/
|
||||
class context_test extends \advanced_testcase {
|
||||
/**
|
||||
* Tests legacy class name.
|
||||
* @coversNothing
|
||||
*/
|
||||
public function test_legacy_classname() {
|
||||
$this->assertSame('core\context', context::class);
|
||||
|
||||
$context = \context_system::instance();
|
||||
$this->assertInstanceOf(context::class, $context);
|
||||
$this->assertInstanceOf('context', $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::instance_by_id
|
||||
*/
|
||||
public function test_factory_methods() {
|
||||
$context = context::instance_by_id(SYSCONTEXTID);
|
||||
$this->assertSame('core\\context\\system', get_class($context));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered methods.
|
||||
* @covers ::__set
|
||||
* @covers ::__unset
|
||||
*/
|
||||
public function test_propery_change_protection() {
|
||||
$context = context\system::instance();
|
||||
|
||||
$context->contextlevel = -10;
|
||||
$this->assertEquals($context::LEVEL, $context->contextlevel);
|
||||
$this->assertDebuggingCalled('Can not change context instance properties!');
|
||||
|
||||
$context->instanceid = -10;
|
||||
$this->assertEquals(0, $context->instanceid);
|
||||
$this->assertDebuggingCalled('Can not change context instance properties!');
|
||||
|
||||
$context->id = -10;
|
||||
$this->assertDebuggingCalled('Can not change context instance properties!');
|
||||
|
||||
$context->locked = -10;
|
||||
$this->assertDebuggingCalled('Can not change context instance properties!');
|
||||
|
||||
unset($context->contextlevel);
|
||||
$this->assertEquals($context::LEVEL, $context->contextlevel);
|
||||
$this->assertDebuggingCalled('Can not unset context instance properties!');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::__get
|
||||
*/
|
||||
public function test_incorrect_property() {
|
||||
$context = context\system::instance();
|
||||
|
||||
$a = $context->whatever;
|
||||
$this->assertDebuggingCalled('Invalid context property accessed! whatever');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests covered method.
|
||||
* @covers ::getIterator
|
||||
*/
|
||||
public function test_iterator() {
|
||||
$context = context\system::instance();
|
||||
$array = iterator_to_array($context->getIterator());
|
||||
$expected = [
|
||||
'id' => $context->id,
|
||||
'contextlevel' => $context->contextlevel,
|
||||
'instanceid' => $context->instanceid,
|
||||
'path' => $context->path,
|
||||
'depth' => $context->depth,
|
||||
'locked' => false,
|
||||
];
|
||||
$this->assertSame($expected, $array);
|
||||
}
|
||||
}
|
||||
@@ -84,10 +84,19 @@ class customcontext_test extends \advanced_testcase {
|
||||
* Bogus custom context class for testing
|
||||
*/
|
||||
class context_bogus1 extends context {
|
||||
/**
|
||||
* Returns context shortname.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_short_name(): string {
|
||||
return 'bogus1';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the most relevant URL for this context.
|
||||
*
|
||||
* @return moodle_url
|
||||
* @return \moodle_url
|
||||
*/
|
||||
public function get_url() {
|
||||
global $ME;
|
||||
@@ -108,10 +117,19 @@ class context_bogus1 extends context {
|
||||
* Bogus custom context class for testing
|
||||
*/
|
||||
class context_bogus2 extends context {
|
||||
/**
|
||||
* Returns context shortname.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_short_name(): string {
|
||||
return 'bogus2';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the most relevant URL for this context.
|
||||
*
|
||||
* @return moodle_url
|
||||
* @return \moodle_url
|
||||
*/
|
||||
public function get_url() {
|
||||
global $ME;
|
||||
@@ -132,10 +150,19 @@ class context_bogus2 extends context {
|
||||
* Bogus custom context class for testing
|
||||
*/
|
||||
class context_bogus3 extends context {
|
||||
/**
|
||||
* Returns context shortname.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_short_name(): string {
|
||||
return 'bogus3';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the most relevant URL for this context.
|
||||
*
|
||||
* @return moodle_url
|
||||
* @return \moodle_url
|
||||
*/
|
||||
public function get_url() {
|
||||
global $ME;
|
||||
|
||||
@@ -3,6 +3,9 @@ information provided here is intended especially for developers.
|
||||
|
||||
=== 4.2 ===
|
||||
|
||||
* All context classes were moved to \core\context namespace while keeping full backwards compatibility.
|
||||
Please note it is not necessary to update plugins unless they rely on exact class names returned from
|
||||
get_class() or context_helper.
|
||||
* Travis CI integration has been removed from core (MDLSITE-7135). Please, use GitHub Actions (GHA) instead. For
|
||||
more information visit https://moodledev.io/general/development/tools/gha
|
||||
* A new constant COMPLETION_COMPLETE_FAIL_HIDDEN is introduced to mark that user has received a failing grade
|
||||
|
||||
Reference in New Issue
Block a user