first commit

This commit is contained in:
CHIEFSOFT\ameye
2024-09-30 18:11:26 -04:00
commit e592ca6823
27270 changed files with 5002257 additions and 0 deletions
+265
View File
@@ -0,0 +1,265 @@
<?php
// This file is part of Moodle - https://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <https://www.gnu.org/licenses/>.
namespace core\context;
use core\context;
use stdClass;
use coding_exception, moodle_url;
/**
* Block context class
*
* @package core_access
* @category access
* @copyright Petr Skoda
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @since Moodle 4.2
*/
class block extends context {
/** @var int numeric context level value matching legacy CONTEXT_BLOCK */
public const LEVEL = 80;
/**
* Please use \core\context\block::instance($blockinstanceid) if you need the instance of context.
* Alternatively if you know only the context id use \core\context::instance_by_id($contextid)
*
* @param stdClass $record
*/
protected function __construct(stdClass $record) {
parent::__construct($record);
if ($record->contextlevel != self::LEVEL) {
throw new coding_exception('Invalid $record->contextlevel in core\context\block constructor');
}
}
/**
* Returns short context name.
*
* @since Moodle 4.2
*
* @return string
*/
public static function get_short_name(): string {
return 'block';
}
/**
* Returns human readable context level name.
*
* @return string the human readable context level name.
*/
public static function get_level_name() {
return get_string('block');
}
/**
* Returns human readable context identifier.
*
* @param boolean $withprefix whether to prefix the name of the context with Block
* @param boolean $short does not apply to block context
* @param boolean $escape does not apply to block context
* @return string the human readable context name.
*/
public function get_context_name($withprefix = true, $short = false, $escape = true) {
global $DB;
$name = '';
if ($blockinstance = $DB->get_record('block_instances', array('id' => $this->_instanceid))) {
$blockobject = block_instance($blockinstance->blockname);
if ($blockobject) {
if ($withprefix) {
$name = get_string('block').': ';
}
$name .= $blockobject->title;
}
}
return $name;
}
/**
* Returns the most relevant URL for this context.
*
* @return moodle_url
*/
public function get_url() {
$parentcontexts = $this->get_parent_context();
return $parentcontexts->get_url();
}
/**
* Returns list of all possible parent context levels.
* @since Moodle 4.2
*
* @return int[]
*/
public static function get_possible_parent_levels(): array {
// Blocks may be added to any other context instance.
$alllevels = \core\context_helper::get_all_levels();
unset($alllevels[self::LEVEL]);
return array_keys($alllevels);
}
/**
* Returns array of relevant context capability records.
*
* @param string $sort
* @return array
*/
public function get_capabilities(string $sort = self::DEFAULT_CAPABILITY_SORT) {
global $DB;
$bi = $DB->get_record('block_instances', array('id' => $this->_instanceid));
$select = '(contextlevel = :level AND component = :component)';
$params = [
'level' => self::LEVEL,
'component' => 'block_' . $bi->blockname,
];
$extracaps = block_method_result($bi->blockname, 'get_extra_capabilities');
if ($extracaps) {
list($extra, $extraparams) = $DB->get_in_or_equal($extracaps, SQL_PARAMS_NAMED, 'cap');
$select .= " OR name $extra";
$params = array_merge($params, $extraparams);
}
return $DB->get_records_select('capabilities', $select, $params, $sort);
}
/**
* Is this context part of any course? If yes return course context.
*
* @param bool $strict true means throw exception if not found, false means return false if not found
* @return course context of the enclosing course, null if not found or exception
*/
public function get_course_context($strict = true) {
$parentcontext = $this->get_parent_context();
return $parentcontext->get_course_context($strict);
}
/**
* Returns block context instance.
*
* @param int $blockinstanceid id from {block_instances} table.
* @param int $strictness
* @return block|false context instance
*/
public static function instance($blockinstanceid, $strictness = MUST_EXIST) {
global $DB;
if ($context = context::cache_get(self::LEVEL, $blockinstanceid)) {
return $context;
}
if (!$record = $DB->get_record('context', array('contextlevel' => self::LEVEL, 'instanceid' => $blockinstanceid))) {
if ($bi = $DB->get_record('block_instances', array('id' => $blockinstanceid), 'id,parentcontextid', $strictness)) {
$parentcontext = context::instance_by_id($bi->parentcontextid);
$record = context::insert_context_record(self::LEVEL, $bi->id, $parentcontext->path);
}
}
if ($record) {
$context = new block($record);
context::cache_add($context);
return $context;
}
return false;
}
/**
* Block do not have child contexts...
* @return array
*/
public function get_child_contexts() {
return array();
}
/**
* Create missing context instances at block context level
*/
protected static function create_level_instances() {
global $DB;
$sql = <<<EOF
INSERT INTO {context} (
contextlevel,
instanceid
) SELECT
:contextlevel,
bi.id as instanceid
FROM {block_instances} bi
WHERE NOT EXISTS (
SELECT 'x' FROM {context} cx WHERE bi.id = cx.instanceid AND cx.contextlevel = :existingcontextlevel
)
EOF;
$DB->execute($sql, [
'contextlevel' => self::LEVEL,
'existingcontextlevel' => self::LEVEL,
]);
}
/**
* Returns sql necessary for purging of stale context instances.
*
* @return string cleanup SQL
*/
protected static function get_cleanup_sql() {
$sql = "
SELECT c.*
FROM {context} c
LEFT OUTER JOIN {block_instances} bi ON c.instanceid = bi.id
WHERE bi.id IS NULL AND c.contextlevel = ".self::LEVEL."
";
return $sql;
}
/**
* Rebuild context paths and depths at block context level.
*
* @param bool $force
*/
protected static function build_paths($force) {
global $DB;
if ($force || $DB->record_exists_select('context', "contextlevel = ".self::LEVEL." AND (depth = 0 OR path IS NULL)")) {
if ($force) {
$ctxemptyclause = '';
} else {
$ctxemptyclause = "AND (ctx.path IS NULL OR ctx.depth = 0)";
}
// The pctx.path IS NOT NULL prevents fatal problems with broken block instances that point to invalid context parent.
$sql = "INSERT INTO {context_temp} (id, path, depth, locked)
SELECT ctx.id, ".$DB->sql_concat('pctx.path', "'/'", 'ctx.id').", pctx.depth+1, ctx.locked
FROM {context} ctx
JOIN {block_instances} bi ON (bi.id = ctx.instanceid AND ctx.contextlevel = " . self::LEVEL . ")
JOIN {context} pctx ON (pctx.id = bi.parentcontextid)
WHERE (pctx.path IS NOT NULL AND pctx.depth > 0)
$ctxemptyclause";
$trans = $DB->start_delegated_transaction();
$DB->delete_records('context_temp');
$DB->execute($sql);
context::merge_context_temp_table();
$DB->delete_records('context_temp');
$trans->allow_commit();
}
}
}
+298
View File
@@ -0,0 +1,298 @@
<?php
// This file is part of Moodle - https://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <https://www.gnu.org/licenses/>.
namespace core\context;
use core\context;
use stdClass;
use coding_exception, moodle_url;
/**
* Course context class
*
* @package core_access
* @category access
* @copyright Petr Skoda
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @since Moodle 4.2
*/
class course extends context {
/** @var int numeric context level value matching legacy CONTEXT_COURSE */
public const LEVEL = 50;
/**
* Please use \core\context\course::instance($courseid) if you need the instance of context.
* Alternatively if you know only the context id use \core\context::instance_by_id($contextid)
*
* @param stdClass $record
*/
protected function __construct(stdClass $record) {
parent::__construct($record);
if ($record->contextlevel != self::LEVEL) {
throw new coding_exception('Invalid $record->contextlevel in core\context\course constructor.');
}
}
/**
* Returns short context name.
*
* @since Moodle 4.2
*
* @return string
*/
public static function get_short_name(): string {
return 'course';
}
/**
* Returns human readable context level name.
*
* @return string the human readable context level name.
*/
public static function get_level_name() {
return get_string('course');
}
/**
* Returns human readable context identifier.
*
* @param boolean $withprefix whether to prefix the name of the context with Course
* @param boolean $short whether to use the short name of the thing.
* @param bool $escape Whether the returned category name is to be HTML escaped or not.
* @return string the human readable context name.
*/
public function get_context_name($withprefix = true, $short = false, $escape = true) {
global $DB;
$name = '';
if ($this->_instanceid == SITEID) {
$name = get_string('frontpage', 'admin');
} else {
if ($course = $DB->get_record('course', array('id' => $this->_instanceid))) {
if ($withprefix) {
$name = get_string('course').': ';
}
if ($short) {
if (!$escape) {
$name .= format_string($course->shortname, true, array('context' => $this, 'escape' => false));
} else {
$name .= format_string($course->shortname, true, array('context' => $this));
}
} else {
if (!$escape) {
$name .= format_string(get_course_display_name_for_list($course), true, array('context' => $this,
'escape' => false));
} else {
$name .= format_string(get_course_display_name_for_list($course), true, array('context' => $this));
}
}
}
}
return $name;
}
/**
* Returns the most relevant URL for this context.
*
* @return moodle_url
*/
public function get_url() {
if ($this->_instanceid != SITEID) {
return new moodle_url('/course/view.php', array('id' => $this->_instanceid));
}
return new moodle_url('/');
}
/**
* Returns context instance database name.
*
* @return string|null table name for all levels except system.
*/
protected static function get_instance_table(): ?string {
return 'course';
}
/**
* Returns list of columns that can be used from behat
* to look up context by reference.
*
* @return array list of column names from instance table
*/
protected static function get_behat_reference_columns(): array {
return ['shortname'];
}
/**
* Returns list of all role archetypes that are compatible
* with role assignments in context level.
* @since Moodle 4.2
*
* @return int[]
*/
protected static function get_compatible_role_archetypes(): array {
return ['manager', 'editingteacher', 'teacher', 'student'];
}
/**
* Returns list of all possible parent context levels.
* @since Moodle 4.2
*
* @return int[]
*/
public static function get_possible_parent_levels(): array {
return [coursecat::LEVEL];
}
/**
* Returns array of relevant context capability records.
*
* @param string $sort
* @return array
*/
public function get_capabilities(string $sort = self::DEFAULT_CAPABILITY_SORT) {
global $DB;
$levels = \core\context_helper::get_child_levels(self::LEVEL);
$levels[] = self::LEVEL;
return $DB->get_records_list('capabilities', 'contextlevel', $levels, $sort);
}
/**
* Is this context part of any course? If yes return course context.
*
* @param bool $strict true means throw exception if not found, false means return false if not found
* @return course context of the enclosing course, null if not found or exception
*/
public function get_course_context($strict = true) {
return $this;
}
/**
* Returns course context instance.
*
* @param int $courseid id from {course} table
* @param int $strictness
* @return course|false context instance
*/
public static function instance($courseid, $strictness = MUST_EXIST) {
global $DB;
if ($context = context::cache_get(self::LEVEL, $courseid)) {
return $context;
}
if (!$record = $DB->get_record('context', array('contextlevel' => self::LEVEL, 'instanceid' => $courseid))) {
if ($course = $DB->get_record('course', array('id' => $courseid), 'id,category', $strictness)) {
if ($course->category) {
$parentcontext = coursecat::instance($course->category);
$record = context::insert_context_record(self::LEVEL, $course->id, $parentcontext->path);
} else {
$record = context::insert_context_record(self::LEVEL, $course->id, '/'.SYSCONTEXTID, 0);
}
}
}
if ($record) {
$context = new course($record);
context::cache_add($context);
return $context;
}
return false;
}
/**
* Create missing context instances at course context level
*/
protected static function create_level_instances() {
global $DB;
$sql = "SELECT ".self::LEVEL.", c.id
FROM {course} c
WHERE NOT EXISTS (SELECT 'x'
FROM {context} cx
WHERE c.id = cx.instanceid AND cx.contextlevel=".self::LEVEL.")";
$contextdata = $DB->get_recordset_sql($sql);
foreach ($contextdata as $context) {
context::insert_context_record(self::LEVEL, $context->id, null);
}
$contextdata->close();
}
/**
* Returns sql necessary for purging of stale context instances.
*
* @return string cleanup SQL
*/
protected static function get_cleanup_sql() {
$sql = "
SELECT c.*
FROM {context} c
LEFT OUTER JOIN {course} co ON c.instanceid = co.id
WHERE co.id IS NULL AND c.contextlevel = ".self::LEVEL."
";
return $sql;
}
/**
* Rebuild context paths and depths at course context level.
*
* @param bool $force
*/
protected static function build_paths($force) {
global $DB;
if ($force || $DB->record_exists_select('context', "contextlevel = ".self::LEVEL." AND (depth = 0 OR path IS NULL)")) {
if ($force) {
$ctxemptyclause = $emptyclause = '';
} else {
$ctxemptyclause = "AND (ctx.path IS NULL OR ctx.depth = 0)";
$emptyclause = "AND ({context}.path IS NULL OR {context}.depth = 0)";
}
$base = '/'.SYSCONTEXTID;
// Standard frontpage.
$sql = "UPDATE {context}
SET depth = 2,
path = ".$DB->sql_concat("'$base/'", 'id')."
WHERE contextlevel = ".self::LEVEL."
AND EXISTS (SELECT 'x'
FROM {course} c
WHERE c.id = {context}.instanceid AND c.category = 0)
$emptyclause";
$DB->execute($sql);
// Standard courses.
$sql = "INSERT INTO {context_temp} (id, path, depth, locked)
SELECT ctx.id, ".$DB->sql_concat('pctx.path', "'/'", 'ctx.id').", pctx.depth+1, ctx.locked
FROM {context} ctx
JOIN {course} c ON (c.id = ctx.instanceid AND ctx.contextlevel = ".self::LEVEL." AND c.category <> 0)
JOIN {context} pctx ON (pctx.instanceid = c.category AND pctx.contextlevel = ".coursecat::LEVEL.")
WHERE pctx.path IS NOT NULL AND pctx.depth > 0
$ctxemptyclause";
$trans = $DB->start_delegated_transaction();
$DB->delete_records('context_temp');
$DB->execute($sql);
context::merge_context_temp_table();
$DB->delete_records('context_temp');
$trans->allow_commit();
}
}
}
+304
View File
@@ -0,0 +1,304 @@
<?php
// This file is part of Moodle - https://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <https://www.gnu.org/licenses/>.
namespace core\context;
use core\context;
use stdClass;
use coding_exception, moodle_url;
/**
* Course category context class
*
* @package core_access
* @category access
* @copyright Petr Skoda
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @since Moodle 4.2
*/
class coursecat extends context {
/** @var int numeric context level value matching legacy CONTEXT_COURSECAT */
public const LEVEL = 40;
/**
* Please use \core\context\coursecat::instance($coursecatid) if you need the instance of context.
* Alternatively if you know only the context id use \core\context::instance_by_id($contextid)
*
* @param stdClass $record
*/
protected function __construct(stdClass $record) {
parent::__construct($record);
if ($record->contextlevel != self::LEVEL) {
throw new coding_exception('Invalid $record->contextlevel in core\context\coursecat constructor.');
}
}
/**
* Returns short context name.
*
* @since Moodle 4.2
*
* @return string
*/
public static function get_short_name(): string {
return 'coursecat';
}
/**
* Returns human readable context level name.
*
* @return string the human readable context level name.
*/
public static function get_level_name() {
return get_string('category');
}
/**
* Returns human readable context identifier.
*
* @param boolean $withprefix whether to prefix the name of the context with Category
* @param boolean $short does not apply to course categories
* @param boolean $escape Whether the returned name of the context is to be HTML escaped or not.
* @return string the human readable context name.
*/
public function get_context_name($withprefix = true, $short = false, $escape = true) {
global $DB;
$name = '';
if ($category = $DB->get_record('course_categories', array('id' => $this->_instanceid))) {
if ($withprefix) {
$name = get_string('category').': ';
}
if (!$escape) {
$name .= format_string($category->name, true, array('context' => $this, 'escape' => false));
} else {
$name .= format_string($category->name, true, array('context' => $this));
}
}
return $name;
}
/**
* Returns the most relevant URL for this context.
*
* @return moodle_url
*/
public function get_url() {
return new moodle_url('/course/index.php', array('categoryid' => $this->_instanceid));
}
/**
* Returns context instance database name.
*
* @return string|null table name for all levels except system.
*/
protected static function get_instance_table(): ?string {
return 'course_categories';
}
/**
* Returns list of columns that can be used from behat
* to look up context by reference.
*
* @return array list of column names from instance table
*/
protected static function get_behat_reference_columns(): array {
return ['idnumber'];
}
/**
* Returns list of all role archetypes that are compatible
* with role assignments in context level.
* @since Moodle 4.2
*
* @return int[]
*/
protected static function get_compatible_role_archetypes(): array {
return ['manager', 'coursecreator'];
}
/**
* Returns list of all possible parent context levels.
* @since Moodle 4.2
*
* @return int[]
*/
public static function get_possible_parent_levels(): array {
return [system::LEVEL, self::LEVEL];
}
/**
* Returns array of relevant context capability records.
*
* @param string $sort
* @return array
*/
public function get_capabilities(string $sort = self::DEFAULT_CAPABILITY_SORT) {
global $DB;
$levels = \core\context_helper::get_child_levels(self::LEVEL);
$levels[] = self::LEVEL;
return $DB->get_records_list('capabilities', 'contextlevel', $levels, $sort);
}
/**
* Returns course category context instance.
*
* @param int $categoryid id from {course_categories} table
* @param int $strictness
* @return coursecat|false context instance
*/
public static function instance($categoryid, $strictness = MUST_EXIST) {
global $DB;
if ($context = context::cache_get(self::LEVEL, $categoryid)) {
return $context;
}
if (!$record = $DB->get_record('context', array('contextlevel' => self::LEVEL, 'instanceid' => $categoryid))) {
if ($category = $DB->get_record('course_categories', array('id' => $categoryid), 'id,parent', $strictness)) {
if ($category->parent) {
$parentcontext = self::instance($category->parent);
$record = context::insert_context_record(self::LEVEL, $category->id, $parentcontext->path);
} else {
$record = context::insert_context_record(self::LEVEL, $category->id, '/'.SYSCONTEXTID, 0);
}
}
}
if ($record) {
$context = new coursecat($record);
context::cache_add($context);
return $context;
}
return false;
}
/**
* Returns immediate child contexts of category and all subcategories,
* children of subcategories and courses are not returned.
*
* @return array
*/
public function get_child_contexts() {
global $DB;
if (empty($this->_path) || empty($this->_depth)) {
debugging('Can not find child contexts of context '.$this->_id.' try rebuilding of context paths');
return array();
}
$sql = "SELECT ctx.*
FROM {context} ctx
WHERE ctx.path LIKE ? AND (ctx.depth = ? OR ctx.contextlevel = ?)";
$params = array($this->_path.'/%', $this->depth + 1, self::LEVEL);
$records = $DB->get_records_sql($sql, $params);
$result = array();
foreach ($records as $record) {
$result[$record->id] = context::create_instance_from_record($record);
}
return $result;
}
/**
* Create missing context instances at course category context level
*/
protected static function create_level_instances() {
global $DB;
$sql = "SELECT ".self::LEVEL.", cc.id
FROM {course_categories} cc
WHERE NOT EXISTS (SELECT 'x'
FROM {context} cx
WHERE cc.id = cx.instanceid AND cx.contextlevel=".self::LEVEL.")";
$contextdata = $DB->get_recordset_sql($sql);
foreach ($contextdata as $context) {
context::insert_context_record(self::LEVEL, $context->id, null);
}
$contextdata->close();
}
/**
* Returns sql necessary for purging of stale context instances.
*
* @return string cleanup SQL
*/
protected static function get_cleanup_sql() {
$sql = "
SELECT c.*
FROM {context} c
LEFT OUTER JOIN {course_categories} cc ON c.instanceid = cc.id
WHERE cc.id IS NULL AND c.contextlevel = ".self::LEVEL."
";
return $sql;
}
/**
* Rebuild context paths and depths at course category context level.
*
* @param bool $force
*/
protected static function build_paths($force) {
global $DB;
if ($force || $DB->record_exists_select('context', "contextlevel = ".self::LEVEL." AND (depth = 0 OR path IS NULL)")) {
if ($force) {
$ctxemptyclause = $emptyclause = '';
} else {
$ctxemptyclause = "AND (ctx.path IS NULL OR ctx.depth = 0)";
$emptyclause = "AND ({context}.path IS NULL OR {context}.depth = 0)";
}
$base = '/'.SYSCONTEXTID;
// Normal top level categories.
$sql = "UPDATE {context}
SET depth=2,
path=".$DB->sql_concat("'$base/'", 'id')."
WHERE contextlevel=".self::LEVEL."
AND EXISTS (SELECT 'x'
FROM {course_categories} cc
WHERE cc.id = {context}.instanceid AND cc.depth=1)
$emptyclause";
$DB->execute($sql);
// Deeper categories - one query per depthlevel.
$maxdepth = $DB->get_field_sql("SELECT MAX(depth) FROM {course_categories}");
for ($n = 2; $n <= $maxdepth; $n++) {
$sql = "INSERT INTO {context_temp} (id, path, depth, locked)
SELECT ctx.id, ".$DB->sql_concat('pctx.path', "'/'", 'ctx.id').", pctx.depth+1, ctx.locked
FROM {context} ctx
JOIN {course_categories} cc ON (cc.id = ctx.instanceid AND ctx.contextlevel = ".self::LEVEL."
AND cc.depth = $n)
JOIN {context} pctx ON (pctx.instanceid = cc.parent AND pctx.contextlevel = ".self::LEVEL.")
WHERE pctx.path IS NOT NULL AND pctx.depth > 0
$ctxemptyclause";
$trans = $DB->start_delegated_transaction();
$DB->delete_records('context_temp');
$DB->execute($sql);
context::merge_context_temp_table();
$DB->delete_records('context_temp');
$trans->allow_commit();
}
}
}
}
+349
View File
@@ -0,0 +1,349 @@
<?php
// This file is part of Moodle - https://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <https://www.gnu.org/licenses/>.
namespace core\context;
use core\context;
use stdClass;
use coding_exception, moodle_url;
/**
* Course module context class
*
* @package core_access
* @category access
* @copyright Petr Skoda
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @since Moodle 4.2
*/
class module extends context {
/** @var int numeric context level value matching legacy CONTEXT_MODULE */
public const LEVEL = 70;
/**
* Please use \core\context\module::instance($cmid) if you need the instance of context.
* Alternatively if you know only the context id use \core\context::instance_by_id($contextid)
*
* @param stdClass $record
*/
protected function __construct(stdClass $record) {
parent::__construct($record);
if ($record->contextlevel != self::LEVEL) {
throw new coding_exception('Invalid $record->contextlevel in core\context\module constructor.');
}
}
/**
* Returns short context name.
*
* @since Moodle 4.2
*
* @return string
*/
public static function get_short_name(): string {
return 'module';
}
/**
* Returns human readable context level name.
*
* @return string the human readable context level name.
*/
public static function get_level_name() {
return get_string('activitymodule');
}
/**
* Returns human readable context identifier.
*
* @param boolean $withprefix whether to prefix the name of the context with the
* module name, e.g. Forum, Glossary, etc.
* @param boolean $short does not apply to module context
* @param boolean $escape Whether the returned name of the context is to be HTML escaped or not.
* @return string the human readable context name.
*/
public function get_context_name($withprefix = true, $short = false, $escape = true) {
global $DB;
$name = '';
if ($cm = $DB->get_record_sql("SELECT cm.*, md.name AS modname
FROM {course_modules} cm
JOIN {modules} md ON md.id = cm.module
WHERE cm.id = ?", array($this->_instanceid))) {
if ($mod = $DB->get_record($cm->modname, array('id' => $cm->instance))) {
if ($withprefix) {
$name = get_string('modulename', $cm->modname).': ';
}
if (!$escape) {
$name .= format_string($mod->name, true, array('context' => $this, 'escape' => false));
} else {
$name .= format_string($mod->name, true, array('context' => $this));
}
}
}
return $name;
}
/**
* Returns the most relevant URL for this context.
*
* @return moodle_url
*/
public function get_url() {
global $DB;
if ($modname = $DB->get_field_sql("SELECT md.name AS modname
FROM {course_modules} cm
JOIN {modules} md ON md.id = cm.module
WHERE cm.id = ?", array($this->_instanceid))) {
return new moodle_url('/mod/' . $modname . '/view.php', array('id' => $this->_instanceid));
}
return new moodle_url('/');
}
/**
* Returns context instance database name.
*
* @return string|null table name for all levels except system.
*/
protected static function get_instance_table(): ?string {
return 'course_modules';
}
/**
* Returns list of columns that can be used from behat
* to look up context by reference.
*
* @return array list of column names from instance table
*/
protected static function get_behat_reference_columns(): array {
return ['idnumber'];
}
/**
* Returns list of all role archetypes that are compatible
* with role assignments in context level.
* @since Moodle 4.2
*
* @return int[]
*/
protected static function get_compatible_role_archetypes(): array {
return ['editingteacher', 'teacher', 'student'];
}
/**
* Returns list of all possible parent context levels.
* @since Moodle 4.2
*
* @return int[]
*/
public static function get_possible_parent_levels(): array {
return [course::LEVEL];
}
/**
* Returns array of relevant context capability records.
*
* @param string $sort
* @return array
*/
public function get_capabilities(string $sort = self::DEFAULT_CAPABILITY_SORT) {
global $DB, $CFG;
$cm = $DB->get_record('course_modules', array('id' => $this->_instanceid));
$module = $DB->get_record('modules', array('id' => $cm->module));
$subcaps = array();
$modulepath = "{$CFG->dirroot}/mod/{$module->name}";
if (file_exists("{$modulepath}/db/subplugins.json")) {
$subplugins = (array) json_decode(file_get_contents("{$modulepath}/db/subplugins.json"))->plugintypes;
} else if (file_exists("{$modulepath}/db/subplugins.php")) {
debugging('Use of subplugins.php has been deprecated. ' .
'Please update your plugin to provide a subplugins.json file instead.',
DEBUG_DEVELOPER);
$subplugins = array(); // Should be redefined in the file.
include("{$modulepath}/db/subplugins.php");
}
if (!empty($subplugins)) {
foreach (array_keys($subplugins) as $subplugintype) {
foreach (array_keys(\core_component::get_plugin_list($subplugintype)) as $subpluginname) {
$subcaps = array_merge($subcaps, array_keys(load_capability_def($subplugintype.'_'.$subpluginname)));
}
}
}
$modfile = "{$modulepath}/lib.php";
$extracaps = array();
if (file_exists($modfile)) {
include_once($modfile);
$modfunction = $module->name.'_get_extra_capabilities';
if (function_exists($modfunction)) {
$extracaps = $modfunction();
}
}
$extracaps = array_merge($subcaps, $extracaps);
$extra = '';
list($extra, $params) = $DB->get_in_or_equal(
$extracaps, SQL_PARAMS_NAMED, 'cap0', true, '');
if (!empty($extra)) {
$extra = "OR name $extra";
}
// Fetch the list of modules, and remove this one.
$components = \core_component::get_component_list();
$componentnames = $components['mod'];
unset($componentnames["mod_{$module->name}"]);
$componentnames = array_keys($componentnames);
// Exclude all other modules.
list($notcompsql, $notcompparams) = $DB->get_in_or_equal($componentnames, SQL_PARAMS_NAMED, 'notcomp', false);
$params = array_merge($params, $notcompparams);
// Exclude other component submodules.
$i = 0;
$ignorecomponents = [];
foreach ($componentnames as $mod) {
if ($subplugins = \core_component::get_subplugins($mod)) {
foreach (array_keys($subplugins) as $subplugintype) {
$paramname = "notlike{$i}";
$ignorecomponents[] = $DB->sql_like('component', ":{$paramname}", true, true, true);
$params[$paramname] = "{$subplugintype}_%";
$i++;
}
}
}
$notlikesql = "(" . implode(' AND ', $ignorecomponents) . ")";
$sql = "SELECT *
FROM {capabilities}
WHERE (contextlevel = ".self::LEVEL."
AND component {$notcompsql}
AND {$notlikesql})
$extra
ORDER BY $sort";
return $DB->get_records_sql($sql, $params);
}
/**
* Is this context part of any course? If yes return course context.
*
* @param bool $strict true means throw exception if not found, false means return false if not found
* @return course|false context of the enclosing course, null if not found or exception
*/
public function get_course_context($strict = true) {
return $this->get_parent_context();
}
/**
* Returns module context instance.
*
* @param int $cmid id of the record from {course_modules} table; pass cmid there, NOT id in the instance column
* @param int $strictness
* @return module|false context instance
*/
public static function instance($cmid, $strictness = MUST_EXIST) {
global $DB;
if ($context = context::cache_get(self::LEVEL, $cmid)) {
return $context;
}
if (!$record = $DB->get_record('context', array('contextlevel' => self::LEVEL, 'instanceid' => $cmid))) {
if ($cm = $DB->get_record('course_modules', array('id' => $cmid), 'id,course', $strictness)) {
$parentcontext = course::instance($cm->course);
$record = context::insert_context_record(self::LEVEL, $cm->id, $parentcontext->path);
}
}
if ($record) {
$context = new module($record);
context::cache_add($context);
return $context;
}
return false;
}
/**
* Create missing context instances at module context level
*/
protected static function create_level_instances() {
global $DB;
$sql = "SELECT " . self::LEVEL . ", cm.id
FROM {course_modules} cm
WHERE NOT EXISTS (SELECT 'x'
FROM {context} cx
WHERE cm.id = cx.instanceid AND cx.contextlevel=" . self::LEVEL . ")";
$contextdata = $DB->get_recordset_sql($sql);
foreach ($contextdata as $context) {
context::insert_context_record(self::LEVEL, $context->id, null);
}
$contextdata->close();
}
/**
* Returns sql necessary for purging of stale context instances.
*
* @return string cleanup SQL
*/
protected static function get_cleanup_sql() {
$sql = "
SELECT c.*
FROM {context} c
LEFT OUTER JOIN {course_modules} cm ON c.instanceid = cm.id
WHERE cm.id IS NULL AND c.contextlevel = " . self::LEVEL . "
";
return $sql;
}
/**
* Rebuild context paths and depths at module context level.
*
* @param bool $force
*/
protected static function build_paths($force) {
global $DB;
if ($force || $DB->record_exists_select('context', "contextlevel = " . self::LEVEL . " AND (depth = 0 OR path IS NULL)")) {
if ($force) {
$ctxemptyclause = '';
} else {
$ctxemptyclause = "AND (ctx.path IS NULL OR ctx.depth = 0)";
}
$sql = "INSERT INTO {context_temp} (id, path, depth, locked)
SELECT ctx.id, ".$DB->sql_concat('pctx.path', "'/'", 'ctx.id').", pctx.depth+1, ctx.locked
FROM {context} ctx
JOIN {course_modules} cm ON (cm.id = ctx.instanceid AND ctx.contextlevel = " . self::LEVEL . ")
JOIN {context} pctx ON (pctx.instanceid = cm.course AND pctx.contextlevel = " . course::LEVEL . ")
WHERE pctx.path IS NOT NULL AND pctx.depth > 0
$ctxemptyclause";
$trans = $DB->start_delegated_transaction();
$DB->delete_records('context_temp');
$DB->execute($sql);
context::merge_context_temp_table();
$DB->delete_records('context_temp');
$trans->allow_commit();
}
}
}
+307
View File
@@ -0,0 +1,307 @@
<?php
// This file is part of Moodle - https://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <https://www.gnu.org/licenses/>.
namespace core\context;
use core\context;
use stdClass;
use coding_exception, moodle_url;
/**
* System context class
*
* @package core_access
* @category access
* @copyright Petr Skoda
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @since Moodle 4.2
*/
class system extends context {
/** @var int numeric context level value matching legacy CONTEXT_SYSTEM */
public const LEVEL = 10;
/**
* Please use \core\context\system::instance() if you need the instance of context.
*
* @param stdClass $record
*/
protected function __construct(stdClass $record) {
parent::__construct($record);
if ($record->contextlevel != self::LEVEL) {
throw new coding_exception('Invalid $record->contextlevel in core\context\system constructor.');
}
}
/**
* Returns short context name.
*
* @since Moodle 4.2
*
* @return string
*/
public static function get_short_name(): string {
return 'system';
}
/**
* Returns human readable context level name.
*
* @return string the human readable context level name.
*/
public static function get_level_name() {
return get_string('coresystem');
}
/**
* Returns human readable context identifier.
*
* @param boolean $withprefix does not apply to system context
* @param boolean $short does not apply to system context
* @param boolean $escape does not apply to system context
* @return string the human readable context name.
*/
public function get_context_name($withprefix = true, $short = false, $escape = true) {
return self::get_level_name();
}
/**
* Returns the most relevant URL for this context.
*
* @return moodle_url
*/
public function get_url() {
return new moodle_url('/');
}
/**
* Returns list of all role archetypes that are compatible
* with role assignments in context level.
* @since Moodle 4.2
*
* @return int[]
*/
protected static function get_compatible_role_archetypes(): array {
return ['manager', 'coursecreator'];
}
/**
* Returns list of all possible parent context levels.
* @since Moodle 4.2
*
* @return int[]
*/
public static function get_possible_parent_levels(): array {
return [];
}
/**
* Returns array of relevant context capability records.
*
* @param string $sort
* @return array
*/
public function get_capabilities(string $sort = self::DEFAULT_CAPABILITY_SORT) {
global $DB;
return $DB->get_records('capabilities', [], $sort);
}
/**
* Create missing context instances at system context
*/
protected static function create_level_instances() {
// Nothing to do here, the system context is created automatically in installer.
self::instance(0);
}
/**
* Returns system context instance.
*
* @param int $instanceid should be 0
* @param int $strictness
* @param bool $cache
* @return system context instance
*/
public static function instance($instanceid = 0, $strictness = MUST_EXIST, $cache = true) {
global $DB;
if ($instanceid != 0) {
debugging('context_system::instance(): invalid $id parameter detected, should be 0');
}
// SYSCONTEXTID is cached in local cache to eliminate 1 query per page.
if (defined('SYSCONTEXTID') && $cache) {
if (!isset(context::$systemcontext)) {
$record = new stdClass();
$record->id = SYSCONTEXTID;
$record->contextlevel = self::LEVEL;
$record->instanceid = 0;
$record->path = '/'.SYSCONTEXTID;
$record->depth = 1;
$record->locked = 0;
context::$systemcontext = new system($record);
}
return context::$systemcontext;
}
try {
// We ignore the strictness completely because system context must exist except during install.
$record = $DB->get_record('context', array('contextlevel' => self::LEVEL), '*', MUST_EXIST);
} catch (\dml_exception $e) {
// Table or record does not exist.
if (!during_initial_install()) {
// Do not mess with system context after install, it simply must exist.
throw $e;
}
$record = null;
}
if (!$record) {
$record = new stdClass();
$record->contextlevel = self::LEVEL;
$record->instanceid = 0;
$record->depth = 1;
$record->path = null; // Not known before insert.
$record->locked = 0;
try {
if ($DB->count_records('context')) {
// Contexts already exist, this is very weird, system must be first!!!
return null;
}
if (defined('SYSCONTEXTID')) {
// This would happen only in unittest on sites that went through weird 1.7 upgrade.
$record->id = SYSCONTEXTID;
$DB->import_record('context', $record);
$DB->get_manager()->reset_sequence('context');
} else {
$record->id = $DB->insert_record('context', $record);
}
} catch (\dml_exception $e) {
// Can not create context - table does not exist yet, sorry.
return null;
}
}
if ($record->instanceid != 0) {
// This is very weird, somebody must be messing with context table.
debugging('Invalid system context detected');
}
if ($record->depth != 1 || $record->path != '/'.$record->id) {
// Fix path if necessary, initial install or path reset.
$record->depth = 1;
$record->path = '/'.$record->id;
$DB->update_record('context', $record);
}
if (empty($record->locked)) {
$record->locked = 0;
}
if (!defined('SYSCONTEXTID')) {
define('SYSCONTEXTID', $record->id);
}
context::$systemcontext = new system($record);
return context::$systemcontext;
}
/**
* Returns all site contexts except the system context, DO NOT call on production servers!!
*
* Contexts are not cached.
*
* @return array
*/
public function get_child_contexts() {
global $DB;
debugging('Fetching of system context child courses is strongly discouraged'
. ' on production servers (it may eat all available memory)!');
// Just get all the contexts except for system level
// and hope we don't OOM in the process - don't cache.
$sql = "SELECT c.*
FROM {context} c
WHERE contextlevel > " . self::LEVEL;
$records = $DB->get_records_sql($sql);
$result = array();
foreach ($records as $record) {
$result[$record->id] = context::create_instance_from_record($record);
}
return $result;
}
/**
* Returns sql necessary for purging of stale context instances.
*
* @return string cleanup SQL
*/
protected static function get_cleanup_sql() {
$sql = "
SELECT c.*
FROM {context} c
WHERE 1=2
";
return $sql;
}
/**
* Rebuild context paths and depths at system context level.
*
* @param bool $force
*/
protected static function build_paths($force) {
global $DB;
/* note: ignore $force here, we always do full test of system context */
// Exactly one record must exist.
$record = $DB->get_record('context', array('contextlevel' => self::LEVEL), '*', MUST_EXIST);
if ($record->instanceid != 0) {
debugging('Invalid system context detected');
}
if (defined('SYSCONTEXTID') && $record->id != SYSCONTEXTID) {
debugging('Invalid SYSCONTEXTID detected');
}
if ($record->depth != 1 || $record->path != '/'.$record->id) {
// Fix path if necessary, initial install or path reset.
$record->depth = 1;
$record->path = '/'.$record->id;
$DB->update_record('context', $record);
}
}
/**
* Set whether this context has been locked or not.
*
* @param bool $locked
* @return $this
*/
public function set_locked(bool $locked) {
if ($locked) {
throw new \coding_exception('It is not possible to lock the system context');
}
return parent::set_locked($locked);
}
}
+242
View File
@@ -0,0 +1,242 @@
<?php
// This file is part of Moodle - https://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <https://www.gnu.org/licenses/>.
namespace core\context;
use core\context;
use stdClass;
use coding_exception, moodle_url;
/**
* User context class
*
* @package core_access
* @category access
* @copyright Petr Skoda
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @since Moodle 4.2
*/
class user extends context {
/** @var int numeric context level value matching legacy CONTEXT_USER */
public const LEVEL = 30;
/**
* Please use \core\context\user::instance($userid) if you need the instance of context.
* Alternatively if you know only the context id use \core\context::instance_by_id($contextid)
*
* @param stdClass $record
*/
protected function __construct(stdClass $record) {
parent::__construct($record);
if ($record->contextlevel != self::LEVEL) {
throw new coding_exception('Invalid $record->contextlevel in core\context\user constructor.');
}
}
/**
* Returns short context name.
*
* @since Moodle 4.2
*
* @return string
*/
public static function get_short_name(): string {
return 'user';
}
/**
* Returns human readable context level name.
*
* @return string the human readable context level name.
*/
public static function get_level_name() {
return get_string('user');
}
/**
* Returns human readable context identifier.
*
* @param boolean $withprefix whether to prefix the name of the context with User
* @param boolean $short does not apply to user context
* @param boolean $escape does not apply to user context
* @return string the human readable context name.
*/
public function get_context_name($withprefix = true, $short = false, $escape = true) {
global $DB;
$name = '';
if ($user = $DB->get_record('user', array('id' => $this->_instanceid, 'deleted' => 0))) {
if ($withprefix) {
$name = get_string('user').': ';
}
$name .= fullname($user);
}
return $name;
}
/**
* Returns the most relevant URL for this context.
*
* @return moodle_url
*/
public function get_url() {
global $COURSE;
if ($COURSE->id == SITEID) {
$url = new moodle_url('/user/profile.php', array('id' => $this->_instanceid));
} else {
$url = new moodle_url('/user/view.php', array('id' => $this->_instanceid, 'courseid' => $COURSE->id));
}
return $url;
}
/**
* Returns list of all possible parent context levels.
* @since Moodle 4.2
*
* @return int[]
*/
public static function get_possible_parent_levels(): array {
return [system::LEVEL];
}
/**
* Returns context instance database name.
*
* @return string|null table name for all levels except system.
*/
protected static function get_instance_table(): ?string {
return 'user';
}
/**
* Returns list of columns that can be used from behat
* to look up context by reference.
*
* @return array list of column names from instance table
*/
protected static function get_behat_reference_columns(): array {
return ['username'];
}
/**
* Returns array of relevant context capability records.
*
* @param string $sort
* @return array
*/
public function get_capabilities(string $sort = self::DEFAULT_CAPABILITY_SORT) {
global $DB;
$extracaps = array('moodle/grade:viewall');
list($extra, $params) = $DB->get_in_or_equal($extracaps, SQL_PARAMS_NAMED, 'cap');
return $DB->get_records_select('capabilities', "contextlevel = :level OR name {$extra}",
$params + ['level' => self::LEVEL], $sort);
}
/**
* Returns user context instance.
*
* @param int $userid id from {user} table
* @param int $strictness
* @return user|false context instance
*/
public static function instance($userid, $strictness = MUST_EXIST) {
global $DB;
if ($context = context::cache_get(self::LEVEL, $userid)) {
return $context;
}
if (!$record = $DB->get_record('context', array('contextlevel' => self::LEVEL, 'instanceid' => $userid))) {
if ($user = $DB->get_record('user', array('id' => $userid, 'deleted' => 0), 'id', $strictness)) {
$record = context::insert_context_record(self::LEVEL, $user->id, '/'.SYSCONTEXTID, 0);
}
}
if ($record) {
$context = new user($record);
context::cache_add($context);
return $context;
}
return false;
}
/**
* Create missing context instances at user context level
*/
protected static function create_level_instances() {
global $DB;
$sql = "SELECT " . self::LEVEL . ", u.id
FROM {user} u
WHERE u.deleted = 0
AND NOT EXISTS (SELECT 'x'
FROM {context} cx
WHERE u.id = cx.instanceid AND cx.contextlevel=" . self::LEVEL . ")";
$contextdata = $DB->get_recordset_sql($sql);
foreach ($contextdata as $context) {
context::insert_context_record(self::LEVEL, $context->id, null);
}
$contextdata->close();
}
/**
* Returns sql necessary for purging of stale context instances.
*
* @return string cleanup SQL
*/
protected static function get_cleanup_sql() {
$sql = "
SELECT c.*
FROM {context} c
LEFT OUTER JOIN {user} u ON (c.instanceid = u.id AND u.deleted = 0)
WHERE u.id IS NULL AND c.contextlevel = " . self::LEVEL . "
";
return $sql;
}
/**
* Rebuild context paths and depths at user context level.
*
* @param bool $force
*/
protected static function build_paths($force) {
global $DB;
// First update normal users.
$path = $DB->sql_concat('?', 'id');
$pathstart = '/' . SYSCONTEXTID . '/';
$params = array($pathstart);
if ($force) {
$where = "depth <> 2 OR path IS NULL OR path <> ({$path})";
$params[] = $pathstart;
} else {
$where = "depth = 0 OR path IS NULL";
}
$sql = "UPDATE {context}
SET depth = 2,
path = {$path}
WHERE contextlevel = " . self::LEVEL . "
AND ($where)";
$DB->execute($sql, $params);
}
}