2 // This file is part of Moodle - http://moodle.org/
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.
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.
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/>.
18 * Data registry business logic methods. Mostly internal stuff.
20 * All methods should be considered part of the internal tool_dataprivacy API
21 * unless something different is specified.
23 * @package tool_dataprivacy
24 * @copyright 2018 David Monllao
25 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
28 namespace tool_dataprivacy
;
31 use tool_dataprivacy\purpose
;
32 use tool_dataprivacy\category
;
33 use tool_dataprivacy\contextlevel
;
34 use tool_dataprivacy\context_instance
;
36 defined('MOODLE_INTERNAL') ||
die();
38 require_once($CFG->libdir
. '/coursecatlib.php');
41 * Data registry business logic methods. Mostly internal stuff.
43 * @copyright 2018 David Monllao
44 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
49 * @var array Inheritance between context levels.
51 private static $contextlevelinheritance = [
52 CONTEXT_USER
=> [CONTEXT_SYSTEM
],
53 CONTEXT_COURSECAT
=> [CONTEXT_SYSTEM
],
54 CONTEXT_COURSE
=> [CONTEXT_COURSECAT
, CONTEXT_SYSTEM
],
55 CONTEXT_MODULE
=> [CONTEXT_COURSE
, CONTEXT_COURSECAT
, CONTEXT_SYSTEM
],
56 CONTEXT_BLOCK
=> [CONTEXT_COURSE
, CONTEXT_COURSECAT
, CONTEXT_SYSTEM
],
60 * Returns purpose and category var names from a context class name
63 * @param string $classname
66 public static function var_names_from_context($classname) {
68 $classname . '_purpose',
69 $classname . '_category',
74 * Returns the default purpose id and category id for the provided context level.
76 * The caller code is responsible of checking that $contextlevel is an integer.
79 * @param int $contextlevel
82 public static function get_defaults($contextlevel) {
84 $classname = \context_helper
::get_class_for_level($contextlevel);
85 list($purposevar, $categoryvar) = self
::var_names_from_context($classname);
87 $purposeid = get_config('tool_dataprivacy', $purposevar);
88 $categoryid = get_config('tool_dataprivacy', $categoryvar);
90 if (empty($purposeid)) {
93 if (empty($categoryid)) {
97 return [$purposeid, $categoryid];
101 * Are data registry defaults set?
103 * At least the system defaults need to be set.
108 public static function defaults_set() {
109 list($purposeid, $categoryid) = self
::get_defaults(CONTEXT_SYSTEM
);
110 if (empty($purposeid) ||
empty($categoryid)) {
117 * Returns all site categories that are visible to the current user.
120 * @return \coursecat[]
122 public static function get_site_categories() {
125 if (method_exists('\coursecat', 'get_all')) {
126 $categories = \coursecat
::get_all(['returnhidden' => true]);
128 // Fallback (to be removed once this gets integrated into master).
129 $ids = $DB->get_fieldset_select('course_categories', 'id', '');
130 $categories = \coursecat
::get_many($ids);
133 foreach ($categories as $key => $category) {
134 if (!$category->is_uservisible()) {
135 unset($categories[$key]);
142 * Returns the roles assigned to the provided level.
144 * Important to note that it returns course-level assigned roles
145 * if the provided context level is below course.
148 * @param \context $context
151 public static function get_subject_scope(\context
$context) {
153 if ($contextcourse = $context->get_course_context(false)) {
154 // Below course level we only look at course-assigned roles.
155 $roles = get_user_roles($contextcourse, 0, false);
157 $roles = get_user_roles($context, 0, false);
160 return array_map(function($role) {
164 return $role->shortname
;
170 * Returns the effective value given a context instance
173 * @param \context $context
174 * @param string $element 'category' or 'purpose'
175 * @param int|false $forcedvalue Use this value as if this was this context instance value.
176 * @return persistent|false It return a 'purpose' instance or a 'category' instance, depending on $element
178 public static function get_effective_context_value(\context
$context, $element, $forcedvalue=false) {
180 if ($element !== 'purpose' && $element !== 'category') {
181 throw new coding_exception('Only \'purpose\' and \'category\' are supported.');
183 $fieldname = $element . 'id';
185 if ($forcedvalue === false) {
186 $instance = context_instance
::get_record_by_contextid($context->id
, false);
189 // If the instance does not have a value defaults to not set, so we grab the context level default as its value.
190 $instancevalue = context_instance
::NOTSET
;
192 $instancevalue = $instance->get($fieldname);
195 $instancevalue = $forcedvalue;
199 if ($instancevalue == context_instance
::NOTSET
) {
201 // The effective value varies depending on the context level.
202 if ($context->contextlevel
== CONTEXT_USER
) {
203 // Use the context level value as we don't allow people to set specific instances values.
204 return self
::get_effective_contextlevel_value($context->contextlevel
, $element);
206 // Use the default context level value.
207 list($purposeid, $categoryid) = self
::get_effective_default_contextlevel_purpose_and_category(
208 $context->contextlevel
210 return self
::get_element_instance($element, $
$fieldname);
214 // Specific value for this context instance.
215 if ($instancevalue != context_instance
::INHERIT
) {
216 return self
::get_element_instance($element, $instancevalue);
219 // This context is using inherited so let's return the parent effective value.
220 $parentcontext = $context->get_parent_context();
221 if (!$parentcontext) {
225 // The forced value should not be transmitted to parent contexts.
226 return self
::get_effective_context_value($parentcontext, $element);
230 * Returns the effective value for a context level.
232 * Note that this is different from the effective default context level
233 * (see get_effective_default_contextlevel_purpose_and_category) as this is returning
234 * the value set in the data registry, not in the defaults page.
236 * @param int $contextlevel
237 * @param string $element 'category' or 'purpose'
238 * @param int $forcedvalue Use this value as if this was this context level purpose.
239 * @return \tool_dataprivacy\purpose|false
241 public static function get_effective_contextlevel_value($contextlevel, $element, $forcedvalue = false) {
243 if ($element !== 'purpose' && $element !== 'category') {
244 throw new coding_exception('Only \'purpose\' and \'category\' are supported.');
246 $fieldname = $element . 'id';
248 if ($contextlevel != CONTEXT_SYSTEM
&& $contextlevel != CONTEXT_USER
) {
249 throw new \
coding_exception('Only context_system and context_user values can be retrieved, no other context levels ' .
250 'have a purpose or a category.');
253 if ($forcedvalue === false) {
254 $instance = contextlevel
::get_record_by_contextlevel($contextlevel, false);
256 // If the context level does not have a value defaults to not set, so we grab the context level default as
258 $instancevalue = context_instance
::NOTSET
;
260 $instancevalue = $instance->get($fieldname);
263 $instancevalue = $forcedvalue;
266 // Not set -> Use the default context level value.
267 if ($instancevalue == context_instance
::NOTSET
) {
268 list($purposeid, $categoryid) = self
::get_effective_default_contextlevel_purpose_and_category($contextlevel);
269 return self
::get_element_instance($element, $
$fieldname);
272 // Specific value for this context instance.
273 if ($instancevalue != context_instance
::INHERIT
) {
274 return self
::get_element_instance($element, $instancevalue);
277 if ($contextlevel == CONTEXT_SYSTEM
) {
278 throw new coding_exception('Something went wrong, system defaults should be set and we should already have a value.');
281 // If we reach this point is that we are inheriting so get the parent context level and repeat.
282 $parentcontextlevel = reset(self
::$contextlevelinheritance[$contextlevel]);
284 // Forced value are intentionally not passed as the force value should only affect the immediate context level.
285 return self
::get_effective_contextlevel_value($parentcontextlevel, $element);
289 * Returns the effective default purpose and category for a context level.
291 * @param int $contextlevel
292 * @param int $forcedpurposevalue Use this value as if this was this context level purpose.
293 * @param int $forcedcategoryvalue Use this value as if this was this context level category.
296 public static function get_effective_default_contextlevel_purpose_and_category($contextlevel, $forcedpurposevalue = false, $forcedcategoryvalue = false) {
298 list($purposeid, $categoryid) = self
::get_defaults($contextlevel);
300 // Honour forced values.
301 if ($forcedpurposevalue) {
302 $purposeid = $forcedpurposevalue;
304 if ($forcedcategoryvalue) {
305 $categoryid = $forcedcategoryvalue;
308 // Not set == INHERIT for defaults.
309 if ($purposeid == context_instance
::INHERIT ||
$purposeid == context_instance
::NOTSET
) {
312 if ($categoryid == context_instance
::INHERIT ||
$categoryid == context_instance
::NOTSET
) {
316 if ($contextlevel != CONTEXT_SYSTEM
&& ($purposeid === false ||
$categoryid === false)) {
317 foreach (self
::$contextlevelinheritance[$contextlevel] as $parent) {
319 list($parentpurposeid, $parentcategoryid) = self
::get_defaults($parent);
320 // Not set == INHERIT for defaults.
321 if ($parentpurposeid == context_instance
::INHERIT ||
$parentpurposeid == context_instance
::NOTSET
) {
322 $parentpurposeid = false;
324 if ($parentcategoryid == context_instance
::INHERIT ||
$parentcategoryid == context_instance
::NOTSET
) {
325 $parentcategoryid = false;
328 if ($purposeid === false && $parentpurposeid) {
329 $purposeid = $parentpurposeid;
332 if ($categoryid === false && $parentcategoryid) {
333 $categoryid = $parentcategoryid;
338 // They may still be false, but we return anyway.
339 return [$purposeid, $categoryid];
343 * Returns an instance of the provided element.
345 * @throws \coding_exception
346 * @param string $element The element name 'purpose' or 'category'
347 * @param int $id The element id
348 * @return \core\persistent
350 private static function get_element_instance($element, $id) {
352 if ($element !== 'purpose' && $element !== 'category') {
353 throw new coding_exception('No other elements than purpose and category are allowed');
356 $classname = '\tool_dataprivacy\\' . $element;
357 return new $classname($id);