MDL-46481 backup: implemented quick backup process (one click)
[moodle.git] / backup / util / ui / backup_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  * Backup user interface stages
20  *
21  * This file contains the classes required to manage the stages that make up the
22  * backup user interface.
23  * These will be primarily operated a {@see backup_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 backup stages (a requirement of many backup ui functions).
34  * Each stage must then define two abstract methods
35  *  - process : To process the stage
36  *  - initialise_stage_form : To get a backup_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 backup_ui_stage extends base_ui_stage {
43     public function __construct(backup_ui $ui, array $params = null) {
44        parent::__construct($ui, $params);
45     }
46     /**
47      * The backup id from the backup controller
48      * @return string
49      */
50     final public function get_backupid() {
51         return $this->get_uniqueid();
52     }
53 }
55 /**
56  * Class representing the initial stage of a backup.
57  *
58  * In this stage the user is required to set the root level settings.
59  *
60  * @copyright 2010 Sam Hemelryk
61  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
62  */
63 class backup_ui_stage_initial extends backup_ui_stage {
65     /**
66      * When set to true we skip all stages and jump to immediately processing the backup.
67      * @var bool
68      */
69     protected $oneclickbackup = false;
71     /**
72      * Initial backup stage constructor
73      * @param backup_ui $ui
74      */
75     public function __construct(backup_ui $ui, array $params=null) {
76         $this->stage = backup_ui::STAGE_INITIAL;
77         parent::__construct($ui, $params);
78     }
80     /**
81      * Processes the initial backup stage
82      * @param backup_moodleform $form
83      * @return int The number of changes
84      */
85     public function process(base_moodleform $m = null) {
87         $form = $this->initialise_stage_form();
89         if ($form->is_cancelled()) {
90             $this->ui->cancel_process();
91         }
93         $data = $form->get_data();
94         if ($data && confirm_sesskey()) {
95             if (isset($data->oneclickbackup)) {
96                 $this->oneclickbackup = true;
97             }
98             $tasks = $this->ui->get_tasks();
99             $changes = 0;
100             foreach ($tasks as &$task) {
101                 // We are only interesting in the backup root task for this stage
102                 if ($task instanceof backup_root_task) {
103                     // Get all settings into a var so we can iterate by reference
104                     $settings = $task->get_settings();
105                     foreach ($settings as &$setting) {
106                         $name = $setting->get_ui_name();
107                         if (isset($data->$name) &&  $data->$name != $setting->get_value()) {
108                             $setting->set_value($data->$name);
109                             $changes++;
110                         } else if (!isset($data->$name) && $setting->get_ui_type() == backup_setting::UI_HTML_CHECKBOX && $setting->get_value()) {
111                             $setting->set_value(0);
112                             $changes++;
113                         }
114                     }
115                 }
116             }
117             // Return the number of changes the user made
118             return $changes;
119         } else {
120             return false;
121         }
122     }
124     /**
125      * Gets the next stage for the backup.
126      *
127      * We override this function to implement the one click backup.
128      * When the user performs a one click backup we jump straight to the final stage.
129      *
130      * @return int
131      */
132     public function get_next_stage() {
133         if ($this->oneclickbackup) {
134             // Its a one click backup.
135             // The default filename is backup.mbz, this normally gets set to something useful in the confirmation stage.
136             // because we skipped that stage we must manually set this to a useful value.
137             $tasks = $this->ui->get_tasks();
138             foreach ($tasks as $task) {
139                 if ($task instanceof backup_root_task) {
140                     // Find the filename setting.
141                     $setting = $task->get_setting('filename');
142                     if ($setting) {
143                         // Use the helper objects to get a useful name.
144                         $filename = backup_plan_dbops::get_default_backup_filename(
145                             $this->ui->get_format(),
146                             $this->ui->get_type(),
147                             $this->ui->get_controller_id(),
148                             $this->ui->get_setting_value('users'),
149                             $this->ui->get_setting_value('anonymize')
150                         );
151                         $setting->set_value($filename);
152                     }
153                 }
154             }
155             return backup_ui::STAGE_FINAL;
156         }
157         return parent::get_next_stage();
158     }
160     /**
161      * Initialises the backup_moodleform instance for this stage
162      *
163      * @return backup_initial_form
164      */
165     protected function initialise_stage_form() {
166         global $PAGE;
167         if ($this->stageform === null) {
168             $form = new backup_initial_form($this, $PAGE->url);
169             // Store as a variable so we can iterate by reference
170             $tasks = $this->ui->get_tasks();
171             // Iterate all tasks by reference
172             $add_settings = array();
173             $dependencies = array();
174             foreach ($tasks as &$task) {
175                 // For the initial stage we are only interested in the root settings
176                 if ($task instanceof backup_root_task) {
177                     $form->add_heading('rootsettings', get_string('rootsettings', 'backup'));
178                     $form->collapse_heading('rootsettings');
179                     $settings = $task->get_settings();
180                     // First add all settings except the filename setting
181                     foreach ($settings as &$setting) {
182                         if ($setting->get_name() == 'filename') {
183                             continue;
184                         }
185                         $add_settings[] = array($setting, $task);
186                     }
187                     // Then add all dependencies
188                     foreach ($settings as &$setting) {
189                         if ($setting->get_name() == 'filename') {
190                             continue;
191                         }
192                         $dependencies[] = $setting;
193                     }
194                 }
195             }
196             // Add all settings at once.
197             $form->add_settings($add_settings);
198             // Add dependencies.
199             foreach ($dependencies as $depsetting) {
200                 $form->add_dependencies($depsetting);
201             }
202             $this->stageform = $form;
203         }
204         // Return the form
205         return $this->stageform;
206     }
209 /**
210  * Schema stage of backup process
211  *
212  * During the schema stage the user is required to set the settings that relate
213  * to the area that they are backing up as well as its children.
214  *
215  * @copyright 2010 Sam Hemelryk
216  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
217  */
218 class backup_ui_stage_schema extends backup_ui_stage {
219     /**
220      * @var int Maximum number of settings to add to form at once
221      */
222     const MAX_SETTINGS_BATCH = 1000;
224     /**
225      * Schema stage constructor
226      * @param backup_moodleform $ui
227      */
228     public function __construct(backup_ui $ui, array $params=null) {
229         $this->stage = backup_ui::STAGE_SCHEMA;
230         parent::__construct($ui, $params);
231     }
232     /**
233      * Processes the schema stage
234      *
235      * @param backup_moodleform|null $form
236      * @return int The number of changes the user made
237      */
238     public function process(base_moodleform $form = null) {
239         $form = $this->initialise_stage_form();
240         // Check it wasn't cancelled
241         if ($form->is_cancelled()) {
242             $this->ui->cancel_process();
243         }
245         // Check it has been submit
246         $data = $form->get_data();
247         if ($data && confirm_sesskey()) {
248             // Get the tasks into a var so we can iterate by reference
249             $tasks = $this->ui->get_tasks();
250             $changes = 0;
251             // Iterate all tasks by reference
252             foreach ($tasks as &$task) {
253                 // We are only interested in schema settings
254                 if (!($task instanceof backup_root_task)) {
255                     // Store as a variable so we can iterate by reference
256                     $settings = $task->get_settings();
257                     // Iterate by reference
258                     foreach ($settings as &$setting) {
259                         $name = $setting->get_ui_name();
260                         if (isset($data->$name) &&  $data->$name != $setting->get_value()) {
261                             $setting->set_value($data->$name);
262                             $changes++;
263                         } else if (!isset($data->$name) && $setting->get_ui_type() == backup_setting::UI_HTML_CHECKBOX && $setting->get_value()) {
264                             $setting->set_value(0);
265                             $changes++;
266                         }
267                     }
268                 }
269             }
270             // Return the number of changes the user made
271             return $changes;
272         } else {
273             return false;
274         }
275     }
276     /**
277      * Creates the backup_schema_form instance for this stage
278      *
279      * @return backup_schema_form
280      */
281     protected function initialise_stage_form() {
282         global $PAGE;
283         if ($this->stageform === null) {
284             $form = new backup_schema_form($this, $PAGE->url);
285             $tasks = $this->ui->get_tasks();
286             $content = '';
287             $courseheading = false;
288             $add_settings = array();
289             $dependencies = array();
291             // Track progress through each stage.
292             $progress = $this->ui->get_controller()->get_progress();
293             $progress->start_progress('Initialise stage form', 3);
295             // Get settings for all tasks.
296             $progress->start_progress('', count($tasks));
297             $done = 1;
298             foreach ($tasks as $task) {
299                 if (!($task instanceof backup_root_task)) {
300                     if (!$courseheading) {
301                         // If we havn't already display a course heading to group nicely
302                         $form->add_heading('coursesettings', get_string('includeactivities', 'backup'));
303                         $courseheading = true;
304                     }
305                     // First add each setting
306                     foreach ($task->get_settings() as $setting) {
307                         $add_settings[] = array($setting, $task);
308                     }
309                     // The add all the dependencies
310                     foreach ($task->get_settings() as $setting) {
311                         $dependencies[] = $setting;
312                     }
313                 } else if ($this->ui->enforce_changed_dependencies()) {
314                     // Only show these settings if dependencies changed them.
315                     // Add a root settings heading to group nicely
316                     $form->add_heading('rootsettings', get_string('rootsettings', 'backup'));
317                     // Iterate all settings and add them to the form as a fixed
318                     // setting. We only want schema settings to be editable
319                     foreach ($task->get_settings() as $setting) {
320                         if ($setting->get_name() != 'filename') {
321                             $form->add_fixed_setting($setting, $task);
322                         }
323                     }
324                 }
325                 // Update progress.
326                 $progress->progress($done++);
327             }
328             $progress->end_progress();
330             // Add settings for tasks in batches of up to 1000. Adding settings
331             // in larger batches improves performance, but if it takes too long,
332             // we won't be able to update the progress bar so the backup might
333             // time out. 1000 is chosen to balance this.
334             $numsettings = count($add_settings);
335             $progress->start_progress('', ceil($numsettings / self::MAX_SETTINGS_BATCH));
336             $start = 0;
337             $done = 1;
338             while($start < $numsettings) {
339                 $length = min(self::MAX_SETTINGS_BATCH, $numsettings - $start);
340                 $form->add_settings(array_slice($add_settings, $start, $length));
341                 $start += $length;
342                 $progress->progress($done++);
343             }
344             $progress->end_progress();
346             $progress->start_progress('', count($dependencies));
347             $done = 1;
348             foreach ($dependencies as $depsetting) {
349                 $form->add_dependencies($depsetting);
350                 $progress->progress($done++);
351             }
352             $progress->end_progress();
354             // End overall progress through creating form.
355             $progress->end_progress();
356             $this->stageform = $form;
357         }
358         return $this->stageform;
359     }
362 /**
363  * Confirmation stage
364  *
365  * On this stage the user reviews the setting for the backup and can change the filename
366  * of the file that will be generated.
367  *
368  * @copyright 2010 Sam Hemelryk
369  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
370  */
371 class backup_ui_stage_confirmation extends backup_ui_stage {
372     /**
373      * Constructs the stage
374      * @param backup_ui $ui
375      */
376     public function __construct($ui, array $params=null) {
377         $this->stage = backup_ui::STAGE_CONFIRMATION;
378         parent::__construct($ui, $params);
379     }
380     /**
381      * Processes the confirmation stage
382      *
383      * @param backup_moodleform $form
384      * @return int The number of changes the user made
385      */
386     public function process(base_moodleform $form = null) {
387         $form = $this->initialise_stage_form();
388         // Check it hasn't been cancelled
389         if ($form->is_cancelled()) {
390             $this->ui->cancel_process();
391         }
393         $data = $form->get_data();
394         if ($data && confirm_sesskey()) {
395             // Collect into a variable so we can iterate by reference
396             $tasks = $this->ui->get_tasks();
397             $changes = 0;
398             // Iterate each task by reference
399             foreach ($tasks as &$task) {
400                 if ($task instanceof backup_root_task) {
401                     // At this stage all we are interested in is the filename setting
402                     $setting = $task->get_setting('filename');
403                     $name = $setting->get_ui_name();
404                     if (isset($data->$name) &&  $data->$name != $setting->get_value()) {
405                         $setting->set_value($data->$name);
406                         $changes++;
407                     }
408                 }
409             }
410             // Return the number of changes the user made
411             return $changes;
412         } else {
413             return false;
414         }
415     }
416     /**
417      * Creates the backup_confirmation_form instance this stage requires
418      *
419      * @return backup_confirmation_form
420      */
421     protected function initialise_stage_form() {
422         global $PAGE;
423         if ($this->stageform === null) {
424             // Get the form
425             $form = new backup_confirmation_form($this, $PAGE->url);
426             $content = '';
427             $courseheading = false;
429             foreach ($this->ui->get_tasks() as $task) {
430                 if ($setting = $task->get_setting('filename')) {
431                     $form->add_heading('filenamesetting', get_string('filename', 'backup'));
432                     if ($setting->get_value() == 'backup.mbz') {
433                         $format = $this->ui->get_format();
434                         $type = $this->ui->get_type();
435                         $id = $this->ui->get_controller_id();
436                         $users = $this->ui->get_setting_value('users');
437                         $anonymised = $this->ui->get_setting_value('anonymize');
438                         $setting->set_value(backup_plan_dbops::get_default_backup_filename($format, $type, $id, $users, $anonymised));
439                     }
440                     $form->add_setting($setting, $task);
441                     break;
442                 }
443             }
445             // Track progress through tasks.
446             $progress = $this->ui->get_controller()->get_progress();
447             $tasks = $this->ui->get_tasks();
448             $progress->start_progress('initialise_stage_form', count($tasks));
449             $done = 1;
451             foreach ($tasks as $task) {
452                 if ($task instanceof backup_root_task) {
453                     // If its a backup root add a root settings heading to group nicely
454                     $form->add_heading('rootsettings', get_string('rootsettings', 'backup'));
455                 } else if (!$courseheading) {
456                     // we havn't already add a course heading
457                     $form->add_heading('coursesettings', get_string('includeditems', 'backup'));
458                     $courseheading = true;
459                 }
460                 // Iterate all settings, doesnt need to happen by reference
461                 foreach ($task->get_settings() as $setting) {
462                     // For this stage only the filename setting should be editable
463                     if ($setting->get_name() != 'filename') {
464                         $form->add_fixed_setting($setting, $task);
465                     }
466                 }
467                 // Update progress.
468                 $progress->progress($done++);
469             }
470             $progress->end_progress();
471             $this->stageform = $form;
472         }
473         return $this->stageform;
474     }
477 /**
478  * Final stage of backup
479  *
480  * This stage is special in that it is does not make use of a form. The reason for
481  * this is the order of procession of backup at this stage.
482  * The processesion is:
483  * 1. The final stage will be intialise.
484  * 2. The confirmation stage will be processed.
485  * 3. The backup will be executed
486  * 4. The complete stage will be loaded by execution
487  * 5. The complete stage will be displayed
488  *
489  * This highlights that we neither need a form nor a display method for this stage
490  * we simply need to process.
491  *
492  * @copyright 2010 Sam Hemelryk
493  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
494  */
495 class backup_ui_stage_final extends backup_ui_stage {
496     /**
497      * Constructs the final stage
498      * @param backup_ui $ui
499      */
500     public function __construct(backup_ui $ui, array $params=null) {
501         $this->stage = backup_ui::STAGE_FINAL;
502         parent::__construct($ui, $params);
503     }
504     /**
505      * Processes the final stage.
506      *
507      * In this case it ALWAYS passes processing to the previous stage (confirmation)
508      */
509     public function process(base_moodleform $form=null) {
510         return true;
511     }
512     /**
513      * should NEVER be called... throws an exception
514      */
515     protected function initialise_stage_form() {
516         throw new backup_ui_exception('backup_ui_must_execute_first');
517     }
518     /**
519      * should NEVER be called... throws an exception
520      */
521     public function display(core_backup_renderer $renderer) {
522         throw new backup_ui_exception('backup_ui_must_execute_first');
523     }
526 /**
527  * The completed backup stage
528  *
529  * At this stage everything is done and the user will be redirected to view the
530  * backup file in the file browser.
531  *
532  * @copyright 2010 Sam Hemelryk
533  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
534  */
535 class backup_ui_stage_complete extends backup_ui_stage_final {
536     /**
537      * The results of the backup execution
538      * @var array
539      */
540     protected $results;
541     /**
542      * Constructs the complete backup stage
543      * @param backup_ui $ui
544      * @param array|null $params
545      * @param array $results
546      */
547     public function __construct(backup_ui $ui, array $params=null, array $results=null) {
548         $this->results = $results;
549         parent::__construct($ui, $params);
550         $this->stage = backup_ui::STAGE_COMPLETE;
551     }
552     /**
553      * Displays the completed backup stage.
554      *
555      * Currently this just involves redirecting to the file browser with an
556      * appropriate message.
557      *
558      * @param core_backup_renderer $renderer
559      * @return string HTML code to echo
560      */
561     public function display(core_backup_renderer $renderer) {
563         // Get the resulting stored_file record
564         $type = $this->get_ui()->get_controller()->get_type();
565         $courseid = $this->get_ui()->get_controller()->get_courseid();
566         switch ($type) {
567         case 'activity':
568             $cmid = $this->get_ui()->get_controller()->get_id();
569             $cm = get_coursemodule_from_id(null, $cmid, $courseid);
570             $modcontext = context_module::instance($cm->id);
571             $restorerul = new moodle_url('/backup/restorefile.php', array('contextid'=>$modcontext->id));
572             break;
573         case 'course':
574         default:
575             $coursecontext = context_course::instance($courseid);
576             $restorerul = new moodle_url('/backup/restorefile.php', array('contextid'=>$coursecontext->id));
577         }
579         $output = '';
580         $output .= $renderer->box_start();
581         if (!empty($this->results['include_file_references_to_external_content'])) {
582             $output .= $renderer->notification(get_string('filereferencesincluded', 'backup'), 'notifyproblem');
583         }
584         if (!empty($this->results['missing_files_in_pool'])) {
585             $output .= $renderer->notification(get_string('missingfilesinpool', 'backup'), 'notifyproblem');
586         }
587         $output .= $renderer->notification(get_string('executionsuccess', 'backup'), 'notifysuccess');
588         $output .= $renderer->continue_button($restorerul);
589         $output .= $renderer->box_end();
591         return $output;
592     }