f656577bc781b4b067c7345bbcd47d97dfd36911
[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         $html .= $this->output->heading(get_string('backupsettings', 'backup'), 2, array('class' => 'header'));
129         foreach ($details->root_settings as $label => $value) {
130             if ($label == 'filename' or $label == 'user_files') {
131                 continue;
132             }
133             $html .= $this->backup_detail_pair(get_string('rootsetting'.str_replace('_', '', $label), 'backup'), $value ? $yestick : $notick);
134         }
135         $html .= html_writer::end_tag('div');
137         if ($details->type === 'course') {
138             $html .= html_writer::start_tag('div', array('class' => 'backup-section'));
139             $html .= $this->output->heading(get_string('backupcoursedetails', 'backup'), 2, array('class' => 'header'));
140             $html .= $this->backup_detail_pair(get_string('coursetitle', 'backup'), $details->course->title);
141             $html .= $this->backup_detail_pair(get_string('courseid', 'backup'), $details->course->courseid);
143             // Warning users about front page backups.
144             if ($details->original_course_format === 'site') {
145                 $html .= $this->backup_detail_pair(get_string('type_format', 'plugin'), get_string('sitecourseformatwarning', 'backup'));
146             }
147             $html .= html_writer::start_tag('div', array('class' => 'backup-sub-section'));
148             $html .= $this->output->heading(get_string('backupcoursesections', 'backup'), 3, array('class' => 'subheader'));
149             foreach ($details->sections as $key => $section) {
150                 $included = $key.'_included';
151                 $userinfo = $key.'_userinfo';
152                 if ($section->settings[$included] && $section->settings[$userinfo]) {
153                     $value = get_string('sectionincanduser', 'backup');
154                 } else if ($section->settings[$included]) {
155                     $value = get_string('sectioninc', 'backup');
156                 } else {
157                     continue;
158                 }
159                 $html .= $this->backup_detail_pair(get_string('backupcoursesection', 'backup', $section->title), $value);
160                 $table = null;
161                 foreach ($details->activities as $activitykey => $activity) {
162                     if ($activity->sectionid != $section->sectionid) {
163                         continue;
164                     }
165                     if (empty($table)) {
166                         $table = new html_table();
167                         $table->head = array(get_string('module', 'backup'), get_string('title', 'backup'), get_string('userinfo', 'backup'));
168                         $table->colclasses = array('modulename', 'moduletitle', 'userinfoincluded');
169                         $table->align = array('left', 'left', 'center');
170                         $table->attributes = array('class' => 'activitytable generaltable');
171                         $table->data = array();
172                     }
173                     $name = get_string('pluginname', $activity->modulename);
174                     $icon = new image_icon('icon', $name, $activity->modulename, array('class' => 'iconlarge icon-pre'));
175                     $table->data[] = array(
176                         $this->output->render($icon).$name,
177                         $activity->title,
178                         ($activity->settings[$activitykey.'_userinfo']) ? $yestick : $notick,
179                     );
180                 }
181                 if (!empty($table)) {
182                     $html .= $this->backup_detail_pair(get_string('sectionactivities', 'backup'), html_writer::table($table));
183                 }
185             }
186             $html .= html_writer::end_tag('div');
187             $html .= html_writer::end_tag('div');
188         }
190         $html .= $this->output->single_button($nextstageurl, get_string('continue'), 'post');
191         $html .= html_writer::end_tag('div');
193         return $html;
194     }
196     /**
197      * Displays the general information about a backup file with non-standard format
198      *
199      * @param moodle_url $nextstageurl URL to send user to
200      * @param array $details basic info about the file (format, type)
201      * @return string HTML code to display
202      */
203     public function backup_details_nonstandard($nextstageurl, array $details) {
205         $html  = html_writer::start_tag('div', array('class' => 'backup-restore nonstandardformat'));
206         $html .= html_writer::start_tag('div', array('class' => 'backup-section'));
207         $html .= $this->output->heading(get_string('backupdetails', 'backup'), 2, 'header');
208         $html .= $this->output->box(get_string('backupdetailsnonstandardinfo', 'backup'), 'noticebox');
209         $html .= $this->backup_detail_pair(
210             get_string('backupformat', 'backup'),
211             get_string('backupformat'.$details['format'], 'backup'));
212         $html .= $this->backup_detail_pair(
213             get_string('backuptype', 'backup'),
214             get_string('backuptype'.$details['type'], 'backup'));
215         $html .= html_writer::end_tag('div');
216         $html .= $this->output->single_button($nextstageurl, get_string('continue'), 'post');
217         $html .= html_writer::end_tag('div');
219         return $html;
220     }
222     /**
223      * Displays the general information about a backup file with unknown format
224      *
225      * @param moodle_url $nextstageurl URL to send user to
226      * @return string HTML code to display
227      */
228     public function backup_details_unknown(moodle_url $nextstageurl) {
230         $html  = html_writer::start_div('unknownformat');
231         $html .= $this->output->heading(get_string('errorinvalidformat', 'backup'), 2);
232         $html .= $this->output->notification(get_string('errorinvalidformatinfo', 'backup'), 'notifyproblem');
233         $html .= $this->output->single_button($nextstageurl, get_string('continue'), 'post');
234         $html .= html_writer::end_div();
236         return $html;
237     }
239     /**
240      * Displays a course selector for restore
241      *
242      * @param moodle_url $nextstageurl
243      * @param bool $wholecourse true if we are restoring whole course (as with backup::TYPE_1COURSE), false otherwise
244      * @param restore_category_search $categories
245      * @param restore_course_search $courses
246      * @param int $currentcourse
247      * @return string
248      */
249     public function course_selector(moodle_url $nextstageurl, $wholecourse = true, restore_category_search $categories = null,
250                                     restore_course_search $courses = null, $currentcourse = null) {
251         global $CFG, $PAGE;
252         require_once($CFG->dirroot.'/course/lib.php');
254         // These variables are used to check if the form using this function was submitted.
255         $target = optional_param('target', false, PARAM_INT);
256         $targetid = optional_param('targetid', null, PARAM_INT);
258         // Check if they submitted the form but did not provide all the data we need.
259         $missingdata = false;
260         if ($target and is_null($targetid)) {
261             $missingdata = true;
262         }
264         $nextstageurl->param('sesskey', sesskey());
266         $form = html_writer::start_tag('form', array('method' => 'post', 'action' => $nextstageurl->out_omit_querystring(),
267             'class' => 'mform'));
268         foreach ($nextstageurl->params() as $key => $value) {
269             $form .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => $key, 'value' => $value));
270         }
272         $hasrestoreoption = false;
274         $html  = html_writer::start_tag('div', array('class' => 'backup-course-selector backup-restore'));
275         if ($wholecourse && !empty($categories) && ($categories->get_count() > 0 || $categories->get_search())) {
276             // New course.
277             $hasrestoreoption = true;
278             $html .= $form;
279             $html .= html_writer::start_tag('div', array('class' => 'bcs-new-course backup-section'));
280             $html .= $this->output->heading(get_string('restoretonewcourse', 'backup'), 2, array('class' => 'header'));
281             $html .= $this->backup_detail_input(get_string('restoretonewcourse', 'backup'), 'radio', 'target',
282                 backup::TARGET_NEW_COURSE, array('checked' => 'checked'));
283             $selectacategoryhtml = $this->backup_detail_pair(get_string('selectacategory', 'backup'), $this->render($categories));
284             // Display the category selection as required if the form was submitted but this data was not supplied.
285             if ($missingdata && $target == backup::TARGET_NEW_COURSE) {
286                 $html .= html_writer::span(get_string('required'), 'error');
287                 $html .= html_writer::start_tag('fieldset', array('class' => 'error'));
288                 $html .= $selectacategoryhtml;
289                 $html .= html_writer::end_tag('fieldset');
290             } else {
291                 $html .= $selectacategoryhtml;
292             }
293             $attrs = array('type' => 'submit', 'value' => get_string('continue'), 'class' => 'btn btn-primary');
294             $html .= $this->backup_detail_pair('', html_writer::empty_tag('input', $attrs));
295             $html .= html_writer::end_tag('div');
296             $html .= html_writer::end_tag('form');
297         }
299         if ($wholecourse && !empty($currentcourse)) {
300             // Current course.
301             $hasrestoreoption = true;
302             $html .= $form;
303             $html .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'targetid', 'value' => $currentcourse));
304             $html .= html_writer::start_tag('div', array('class' => 'bcs-current-course backup-section'));
305             $html .= $this->output->heading(get_string('restoretocurrentcourse', 'backup'), 2, array('class' => 'header'));
306             $html .= $this->backup_detail_input(get_string('restoretocurrentcourseadding', 'backup'), 'radio', 'target',
307                 backup::TARGET_CURRENT_ADDING, array('checked' => 'checked'));
308             $html .= $this->backup_detail_input(get_string('restoretocurrentcoursedeleting', 'backup'), 'radio', 'target',
309                 backup::TARGET_CURRENT_DELETING);
310             $attrs = array('type' => 'submit', 'value' => get_string('continue'), 'class' => 'btn btn-primary');
311             $html .= $this->backup_detail_pair('', html_writer::empty_tag('input', $attrs));
312             $html .= html_writer::end_tag('div');
313             $html .= html_writer::end_tag('form');
314         }
316         // If we are restoring an activity, then include the current course.
317         if (!$wholecourse) {
318             $courses->invalidate_results(); // Clean list of courses.
319             $courses->set_include_currentcourse();
320         }
321         if (!empty($courses) && ($courses->get_count() > 0 || $courses->get_search())) {
322             // Existing course.
323             $hasrestoreoption = true;
324             $html .= $form;
325             $html .= html_writer::start_tag('div', array('class' => 'bcs-existing-course backup-section'));
326             $html .= $this->output->heading(get_string('restoretoexistingcourse', 'backup'), 2, array('class' => 'header'));
327             if ($wholecourse) {
328                 $html .= $this->backup_detail_input(get_string('restoretoexistingcourseadding', 'backup'), 'radio', 'target',
329                     backup::TARGET_EXISTING_ADDING, array('checked' => 'checked'));
330                 $html .= $this->backup_detail_input(get_string('restoretoexistingcoursedeleting', 'backup'), 'radio', 'target',
331                     backup::TARGET_EXISTING_DELETING);
332             } else {
333                 $html .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'target', 'value' => backup::TARGET_EXISTING_ADDING));
334             }
335             $selectacoursehtml = $this->backup_detail_pair(get_string('selectacourse', 'backup'), $this->render($courses));
336             // Display the course selection as required if the form was submitted but this data was not supplied.
337             if ($missingdata && $target == backup::TARGET_EXISTING_ADDING) {
338                 $html .= html_writer::span(get_string('required'), 'error');
339                 $html .= html_writer::start_tag('fieldset', array('class' => 'error'));
340                 $html .= $selectacoursehtml;
341                 $html .= html_writer::end_tag('fieldset');
342             } else {
343                 $html .= $selectacoursehtml;
344             }
345             $attrs = array('type' => 'submit', 'value' => get_string('continue'), 'class' => 'btn btn-primary');
346             $html .= $this->backup_detail_pair('', html_writer::empty_tag('input', $attrs));
347             $html .= html_writer::end_tag('div');
348             $html .= html_writer::end_tag('form');
349         }
351         if (!$hasrestoreoption) {
352             echo $this->output->notification(get_string('norestoreoptions', 'backup'));
353         }
355         $html .= html_writer::end_tag('div');
356         return $html;
357     }
359     /**
360      * Displays the import course selector
361      *
362      * @param moodle_url $nextstageurl
363      * @param import_course_search $courses
364      * @return string
365      */
366     public function import_course_selector(moodle_url $nextstageurl, import_course_search $courses = null) {
367         $html  = html_writer::start_tag('div', array('class' => 'import-course-selector backup-restore'));
368         $html .= html_writer::start_tag('form', array('method' => 'post', 'action' => $nextstageurl->out_omit_querystring()));
369         foreach ($nextstageurl->params() as $key => $value) {
370             $html .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => $key, 'value' => $value));
371         }
372         // We only allow import adding for now. Enforce it here.
373         $html .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'target', 'value' => backup::TARGET_CURRENT_ADDING));
374         $html .= html_writer::start_tag('div', array('class' => 'ics-existing-course backup-section'));
375         $html .= $this->output->heading(get_string('importdatafrom'), 2, array('class' => 'header'));
376         $html .= $this->backup_detail_pair(get_string('selectacourse', 'backup'), $this->render($courses));
377         $attrs = array('type' => 'submit', 'value' => get_string('continue'), 'class' => 'btn btn-primary');
378         $html .= $this->backup_detail_pair('', html_writer::empty_tag('input', $attrs));
379         $html .= html_writer::end_tag('div');
380         $html .= html_writer::end_tag('form');
381         $html .= html_writer::end_tag('div');
382         return $html;
383     }
385     /**
386      * Creates a detailed pairing (key + value)
387      *
388      * @staticvar int $count
389      * @param string $label
390      * @param string $value
391      * @return string
392      */
393     protected function backup_detail_pair($label, $value) {
394         static $count = 0;
395         $count ++;
396         $html  = html_writer::start_tag('div', array('class' => 'detail-pair'));
397         $html .= html_writer::tag('label', $label, array('class' => 'detail-pair-label', 'for' => 'detail-pair-value-'.$count));
398         $html .= html_writer::tag('div', $value, array('class' => 'detail-pair-value pl-2', 'name' => 'detail-pair-value-'.$count));
399         $html .= html_writer::end_tag('div');
400         return $html;
401     }
403     /**
404      * Created a detailed pairing with an input
405      *
406      * @param string $label
407      * @param string $type
408      * @param string $name
409      * @param string $value
410      * @param array $attributes
411      * @param string|null $description
412      * @return string
413      */
414     protected function backup_detail_input($label, $type, $name, $value, array $attributes = array(), $description = null) {
415         if (!empty($description)) {
416             $description = html_writer::tag('span', $description, array('class' => 'description'));
417         } else {
418             $description = '';
419         }
420         return $this->backup_detail_pair(
421             $label,
422             html_writer::empty_tag('input', $attributes + array('name' => $name, 'type' => $type, 'value' => $value)) . $description
423         );
424     }
426     /**
427      * Creates a detailed pairing with a select
428      *
429      * @param string $label
430      * @param string $name
431      * @param array $options
432      * @param string $selected
433      * @param bool $nothing
434      * @param array $attributes
435      * @param string|null $description
436      * @return string
437      */
438     protected function backup_detail_select($label, $name, $options, $selected = '', $nothing = false, array $attributes = array(), $description = null) {
439         if (!empty ($description)) {
440             $description = html_writer::tag('span', $description, array('class' => 'description'));
441         } else {
442             $description = '';
443         }
444         return $this->backup_detail_pair($label, html_writer::select($options, $name, $selected, false, $attributes).$description);
445     }
447     /**
448      * Displays precheck notices
449      *
450      * @param array $results
451      * @return string
452      */
453     public function precheck_notices($results) {
454         $output = html_writer::start_tag('div', array('class' => 'restore-precheck-notices'));
455         if (array_key_exists('errors', $results)) {
456             foreach ($results['errors'] as $error) {
457                 $output .= $this->output->notification($error);
458             }
459         }
460         if (array_key_exists('warnings', $results)) {
461             foreach ($results['warnings'] as $warning) {
462                 $output .= $this->output->notification($warning, 'notifyproblem');
463             }
464         }
465         return $output.html_writer::end_tag('div');
466     }
468     /**
469      * Displays substage buttons
470      *
471      * @param bool $haserrors
472      * @return string
473      */
474     public function substage_buttons($haserrors) {
475         $output  = html_writer::start_tag('div', array('continuebutton'));
476         if (!$haserrors) {
477             $attrs = array('type' => 'submit', 'value' => get_string('continue'), 'class' => 'btn btn-primary');
478             $output .= html_writer::empty_tag('input', $attrs);
479         }
480         $attrs = array('type' => 'submit', 'name' => 'cancel', 'value' => get_string('cancel'), 'class' => 'btn btn-secondary');
481         $output .= html_writer::empty_tag('input', $attrs);
482         $output .= html_writer::end_tag('div');
483         return $output;
484     }
486     /**
487      * Displays a role mapping interface
488      *
489      * @param array $rolemappings
490      * @param array $roles
491      * @return string
492      */
493     public function role_mappings($rolemappings, $roles) {
494         $roles[0] = get_string('none');
495         $output  = html_writer::start_tag('div', array('class' => 'restore-rolemappings'));
496         $output .= $this->output->heading(get_string('restorerolemappings', 'backup'), 2);
497         foreach ($rolemappings as $id => $mapping) {
498             $label = $mapping->name;
499             $name = 'mapping'.$id;
500             $selected = $mapping->targetroleid;
501             $output .= $this->backup_detail_select($label, $name, $roles, $mapping->targetroleid, false, array(), $mapping->description);
502         }
503         $output .= html_writer::end_tag('div');
504         return $output;
505     }
507     /**
508      * Displays a continue button
509      *
510      * @param string|moodle_url $url
511      * @param string $method
512      * @return string
513      */
514     public function continue_button($url, $method = 'post') {
515         if (!($url instanceof moodle_url)) {
516             $url = new moodle_url($url);
517         }
518         if ($method != 'post') {
519             $method = 'get';
520         }
521         $url->param('sesskey', sesskey());
522         $button = new single_button($url, get_string('continue'), $method, true);
523         $button->class = 'continuebutton';
524         return $this->render($button);
525     }
526     /**
527      * Print a backup files tree
528      * @param array $options
529      * @return string
530      */
531     public function backup_files_viewer(array $options = null) {
532         $files = new backup_files_viewer($options);
533         return $this->render($files);
534     }
536     /**
537      * Displays a backup files viewer
538      *
539      * @global stdClass $USER
540      * @param backup_files_viewer $viewer
541      * @return string
542      */
543     public function render_backup_files_viewer(backup_files_viewer $viewer) {
544         global $CFG;
545         $files = $viewer->files;
547         $table = new html_table();
548         $table->attributes['class'] = 'backup-files-table generaltable';
549         $table->head = array(get_string('filename', 'backup'), get_string('time'), get_string('size'), get_string('download'), get_string('restore'));
550         $table->width = '100%';
551         $table->data = array();
553         foreach ($files as $file) {
554             if ($file->is_directory()) {
555                 continue;
556             }
557             $fileurl = moodle_url::make_pluginfile_url(
558                 $file->get_contextid(),
559                 $file->get_component(),
560                 $file->get_filearea(),
561                 null,
562                 $file->get_filepath(),
563                 $file->get_filename(),
564                 true
565             );
566             $params = array();
567             $params['action'] = 'choosebackupfile';
568             $params['filename'] = $file->get_filename();
569             $params['filepath'] = $file->get_filepath();
570             $params['component'] = $file->get_component();
571             $params['filearea'] = $file->get_filearea();
572             $params['filecontextid'] = $file->get_contextid();
573             $params['contextid'] = $viewer->currentcontext->id;
574             $params['itemid'] = $file->get_itemid();
575             $restoreurl = new moodle_url('/backup/restorefile.php', $params);
576             $restorelink = html_writer::link($restoreurl, get_string('restore'));
577             $downloadlink = html_writer::link($fileurl, get_string('download'));
579             // Conditional display of the restore and download links, initially only for the 'automated' filearea.
580             if ($params['filearea'] == 'automated') {
581                 if (!has_capability('moodle/restore:viewautomatedfilearea', $viewer->currentcontext)) {
582                     $restorelink = '';
583                 }
584                 if (!can_download_from_backup_filearea($params['filearea'], $viewer->currentcontext)) {
585                     $downloadlink = '';
586                 }
587             }
588             $table->data[] = array(
589                 $file->get_filename(),
590                 userdate($file->get_timemodified()),
591                 display_size($file->get_filesize()),
592                 $downloadlink,
593                 $restorelink,
594                 );
595         }
597         $html = html_writer::table($table);
599         // For automated backups, the ability to manage backup files is controlled by the ability to download them.
600         // All files must be from the same file area in a backup_files_viewer.
601         $canmanagebackups = true;
602         if ($viewer->filearea == 'automated') {
603             if (!can_download_from_backup_filearea($viewer->filearea, $viewer->currentcontext)) {
604                 $canmanagebackups = false;
605             }
606         }
608         if ($canmanagebackups) {
609             $html .= $this->output->single_button(
610                 new moodle_url('/backup/backupfilesedit.php', array(
611                         'currentcontext' => $viewer->currentcontext->id,
612                         'contextid' => $viewer->filecontext->id,
613                         'filearea' => $viewer->filearea,
614                         'component' => $viewer->component,
615                         'returnurl' => $this->page->url->out())
616                 ),
617                 get_string('managefiles', 'backup'),
618                 'post'
619             );
620         }
622         return $html;
623     }
625     /**
626      * Renders a restore course search object
627      *
628      * @param restore_course_search $component
629      * @return string
630      */
631     public function render_restore_course_search(restore_course_search $component) {
632         $url = $component->get_url();
634         $output = html_writer::start_tag('div', array('class' => 'restore-course-search form-inline m-b-1'));
635         $output .= html_writer::start_tag('div', array('class' => 'rcs-results'));
637         $table = new html_table();
638         $table->head = array('', get_string('shortnamecourse'), get_string('fullnamecourse'));
639         $table->data = array();
640         if ($component->get_count() !== 0) {
641             foreach ($component->get_results() as $course) {
642                 $row = new html_table_row();
643                 $row->attributes['class'] = 'rcs-course';
644                 if (!$course->visible) {
645                     $row->attributes['class'] .= ' dimmed';
646                 }
647                 $row->cells = array(
648                     html_writer::empty_tag('input', array('type' => 'radio', 'name' => 'targetid', 'value' => $course->id)),
649                     format_string($course->shortname, true, array('context' => context_course::instance($course->id))),
650                     format_string($course->fullname, true, array('context' => context_course::instance($course->id)))
651                 );
652                 $table->data[] = $row;
653             }
654             if ($component->has_more_results()) {
655                 $cell = new html_table_cell(get_string('moreresults', 'backup'));
656                 $cell->colspan = 3;
657                 $cell->attributes['class'] = 'notifyproblem';
658                 $row = new html_table_row(array($cell));
659                 $row->attributes['class'] = 'rcs-course';
660                 $table->data[] = $row;
661             }
662         } else {
663             $cell = new html_table_cell(get_string('nomatchingcourses', 'backup'));
664             $cell->colspan = 3;
665             $cell->attributes['class'] = 'notifyproblem';
666             $row = new html_table_row(array($cell));
667             $row->attributes['class'] = 'rcs-course';
668             $table->data[] = $row;
669         }
670         $output .= html_writer::table($table);
671         $output .= html_writer::end_tag('div');
673         $output .= html_writer::start_tag('div', array('class' => 'rcs-search'));
674         $attrs = array(
675             'type' => 'text',
676             'name' => restore_course_search::$VAR_SEARCH,
677             'value' => $component->get_search(),
678             'class' => 'form-control'
679         );
680         $output .= html_writer::empty_tag('input', $attrs);
681         $attrs = array(
682             'type' => 'submit',
683             'name' => 'searchcourses',
684             'value' => get_string('search'),
685             'class' => 'btn btn-secondary'
686         );
687         $output .= html_writer::empty_tag('input', $attrs);
688         $output .= html_writer::end_tag('div');
690         $output .= html_writer::end_tag('div');
691         return $output;
692     }
694     /**
695      * Renders an import course search object
696      *
697      * @param import_course_search $component
698      * @return string
699      */
700     public function render_import_course_search(import_course_search $component) {
701         $url = $component->get_url();
703         $output = html_writer::start_tag('div', array('class' => 'import-course-search'));
704         if ($component->get_count() === 0) {
705             $output .= $this->output->notification(get_string('nomatchingcourses', 'backup'));
707             $output .= html_writer::start_tag('div', array('class' => 'ics-search'));
708             $attrs = array(
709                 'type' => 'text',
710                 'name' => restore_course_search::$VAR_SEARCH,
711                 'value' => $component->get_search(),
712                 'class' => 'form-control'
713             );
714             $output .= html_writer::empty_tag('input', $attrs);
715             $attrs = array(
716                 'type' => 'submit',
717                 'name' => 'searchcourses',
718                 'value' => get_string('search'),
719                 'class' => 'btn btn-secondary'
720             );
721             $output .= html_writer::empty_tag('input', $attrs);
722             $output .= html_writer::end_tag('div');
724             $output .= html_writer::end_tag('div');
725             return $output;
726         }
728         $countstr = '';
729         if ($component->has_more_results()) {
730             $countstr = get_string('morecoursesearchresults', 'backup', $component->get_count());
731         } else {
732             $countstr = get_string('totalcoursesearchresults', 'backup', $component->get_count());
733         }
735         $output .= html_writer::tag('div', $countstr, array('class' => 'ics-totalresults'));
736         $output .= html_writer::start_tag('div', array('class' => 'ics-results'));
738         $table = new html_table();
739         $table->head = array('', get_string('shortnamecourse'), get_string('fullnamecourse'));
740         $table->data = array();
741         foreach ($component->get_results() as $course) {
742             $row = new html_table_row();
743             $row->attributes['class'] = 'ics-course';
744             if (!$course->visible) {
745                 $row->attributes['class'] .= ' dimmed';
746             }
747             $row->cells = array(
748                 html_writer::empty_tag('input', array('type' => 'radio', 'name' => 'importid', 'value' => $course->id)),
749                 format_string($course->shortname, true, array('context' => context_course::instance($course->id))),
750                 format_string($course->fullname, true, array('context' => context_course::instance($course->id)))
751             );
752             $table->data[] = $row;
753         }
754         if ($component->has_more_results()) {
755             $cell = new html_table_cell(get_string('moreresults', 'backup'));
756             $cell->colspan = 3;
757             $cell->attributes['class'] = 'notifyproblem';
758             $row = new html_table_row(array($cell));
759             $row->attributes['class'] = 'rcs-course';
760             $table->data[] = $row;
761         }
762         $output .= html_writer::table($table);
763         $output .= html_writer::end_tag('div');
765         $output .= html_writer::start_tag('div', array('class' => 'ics-search'));
766         $attrs = array(
767             'type' => 'text',
768             'name' => restore_course_search::$VAR_SEARCH,
769             'value' => $component->get_search(),
770             'class' => 'form-control');
771         $output .= html_writer::empty_tag('input', $attrs);
772         $attrs = array(
773             'type' => 'submit',
774             'name' => 'searchcourses',
775             'value' => get_string('search'),
776             'class' => 'btn btn-secondary'
777         );
778         $output .= html_writer::empty_tag('input', $attrs);
779         $output .= html_writer::end_tag('div');
781         $output .= html_writer::end_tag('div');
782         return $output;
783     }
785     /**
786      * Renders a restore category search object
787      *
788      * @param restore_category_search $component
789      * @return string
790      */
791     public function render_restore_category_search(restore_category_search $component) {
792         $url = $component->get_url();
794         $output = html_writer::start_tag('div', array('class' => 'restore-course-search form-inline m-b-1'));
795         $output .= html_writer::start_tag('div', array('class' => 'rcs-results w-100'));
797         $table = new html_table();
798         $table->head = array('', get_string('name'), get_string('description'));
799         $table->data = array();
801         if ($component->get_count() !== 0) {
802             foreach ($component->get_results() as $category) {
803                 $row = new html_table_row();
804                 $row->attributes['class'] = 'rcs-course';
805                 if (!$category->visible) {
806                     $row->attributes['class'] .= ' dimmed';
807                 }
808                 $context = context_coursecat::instance($category->id);
809                 $row->cells = array(
810                     html_writer::empty_tag('input', array('type' => 'radio', 'name' => 'targetid', 'value' => $category->id)),
811                     format_string($category->name, true, array('context' => context_coursecat::instance($category->id))),
812                     format_text(file_rewrite_pluginfile_urls($category->description, 'pluginfile.php', $context->id,
813                         'coursecat', 'description', null), $category->descriptionformat, array('overflowdiv' => true))
814                 );
815                 $table->data[] = $row;
816             }
817             if ($component->has_more_results()) {
818                 $cell = new html_table_cell(get_string('moreresults', 'backup'));
819                 $cell->attributes['class'] = 'notifyproblem';
820                 $cell->colspan = 3;
821                 $row = new html_table_row(array($cell));
822                 $row->attributes['class'] = 'rcs-course';
823                 $table->data[] = $row;
824             }
825         } else {
826             $cell = new html_table_cell(get_string('nomatchingcourses', 'backup'));
827             $cell->colspan = 3;
828             $cell->attributes['class'] = 'notifyproblem';
829             $row = new html_table_row(array($cell));
830             $row->attributes['class'] = 'rcs-course';
831             $table->data[] = $row;
832         }
833         $output .= html_writer::table($table);
834         $output .= html_writer::end_tag('div');
836         $output .= html_writer::start_tag('div', array('class' => 'rcs-search'));
837         $attrs = array(
838             'type' => 'text',
839             'name' => restore_category_search::$VAR_SEARCH,
840             'value' => $component->get_search(),
841             'class' => 'form-control'
842         );
843         $output .= html_writer::empty_tag('input', $attrs);
844         $attrs = array(
845             'type' => 'submit',
846             'name' => 'searchcourses',
847             'value' => get_string('search'),
848             'class' => 'btn btn-secondary'
849         );
850         $output .= html_writer::empty_tag('input', $attrs);
851         $output .= html_writer::end_tag('div');
853         $output .= html_writer::end_tag('div');
854         return $output;
855     }
858 /**
859  * Data structure representing backup files viewer
860  *
861  * @copyright 2010 Dongsheng Cai
862  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
863  * @since     Moodle 2.0
864  */
865 class backup_files_viewer implements renderable {
867     /**
868      * @var array
869      */
870     public $files;
872     /**
873      * @var context
874      */
875     public $filecontext;
877     /**
878      * @var string
879      */
880     public $component;
882     /**
883      * @var string
884      */
885     public $filearea;
887     /**
888      * @var context
889      */
890     public $currentcontext;
892     /**
893      * Constructor of backup_files_viewer class
894      * @param array $options
895      */
896     public function __construct(array $options = null) {
897         global $CFG, $USER;
898         $fs = get_file_storage();
899         $this->currentcontext = $options['currentcontext'];
900         $this->filecontext    = $options['filecontext'];
901         $this->component      = $options['component'];
902         $this->filearea       = $options['filearea'];
903         $files = $fs->get_area_files($this->filecontext->id, $this->component, $this->filearea, false, 'timecreated');
904         $this->files = array_reverse($files);
905     }