MDL-22309 report_singleview: Fixing wrong uses of get_role_users()
[moodle.git] / backup / util / ui / restore_ui_stage.class.php
1 <?php
3 // This file is part of Moodle - http://moodle.org/
4 //
5 // Moodle is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // Moodle is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
18 /**
19  * restore user interface stages
20  *
21  * This file contains the classes required to manage the stages that make up the
22  * restore user interface.
23  * These will be primarily operated a {@see restore_ui} instance.
24  *
25  * @package   moodlecore
26  * @copyright 2010 Sam Hemelryk
27  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
28  */
30 /**
31  * Abstract stage class
32  *
33  * This class should be extended by all restore stages (a requirement of many restore ui functions).
34  * Each stage must then define two abstract methods
35  *  - process : To process the stage
36  *  - initialise_stage_form : To get a restore_moodleform instance for the stage
37  *
38  * @copyright 2010 Sam Hemelryk
39  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
40  */
41 abstract class restore_ui_stage extends base_ui_stage {
42     /**
43      * Constructor
44      * @param restore_ui $ui
45      */
46     public function __construct(restore_ui $ui, array $params=null) {
47         $this->ui = $ui;
48         $this->params = $params;
49     }
50     /**
51      * The restore id from the restore controller
52      * @return string
53      */
54     final public function get_restoreid() {
55         return $this->get_uniqueid();
56     }
57     /**
58      * This is an independent stage
59      * @return int
60      */
61     final public function is_independent() {
62         return false;
63     }
65     /**
66      * No sub stages for this stage
67      * @return false
68      */
69     public function has_sub_stages() {
70         return false;
71     }
73     /**
74      * The name of this stage
75      * @return string
76      */
77     final public function get_name() {
78         return get_string('restorestage'.$this->stage,'backup');
79     }
80     /**
81      * Returns true if this is the settings stage
82      * @return bool
83      */
84     final public function is_first_stage() {
85         return $this->stage == restore_ui::STAGE_SETTINGS;
86     }
87 }
89 /**
90  * Abstract class used to represent a restore stage that is indenependent.
91  *
92  * An independent stage is a judged to be so because it doesn't require, and has
93  * no use for the restore controller.
94  */
95 abstract class restore_ui_independent_stage {
96     /**
97      * @var \core\progress\base Optional progress reporter
98      */
99     private $progressreporter;
101     abstract public function __construct($contextid);
102     abstract public function process();
103     abstract public function display(core_backup_renderer $renderer);
104     abstract public function get_stage();
106     /**
107      * Gets the progress reporter object in use for this restore UI stage.
108      *
109      * IMPORTANT: This progress reporter is used only for UI progress that is
110      * outside the restore controller. The restore controller has its own
111      * progress reporter which is used for progress during the main restore.
112      * Use the restore controller's progress reporter to report progress during
113      * a restore operation, not this one.
114      *
115      * This extra reporter is necessary because on some restore UI screens,
116      * there are long-running tasks even though there is no restore controller
117      * in use. There is a similar function in restore_ui. but that class is not
118      * used on some stages.
119      *
120      * @return \core\progress\null
121      */
122     public function get_progress_reporter() {
123         if (!$this->progressreporter) {
124             $this->progressreporter = new \core\progress\null();
125         }
126         return $this->progressreporter;
127     }
129     /**
130      * Sets the progress reporter that will be returned by get_progress_reporter.
131      *
132      * @param \core\progress\base $progressreporter Progress reporter
133      */
134     public function set_progress_reporter(\core\progress\base $progressreporter) {
135         $this->progressreporter = $progressreporter;
136     }
138     /**
139      * Gets an array of progress bar items that can be displayed through the restore renderer.
140      * @return array Array of items for the progress bar
141      */
142     public function get_progress_bar() {
143         global $PAGE;
144         $stage = restore_ui::STAGE_COMPLETE;
145         $currentstage = $this->get_stage();
146         $items = array();
147         while ($stage > 0) {
148             $classes = array('backup_stage');
149             if (floor($stage/2) == $currentstage) {
150                 $classes[] = 'backup_stage_next';
151             } else if ($stage == $currentstage) {
152                 $classes[] = 'backup_stage_current';
153             } else if ($stage < $currentstage) {
154                 $classes[] = 'backup_stage_complete';
155             }
156             $item = array('text' => strlen(decbin($stage)).'. '.get_string('restorestage'.$stage, 'backup'),'class' => join(' ', $classes));
157             if ($stage < $currentstage && $currentstage < restore_ui::STAGE_COMPLETE) {
158                 //$item['link'] = new moodle_url($PAGE->url, array('restore'=>$this->get_restoreid(), 'stage'=>$stage));
159             }
160             array_unshift($items, $item);
161             $stage = floor($stage/2);
162         }
163         return $items;
164     }
165     abstract public function get_stage_name();
166     /**
167      * Obviously true
168      * @return true
169      */
170     final public function is_independent() {
171         return true;
172     }
173     public function destroy() {
174         // Nothing to destroy here!
175     }
178 /**
179  * The confirmation stage.
180  *
181  * This is the first stage, it is independent.
182  */
183 class restore_ui_stage_confirm extends restore_ui_independent_stage implements file_progress {
185     protected $contextid;
186     protected $filename = null;
187     protected $filepath = null;
189     /**
190      * @var string Content hash of archive file to restore (if specified by hash)
191      */
192     protected $contenthash = null;
193     /**
194      * @var string Pathname hash of stored_file object to restore
195      */
196     protected $pathnamehash = null;
198     protected $details;
200     /**
201      * @var bool True if we have started reporting progress
202      */
203     protected $startedprogress = false;
205     public function __construct($contextid) {
206         $this->contextid = $contextid;
207         $this->filename = optional_param('filename', null, PARAM_FILE);
208         if ($this->filename === null) {
209             // Identify file object by its pathname hash.
210             $this->pathnamehash = required_param('pathnamehash', PARAM_ALPHANUM);
212             // The file content hash is also passed for security; users
213             // cannot guess the content hash (unless they know the file contents),
214             // so this guarantees that either the system generated this link or
215             // else the user has access to the restore archive anyhow.
216             $this->contenthash = required_param('contenthash', PARAM_ALPHANUM);
217         }
218     }
220     public function process() {
221         global $CFG;
222         if ($this->filename) {
223             $archivepath = $CFG->tempdir . '/backup/' . $this->filename;
224             if (!file_exists($archivepath)) {
225                 throw new restore_ui_exception('invalidrestorefile');
226             }
227             $outcome = $this->extract_file_to_dir($archivepath);
228             if ($outcome) {
229                 fulldelete($archivepath);
230             }
231         } else {
232             $fs = get_file_storage();
233             $storedfile = $fs->get_file_by_hash($this->pathnamehash);
234             if (!$storedfile || $storedfile->get_contenthash() !== $this->contenthash) {
235                 throw new restore_ui_exception('invalidrestorefile');
236             }
237             $outcome = $this->extract_file_to_dir($storedfile);
238         }
239         return $outcome;
240     }
242     /**
243      * Extracts the file.
244      *
245      * @param string|stored_file $source Archive file to extract
246      */
247     protected function extract_file_to_dir($source) {
248         global $CFG, $USER;
250         $this->filepath = restore_controller::get_tempdir_name($this->contextid, $USER->id);
252         $fb = get_file_packer('application/vnd.moodle.backup');
253         $result = $fb->extract_to_pathname($source,
254                 $CFG->tempdir . '/backup/' . $this->filepath . '/', null, $this);
256         // If any progress happened, end it.
257         if ($this->startedprogress) {
258             $this->get_progress_reporter()->end_progress();
259         }
260         return $result;
261     }
263     /**
264      * Implementation for file_progress interface to display unzip progress.
265      *
266      * @param int $progress Current progress
267      * @param int $max Max value
268      */
269     public function progress($progress = file_progress::INDETERMINATE, $max = file_progress::INDETERMINATE) {
270         $reporter = $this->get_progress_reporter();
272         // Start tracking progress if necessary.
273         if (!$this->startedprogress) {
274             $reporter->start_progress('extract_file_to_dir',
275                     ($max == file_progress::INDETERMINATE) ? \core\progress\base::INDETERMINATE : $max);
276             $this->startedprogress = true;
277         }
279         // Pass progress through to whatever handles it.
280         $reporter->progress(
281                 ($progress == file_progress::INDETERMINATE) ? \core\progress\base::INDETERMINATE : $progress);
282     }
284     /**
285      * Renders the confirmation stage screen
286      *
287      * @param core_backup_renderer $renderer renderer instance to use
288      * @return string HTML code
289      */
290     public function display(core_backup_renderer $renderer) {
292         $prevstageurl = new moodle_url('/backup/restorefile.php', array('contextid' => $this->contextid));
293         $nextstageurl = new moodle_url('/backup/restore.php', array(
294             'contextid' => $this->contextid,
295             'filepath'  => $this->filepath,
296             'stage'     => restore_ui::STAGE_DESTINATION));
298         $format = backup_general_helper::detect_backup_format($this->filepath);
300         if ($format === backup::FORMAT_UNKNOWN) {
301             // unknown format - we can't do anything here
302             return $renderer->backup_details_unknown($prevstageurl);
304         } else if ($format !== backup::FORMAT_MOODLE) {
305             // non-standard format to be converted
306             $details = array('format' => $format, 'type' => backup::TYPE_1COURSE); // todo type to be returned by a converter
307             return $renderer->backup_details_nonstandard($nextstageurl, $details);
309         } else {
310             // standard MBZ backup, let us get information from it and display
311             $this->details = backup_general_helper::get_backup_information($this->filepath);
312             return $renderer->backup_details($this->details, $nextstageurl);
313         }
314     }
316     public function get_stage_name() {
317         return get_string('restorestage'.restore_ui::STAGE_CONFIRM, 'backup');
318     }
319     public function get_stage() {
320         return restore_ui::STAGE_CONFIRM;
321     }
324 /**
325  * This is the destination stage.
326  *
327  * This stage is the second stage and is also independent
328  */
329 class restore_ui_stage_destination extends restore_ui_independent_stage {
330     protected $contextid;
331     protected $filepath = null;
332     protected $courseid = null;
333     protected $target = backup::TARGET_NEW_COURSE;
334     protected $coursesearch = null;
335     protected $categorysearch = null;
336     public function __construct($contextid) {
337         global $PAGE;
338         $this->contextid = $contextid;
339         $this->filepath = required_param('filepath', PARAM_ALPHANUM);
340         $url = new moodle_url($PAGE->url, array(
341             'filepath'=>$this->filepath,
342             'contextid'=>$this->contextid,
343             'stage'=>restore_ui::STAGE_DESTINATION));
344         $this->coursesearch = new restore_course_search(array('url'=>$url), context::instance_by_id($contextid)->instanceid);
345         $this->categorysearch = new restore_category_search(array('url'=>$url));
346     }
347     public function process() {
348         global $CFG, $DB;
349         if (!file_exists("$CFG->tempdir/backup/".$this->filepath) || !is_dir("$CFG->tempdir/backup/".$this->filepath)) {
350             throw new restore_ui_exception('invalidrestorepath');
351         }
352         if (optional_param('searchcourses', false, PARAM_BOOL)) {
353             return false;
354         }
355         $this->target = optional_param('target', backup::TARGET_NEW_COURSE, PARAM_INT);
356         $targetid = optional_param('targetid', null, PARAM_INT);
357         if (!is_null($this->target) && !is_null($targetid) && confirm_sesskey()) {
358             if ($this->target == backup::TARGET_NEW_COURSE) {
359                 list($fullname, $shortname) = restore_dbops::calculate_course_names(0, get_string('restoringcourse', 'backup'), get_string('restoringcourseshortname', 'backup'));
360                 $this->courseid = restore_dbops::create_new_course($fullname, $shortname, $targetid);
361             } else {
362                 $this->courseid = $targetid;
363             }
364             return ($DB->record_exists('course', array('id'=>$this->courseid)));
365         }
366         return false;
367     }
369     /**
370      * Renders the destination stage screen
371      *
372      * @param core_backup_renderer $renderer renderer instance to use
373      * @return string HTML code
374      */
375     public function display(core_backup_renderer $renderer) {
377         $format = backup_general_helper::detect_backup_format($this->filepath);
379         if ($format === backup::FORMAT_MOODLE) {
380             // standard Moodle 2 format, let use get the type of the backup
381             $details = backup_general_helper::get_backup_information($this->filepath);
382             if ($details->type === backup::TYPE_1COURSE) {
383                 $wholecourse = true;
384             } else {
385                 $wholecourse = false;
386             }
388         } else {
389             // non-standard format to be converted. We assume it contains the
390             // whole course for now. However, in the future there might be a callback
391             // to the installed converters
392             $wholecourse = true;
393         }
395         $nextstageurl = new moodle_url('/backup/restore.php', array(
396             'contextid' => $this->contextid,
397             'filepath'  => $this->filepath,
398             'stage'     => restore_ui::STAGE_SETTINGS));
399         $context = context::instance_by_id($this->contextid);
401         if ($context->contextlevel == CONTEXT_COURSE and has_capability('moodle/restore:restorecourse', $context)) {
402             $currentcourse = $context->instanceid;
403         } else {
404             $currentcourse = false;
405         }
407         return $renderer->course_selector($nextstageurl, $wholecourse, $this->categorysearch, $this->coursesearch, $currentcourse);
408     }
410     public function get_stage_name() {
411         return get_string('restorestage'.restore_ui::STAGE_DESTINATION, 'backup');
412     }
413     public function get_filepath() {
414         return $this->filepath;
415     }
416     public function get_course_id() {
417         return $this->courseid;
418     }
419     public function get_stage() {
420         return restore_ui::STAGE_DESTINATION;
421     }
422     public function get_target() {
423         return $this->target;
424     }
426 /**
427  * This stage is the settings stage.
428  *
429  * This stage is the third stage, it is dependent on a restore controller and
430  * is the first stage as such.
431  */
432 class restore_ui_stage_settings extends restore_ui_stage {
433     /**
434      * Initial restore stage constructor
435      * @param restore_ui $ui
436      */
437     public function __construct(restore_ui $ui, array $params=null) {
438         $this->stage = restore_ui::STAGE_SETTINGS;
439         parent::__construct($ui, $params);
440     }
442     public function process(base_moodleform $form=null) {
443         $form = $this->initialise_stage_form();
445         if ($form->is_cancelled()) {
446             $this->ui->cancel_process();
447         }
449         $data = $form->get_data();
450         if ($data && confirm_sesskey()) {
451             $tasks = $this->ui->get_tasks();
452             $changes = 0;
453             foreach ($tasks as &$task) {
454                 // We are only interesting in the backup root task for this stage
455                 if ($task instanceof restore_root_task || $task instanceof restore_course_task) {
456                     // Get all settings into a var so we can iterate by reference
457                     $settings = $task->get_settings();
458                     foreach ($settings as &$setting) {
459                         $name = $setting->get_ui_name();
460                         if (isset($data->$name) &&  $data->$name != $setting->get_value()) {
461                             $setting->set_value($data->$name);
462                             $changes++;
463                         } else if (!isset($data->$name) && $setting->get_ui_type() == backup_setting::UI_HTML_CHECKBOX && $setting->get_value()) {
464                             $setting->set_value(0);
465                             $changes++;
466                         }
467                     }
468                 }
469             }
470             // Return the number of changes the user made
471             return $changes;
472         } else {
473             return false;
474         }
475     }
477     protected function initialise_stage_form() {
478         global $PAGE;
479         if ($this->stageform === null) {
480             $form = new restore_settings_form($this, $PAGE->url);
481             // Store as a variable so we can iterate by reference
482             $tasks = $this->ui->get_tasks();
483             $headingprinted = false;
484             // Iterate all tasks by reference
485             foreach ($tasks as &$task) {
486                 // For the initial stage we are only interested in the root settings
487                 if ($task instanceof restore_root_task) {
488                     if (!$headingprinted) {
489                         $form->add_heading('rootsettings', get_string('restorerootsettings', 'backup'));
490                         $headingprinted = true;
491                     }
492                     $settings = $task->get_settings();
493                     // First add all settings except the filename setting
494                     foreach ($settings as &$setting) {
495                         if ($setting->get_name() == 'filename') {
496                             continue;
497                         }
498                         $form->add_setting($setting, $task);
499                     }
500                     // Then add all dependencies
501                     foreach ($settings as &$setting) {
502                         if ($setting->get_name() == 'filename') {
503                             continue;
504                         }
505                         $form->add_dependencies($setting);
506                     }
507                 }
508             }
509             $this->stageform = $form;
510         }
511         // Return the form
512         return $this->stageform;
513     }
516 /**
517  * Schema stage of backup process
518  *
519  * During the schema stage the user is required to set the settings that relate
520  * to the area that they are backing up as well as its children.
521  *
522  * @copyright 2010 Sam Hemelryk
523  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
524  */
525 class restore_ui_stage_schema extends restore_ui_stage {
526     /**
527      * @var int Maximum number of settings to add to form at once
528      */
529     const MAX_SETTINGS_BATCH = 1000;
531     /**
532      * Schema stage constructor
533      * @param backup_moodleform $ui
534      */
535     public function __construct(restore_ui $ui, array $params=null) {
536         $this->stage = restore_ui::STAGE_SCHEMA;
537         parent::__construct($ui, $params);
538     }
539     /**
540      * Processes the schema stage
541      *
542      * @param backup_moodleform|null $form
543      * @return int The number of changes the user made
544      */
545     public function process(base_moodleform $form = null) {
546         $form = $this->initialise_stage_form();
547         // Check it wasn't cancelled
548         if ($form->is_cancelled()) {
549             $this->ui->cancel_process();
550         }
552         // Check it has been submit
553         $data = $form->get_data();
554         if ($data && confirm_sesskey()) {
555             // Get the tasks into a var so we can iterate by reference
556             $tasks = $this->ui->get_tasks();
557             $changes = 0;
558             // Iterate all tasks by reference
559             foreach ($tasks as &$task) {
560                 // We are only interested in schema settings
561                 if (!($task instanceof restore_root_task)) {
562                     // Store as a variable so we can iterate by reference
563                     $settings = $task->get_settings();
564                     // Iterate by reference
565                     foreach ($settings as &$setting) {
566                         $name = $setting->get_ui_name();
567                         if (isset($data->$name) &&  $data->$name != $setting->get_value()) {
568                             $setting->set_value($data->$name);
569                             $changes++;
570                         } else if (!isset($data->$name) && $setting->get_ui_type() == backup_setting::UI_HTML_CHECKBOX && $setting->get_value()) {
571                             $setting->set_value(0);
572                             $changes++;
573                         }
574                     }
575                 }
576             }
577             // Return the number of changes the user made
578             return $changes;
579         } else {
580             return false;
581         }
582     }
583     /**
584      * Creates the backup_schema_form instance for this stage
585      *
586      * @return backup_schema_form
587      */
588     protected function initialise_stage_form() {
589         global $PAGE;
590         if ($this->stageform === null) {
591             $form = new restore_schema_form($this, $PAGE->url);
592             $tasks = $this->ui->get_tasks();
593             $courseheading = false;
595             // Track progress through each stage.
596             $progress = $this->ui->get_progress_reporter();
597             $progress->start_progress('Initialise schema stage form', 3);
599             $progress->start_progress('', count($tasks));
600             $done = 1;
601             $allsettings = array();
602             foreach ($tasks as $task) {
603                 if (!($task instanceof restore_root_task)) {
604                     if (!$courseheading) {
605                         // If we havn't already display a course heading to group nicely
606                         $form->add_heading('coursesettings', get_string('coursesettings', 'backup'));
607                         $courseheading = true;
608                     }
609                     // Put each setting into an array of settings to add. Adding
610                     // a setting individually is a very slow operation, so we add
611                     // them all in a batch later on.
612                     foreach ($task->get_settings() as $setting) {
613                         $allsettings[] = array($setting, $task);
614                     }
615                 } else if ($this->ui->enforce_changed_dependencies()) {
616                     // Only show these settings if dependencies changed them.
617                     // Add a root settings heading to group nicely
618                     $form->add_heading('rootsettings', get_string('rootsettings', 'backup'));
619                     // Iterate all settings and add them to the form as a fixed
620                     // setting. We only want schema settings to be editable
621                     foreach ($task->get_settings() as $setting) {
622                         if ($setting->get_name() != 'filename') {
623                             $form->add_fixed_setting($setting, $task);
624                         }
625                     }
626                 }
627                 // Update progress.
628                 $progress->progress($done++);
629             }
630             $progress->end_progress();
632             // Add settings for tasks in batches of up to 1000. Adding settings
633             // in larger batches improves performance, but if it takes too long,
634             // we won't be able to update the progress bar so the backup might
635             // time out. 1000 is chosen to balance this.
636             $numsettings = count($allsettings);
637             $progress->start_progress('', ceil($numsettings / self::MAX_SETTINGS_BATCH));
638             $start = 0;
639             $done = 1;
640             while($start < $numsettings) {
641                 $length = min(self::MAX_SETTINGS_BATCH, $numsettings - $start);
642                 $form->add_settings(array_slice($allsettings, $start, $length));
643                 $start += $length;
644                 $progress->progress($done++);
645             }
646             $progress->end_progress();
648             // Add the dependencies for all the settings.
649             $progress->start_progress('', count($allsettings));
650             $done = 1;
651             foreach ($allsettings as $settingtask) {
652                 $form->add_dependencies($settingtask[0]);
653                 $progress->progress($done++);
654             }
655             $progress->end_progress();
657             $progress->end_progress();
658             $this->stageform = $form;
659         }
660         return $this->stageform;
661     }
664 /**
665  * Confirmation stage
666  *
667  * On this stage the user reviews the setting for the backup and can change the filename
668  * of the file that will be generated.
669  *
670  * @copyright 2010 Sam Hemelryk
671  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
672  */
673 class restore_ui_stage_review extends restore_ui_stage {
674     /**
675      * Constructs the stage
676      * @param restore_ui $ui
677      */
678     public function __construct($ui, array $params=null) {
679         $this->stage = restore_ui::STAGE_REVIEW;
680         parent::__construct($ui, $params);
681     }
682     /**
683      * Processes the confirmation stage
684      *
685      * @param backup_moodleform $form
686      * @return int The number of changes the user made
687      */
688     public function process(base_moodleform $form = null) {
689         $form = $this->initialise_stage_form();
690         // Check it hasn't been cancelled
691         if ($form->is_cancelled()) {
692             $this->ui->cancel_process();
693         }
695         $data = $form->get_data();
696         if ($data && confirm_sesskey()) {
697             return 0;
698         } else {
699             return false;
700         }
701     }
702     /**
703      * Creates the backup_confirmation_form instance this stage requires
704      *
705      * @return backup_confirmation_form
706      */
707     protected function initialise_stage_form() {
708         global $PAGE;
709         if ($this->stageform === null) {
710             // Get the form
711             $form = new restore_review_form($this, $PAGE->url);
712             $content = '';
713             $courseheading = false;
715             $progress = $this->ui->get_progress_reporter();
716             $tasks = $this->ui->get_tasks();
717             $progress->start_progress('initialise_stage_form', count($tasks));
718             $done = 1;
719             foreach ($tasks as $task) {
720                 if ($task instanceof restore_root_task) {
721                     // If its a backup root add a root settings heading to group nicely
722                     $form->add_heading('rootsettings', get_string('rootsettings', 'backup'));
723                 } else if (!$courseheading) {
724                     // we havn't already add a course heading
725                     $form->add_heading('coursesettings', get_string('coursesettings', 'backup'));
726                     $courseheading = true;
727                 }
728                 // Iterate all settings, doesnt need to happen by reference
729                 foreach ($task->get_settings() as $setting) {
730                     $form->add_fixed_setting($setting, $task);
731                 }
732                 // Update progress.
733                 $progress->progress($done++);
734             }
735             $progress->end_progress();
736             $this->stageform = $form;
737         }
738         return $this->stageform;
739     }
742 /**
743  * Final stage of backup
744  *
745  * This stage is special in that it is does not make use of a form. The reason for
746  * this is the order of procession of backup at this stage.
747  * The processesion is:
748  * 1. The final stage will be intialise.
749  * 2. The confirmation stage will be processed.
750  * 3. The backup will be executed
751  * 4. The complete stage will be loaded by execution
752  * 5. The complete stage will be displayed
753  *
754  * This highlights that we neither need a form nor a display method for this stage
755  * we simply need to process.
756  *
757  * @copyright 2010 Sam Hemelryk
758  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
759  */
760 class restore_ui_stage_process extends restore_ui_stage {
762     const SUBSTAGE_NONE = 0;
763     const SUBSTAGE_PRECHECKS = 2;
765     protected $substage = 0;
767     /**
768      * Constructs the final stage
769      * @param backup_ui $ui
770      */
771     public function __construct(base_ui $ui, array $params=null) {
772         $this->stage = restore_ui::STAGE_PROCESS;
773         parent::__construct($ui, $params);
774     }
775     /**
776      * Processes the final stage.
777      *
778      * In this case it checks to see if there is a sub stage that we need to display
779      * before execution, if there is we gear up to display the subpage, otherwise
780      * we return true which will lead to execution of the restore and the loading
781      * of the completed stage.
782      */
783     public function process(base_moodleform $form=null) {
784         if (optional_param('cancel', false, PARAM_BOOL)) {
785             redirect(new moodle_url('/course/view.php', array('id'=>$this->get_ui()->get_controller()->get_courseid())));
786         }
788         // First decide whether a substage is needed
789         $rc = $this->ui->get_controller();
790         if ($rc->get_status() == backup::STATUS_SETTING_UI) {
791             $rc->finish_ui();
792         }
793         if ($rc->get_status() == backup::STATUS_NEED_PRECHECK) {
794             if (!$rc->precheck_executed()) {
795                 $rc->execute_precheck(true);
796             }
797             $results = $rc->get_precheck_results();
798             if (!empty($results)) {
799                 $this->substage = self::SUBSTAGE_PRECHECKS;
800             }
801         }
803         $substage = optional_param('substage', null, PARAM_INT);
804         if (empty($this->substage) && !empty($substage)) {
805             $this->substage = $substage;
806             // Now check whether that substage has already been submit
807             if ($this->substage == self::SUBSTAGE_PRECHECKS && optional_param('sesskey', null, PARAM_RAW) == sesskey()) {
808                 $info = $rc->get_info();
809                 if (!empty($info->role_mappings->mappings)) {
810                     foreach ($info->role_mappings->mappings as $key=>&$mapping) {
811                         $mapping->targetroleid = optional_param('mapping'.$key, $mapping->targetroleid, PARAM_INT);
812                     }
813                     $info->role_mappings->modified = true;
814                 }
815                 // We've processed the substage now setting it back to none so we
816                 // can move to the next stage.
817                 $this->substage = self::SUBSTAGE_NONE;
818             }
819         }
821         return empty($this->substage);
822     }
823     /**
824      * should NEVER be called... throws an exception
825      */
826     protected function initialise_stage_form() {
827         throw new backup_ui_exception('backup_ui_must_execute_first');
828     }
830     /**
831      * Renders the process stage screen
832      *
833      * @param core_backup_renderer $renderer renderer instance to use
834      * @return string HTML code
835      */
836     public function display(core_backup_renderer $renderer) {
837         global $PAGE;
839         $html = '';
840         $haserrors = false;
841         $url = new moodle_url($PAGE->url, array(
842             'restore'   => $this->get_uniqueid(),
843             'stage'     => restore_ui::STAGE_PROCESS,
844             'substage'  => $this->substage,
845             'sesskey'   => sesskey()));
846         $html .= html_writer::start_tag('form', array(
847             'action'    => $url->out_omit_querystring(),
848             'class'     => 'backup-restore',
849             'enctype'   => 'application/x-www-form-urlencoded', // Enforce compatibility with our max_input_vars hack.
850             'method'    => 'post'));
851         foreach ($url->params() as $name => $value) {
852             $html .= html_writer::empty_tag('input', array(
853                 'type'  => 'hidden',
854                 'name'  => $name,
855                 'value' => $value));
856         }
857         switch ($this->substage) {
858             case self::SUBSTAGE_PRECHECKS :
859                 $results = $this->ui->get_controller()->get_precheck_results();
860                 $info = $this->ui->get_controller()->get_info();
861                 $haserrors = (!empty($results['errors']));
862                 $html .= $renderer->precheck_notices($results);
863                 if (!empty($info->role_mappings->mappings)) {
864                     $context = context_course::instance($this->ui->get_controller()->get_courseid());
865                     $assignableroles = get_assignable_roles($context, ROLENAME_ALIAS, false);
866                     $html .= $renderer->role_mappings($info->role_mappings->mappings, $assignableroles);
867                 }
868                 break;
869             default:
870                 throw new restore_ui_exception('backup_ui_must_execute_first');
871         }
872         $html .= $renderer->substage_buttons($haserrors);
873         $html .= html_writer::end_tag('form');
875         return $html;
876     }
878     public function has_sub_stages() {
879         return true;
880     }
883 /**
884  * This is the completed stage.
885  *
886  * Once this is displayed there is nothing more to do.
887  */
888 class restore_ui_stage_complete extends restore_ui_stage_process {
889     /**
890      * The results of the backup execution
891      * @var array
892      */
893     protected $results;
894     /**
895      * Constructs the complete backup stage
896      * @param backup_ui $ui
897      * @param array|null $params
898      * @param array $results
899      */
900     public function __construct(restore_ui $ui, array $params=null, array $results=null) {
901         $this->results = $results;
902         parent::__construct($ui, $params);
903         $this->stage = restore_ui::STAGE_COMPLETE;
904     }
906     /**
907      * Displays the completed backup stage.
908      *
909      * Currently this just envolves redirecting to the file browser with an
910      * appropriate message.
911      *
912      * @param core_backup_renderer $renderer
913      * @return string HTML code to echo
914      */
915     public function display(core_backup_renderer $renderer) {
917         $html  = '';
918         if (!empty($this->results['file_aliases_restore_failures'])) {
919             $html .= $renderer->box_start('generalbox filealiasesfailures');
920             $html .= $renderer->heading_with_help(get_string('filealiasesrestorefailures', 'core_backup'),
921                 'filealiasesrestorefailures', 'core_backup');
922             $html .= $renderer->container(get_string('filealiasesrestorefailuresinfo', 'core_backup'));
923             $html .= $renderer->container_start('aliaseslist');
924             $html .= html_writer::start_tag('ul');
925             foreach ($this->results['file_aliases_restore_failures'] as $alias) {
926                 $html .= html_writer::tag('li', s($alias));
927             }
928             $html .= html_writer::end_tag('ul');
929             $html .= $renderer->container_end();
930             $html .= $renderer->box_end();
931         }
932         $html .= $renderer->box_start();
933         if (array_key_exists('file_missing_in_backup', $this->results)) {
934             $html .= $renderer->notification(get_string('restorefileweremissing', 'backup'), 'notifyproblem');
935         }
936         $html .= $renderer->notification(get_string('restoreexecutionsuccess', 'backup'), 'notifysuccess');
937         $html .= $renderer->continue_button(new moodle_url('/course/view.php', array(
938             'id' => $this->get_ui()->get_controller()->get_courseid())), 'get');
939         $html .= $renderer->box_end();
941         return $html;
942     }