Differences Between: [Versions 310 and 403] [Versions 311 and 403] [Versions 39 and 403] [Versions 400 and 403] [Versions 401 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 // Unfortunately authors of privacy API did not expect that we would be 51 // on day fixing auto-loading of context classes. 52 // The best way would have been probably level numbers at the end of vars, 53 // but it is probably too late to fix it. 54 $classname = preg_replace('/^[a-z0-9_]+\\\\context\\\\/', 'context_', $classname); 55 56 $pluginname = trim($pluginname ?? ''); 57 if (!empty($pluginname)) { 58 $categoryvar = $classname . '_' . $pluginname . '_category'; 59 $purposevar = $classname . '_' . $pluginname . '_purpose'; 60 } else { 61 $categoryvar = $classname . '_category'; 62 $purposevar = $classname . '_purpose'; 63 } 64 return [ 65 $purposevar, 66 $categoryvar 67 ]; 68 } 69 70 /** 71 * Returns the default purpose id and category id for the provided context level. 72 * 73 * The caller code is responsible of checking that $contextlevel is an integer. 74 * 75 * @param int $contextlevel The context level. 76 * @param string $pluginname The name of the plugin associated with the context level. 77 * @return int[]|false[] 78 */ 79 public static function get_defaults($contextlevel, $pluginname = '') { 80 $classname = \context_helper::get_class_for_level($contextlevel); 81 list($purposevar, $categoryvar) = self::var_names_from_context($classname, $pluginname); 82 83 $purposeid = get_config('tool_dataprivacy', $purposevar); 84 $categoryid = get_config('tool_dataprivacy', $categoryvar); 85 86 if (!empty($pluginname)) { 87 list($purposevar, $categoryvar) = self::var_names_from_context($classname); 88 // If the plugin-level doesn't have a default purpose set, try the context level. 89 if ($purposeid == false) { 90 $purposeid = get_config('tool_dataprivacy', $purposevar); 91 } 92 93 // If the plugin-level doesn't have a default category set, try the context level. 94 if ($categoryid == false) { 95 $categoryid = get_config('tool_dataprivacy', $categoryvar); 96 } 97 } 98 99 if (empty($purposeid)) { 100 $purposeid = context_instance::NOTSET; 101 } 102 if (empty($categoryid)) { 103 $categoryid = context_instance::NOTSET; 104 } 105 106 return [$purposeid, $categoryid]; 107 } 108 109 /** 110 * Are data registry defaults set? 111 * 112 * At least the system defaults need to be set. 113 * 114 * @return bool 115 */ 116 public static function defaults_set() { 117 list($purposeid, $categoryid) = self::get_defaults(CONTEXT_SYSTEM); 118 if (empty($purposeid) || empty($categoryid)) { 119 return false; 120 } 121 return true; 122 } 123 124 /** 125 * Returns all site categories that are visible to the current user. 126 * 127 * @return \core_course_category[] 128 */ 129 public static function get_site_categories() { 130 global $DB; 131 132 if (method_exists('\core_course_category', 'get_all')) { 133 $categories = \core_course_category::get_all(['returnhidden' => true]); 134 } else { 135 // Fallback (to be removed once this gets integrated into master). 136 $ids = $DB->get_fieldset_select('course_categories', 'id', ''); 137 $categories = \core_course_category::get_many($ids); 138 } 139 140 foreach ($categories as $key => $category) { 141 if (!$category->is_uservisible()) { 142 unset($categories[$key]); 143 } 144 } 145 return $categories; 146 } 147 148 /** 149 * Returns the roles assigned to the provided level. 150 * 151 * Important to note that it returns course-level assigned roles 152 * if the provided context level is below course. 153 * 154 * @param \context $context 155 * @return array 156 */ 157 public static function get_subject_scope(\context $context) { 158 159 if ($contextcourse = $context->get_course_context(false)) { 160 // Below course level we look at module or block level roles + course-assigned roles. 161 $courseroles = get_roles_used_in_context($contextcourse, false); 162 $roles = $courseroles + get_roles_used_in_context($context, false); 163 } else { 164 // We list category + system for others (we don't work with user instances so no need to work about them). 165 $roles = get_roles_used_in_context($context); 166 } 167 168 return array_map(function($role) { 169 if ($role->name) { 170 return $role->name; 171 } else { 172 return $role->shortname; 173 } 174 }, $roles); 175 } 176 177 /** 178 * Returns the effective value given a context instance 179 * 180 * @param \context $context 181 * @param string $element 'category' or 'purpose' 182 * @param int|false $forcedvalue Use this value as if this was this context instance value. 183 * @return persistent|false It return a 'purpose' instance or a 'category' instance, depending on $element 184 */ 185 public static function get_effective_context_value(\context $context, $element, $forcedvalue = false) { 186 global $DB; 187 188 if ($element !== 'purpose' && $element !== 'category') { 189 throw new coding_exception('Only \'purpose\' and \'category\' are supported.'); 190 } 191 $fieldname = $element . 'id'; 192 193 if (!empty($forcedvalue) && ($forcedvalue == context_instance::INHERIT)) { 194 // Do not include the current context when calculating the value. 195 // This has the effect that an inheritted value is calculated. 196 $parentcontextids = $context->get_parent_context_ids(false); 197 } else if (!empty($forcedvalue) && ($forcedvalue != context_instance::NOTSET)) { 198 return self::get_element_instance($element, $forcedvalue); 199 } else { 200 // Fetch all parent contexts, including self. 201 $parentcontextids = $context->get_parent_context_ids(true); 202 } 203 list($insql, $inparams) = $DB->get_in_or_equal($parentcontextids, SQL_PARAMS_NAMED); 204 $inparams['contextmodule'] = CONTEXT_MODULE; 205 206 if ('purpose' === $element) { 207 $elementjoin = 'LEFT JOIN {tool_dataprivacy_purpose} ele ON ctxins.purposeid = ele.id'; 208 $elementfields = purpose::get_sql_fields('ele', 'ele'); 209 } else { 210 $elementjoin = 'LEFT JOIN {tool_dataprivacy_category} ele ON ctxins.categoryid = ele.id'; 211 $elementfields = category::get_sql_fields('ele', 'ele'); 212 } 213 $contextfields = \context_helper::get_preload_record_columns_sql('ctx'); 214 $fields = implode(', ', ['ctx.id', 'm.name AS modname', $contextfields, $elementfields]); 215 216 $sql = "SELECT $fields 217 FROM {context} ctx 218 LEFT JOIN {tool_dataprivacy_ctxinstance} ctxins ON ctx.id = ctxins.contextid 219 LEFT JOIN {course_modules} cm ON ctx.contextlevel = :contextmodule AND ctx.instanceid = cm.id 220 LEFT JOIN {modules} m ON m.id = cm.module 221 {$elementjoin} 222 WHERE ctx.id {$insql} 223 ORDER BY ctx.path DESC"; 224 $contextinstances = $DB->get_records_sql($sql, $inparams); 225 226 // Check whether this context is a user context, or a child of a user context. 227 // All children of a User context share the same context and cannot be set individually. 228 foreach ($contextinstances as $record) { 229 \context_helper::preload_from_record($record); 230 $parent = \context::instance_by_id($record->id, false); 231 232 if ($parent->contextlevel == CONTEXT_USER) { 233 // Use the context level value for the user. 234 return self::get_effective_contextlevel_value(CONTEXT_USER, $element); 235 } 236 } 237 238 foreach ($contextinstances as $record) { 239 $parent = \context::instance_by_id($record->id, false); 240 241 $checkcontextlevel = false; 242 if (empty($record->eleid)) { 243 $checkcontextlevel = true; 244 } 245 246 if (!empty($forcedvalue) && context_instance::NOTSET == $forcedvalue) { 247 $checkcontextlevel = true; 248 } 249 250 if ($checkcontextlevel) { 251 // Check for a value at the contextlevel 252 $forplugin = empty($record->modname) ? '' : $record->modname; 253 list($purposeid, $categoryid) = self::get_effective_default_contextlevel_purpose_and_category( 254 $parent->contextlevel, false, false, $forplugin); 255 256 $instancevalue = $$fieldname; 257 258 if (context_instance::NOTSET != $instancevalue && context_instance::INHERIT != $instancevalue) { 259 // There is an actual value. Return it. 260 return self::get_element_instance($element, $instancevalue); 261 } 262 } else { 263 $elementclass = "\\tool_dataprivacy\\{$element}"; 264 $instance = new $elementclass(null, $elementclass::extract_record($record, 'ele')); 265 $instance->validate(); 266 267 return $instance; 268 } 269 } 270 271 throw new coding_exception('Something went wrong, system defaults should be set and we should already have a value.'); 272 } 273 274 /** 275 * Returns the effective value for a context level. 276 * 277 * Note that this is different from the effective default context level 278 * (see get_effective_default_contextlevel_purpose_and_category) as this is returning 279 * the value set in the data registry, not in the defaults page. 280 * 281 * @param int $contextlevel 282 * @param string $element 'category' or 'purpose' 283 * @return \tool_dataprivacy\purpose|false 284 */ 285 public static function get_effective_contextlevel_value($contextlevel, $element) { 286 if ($element !== 'purpose' && $element !== 'category') { 287 throw new coding_exception('Only \'purpose\' and \'category\' are supported.'); 288 } 289 $fieldname = $element . 'id'; 290 291 if ($contextlevel != CONTEXT_SYSTEM && $contextlevel != CONTEXT_USER) { 292 throw new \coding_exception('Only context_system and context_user values can be retrieved, no other context levels ' . 293 'have a purpose or a category.'); 294 } 295 296 list($purposeid, $categoryid) = self::get_effective_default_contextlevel_purpose_and_category($contextlevel); 297 298 // Note: The $$fieldname points to either $purposeid, or $categoryid. 299 if (context_instance::NOTSET != $$fieldname && context_instance::INHERIT != $$fieldname) { 300 // There is a specific value set. 301 return self::get_element_instance($element, $$fieldname); 302 } 303 304 throw new coding_exception('Something went wrong, system defaults should be set and we should already have a value.'); 305 } 306 307 /** 308 * Returns the effective default purpose and category for a context level. 309 * 310 * @param int $contextlevel 311 * @param int|bool $forcedpurposevalue Use this value as if this was this context level purpose. 312 * @param int|bool $forcedcategoryvalue Use this value as if this was this context level category. 313 * @param string $component The name of the component to check. 314 * @return int[] 315 */ 316 public static function get_effective_default_contextlevel_purpose_and_category($contextlevel, $forcedpurposevalue = false, 317 $forcedcategoryvalue = false, $component = '') { 318 // Get the defaults for this context level. 319 list($purposeid, $categoryid) = self::get_defaults($contextlevel, $component); 320 321 // Honour forced values. 322 if ($forcedpurposevalue) { 323 $purposeid = $forcedpurposevalue; 324 } 325 if ($forcedcategoryvalue) { 326 $categoryid = $forcedcategoryvalue; 327 } 328 329 if ($contextlevel == CONTEXT_USER) { 330 // Only user context levels inherit from a parent context level. 331 list($parentpurposeid, $parentcategoryid) = self::get_defaults(CONTEXT_SYSTEM); 332 333 if (context_instance::INHERIT == $purposeid || context_instance::NOTSET == $purposeid) { 334 $purposeid = (int)$parentpurposeid; 335 } 336 337 if (context_instance::INHERIT == $categoryid || context_instance::NOTSET == $categoryid) { 338 $categoryid = $parentcategoryid; 339 } 340 } 341 342 return [$purposeid, $categoryid]; 343 } 344 345 /** 346 * Returns an instance of the provided element. 347 * 348 * @throws \coding_exception 349 * @param string $element The element name 'purpose' or 'category' 350 * @param int $id The element id 351 * @return \core\persistent 352 */ 353 private static function get_element_instance($element, $id) { 354 if ($element !== 'purpose' && $element !== 'category') { 355 throw new coding_exception('No other elements than purpose and category are allowed'); 356 } 357 358 $classname = '\tool_dataprivacy\\' . $element; 359 return new $classname($id); 360 } 361 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body