Merge branch 'MDL-22309_master' of git://github.com/dmonllao/moodle
[moodle.git] / backup / util / ui / restore_ui_stage.class.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  * restore user interface stages
19  *
20  * This file contains the classes required to manage the stages that make up the
21  * restore user interface.
22  * These will be primarily operated a {@link restore_ui} instance.
23  *
24  * @package   core_backup
25  * @copyright 2010 Sam Hemelryk
26  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
27  */
29 /**
30  * Abstract stage class
31  *
32  * This class should be extended by all restore stages (a requirement of many restore ui functions).
33  * Each stage must then define two abstract methods
34  *  - process : To process the stage
35  *  - initialise_stage_form : To get a restore_moodleform instance for the stage
36  *
37  * @package   core_backup
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      * @param array $params
46      */
47     public function __construct(restore_ui $ui, array $params = null) {
48         $this->ui = $ui;
49         $this->params = $params;
50     }
51     /**
52      * The restore id from the restore controller
53      * @return string
54      */
55     final public function get_restoreid() {
56         return $this->get_uniqueid();
57     }
59     /**
60      * This is an independent stage
61      * @return int
62      */
63     final public function is_independent() {
64         return false;
65     }
67     /**
68      * No sub stages for this stage
69      * @return false
70      */
71     public function has_sub_stages() {
72         return false;
73     }
75     /**
76      * The name of this stage
77      * @return string
78      */
79     final public function get_name() {
80         return get_string('restorestage'.$this->stage, 'backup');
81     }
83     /**
84      * Returns true if this is the settings stage
85      * @return bool
86      */
87     final public function is_first_stage() {
88         return $this->stage == restore_ui::STAGE_SETTINGS;
89     }
90 }
92 /**
93  * Abstract class used to represent a restore stage that is indenependent.
94  *
95  * An independent stage is a judged to be so because it doesn't require, and has
96  * no use for the restore controller.
97  *
98  * @package   core_backup
99  * @copyright 2010 Sam Hemelryk
100  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
101  */
102 abstract class restore_ui_independent_stage {
103     /**
104      * @var \core\progress\base Optional progress reporter
105      */
106     private $progressreporter;
108     /**
109      * Constructs the restore stage.
110      * @param int $contextid
111      */
112     abstract public function __construct($contextid);
114     /**
115      * Processes the current restore stage.
116      * @return mixed
117      */
118     abstract public function process();
120     /**
121      * Displays this restore stage.
122      * @param core_backup_renderer $renderer
123      * @return mixed
124      */
125     abstract public function display(core_backup_renderer $renderer);
127     /**
128      * Returns the current restore stage.
129      * @return int
130      */
131     abstract public function get_stage();
133     /**
134      * Gets the progress reporter object in use for this restore UI stage.
135      *
136      * IMPORTANT: This progress reporter is used only for UI progress that is
137      * outside the restore controller. The restore controller has its own
138      * progress reporter which is used for progress during the main restore.
139      * Use the restore controller's progress reporter to report progress during
140      * a restore operation, not this one.
141      *
142      * This extra reporter is necessary because on some restore UI screens,
143      * there are long-running tasks even though there is no restore controller
144      * in use. There is a similar function in restore_ui. but that class is not
145      * used on some stages.
146      *
147      * @return \core\progress\null
148      */
149     public function get_progress_reporter() {
150         if (!$this->progressreporter) {
151             $this->progressreporter = new \core\progress\null();
152         }
153         return $this->progressreporter;
154     }
156     /**
157      * Sets the progress reporter that will be returned by get_progress_reporter.
158      *
159      * @param \core\progress\base $progressreporter Progress reporter
160      */
161     public function set_progress_reporter(\core\progress\base $progressreporter) {
162         $this->progressreporter = $progressreporter;
163     }
165     /**
166      * Gets an array of progress bar items that can be displayed through the restore renderer.
167      * @return array Array of items for the progress bar
168      */
169     public function get_progress_bar() {
170         global $PAGE;
171         $stage = restore_ui::STAGE_COMPLETE;
172         $currentstage = $this->get_stage();
173         $items = array();
174         while ($stage > 0) {
175             $classes = array('backup_stage');
176             if (floor($stage / 2) == $currentstage) {
177                 $classes[] = 'backup_stage_next';
178             } else if ($stage == $currentstage) {
179                 $classes[] = 'backup_stage_current';
180             } else if ($stage < $currentstage) {
181                 $classes[] = 'backup_stage_complete';
182             }
183             $item = array('text' => strlen(decbin($stage)).'. '.get_string('restorestage'.$stage, 'backup'), 'class' => join(' ', $classes));
184             if ($stage < $currentstage && $currentstage < restore_ui::STAGE_COMPLETE) {
185                 // By default you can't go back to independent stages, if that changes in the future uncomment the next line.
186                 // $item['link'] = new moodle_url($PAGE->url, array('restore' => $this->get_restoreid(), 'stage' => $stage));
187             }
188             array_unshift($items, $item);
189             $stage = floor($stage / 2);
190         }
191         return $items;
192     }
194     /**
195      * Returns the restore stage name.
196      * @return string
197      */
198     abstract public function get_stage_name();
200     /**
201      * Obviously true
202      * @return true
203      */
204     final public function is_independent() {
205         return true;
206     }
208     /**
209      * Handles the destruction of this object.
210      */
211     public function destroy() {
212         // Nothing to destroy here!.
213     }
216 /**
217  * The confirmation stage.
218  *
219  * This is the first stage, it is independent.
220  *
221  * @package   core_backup
222  * @copyright 2010 Sam Hemelryk
223  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
224  */
225 class restore_ui_stage_confirm extends restore_ui_independent_stage implements file_progress {
227     /**
228      * The context ID.
229      * @var int
230      */
231     protected $contextid;
233     /**
234      * The file name.
235      * @var string
236      */
237     protected $filename = null;
239     /**
240      * The file path.
241      * @var string
242      */
243     protected $filepath = null;
245     /**
246      * @var string Content hash of archive file to restore (if specified by hash)
247      */
248     protected $contenthash = null;
250     /**
251      * @var string Pathname hash of stored_file object to restore
252      */
253     protected $pathnamehash = null;
255     /**
256      * @var array
257      */
258     protected $details;
260     /**
261      * @var bool True if we have started reporting progress
262      */
263     protected $startedprogress = false;
265     /**
266      * Constructor
267      * @param int $contextid
268      * @throws coding_exception
269      */
270     public function __construct($contextid) {
271         $this->contextid = $contextid;
272         $this->filename = optional_param('filename', null, PARAM_FILE);
273         if ($this->filename === null) {
274             // Identify file object by its pathname hash.
275             $this->pathnamehash = required_param('pathnamehash', PARAM_ALPHANUM);
277             // The file content hash is also passed for security; users
278             // cannot guess the content hash (unless they know the file contents),
279             // so this guarantees that either the system generated this link or
280             // else the user has access to the restore archive anyhow.
281             $this->contenthash = required_param('contenthash', PARAM_ALPHANUM);
282         }
283     }
285     /**
286      * Processes this restore stage
287      * @return bool
288      * @throws restore_ui_exception
289      */
290     public function process() {
291         global $CFG;
292         if ($this->filename) {
293             $archivepath = $CFG->tempdir . '/backup/' . $this->filename;
294             if (!file_exists($archivepath)) {
295                 throw new restore_ui_exception('invalidrestorefile');
296             }
297             $outcome = $this->extract_file_to_dir($archivepath);
298             if ($outcome) {
299                 fulldelete($archivepath);
300             }
301         } else {
302             $fs = get_file_storage();
303             $storedfile = $fs->get_file_by_hash($this->pathnamehash);
304             if (!$storedfile || $storedfile->get_contenthash() !== $this->contenthash) {
305                 throw new restore_ui_exception('invalidrestorefile');
306             }
307             $outcome = $this->extract_file_to_dir($storedfile);
308         }
309         return $outcome;
310     }
312     /**
313      * Extracts the file.
314      *
315      * @param string|stored_file $source Archive file to extract
316      * @return bool
317      */
318     protected function extract_file_to_dir($source) {
319         global $CFG, $USER;
321         $this->filepath = restore_controller::get_tempdir_name($this->contextid, $USER->id);
323         $fb = get_file_packer('application/vnd.moodle.backup');
324         $result = $fb->extract_to_pathname($source,
325                 $CFG->tempdir . '/backup/' . $this->filepath . '/', null, $this);
327         // If any progress happened, end it.
328         if ($this->startedprogress) {
329             $this->get_progress_reporter()->end_progress();
330         }
331         return $result;
332     }
334     /**
335      * Implementation for file_progress interface to display unzip progress.
336      *
337      * @param int $progress Current progress
338      * @param int $max Max value
339      */
340     public function progress($progress = file_progress::INDETERMINATE, $max = file_progress::INDETERMINATE) {
341         $reporter = $this->get_progress_reporter();
343         // Start tracking progress if necessary.
344         if (!$this->startedprogress) {
345             $reporter->start_progress('extract_file_to_dir',
346                     ($max == file_progress::INDETERMINATE) ? \core\progress\base::INDETERMINATE : $max);
347             $this->startedprogress = true;
348         }
350         // Pass progress through to whatever handles it.
351         $reporter->progress(
352                 ($progress == file_progress::INDETERMINATE) ? \core\progress\base::INDETERMINATE : $progress);
353     }
355     /**
356      * Renders the confirmation stage screen
357      *
358      * @param core_backup_renderer $renderer renderer instance to use
359      * @return string HTML code
360      */
361     public function display(core_backup_renderer $renderer) {
363         $prevstageurl = new moodle_url('/backup/restorefile.php', array('contextid' => $this->contextid));
364         $nextstageurl = new moodle_url('/backup/restore.php', array(
365             'contextid' => $this->contextid,
366             'filepath'  => $this->filepath,
367             'stage'     => restore_ui::STAGE_DESTINATION));
369         $format = backup_general_helper::detect_backup_format($this->filepath);
371         if ($format === backup::FORMAT_UNKNOWN) {
372             // Unknown format - we can't do anything here.
373             return $renderer->backup_details_unknown($prevstageurl);
375         } else if ($format !== backup::FORMAT_MOODLE) {
376             // Non-standard format to be converted.
377             $details = array('format' => $format, 'type' => backup::TYPE_1COURSE); // todo type to be returned by a converter
378             return $renderer->backup_details_nonstandard($nextstageurl, $details);
380         } else {
381             // Standard MBZ backup, let us get information from it and display.
382             $this->details = backup_general_helper::get_backup_information($this->filepath);
383             return $renderer->backup_details($this->details, $nextstageurl);
384         }
385     }
387     /**
388      * The restore stage name.
389      * @return string
390      * @throws coding_exception
391      */
392     public function get_stage_name() {
393         return get_string('restorestage'.restore_ui::STAGE_CONFIRM, 'backup');
394     }
396     /**
397      * The restore stage this class is for.
398      * @return int
399      */
400     public function get_stage() {
401         return restore_ui::STAGE_CONFIRM;
402     }
405 /**
406  * This is the destination stage.
407  *
408  * This stage is the second stage and is also independent
409  *
410  * @package   core_backup
411  * @copyright 2010 Sam Hemelryk
412  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
413  */
414 class restore_ui_stage_destination extends restore_ui_independent_stage {
416     /**
417      * The context ID.
418      * @var int
419      */
420     protected $contextid;
422     /**
423      * The backup file path.
424      * @var mixed|null
425      */
426     protected $filepath = null;
428     /**
429      * The course ID.
430      * @var null
431      */
432     protected $courseid = null;
434     /**
435      * The restore target. One of backup::TARGET_NEW
436      * @var int
437      */
438     protected $target = backup::TARGET_NEW_COURSE;
440     /**
441      * The course search component.
442      * @var null|restore_course_search
443      */
444     protected $coursesearch = null;
446     /**
447      * The category search component.
448      * @var null|restore_category_search
449      */
450     protected $categorysearch = null;
452     /**
453      * Constructs the destination stage.
454      * @param int $contextid
455      * @throws coding_exception
456      */
457     public function __construct($contextid) {
458         global $PAGE;
459         $this->contextid = $contextid;
460         $this->filepath = required_param('filepath', PARAM_ALPHANUM);
461         $url = new moodle_url($PAGE->url, array(
462             'filepath' => $this->filepath,
463             'contextid' => $this->contextid,
464             'stage' => restore_ui::STAGE_DESTINATION));
465         $this->coursesearch = new restore_course_search(array('url' => $url), context::instance_by_id($contextid)->instanceid);
466         $this->categorysearch = new restore_category_search(array('url' => $url));
467     }
469     /**
470      * Processes the destination stage.
471      * @return bool
472      * @throws coding_exception
473      * @throws restore_ui_exception
474      */
475     public function process() {
476         global $CFG, $DB;
477         if (!file_exists("$CFG->tempdir/backup/".$this->filepath) || !is_dir("$CFG->tempdir/backup/".$this->filepath)) {
478             throw new restore_ui_exception('invalidrestorepath');
479         }
480         if (optional_param('searchcourses', false, PARAM_BOOL)) {
481             return false;
482         }
483         $this->target = optional_param('target', backup::TARGET_NEW_COURSE, PARAM_INT);
484         $targetid = optional_param('targetid', null, PARAM_INT);
485         if (!is_null($this->target) && !is_null($targetid) && confirm_sesskey()) {
486             if ($this->target == backup::TARGET_NEW_COURSE) {
487                 list($fullname, $shortname) = restore_dbops::calculate_course_names(0, get_string('restoringcourse', 'backup'), get_string('restoringcourseshortname', 'backup'));
488                 $this->courseid = restore_dbops::create_new_course($fullname, $shortname, $targetid);
489             } else {
490                 $this->courseid = $targetid;
491             }
492             return ($DB->record_exists('course', array('id' => $this->courseid)));
493         }
494         return false;
495     }
497     /**
498      * Renders the destination stage screen
499      *
500      * @param core_backup_renderer $renderer renderer instance to use
501      * @return string HTML code
502      */
503     public function display(core_backup_renderer $renderer) {
505         $format = backup_general_helper::detect_backup_format($this->filepath);
507         if ($format === backup::FORMAT_MOODLE) {
508             // Standard Moodle 2 format, let use get the type of the backup.
509             $details = backup_general_helper::get_backup_information($this->filepath);
510             if ($details->type === backup::TYPE_1COURSE) {
511                 $wholecourse = true;
512             } else {
513                 $wholecourse = false;
514             }
516         } else {
517             // Non-standard format to be converted. We assume it contains the
518             // whole course for now. However, in the future there might be a callback
519             // to the installed converters.
520             $wholecourse = true;
521         }
523         $nextstageurl = new moodle_url('/backup/restore.php', array(
524             'contextid' => $this->contextid,
525             'filepath'  => $this->filepath,
526             'stage'     => restore_ui::STAGE_SETTINGS));
527         $context = context::instance_by_id($this->contextid);
529         if ($context->contextlevel == CONTEXT_COURSE and has_capability('moodle/restore:restorecourse', $context)) {
530             $currentcourse = $context->instanceid;
531         } else {
532             $currentcourse = false;
533         }
535         return $renderer->course_selector($nextstageurl, $wholecourse, $this->categorysearch, $this->coursesearch, $currentcourse);
536     }
538     /**
539      * Returns the stage name.
540      * @return string
541      * @throws coding_exception
542      */
543     public function get_stage_name() {
544         return get_string('restorestage'.restore_ui::STAGE_DESTINATION, 'backup');
545     }
547     /**
548      * Returns the backup file path
549      * @return mixed|null
550      */
551     public function get_filepath() {
552         return $this->filepath;
553     }
555     /**
556      * Returns the course id.
557      * @return null
558      */
559     public function get_course_id() {
560         return $this->courseid;
561     }
563     /**
564      * Returns the current restore stage
565      * @return int
566      */
567     public function get_stage() {
568         return restore_ui::STAGE_DESTINATION;
569     }
571     /**
572      * Returns the target for this restore.
573      * One of backup::TARGET_*
574      * @return int
575      */
576     public function get_target() {
577         return $this->target;
578     }
581 /**
582  * This stage is the settings stage.
583  *
584  * This stage is the third stage, it is dependent on a restore controller and
585  * is the first stage as such.
586  *
587  * @package   core_backup
588  * @copyright 2010 Sam Hemelryk
589  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
590  */
591 class restore_ui_stage_settings extends restore_ui_stage {
592     /**
593      * Initial restore stage constructor
594      * @param restore_ui $ui
595      * @param array $params
596      */
597     public function __construct(restore_ui $ui, array $params = null) {
598         $this->stage = restore_ui::STAGE_SETTINGS;
599         parent::__construct($ui, $params);
600     }
602     /**
603      * Process the settings stage.
604      *
605      * @param base_moodleform $form
606      * @return bool|int
607      */
608     public function process(base_moodleform $form = null) {
609         $form = $this->initialise_stage_form();
611         if ($form->is_cancelled()) {
612             $this->ui->cancel_process();
613         }
615         $data = $form->get_data();
616         if ($data && confirm_sesskey()) {
617             $tasks = $this->ui->get_tasks();
618             $changes = 0;
619             foreach ($tasks as &$task) {
620                 // We are only interesting in the backup root task for this stage.
621                 if ($task instanceof restore_root_task || $task instanceof restore_course_task) {
622                     // Get all settings into a var so we can iterate by reference.
623                     $settings = $task->get_settings();
624                     foreach ($settings as &$setting) {
625                         $name = $setting->get_ui_name();
626                         if (isset($data->$name) &&  $data->$name != $setting->get_value()) {
627                             $setting->set_value($data->$name);
628                             $changes++;
629                         } else if (!isset($data->$name) && $setting->get_ui_type() == backup_setting::UI_HTML_CHECKBOX && $setting->get_value()) {
630                             $setting->set_value(0);
631                             $changes++;
632                         }
633                     }
634                 }
635             }
636             // Return the number of changes the user made.
637             return $changes;
638         } else {
639             return false;
640         }
641     }
643     /**
644      * Initialise the stage form.
645      *
646      * @return backup_moodleform|base_moodleform|restore_settings_form
647      * @throws coding_exception
648      */
649     protected function initialise_stage_form() {
650         global $PAGE;
651         if ($this->stageform === null) {
652             $form = new restore_settings_form($this, $PAGE->url);
653             // Store as a variable so we can iterate by reference.
654             $tasks = $this->ui->get_tasks();
655             $headingprinted = false;
656             // Iterate all tasks by reference.
657             foreach ($tasks as &$task) {
658                 // For the initial stage we are only interested in the root settings.
659                 if ($task instanceof restore_root_task) {
660                     if (!$headingprinted) {
661                         $form->add_heading('rootsettings', get_string('restorerootsettings', 'backup'));
662                         $headingprinted = true;
663                     }
664                     $settings = $task->get_settings();
665                     // First add all settings except the filename setting.
666                     foreach ($settings as &$setting) {
667                         if ($setting->get_name() == 'filename') {
668                             continue;
669                         }
670                         $form->add_setting($setting, $task);
671                     }
672                     // Then add all dependencies.
673                     foreach ($settings as &$setting) {
674                         if ($setting->get_name() == 'filename') {
675                             continue;
676                         }
677                         $form->add_dependencies($setting);
678                     }
679                 }
680             }
681             $this->stageform = $form;
682         }
683         // Return the form.
684         return $this->stageform;
685     }
688 /**
689  * Schema stage of backup process
690  *
691  * During the schema stage the user is required to set the settings that relate
692  * to the area that they are backing up as well as its children.
693  *
694  * @package   core_backup
695  * @copyright 2010 Sam Hemelryk
696  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
697  */
698 class restore_ui_stage_schema extends restore_ui_stage {
699     /**
700      * @var int Maximum number of settings to add to form at once
701      */
702     const MAX_SETTINGS_BATCH = 1000;
704     /**
705      * Schema stage constructor
706      * @param restore_ui $ui
707      * @param array $params
708      */
709     public function __construct(restore_ui $ui, array $params = null) {
710         $this->stage = restore_ui::STAGE_SCHEMA;
711         parent::__construct($ui, $params);
712     }
714     /**
715      * Processes the schema stage
716      *
717      * @param base_moodleform $form
718      * @return int The number of changes the user made
719      */
720     public function process(base_moodleform $form = null) {
721         $form = $this->initialise_stage_form();
722         // Check it wasn't cancelled.
723         if ($form->is_cancelled()) {
724             $this->ui->cancel_process();
725         }
727         // Check it has been submit.
728         $data = $form->get_data();
729         if ($data && confirm_sesskey()) {
730             // Get the tasks into a var so we can iterate by reference.
731             $tasks = $this->ui->get_tasks();
732             $changes = 0;
733             // Iterate all tasks by reference.
734             foreach ($tasks as &$task) {
735                 // We are only interested in schema settings.
736                 if (!($task instanceof restore_root_task)) {
737                     // Store as a variable so we can iterate by reference.
738                     $settings = $task->get_settings();
739                     // Iterate by reference.
740                     foreach ($settings as &$setting) {
741                         $name = $setting->get_ui_name();
742                         if (isset($data->$name) &&  $data->$name != $setting->get_value()) {
743                             $setting->set_value($data->$name);
744                             $changes++;
745                         } else if (!isset($data->$name) && $setting->get_ui_type() == backup_setting::UI_HTML_CHECKBOX && $setting->get_value()) {
746                             $setting->set_value(0);
747                             $changes++;
748                         }
749                     }
750                 }
751             }
752             // Return the number of changes the user made.
753             return $changes;
754         } else {
755             return false;
756         }
757     }
759     /**
760      * Creates the backup_schema_form instance for this stage
761      *
762      * @return backup_schema_form
763      */
764     protected function initialise_stage_form() {
765         global $PAGE;
766         if ($this->stageform === null) {
767             $form = new restore_schema_form($this, $PAGE->url);
768             $tasks = $this->ui->get_tasks();
769             $courseheading = false;
771             // Track progress through each stage.
772             $progress = $this->ui->get_progress_reporter();
773             $progress->start_progress('Initialise schema stage form', 3);
775             $progress->start_progress('', count($tasks));
776             $done = 1;
777             $allsettings = array();
778             foreach ($tasks as $task) {
779                 if (!($task instanceof restore_root_task)) {
780                     if (!$courseheading) {
781                         // If we haven't already display a course heading to group nicely.
782                         $form->add_heading('coursesettings', get_string('coursesettings', 'backup'));
783                         $courseheading = true;
784                     }
785                     // Put each setting into an array of settings to add. Adding
786                     // a setting individually is a very slow operation, so we add.
787                     // them all in a batch later on.
788                     foreach ($task->get_settings() as $setting) {
789                         $allsettings[] = array($setting, $task);
790                     }
791                 } else if ($this->ui->enforce_changed_dependencies()) {
792                     // Only show these settings if dependencies changed them.
793                     // Add a root settings heading to group nicely.
794                     $form->add_heading('rootsettings', get_string('rootsettings', 'backup'));
795                     // Iterate all settings and add them to the form as a fixed
796                     // setting. We only want schema settings to be editable.
797                     foreach ($task->get_settings() as $setting) {
798                         if ($setting->get_name() != 'filename') {
799                             $form->add_fixed_setting($setting, $task);
800                         }
801                     }
802                 }
803                 // Update progress.
804                 $progress->progress($done++);
805             }
806             $progress->end_progress();
808             // Add settings for tasks in batches of up to 1000. Adding settings
809             // in larger batches improves performance, but if it takes too long,
810             // we won't be able to update the progress bar so the backup might.
811             // time out. 1000 is chosen to balance this.
812             $numsettings = count($allsettings);
813             $progress->start_progress('', ceil($numsettings / self::MAX_SETTINGS_BATCH));
814             $start = 0;
815             $done = 1;
816             while ($start < $numsettings) {
817                 $length = min(self::MAX_SETTINGS_BATCH, $numsettings - $start);
818                 $form->add_settings(array_slice($allsettings, $start, $length));
819                 $start += $length;
820                 $progress->progress($done++);
821             }
822             $progress->end_progress();
824             // Add the dependencies for all the settings.
825             $progress->start_progress('', count($allsettings));
826             $done = 1;
827             foreach ($allsettings as $settingtask) {
828                 $form->add_dependencies($settingtask[0]);
829                 $progress->progress($done++);
830             }
831             $progress->end_progress();
833             $progress->end_progress();
834             $this->stageform = $form;
835         }
836         return $this->stageform;
837     }
840 /**
841  * Confirmation stage
842  *
843  * On this stage the user reviews the setting for the backup and can change the filename
844  * of the file that will be generated.
845  *
846  * @package   core_backup
847  * @copyright 2010 Sam Hemelryk
848  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
849  */
850 class restore_ui_stage_review extends restore_ui_stage {
852     /**
853      * Constructs the stage
854      * @param restore_ui $ui
855      * @param array $params
856      */
857     public function __construct($ui, array $params = null) {
858         $this->stage = restore_ui::STAGE_REVIEW;
859         parent::__construct($ui, $params);
860     }
862     /**
863      * Processes the confirmation stage
864      *
865      * @param base_moodleform $form
866      * @return int The number of changes the user made
867      */
868     public function process(base_moodleform $form = null) {
869         $form = $this->initialise_stage_form();
870         // Check it hasn't been cancelled.
871         if ($form->is_cancelled()) {
872             $this->ui->cancel_process();
873         }
875         $data = $form->get_data();
876         if ($data && confirm_sesskey()) {
877             return 0;
878         } else {
879             return false;
880         }
881     }
882     /**
883      * Creates the backup_confirmation_form instance this stage requires
884      *
885      * @return backup_confirmation_form
886      */
887     protected function initialise_stage_form() {
888         global $PAGE;
889         if ($this->stageform === null) {
890             // Get the form.
891             $form = new restore_review_form($this, $PAGE->url);
892             $content = '';
893             $courseheading = false;
895             $progress = $this->ui->get_progress_reporter();
896             $tasks = $this->ui->get_tasks();
897             $progress->start_progress('initialise_stage_form', count($tasks));
898             $done = 1;
899             foreach ($tasks as $task) {
900                 if ($task instanceof restore_root_task) {
901                     // If its a backup root add a root settings heading to group nicely.
902                     $form->add_heading('rootsettings', get_string('rootsettings', 'backup'));
903                 } else if (!$courseheading) {
904                     // We haven't already add a course heading.
905                     $form->add_heading('coursesettings', get_string('coursesettings', 'backup'));
906                     $courseheading = true;
907                 }
908                 // Iterate all settings, doesnt need to happen by reference.
909                 foreach ($task->get_settings() as $setting) {
910                     $form->add_fixed_setting($setting, $task);
911                 }
912                 // Update progress.
913                 $progress->progress($done++);
914             }
915             $progress->end_progress();
916             $this->stageform = $form;
917         }
918         return $this->stageform;
919     }
922 /**
923  * Final stage of backup
924  *
925  * This stage is special in that it is does not make use of a form. The reason for
926  * this is the order of procession of backup at this stage.
927  * The processesion is:
928  * 1. The final stage will be intialise.
929  * 2. The confirmation stage will be processed.
930  * 3. The backup will be executed
931  * 4. The complete stage will be loaded by execution
932  * 5. The complete stage will be displayed
933  *
934  * This highlights that we neither need a form nor a display method for this stage
935  * we simply need to process.
936  *
937  * @package   core_backup
938  * @copyright 2010 Sam Hemelryk
939  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
940  */
941 class restore_ui_stage_process extends restore_ui_stage {
943     /**
944      * There is no substage required.
945      */
946     const SUBSTAGE_NONE = 0;
948     /**
949      * The prechecks substage is required/the current substage.
950      */
951     const SUBSTAGE_PRECHECKS = 2;
953     /**
954      * The current substage.
955      * @var int
956      */
957     protected $substage = 0;
959     /**
960      * Constructs the final stage
961      * @param base_ui $ui
962      * @param array $params
963      */
964     public function __construct(base_ui $ui, array $params = null) {
965         $this->stage = restore_ui::STAGE_PROCESS;
966         parent::__construct($ui, $params);
967     }
968     /**
969      * Processes the final stage.
970      *
971      * In this case it checks to see if there is a sub stage that we need to display
972      * before execution, if there is we gear up to display the subpage, otherwise
973      * we return true which will lead to execution of the restore and the loading
974      * of the completed stage.
975      *
976      * @param base_moodleform $form
977      */
978     public function process(base_moodleform $form = null) {
979         if (optional_param('cancel', false, PARAM_BOOL)) {
980             redirect(new moodle_url('/course/view.php', array('id' => $this->get_ui()->get_controller()->get_courseid())));
981         }
983         // First decide whether a substage is needed.
984         $rc = $this->ui->get_controller();
985         if ($rc->get_status() == backup::STATUS_SETTING_UI) {
986             $rc->finish_ui();
987         }
988         if ($rc->get_status() == backup::STATUS_NEED_PRECHECK) {
989             if (!$rc->precheck_executed()) {
990                 $rc->execute_precheck(true);
991             }
992             $results = $rc->get_precheck_results();
993             if (!empty($results)) {
994                 $this->substage = self::SUBSTAGE_PRECHECKS;
995             }
996         }
998         $substage = optional_param('substage', null, PARAM_INT);
999         if (empty($this->substage) && !empty($substage)) {
1000             $this->substage = $substage;
1001             // Now check whether that substage has already been submit.
1002             if ($this->substage == self::SUBSTAGE_PRECHECKS && optional_param('sesskey', null, PARAM_RAW) == sesskey()) {
1003                 $info = $rc->get_info();
1004                 if (!empty($info->role_mappings->mappings)) {
1005                     foreach ($info->role_mappings->mappings as $key => &$mapping) {
1006                         $mapping->targetroleid = optional_param('mapping'.$key, $mapping->targetroleid, PARAM_INT);
1007                     }
1008                     $info->role_mappings->modified = true;
1009                 }
1010                 // We've processed the substage now setting it back to none so we
1011                 // can move to the next stage.
1012                 $this->substage = self::SUBSTAGE_NONE;
1013             }
1014         }
1016         return empty($this->substage);
1017     }
1018     /**
1019      * should NEVER be called... throws an exception
1020      */
1021     protected function initialise_stage_form() {
1022         throw new backup_ui_exception('backup_ui_must_execute_first');
1023     }
1025     /**
1026      * Renders the process stage screen
1027      *
1028      * @throws restore_ui_exception
1029      * @param core_backup_renderer $renderer renderer instance to use
1030      * @return string HTML code
1031      */
1032     public function display(core_backup_renderer $renderer) {
1033         global $PAGE;
1035         $html = '';
1036         $haserrors = false;
1037         $url = new moodle_url($PAGE->url, array(
1038             'restore'   => $this->get_uniqueid(),
1039             'stage'     => restore_ui::STAGE_PROCESS,
1040             'substage'  => $this->substage,
1041             'sesskey'   => sesskey()));
1042         $html .= html_writer::start_tag('form', array(
1043             'action'    => $url->out_omit_querystring(),
1044             'class'     => 'backup-restore',
1045             'enctype'   => 'application/x-www-form-urlencoded', // Enforce compatibility with our max_input_vars hack.
1046             'method'    => 'post'));
1047         foreach ($url->params() as $name => $value) {
1048             $html .= html_writer::empty_tag('input', array(
1049                 'type'  => 'hidden',
1050                 'name'  => $name,
1051                 'value' => $value));
1052         }
1053         switch ($this->substage) {
1054             case self::SUBSTAGE_PRECHECKS :
1055                 $results = $this->ui->get_controller()->get_precheck_results();
1056                 $info = $this->ui->get_controller()->get_info();
1057                 $haserrors = (!empty($results['errors']));
1058                 $html .= $renderer->precheck_notices($results);
1059                 if (!empty($info->role_mappings->mappings)) {
1060                     $context = context_course::instance($this->ui->get_controller()->get_courseid());
1061                     $assignableroles = get_assignable_roles($context, ROLENAME_ALIAS, false);
1062                     $html .= $renderer->role_mappings($info->role_mappings->mappings, $assignableroles);
1063                 }
1064                 break;
1065             default:
1066                 throw new restore_ui_exception('backup_ui_must_execute_first');
1067         }
1068         $html .= $renderer->substage_buttons($haserrors);
1069         $html .= html_writer::end_tag('form');
1071         return $html;
1072     }
1074     /**
1075      * Returns true if this stage can have sub-stages.
1076      * @return bool|false
1077      */
1078     public function has_sub_stages() {
1079         return true;
1080     }
1083 /**
1084  * This is the completed stage.
1085  *
1086  * Once this is displayed there is nothing more to do.
1087  *
1088  * @package   core_backup
1089  * @copyright 2010 Sam Hemelryk
1090  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1091  */
1092 class restore_ui_stage_complete extends restore_ui_stage_process {
1094     /**
1095      * The results of the backup execution
1096      * @var array
1097      */
1098     protected $results;
1100     /**
1101      * Constructs the complete backup stage
1102      * @param restore_ui $ui
1103      * @param array $params
1104      * @param array $results
1105      */
1106     public function __construct(restore_ui $ui, array $params = null, array $results = null) {
1107         $this->results = $results;
1108         parent::__construct($ui, $params);
1109         $this->stage = restore_ui::STAGE_COMPLETE;
1110     }
1112     /**
1113      * Displays the completed backup stage.
1114      *
1115      * Currently this just envolves redirecting to the file browser with an
1116      * appropriate message.
1117      *
1118      * @param core_backup_renderer $renderer
1119      * @return string HTML code to echo
1120      */
1121     public function display(core_backup_renderer $renderer) {
1123         $html  = '';
1124         if (!empty($this->results['file_aliases_restore_failures'])) {
1125             $html .= $renderer->box_start('generalbox filealiasesfailures');
1126             $html .= $renderer->heading_with_help(get_string('filealiasesrestorefailures', 'core_backup'),
1127                 'filealiasesrestorefailures', 'core_backup');
1128             $html .= $renderer->container(get_string('filealiasesrestorefailuresinfo', 'core_backup'));
1129             $html .= $renderer->container_start('aliaseslist');
1130             $html .= html_writer::start_tag('ul');
1131             foreach ($this->results['file_aliases_restore_failures'] as $alias) {
1132                 $html .= html_writer::tag('li', s($alias));
1133             }
1134             $html .= html_writer::end_tag('ul');
1135             $html .= $renderer->container_end();
1136             $html .= $renderer->box_end();
1137         }
1138         $html .= $renderer->box_start();
1139         if (array_key_exists('file_missing_in_backup', $this->results)) {
1140             $html .= $renderer->notification(get_string('restorefileweremissing', 'backup'), 'notifyproblem');
1141         }
1142         $html .= $renderer->notification(get_string('restoreexecutionsuccess', 'backup'), 'notifysuccess');
1143         $html .= $renderer->continue_button(new moodle_url('/course/view.php', array(
1144             'id' => $this->get_ui()->get_controller()->get_courseid())), 'get');
1145         $html .= $renderer->box_end();
1147         return $html;
1148     }