Differences Between: [Versions 310 and 401] [Versions 310 and 402] [Versions 310 and 403]
1 <?php 2 // This file is part of Moodle - http://moodle.org/ 3 // 4 // Moodle is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // Moodle is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 16 17 /** 18 * Data registry business logic methods. Mostly internal stuff. 19 * 20 * All methods should be considered part of the internal tool_dataprivacy API 21 * unless something different is specified. 22 * 23 * @package tool_dataprivacy 24 * @copyright 2018 David Monllao 25 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 26 */ 27 28 namespace tool_dataprivacy; 29 30 use coding_exception; 31 use core\persistent; 32 33 defined('MOODLE_INTERNAL') || die(); 34 35 /** 36 * Data registry business logic methods. Mostly internal stuff. 37 * 38 * @copyright 2018 David Monllao 39 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 40 */ 41 class data_registry { 42 /** 43 * Returns purpose and category var names from a context class name 44 * 45 * @param string $classname The context level's class. 46 * @param string $pluginname The name of the plugin associated with the context level. 47 * @return string[] 48 */ 49 public static function var_names_from_context($classname, $pluginname = '') { 50 $pluginname = trim($pluginname); 51 if (!empty($pluginname)) { 52 $categoryvar = $classname . '_' . $pluginname . '_category'; 53 $purposevar = $classname . '_' . $pluginname . '_purpose'; 54 } else { 55 $categoryvar = $classname . '_category'; 56 $purposevar = $classname . '_purpose'; 57 } 58 return [ 59 $purposevar, 60 $categoryvar 61 ]; 62 } 63 64 /** 65 * Returns the default purpose id and category id for the provided context level. 66 * 67 * The caller code is responsible of checking that $contextlevel is an integer. 68 * 69 * @param int $contextlevel The context level. 70 * @param string $pluginname The name of the plugin associated with the context level. 71 * @return int[]|false[] 72 */ 73 public static function get_defaults($contextlevel, $pluginname = '') { 74 $classname = \context_helper::get_class_for_level($contextlevel); 75 list($purposevar, $categoryvar) = self::var_names_from_context($classname, $pluginname); 76 77 $purposeid = get_config('tool_dataprivacy', $purposevar); 78 $categoryid = get_config('tool_dataprivacy', $categoryvar); 79 80 if (!empty($pluginname)) { 81 list($purposevar, $categoryvar) = self::var_names_from_context($classname); 82 // If the plugin-level doesn't have a default purpose set, try the context level. 83 if ($purposeid == false) { 84 $purposeid = get_config('tool_dataprivacy', $purposevar); 85 } 86 87 // If the plugin-level doesn't have a default category set, try the context level. 88 if ($categoryid == false) { 89 $categoryid = get_config('tool_dataprivacy', $categoryvar); 90 } 91 } 92 93 if (empty($purposeid)) { 94 $purposeid = context_instance::NOTSET; 95 } 96 if (empty($categoryid)) { 97 $categoryid = context_instance::NOTSET; 98 } 99 100 return [$purposeid, $categoryid]; 101 } 102 103 /** 104 * Are data registry defaults set? 105 * 106 * At least the system defaults need to be set. 107 * 108 * @return bool 109 */ 110 public static function defaults_set() { 111 list($purposeid, $categoryid) = self::get_defaults(CONTEXT_SYSTEM); 112 if (empty($purposeid) || empty($categoryid)) { 113 return false; 114 } 115 return true; 116 } 117 118 /** 119 * Returns all site categories that are visible to the current user. 120 * 121 * @return \core_course_category[] 122 */ 123 public static function get_site_categories() { 124 global $DB; 125 126 if (method_exists('\core_course_category', 'get_all')) { 127 $categories = \core_course_category::get_all(['returnhidden' => true]); 128 } else { 129 // Fallback (to be removed once this gets integrated into master). 130 $ids = $DB->get_fieldset_select('course_categories', 'id', ''); 131 $categories = \core_course_category::get_many($ids); 132 } 133 134 foreach ($categories as $key => $category) { 135 if (!$category->is_uservisible()) { 136 unset($categories[$key]); 137 } 138 } 139 return $categories; 140 } 141 142 /** 143 * Returns the roles assigned to the provided level. 144 * 145 * Important to note that it returns course-level assigned roles 146 * if the provided context level is below course. 147 * 148 * @param \context $context 149 * @return array 150 */ 151 public static function get_subject_scope(\context $context) { 152 153 if ($contextcourse = $context->get_course_context(false)) { 154 // Below course level we look at module or block level roles + course-assigned roles. 155 $courseroles = get_roles_used_in_context($contextcourse, false); 156 $roles = $courseroles + get_roles_used_in_context($context, false); 157 } else { 158 // We list category + system for others (we don't work with user instances so no need to work about them). 159 $roles = get_roles_used_in_context($context); 160 } 161 162 return array_map(function($role) { 163 if ($role->name) { 164 return $role->name; 165 } else { 166 return $role->shortname; 167 } 168 }, $roles); 169 } 170 171 /** 172 * Returns the effective value given a context instance 173 * 174 * @param \context $context 175 * @param string $element 'category' or 'purpose' 176 * @param int|false $forcedvalue Use this value as if this was this context instance value. 177 * @return persistent|false It return a 'purpose' instance or a 'category' instance, depending on $element 178 */ 179 public static function get_effective_context_value(\context $context, $element, $forcedvalue = false) { 180 global $DB; 181 182 if ($element !== 'purpose' && $element !== 'category') { 183 throw new coding_exception('Only \'purpose\' and \'category\' are supported.'); 184 } 185 $fieldname = $element . 'id'; 186 187 if (!empty($forcedvalue) && ($forcedvalue == context_instance::INHERIT)) { 188 // Do not include the current context when calculating the value. 189 // This has the effect that an inheritted value is calculated. 190 $parentcontextids = $context->get_parent_context_ids(false); 191 } else if (!empty($forcedvalue) && ($forcedvalue != context_instance::NOTSET)) { 192 return self::get_element_instance($element, $forcedvalue); 193 } else { 194 // Fetch all parent contexts, including self. 195 $parentcontextids = $context->get_parent_context_ids(true); 196 } 197 list($insql, $inparams) = $DB->get_in_or_equal($parentcontextids, SQL_PARAMS_NAMED); 198 $inparams['contextmodule'] = CONTEXT_MODULE; 199 200 if ('purpose' === $element) { 201 $elementjoin = 'LEFT JOIN {tool_dataprivacy_purpose} ele ON ctxins.purposeid = ele.id'; 202 $elementfields = purpose::get_sql_fields('ele', 'ele'); 203 } else { 204 $elementjoin = 'LEFT JOIN {tool_dataprivacy_category} ele ON ctxins.categoryid = ele.id'; 205 $elementfields = category::get_sql_fields('ele', 'ele'); 206 } 207 $contextfields = \context_helper::get_preload_record_columns_sql('ctx'); 208 $fields = implode(', ', ['ctx.id', 'm.name AS modname', $contextfields, $elementfields]); 209 210 $sql = "SELECT $fields 211 FROM {context} ctx 212 LEFT JOIN {tool_dataprivacy_ctxinstance} ctxins ON ctx.id = ctxins.contextid 213 LEFT JOIN {course_modules} cm ON ctx.contextlevel = :contextmodule AND ctx.instanceid = cm.id 214 LEFT JOIN {modules} m ON m.id = cm.module 215 {$elementjoin} 216 WHERE ctx.id {$insql} 217 ORDER BY ctx.path DESC"; 218 $contextinstances = $DB->get_records_sql($sql, $inparams); 219 220 // Check whether this context is a user context, or a child of a user context. 221 // All children of a User context share the same context and cannot be set individually. 222 foreach ($contextinstances as $record) { 223 \context_helper::preload_from_record($record); 224 $parent = \context::instance_by_id($record->id, false); 225 226 if ($parent->contextlevel == CONTEXT_USER) { 227 // Use the context level value for the user. 228 return self::get_effective_contextlevel_value(CONTEXT_USER, $element); 229 } 230 } 231 232 foreach ($contextinstances as $record) { 233 $parent = \context::instance_by_id($record->id, false); 234 235 $checkcontextlevel = false; 236 if (empty($record->eleid)) { 237 $checkcontextlevel = true; 238 } 239 240 if (!empty($forcedvalue) && context_instance::NOTSET == $forcedvalue) { 241 $checkcontextlevel = true; 242 } 243 244 if ($checkcontextlevel) { 245 // Check for a value at the contextlevel 246 $forplugin = empty($record->modname) ? '' : $record->modname; 247 list($purposeid, $categoryid) = self::get_effective_default_contextlevel_purpose_and_category( 248 $parent->contextlevel, false, false, $forplugin); 249 250 $instancevalue = $$fieldname; 251 252 if (context_instance::NOTSET != $instancevalue && context_instance::INHERIT != $instancevalue) { 253 // There is an actual value. Return it. 254 return self::get_element_instance($element, $instancevalue); 255 } 256 } else { 257 $elementclass = "\\tool_dataprivacy\\{$element}"; 258 $instance = new $elementclass(null, $elementclass::extract_record($record, 'ele')); 259 $instance->validate(); 260 261 return $instance; 262 } 263 } 264 265 throw new coding_exception('Something went wrong, system defaults should be set and we should already have a value.'); 266 } 267 268 /** 269 * Returns the effective value for a context level. 270 * 271 * Note that this is different from the effective default context level 272 * (see get_effective_default_contextlevel_purpose_and_category) as this is returning 273 * the value set in the data registry, not in the defaults page. 274 * 275 * @param int $contextlevel 276 * @param string $element 'category' or 'purpose' 277 * @return \tool_dataprivacy\purpose|false 278 */ 279 public static function get_effective_contextlevel_value($contextlevel, $element) { 280 if ($element !== 'purpose' && $element !== 'category') { 281 throw new coding_exception('Only \'purpose\' and \'category\' are supported.'); 282 } 283 $fieldname = $element . 'id'; 284 285 if ($contextlevel != CONTEXT_SYSTEM && $contextlevel != CONTEXT_USER) { 286 throw new \coding_exception('Only context_system and context_user values can be retrieved, no other context levels ' . 287 'have a purpose or a category.'); 288 } 289 290 list($purposeid, $categoryid) = self::get_effective_default_contextlevel_purpose_and_category($contextlevel); 291 292 // Note: The $$fieldname points to either $purposeid, or $categoryid. 293 if (context_instance::NOTSET != $$fieldname && context_instance::INHERIT != $$fieldname) { 294 // There is a specific value set. 295 return self::get_element_instance($element, $$fieldname); 296 } 297 298 throw new coding_exception('Something went wrong, system defaults should be set and we should already have a value.'); 299 } 300 301 /** 302 * Returns the effective default purpose and category for a context level. 303 * 304 * @param int $contextlevel 305 * @param int|bool $forcedpurposevalue Use this value as if this was this context level purpose. 306 * @param int|bool $forcedcategoryvalue Use this value as if this was this context level category. 307 * @param string $component The name of the component to check. 308 * @return int[] 309 */ 310 public static function get_effective_default_contextlevel_purpose_and_category($contextlevel, $forcedpurposevalue = false, 311 $forcedcategoryvalue = false, $component = '') { 312 // Get the defaults for this context level. 313 list($purposeid, $categoryid) = self::get_defaults($contextlevel, $component); 314 315 // Honour forced values. 316 if ($forcedpurposevalue) { 317 $purposeid = $forcedpurposevalue; 318 } 319 if ($forcedcategoryvalue) { 320 $categoryid = $forcedcategoryvalue; 321 } 322 323 if ($contextlevel == CONTEXT_USER) { 324 // Only user context levels inherit from a parent context level. 325 list($parentpurposeid, $parentcategoryid) = self::get_defaults(CONTEXT_SYSTEM); 326 327 if (context_instance::INHERIT == $purposeid || context_instance::NOTSET == $purposeid) { 328 $purposeid = (int)$parentpurposeid; 329 } 330 331 if (context_instance::INHERIT == $categoryid || context_instance::NOTSET == $categoryid) { 332 $categoryid = $parentcategoryid; 333 } 334 } 335 336 return [$purposeid, $categoryid]; 337 } 338 339 /** 340 * Returns an instance of the provided element. 341 * 342 * @throws \coding_exception 343 * @param string $element The element name 'purpose' or 'category' 344 * @param int $id The element id 345 * @return \core\persistent 346 */ 347 private static function get_element_instance($element, $id) { 348 if ($element !== 'purpose' && $element !== 'category') { 349 throw new coding_exception('No other elements than purpose and category are allowed'); 350 } 351 352 $classname = '\tool_dataprivacy\\' . $element; 353 return new $classname($id); 354 } 355 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body