MDL-31443 backup: Title and cancel messages stating import and restore
[moodle.git] / backup / util / ui / renderer.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 backup and restore output renderers
19  *
20  * @package   core_backup
21  * @copyright 2010 Sam Hemelryk
22  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23  */
25 /**
26  * The primary renderer for the backup.
27  *
28  * Can be retrieved with the following code:
29  * <?php
30  * $renderer = $PAGE->get_renderer('core', 'backup');
31  * ?>
32  *
33  * @package   core_backup
34  * @copyright 2010 Sam Hemelryk
35  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
36  */
37 class core_backup_renderer extends plugin_renderer_base {
39     /**
40      * Renderers a progress bar for the backup or restore given the items that make it up.
41      *
42      * @param array $items An array of items
43      * @return string
44      */
45     public function progress_bar(array $items) {
46         foreach ($items as &$item) {
47             $text = $item['text'];
48             unset($item['text']);
49             if (array_key_exists('link', $item)) {
50                 $link = $item['link'];
51                 unset($item['link']);
52                 $item = html_writer::link($link, $text, $item);
53             } else {
54                 $item = html_writer::tag('span', $text, $item);
55             }
56         }
57         return html_writer::tag('div', join(get_separator(), $items), array('class' => 'backup_progress clearfix'));
58     }
60     /**
61      * The backup and restore pages may display a log (if any) in a scrolling box.
62      *
63      * @param string $loghtml Log content in HTML format
64      * @return string HTML content that shows the log
65      */
66     public function log_display($loghtml) {
67         global $OUTPUT;
68         $out = html_writer::start_div('backup_log');
69         $out .= $OUTPUT->heading(get_string('backuplog', 'backup'));
70         $out .= html_writer::start_div('backup_log_contents');
71         $out .= $loghtml;
72         $out .= html_writer::end_div();
73         $out .= html_writer::end_div();
74         return $out;
75     }
77     /**
78      * Prints a dependency notification
79      *
80      * @param string $message
81      * @return string
82      */
83     public function dependency_notification($message) {
84         return html_writer::tag('div', $message, array('class' => 'notification dependencies_enforced'));
85     }
87     /**
88      * Displays the details of a backup file
89      *
90      * @param stdClass $details
91      * @param moodle_url $nextstageurl
92      * @return string
93      */
94     public function backup_details($details, $nextstageurl) {
95         $yestick = $this->output->pix_icon('i/valid', get_string('yes'));
96         $notick = $this->output->pix_icon('i/invalid', get_string('no'));
98         $html  = html_writer::start_tag('div', array('class' => 'backup-restore'));
100         $html .= html_writer::start_tag('div', array('class' => 'backup-section'));
101         $html .= $this->output->heading(get_string('backupdetails', 'backup'), 2, array('class' => 'header'));
102         $html .= $this->backup_detail_pair(get_string('backuptype', 'backup'), get_string('backuptype'.$details->type, 'backup'));
103         $html .= $this->backup_detail_pair(get_string('backupformat', 'backup'), get_string('backupformat'.$details->format, 'backup'));
104         $html .= $this->backup_detail_pair(get_string('backupmode', 'backup'), get_string('backupmode'.$details->mode, 'backup'));
105         $html .= $this->backup_detail_pair(get_string('backupdate', 'backup'), userdate($details->backup_date));
106         $html .= $this->backup_detail_pair(get_string('moodleversion', 'backup'),
107                 html_writer::tag('span', $details->moodle_release, array('class' => 'moodle_release')).
108                 html_writer::tag('span', '['.$details->moodle_version.']', array('class' => 'moodle_version sub-detail')));
109         $html .= $this->backup_detail_pair(get_string('backupversion', 'backup'),
110                 html_writer::tag('span', $details->backup_release, array('class' => 'moodle_release')).
111                 html_writer::tag('span', '['.$details->backup_version.']', array('class' => 'moodle_version sub-detail')));
112         $html .= $this->backup_detail_pair(get_string('originalwwwroot', 'backup'),
113                 html_writer::tag('span', $details->original_wwwroot, array('class' => 'originalwwwroot')).
114                 html_writer::tag('span', '['.$details->original_site_identifier_hash.']', array('class' => 'sitehash sub-detail')));
115         if (!empty($details->include_file_references_to_external_content)) {
116             $message = '';
117             if (backup_general_helper::backup_is_samesite($details)) {
118                 $message = $yestick . ' ' . get_string('filereferencessamesite', 'backup');
119             } else {
120                 $message = $notick . ' ' . get_string('filereferencesnotsamesite', 'backup');
121             }
122             $html .= $this->backup_detail_pair(get_string('includefilereferences', 'backup'), $message);
123         }
125         $html .= html_writer::end_tag('div');
127         $html .= html_writer::start_tag('div', array('class' => 'backup-section settings-section'));
128         if ($this instanceof import_ui_stage_inital) {
129             $html .= $this->output->heading(get_string('importrootsettings', 'backup'), 2, array('class' => 'header'));
130         } else if ($this instanceof restore_ui_stage_settings) {
131             $html .= $this->output->heading(get_string('restorerootsettings', 'backup'), 2, array('class' => 'header'));
132         } else {
133             $html .= $this->output->heading(get_string('backupsettings', 'backup'), 2, array('class' => 'header'));
134         }
135         foreach ($details->root_settings as $label => $value) {
136             if ($label == 'filename' or $label == 'user_files') {
137                 continue;
138             }
139             $html .= $this->backup_detail_pair(get_string('rootsetting'.str_replace('_', '', $label), 'backup'), $value ? $yestick : $notick);
140         }
141         $html .= html_writer::end_tag('div');
143         if ($details->type === 'course') {
144             $html .= html_writer::start_tag('div', array('class' => 'backup-section'));
145             $html .= $this->output->heading(get_string('backupcoursedetails', 'backup'), 2, array('class' => 'header'));
146             $html .= $this->backup_detail_pair(get_string('coursetitle', 'backup'), $details->course->title);
147             $html .= $this->backup_detail_pair(get_string('courseid', 'backup'), $details->course->courseid);
149             // Warning users about front page backups.
150             if ($details->original_course_format === 'site') {
151                 $html .= $this->backup_detail_pair(get_string('type_format', 'plugin'), get_string('sitecourseformatwarning', 'backup'));
152             }
153             $html .= html_writer::start_tag('div', array('class' => 'backup-sub-section'));
154             $html .= $this->output->heading(get_string('backupcoursesections', 'backup'), 3, array('class' => 'subheader'));
155             foreach ($details->sections as $key => $section) {
156                 $included = $key.'_included';
157                 $userinfo = $key.'_userinfo';
158                 if ($section->settings[$included] && $section->settings[$userinfo]) {
159                     $value = get_string('sectionincanduser', 'backup');
160                 } else if ($section->settings[$included]) {
161                     $value = get_string('sectioninc', 'backup');
162                 } else {
163                     continue;
164                 }
165                 $html .= $this->backup_detail_pair(get_string('backupcoursesection', 'backup', $section->title), $value);
166                 $table = null;
167                 foreach ($details->activities as $activitykey => $activity) {
168                     if ($activity->sectionid != $section->sectionid) {
169                         continue;
170                     }
171                     if (empty($table)) {
172                         $table = new html_table();
173                         $table->head = array(get_string('module', 'backup'), get_string('title', 'backup'), get_string('userinfo', 'backup'));
174                         $table->colclasses = array('modulename', 'moduletitle', 'userinfoincluded');
175                         $table->align = array('left', 'left', 'center');
176                         $table->attributes = array('class' => 'activitytable generaltable');
177                         $table->data = array();
178                     }
179                     $name = get_string('pluginname', $activity->modulename);
180                     $icon = new image_icon('icon', $name, $activity->modulename, array('class' => 'iconlarge icon-pre'));
181                     $table->data[] = array(
182                         $this->output->render($icon).$name,
183                         $activity->title,
184                         ($activity->settings[$activitykey.'_userinfo']) ? $yestick : $notick,
185                     );
186                 }
187                 if (!empty($table)) {
188                     $html .= $this->backup_detail_pair(get_string('sectionactivities', 'backup'), html_writer::table($table));
189                 }
191             }
192             $html .= html_writer::end_tag('div');
193             $html .= html_writer::end_tag('div');
194         }
196         $html .= $this->output->single_button($nextstageurl, get_string('continue'), 'post');
197         $html .= html_writer::end_tag('div');
199         return $html;
200     }
202     /**
203      * Displays the general information about a backup file with non-standard format
204      *
205      * @param moodle_url $nextstageurl URL to send user to
206      * @param array $details basic info about the file (format, type)
207      * @return string HTML code to display
208      */
209     public function backup_details_nonstandard($nextstageurl, array $details) {
211         $html  = html_writer::start_tag('div', array('class' => 'backup-restore nonstandardformat'));
212         $html .= html_writer::start_tag('div', array('class' => 'backup-section'));
213         $html .= $this->output->heading(get_string('backupdetails', 'backup'), 2, 'header');
214         $html .= $this->output->box(get_string('backupdetailsnonstandardinfo', 'backup'), 'noticebox');
215         $html .= $this->backup_detail_pair(
216             get_string('backupformat', 'backup'),
217             get_string('backupformat'.$details['format'], 'backup'));
218         $html .= $this->backup_detail_pair(
219             get_string('backuptype', 'backup'),
220             get_string('backuptype'.$details['type'], 'backup'));
221         $html .= html_writer::end_tag('div');
222         $html .= $this->output->single_button($nextstageurl, get_string('continue'), 'post');
223         $html .= html_writer::end_tag('div');
225         return $html;
226     }
228     /**
229      * Displays the general information about a backup file with unknown format
230      *
231      * @param moodle_url $nextstageurl URL to send user to
232      * @return string HTML code to display
233      */
234     public function backup_details_unknown(moodle_url $nextstageurl) {
236         $html  = html_writer::start_div('unknownformat');
237         $html .= $this->output->heading(get_string('errorinvalidformat', 'backup'), 2);
238         $html .= $this->output->notification(get_string('errorinvalidformatinfo', 'backup'), 'notifyproblem');
239         $html .= $this->output->single_button($nextstageurl, get_string('continue'), 'post');
240         $html .= html_writer::end_div();
242         return $html;
243     }
245     /**
246      * Displays a course selector for restore
247      *
248      * @param moodle_url $nextstageurl
249      * @param bool $wholecourse true if we are restoring whole course (as with backup::TYPE_1COURSE), false otherwise
250      * @param restore_category_search $categories
251      * @param restore_course_search $courses
252      * @param int $currentcourse
253      * @return string
254      */
255     public function course_selector(moodle_url $nextstageurl, $wholecourse = true, restore_category_search $categories = null,
256                                     restore_course_search $courses = null, $currentcourse = null) {
257         global $CFG, $PAGE;
258         require_once($CFG->dirroot.'/course/lib.php');
260         // These variables are used to check if the form using this function was submitted.
261         $target = optional_param('target', false, PARAM_INT);
262         $targetid = optional_param('targetid', null, PARAM_INT);
264         // Check if they submitted the form but did not provide all the data we need.
265         $missingdata = false;
266         if ($target and is_null($targetid)) {
267             $missingdata = true;
268         }
270         $nextstageurl->param('sesskey', sesskey());
272         $form = html_writer::start_tag('form', array('method' => 'post', 'action' => $nextstageurl->out_omit_querystring(),
273             'class' => 'mform'));
274         foreach ($nextstageurl->params() as $key => $value) {
275             $form .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => $key, 'value' => $value));
276         }
278         $hasrestoreoption = false;
280         $html  = html_writer::start_tag('div', array('class' => 'backup-course-selector backup-restore'));
281         if ($wholecourse && !empty($categories) && ($categories->get_count() > 0 || $categories->get_search())) {
282             // New course.
283             $hasrestoreoption = true;
284             $html .= $form;
285             $html .= html_writer::start_tag('div', array('class' => 'bcs-new-course backup-section'));
286             $html .= $this->output->heading(get_string('restoretonewcourse', 'backup'), 2, array('class' => 'header'));
287             $html .= $this->backup_detail_input(get_string('restoretonewcourse', 'backup'), 'radio', 'target',
288                 backup::TARGET_NEW_COURSE, array('checked' => 'checked'));
289             $selectacategoryhtml = $this->backup_detail_pair(get_string('selectacategory', 'backup'), $this->render($categories));
290             // Display the category selection as required if the form was submitted but this data was not supplied.
291             if ($missingdata && $target == backup::TARGET_NEW_COURSE) {
292                 $html .= html_writer::span(get_string('required'), 'error');
293                 $html .= html_writer::start_tag('fieldset', array('class' => 'error'));
294                 $html .= $selectacategoryhtml;
295                 $html .= html_writer::end_tag('fieldset');
296             } else {
297                 $html .= $selectacategoryhtml;
298             }
299             $attrs = array('type' => 'submit', 'value' => get_string('continue'), 'class' => 'btn btn-primary');
300             $html .= $this->backup_detail_pair('', html_writer::empty_tag('input', $attrs));
301             $html .= html_writer::end_tag('div');
302             $html .= html_writer::end_tag('form');
303         }
305         if ($wholecourse && !empty($currentcourse)) {
306             // Current course.
307             $hasrestoreoption = true;
308             $html .= $form;
309             $html .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'targetid', 'value' => $currentcourse));
310             $html .= html_writer::start_tag('div', array('class' => 'bcs-current-course backup-section'));
311             $html .= $this->output->heading(get_string('restoretocurrentcourse', 'backup'), 2, array('class' => 'header'));
312             $html .= $this->backup_detail_input(get_string('restoretocurrentcourseadding', 'backup'), 'radio', 'target',
313                 backup::TARGET_CURRENT_ADDING, array('checked' => 'checked'));
314             $html .= $this->backup_detail_input(get_string('restoretocurrentcoursedeleting', 'backup'), 'radio', 'target',
315                 backup::TARGET_CURRENT_DELETING);
316             $attrs = array('type' => 'submit', 'value' => get_string('continue'), 'class' => 'btn btn-primary');
317             $html .= $this->backup_detail_pair('', html_writer::empty_tag('input', $attrs));
318             $html .= html_writer::end_tag('div');
319             $html .= html_writer::end_tag('form');
320         }
322         // If we are restoring an activity, then include the current course.
323         if (!$wholecourse) {
324             $courses->invalidate_results(); // Clean list of courses.
325             $courses->set_include_currentcourse();
326         }
327         if (!empty($courses) && ($courses->get_count() > 0 || $courses->get_search())) {
328             // Existing course.
329             $hasrestoreoption = true;
330             $html .= $form;
331             $html .= html_writer::start_tag('div', array('class' => 'bcs-existing-course backup-section'));
332             $html .= $this->output->heading(get_string('restoretoexistingcourse', 'backup'), 2, array('class' => 'header'));
333             if ($wholecourse) {
334                 $html .= $this->backup_detail_input(get_string('restoretoexistingcourseadding', 'backup'), 'radio', 'target',
335                     backup::TARGET_EXISTING_ADDING, array('checked' => 'checked'));
336                 $html .= $this->backup_detail_input(get_string('restoretoexistingcoursedeleting', 'backup'), 'radio', 'target',
337                     backup::TARGET_EXISTING_DELETING);
338             } else {
339                 $html .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'target', 'value' => backup::TARGET_EXISTING_ADDING));
340             }
341             $selectacoursehtml = $this->backup_detail_pair(get_string('selectacourse', 'backup'), $this->render($courses));
342             // Display the course selection as required if the form was submitted but this data was not supplied.
343             if ($missingdata && $target == backup::TARGET_EXISTING_ADDING) {
344                 $html .= html_writer::span(get_string('required'), 'error');
345                 $html .= html_writer::start_tag('fieldset', array('class' => 'error'));
346                 $html .= $selectacoursehtml;
347                 $html .= html_writer::end_tag('fieldset');
348             } else {
349                 $html .= $selectacoursehtml;
350             }
351             $attrs = array('type' => 'submit', 'value' => get_string('continue'), 'class' => 'btn btn-primary');
352             $html .= $this->backup_detail_pair('', html_writer::empty_tag('input', $attrs));
353             $html .= html_writer::end_tag('div');
354             $html .= html_writer::end_tag('form');
355         }
357         if (!$hasrestoreoption) {
358             echo $this->output->notification(get_string('norestoreoptions', 'backup'));
359         }
361         $html .= html_writer::end_tag('div');
362         return $html;
363     }
365     /**
366      * Displays the import course selector
367      *
368      * @param moodle_url $nextstageurl
369      * @param import_course_search $courses
370      * @return string
371      */
372     public function import_course_selector(moodle_url $nextstageurl, import_course_search $courses = null) {
373         $html  = html_writer::start_tag('div', array('class' => 'import-course-selector backup-restore'));
374         $html .= html_writer::start_tag('form', array('method' => 'post', 'action' => $nextstageurl->out_omit_querystring()));
375         foreach ($nextstageurl->params() as $key => $value) {
376             $html .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => $key, 'value' => $value));
377         }
378         // We only allow import adding for now. Enforce it here.
379         $html .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'target', 'value' => backup::TARGET_CURRENT_ADDING));
380         $html .= html_writer::start_tag('div', array('class' => 'ics-existing-course backup-section'));
381         $html .= $this->output->heading(get_string('importdatafrom'), 2, array('class' => 'header'));
382         $html .= $this->backup_detail_pair(get_string('selectacourse', 'backup'), $this->render($courses));
383         $attrs = array('type' => 'submit', 'value' => get_string('continue'), 'class' => 'btn btn-primary');
384         $html .= $this->backup_detail_pair('', html_writer::empty_tag('input', $attrs));
385         $html .= html_writer::end_tag('div');
386         $html .= html_writer::end_tag('form');
387         $html .= html_writer::end_tag('div');
388         return $html;
389     }
391     /**
392      * Creates a detailed pairing (key + value)
393      *
394      * @staticvar int $count
395      * @param string $label
396      * @param string $value
397      * @return string
398      */
399     protected function backup_detail_pair($label, $value) {
400         static $count = 0;
401         $count ++;
402         $html  = html_writer::start_tag('div', array('class' => 'detail-pair'));
403         $html .= html_writer::tag('label', $label, array('class' => 'detail-pair-label', 'for' => 'detail-pair-value-'.$count));
404         $html .= html_writer::tag('div', $value, array('class' => 'detail-pair-value pl-2', 'name' => 'detail-pair-value-'.$count));
405         $html .= html_writer::end_tag('div');
406         return $html;
407     }
409     /**
410      * Created a detailed pairing with an input
411      *
412      * @param string $label
413      * @param string $type
414      * @param string $name
415      * @param string $value
416      * @param array $attributes
417      * @param string|null $description
418      * @return string
419      */
420     protected function backup_detail_input($label, $type, $name, $value, array $attributes = array(), $description = null) {
421         if (!empty($description)) {
422             $description = html_writer::tag('span', $description, array('class' => 'description'));
423         } else {
424             $description = '';
425         }
426         return $this->backup_detail_pair(
427             $label,
428             html_writer::empty_tag('input', $attributes + array('name' => $name, 'type' => $type, 'value' => $value)) . $description
429         );
430     }
432     /**
433      * Creates a detailed pairing with a select
434      *
435      * @param string $label
436      * @param string $name
437      * @param array $options
438      * @param string $selected
439      * @param bool $nothing
440      * @param array $attributes
441      * @param string|null $description
442      * @return string
443      */
444     protected function backup_detail_select($label, $name, $options, $selected = '', $nothing = false, array $attributes = array(), $description = null) {
445         if (!empty ($description)) {
446             $description = html_writer::tag('span', $description, array('class' => 'description'));
447         } else {
448             $description = '';
449         }
450         return $this->backup_detail_pair($label, html_writer::select($options, $name, $selected, false, $attributes).$description);
451     }
453     /**
454      * Displays precheck notices
455      *
456      * @param array $results
457      * @return string
458      */
459     public function precheck_notices($results) {
460         $output = html_writer::start_tag('div', array('class' => 'restore-precheck-notices'));
461         if (array_key_exists('errors', $results)) {
462             foreach ($results['errors'] as $error) {
463                 $output .= $this->output->notification($error);
464             }
465         }
466         if (array_key_exists('warnings', $results)) {
467             foreach ($results['warnings'] as $warning) {
468                 $output .= $this->output->notification($warning, 'notifyproblem');
469             }
470         }
471         return $output.html_writer::end_tag('div');
472     }
474     /**
475      * Displays substage buttons
476      *
477      * @param bool $haserrors
478      * @return string
479      */
480     public function substage_buttons($haserrors) {
481         $output  = html_writer::start_tag('div', array('continuebutton'));
482         if (!$haserrors) {
483             $attrs = array('type' => 'submit', 'value' => get_string('continue'), 'class' => 'btn btn-primary');
484             $output .= html_writer::empty_tag('input', $attrs);
485         }
486         $attrs = array('type' => 'submit', 'name' => 'cancel', 'value' => get_string('cancel'), 'class' => 'btn btn-secondary');
487         $output .= html_writer::empty_tag('input', $attrs);
488         $output .= html_writer::end_tag('div');
489         return $output;
490     }
492     /**
493      * Displays a role mapping interface
494      *
495      * @param array $rolemappings
496      * @param array $roles
497      * @return string
498      */
499     public function role_mappings($rolemappings, $roles) {
500         $roles[0] = get_string('none');
501         $output  = html_writer::start_tag('div', array('class' => 'restore-rolemappings'));
502         $output .= $this->output->heading(get_string('restorerolemappings', 'backup'), 2);
503         foreach ($rolemappings as $id => $mapping) {
504             $label = $mapping->name;
505             $name = 'mapping'.$id;
506             $selected = $mapping->targetroleid;
507             $output .= $this->backup_detail_select($label, $name, $roles, $mapping->targetroleid, false, array(), $mapping->description);
508         }
509         $output .= html_writer::end_tag('div');
510         return $output;
511     }
513     /**
514      * Displays a continue button
515      *
516      * @param string|moodle_url $url
517      * @param string $method
518      * @return string
519      */
520     public function continue_button($url, $method = 'post') {
521         if (!($url instanceof moodle_url)) {
522             $url = new moodle_url($url);
523         }
524         if ($method != 'post') {
525             $method = 'get';
526         }
527         $url->param('sesskey', sesskey());
528         $button = new single_button($url, get_string('continue'), $method, true);
529         $button->class = 'continuebutton';
530         return $this->render($button);
531     }
532     /**
533      * Print a backup files tree
534      * @param array $options
535      * @return string
536      */
537     public function backup_files_viewer(array $options = null) {
538         $files = new backup_files_viewer($options);
539         return $this->render($files);
540     }
542     /**
543      * Displays a backup files viewer
544      *
545      * @global stdClass $USER
546      * @param backup_files_viewer $viewer
547      * @return string
548      */
549     public function render_backup_files_viewer(backup_files_viewer $viewer) {
550         global $CFG;
551         $files = $viewer->files;
553         $table = new html_table();
554         $table->attributes['class'] = 'backup-files-table generaltable';
555         $table->head = array(get_string('filename', 'backup'), get_string('time'), get_string('size'), get_string('download'), get_string('restore'));
556         $table->width = '100%';
557         $table->data = array();
559         foreach ($files as $file) {
560             if ($file->is_directory()) {
561                 continue;
562             }
563             $fileurl = moodle_url::make_pluginfile_url(
564                 $file->get_contextid(),
565                 $file->get_component(),
566                 $file->get_filearea(),
567                 null,
568                 $file->get_filepath(),
569                 $file->get_filename(),
570                 true
571             );
572             $params = array();
573             $params['action'] = 'choosebackupfile';
574             $params['filename'] = $file->get_filename();
575             $params['filepath'] = $file->get_filepath();
576             $params['component'] = $file->get_component();
577             $params['filearea'] = $file->get_filearea();
578             $params['filecontextid'] = $file->get_contextid();
579             $params['contextid'] = $viewer->currentcontext->id;
580             $params['itemid'] = $file->get_itemid();
581             $restoreurl = new moodle_url('/backup/restorefile.php', $params);
582             $restorelink = html_writer::link($restoreurl, get_string('restore'));
583             $downloadlink = html_writer::link($fileurl, get_string('download'));
585             // Conditional display of the restore and download links, initially only for the 'automated' filearea.
586             if ($params['filearea'] == 'automated') {
587                 if (!has_capability('moodle/restore:viewautomatedfilearea', $viewer->currentcontext)) {
588                     $restorelink = '';
589                 }
590                 if (!can_download_from_backup_filearea($params['filearea'], $viewer->currentcontext)) {
591                     $downloadlink = '';
592                 }
593             }
594             $table->data[] = array(
595                 $file->get_filename(),
596                 userdate($file->get_timemodified()),
597                 display_size($file->get_filesize()),
598                 $downloadlink,
599                 $restorelink,
600                 );
601         }
603         $html = html_writer::table($table);
605         // For automated backups, the ability to manage backup files is controlled by the ability to download them.
606         // All files must be from the same file area in a backup_files_viewer.
607         $canmanagebackups = true;
608         if ($viewer->filearea == 'automated') {
609             if (!can_download_from_backup_filearea($viewer->filearea, $viewer->currentcontext)) {
610                 $canmanagebackups = false;
611             }
612         }
614         if ($canmanagebackups) {
615             $html .= $this->output->single_button(
616                 new moodle_url('/backup/backupfilesedit.php', array(
617                         'currentcontext' => $viewer->currentcontext->id,
618                         'contextid' => $viewer->filecontext->id,
619                         'filearea' => $viewer->filearea,
620                         'component' => $viewer->component,
621                         'returnurl' => $this->page->url->out())
622                 ),
623                 get_string('managefiles', 'backup'),
624                 'post'
625             );
626         }
628         return $html;
629     }
631     /**
632      * Renders a restore course search object
633      *
634      * @param restore_course_search $component
635      * @return string
636      */
637     public function render_restore_course_search(restore_course_search $component) {
638         $url = $component->get_url();
640         $output = html_writer::start_tag('div', array('class' => 'restore-course-search form-inline m-b-1'));
641         $output .= html_writer::start_tag('div', array('class' => 'rcs-results'));
643         $table = new html_table();
644         $table->head = array('', get_string('shortnamecourse'), get_string('fullnamecourse'));
645         $table->data = array();
646         if ($component->get_count() !== 0) {
647             foreach ($component->get_results() as $course) {
648                 $row = new html_table_row();
649                 $row->attributes['class'] = 'rcs-course';
650                 if (!$course->visible) {
651                     $row->attributes['class'] .= ' dimmed';
652                 }
653                 $row->cells = array(
654                     html_writer::empty_tag('input', array('type' => 'radio', 'name' => 'targetid', 'value' => $course->id)),
655                     format_string($course->shortname, true, array('context' => context_course::instance($course->id))),
656                     format_string($course->fullname, true, array('context' => context_course::instance($course->id)))
657                 );
658                 $table->data[] = $row;
659             }
660             if ($component->has_more_results()) {
661                 $cell = new html_table_cell(get_string('moreresults', 'backup'));
662                 $cell->colspan = 3;
663                 $cell->attributes['class'] = 'notifyproblem';
664                 $row = new html_table_row(array($cell));
665                 $row->attributes['class'] = 'rcs-course';
666                 $table->data[] = $row;
667             }
668         } else {
669             $cell = new html_table_cell(get_string('nomatchingcourses', 'backup'));
670             $cell->colspan = 3;
671             $cell->attributes['class'] = 'notifyproblem';
672             $row = new html_table_row(array($cell));
673             $row->attributes['class'] = 'rcs-course';
674             $table->data[] = $row;
675         }
676         $output .= html_writer::table($table);
677         $output .= html_writer::end_tag('div');
679         $output .= html_writer::start_tag('div', array('class' => 'rcs-search'));
680         $attrs = array(
681             'type' => 'text',
682             'name' => restore_course_search::$VAR_SEARCH,
683             'value' => $component->get_search(),
684             'class' => 'form-control'
685         );
686         $output .= html_writer::empty_tag('input', $attrs);
687         $attrs = array(
688             'type' => 'submit',
689             'name' => 'searchcourses',
690             'value' => get_string('search'),
691             'class' => 'btn btn-secondary'
692         );
693         $output .= html_writer::empty_tag('input', $attrs);
694         $output .= html_writer::end_tag('div');
696         $output .= html_writer::end_tag('div');
697         return $output;
698     }
700     /**
701      * Renders an import course search object
702      *
703      * @param import_course_search $component
704      * @return string
705      */
706     public function render_import_course_search(import_course_search $component) {
707         $url = $component->get_url();
709         $output = html_writer::start_tag('div', array('class' => 'import-course-search'));
710         if ($component->get_count() === 0) {
711             $output .= $this->output->notification(get_string('nomatchingcourses', 'backup'));
713             $output .= html_writer::start_tag('div', array('class' => 'ics-search'));
714             $attrs = array(
715                 'type' => 'text',
716                 'name' => restore_course_search::$VAR_SEARCH,
717                 'value' => $component->get_search(),
718                 'class' => 'form-control'
719             );
720             $output .= html_writer::empty_tag('input', $attrs);
721             $attrs = array(
722                 'type' => 'submit',
723                 'name' => 'searchcourses',
724                 'value' => get_string('search'),
725                 'class' => 'btn btn-secondary'
726             );
727             $output .= html_writer::empty_tag('input', $attrs);
728             $output .= html_writer::end_tag('div');
730             $output .= html_writer::end_tag('div');
731             return $output;
732         }
734         $countstr = '';
735         if ($component->has_more_results()) {
736             $countstr = get_string('morecoursesearchresults', 'backup', $component->get_count());
737         } else {
738             $countstr = get_string('totalcoursesearchresults', 'backup', $component->get_count());
739         }
741         $output .= html_writer::tag('div', $countstr, array('class' => 'ics-totalresults'));
742         $output .= html_writer::start_tag('div', array('class' => 'ics-results'));
744         $table = new html_table();
745         $table->head = array('', get_string('shortnamecourse'), get_string('fullnamecourse'));
746         $table->data = array();
747         foreach ($component->get_results() as $course) {
748             $row = new html_table_row();
749             $row->attributes['class'] = 'ics-course';
750             if (!$course->visible) {
751                 $row->attributes['class'] .= ' dimmed';
752             }
753             $row->cells = array(
754                 html_writer::empty_tag('input', array('type' => 'radio', 'name' => 'importid', 'value' => $course->id)),
755                 format_string($course->shortname, true, array('context' => context_course::instance($course->id))),
756                 format_string($course->fullname, true, array('context' => context_course::instance($course->id)))
757             );
758             $table->data[] = $row;
759         }
760         if ($component->has_more_results()) {
761             $cell = new html_table_cell(get_string('moreresults', 'backup'));
762             $cell->colspan = 3;
763             $cell->attributes['class'] = 'notifyproblem';
764             $row = new html_table_row(array($cell));
765             $row->attributes['class'] = 'rcs-course';
766             $table->data[] = $row;
767         }
768         $output .= html_writer::table($table);
769         $output .= html_writer::end_tag('div');
771         $output .= html_writer::start_tag('div', array('class' => 'ics-search'));
772         $attrs = array(
773             'type' => 'text',
774             'name' => restore_course_search::$VAR_SEARCH,
775             'value' => $component->get_search(),
776             'class' => 'form-control');
777         $output .= html_writer::empty_tag('input', $attrs);
778         $attrs = array(
779             'type' => 'submit',
780             'name' => 'searchcourses',
781             'value' => get_string('search'),
782             'class' => 'btn btn-secondary'
783         );
784         $output .= html_writer::empty_tag('input', $attrs);
785         $output .= html_writer::end_tag('div');
787         $output .= html_writer::end_tag('div');
788         return $output;
789     }
791     /**
792      * Renders a restore category search object
793      *
794      * @param restore_category_search $component
795      * @return string
796      */
797     public function render_restore_category_search(restore_category_search $component) {
798         $url = $component->get_url();
800         $output = html_writer::start_tag('div', array('class' => 'restore-course-search form-inline m-b-1'));
801         $output .= html_writer::start_tag('div', array('class' => 'rcs-results w-100'));
803         $table = new html_table();
804         $table->head = array('', get_string('name'), get_string('description'));
805         $table->data = array();
807         if ($component->get_count() !== 0) {
808             foreach ($component->get_results() as $category) {
809                 $row = new html_table_row();
810                 $row->attributes['class'] = 'rcs-course';
811                 if (!$category->visible) {
812                     $row->attributes['class'] .= ' dimmed';
813                 }
814                 $context = context_coursecat::instance($category->id);
815                 $row->cells = array(
816                     html_writer::empty_tag('input', array('type' => 'radio', 'name' => 'targetid', 'value' => $category->id)),
817                     format_string($category->name, true, array('context' => context_coursecat::instance($category->id))),
818                     format_text(file_rewrite_pluginfile_urls($category->description, 'pluginfile.php', $context->id,
819                         'coursecat', 'description', null), $category->descriptionformat, array('overflowdiv' => true))
820                 );
821                 $table->data[] = $row;
822             }
823             if ($component->has_more_results()) {
824                 $cell = new html_table_cell(get_string('moreresults', 'backup'));
825                 $cell->attributes['class'] = 'notifyproblem';
826                 $cell->colspan = 3;
827                 $row = new html_table_row(array($cell));
828                 $row->attributes['class'] = 'rcs-course';
829                 $table->data[] = $row;
830             }
831         } else {
832             $cell = new html_table_cell(get_string('nomatchingcourses', 'backup'));
833             $cell->colspan = 3;
834             $cell->attributes['class'] = 'notifyproblem';
835             $row = new html_table_row(array($cell));
836             $row->attributes['class'] = 'rcs-course';
837             $table->data[] = $row;
838         }
839         $output .= html_writer::table($table);
840         $output .= html_writer::end_tag('div');
842         $output .= html_writer::start_tag('div', array('class' => 'rcs-search'));
843         $attrs = array(
844             'type' => 'text',
845             'name' => restore_category_search::$VAR_SEARCH,
846             'value' => $component->get_search(),
847             'class' => 'form-control'
848         );
849         $output .= html_writer::empty_tag('input', $attrs);
850         $attrs = array(
851             'type' => 'submit',
852             'name' => 'searchcourses',
853             'value' => get_string('search'),
854             'class' => 'btn btn-secondary'
855         );
856         $output .= html_writer::empty_tag('input', $attrs);
857         $output .= html_writer::end_tag('div');
859         $output .= html_writer::end_tag('div');
860         return $output;
861     }
864 /**
865  * Data structure representing backup files viewer
866  *
867  * @copyright 2010 Dongsheng Cai
868  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
869  * @since     Moodle 2.0
870  */
871 class backup_files_viewer implements renderable {
873     /**
874      * @var array
875      */
876     public $files;
878     /**
879      * @var context
880      */
881     public $filecontext;
883     /**
884      * @var string
885      */
886     public $component;
888     /**
889      * @var string
890      */
891     public $filearea;
893     /**
894      * @var context
895      */
896     public $currentcontext;
898     /**
899      * Constructor of backup_files_viewer class
900      * @param array $options
901      */
902     public function __construct(array $options = null) {
903         global $CFG, $USER;
904         $fs = get_file_storage();
905         $this->currentcontext = $options['currentcontext'];
906         $this->filecontext    = $options['filecontext'];
907         $this->component      = $options['component'];
908         $this->filearea       = $options['filearea'];
909         $files = $fs->get_area_files($this->filecontext->id, $this->component, $this->filearea, false, 'timecreated');
910         $this->files = array_reverse($files);
911     }