MDL-64506 templates: Move BS2 btns' to BS4 btns'
[moodle.git] / admin / tool / dataprivacy / classes / output / data_registry_page.php
CommitLineData
5efc1f9e
DM
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 renderable.
19 *
20 * @package tool_dataprivacy
21 * @copyright 2018 David Monllao
22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23 */
24namespace tool_dataprivacy\output;
25defined('MOODLE_INTERNAL') || die();
26
27use renderable;
28use renderer_base;
29use stdClass;
30use templatable;
31use tool_dataprivacy\data_registry;
32
5efc1f9e
DM
33require_once($CFG->dirroot . '/' . $CFG->admin . '/tool/dataprivacy/lib.php');
34require_once($CFG->libdir . '/blocklib.php');
35
36/**
37 * Class containing the data registry renderable
38 *
39 * @copyright 2018 David Monllao
40 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
41 */
42class data_registry_page implements renderable, templatable {
43
44 /**
45 * @var int
46 */
47 private $defaultcontextlevel;
48
49 /**
50 * @var int
51 */
52 private $defaultcontextid;
53
54 /**
55 * Constructor.
56 *
57 * @param int $defaultcontextlevel
58 * @param int $defaultcontextid
59 * @return null
60 */
61 public function __construct($defaultcontextlevel = false, $defaultcontextid = false) {
62 $this->defaultcontextlevel = $defaultcontextlevel;
63 $this->defaultcontextid = $defaultcontextid;
64 }
65
66 /**
67 * Export this data so it can be used as the context for a mustache template.
68 *
69 * @param renderer_base $output
70 * @return stdClass
71 */
72 public function export_for_template(renderer_base $output) {
73 global $PAGE;
74
75 $params = [\context_system::instance()->id, $this->defaultcontextlevel, $this->defaultcontextid];
76 $PAGE->requires->js_call_amd('tool_dataprivacy/data_registry', 'init', $params);
77
78 $data = new stdClass();
79 $defaultsbutton = new \action_link(
80 new \moodle_url('/admin/tool/dataprivacy/defaults.php'),
81 get_string('setdefaults', 'tool_dataprivacy'),
82 null,
29551c4b 83 ['class' => 'btn btn-primary']
5efc1f9e
DM
84 );
85 $data->defaultsbutton = $defaultsbutton->export_for_template($output);
86
87 $actionmenu = new \action_menu();
29551c4b 88 $actionmenu->set_menu_trigger(get_string('edit'), 'btn btn-primary');
5efc1f9e
DM
89 $actionmenu->set_owner_selector('dataregistry-actions');
90 $actionmenu->set_alignment(\action_menu::TL, \action_menu::BL);
91
92 $url = new \moodle_url('/admin/tool/dataprivacy/categories.php');
93 $categories = new \action_menu_link_secondary($url, null, get_string('categories', 'tool_dataprivacy'));
94 $actionmenu->add($categories);
95
96 $url = new \moodle_url('/admin/tool/dataprivacy/purposes.php');
97 $purposes = new \action_menu_link_secondary($url, null, get_string('purposes', 'tool_dataprivacy'));
98 $actionmenu->add($purposes);
99
100 $data->actions = $actionmenu->export_for_template($output);
101
102 if (!data_registry::defaults_set()) {
4023f84b
DM
103 $data->info = (object)[
104 'message' => get_string('dataregistryinfo', 'tool_dataprivacy'),
105 'announce' => 1
106 ];
5efc1f9e
DM
107 $data->nosystemdefaults = (object)[
108 'message' => get_string('nosystemdefaults', 'tool_dataprivacy'),
109 'announce' => 1
110 ];
111 }
112
113 $data->tree = $this->get_default_tree_structure();
114
115 return $data;
116 }
117
118 /**
119 * Returns the tree default structure.
120 *
121 * @return array
122 */
123 private function get_default_tree_structure() {
124
125 $frontpage = \context_course::instance(SITEID);
126
127 $categorybranches = $this->get_all_category_branches();
128
129 $elements = [
130 'text' => get_string('contextlevelname' . CONTEXT_SYSTEM, 'tool_dataprivacy'),
131 'contextlevel' => CONTEXT_SYSTEM,
132 'branches' => [
133 [
134 'text' => get_string('user'),
135 'contextlevel' => CONTEXT_USER,
136 ], [
4023f84b 137 'text' => get_string('categories'),
5efc1f9e
DM
138 'branches' => $categorybranches,
139 'expandelement' => 'category',
140 ], [
141 'text' => get_string('frontpagecourse', 'tool_dataprivacy'),
142 'contextid' => $frontpage->id,
143 'branches' => [
144 [
145 'text' => get_string('activitiesandresources', 'tool_dataprivacy'),
146 'expandcontextid' => $frontpage->id,
147 'expandelement' => 'module',
148 'expanded' => 0,
149 ], [
150 'text' => get_string('blocks'),
151 'expandcontextid' => $frontpage->id,
152 'expandelement' => 'block',
153 'expanded' => 0,
154 ],
155 ]
156 ]
157 ]
158 ];
159
160 // Returned as an array to follow a common array format.
161 return [self::complete($elements, $this->defaultcontextlevel, $this->defaultcontextid)];
162 }
163
164 /**
165 * Returns the hierarchy of system course categories.
166 *
167 * @return array
168 */
169 private function get_all_category_branches() {
170
171 $categories = data_registry::get_site_categories();
172
173 $categoriesbranch = [];
174 while (count($categories) > 0) {
175 foreach ($categories as $key => $category) {
176
177 $context = \context_coursecat::instance($category->id);
178 $newnode = [
179 'text' => shorten_text(format_string($category->name, true, ['context' => $context])),
180 'categoryid' => $category->id,
181 'contextid' => $context->id,
182 ];
183 if ($category->coursecount > 0) {
184 $newnode['branches'] = [
185 [
186 'text' => get_string('courses'),
187 'expandcontextid' => $context->id,
188 'expandelement' => 'course',
189 'expanded' => 0,
190 ]
191 ];
192 }
193
194 $added = false;
195 if ($category->parent == 0) {
196 // New categories root-level node.
197 $categoriesbranch[] = $newnode;
198 $added = true;
199
200 } else {
201 // Add the new node under the appropriate parent.
202 if ($this->add_to_parent_category_branch($category, $newnode, $categoriesbranch)) {
203 $added = true;
204 }
205 }
206
207 if ($added) {
208 unset($categories[$key]);
209 }
210 }
211 }
212
213 return $categoriesbranch;
214 }
215
216 /**
217 * Gets the courses branch for the provided category.
218 *
219 * @param \context $catcontext
220 * @return array
221 */
222 public static function get_courses_branch(\context $catcontext) {
223
224 if ($catcontext->contextlevel !== CONTEXT_COURSECAT) {
225 throw new \coding_exception('A course category context should be provided');
226 }
227
442f12f8 228 $coursecat = \core_course_category::get($catcontext->instanceid);
5efc1f9e
DM
229 $courses = $coursecat->get_courses();
230
231 $branches = [];
232
233 foreach ($courses as $course) {
234
235 $coursecontext = \context_course::instance($course->id);
236
237 if (!$course->visible && !has_capability('moodle/course:viewhiddencourses', $coursecontext)) {
238 continue;
239 }
240
241 $coursenode = [
242 'text' => shorten_text(format_string($course->shortname, true, ['context' => $coursecontext])),
243 'contextid' => $coursecontext->id,
244 'branches' => [
245 [
246 'text' => get_string('activitiesandresources', 'tool_dataprivacy'),
247 'expandcontextid' => $coursecontext->id,
248 'expandelement' => 'module',
249 'expanded' => 0,
250 ], [
251 'text' => get_string('blocks'),
252 'expandcontextid' => $coursecontext->id,
253 'expandelement' => 'block',
254 'expanded' => 0,
255 ],
256 ]
257 ];
258 $branches[] = self::complete($coursenode);
259 }
260
261 return $branches;
262 }
263
264 /**
265 * Gets the modules branch for the provided course.
266 *
267 * @param \context $coursecontext
268 * @return array
269 */
270 public static function get_modules_branch(\context $coursecontext) {
271
272 if ($coursecontext->contextlevel !== CONTEXT_COURSE) {
273 throw new \coding_exception('A course context should be provided');
274 }
275
276 $branches = [];
277
278 // Using the current user.
279 $modinfo = get_fast_modinfo($coursecontext->instanceid);
280 foreach ($modinfo->get_instances() as $moduletype => $instances) {
281 foreach ($instances as $cm) {
282
283 if (!$cm->uservisible) {
284 continue;
285 }
286
287 $a = (object)[
288 'instancename' => shorten_text($cm->get_formatted_name()),
289 'modulename' => get_string('pluginname', 'mod_' . $moduletype),
290 ];
291
292 $text = get_string('moduleinstancename', 'tool_dataprivacy', $a);
293 $branches[] = self::complete([
294 'text' => $text,
295 'contextid' => $cm->context->id,
296 ]);
297 }
298 }
299
300 return $branches;
301 }
302
303 /**
304 * Gets the blocks branch for the provided course.
305 *
306 * @param \context $coursecontext
307 * @return null
308 */
309 public static function get_blocks_branch(\context $coursecontext) {
310 global $DB;
311
312 if ($coursecontext->contextlevel !== CONTEXT_COURSE) {
313 throw new \coding_exception('A course context should be provided');
314 }
315
316 $branches = [];
317
c459409c
DM
318 $children = $coursecontext->get_child_contexts();
319 foreach ($children as $childcontext) {
320
321 if ($childcontext->contextlevel !== CONTEXT_BLOCK) {
322 continue;
323 }
5efc1f9e 324
868fc2ab
DM
325 $blockinstance = block_instance_by_id($childcontext->instanceid);
326 $displayname = shorten_text(format_string($blockinstance->get_title(), true, ['context' => $childcontext]));
5efc1f9e
DM
327 $branches[] = self::complete([
328 'text' => $displayname,
c459409c 329 'contextid' => $childcontext->id,
5efc1f9e 330 ]);
c459409c 331
5efc1f9e
DM
332 }
333
334 return $branches;
5efc1f9e
DM
335 }
336
337 /**
338 * Adds the provided category to the categories branch.
339 *
340 * @param stdClass $category
341 * @param array $newnode
342 * @param array $categoriesbranch
343 * @return bool
344 */
345 private function add_to_parent_category_branch($category, $newnode, &$categoriesbranch) {
346
347 foreach ($categoriesbranch as $key => $branch) {
348 if (!empty($branch['categoryid']) && $branch['categoryid'] == $category->parent) {
349 // It may be empty (if it does not contain courses and this is the first child cat).
350 if (!isset($categoriesbranch[$key]['branches'])) {
351 $categoriesbranch[$key]['branches'] = [];
352 }
353 $categoriesbranch[$key]['branches'][] = $newnode;
354 return true;
355 }
356 if (!empty($branch['branches'])) {
357 $parent = $this->add_to_parent_category_branch($category, $newnode, $categoriesbranch[$key]['branches']);
358 if ($parent) {
359 return true;
360 }
361 }
362 }
363
364 return false;
365 }
366
367 /**
368 * Completes tree nodes with default values.
369 *
370 * @param array $node
371 * @param int|false $currentcontextlevel
372 * @param int|false $currentcontextid
373 * @return array
374 */
375 private static function complete($node, $currentcontextlevel = false, $currentcontextid = false) {
376 if (!isset($node['active'])) {
377 if ($currentcontextlevel && !empty($node['contextlevel']) &&
378 $currentcontextlevel == $node['contextlevel'] &&
379 empty($currentcontextid)) {
380 // This is the active context level, we also checked that there
381 // is no default contextid set.
382 $node['active'] = true;
383 } else if ($currentcontextid && !empty($node['contextid']) &&
384 $currentcontextid == $node['contextid']) {
385 $node['active'] = true;
386 } else {
387 $node['active'] = null;
388 }
389 }
390
391 if (!isset($node['branches'])) {
392 $node['branches'] = [];
393 } else {
394 foreach ($node['branches'] as $key => $childnode) {
395 $node['branches'][$key] = self::complete($childnode, $currentcontextlevel, $currentcontextid);
396 }
397 }
398
399 if (!isset($node['expandelement'])) {
400 $node['expandelement'] = null;
401 }
402
403 if (!isset($node['expandcontextid'])) {
404 $node['expandcontextid'] = null;
405 }
406
407 if (!isset($node['contextid'])) {
408 $node['contextid'] = null;
409 }
410
411 if (!isset($node['contextlevel'])) {
412 $node['contextlevel'] = null;
413 }
414
415 if (!isset($node['expanded'])) {
416 if (!empty($node['branches'])) {
417 $node['expanded'] = 1;
418 } else {
419 $node['expanded'] = 0;
420 }
421 }
422 return $node;
423 }
424
425 /**
426 * From a list of purpose persistents to a list of id => name purposes.
427 *
01f098af 428 * @param \tool_dataprivacy\purpose[] $purposes
5efc1f9e
DM
429 * @param bool $includenotset
430 * @param bool $includeinherit
431 * @return string[]
432 */
433 public static function purpose_options($purposes, $includenotset = true, $includeinherit = true) {
434 $options = self::base_options($includenotset, $includeinherit);
435 foreach ($purposes as $purpose) {
436 $options[$purpose->get('id')] = $purpose->get('name');
437 }
438
439 return $options;
440 }
441
442 /**
443 * From a list of category persistents to a list of id => name categories.
444 *
01f098af 445 * @param \tool_dataprivacy\category[] $categories
5efc1f9e
DM
446 * @param bool $includenotset
447 * @param bool $includeinherit
448 * @return string[]
449 */
450 public static function category_options($categories, $includenotset = true, $includeinherit = true) {
451 $options = self::base_options($includenotset, $includeinherit);
452 foreach ($categories as $category) {
453 $options[$category->get('id')] = $category->get('name');
454 }
455
456 return $options;
457 }
458
459 /**
460 * Base not set and inherit options.
461 *
462 * @param bool $includenotset
463 * @param bool $includeinherit
464 * @return array
465 */
466 private static function base_options($includenotset = true, $includeinherit = true) {
467
468 $options = [];
469
470 if ($includenotset) {
471 $options[\tool_dataprivacy\context_instance::NOTSET] = get_string('notset', 'tool_dataprivacy');
472 }
473
474 if ($includeinherit) {
475 $options[\tool_dataprivacy\context_instance::INHERIT] = get_string('inherit', 'tool_dataprivacy');
476 }
477
478 return $options;
479 }
480}