ca185a698afe992b943476bf5f5f45560cb04d09
[moodle.git] / privacy / classes / tests / request / content_writer.php
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/>.
17 /**
18  * This file contains the moodle format implementation of the content writer.
19  *
20  * @package core_privacy
21  * @copyright 2018 Andrew Nicols <andrew@nicols.co.uk>
22  * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23  */
24 namespace core_privacy\tests\request;
26 defined('MOODLE_INTERNAL') || die();
28 /**
29  * An implementation of the content_writer for use in unit tests.
30  *
31  * This implementation does not export any data but instead stores it in
32  * structures within the instance which can be easily queried for use
33  * during unit tests.
34  *
35  * @copyright 2018 Andrew Nicols <andrew@nicols.co.uk>
36  * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
37  */
38 class content_writer implements \core_privacy\local\request\content_writer {
39     /**
40      * @var \context The context currently being exported.
41      */
42     protected $context = null;
44     /**
45      * @var array The collection of metadata which has been exported.
46      */
47     protected $metadata = [];
49     /**
50      * @var array The data which has been exported.
51      */
52     protected $data = [];
54     /**
55      * @var array The related data which has been exported.
56      */
57     protected $relateddata = [];
59     /**
60      * @var array The list of stored files which have been exported.
61      */
62     protected $files = [];
64     /**
65      * @var array The custom files which have been exported.
66      */
67     protected $customfiles = [];
69     /**
70      * @var array The site-wide user preferences which have been exported.
71      */
72     protected $userprefs = [];
74     /**
75      * Whether any data has been exported at all within the current context.
76      */
77     public function has_any_data() {
78         $hasdata = !empty($this->data[$this->context->id]);
79         $hasrelateddata = !empty($this->relateddata[$this->context->id]);
80         $hasmetadata = !empty($this->metadata[$this->context->id]);
81         $hasfiles = !empty($this->files[$this->context->id]);
82         $hascustomfiles = !empty($this->customfiles[$this->context->id]);
83         $hasuserprefs = !empty($this->userprefs);
85         return $hasdata || $hasrelateddata || $hasmetadata || $hasfiles || $hascustomfiles || $hasuserprefs;
86     }
88     /**
89      * Constructor for the content writer.
90      *
91      * Note: The writer_factory must be passed.
92      * @param   \core_privacy\local\request\writer          $writer    The writer factory.
93      */
94     public function __construct(\core_privacy\local\request\writer $writer) {
95     }
97     /**
98      * Set the context for the current item being processed.
99      *
100      * @param   \context        $context    The context to use
101      */
102     public function set_context(\context $context) : \core_privacy\local\request\content_writer {
103         $this->context = $context;
105         if (empty($this->data[$this->context->id])) {
106             $this->data[$this->context->id] = [];
107         }
109         if (empty($this->relateddata[$this->context->id])) {
110             $this->relateddata[$this->context->id] = [];
111         }
113         if (empty($this->metadata[$this->context->id])) {
114             $this->metadata[$this->context->id] = [];
115         }
117         if (empty($this->files[$this->context->id])) {
118             $this->files[$this->context->id] = [];
119         }
121         if (empty($this->customfiles[$this->context->id])) {
122             $this->customfiles[$this->context->id] = [];
123         }
125         return $this;
126     }
128     /**
129      * Return the current context.
130      *
131      * @return  \context
132      */
133     public function get_current_context() : \context {
134         return $this->context;
135     }
137     /**
138      * Export the supplied data within the current context, at the supplied subcontext.
139      *
140      * @param   array           $subcontext The location within the current context that this data belongs.
141      * @param   \stdClass       $data       The data to be exported
142      */
143     public function export_data(array $subcontext, \stdClass $data) : \core_privacy\local\request\content_writer {
144         array_push($subcontext, 'data');
146         $finalcontent = $data;
148         while ($pathtail = array_pop($subcontext)) {
149             $finalcontent = [
150                 $pathtail => $finalcontent,
151             ];
152         }
154         $this->data[$this->context->id] = array_replace_recursive($this->data[$this->context->id], $finalcontent);
156         return $this;
157     }
159     /**
160      * Get all data within the subcontext.
161      *
162      * @param   array           $subcontext The location within the current context that this data belongs.
163      * @return  array                       The metadata as a series of keys to value + descrition objects.
164      */
165     public function get_data(array $subcontext = []) {
166         $basepath = $this->data[$this->context->id];
167         while ($subpath = array_shift($subcontext)) {
168             if (isset($basepath[$subpath])) {
169                 $basepath = $basepath[$subpath];
170             } else {
171                 return [];
172             }
173         }
175         if (isset($basepath['data'])) {
176             return $basepath['data'];
177         } else {
178             return [];
179         }
180     }
182     /**
183      * Export metadata about the supplied subcontext.
184      *
185      * Metadata consists of a key/value pair and a description of the value.
186      *
187      * @param   array           $subcontext The location within the current context that this data belongs.
188      * @param   string          $key        The metadata name.
189      * @param   string          $value      The metadata value.
190      * @param   string          $description    The description of the value.
191      * @return  $this
192      */
193     public function export_metadata(array $subcontext,
194         string $key,
195         $value,
196         string $description
197     ) : \core_privacy\local\request\content_writer {
198         array_push($subcontext, 'metadata');
200         $finalcontent = [
201             $key => (object) [
202                 'value' => $value,
203                 'description' => $description,
204             ],
205         ];
207         while ($pathtail = array_pop($subcontext)) {
208             $finalcontent = [
209                 $pathtail => $finalcontent,
210             ];
211         }
213         $this->metadata[$this->context->id] = array_replace_recursive($this->metadata[$this->context->id], $finalcontent);
215         return $this;
216     }
218     /**
219      * Get all metadata within the subcontext.
220      *
221      * @param   array           $subcontext The location within the current context that this data belongs.
222      * @return  array                       The metadata as a series of keys to value + descrition objects.
223      */
224     public function get_all_metadata(array $subcontext = []) {
225         $basepath = $this->metadata[$this->context->id];
226         while ($subpath = array_shift($subcontext)) {
227             if (isset($basepath[$subpath])) {
228                 $basepath = $basepath[$subpath];
229             }
230         }
232         if (isset($basepath['metadata'])) {
233             return $basepath['metadata'];
234         } else {
235             return [];
236         }
237     }
239     /**
240      * Get the specified metadata within the subcontext.
241      *
242      * @param   array           $subcontext The location within the current context that this data belongs.
243      * @param   string          $key        The metadata to be fetched within the context + subcontext.
244      * @param   boolean         $valueonly  Whether to fetch only the value, rather than the value + description.
245      * @return  array                       The metadata as a series of keys to value + descrition objects.
246      */
247     public function get_metadata(array $subcontext = [], $key, $valueonly = true) {
248         $data = $this->get_all_metadata($subcontext);
250         if (!isset($data[$key])) {
251             return null;
252         }
254         $metadata = $data[$key];
255         if ($valueonly) {
256             return $metadata->value;
257         } else {
258             return $metadata;
259         }
260     }
262     /**
263      * Export a piece of related data.
264      *
265      * @param   array           $subcontext The location within the current context that this data belongs.
266      * @param   string          $name       The name of the file to be exported.
267      * @param   \stdClass       $data       The related data to export.
268      */
269     public function export_related_data(array $subcontext, $name, $data) : \core_privacy\local\request\content_writer {
270         array_push($subcontext, $name);
271         array_push($subcontext, 'data');
273         $finalcontent = $data;
275         while ($pathtail = array_pop($subcontext)) {
276             $finalcontent = [
277                 $pathtail => $finalcontent,
278             ];
279         }
281         $this->relateddata[$this->context->id] = array_replace_recursive($this->relateddata[$this->context->id], $finalcontent);
283         return $this;
284     }
286     /**
287      * Get all data within the subcontext.
288      *
289      * @param   array           $subcontext The location within the current context that this data belongs.
290      * @param   string          $filename   The name of the intended filename.
291      * @return  array                       The metadata as a series of keys to value + descrition objects.
292      */
293     public function get_related_data(array $subcontext = [], $filename) {
294         $basepath = $this->relateddata[$this->context->id];
295         $subcontext[] = $filename;
296         while ($subpath = array_shift($subcontext)) {
297             if (isset($basepath[$subpath])) {
298                 $basepath = $basepath[$subpath];
299             } else {
300                 return [];
301             }
302         }
304         if (isset($basepath['data'])) {
305             return $basepath['data'];
306         } else {
307             return [];
308         }
309     }
311     /**
312      * Export a piece of data in a custom format.
313      *
314      * @param   array           $subcontext The location within the current context that this data belongs.
315      * @param   string          $filename   The name of the file to be exported.
316      * @param   string          $filecontent    The content to be exported.
317      */
318     public function export_custom_file(array $subcontext, $filename, $filecontent) : \core_privacy\local\request\content_writer {
319         $filename = clean_param($filename, PARAM_FILE);
321         $finalcontent = [
322             $filename => $filecontent,
323         ];
324         while ($pathtail = array_pop($subcontext)) {
325             $finalcontent = [
326                 $pathtail => $finalcontent,
327             ];
328         }
330         $this->customfiles[$this->context->id] = array_replace_recursive($this->customfiles[$this->context->id], $finalcontent);
332         return $this;
333     }
335     /**
336      * Get the specified custom file within the subcontext.
337      *
338      * @param   array           $subcontext The location within the current context that this data belongs.
339      * @param   string          $filename   The name of the file to be fetched within the context + subcontext.
340      * @return  string                      The content of the file.
341      */
342     public function get_custom_file(array $subcontext = [], $filename = null) {
343         if (!empty($filename)) {
344             array_push($subcontext, $filename);
345         }
347         $basepath = $this->customfiles[$this->context->id];
348         while ($subpath = array_shift($subcontext)) {
349             if (isset($basepath[$subpath])) {
350                 $basepath = $basepath[$subpath];
351             }
352         }
354         return $basepath;
355     }
357     /**
358      * Prepare a text area by processing pluginfile URLs within it.
359      *
360      * @param   array           $subcontext The location within the current context that this data belongs.
361      * @param   string          $component  The name of the component that the files belong to.
362      * @param   string          $filearea   The filearea within that component.
363      * @param   string          $itemid     Which item those files belong to.
364      * @param   string          $text       The text to be processed
365      * @return  string                      The processed string
366      */
367     public function rewrite_pluginfile_urls(array $subcontext, $component, $filearea, $itemid, $text) : string {
368         return str_replace('@@PLUGINFILE@@/', 'files/', $text);
369     }
371     /**
372      * Export all files within the specified component, filearea, itemid combination.
373      *
374      * @param   array           $subcontext The location within the current context that this data belongs.
375      * @param   string          $component  The name of the component that the files belong to.
376      * @param   string          $filearea   The filearea within that component.
377      * @param   string          $itemid     Which item those files belong to.
378      */
379     public function export_area_files(array $subcontext, $component, $filearea, $itemid) : \core_privacy\local\request\content_writer  {
380         $fs = get_file_storage();
381         $files = $fs->get_area_files($this->context->id, $component, $filearea, $itemid);
382         foreach ($files as $file) {
383             $this->export_file($subcontext, $file);
384         }
386         return $this;
387     }
389     /**
390      * Export the specified file in the target location.
391      *
392      * @param   array           $subcontext The location within the current context that this data belongs.
393      * @param   \stored_file    $file       The file to be exported.
394      */
395     public function export_file(array $subcontext, \stored_file $file) : \core_privacy\local\request\content_writer  {
396         if (!$file->is_directory()) {
397             $subcontextextra = [
398                 'files',
399                 $file->get_filepath(),
400             ];
401             $newsubcontext = array_merge($subcontext, $subcontextextra);
403             $finalcontent = [
404                 $file,
405             ];
406             while ($pathtail = array_pop($subcontext)) {
407                 $finalcontent = [
408                     $pathtail => $finalcontent,
409                 ];
410             }
412             $this->customfiles[$this->context->id] = array_replace_recursive($this->customfiles[$this->context->id], $finalcontent);
413         }
415         return $this;
416     }
418     /**
419      * Get all files in the specfied subcontext.
420      *
421      * @param   array           $subcontext The location within the current context that this data belongs.
422      * @return  \stored_file[]              The list of stored_files in this context + subcontext.
423      */
424     public function get_files(array $subcontext = []) {
425         $basepath = $this->files[$this->context->id];
426         while ($subpath = array_shift($subcontext)) {
427             if (isset($basepath[$subpath])) {
428                 $basepath = $basepath[$subpath];
429             }
430         }
432         return $basepath;
433     }
435     /**
436      * Export the specified user preference.
437      *
438      * @param   string          $component  The name of the component.
439      * @param   string          $key        The name of th key to be exported.
440      * @param   string          $value      The value of the preference
441      * @param   string          $description    A description of the value
442      * @return  \core_privacy\local\request\content_writer
443      */
444     public function export_user_preference(
445         string $component,
446         string $key,
447         string $value,
448         string $description
449     ) : \core_privacy\local\request\content_writer {
450         if (!isset($this->userprefs[$component])) {
451             $this->userprefs[$component] = (object) [];
452         }
454         $this->userprefs[$component]->$key = (object) [
455             'value' => $value,
456             'description' => $description,
457         ];
459         return $this;
460     }
462     /**
463      * Get all user preferences for the specified component.
464      *
465      * @param   string          $component  The name of the component.
466      * @return  \stdClass
467      */
468     public function get_user_preferences(string $component) {
469         if (isset($this->userprefs[$component])) {
470             return $this->userprefs[$component];
471         } else {
472             return (object) [];
473         }
474     }
476     /**
477      * Perform any required finalisation steps and return the location of the finalised export.
478      *
479      * @return  string
480      */
481     public function finalise_content() : string {
482         return 'mock_path';
483     }