MDL-74936 core: tidy up context API
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';
|
||||
|
||||
+2
-16
@@ -3549,21 +3549,7 @@ function get_roles_for_contextlevels($contextlevel) {
|
||||
* @return array list of the context levels at which this type of role may be assigned by default.
|
||||
*/
|
||||
function get_default_contextlevels($rolearchetype) {
|
||||
static $defaults = array(
|
||||
'manager' => array(CONTEXT_SYSTEM, CONTEXT_COURSECAT, CONTEXT_COURSE),
|
||||
'coursecreator' => array(CONTEXT_SYSTEM, CONTEXT_COURSECAT),
|
||||
'editingteacher' => array(CONTEXT_COURSE, CONTEXT_MODULE),
|
||||
'teacher' => array(CONTEXT_COURSE, CONTEXT_MODULE),
|
||||
'student' => array(CONTEXT_COURSE, CONTEXT_MODULE),
|
||||
'guest' => array(),
|
||||
'user' => array(),
|
||||
'frontpage' => array());
|
||||
|
||||
if (isset($defaults[$rolearchetype])) {
|
||||
return $defaults[$rolearchetype];
|
||||
} else {
|
||||
return array();
|
||||
}
|
||||
return \context_helper::get_compatible_levels($rolearchetype);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -5024,7 +5010,7 @@ function role_change_permission($roleid, $context, $capname, $permission) {
|
||||
assign_capability($capname, $permission, $roleid, $context->id, true);
|
||||
}
|
||||
|
||||
// ============== DEPRECATED FUNCTIONS ==========================================
|
||||
/* ============== DEPRECATED FUNCTIONS ========================================== */
|
||||
// Old context related functions were deprecated in 2.0, it is recommended
|
||||
// to use context classes in new code. Old function can be used when
|
||||
// creating patches that are supposed to be backported to older stable branches.
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+161
-115
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
// 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
|
||||
@@ -12,13 +12,12 @@
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
// along with Moodle. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
namespace core;
|
||||
|
||||
use stdClass, IteratorAggregate, ArrayIterator;
|
||||
use coding_exception, moodle_url;
|
||||
use core\context\system;
|
||||
|
||||
/**
|
||||
* Basic moodle context abstraction class.
|
||||
@@ -33,7 +32,7 @@ use core\context\system;
|
||||
* @package core_access
|
||||
* @category access
|
||||
* @copyright Petr Skoda
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @since Moodle 4.2
|
||||
*
|
||||
* @property-read int $id context id
|
||||
@@ -41,6 +40,7 @@ use core\context\system;
|
||||
* @property-read int $instanceid id of related instance in each context
|
||||
* @property-read string $path path to context, starts with system context
|
||||
* @property-read int $depth
|
||||
* @property-read bool $locked true means write capabilities are ignored in this context or parents
|
||||
*/
|
||||
abstract class context extends stdClass implements IteratorAggregate {
|
||||
|
||||
@@ -99,34 +99,46 @@ abstract class context extends stdClass implements IteratorAggregate {
|
||||
/**
|
||||
* @var array Context caching info
|
||||
*/
|
||||
private static $cache_contexts = array();
|
||||
private static $cache_contexts = array();
|
||||
|
||||
/**
|
||||
* Context count
|
||||
* Why do we do count contexts? Because count($array) is horribly slow for large arrays
|
||||
* @var int
|
||||
*/
|
||||
protected static $cache_count = 0;
|
||||
protected static $cache_count = 0;
|
||||
|
||||
/**
|
||||
* @var array Context caching info
|
||||
*/
|
||||
protected static $cache_preloaded = array();
|
||||
protected static $cache_preloaded = array();
|
||||
|
||||
/**
|
||||
* @var system The system context once initialised
|
||||
* @var context\system The system context once initialised
|
||||
*/
|
||||
protected static $systemcontext = null;
|
||||
protected static $systemcontext = null;
|
||||
|
||||
/**
|
||||
* Returns short context name.
|
||||
*
|
||||
* @since Moodle 4.2
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_short_name(): string {
|
||||
// NOTE: it would be more correct to make this abstract,
|
||||
// unfortunately there are tests that attempt to mock context classes.
|
||||
throw new \coding_exception('get_short_name() method must be overridden in custom context levels');
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the cache to remove all data.
|
||||
* @static
|
||||
*/
|
||||
protected static function reset_caches() {
|
||||
self::$cache_contextsbyid = array();
|
||||
self::$cache_contexts = array();
|
||||
self::$cache_count = 0;
|
||||
self::$cache_preloaded = array();
|
||||
self::$cache_contexts = array();
|
||||
self::$cache_count = 0;
|
||||
self::$cache_preloaded = array();
|
||||
|
||||
self::$systemcontext = null;
|
||||
}
|
||||
@@ -135,13 +147,12 @@ abstract class context extends stdClass implements IteratorAggregate {
|
||||
* Adds a context to the cache. If the cache is full, discards a batch of
|
||||
* older entries.
|
||||
*
|
||||
* @static
|
||||
* @param context $context New context to add
|
||||
* @return void
|
||||
*/
|
||||
protected static function cache_add(context $context) {
|
||||
if (isset(self::$cache_contextsbyid[$context->id])) {
|
||||
// already cached, no need to do anything - this is relatively cheap, we do all this because count() is slow
|
||||
// Already cached, no need to do anything - this is relatively cheap, we do all this because count() is slow.
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -150,11 +161,11 @@ abstract class context extends stdClass implements IteratorAggregate {
|
||||
foreach (self::$cache_contextsbyid as $ctx) {
|
||||
$i++;
|
||||
if ($i <= 100) {
|
||||
// we want to keep the first contexts to be loaded on this page, hopefully they will be needed again later
|
||||
// We want to keep the first contexts to be loaded on this page, hopefully they will be needed again later.
|
||||
continue;
|
||||
}
|
||||
if ($i > (CONTEXT_CACHE_MAX_SIZE / 3)) {
|
||||
// we remove oldest third of the contexts to make room for more contexts
|
||||
// We remove oldest third of the contexts to make room for more contexts.
|
||||
break;
|
||||
}
|
||||
unset(self::$cache_contextsbyid[$ctx->id]);
|
||||
@@ -171,13 +182,12 @@ abstract class context extends stdClass implements IteratorAggregate {
|
||||
/**
|
||||
* Removes a context from the cache.
|
||||
*
|
||||
* @static
|
||||
* @param context $context Context object to remove
|
||||
* @return void
|
||||
*/
|
||||
protected static function cache_remove(context $context) {
|
||||
if (!isset(self::$cache_contextsbyid[$context->id])) {
|
||||
// not cached, no need to do anything - this is relatively cheap, we do all this because count() is slow
|
||||
// Not cached, no need to do anything - this is relatively cheap, we do all this because count() is slow.
|
||||
return;
|
||||
}
|
||||
unset(self::$cache_contexts[$context->contextlevel][$context->instanceid]);
|
||||
@@ -193,7 +203,6 @@ abstract class context extends stdClass implements IteratorAggregate {
|
||||
/**
|
||||
* Gets a context from the cache.
|
||||
*
|
||||
* @static
|
||||
* @param int $contextlevel Context level
|
||||
* @param int $instance Instance ID
|
||||
* @return context|bool Context or false if not in cache
|
||||
@@ -208,7 +217,6 @@ abstract class context extends stdClass implements IteratorAggregate {
|
||||
/**
|
||||
* Gets a context from the cache based on its id.
|
||||
*
|
||||
* @static
|
||||
* @param int $id Context ID
|
||||
* @return context|bool Context or false if not in cache
|
||||
*/
|
||||
@@ -222,7 +230,6 @@ abstract class context extends stdClass implements IteratorAggregate {
|
||||
/**
|
||||
* Preloads context information from db record and strips the cached info.
|
||||
*
|
||||
* @static
|
||||
* @param stdClass $rec
|
||||
* @return context|null (modifies $rec)
|
||||
*/
|
||||
@@ -243,12 +250,12 @@ abstract class context extends stdClass implements IteratorAggregate {
|
||||
}
|
||||
|
||||
$record = (object) [
|
||||
'id' => $rec->ctxid,
|
||||
'contextlevel' => $rec->ctxlevel,
|
||||
'instanceid' => $rec->ctxinstance,
|
||||
'path' => $rec->ctxpath,
|
||||
'depth' => $rec->ctxdepth,
|
||||
'locked' => $rec->ctxlocked,
|
||||
'id' => $rec->ctxid,
|
||||
'contextlevel' => $rec->ctxlevel,
|
||||
'instanceid' => $rec->ctxinstance,
|
||||
'path' => $rec->ctxpath,
|
||||
'depth' => $rec->ctxdepth,
|
||||
'locked' => $rec->ctxlocked,
|
||||
];
|
||||
|
||||
unset($rec->ctxid);
|
||||
@@ -258,11 +265,11 @@ abstract class context extends stdClass implements IteratorAggregate {
|
||||
unset($rec->ctxdepth);
|
||||
unset($rec->ctxlocked);
|
||||
|
||||
return context::create_instance_from_record($record);
|
||||
return self::create_instance_from_record($record);
|
||||
}
|
||||
|
||||
|
||||
// ====== magic methods =======
|
||||
/* ====== magic methods ======= */
|
||||
|
||||
/**
|
||||
* Magic setter method, we do not want anybody to modify properties from the outside
|
||||
@@ -332,7 +339,7 @@ abstract class context extends stdClass implements IteratorAggregate {
|
||||
debugging('Can not unset context instance properties!');
|
||||
}
|
||||
|
||||
// ====== implementing method from interface IteratorAggregate ======
|
||||
/* ====== implementing method from interface IteratorAggregate ====== */
|
||||
|
||||
/**
|
||||
* Create an iterator because magic vars can't be seen by 'foreach'.
|
||||
@@ -342,17 +349,17 @@ abstract class context extends stdClass implements IteratorAggregate {
|
||||
*/
|
||||
public function getIterator(): \Traversable {
|
||||
$ret = array(
|
||||
'id' => $this->id,
|
||||
'id' => $this->id,
|
||||
'contextlevel' => $this->contextlevel,
|
||||
'instanceid' => $this->instanceid,
|
||||
'path' => $this->path,
|
||||
'depth' => $this->depth,
|
||||
'locked' => $this->locked,
|
||||
'instanceid' => $this->instanceid,
|
||||
'path' => $this->path,
|
||||
'depth' => $this->depth,
|
||||
'locked' => $this->locked,
|
||||
);
|
||||
return new ArrayIterator($ret);
|
||||
}
|
||||
|
||||
// ====== general context methods ======
|
||||
/* ====== general context methods ====== */
|
||||
|
||||
/**
|
||||
* Constructor is protected so that devs are forced to
|
||||
@@ -361,11 +368,11 @@ abstract class context extends stdClass implements IteratorAggregate {
|
||||
* @param stdClass $record
|
||||
*/
|
||||
protected function __construct(stdClass $record) {
|
||||
$this->_id = (int)$record->id;
|
||||
$this->_id = (int)$record->id;
|
||||
$this->_contextlevel = (int)$record->contextlevel;
|
||||
$this->_instanceid = $record->instanceid;
|
||||
$this->_path = $record->path;
|
||||
$this->_depth = $record->depth;
|
||||
$this->_instanceid = $record->instanceid;
|
||||
$this->_path = $record->path;
|
||||
$this->_depth = $record->depth;
|
||||
|
||||
if (isset($record->locked)) {
|
||||
$this->_locked = $record->locked;
|
||||
@@ -376,19 +383,19 @@ abstract class context extends stdClass implements IteratorAggregate {
|
||||
|
||||
/**
|
||||
* This function is also used to work around 'protected' keyword problems in context_helper.
|
||||
* @static
|
||||
*
|
||||
* @param stdClass $record
|
||||
* @return context instance
|
||||
*/
|
||||
protected static function create_instance_from_record(stdClass $record) {
|
||||
$classname = context_helper::get_class_for_level($record->contextlevel);
|
||||
|
||||
if ($context = context::cache_get_by_id($record->id)) {
|
||||
if ($context = self::cache_get_by_id($record->id)) {
|
||||
return $context;
|
||||
}
|
||||
|
||||
$context = new $classname($record);
|
||||
context::cache_add($context);
|
||||
self::cache_add($context);
|
||||
|
||||
return $context;
|
||||
}
|
||||
@@ -396,7 +403,6 @@ abstract class context extends stdClass implements IteratorAggregate {
|
||||
/**
|
||||
* Copy prepared new contexts from temp table to context table,
|
||||
* we do this in db specific way for perf reasons only.
|
||||
* @static
|
||||
*/
|
||||
protected static function merge_context_temp_table() {
|
||||
global $DB;
|
||||
@@ -413,9 +419,9 @@ abstract class context extends stdClass implements IteratorAggregate {
|
||||
$dbfamily = $DB->get_dbfamily();
|
||||
if ($dbfamily == 'mysql') {
|
||||
$updatesql = "UPDATE {context} ct, {context_temp} temp
|
||||
SET ct.path = temp.path,
|
||||
ct.depth = temp.depth,
|
||||
ct.locked = temp.locked
|
||||
SET ct.path = temp.path,
|
||||
ct.depth = temp.depth,
|
||||
ct.locked = temp.locked
|
||||
WHERE ct.id = temp.id";
|
||||
} else if ($dbfamily == 'oracle') {
|
||||
$updatesql = "UPDATE {context} ct
|
||||
@@ -426,19 +432,19 @@ abstract class context extends stdClass implements IteratorAggregate {
|
||||
WHERE EXISTS (SELECT 'x'
|
||||
FROM {context_temp} temp
|
||||
WHERE temp.id = ct.id)";
|
||||
} else if ($dbfamily == 'postgres' or $dbfamily == 'mssql') {
|
||||
} else if ($dbfamily == 'postgres' || $dbfamily == 'mssql') {
|
||||
$updatesql = "UPDATE {context}
|
||||
SET path = temp.path,
|
||||
depth = temp.depth,
|
||||
locked = temp.locked
|
||||
SET path = temp.path,
|
||||
depth = temp.depth,
|
||||
locked = temp.locked
|
||||
FROM {context_temp} temp
|
||||
WHERE temp.id={context}.id";
|
||||
} else {
|
||||
// sqlite and others
|
||||
// Sqlite and others.
|
||||
$updatesql = "UPDATE {context}
|
||||
SET path = (SELECT path FROM {context_temp} WHERE id = {context}.id),
|
||||
depth = (SELECT depth FROM {context_temp} WHERE id = {context}.id),
|
||||
locked = (SELECT locked FROM {context_temp} WHERE id = {context}.id)
|
||||
SET path = (SELECT path FROM {context_temp} WHERE id = {context}.id),
|
||||
depth = (SELECT depth FROM {context_temp} WHERE id = {context}.id),
|
||||
locked = (SELECT locked FROM {context_temp} WHERE id = {context}.id)
|
||||
WHERE id IN (SELECT id FROM {context_temp})";
|
||||
}
|
||||
|
||||
@@ -448,7 +454,6 @@ abstract class context extends stdClass implements IteratorAggregate {
|
||||
/**
|
||||
* Get a context instance as an object, from a given context id.
|
||||
*
|
||||
* @static
|
||||
* @param int $id context id
|
||||
* @param int $strictness IGNORE_MISSING means compatible mode, false returned if record not found, debug message if more found;
|
||||
* MUST_EXIST means throw exception if no record found
|
||||
@@ -457,25 +462,25 @@ abstract class context extends stdClass implements IteratorAggregate {
|
||||
public static function instance_by_id($id, $strictness = MUST_EXIST) {
|
||||
global $DB;
|
||||
|
||||
if (get_called_class() !== 'core\context' and get_called_class() !== 'core\context_helper') {
|
||||
// some devs might confuse context->id and instanceid, better prevent these mistakes completely
|
||||
if (get_called_class() !== 'core\context' && get_called_class() !== 'core\context_helper') {
|
||||
// Some devs might confuse context->id and instanceid, better prevent these mistakes completely.
|
||||
throw new coding_exception('use only context::instance_by_id() for real context levels use ::instance() methods');
|
||||
}
|
||||
|
||||
if ($id == SYSCONTEXTID) {
|
||||
return system::instance(0, $strictness);
|
||||
return context\system::instance(0, $strictness);
|
||||
}
|
||||
|
||||
if (is_array($id) or is_object($id) or empty($id)) {
|
||||
if (is_array($id) || is_object($id) || empty($id)) {
|
||||
throw new coding_exception('Invalid context id specified context::instance_by_id()');
|
||||
}
|
||||
|
||||
if ($context = context::cache_get_by_id($id)) {
|
||||
if ($context = self::cache_get_by_id($id)) {
|
||||
return $context;
|
||||
}
|
||||
|
||||
if ($record = $DB->get_record('context', array('id'=>$id), '*', $strictness)) {
|
||||
return context::create_instance_from_record($record);
|
||||
if ($record = $DB->get_record('context', array('id' => $id), '*', $strictness)) {
|
||||
return self::create_instance_from_record($record);
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -491,12 +496,12 @@ abstract class context extends stdClass implements IteratorAggregate {
|
||||
global $DB;
|
||||
|
||||
$frompath = $this->_path;
|
||||
$newpath = $newparent->path . '/' . $this->_id;
|
||||
$newpath = $newparent->path . '/' . $this->_id;
|
||||
|
||||
$trans = $DB->start_delegated_transaction();
|
||||
|
||||
$setdepth = '';
|
||||
if (($newparent->depth +1) != $this->_depth) {
|
||||
if (($newparent->depth + 1) != $this->_depth) {
|
||||
$diff = $newparent->depth - $this->_depth + 1;
|
||||
$setdepth = ", depth = depth + $diff";
|
||||
}
|
||||
@@ -507,11 +512,11 @@ abstract class context extends stdClass implements IteratorAggregate {
|
||||
$params = array($newpath, $this->_id);
|
||||
$DB->execute($sql, $params);
|
||||
|
||||
$this->_path = $newpath;
|
||||
$this->_path = $newpath;
|
||||
$this->_depth = $newparent->depth + 1;
|
||||
|
||||
$sql = "UPDATE {context}
|
||||
SET path = ".$DB->sql_concat("?", $DB->sql_substr("path", strlen($frompath)+1))."
|
||||
SET path = ".$DB->sql_concat("?", $DB->sql_substr("path", strlen($frompath) + 1))."
|
||||
$setdepth
|
||||
WHERE path LIKE ?";
|
||||
$params = array($newpath, "{$frompath}/%");
|
||||
@@ -519,7 +524,7 @@ abstract class context extends stdClass implements IteratorAggregate {
|
||||
|
||||
$this->mark_dirty();
|
||||
|
||||
context::reset_caches();
|
||||
self::reset_caches();
|
||||
|
||||
$trans->allow_commit();
|
||||
}
|
||||
@@ -567,10 +572,10 @@ abstract class context extends stdClass implements IteratorAggregate {
|
||||
$this->mark_dirty();
|
||||
}
|
||||
$DB->set_field_select('context', 'depth', 0, "path LIKE '%/$this->_id/%'");
|
||||
$DB->set_field_select('context', 'path', NULL, "path LIKE '%/$this->_id/%'");
|
||||
$DB->set_field_select('context', 'path', null, "path LIKE '%/$this->_id/%'");
|
||||
if ($this->_contextlevel != CONTEXT_SYSTEM) {
|
||||
$DB->set_field('context', 'depth', 0, array('id'=>$this->_id));
|
||||
$DB->set_field('context', 'path', NULL, array('id'=>$this->_id));
|
||||
$DB->set_field('context', 'depth', 0, array('id' => $this->_id));
|
||||
$DB->set_field('context', 'path', null, array('id' => $this->_id));
|
||||
$this->_depth = 0;
|
||||
$this->_path = null;
|
||||
}
|
||||
@@ -579,7 +584,7 @@ abstract class context extends stdClass implements IteratorAggregate {
|
||||
context_helper::build_all_paths(false);
|
||||
}
|
||||
|
||||
context::reset_caches();
|
||||
self::reset_caches();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -592,7 +597,7 @@ abstract class context extends stdClass implements IteratorAggregate {
|
||||
filter_delete_all_for_context($this->_id);
|
||||
|
||||
require_once($CFG->dirroot . '/comment/lib.php');
|
||||
\comment::delete_comments(array('contextid'=>$this->_id));
|
||||
\comment::delete_comments(array('contextid' => $this->_id));
|
||||
|
||||
require_once($CFG->dirroot.'/rating/lib.php');
|
||||
$delopt = new stdclass();
|
||||
@@ -600,7 +605,7 @@ abstract class context extends stdClass implements IteratorAggregate {
|
||||
$rm = new \rating_manager();
|
||||
$rm->delete_ratings($delopt);
|
||||
|
||||
// delete all files attached to this context
|
||||
// Delete all files attached to this context.
|
||||
$fs = get_file_storage();
|
||||
$fs->delete_area_files($this->_id);
|
||||
|
||||
@@ -608,14 +613,14 @@ abstract class context extends stdClass implements IteratorAggregate {
|
||||
require_once($CFG->dirroot . '/repository/lib.php');
|
||||
\repository::delete_all_for_context($this->_id);
|
||||
|
||||
// delete all advanced grading data attached to this context
|
||||
// Delete all advanced grading data attached to this context.
|
||||
require_once($CFG->dirroot.'/grade/grading/lib.php');
|
||||
\grading_manager::delete_all_for_context($this->_id);
|
||||
|
||||
// now delete stuff from role related tables, role_unassign_all
|
||||
// and unenrol should be called earlier to do proper cleanup
|
||||
$DB->delete_records('role_assignments', array('contextid'=>$this->_id));
|
||||
$DB->delete_records('role_names', array('contextid'=>$this->_id));
|
||||
// Now delete stuff from role related tables, role_unassign_all
|
||||
// and unenrol should be called earlier to do proper cleanup.
|
||||
$DB->delete_records('role_assignments', array('contextid' => $this->_id));
|
||||
$DB->delete_records('role_names', array('contextid' => $this->_id));
|
||||
$this->delete_capabilities();
|
||||
}
|
||||
|
||||
@@ -644,27 +649,26 @@ abstract class context extends stdClass implements IteratorAggregate {
|
||||
throw new coding_exception('Cannot delete system context');
|
||||
}
|
||||
|
||||
// double check the context still exists
|
||||
if (!$DB->record_exists('context', array('id'=>$this->_id))) {
|
||||
context::cache_remove($this);
|
||||
// Double check the context still exists.
|
||||
if (!$DB->record_exists('context', array('id' => $this->_id))) {
|
||||
self::cache_remove($this);
|
||||
return;
|
||||
}
|
||||
|
||||
$this->delete_content();
|
||||
$DB->delete_records('context', array('id'=>$this->_id));
|
||||
// purge static context cache if entry present
|
||||
context::cache_remove($this);
|
||||
$DB->delete_records('context', array('id' => $this->_id));
|
||||
// Purge static context cache if entry present.
|
||||
self::cache_remove($this);
|
||||
|
||||
// Inform search engine to delete data related to this context.
|
||||
\core_search\manager::context_deleted($this);
|
||||
}
|
||||
|
||||
// ====== context level related methods ======
|
||||
/* ====== context level related methods ====== */
|
||||
|
||||
/**
|
||||
* Utility method for context creation
|
||||
*
|
||||
* @static
|
||||
* @param int $contextlevel
|
||||
* @param int $instanceid
|
||||
* @param string $parentpath
|
||||
@@ -675,14 +679,14 @@ abstract class context extends stdClass implements IteratorAggregate {
|
||||
|
||||
$record = new stdClass();
|
||||
$record->contextlevel = $contextlevel;
|
||||
$record->instanceid = $instanceid;
|
||||
$record->depth = 0;
|
||||
$record->path = null; //not known before insert
|
||||
$record->locked = 0;
|
||||
$record->instanceid = $instanceid;
|
||||
$record->depth = 0;
|
||||
$record->path = null; // Not known before insert.
|
||||
$record->locked = 0;
|
||||
|
||||
$record->id = $DB->insert_record('context', $record);
|
||||
|
||||
// now add path if known - it can be added later
|
||||
// Now add path if known - it can be added later.
|
||||
if (!is_null($parentpath)) {
|
||||
$record->path = $parentpath.'/'.$record->id;
|
||||
$record->depth = substr_count($record->path, '/');
|
||||
@@ -704,7 +708,7 @@ abstract class context extends stdClass implements IteratorAggregate {
|
||||
* @return string the human readable context name.
|
||||
*/
|
||||
public function get_context_name($withprefix = true, $short = false, $escape = true) {
|
||||
// must be implemented in all context levels
|
||||
// Must be implemented in all context levels.
|
||||
throw new coding_exception('can not get name of abstract context');
|
||||
}
|
||||
|
||||
@@ -730,7 +734,50 @@ abstract class context extends stdClass implements IteratorAggregate {
|
||||
*
|
||||
* @return moodle_url
|
||||
*/
|
||||
public abstract function get_url();
|
||||
abstract public function get_url();
|
||||
|
||||
/**
|
||||
* Returns context instance database name.
|
||||
*
|
||||
* @return string|null table name for all levels except system.
|
||||
*/
|
||||
protected static function get_instance_table(): ?string {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of all role archetypes that are compatible
|
||||
* with role assignments in context level.
|
||||
* @since Moodle 4.2
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
protected static function get_compatible_role_archetypes(): array {
|
||||
// Override if archetype roles should be allowed to be assigned in context level.
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of all possible parent context levels,
|
||||
* it may include itself if nesting is allowed.
|
||||
* @since Moodle 4.2
|
||||
*
|
||||
* @return int[]
|
||||
*/
|
||||
public static function get_possible_parent_levels(): array {
|
||||
// Override if other type of parents are expected.
|
||||
return [context\system::LEVEL];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns array of relevant context capability records.
|
||||
@@ -738,7 +785,7 @@ abstract class context extends stdClass implements IteratorAggregate {
|
||||
* @param string $sort SQL order by snippet for sorting returned capabilities sensibly for display
|
||||
* @return array
|
||||
*/
|
||||
public abstract function get_capabilities(string $sort = self::DEFAULT_CAPABILITY_SORT);
|
||||
abstract public function get_capabilities(string $sort = self::DEFAULT_CAPABILITY_SORT);
|
||||
|
||||
/**
|
||||
* Recursive function which, given a context, find all its children context ids.
|
||||
@@ -758,7 +805,7 @@ abstract class context extends stdClass implements IteratorAggregate {
|
||||
public function get_child_contexts() {
|
||||
global $DB;
|
||||
|
||||
if (empty($this->_path) or empty($this->_depth)) {
|
||||
if (empty($this->_path) || empty($this->_depth)) {
|
||||
debugging('Can not find child contexts of context '.$this->_id.' try rebuilding of context paths');
|
||||
return array();
|
||||
}
|
||||
@@ -771,7 +818,7 @@ abstract class context extends stdClass implements IteratorAggregate {
|
||||
|
||||
$result = array();
|
||||
foreach ($records as $record) {
|
||||
$result[$record->id] = context::create_instance_from_record($record);
|
||||
$result[$record->id] = self::create_instance_from_record($record);
|
||||
}
|
||||
|
||||
return $result;
|
||||
@@ -818,7 +865,8 @@ abstract class context extends stdClass implements IteratorAggregate {
|
||||
|
||||
$result = array();
|
||||
foreach ($contextids as $contextid) {
|
||||
$parent = context::instance_by_id($contextid, MUST_EXIST);
|
||||
// Do NOT change this to self!
|
||||
$parent = context_helper::instance_by_id($contextid, MUST_EXIST);
|
||||
$result[$parent->id] = $parent;
|
||||
}
|
||||
|
||||
@@ -861,10 +909,10 @@ abstract class context extends stdClass implements IteratorAggregate {
|
||||
return array();
|
||||
}
|
||||
|
||||
$parentcontexts = trim($this->_path, '/'); // kill leading slash
|
||||
$parentcontexts = trim($this->_path, '/'); // Kill leading slash.
|
||||
$parentcontexts = explode('/', $parentcontexts);
|
||||
if (!$includeself) {
|
||||
array_pop($parentcontexts); // and remove its own id
|
||||
array_pop($parentcontexts); // And remove its own id.
|
||||
}
|
||||
|
||||
return array_reverse($parentcontexts);
|
||||
@@ -905,16 +953,17 @@ abstract class context extends stdClass implements IteratorAggregate {
|
||||
* @return context|false
|
||||
*/
|
||||
public function get_parent_context() {
|
||||
if (empty($this->_path) or $this->_id == SYSCONTEXTID) {
|
||||
if (empty($this->_path) || $this->_id == SYSCONTEXTID) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$parentcontexts = trim($this->_path, '/'); // kill leading slash
|
||||
$parentcontexts = trim($this->_path, '/'); // Kill leading slash.
|
||||
$parentcontexts = explode('/', $parentcontexts);
|
||||
array_pop($parentcontexts); // self
|
||||
$contextid = array_pop($parentcontexts); // immediate parent
|
||||
array_pop($parentcontexts); // Self.
|
||||
$contextid = array_pop($parentcontexts); // Immediate parent.
|
||||
|
||||
return context::instance_by_id($contextid, MUST_EXIST);
|
||||
// Do NOT change this to self!
|
||||
return context_helper::instance_by_id($contextid, MUST_EXIST);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -934,7 +983,6 @@ abstract class context extends stdClass implements IteratorAggregate {
|
||||
/**
|
||||
* Returns sql necessary for purging of stale context instances.
|
||||
*
|
||||
* @static
|
||||
* @return string cleanup SQL
|
||||
*/
|
||||
protected static function get_cleanup_sql() {
|
||||
@@ -944,7 +992,6 @@ abstract class context extends stdClass implements IteratorAggregate {
|
||||
/**
|
||||
* Rebuild context paths and depths at context level.
|
||||
*
|
||||
* @static
|
||||
* @param bool $force
|
||||
* @return void
|
||||
*/
|
||||
@@ -955,7 +1002,6 @@ abstract class context extends stdClass implements IteratorAggregate {
|
||||
/**
|
||||
* Create missing context instances at given level
|
||||
*
|
||||
* @static
|
||||
* @return void
|
||||
*/
|
||||
protected static function create_level_instances() {
|
||||
@@ -969,10 +1015,10 @@ abstract class context extends stdClass implements IteratorAggregate {
|
||||
public function reload_if_dirty() {
|
||||
global $ACCESSLIB_PRIVATE, $USER;
|
||||
|
||||
// Load dirty contexts list if needed
|
||||
// Load dirty contexts list if needed.
|
||||
if (CLI_SCRIPT) {
|
||||
if (!isset($ACCESSLIB_PRIVATE->dirtycontexts)) {
|
||||
// we do not load dirty flags in CLI and cron
|
||||
// We do not load dirty flags in CLI and cron.
|
||||
$ACCESSLIB_PRIVATE->dirtycontexts = array();
|
||||
}
|
||||
} else {
|
||||
@@ -1025,9 +1071,9 @@ abstract class context extends stdClass implements IteratorAggregate {
|
||||
return;
|
||||
}
|
||||
|
||||
// only if it is a non-empty string
|
||||
// Only if it is a non-empty string.
|
||||
if (is_string($this->_path) && $this->_path !== '') {
|
||||
set_cache_flag('accesslib/dirtycontexts', $this->_path, 1, time()+$CFG->sessiontimeout);
|
||||
set_cache_flag('accesslib/dirtycontexts', $this->_path, 1, time() + $CFG->sessiontimeout);
|
||||
if (isset($ACCESSLIB_PRIVATE->dirtycontexts)) {
|
||||
$ACCESSLIB_PRIVATE->dirtycontexts[$this->_path] = 1;
|
||||
} else {
|
||||
@@ -1035,11 +1081,11 @@ abstract class context extends stdClass implements IteratorAggregate {
|
||||
$ACCESSLIB_PRIVATE->dirtycontexts = array($this->_path => 1);
|
||||
} else {
|
||||
if (isset($USER->access['time'])) {
|
||||
$ACCESSLIB_PRIVATE->dirtycontexts = get_cache_flags('accesslib/dirtycontexts', $USER->access['time']-2);
|
||||
$ACCESSLIB_PRIVATE->dirtycontexts = get_cache_flags('accesslib/dirtycontexts', $USER->access['time'] - 2);
|
||||
} else {
|
||||
$ACCESSLIB_PRIVATE->dirtycontexts = array($this->_path => 1);
|
||||
}
|
||||
// flags not loaded yet, it will be done later in $context->reload_if_dirty()
|
||||
// Flags not loaded yet, it will be done later in $context->reload_if_dirty().
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
// 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
|
||||
@@ -12,7 +12,7 @@
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
// along with Moodle. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
namespace core\context;
|
||||
|
||||
@@ -26,27 +26,40 @@ use coding_exception, moodle_url;
|
||||
* @package core_access
|
||||
* @category access
|
||||
* @copyright Petr Skoda
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @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 context_block::instance($blockinstanceid) if you need the instance of context.
|
||||
* Alternatively if you know only the context id use context::instance_by_id($contextid)
|
||||
* 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 != CONTEXT_BLOCK) {
|
||||
throw new coding_exception('Invalid $record->contextlevel in context_block constructor');
|
||||
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.
|
||||
*
|
||||
* @static
|
||||
* @return string the human readable context level name.
|
||||
*/
|
||||
public static function get_level_name() {
|
||||
@@ -65,13 +78,13 @@ class block extends context {
|
||||
global $DB, $CFG;
|
||||
|
||||
$name = '';
|
||||
if ($blockinstance = $DB->get_record('block_instances', array('id'=>$this->_instanceid))) {
|
||||
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){
|
||||
if ($withprefix) {
|
||||
$name = get_string('block').': ';
|
||||
}
|
||||
$name .= $blockobject->title;
|
||||
@@ -91,6 +104,19 @@ class block extends 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.
|
||||
*
|
||||
@@ -104,7 +130,7 @@ class block extends context {
|
||||
|
||||
$select = '(contextlevel = :level AND component = :component)';
|
||||
$params = [
|
||||
'level' => CONTEXT_BLOCK,
|
||||
'level' => self::LEVEL,
|
||||
'component' => 'block_' . $bi->blockname,
|
||||
];
|
||||
|
||||
@@ -132,7 +158,6 @@ class block extends context {
|
||||
/**
|
||||
* Returns block context instance.
|
||||
*
|
||||
* @static
|
||||
* @param int $blockinstanceid id from {block_instances} table.
|
||||
* @param int $strictness
|
||||
* @return block|false context instance
|
||||
@@ -140,14 +165,14 @@ class block extends context {
|
||||
public static function instance($blockinstanceid, $strictness = MUST_EXIST) {
|
||||
global $DB;
|
||||
|
||||
if ($context = context::cache_get(CONTEXT_BLOCK, $blockinstanceid)) {
|
||||
if ($context = context::cache_get(self::LEVEL, $blockinstanceid)) {
|
||||
return $context;
|
||||
}
|
||||
|
||||
if (!$record = $DB->get_record('context', array('contextlevel' => CONTEXT_BLOCK, 'instanceid' => $blockinstanceid))) {
|
||||
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(CONTEXT_BLOCK, $bi->id, $parentcontext->path);
|
||||
$record = context::insert_context_record(self::LEVEL, $bi->id, $parentcontext->path);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,7 +195,6 @@ class block extends context {
|
||||
|
||||
/**
|
||||
* Create missing context instances at block context level
|
||||
* @static
|
||||
*/
|
||||
protected static function create_level_instances() {
|
||||
global $DB;
|
||||
@@ -189,15 +213,14 @@ class block extends context {
|
||||
EOF;
|
||||
|
||||
$DB->execute($sql, [
|
||||
'contextlevel' => CONTEXT_BLOCK,
|
||||
'existingcontextlevel' => CONTEXT_BLOCK,
|
||||
'contextlevel' => self::LEVEL,
|
||||
'existingcontextlevel' => self::LEVEL,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns sql necessary for purging of stale context instances.
|
||||
*
|
||||
* @static
|
||||
* @return string cleanup SQL
|
||||
*/
|
||||
protected static function get_cleanup_sql() {
|
||||
@@ -205,7 +228,7 @@ class block extends context {
|
||||
SELECT c.*
|
||||
FROM {context} c
|
||||
LEFT OUTER JOIN {block_instances} bi ON c.instanceid = bi.id
|
||||
WHERE bi.id IS NULL AND c.contextlevel = ".CONTEXT_BLOCK."
|
||||
WHERE bi.id IS NULL AND c.contextlevel = ".self::LEVEL."
|
||||
";
|
||||
|
||||
return $sql;
|
||||
@@ -214,24 +237,23 @@ class block extends context {
|
||||
/**
|
||||
* Rebuild context paths and depths at block context level.
|
||||
*
|
||||
* @static
|
||||
* @param bool $force
|
||||
*/
|
||||
protected static function build_paths($force) {
|
||||
global $DB;
|
||||
|
||||
if ($force or $DB->record_exists_select('context', "contextlevel = ".CONTEXT_BLOCK." AND (depth = 0 OR path IS NULL)")) {
|
||||
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)";
|
||||
}
|
||||
|
||||
// pctx.path IS NOT NULL prevents fatal problems with broken block instances that point to invalid context parent
|
||||
// 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 = ".CONTEXT_BLOCK.")
|
||||
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";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
// 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
|
||||
@@ -12,7 +12,7 @@
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
// along with Moodle. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
namespace core\context;
|
||||
|
||||
@@ -26,27 +26,40 @@ use coding_exception, moodle_url;
|
||||
* @package core_access
|
||||
* @category access
|
||||
* @copyright Petr Skoda
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @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 context_course::instance($courseid) if you need the instance of context.
|
||||
* Alternatively if you know only the context id use context::instance_by_id($contextid)
|
||||
* 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 != CONTEXT_COURSE) {
|
||||
throw new coding_exception('Invalid $record->contextlevel in context_course constructor.');
|
||||
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.
|
||||
*
|
||||
* @static
|
||||
* @return string the human readable context level name.
|
||||
*/
|
||||
public static function get_level_name() {
|
||||
@@ -68,11 +81,11 @@ class course extends context {
|
||||
if ($this->_instanceid == SITEID) {
|
||||
$name = get_string('frontpage', 'admin');
|
||||
} else {
|
||||
if ($course = $DB->get_record('course', array('id'=>$this->_instanceid))) {
|
||||
if ($withprefix){
|
||||
if ($course = $DB->get_record('course', array('id' => $this->_instanceid))) {
|
||||
if ($withprefix) {
|
||||
$name = get_string('course').': ';
|
||||
}
|
||||
if ($short){
|
||||
if ($short) {
|
||||
if (!$escape) {
|
||||
$name .= format_string($course->shortname, true, array('context' => $this, 'escape' => false));
|
||||
} else {
|
||||
@@ -98,12 +111,52 @@ class course extends context {
|
||||
*/
|
||||
public function get_url() {
|
||||
if ($this->_instanceid != SITEID) {
|
||||
return new moodle_url('/course/view.php', array('id'=>$this->_instanceid));
|
||||
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.
|
||||
*
|
||||
@@ -113,11 +166,10 @@ class course extends context {
|
||||
public function get_capabilities(string $sort = self::DEFAULT_CAPABILITY_SORT) {
|
||||
global $DB;
|
||||
|
||||
return $DB->get_records_list('capabilities', 'contextlevel', [
|
||||
CONTEXT_COURSE,
|
||||
CONTEXT_MODULE,
|
||||
CONTEXT_BLOCK,
|
||||
], $sort);
|
||||
$levels = \core\context_helper::get_child_levels(self::LEVEL);
|
||||
$levels[] = self::LEVEL;
|
||||
|
||||
return $DB->get_records_list('capabilities', 'contextlevel', $levels, $sort);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -133,25 +185,24 @@ class course extends context {
|
||||
/**
|
||||
* Returns course context instance.
|
||||
*
|
||||
* @static
|
||||
* @param int $courseid id from {course} table
|
||||
* @param int $strictness
|
||||
* @return course context instance
|
||||
* @return course|false context instance
|
||||
*/
|
||||
public static function instance($courseid, $strictness = MUST_EXIST) {
|
||||
global $DB;
|
||||
|
||||
if ($context = context::cache_get(CONTEXT_COURSE, $courseid)) {
|
||||
if ($context = context::cache_get(self::LEVEL, $courseid)) {
|
||||
return $context;
|
||||
}
|
||||
|
||||
if (!$record = $DB->get_record('context', array('contextlevel' => CONTEXT_COURSE, 'instanceid' => $courseid))) {
|
||||
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(CONTEXT_COURSE, $course->id, $parentcontext->path);
|
||||
$record = context::insert_context_record(self::LEVEL, $course->id, $parentcontext->path);
|
||||
} else {
|
||||
$record = context::insert_context_record(CONTEXT_COURSE, $course->id, '/'.SYSCONTEXTID, 0);
|
||||
$record = context::insert_context_record(self::LEVEL, $course->id, '/'.SYSCONTEXTID, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -167,19 +218,18 @@ class course extends context {
|
||||
|
||||
/**
|
||||
* Create missing context instances at course context level
|
||||
* @static
|
||||
*/
|
||||
protected static function create_level_instances() {
|
||||
global $DB;
|
||||
|
||||
$sql = "SELECT ".CONTEXT_COURSE.", c.id
|
||||
$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=".CONTEXT_COURSE.")";
|
||||
WHERE c.id = cx.instanceid AND cx.contextlevel=".self::LEVEL.")";
|
||||
$contextdata = $DB->get_recordset_sql($sql);
|
||||
foreach ($contextdata as $context) {
|
||||
context::insert_context_record(CONTEXT_COURSE, $context->id, null);
|
||||
context::insert_context_record(self::LEVEL, $context->id, null);
|
||||
}
|
||||
$contextdata->close();
|
||||
}
|
||||
@@ -187,7 +237,6 @@ class course extends context {
|
||||
/**
|
||||
* Returns sql necessary for purging of stale context instances.
|
||||
*
|
||||
* @static
|
||||
* @return string cleanup SQL
|
||||
*/
|
||||
protected static function get_cleanup_sql() {
|
||||
@@ -195,7 +244,7 @@ class course extends context {
|
||||
SELECT c.*
|
||||
FROM {context} c
|
||||
LEFT OUTER JOIN {course} co ON c.instanceid = co.id
|
||||
WHERE co.id IS NULL AND c.contextlevel = ".CONTEXT_COURSE."
|
||||
WHERE co.id IS NULL AND c.contextlevel = ".self::LEVEL."
|
||||
";
|
||||
|
||||
return $sql;
|
||||
@@ -204,39 +253,38 @@ class course extends context {
|
||||
/**
|
||||
* Rebuild context paths and depths at course context level.
|
||||
*
|
||||
* @static
|
||||
* @param bool $force
|
||||
*/
|
||||
protected static function build_paths($force) {
|
||||
global $DB;
|
||||
|
||||
if ($force or $DB->record_exists_select('context', "contextlevel = ".CONTEXT_COURSE." AND (depth = 0 OR path IS NULL)")) {
|
||||
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)";
|
||||
$emptyclause = "AND ({context}.path IS NULL OR {context}.depth = 0)";
|
||||
}
|
||||
|
||||
$base = '/'.SYSCONTEXTID;
|
||||
|
||||
// Standard frontpage
|
||||
// Standard frontpage.
|
||||
$sql = "UPDATE {context}
|
||||
SET depth = 2,
|
||||
path = ".$DB->sql_concat("'$base/'", 'id')."
|
||||
WHERE contextlevel = ".CONTEXT_COURSE."
|
||||
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
|
||||
// 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 = ".CONTEXT_COURSE." AND c.category <> 0)
|
||||
JOIN {context} pctx ON (pctx.instanceid = c.category AND pctx.contextlevel = ".CONTEXT_COURSECAT.")
|
||||
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();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
// 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
|
||||
@@ -12,7 +12,7 @@
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
// along with Moodle. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
namespace core\context;
|
||||
|
||||
@@ -26,27 +26,40 @@ use coding_exception, moodle_url;
|
||||
* @package core_access
|
||||
* @category access
|
||||
* @copyright Petr Skoda
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @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 context_coursecat::instance($coursecatid) if you need the instance of context.
|
||||
* Alternatively if you know only the context id use context::instance_by_id($contextid)
|
||||
* 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 != CONTEXT_COURSECAT) {
|
||||
throw new coding_exception('Invalid $record->contextlevel in context_coursecat constructor.');
|
||||
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.
|
||||
*
|
||||
* @static
|
||||
* @return string the human readable context level name.
|
||||
*/
|
||||
public static function get_level_name() {
|
||||
@@ -65,8 +78,8 @@ class coursecat extends context {
|
||||
global $DB;
|
||||
|
||||
$name = '';
|
||||
if ($category = $DB->get_record('course_categories', array('id'=>$this->_instanceid))) {
|
||||
if ($withprefix){
|
||||
if ($category = $DB->get_record('course_categories', array('id' => $this->_instanceid))) {
|
||||
if ($withprefix) {
|
||||
$name = get_string('category').': ';
|
||||
}
|
||||
if (!$escape) {
|
||||
@@ -87,6 +100,46 @@ class coursecat extends context {
|
||||
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.
|
||||
*
|
||||
@@ -96,18 +149,15 @@ class coursecat extends context {
|
||||
public function get_capabilities(string $sort = self::DEFAULT_CAPABILITY_SORT) {
|
||||
global $DB;
|
||||
|
||||
return $DB->get_records_list('capabilities', 'contextlevel', [
|
||||
CONTEXT_COURSECAT,
|
||||
CONTEXT_COURSE,
|
||||
CONTEXT_MODULE,
|
||||
CONTEXT_BLOCK,
|
||||
], $sort);
|
||||
$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.
|
||||
*
|
||||
* @static
|
||||
* @param int $categoryid id from {course_categories} table
|
||||
* @param int $strictness
|
||||
* @return coursecat|false context instance
|
||||
@@ -115,17 +165,17 @@ class coursecat extends context {
|
||||
public static function instance($categoryid, $strictness = MUST_EXIST) {
|
||||
global $DB;
|
||||
|
||||
if ($context = context::cache_get(CONTEXT_COURSECAT, $categoryid)) {
|
||||
if ($context = context::cache_get(self::LEVEL, $categoryid)) {
|
||||
return $context;
|
||||
}
|
||||
|
||||
if (!$record = $DB->get_record('context', array('contextlevel' => CONTEXT_COURSECAT, 'instanceid' => $categoryid))) {
|
||||
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 = coursecat::instance($category->parent);
|
||||
$record = context::insert_context_record(CONTEXT_COURSECAT, $category->id, $parentcontext->path);
|
||||
$parentcontext = self::instance($category->parent);
|
||||
$record = context::insert_context_record(self::LEVEL, $category->id, $parentcontext->path);
|
||||
} else {
|
||||
$record = context::insert_context_record(CONTEXT_COURSECAT, $category->id, '/'.SYSCONTEXTID, 0);
|
||||
$record = context::insert_context_record(self::LEVEL, $category->id, '/'.SYSCONTEXTID, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -148,7 +198,7 @@ class coursecat extends context {
|
||||
public function get_child_contexts() {
|
||||
global $DB;
|
||||
|
||||
if (empty($this->_path) or empty($this->_depth)) {
|
||||
if (empty($this->_path) || empty($this->_depth)) {
|
||||
debugging('Can not find child contexts of context '.$this->_id.' try rebuilding of context paths');
|
||||
return array();
|
||||
}
|
||||
@@ -156,7 +206,7 @@ class coursecat extends context {
|
||||
$sql = "SELECT ctx.*
|
||||
FROM {context} ctx
|
||||
WHERE ctx.path LIKE ? AND (ctx.depth = ? OR ctx.contextlevel = ?)";
|
||||
$params = array($this->_path.'/%', $this->depth+1, CONTEXT_COURSECAT);
|
||||
$params = array($this->_path.'/%', $this->depth + 1, self::LEVEL);
|
||||
$records = $DB->get_records_sql($sql, $params);
|
||||
|
||||
$result = array();
|
||||
@@ -169,19 +219,18 @@ class coursecat extends context {
|
||||
|
||||
/**
|
||||
* Create missing context instances at course category context level
|
||||
* @static
|
||||
*/
|
||||
protected static function create_level_instances() {
|
||||
global $DB;
|
||||
|
||||
$sql = "SELECT ".CONTEXT_COURSECAT.", cc.id
|
||||
$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=".CONTEXT_COURSECAT.")";
|
||||
WHERE cc.id = cx.instanceid AND cx.contextlevel=".self::LEVEL.")";
|
||||
$contextdata = $DB->get_recordset_sql($sql);
|
||||
foreach ($contextdata as $context) {
|
||||
context::insert_context_record(CONTEXT_COURSECAT, $context->id, null);
|
||||
context::insert_context_record(self::LEVEL, $context->id, null);
|
||||
}
|
||||
$contextdata->close();
|
||||
}
|
||||
@@ -189,7 +238,6 @@ class coursecat extends context {
|
||||
/**
|
||||
* Returns sql necessary for purging of stale context instances.
|
||||
*
|
||||
* @static
|
||||
* @return string cleanup SQL
|
||||
*/
|
||||
protected static function get_cleanup_sql() {
|
||||
@@ -197,7 +245,7 @@ class coursecat extends context {
|
||||
SELECT c.*
|
||||
FROM {context} c
|
||||
LEFT OUTER JOIN {course_categories} cc ON c.instanceid = cc.id
|
||||
WHERE cc.id IS NULL AND c.contextlevel = ".CONTEXT_COURSECAT."
|
||||
WHERE cc.id IS NULL AND c.contextlevel = ".self::LEVEL."
|
||||
";
|
||||
|
||||
return $sql;
|
||||
@@ -206,41 +254,41 @@ class coursecat extends context {
|
||||
/**
|
||||
* Rebuild context paths and depths at course category context level.
|
||||
*
|
||||
* @static
|
||||
* @param bool $force
|
||||
*/
|
||||
protected static function build_paths($force) {
|
||||
global $DB;
|
||||
|
||||
if ($force or $DB->record_exists_select('context', "contextlevel = ".CONTEXT_COURSECAT." AND (depth = 0 OR path IS NULL)")) {
|
||||
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)";
|
||||
$emptyclause = "AND ({context}.path IS NULL OR {context}.depth = 0)";
|
||||
}
|
||||
|
||||
$base = '/'.SYSCONTEXTID;
|
||||
|
||||
// Normal top level categories
|
||||
// Normal top level categories.
|
||||
$sql = "UPDATE {context}
|
||||
SET depth=2,
|
||||
path=".$DB->sql_concat("'$base/'", 'id')."
|
||||
WHERE contextlevel=".CONTEXT_COURSECAT."
|
||||
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
|
||||
// Deeper categories - one query per depthlevel.
|
||||
$maxdepth = $DB->get_field_sql("SELECT MAX(depth) FROM {course_categories}");
|
||||
for ($n=2; $n<=$maxdepth; $n++) {
|
||||
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 = ".CONTEXT_COURSECAT." AND cc.depth = $n)
|
||||
JOIN {context} pctx ON (pctx.instanceid = cc.parent AND pctx.contextlevel = ".CONTEXT_COURSECAT.")
|
||||
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();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
// 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
|
||||
@@ -12,7 +12,7 @@
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
// along with Moodle. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
namespace core\context;
|
||||
|
||||
@@ -26,27 +26,40 @@ use coding_exception, moodle_url;
|
||||
* @package core_access
|
||||
* @category access
|
||||
* @copyright Petr Skoda
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @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 context_module::instance($cmid) if you need the instance of context.
|
||||
* Alternatively if you know only the context id use context::instance_by_id($contextid)
|
||||
* 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 != CONTEXT_MODULE) {
|
||||
throw new coding_exception('Invalid $record->contextlevel in context_module constructor.');
|
||||
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.
|
||||
*
|
||||
* @static
|
||||
* @return string the human readable context level name.
|
||||
*/
|
||||
public static function get_level_name() {
|
||||
@@ -71,7 +84,7 @@ class module extends context {
|
||||
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){
|
||||
if ($withprefix) {
|
||||
$name = get_string('modulename', $cm->modname).': ';
|
||||
}
|
||||
if (!$escape) {
|
||||
@@ -96,12 +109,52 @@ class module extends context {
|
||||
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('/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.
|
||||
*
|
||||
@@ -111,8 +164,8 @@ class module extends context {
|
||||
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));
|
||||
$cm = $DB->get_record('course_modules', array('id' => $this->_instanceid));
|
||||
$module = $DB->get_record('modules', array('id' => $cm->module));
|
||||
|
||||
$subcaps = array();
|
||||
|
||||
@@ -123,13 +176,13 @@ class module extends context {
|
||||
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
|
||||
$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) {
|
||||
foreach (array_keys(\core_component::get_plugin_list($subplugintype)) as $subpluginname) {
|
||||
$subcaps = array_merge($subcaps, array_keys(load_capability_def($subplugintype.'_'.$subpluginname)));
|
||||
}
|
||||
}
|
||||
@@ -163,7 +216,6 @@ class module extends context {
|
||||
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 = [];
|
||||
@@ -181,7 +233,7 @@ class module extends context {
|
||||
|
||||
$sql = "SELECT *
|
||||
FROM {capabilities}
|
||||
WHERE (contextlevel = ".CONTEXT_MODULE."
|
||||
WHERE (contextlevel = ".self::LEVEL."
|
||||
AND component {$notcompsql}
|
||||
AND {$notlikesql})
|
||||
$extra
|
||||
@@ -203,7 +255,6 @@ class module extends context {
|
||||
/**
|
||||
* Returns module context instance.
|
||||
*
|
||||
* @static
|
||||
* @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
|
||||
@@ -211,14 +262,14 @@ class module extends context {
|
||||
public static function instance($cmid, $strictness = MUST_EXIST) {
|
||||
global $DB;
|
||||
|
||||
if ($context = context::cache_get(CONTEXT_MODULE, $cmid)) {
|
||||
if ($context = context::cache_get(self::LEVEL, $cmid)) {
|
||||
return $context;
|
||||
}
|
||||
|
||||
if (!$record = $DB->get_record('context', array('contextlevel' => CONTEXT_MODULE, 'instanceid' => $cmid))) {
|
||||
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(CONTEXT_MODULE, $cm->id, $parentcontext->path);
|
||||
$record = context::insert_context_record(self::LEVEL, $cm->id, $parentcontext->path);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -233,19 +284,18 @@ class module extends context {
|
||||
|
||||
/**
|
||||
* Create missing context instances at module context level
|
||||
* @static
|
||||
*/
|
||||
protected static function create_level_instances() {
|
||||
global $DB;
|
||||
|
||||
$sql = "SELECT ".CONTEXT_MODULE.", cm.id
|
||||
$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=".CONTEXT_MODULE.")";
|
||||
WHERE cm.id = cx.instanceid AND cx.contextlevel=" . self::LEVEL . ")";
|
||||
$contextdata = $DB->get_recordset_sql($sql);
|
||||
foreach ($contextdata as $context) {
|
||||
context::insert_context_record(CONTEXT_MODULE, $context->id, null);
|
||||
context::insert_context_record(self::LEVEL, $context->id, null);
|
||||
}
|
||||
$contextdata->close();
|
||||
}
|
||||
@@ -253,7 +303,6 @@ class module extends context {
|
||||
/**
|
||||
* Returns sql necessary for purging of stale context instances.
|
||||
*
|
||||
* @static
|
||||
* @return string cleanup SQL
|
||||
*/
|
||||
protected static function get_cleanup_sql() {
|
||||
@@ -261,7 +310,7 @@ class module extends context {
|
||||
SELECT c.*
|
||||
FROM {context} c
|
||||
LEFT OUTER JOIN {course_modules} cm ON c.instanceid = cm.id
|
||||
WHERE cm.id IS NULL AND c.contextlevel = ".CONTEXT_MODULE."
|
||||
WHERE cm.id IS NULL AND c.contextlevel = " . self::LEVEL . "
|
||||
";
|
||||
|
||||
return $sql;
|
||||
@@ -270,13 +319,12 @@ class module extends context {
|
||||
/**
|
||||
* Rebuild context paths and depths at module context level.
|
||||
*
|
||||
* @static
|
||||
* @param bool $force
|
||||
*/
|
||||
protected static function build_paths($force) {
|
||||
global $DB;
|
||||
|
||||
if ($force or $DB->record_exists_select('context', "contextlevel = ".CONTEXT_MODULE." AND (depth = 0 OR path IS NULL)")) {
|
||||
if ($force || $DB->record_exists_select('context', "contextlevel = " . self::LEVEL . " AND (depth = 0 OR path IS NULL)")) {
|
||||
if ($force) {
|
||||
$ctxemptyclause = '';
|
||||
} else {
|
||||
@@ -286,8 +334,8 @@ class module extends context {
|
||||
$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 = ".CONTEXT_MODULE.")
|
||||
JOIN {context} pctx ON (pctx.instanceid = cm.course AND pctx.contextlevel = ".CONTEXT_COURSE.")
|
||||
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();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
// 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
|
||||
@@ -12,7 +12,7 @@
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
// along with Moodle. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
namespace core\context;
|
||||
|
||||
@@ -26,26 +26,39 @@ use coding_exception, moodle_url;
|
||||
* @package core_access
|
||||
* @category access
|
||||
* @copyright Petr Skoda
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @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 context_system::instance() if you need the instance of context.
|
||||
* 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 != CONTEXT_SYSTEM) {
|
||||
throw new coding_exception('Invalid $record->contextlevel in context_system constructor.');
|
||||
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.
|
||||
*
|
||||
* @static
|
||||
* @return string the human readable context level name.
|
||||
*/
|
||||
public static function get_level_name() {
|
||||
@@ -73,6 +86,27 @@ class system extends context {
|
||||
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.
|
||||
*
|
||||
@@ -87,17 +121,15 @@ class system extends context {
|
||||
|
||||
/**
|
||||
* Create missing context instances at system context
|
||||
* @static
|
||||
*/
|
||||
protected static function create_level_instances() {
|
||||
// nothing to do here, the system context is created automatically in installer
|
||||
// Nothing to do here, the system context is created automatically in installer.
|
||||
self::instance(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns system context instance.
|
||||
*
|
||||
* @static
|
||||
* @param int $instanceid should be 0
|
||||
* @param int $strictness
|
||||
* @param bool $cache
|
||||
@@ -111,15 +143,15 @@ class system extends context {
|
||||
}
|
||||
|
||||
// SYSCONTEXTID is cached in local cache to eliminate 1 query per page.
|
||||
if (defined('SYSCONTEXTID') and $cache) {
|
||||
if (defined('SYSCONTEXTID') && $cache) {
|
||||
if (!isset(context::$systemcontext)) {
|
||||
$record = new stdClass();
|
||||
$record->id = SYSCONTEXTID;
|
||||
$record->contextlevel = CONTEXT_SYSTEM;
|
||||
$record->instanceid = 0;
|
||||
$record->path = '/'.SYSCONTEXTID;
|
||||
$record->depth = 1;
|
||||
$record->locked = 0;
|
||||
$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;
|
||||
@@ -127,11 +159,11 @@ class system extends context {
|
||||
|
||||
try {
|
||||
// We ignore the strictness completely because system context must exist except during install.
|
||||
$record = $DB->get_record('context', array('contextlevel'=>CONTEXT_SYSTEM), '*', MUST_EXIST);
|
||||
$record = $DB->get_record('context', array('contextlevel' => self::LEVEL), '*', MUST_EXIST);
|
||||
} catch (\dml_exception $e) {
|
||||
//table or record does not exist
|
||||
// Table or record does not exist.
|
||||
if (!during_initial_install()) {
|
||||
// do not mess with system context after install, it simply must exist
|
||||
// Do not mess with system context after install, it simply must exist.
|
||||
throw $e;
|
||||
}
|
||||
$record = null;
|
||||
@@ -139,19 +171,19 @@ class system extends context {
|
||||
|
||||
if (!$record) {
|
||||
$record = new stdClass();
|
||||
$record->contextlevel = CONTEXT_SYSTEM;
|
||||
$record->instanceid = 0;
|
||||
$record->depth = 1;
|
||||
$record->path = null; // Not known before insert.
|
||||
$record->locked = 0;
|
||||
$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!!!
|
||||
// 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
|
||||
// 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');
|
||||
@@ -159,20 +191,20 @@ class system extends context {
|
||||
$record->id = $DB->insert_record('context', $record);
|
||||
}
|
||||
} catch (\dml_exception $e) {
|
||||
// can not create context - table does not exist yet, sorry
|
||||
// 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
|
||||
// This is very weird, somebody must be messing with context table.
|
||||
debugging('Invalid system context detected');
|
||||
}
|
||||
|
||||
if ($record->depth != 1 or $record->path != '/'.$record->id) {
|
||||
// fix path if necessary, initial install or path reset
|
||||
if ($record->depth != 1 || $record->path != '/'.$record->id) {
|
||||
// Fix path if necessary, initial install or path reset.
|
||||
$record->depth = 1;
|
||||
$record->path = '/'.$record->id;
|
||||
$record->path = '/'.$record->id;
|
||||
$DB->update_record('context', $record);
|
||||
}
|
||||
|
||||
@@ -198,13 +230,14 @@ class system extends context {
|
||||
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)!');
|
||||
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 CONTEXT_SYSTEM level
|
||||
// and hope we don't OOM in the process - don't cache
|
||||
// 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 > ".CONTEXT_SYSTEM;
|
||||
WHERE contextlevel > " . self::LEVEL;
|
||||
$records = $DB->get_records_sql($sql);
|
||||
|
||||
$result = array();
|
||||
@@ -218,7 +251,6 @@ class system extends context {
|
||||
/**
|
||||
* Returns sql necessary for purging of stale context instances.
|
||||
*
|
||||
* @static
|
||||
* @return string cleanup SQL
|
||||
*/
|
||||
protected static function get_cleanup_sql() {
|
||||
@@ -234,7 +266,6 @@ class system extends context {
|
||||
/**
|
||||
* Rebuild context paths and depths at system context level.
|
||||
*
|
||||
* @static
|
||||
* @param bool $force
|
||||
*/
|
||||
protected static function build_paths($force) {
|
||||
@@ -242,21 +273,21 @@ class system extends context {
|
||||
|
||||
/* note: ignore $force here, we always do full test of system context */
|
||||
|
||||
// exactly one record must exist
|
||||
$record = $DB->get_record('context', array('contextlevel'=>CONTEXT_SYSTEM), '*', MUST_EXIST);
|
||||
// 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') and $record->id != SYSCONTEXTID) {
|
||||
if (defined('SYSCONTEXTID') && $record->id != SYSCONTEXTID) {
|
||||
debugging('Invalid SYSCONTEXTID detected');
|
||||
}
|
||||
|
||||
if ($record->depth != 1 or $record->path != '/'.$record->id) {
|
||||
// fix path if necessary, initial install or path reset
|
||||
$record->depth = 1;
|
||||
$record->path = '/'.$record->id;
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -268,8 +299,9 @@ class system extends context {
|
||||
* @return $this
|
||||
*/
|
||||
public function set_locked(bool $locked) {
|
||||
throw new \coding_exception('It is not possible to lock the system context');
|
||||
|
||||
return $this;
|
||||
if ($locked) {
|
||||
throw new \coding_exception('It is not possible to lock the system context');
|
||||
}
|
||||
return parent::set_locked($locked);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
// 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
|
||||
@@ -12,7 +12,7 @@
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
// along with Moodle. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
namespace core\context;
|
||||
|
||||
@@ -26,27 +26,40 @@ use coding_exception, moodle_url;
|
||||
* @package core_access
|
||||
* @category access
|
||||
* @copyright Petr Skoda
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @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 context_user::instance($userid) if you need the instance of context.
|
||||
* Alternatively if you know only the context id use context::instance_by_id($contextid)
|
||||
* 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 != CONTEXT_USER) {
|
||||
throw new coding_exception('Invalid $record->contextlevel in context_user constructor.');
|
||||
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.
|
||||
*
|
||||
* @static
|
||||
* @return string the human readable context level name.
|
||||
*/
|
||||
public static function get_level_name() {
|
||||
@@ -65,8 +78,8 @@ class user extends context {
|
||||
global $DB;
|
||||
|
||||
$name = '';
|
||||
if ($user = $DB->get_record('user', array('id'=>$this->_instanceid, 'deleted'=>0))) {
|
||||
if ($withprefix){
|
||||
if ($user = $DB->get_record('user', array('id' => $this->_instanceid, 'deleted' => 0))) {
|
||||
if ($withprefix) {
|
||||
$name = get_string('user').': ';
|
||||
}
|
||||
$name .= fullname($user);
|
||||
@@ -83,13 +96,42 @@ class user extends context {
|
||||
global $COURSE;
|
||||
|
||||
if ($COURSE->id == SITEID) {
|
||||
$url = new moodle_url('/user/profile.php', array('id'=>$this->_instanceid));
|
||||
$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));
|
||||
$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.
|
||||
*
|
||||
@@ -103,13 +145,12 @@ class user extends context {
|
||||
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' => CONTEXT_USER], $sort);
|
||||
$params + ['level' => self::LEVEL], $sort);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns user context instance.
|
||||
*
|
||||
* @static
|
||||
* @param int $userid id from {user} table
|
||||
* @param int $strictness
|
||||
* @return user|false context instance
|
||||
@@ -117,13 +158,13 @@ class user extends context {
|
||||
public static function instance($userid, $strictness = MUST_EXIST) {
|
||||
global $DB;
|
||||
|
||||
if ($context = context::cache_get(CONTEXT_USER, $userid)) {
|
||||
if ($context = context::cache_get(self::LEVEL, $userid)) {
|
||||
return $context;
|
||||
}
|
||||
|
||||
if (!$record = $DB->get_record('context', array('contextlevel' => CONTEXT_USER, 'instanceid' => $userid))) {
|
||||
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(CONTEXT_USER, $user->id, '/'.SYSCONTEXTID, 0);
|
||||
$record = context::insert_context_record(self::LEVEL, $user->id, '/'.SYSCONTEXTID, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,20 +179,19 @@ class user extends context {
|
||||
|
||||
/**
|
||||
* Create missing context instances at user context level
|
||||
* @static
|
||||
*/
|
||||
protected static function create_level_instances() {
|
||||
global $DB;
|
||||
|
||||
$sql = "SELECT ".CONTEXT_USER.", u.id
|
||||
$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=".CONTEXT_USER.")";
|
||||
WHERE u.id = cx.instanceid AND cx.contextlevel=" . self::LEVEL . ")";
|
||||
$contextdata = $DB->get_recordset_sql($sql);
|
||||
foreach ($contextdata as $context) {
|
||||
context::insert_context_record(CONTEXT_USER, $context->id, null);
|
||||
context::insert_context_record(self::LEVEL, $context->id, null);
|
||||
}
|
||||
$contextdata->close();
|
||||
}
|
||||
@@ -159,7 +199,6 @@ class user extends context {
|
||||
/**
|
||||
* Returns sql necessary for purging of stale context instances.
|
||||
*
|
||||
* @static
|
||||
* @return string cleanup SQL
|
||||
*/
|
||||
protected static function get_cleanup_sql() {
|
||||
@@ -167,7 +206,7 @@ class user extends context {
|
||||
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 = ".CONTEXT_USER."
|
||||
WHERE u.id IS NULL AND c.contextlevel = " . self::LEVEL . "
|
||||
";
|
||||
|
||||
return $sql;
|
||||
@@ -176,7 +215,6 @@ class user extends context {
|
||||
/**
|
||||
* Rebuild context paths and depths at user context level.
|
||||
*
|
||||
* @static
|
||||
* @param bool $force
|
||||
*/
|
||||
protected static function build_paths($force) {
|
||||
@@ -197,7 +235,7 @@ class user extends context {
|
||||
$sql = "UPDATE {context}
|
||||
SET depth = 2,
|
||||
path = {$path}
|
||||
WHERE contextlevel = " . CONTEXT_USER . "
|
||||
WHERE contextlevel = " . self::LEVEL . "
|
||||
AND ($where)";
|
||||
$DB->execute($sql, $params);
|
||||
}
|
||||
|
||||
+188
-65
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
// 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
|
||||
@@ -12,14 +12,12 @@
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
// along with Moodle. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
namespace core;
|
||||
|
||||
use stdClass;
|
||||
use coding_exception;
|
||||
use core\context\system;
|
||||
use core\context\course;
|
||||
|
||||
/**
|
||||
* Context maintenance and helper methods.
|
||||
@@ -33,22 +31,16 @@ use core\context\course;
|
||||
* @package core_access
|
||||
* @category access
|
||||
* @copyright Petr Skoda
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @since Moodle 4.2
|
||||
*/
|
||||
class context_helper extends context {
|
||||
abstract class context_helper extends context {
|
||||
|
||||
/**
|
||||
* @var array An array mapping context levels to classes
|
||||
* @var array An array definitions of all context levels
|
||||
*/
|
||||
private static $alllevels;
|
||||
|
||||
/**
|
||||
* Instance does not make sense here, only static use
|
||||
*/
|
||||
protected function __construct() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset internal context levels array.
|
||||
*/
|
||||
@@ -59,19 +51,19 @@ class context_helper extends context {
|
||||
/**
|
||||
* Initialise context levels, call before using self::$alllevels.
|
||||
*/
|
||||
private static function init_levels() {
|
||||
private static function init_levels():void {
|
||||
global $CFG;
|
||||
|
||||
if (isset(self::$alllevels)) {
|
||||
return;
|
||||
}
|
||||
self::$alllevels = array(
|
||||
CONTEXT_SYSTEM => 'context_system',
|
||||
CONTEXT_USER => 'context_user',
|
||||
CONTEXT_COURSECAT => 'context_coursecat',
|
||||
CONTEXT_COURSE => 'context_course',
|
||||
CONTEXT_MODULE => 'context_module',
|
||||
CONTEXT_BLOCK => 'context_block',
|
||||
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)) {
|
||||
@@ -94,14 +86,113 @@ class context_helper extends context {
|
||||
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
|
||||
*
|
||||
* @static
|
||||
* @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($contextlevel) {
|
||||
public static function get_class_for_level(int $contextlevel): string {
|
||||
self::init_levels();
|
||||
if (isset(self::$alllevels[$contextlevel])) {
|
||||
return self::$alllevels[$contextlevel];
|
||||
@@ -113,19 +204,75 @@ class context_helper extends context {
|
||||
/**
|
||||
* Returns a list of all context levels
|
||||
*
|
||||
* @static
|
||||
* @return array int=>string (level=>level class name)
|
||||
*/
|
||||
public static function get_all_levels() {
|
||||
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...
|
||||
*
|
||||
* @static
|
||||
* @return void
|
||||
*/
|
||||
public static function cleanup_instances() {
|
||||
@@ -133,13 +280,13 @@ class context_helper extends context {
|
||||
self::init_levels();
|
||||
|
||||
$sqls = array();
|
||||
foreach (self::$alllevels as $level=>$classname) {
|
||||
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
|
||||
// It is probably better to use transactions, it might be faster too.
|
||||
$transaction = $DB->start_delegated_transaction();
|
||||
|
||||
$rs = $DB->get_recordset_sql($sql);
|
||||
@@ -155,16 +302,16 @@ class context_helper extends context {
|
||||
/**
|
||||
* Create all context instances at the given level and above.
|
||||
*
|
||||
* @static
|
||||
* @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 and $level > $contextlevel) {
|
||||
// skip potential sub-contexts
|
||||
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();
|
||||
@@ -177,7 +324,6 @@ class context_helper extends context {
|
||||
/**
|
||||
* Rebuild paths and depths in all context levels.
|
||||
*
|
||||
* @static
|
||||
* @param bool $force false means add missing only
|
||||
* @return void
|
||||
*/
|
||||
@@ -187,13 +333,12 @@ class context_helper extends context {
|
||||
$classname::build_paths($force);
|
||||
}
|
||||
|
||||
// reset static course cache - it might have incorrect cached data
|
||||
// Reset static course cache - it might have incorrect cached data.
|
||||
accesslib_clear_all_caches(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the cache to remove all data.
|
||||
* @static
|
||||
*/
|
||||
public static function reset_caches() {
|
||||
context::reset_caches();
|
||||
@@ -204,7 +349,6 @@ class context_helper extends context {
|
||||
*
|
||||
* This helps with performance when dealing with hundreds of contexts.
|
||||
*
|
||||
* @static
|
||||
* @param string $tablealias context table alias in the query
|
||||
* @return array (table.column=>alias, ...)
|
||||
*/
|
||||
@@ -224,7 +368,6 @@ class context_helper extends context {
|
||||
*
|
||||
* This helps with performance when dealing with hundreds of contexts.
|
||||
*
|
||||
* @static
|
||||
* @param string $tablealias context table alias in the query
|
||||
* @return string
|
||||
*/
|
||||
@@ -242,12 +385,11 @@ class context_helper extends context {
|
||||
*
|
||||
* The db request has to contain all columns from context_helper::get_preload_record_columns().
|
||||
*
|
||||
* @static
|
||||
* @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) {
|
||||
public static function preload_from_record(stdClass $rec): void {
|
||||
context::preload_from_record($rec);
|
||||
}
|
||||
|
||||
@@ -256,7 +398,7 @@ class context_helper extends context {
|
||||
*
|
||||
* @param array $contextids
|
||||
*/
|
||||
public static function preload_contexts_by_id(array $contextids) {
|
||||
public static function preload_contexts_by_id(array $contextids): void {
|
||||
global $DB;
|
||||
|
||||
// Determine which contexts are not already cached.
|
||||
@@ -272,7 +414,7 @@ class context_helper extends context {
|
||||
// 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, '',
|
||||
context_helper::get_preload_record_columns_sql('{context}'));
|
||||
self::get_preload_record_columns_sql('{context}'));
|
||||
foreach ($ctxs as $ctx) {
|
||||
self::preload_from_record($ctx);
|
||||
}
|
||||
@@ -284,15 +426,14 @@ class context_helper extends context {
|
||||
*
|
||||
* To be used if you expect multiple queries for course activities...
|
||||
*
|
||||
* @static
|
||||
* @param int $courseid
|
||||
*/
|
||||
public static function preload_course($courseid) {
|
||||
// Users can call this multiple times without doing any harm
|
||||
// Users can call this multiple times without doing any harm.
|
||||
if (isset(context::$cache_preloaded[$courseid])) {
|
||||
return;
|
||||
}
|
||||
$coursecontext = course::instance($courseid);
|
||||
$coursecontext = context\course::instance($courseid);
|
||||
$coursecontext->get_child_contexts();
|
||||
|
||||
context::$cache_preloaded[$courseid] = true;
|
||||
@@ -301,7 +442,6 @@ class context_helper extends context {
|
||||
/**
|
||||
* Delete context instance
|
||||
*
|
||||
* @static
|
||||
* @param int $contextlevel
|
||||
* @param int $instanceid
|
||||
* @return void
|
||||
@@ -309,24 +449,21 @@ class context_helper extends context {
|
||||
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))) {
|
||||
// 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();
|
||||
} else {
|
||||
// we should try to purge the cache anyway
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of specified context level
|
||||
*
|
||||
* @static
|
||||
* @param int $contextlevel
|
||||
* @return string name of the context level
|
||||
*/
|
||||
public static function get_level_name($contextlevel) {
|
||||
$classname = context_helper::get_class_for_level($contextlevel);
|
||||
$classname = self::get_class_for_level($contextlevel);
|
||||
return $classname::get_level_name();
|
||||
}
|
||||
|
||||
@@ -339,23 +476,9 @@ class context_helper extends context {
|
||||
public static function get_navigation_filter_context(?context $context): ?context {
|
||||
global $CFG;
|
||||
if (!empty($CFG->filternavigationwithsystemcontext)) {
|
||||
return system::instance();
|
||||
return context\system::instance();
|
||||
} else {
|
||||
return $context;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* not used
|
||||
*/
|
||||
public function get_url() {
|
||||
}
|
||||
|
||||
/**
|
||||
* not used
|
||||
*
|
||||
* @param string $sort
|
||||
*/
|
||||
public function get_capabilities(string $sort = self::DEFAULT_CAPABILITY_SORT) {
|
||||
}
|
||||
}
|
||||
|
||||
+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