MDL-48192 tool_monitor: check output buffer and not ignore it.
[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                     $settings = $task->get_settings();
179                     // First add all settings except the filename setting
180                     foreach ($settings as &$setting) {
181                         if ($setting->get_name() == 'filename') {
182                             continue;
183                         }
184                         $add_settings[] = array($setting, $task);
185                     }
186                     // Then add all dependencies
187                     foreach ($settings as &$setting) {
188                         if ($setting->get_name() == 'filename') {
189                             continue;
190                         }
191                         $dependencies[] = $setting;
192                     }
193                 }
194             }
195             // Add all settings at once.
196             $form->add_settings($add_settings);
197             // Add dependencies.
198             foreach ($dependencies as $depsetting) {
199                 $form->add_dependencies($depsetting);
200             }
201             $this->stageform = $form;
202         }
203         // Return the form
204         return $this->stageform;
205     }
208 /**
209  * Schema stage of backup process
210  *
211  * During the schema stage the user is required to set the settings that relate
212  * to the area that they are backing up as well as its children.
213  *
214  * @copyright 2010 Sam Hemelryk
215  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
216  */
217 class backup_ui_stage_schema extends backup_ui_stage {
218     /**
219      * @var int Maximum number of settings to add to form at once
220      */
221     const MAX_SETTINGS_BATCH = 1000;
223     /**
224      * Schema stage constructor
225      * @param backup_moodleform $ui
226      */
227     public function __construct(backup_ui $ui, array $params=null) {
228         $this->stage = backup_ui::STAGE_SCHEMA;
229         parent::__construct($ui, $params);
230     }
231     /**
232      * Processes the schema stage
233      *
234      * @param backup_moodleform|null $form
235      * @return int The number of changes the user made
236      */
237     public function process(base_moodleform $form = null) {
238         $form = $this->initialise_stage_form();
239         // Check it wasn't cancelled
240         if ($form->is_cancelled()) {
241             $this->ui->cancel_process();
242         }
244         // Check it has been submit
245         $data = $form->get_data();
246         if ($data && confirm_sesskey()) {
247             // Get the tasks into a var so we can iterate by reference
248             $tasks = $this->ui->get_tasks();
249             $changes = 0;
250             // Iterate all tasks by reference
251             foreach ($tasks as &$task) {
252                 // We are only interested in schema settings
253                 if (!($task instanceof backup_root_task)) {
254                     // Store as a variable so we can iterate by reference
255                     $settings = $task->get_settings();
256                     // Iterate by reference
257                     foreach ($settings as &$setting) {
258                         $name = $setting->get_ui_name();
259                         if (isset($data->$name) &&  $data->$name != $setting->get_value()) {
260                             $setting->set_value($data->$name);
261                             $changes++;
262                         } else if (!isset($data->$name) && $setting->get_ui_type() == backup_setting::UI_HTML_CHECKBOX && $setting->get_value()) {
263                             $setting->set_value(0);
264                             $changes++;
265                         }
266                     }
267                 }
268             }
269             // Return the number of changes the user made
270             return $changes;
271         } else {
272             return false;
273         }
274     }
275     /**
276      * Creates the backup_schema_form instance for this stage
277      *
278      * @return backup_schema_form
279      */
280     protected function initialise_stage_form() {
281         global $PAGE;
282         if ($this->stageform === null) {
283             $form = new backup_schema_form($this, $PAGE->url);
284             $tasks = $this->ui->get_tasks();
285             $content = '';
286             $courseheading = false;
287             $add_settings = array();
288             $dependencies = array();
290             // Track progress through each stage.
291             $progress = $this->ui->get_controller()->get_progress();
292             $progress->start_progress('Initialise stage form', 3);
294             // Get settings for all tasks.
295             $progress->start_progress('', count($tasks));
296             $done = 1;
297             foreach ($tasks as $task) {
298                 if (!($task instanceof backup_root_task)) {
299                     if (!$courseheading) {
300                         // If we havn't already display a course heading to group nicely
301                         $form->add_heading('coursesettings', get_string('includeactivities', 'backup'));
302                         $courseheading = true;
303                     }
304                     // First add each setting
305                     foreach ($task->get_settings() as $setting) {
306                         $add_settings[] = array($setting, $task);
307                     }
308                     // The add all the dependencies
309                     foreach ($task->get_settings() as $setting) {
310                         $dependencies[] = $setting;
311                     }
312                 } else if ($this->ui->enforce_changed_dependencies()) {
313                     // Only show these settings if dependencies changed them.
314                     // Add a root settings heading to group nicely
315                     $form->add_heading('rootsettings', get_string('rootsettings', 'backup'));
316                     // Iterate all settings and add them to the form as a fixed
317                     // setting. We only want schema settings to be editable
318                     foreach ($task->get_settings() as $setting) {
319                         if ($setting->get_name() != 'filename') {
320                             $form->add_fixed_setting($setting, $task);
321                         }
322                     }
323                 }
324                 // Update progress.
325                 $progress->progress($done++);
326             }
327             $progress->end_progress();
329             // Add settings for tasks in batches of up to 1000. Adding settings
330             // in larger batches improves performance, but if it takes too long,
331             // we won't be able to update the progress bar so the backup might
332             // time out. 1000 is chosen to balance this.
333             $numsettings = count($add_settings);
334             $progress->start_progress('', ceil($numsettings / self::MAX_SETTINGS_BATCH));
335             $start = 0;
336             $done = 1;
337             while($start < $numsettings) {
338                 $length = min(self::MAX_SETTINGS_BATCH, $numsettings - $start);
339                 $form->add_settings(array_slice($add_settings, $start, $length));
340                 $start += $length;
341                 $progress->progress($done++);
342             }
343             $progress->end_progress();
345             $progress->start_progress('', count($dependencies));
346             $done = 1;
347             foreach ($dependencies as $depsetting) {
348                 $form->add_dependencies($depsetting);
349                 $progress->progress($done++);
350             }
351             $progress->end_progress();
353             // End overall progress through creating form.
354             $progress->end_progress();
355             $this->stageform = $form;
356         }
357         return $this->stageform;
358     }
361 /**
362  * Confirmation stage
363  *
364  * On this stage the user reviews the setting for the backup and can change the filename
365  * of the file that will be generated.
366  *
367  * @copyright 2010 Sam Hemelryk
368  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
369  */
370 class backup_ui_stage_confirmation extends backup_ui_stage {
371     /**
372      * Constructs the stage
373      * @param backup_ui $ui
374      */
375     public function __construct($ui, array $params=null) {
376         $this->stage = backup_ui::STAGE_CONFIRMATION;
377         parent::__construct($ui, $params);
378     }
379     /**
380      * Processes the confirmation stage
381      *
382      * @param backup_moodleform $form
383      * @return int The number of changes the user made
384      */
385     public function process(base_moodleform $form = null) {
386         $form = $this->initialise_stage_form();
387         // Check it hasn't been cancelled
388         if ($form->is_cancelled()) {
389             $this->ui->cancel_process();
390         }
392         $data = $form->get_data();
393         if ($data && confirm_sesskey()) {
394             // Collect into a variable so we can iterate by reference
395             $tasks = $this->ui->get_tasks();
396             $changes = 0;
397             // Iterate each task by reference
398             foreach ($tasks as &$task) {
399                 if ($task instanceof backup_root_task) {
400                     // At this stage all we are interested in is the filename setting
401                     $setting = $task->get_setting('filename');
402                     $name = $setting->get_ui_name();
403                     if (isset($data->$name) &&  $data->$name != $setting->get_value()) {
404                         $setting->set_value($data->$name);
405                         $changes++;
406                     }
407                 }
408             }
409             // Return the number of changes the user made
410             return $changes;
411         } else {
412             return false;
413         }
414     }
415     /**
416      * Creates the backup_confirmation_form instance this stage requires
417      *
418      * @return backup_confirmation_form
419      */
420     protected function initialise_stage_form() {
421         global $PAGE;
422         if ($this->stageform === null) {
423             // Get the form
424             $form = new backup_confirmation_form($this, $PAGE->url);
425             $content = '';
426             $courseheading = false;
428             foreach ($this->ui->get_tasks() as $task) {
429                 if ($setting = $task->get_setting('filename')) {
430                     $form->add_heading('filenamesetting', get_string('filename', 'backup'));
431                     if ($setting->get_value() == 'backup.mbz') {
432                         $format = $this->ui->get_format();
433                         $type = $this->ui->get_type();
434                         $id = $this->ui->get_controller_id();
435                         $users = $this->ui->get_setting_value('users');
436                         $anonymised = $this->ui->get_setting_value('anonymize');
437                         $setting->set_value(backup_plan_dbops::get_default_backup_filename($format, $type, $id, $users, $anonymised));
438                     }
439                     $form->add_setting($setting, $task);
440                     break;
441                 }
442             }
444             // Track progress through tasks.
445             $progress = $this->ui->get_controller()->get_progress();
446             $tasks = $this->ui->get_tasks();
447             $progress->start_progress('initialise_stage_form', count($tasks));
448             $done = 1;
450             foreach ($tasks as $task) {
451                 if ($task instanceof backup_root_task) {
452                     // If its a backup root add a root settings heading to group nicely
453                     $form->add_heading('rootsettings', get_string('rootsettings', 'backup'));
454                 } else if (!$courseheading) {
455                     // we havn't already add a course heading
456                     $form->add_heading('coursesettings', get_string('includeditems', 'backup'));
457                     $courseheading = true;
458                 }
459                 // Iterate all settings, doesnt need to happen by reference
460                 foreach ($task->get_settings() as $setting) {
461                     // For this stage only the filename setting should be editable
462                     if ($setting->get_name() != 'filename') {
463                         $form->add_fixed_setting($setting, $task);
464                     }
465                 }
466                 // Update progress.
467                 $progress->progress($done++);
468             }
469             $progress->end_progress();
470             $this->stageform = $form;
471         }
472         return $this->stageform;
473     }
476 /**
477  * Final stage of backup
478  *
479  * This stage is special in that it is does not make use of a form. The reason for
480  * this is the order of procession of backup at this stage.
481  * The processesion is:
482  * 1. The final stage will be intialise.
483  * 2. The confirmation stage will be processed.
484  * 3. The backup will be executed
485  * 4. The complete stage will be loaded by execution
486  * 5. The complete stage will be displayed
487  *
488  * This highlights that we neither need a form nor a display method for this stage
489  * we simply need to process.
490  *
491  * @copyright 2010 Sam Hemelryk
492  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
493  */
494 class backup_ui_stage_final extends backup_ui_stage {
495     /**
496      * Constructs the final stage
497      * @param backup_ui $ui
498      */
499     public function __construct(backup_ui $ui, array $params=null) {
500         $this->stage = backup_ui::STAGE_FINAL;
501         parent::__construct($ui, $params);
502     }
503     /**
504      * Processes the final stage.
505      *
506      * In this case it ALWAYS passes processing to the previous stage (confirmation)
507      */
508     public function process(base_moodleform $form=null) {
509         return true;
510     }
511     /**
512      * should NEVER be called... throws an exception
513      */
514     protected function initialise_stage_form() {
515         throw new backup_ui_exception('backup_ui_must_execute_first');
516     }
517     /**
518      * should NEVER be called... throws an exception
519      */
520     public function display(core_backup_renderer $renderer) {
521         throw new backup_ui_exception('backup_ui_must_execute_first');
522     }
525 /**
526  * The completed backup stage
527  *
528  * At this stage everything is done and the user will be redirected to view the
529  * backup file in the file browser.
530  *
531  * @copyright 2010 Sam Hemelryk
532  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
533  */
534 class backup_ui_stage_complete extends backup_ui_stage_final {
535     /**
536      * The results of the backup execution
537      * @var array
538      */
539     protected $results;
540     /**
541      * Constructs the complete backup stage
542      * @param backup_ui $ui
543      * @param array|null $params
544      * @param array $results
545      */
546     public function __construct(backup_ui $ui, array $params=null, array $results=null) {
547         $this->results = $results;
548         parent::__construct($ui, $params);
549         $this->stage = backup_ui::STAGE_COMPLETE;
550     }
551     /**
552      * Displays the completed backup stage.
553      *
554      * Currently this just involves redirecting to the file browser with an
555      * appropriate message.
556      *
557      * @param core_backup_renderer $renderer
558      * @return string HTML code to echo
559      */
560     public function display(core_backup_renderer $renderer) {
562         // Get the resulting stored_file record
563         $type = $this->get_ui()->get_controller()->get_type();
564         $courseid = $this->get_ui()->get_controller()->get_courseid();
565         switch ($type) {
566         case 'activity':
567             $cmid = $this->get_ui()->get_controller()->get_id();
568             $cm = get_coursemodule_from_id(null, $cmid, $courseid);
569             $modcontext = context_module::instance($cm->id);
570             $restorerul = new moodle_url('/backup/restorefile.php', array('contextid'=>$modcontext->id));
571             break;
572         case 'course':
573         default:
574             $coursecontext = context_course::instance($courseid);
575             $restorerul = new moodle_url('/backup/restorefile.php', array('contextid'=>$coursecontext->id));
576         }
578         $output = '';
579         $output .= $renderer->box_start();
580         if (!empty($this->results['include_file_references_to_external_content'])) {
581             $output .= $renderer->notification(get_string('filereferencesincluded', 'backup'), 'notifyproblem');
582         }
583         if (!empty($this->results['missing_files_in_pool'])) {
584             $output .= $renderer->notification(get_string('missingfilesinpool', 'backup'), 'notifyproblem');
585         }
586         $output .= $renderer->notification(get_string('executionsuccess', 'backup'), 'notifysuccess');
587         $output .= $renderer->continue_button($restorerul);
588         $output .= $renderer->box_end();
590         return $output;
591     }