Message users on NodeSquirrel disable
[project/backup_migrate.git] / backup_migrate.module
CommitLineData
369df44f 1<?php
369df44f 2
0b503e8b 3
921679f9 4
0b503e8b
RD
5/**
6 * @file
7 * Create (manually or scheduled) and restore backups of your Drupal MySQL
8 * database with an option to exclude table data (e.g. cache_*)
9 */
10
921679f9 11define('BACKUP_MIGRATE_VERSION', '7.x-2.x');
10ec24f0
RD
12define('BACKUP_MIGRATE_MENU_PATH', 'admin/config/system/backup_migrate');
13define('BACKUP_MIGRATE_MENU_DEPTH', 4);
af6a0899 14
0b503e8b
RD
15/* Drupal Hooks */
16
17/**
18 * Implementation of hook_help().
19 */
20function backup_migrate_help($section, $arg) {
21 $help = array(
22 array(
23 'body' =>
24 t('Backup and Migrate makes the task of backing up your Drupal database and migrating data from one Drupal install to another easier. It provides a function to backup the entire database to file or download, and to restore from a previous backup. You can also schedule the backup operation. Compression of backup files is also supported. The database backup files created with this module can be imported into this or any other Drupal installation with the !restorelink, or you can use a database tool such as <a href="!phpmyadminurl">phpMyAdmin</a> or the mysql command line command.',
25 array(
af6a0899 26 '!restorelink' => user_access('restore from backup') ? l(t('restore feature'), BACKUP_MIGRATE_MENU_PATH . '/restore') : t('restore feature'),
0b503e8b
RD
27 '!phpmyadminurl' => 'http://www.phpmyadmin.net'
28 )
29 )
30 ),
af6a0899 31 BACKUP_MIGRATE_MENU_PATH => array(
0b503e8b
RD
32 'title' => t('Quick Backup Tab'),
33 'body' => t('Use this form to run simple manual backups of your database. Visit the !helppage for more help using this module',
34 array('!helppage' => l(t('help page'), 'admin/help/backup_migrate'))),
0e4efc96 35 'access arguments' => array('perform backup'),
0b503e8b 36 ),
af6a0899 37 BACKUP_MIGRATE_MENU_PATH . '/export/advanced' => array(
0b503e8b
RD
38 'title' => t('Advanced Backup Tab'),
39 'body' => t('Use this form to run manual backups of your database with more advanced options. If you have any !profilelink saved you can load those settings. You can save any of the changes you make to these settings as a new settings profile.',
af6a0899 40 array("!profilelink" => user_access('administer backup and migrate') ? l(t('settings profiles'), BACKUP_MIGRATE_MENU_PATH . '/profile') : t('settings profiles'), '!restorelink' => user_access('restore from backup') ? l(t('restore feature'), BACKUP_MIGRATE_MENU_PATH . '/restore') : t('restore feature'), '!phpmyadminurl' => 'http://www.phpmyadmin.net')),
0e4efc96 41 'access arguments' => array('perform backup'),
0b503e8b 42 ),
af6a0899 43 BACKUP_MIGRATE_MENU_PATH . '/restore' => array(
0b503e8b 44 'title' => t('Restore Tab'),
b9df6ad2 45 'body' => t('Upload a backup and migrate backup file. The restore function will not work with database dumps from other sources such as phpMyAdmin.'),
0e4efc96 46 'access arguments' => array('restore from backup'),
0b503e8b 47 ),
af6a0899 48 BACKUP_MIGRATE_MENU_PATH . '/destination' => array(
0b503e8b
RD
49 'title' => t('Destinations'),
50 'body' => t('Destinations are the places you can save your backup files to or them load from.'),
51 'more' => t('Files can be saved to a directory on your web server, downloaded to your desktop or emailed to a specified email account. From the Destinations tab you can create, delete and edit destinations or list the files which have already been backed up to the available destinations.'),
0e4efc96 52 'access arguments' => array('administer backup and migrate'),
0b503e8b 53 ),
af6a0899 54 BACKUP_MIGRATE_MENU_PATH . '/profile' => array(
0b503e8b
RD
55 'title' => t('Profiles'),
56 'body' => t('Profiles are saved backup settings. Profiles store your table exclusion settings as well as your backup file name, compression and timestamp settings. You can use profiles in !schedulelink and for !manuallink.',
af6a0899 57 array('!schedulelink' => user_access('administer backup and migrate') ? l(t('schedules'), BACKUP_MIGRATE_MENU_PATH . '/schedule') : t('settings profiles'), '!manuallink' => user_access('perform backups') ? l(t('manual backups'), BACKUP_MIGRATE_MENU_PATH) : t('manual backups'))),
0b503e8b 58 'more' => t('You can create new profiles using the add profiles tab or by checking the "Save these settings" button on the advanced backup page.'),
0e4efc96 59 'access arguments' => array('administer backup and migrate'),
0b503e8b 60 ),
af6a0899 61 BACKUP_MIGRATE_MENU_PATH . '/schedule' => array(
0b503e8b
RD
62 'title' => t('Scheduling'),
63 'body' => t('Automatically backup up your database on a regular schedule using <a href="!cronurl">cron</a>.',
64 array('!cronurl' => 'http://drupal.org/cron')),
65 'more' => t('Each schedule will run a maximum of once per cron run, so they will not run more frequently than your cron is configured to run. If you specify a number of backups to keep for a schedule, old backups will be deleted as new ones created. <strong>If specifiy a number of files to keep other backup files in that schedule\'s destination will get deleted</strong>.'),
0e4efc96 66 'access arguments' => array('administer backup and migrate'),
0b503e8b
RD
67 ),
68 );
69
70 if (isset($help[$section])) {
71 return $help[$section]['body'];
72 }
73
74 if ($section == 'admin/help#backup_migrate') {
75 $out = "";
76 foreach ($help as $key => $section) {
0e4efc96
RD
77 if (isset($section['access arguments'])) {
78 foreach($section['access arguments'] as $access) {
79 if (!user_access($access)) {
80 continue 2;
81 }
82 }
83 }
0b503e8b
RD
84 if (@$section['title']) {
85 if (!is_numeric($key)) {
86 $section['title'] = l($section['title'], $key);
87 }
88 $out .= "<h3>". $section['title'] ."</h3>";
89 }
90 $out .= "<p>". $section['body'] ."</p>";
91 if (!empty($section['more'])) {
92 $out .= "<p>". $section['more'] ."</p>";
93 }
94 }
95 return $out;
96 }
97}
98
99/**
100 * Implementation of hook_menu().
101 */
6f203ddd 102function backup_migrate_menu() {
369df44f 103 $items = array();
0b503e8b 104
af6a0899 105 $items[BACKUP_MIGRATE_MENU_PATH] = array(
0b503e8b
RD
106 'title' => 'Backup and Migrate',
107 'description' => 'Backup/restore your database or migrate data to or from another Drupal site.',
b9df6ad2
RD
108 'page callback' => 'backup_migrate_menu_callback',
109 'page arguments' => array('', 'backup_migrate_ui_manual_backup_quick', TRUE),
0b503e8b 110 'access arguments' => array('access backup and migrate'),
6f203ddd
RD
111 'type' => MENU_NORMAL_ITEM,
112 );
af6a0899 113 $items[BACKUP_MIGRATE_MENU_PATH . '/export'] = array(
0b503e8b
RD
114 'title' => 'Backup',
115 'description' => 'Backup the database.',
116 'page callback' => 'backup_migrate_menu_callback',
117 'page arguments' => array('', 'backup_migrate_ui_manual_backup_quick', TRUE),
118 'access arguments' => array('access backup and migrate'),
6f203ddd
RD
119 'weight' => 0,
120 'type' => MENU_DEFAULT_LOCAL_TASK,
121 );
af6a0899 122 $items[BACKUP_MIGRATE_MENU_PATH . '/export/quick'] = array(
0b503e8b
RD
123 'title' => 'Quick Backup',
124 'description' => 'Backup the database.',
125 'page callback' => 'backup_migrate_menu_callback',
126 'page arguments' => array('', 'backup_migrate_ui_manual_backup_quick', TRUE),
127 'access arguments' => array('access backup and migrate'),
128 'weight' => 0,
6f203ddd
RD
129 'type' => MENU_DEFAULT_LOCAL_TASK,
130 );
af6a0899 131 $items[BACKUP_MIGRATE_MENU_PATH . '/export/advanced'] = array(
0b503e8b
RD
132 'title' => 'Advanced Backup',
133 'description' => 'Backup the database.',
134 'page callback' => 'backup_migrate_menu_callback',
135 'page arguments' => array('', 'backup_migrate_ui_manual_backup_advanced', TRUE),
136 'access arguments' => array('perform backup'),
137 'weight' => 1,
6f203ddd
RD
138 'type' => MENU_LOCAL_TASK,
139 );
af6a0899 140 $items[BACKUP_MIGRATE_MENU_PATH . '/restore'] = array(
0b503e8b
RD
141 'title' => 'Restore',
142 'description' => 'Restore the database from a previous backup',
143 'page callback' => 'backup_migrate_menu_callback',
144 'page arguments' => array('', 'backup_migrate_ui_manual_restore', TRUE),
6f203ddd 145 'access arguments' => array('restore from backup'),
0b503e8b
RD
146 'weight' => 1,
147 'type' => MENU_LOCAL_TASK,
6f203ddd 148 );
710d8d1c
DG
149 $items[BACKUP_MIGRATE_MENU_PATH . '/nodesquirrel'] = array(
150 'title' => 'NodeSquirrel',
c6103791
RD
151 'page callback' => 'backup_migrate_menu_callback',
152 'page arguments' => array('destinations.nodesquirrel', 'nodesquirrel_settings_page'),
153 'access arguments' => array('administer backup and migrate'),
154 'weight' => 10,
155 'type' => MENU_LOCAL_TASK,
156 );
6f203ddd 157
0b503e8b
RD
158 backup_migrate_include('crud');
159 $items += backup_migrate_crud_menu();
369df44f
RD
160 return $items;
161}
162
163/**
0b503e8b
RD
164 * Implementation of hook_cron().
165 *
166 * Takes care of scheduled backups and deletes abandoned temp files.
369df44f
RD
167 */
168function backup_migrate_cron() {
b9df6ad2
RD
169 // Set the message mode to logging.
170 _backup_migrate_message_callback('_backup_migrate_message_log');
171
0b503e8b
RD
172 backup_migrate_include('schedules');
173 backup_migrate_schedules_run();
ca3e9195
RD
174
175 backup_migrate_include('files');
176 _backup_migrate_temp_files_delete();
369df44f
RD
177}
178
369df44f 179/**
b9df6ad2 180 * Implementation of hook_permission().
369df44f 181 */
b9df6ad2
RD
182function backup_migrate_permission() {
183 return array(
184 'access backup and migrate' => array(
185 'title' => t('Access Backup and Migrate'),
186 'description' => t('Access the Backup and Migrate admin section.'),
187 ),
188 'perform backup' => array(
189 'title' => t('Perform a backup'),
190 'description' => t('Back up any of the available databases.'),
191 ),
192 'access backup files' => array(
193 'title' => t('Access backup files'),
194 'description' => t('Access and download the previously created backup files.'),
195 ),
196 'delete backup files' => array(
197 'title' => t('Delete backup files'),
198 'description' => t('Delete the previously created backup files.'),
199 ),
200 'restore from backup' => array(
201 'title' => t('Restore the site'),
202 'description' => t('Restore the site\'s database from a backup file.'),
203 ),
204 'administer backup and migrate' => array(
ce126c42 205 'title' => t('Administer Backup and Migrate'),
b9df6ad2
RD
206 'description' => t('Edit Backup and Migrate profiles, schedules and destinations.'),
207 ),
208 );
369df44f
RD
209}
210
211/**
0b503e8b 212 * Implementation of hook_simpletest().
369df44f 213 */
0b503e8b
RD
214function backup_migrate_simpletest() {
215 $dir = drupal_get_path('module', 'backup_migrate') .'/tests';
216 $tests = file_scan_directory($dir, '\.test$');
217 return array_keys($tests);
369df44f
RD
218}
219
220/**
0b503e8b 221 * Implementation of hook_theme().
369df44f 222 */
0b503e8b
RD
223function backup_migrate_theme() {
224 $themes = array(
225 'backup_migrate_ui_manual_quick_backup_form' => array(
226 'arguments' => array('form'),
b9df6ad2 227 'render element' => 'form',
0b503e8b 228 ),
369df44f 229 );
0b503e8b 230 return $themes;
369df44f
RD
231}
232
0b503e8b
RD
233/* Menu Callbacks */
234
369df44f 235/**
0b503e8b 236 * A menu callback helper. Handles file includes and interactivity setting.
369df44f 237 */
0b503e8b
RD
238function backup_migrate_menu_callback($include, $function, $interactive = TRUE) {
239 if ($include) {
240 backup_migrate_include($include);
369df44f 241 }
b9df6ad2
RD
242 // Set the message handler based on interactivity setting.
243 _backup_migrate_message_callback($interactive ? '_backup_migrate_message_browser' : '_backup_migrate_message_log');
0b503e8b
RD
244 // Get the arguments with the first 3 removed.
245 $args = array_slice(func_get_args(), 3);
246 return call_user_func_array($function, $args);
369df44f
RD
247}
248
249/**
0b503e8b 250 * Include views .inc files as necessary.
369df44f 251 */
0b503e8b
RD
252function backup_migrate_include() {
253 static $used = array();
254 foreach (func_get_args() as $file) {
255 if (!isset($used[$file])) {
b9df6ad2 256 require_once DRUPAL_ROOT . '/' . drupal_get_path('module', 'backup_migrate') . "/includes/$file.inc";
0b503e8b
RD
257 }
258
259 $used[$file] = TRUE;
369df44f 260 }
369df44f
RD
261}
262
263
264/**
0b503e8b 265 * The menu callback for easy manual backups.
369df44f 266 */
0b503e8b 267function backup_migrate_ui_manual_backup_quick() {
b9df6ad2 268 $out = array();
0b503e8b 269 if (user_access('perform backup')) {
b9df6ad2 270 return drupal_get_form('backup_migrate_ui_manual_quick_backup_form');
0b503e8b
RD
271 }
272 else {
b9df6ad2 273 return t('You do not have permission to back up this site.');
0b503e8b
RD
274 }
275 return $out;
369df44f
RD
276}
277
0b503e8b
RD
278/**
279 * The menu callback for advanced manual backups.
280 */
281function backup_migrate_ui_manual_backup_advanced() {
282 backup_migrate_include('profiles');
b9df6ad2 283 $out = array();
43fc2fb5 284 $profile_id = arg(BACKUP_MIGRATE_MENU_DEPTH + 2);
0b503e8b 285 $profile = _backup_migrate_profile_saved_default_profile($profile_id);
b9df6ad2
RD
286
287 $out[] = drupal_get_form('backup_migrate_ui_manual_backup_load_profile_form', $profile);
288 $out[] = drupal_get_form('backup_migrate_ui_manual_backup_form', $profile);
0b503e8b 289 return $out;
369df44f
RD
290}
291
292/**
0b503e8b 293 * The backup/export load profile form.
369df44f 294 */
b9df6ad2 295function backup_migrate_ui_manual_backup_load_profile_form($form, &$form_state, $profile = NULL) {
369df44f 296 $form = array();
0b503e8b
RD
297 $profile_options = _backup_migrate_get_profile_form_item_options();
298 if (count($profile_options) > 0) {
299 $profile_options = array(0 => t('-- Select a Settings Profile --')) + $profile_options;
300 $form['profile'] = array(
301 "#title" => t("Settings Profile"),
302 "#collapsible" => TRUE,
303 "#collapsed" => FALSE,
304 "#prefix" => '<div class="container-inline">',
305 "#suffix" => '</div>',
306 "#tree" => FALSE,
307 "#description" => t("You can load a profile. Any changes you made below will be lost."),
308 );
309 $form['profile']['profile_id'] = array(
310 "#type" => "select",
311 "#title" => t("Load Settings"),
b9df6ad2 312 '#default_value' => is_object($profile) ? $profile->get_id() : 0,
0b503e8b
RD
313 "#options" => $profile_options,
314 );
315 $form['profile']['load_profile'] = array(
316 '#type' => 'submit',
317 '#value' => t('Load Profile'),
369df44f
RD
318 );
319 }
369df44f
RD
320 return $form;
321}
322
323/**
0b503e8b 324 * Submit the profile load form.
369df44f 325 */
0b503e8b
RD
326function backup_migrate_ui_manual_backup_load_profile_form_submit($form, &$form_state) {
327 if ($profile = backup_migrate_get_profile($form_state['values']['profile_id'])) {
b9df6ad2 328 variable_set("backup_migrate_profile_id", $profile->get_id());
af6a0899 329 $form_state['redirect'] = BACKUP_MIGRATE_MENU_PATH . '/export/advanced';
0b503e8b
RD
330 }
331 else {
332 variable_set("backup_migrate_profile_id", NULL);
369df44f 333 }
369df44f
RD
334}
335
336/**
0b503e8b 337 * The quick backup form.
ff90901b 338 */
b9df6ad2
RD
339function backup_migrate_ui_manual_quick_backup_form($form, &$form_state) {
340 backup_migrate_include('profiles', 'destinations');
0b503e8b 341 drupal_add_js(drupal_get_path('module', 'backup_migrate') .'/backup_migrate.js');
ff90901b 342
0b503e8b 343 $form = array();
ff90901b 344
0b503e8b
RD
345 $form['quickbackup'] = array(
346 '#type' => 'fieldset',
347 "#title" => t("Quick Backup"),
348 "#collapsible" => FALSE,
349 "#collapsed" => FALSE,
350 "#tree" => FALSE,
351 );
ff90901b 352
0b503e8b 353 $form['quickbackup']['source_id'] = _backup_migrate_get_source_pulldown(variable_get('backup_migrate_source_id', NULL));
ff90901b 354
0b503e8b
RD
355 $form['quickbackup']['destination_id'] = array(
356 "#type" => "select",
357 "#title" => t("Destination"),
358 "#options" => _backup_migrate_get_destination_form_item_options('manual backup'),
359 "#default_value" => variable_get("backup_migrate_destination_id", "download"),
360 );
361 $profile_options = _backup_migrate_get_profile_form_item_options();
362 $form['quickbackup']['profile_id'] = array(
363 "#type" => "select",
364 "#title" => t("Settings Profile"),
365 '#default_value' => variable_get('backup_migrate_profile_id', NULL),
366 "#options" => $profile_options,
367 );
ff90901b 368
0b503e8b
RD
369 $form['quickbackup']['submit'] = array(
370 '#type' => 'submit',
371 '#value' => t('Backup now'),
372 '#weight' => 1,
ff90901b 373 );
0b503e8b
RD
374
375 $form['advanced'] = array(
376 '#type' => 'markup',
af6a0899 377 '#markup' => t('For more backup options, try the <a href="!advancedurl">advanced backup page</a>.', array('!advancedurl' => url(BACKUP_MIGRATE_MENU_PATH . '/export/advanced'))),
0b503e8b
RD
378 );
379
380
381 return $form;
ff90901b
RD
382}
383
0b503e8b 384/**
b9df6ad2
RD
385 * Validate the quick backup form.
386 */
387function backup_migrate_ui_manual_quick_backup_form_validate($form, &$form_state) {
388 if ($form_state['values']['source_id'] == $form_state['values']['destination_id']) {
389 form_set_error('destination_id', t('A source cannot be backed up to itself. Please pick a different destination for this backup.'));
390 }
391}
392
393/**
0b503e8b
RD
394 * Submit the quick backup form.
395 */
396function backup_migrate_ui_manual_quick_backup_form_submit($form, &$form_state) {
51b5a20e 397 backup_migrate_include('profiles', 'destinations');
0b503e8b
RD
398 if (user_access('perform backup')) {
399 // For a quick backup use the default settings.
400 $settings = _backup_migrate_profile_saved_default_profile($form_state['values']['profile_id']);
401
402 // Set the destination to the one chosen in the pulldown.
403 $settings->destination_id = $form_state['values']['destination_id'];
404 $settings->source_id = $form_state['values']['source_id'];
405
406 // Save the settings for next time.
407 variable_set("backup_migrate_source_id", $form_state['values']['source_id']);
408 variable_set("backup_migrate_destination_id", $form_state['values']['destination_id']);
409 variable_set("backup_migrate_profile_id", $form_state['values']['profile_id']);
410
411 // Do the backup.
412 backup_migrate_ui_manual_backup_perform($settings);
413 }
af6a0899 414 $form_state['redirect'] = BACKUP_MIGRATE_MENU_PATH;
ff90901b
RD
415}
416
417/**
0b503e8b 418 * Theme the quick backup form.
ff90901b 419 */
0b503e8b 420function theme_backup_migrate_ui_manual_quick_backup_form($form) {
b9df6ad2
RD
421 $form = $form['form'];
422
0b503e8b
RD
423 // Remove the titles so that the pulldowns can be displayed inline.
424 unset($form['quickbackup']['source_id']['#title']);
425 unset($form['quickbackup']['destination_id']['#title']);
426 unset($form['quickbackup']['profile_id']['#title']);
427
428 $replacements = array(
429 '!from' => drupal_render($form['quickbackup']['source_id']),
430 '!to' => drupal_render($form['quickbackup']['destination_id']),
431 '!profile' => drupal_render($form['quickbackup']['profile_id']),
432 '!submit' => drupal_render($form['quickbackup']['submit']),
433 );
434 $form['quickbackup']['markup'] = array(
435 '#type' => 'markup',
436 "#prefix" => '<div class="container-inline">',
437 "#suffix" => '</div>',
b9df6ad2 438 '#markup' => t('Backup from !from to !to using !profile !submit', $replacements),
ff90901b 439 );
0b503e8b
RD
440 unset($form['quickbackup']['source_id']);
441 unset($form['quickbackup']['destination_id']);
442 unset($form['quickbackup']['profile_id']);
443 unset($form['quickbackup']['submit']);
b9df6ad2
RD
444
445 return drupal_render_children($form);
ff90901b
RD
446}
447
448/**
0b503e8b 449 * The backup/export form.
369df44f 450 */
b9df6ad2
RD
451function backup_migrate_ui_manual_backup_form($form, &$form_state, $profile) {
452 drupal_add_js(drupal_get_path('module', 'backup_migrate') .'/backup_migrate.js', array('type' => 'module', 'scope' => 'footer'));
369df44f 453
0b503e8b 454 $form = array();
51b5a20e 455
b9df6ad2 456 $form += _backup_migrate_get_source_form('db');
0b503e8b 457 $form += _backup_migrate_ui_backup_settings_form($profile);
b9df6ad2 458
0b503e8b
RD
459 $form['profile_id'] = array(
460 "#type" => "value",
461 '#default_value' => $profile->get_id(),
462 );
463 $form['storage'] = array(
464 "#type" => "value",
465 '#default_value' => $profile->storage,
466 );
467 $form['destination'] = array(
468 "#type" => "fieldset",
469 "#title" => t("Backup Destination"),
470 "#collapsible" => TRUE,
471 "#collapsed" => FALSE,
472 "#tree" => FALSE,
473 "#description" => t("Choose where the backup file will be saved. Backup files contain sensitive data, so be careful where you save them. Select 'Download' to download the file to your desktop."),
b9df6ad2 474 '#weight' => 70,
0b503e8b
RD
475 );
476 $form['destination']['destination_id'] = array(
477 "#type" => "select",
478 "#title" => t("Destination"),
479 "#options" => _backup_migrate_get_destination_form_item_options('manual backup'),
480 "#default_value" => variable_get("backup_migrate_destination_id", "download"),
481 );
482 if (user_access('administer backup and migrate')) {
af6a0899 483 $form['destination']['destination_id']['#description'] = l(t("Create new destination"), BACKUP_MIGRATE_MENU_PATH . "/destination/add");
369df44f 484 }
369df44f 485
0b503e8b
RD
486 if (user_access('administer backup and migrate')) {
487 $form['save_settings'] = array(
488 "#type" => "checkbox",
489 "#title" => t('Save these settings.'),
490 "#default_value" => FALSE,
b9df6ad2 491 '#weight' => 80,
0b503e8b
RD
492 );
493 $form['save_options'] = array(
494 '#prefix' => '<div class="backup-migrate-save-options">',
495 '#suffix' => '</div>',
b9df6ad2 496 '#weight' => 90,
0b503e8b
RD
497 );
498 $name = array(
499 '#default_value' => $profile->get('name'),
500 '#type' => 'textfield',
501 '#title' => t('Save the settings as'),
502 );
369df44f 503
0b503e8b
RD
504 if ($profile->get_id()) {
505 $form['save_options']['create_new'] = array(
506 '#default_value' => $profile->get('name'),
507 '#type' => 'radios',
508 '#default_value' => 0,
509 '#options' => array(
510 0 => t("Replace the '%profile' profile", array('%profile' => $profile->get('name'))),
511 1 => t('Create new profile'),
512 ),
513 );
369df44f 514
0b503e8b
RD
515 $name["#title"] = t('Profile name');
516 $name["#description"] = t("This will be the name of your new profile if you select 'Create new profile' otherwise it will become the name of the '%profile' profile.", array('%profile' => $profile->get('name')));
369df44f 517 }
0b503e8b
RD
518 else {
519 $name["#title"] = t('Save the settings as');
af6a0899 520 $name["#description"] = t('Pick a name for the settings. Your settings will be saved as a profile and will appear in the <a href="!url">Profiles Tab</a>.', array('!url' => url(BACKUP_MIGRATE_MENU_PATH . '/profile')));
0b503e8b
RD
521 $name["#default_value"] = t('Untitled Profile');
522 }
523 $form['save_options']['name'] = $name;
524 $form['save_options'][] = array(
525 '#type' => 'submit',
526 '#value' => t('Save Without Backing Up'),
527 );
369df44f 528 }
51b5a20e
RD
529 $form['#validate'][] = 'backup_migrate_ui_manual_quick_backup_form_validate';
530 $form['#submit'][] = 'backup_migrate_ui_manual_backup_form_submit';
0b503e8b
RD
531
532 $form[] = array(
533 '#type' => 'submit',
b9df6ad2
RD
534 '#value' => t('Backup now'),
535 '#weight' => 100,
0b503e8b
RD
536 );
537 return $form;
369df44f
RD
538}
539
b9df6ad2 540
369df44f 541/**
0b503e8b 542 * Submit the form. Save the values as defaults if desired and output the backup file.
369df44f 543 */
0b503e8b
RD
544function backup_migrate_ui_manual_backup_form_submit($form, &$form_state) {
545 // Save the settings profile if the save box is checked.
b9df6ad2
RD
546// $form_state['values']['nodata_tables'] = array_filter((array)$form_state['values']['nodata_tables']);
547// $form_state['values']['exclude_tables'] = array_filter((array)$form_state['values']['exclude_tables']);
548
549 $profile = backup_migrate_crud_create_item('profile', $form_state['values']);
550
51b5a20e 551 // Save the settings profile if the save box is checked.
0b503e8b
RD
552 if ($form_state['values']['save_settings'] && user_access('administer backup and migrate')) {
553 if (@$form_state['values']['create_new']) {
51b5a20e 554 // Reset the id and storage so a new item will be saved.
b9df6ad2 555 $profile->set_id(NULL);
51b5a20e 556 $profile->storage = BACKUP_MIGRATE_STORAGE_NONE;
369df44f 557 }
0b503e8b
RD
558 $profile->save();
559 variable_set("backup_migrate_profile_id", $profile->get_id());
560 variable_set("backup_migrate_destination_id", $form_state['values']['destination_id']);
369df44f 561 }
0b503e8b
RD
562
563 // Perform the actual backup if that is what was selected.
b9df6ad2
RD
564 if ($form_state['values']['op'] == t('Backup now') && user_access('perform backup')) {
565 backup_migrate_ui_manual_backup_perform($profile);
369df44f 566 }
af6a0899 567 $form_state['redirect'] = BACKUP_MIGRATE_MENU_PATH . "/export/advanced";
369df44f
RD
568}
569
570/**
0b503e8b 571 * Perform an actual manual backup and tell the user of the progress.
369df44f 572 */
0b503e8b
RD
573
574function backup_migrate_ui_manual_backup_perform($settings) {
0b503e8b 575 // Peform the actual backup.
b9df6ad2 576 backup_migrate_perform_backup($settings);
369df44f
RD
577}
578
579/**
0b503e8b 580 * The restore/import upload page.
369df44f 581 */
0b503e8b
RD
582function backup_migrate_ui_manual_restore() {
583 return drupal_get_form('backup_migrate_ui_manual_restore_form');
369df44f
RD
584}
585
369df44f 586/**
0b503e8b 587 * The restore/import upload form.
369df44f 588 */
0b503e8b
RD
589function backup_migrate_ui_manual_restore_form() {
590 backup_migrate_include('filters', 'destinations');
369df44f 591
0b503e8b 592 $form = array();
369df44f 593
0b503e8b
RD
594 $sources = _backup_migrate_get_destination_form_item_options('source');
595 if (count($sources) > 1) {
596 $form['source_id'] = array(
597 "#type" => "select",
598 "#title" => t("Restore to"),
599 "#options" => _backup_migrate_get_destination_form_item_options('source'),
600 "#description" => t("Choose the database to restore to. Any database destinations you have created and any databases specified in your settings.php can be restored to."),
b9df6ad2 601 "#default_value" => 'db',
0b503e8b
RD
602 );
603 }
604 else {
605 $form['source_id'] = array(
606 "#type" => "value",
b9df6ad2 607 "#value" => 'db',
0b503e8b
RD
608 );
609 }
610
611 $form['backup_migrate_restore_upload'] = array(
612 '#title' => t('Upload a Backup File'),
613 '#type' => 'file',
614 '#description' => t("Upload a backup file created by this version of this module. For other database backups please use another tool for import. Max file size: %size", array("%size" => format_size(file_upload_max_size()))),
615 );
b9df6ad2 616 drupal_set_message(t('Restoring will delete some or all of your data and cannot be undone. <strong>Always test your backups on a non-production server!</strong>'), 'warning', FALSE);
0b503e8b
RD
617
618 $form = array_merge_recursive($form, backup_migrate_filters_settings_form(backup_migrate_filters_settings_default('restore'), 'restore'));
619 // Add the advanced fieldset if there are any fields in it.
620 if (@$form['advanced']) {
621 $form['advanced']['#type'] = 'fieldset';
622 $form['advanced']['#title'] = t('Advanced Options');
623 $form['advanced']['#collapsed'] = true;
624 $form['advanced']['#collapsible'] = true;
625 }
626
627 $form['submit'] = array(
628 '#type' => 'submit',
b9df6ad2 629 '#value' => t('Restore now'),
0b503e8b
RD
630 );
631 if (user_access('access backup files')) {
632 $form[] = array(
633 '#type' => 'markup',
af6a0899 634 '#markup' => t('<p>Or you can restore one of the files in your <a href="!url">saved backup destinations.</a></p>', array("!url" => url(BACKUP_MIGRATE_MENU_PATH . "/destination"))),
0b503e8b
RD
635 );
636 }
637 $form['#attributes'] = array('enctype' => 'multipart/form-data');
638 return $form;
369df44f
RD
639}
640
641/**
0b503e8b 642 * The restore submit. Do the restore.
369df44f 643 */
0b503e8b 644function backup_migrate_ui_manual_restore_form_submit($form, &$form_state) {
213d2e18 645 $validators = array('file_validate_extensions' => array('gz zip sql mysql bz bz2'));
f0a4737d 646 if ($file = file_save_upload('backup_migrate_restore_upload', $validators)) {
0b503e8b 647 backup_migrate_include('destinations');
b9df6ad2 648 backup_migrate_perform_restore('upload', $file->uri, $form_state['values']);
0b503e8b 649 }
af6a0899 650 $form_state['redirect'] = BACKUP_MIGRATE_MENU_PATH . '/restore';
369df44f
RD
651}
652
653/**
0b503e8b
RD
654 * Convert an item to an 'exportable'.
655 */
b9df6ad2 656function backup_migrate_ui_export_form($form, &$form_state, $item) {
0b503e8b
RD
657 if ($item && function_exists('ctools_var_export')) {
658 $code = ctools_var_export($item);
659 $form = ctools_export_form($form_state, $code);
660 return $form;
369df44f 661 }
0b503e8b 662 return array();
369df44f
RD
663}
664
665/**
0b503e8b
RD
666 * Perform a backup with the given settings.
667 */
668function backup_migrate_perform_backup(&$settings) {
b9df6ad2
RD
669 backup_migrate_include('destinations', 'files', 'filters');
670 timer_start('backup_migrate_backup');
0b503e8b
RD
671
672 // If not in 'safe mode', increase the maximum execution time:
eb960f37 673 if (!ini_get('safe_mode') && strpos(ini_get('disable_functions'), 'set_time_limit') === FALSE && ini_get('max_execution_time') < 1200) {
0b503e8b 674 set_time_limit(variable_get('backup_migrate_backup_max_time', 1200));
369df44f 675 }
369df44f 676
67158da5 677 $timestamp = '';
0b503e8b 678 if ($settings->append_timestamp && $settings->timestamp_format) {
67158da5 679 $timestamp = format_date(time(), 'custom', $settings->timestamp_format);
0b503e8b 680 }
67158da5 681 $filename = _backup_migrate_construct_filename($settings->filename, $timestamp);
369df44f 682
a3f879bd 683 $file = new backup_file(array('filename' => $filename));
0b503e8b
RD
684 if (!$file) {
685 backup_migrate_backup_fail("Could not run backup because a temporary file could not be created.", array(), $settings);
686 return FALSE;
369df44f 687 }
0b503e8b 688
b9df6ad2
RD
689 // Register shutdown callback to deal with timeouts.
690 register_shutdown_function('backup_migrate_shutdown', $settings);
691
692 $file = backup_migrate_filters_backup($file, $settings);
0b503e8b
RD
693 if (!$file) {
694 if (_backup_migrate_check_timeout()) {
b9df6ad2 695 backup_migrate_backup_fail('Could not complete the backup because the script timed out. Try increasing your PHP <a href="!url">max_execution_time setting</a>.', array('!url' => 'http://www.php.net/manual/en/info.configuration.php#ini.max-execution-time'), $settings);
369df44f 696 }
369df44f 697 else {
b9df6ad2 698 backup_migrate_backup_fail("Could not complete the backup.", array(), $settings);
369df44f 699 }
0b503e8b 700 return FALSE;
369df44f 701 }
369df44f 702
0b503e8b
RD
703 $file = backup_migrate_destination_save_file($file, $settings);
704 if (!$file) {
705 backup_migrate_backup_fail("Could not run backup because the file could not be saved to the destination.", array(), $settings);
706 return FALSE;
707 }
369df44f 708
b9df6ad2
RD
709 // Backup succeeded,
710 $time = timer_stop('backup_migrate_backup');
711 $message = '%source backed up successfully to %file in destination %dest in !time ms. !action';
712 $params = array(
a3f879bd 713 '%file' => $filename,
b9df6ad2
RD
714 '%dest' => $settings->get_destination_name(),
715 '%source' => $settings->get_source_name(),
716 '!time' => $time['time'],
717 '!action' => !empty($settings->performed_action) ? $settings->performed_action : '',
718 );
719 if (($destination = $settings->get_destination()) && ($links = $destination->get_file_links($file->file_id()))) {
720 $params['!links'] = implode(", ", $links);
721 }
722
723 backup_migrate_backup_succeed($message, $params, $settings);
0b503e8b 724 return $file;
6f203ddd
RD
725}
726
727/**
0b503e8b 728 * Restore from a file in the given destination.
6f203ddd 729 */
b9df6ad2 730function backup_migrate_perform_restore($destination_id, $file, $settings = array()) {
0b503e8b 731 backup_migrate_include('files', 'filters');
b9df6ad2 732 timer_start('backup_migrate_restore');
0b503e8b
RD
733
734 // If not in 'safe mode', increase the maximum execution time:
eb960f37 735 if (!ini_get('safe_mode') && strpos(ini_get('disable_functions'), 'set_time_limit') === FALSE && ini_get('max_execution_time') < variable_get('backup_migrate_backup_max_time', 1200)) {
b9df6ad2 736 set_time_limit(variable_get('backup_migrate_restore_max_time', 1200));
6f203ddd 737 }
369df44f 738
b9df6ad2
RD
739 // Make the settings into a default profile.
740 if (!is_object($settings)) {
741 $settings = backup_migrate_crud_create_item('profile', $settings);
742 $settings->source_id = empty($settings->source_id) ? 'db' : $settings->source_id;
369df44f 743 }
369df44f 744
b9df6ad2
RD
745 // Register shutdown callback.
746 register_shutdown_function('backup_migrate_shutdown', $settings);
747
748 if (!is_object($file)) {
749 // Load the file from the destination.
750 $file = backup_migrate_destination_get_file($destination_id, $file);
751 if (!$file) {
752 _backup_migrate_message("Could not restore because the file could not be loaded from the destination.", array(), 'error');
753 backup_migrate_cleanup();
754 return FALSE;
755 }
0b503e8b 756 }
43fc2fb5 757 $file_id = $file->file_id();
0b503e8b 758
b9df6ad2
RD
759 // Filter the file and perform the restore.
760 $file = backup_migrate_filters_restore($file, $settings);
0b503e8b
RD
761 if (!$file) {
762 if (_backup_migrate_check_timeout()) {
b9df6ad2 763 backup_migrate_restore_fail('Could not perform the restore because the script timed out. Try increasing your PHP <a href="!url">max_execution_time setting</a>.', array('!url' => 'http://www.php.net/manual/en/info.configuration.php#ini.max-execution-time'), 'error');
6f203ddd 764 }
0b503e8b 765 else {
b9df6ad2 766 backup_migrate_restore_fail("Could not perform the restore.", array(), 'error');
0b503e8b
RD
767 }
768 backup_migrate_cleanup();
769 return FALSE;
369df44f 770 }
369df44f 771
b9df6ad2
RD
772 $time = timer_stop('backup_migrate_restore');
773 if ($file) {
774 $destination = backup_migrate_get_destination($destination_id);
775 $message = '%source restored from %dest file %file in !time ms. !action';
776 $params = array(
777 '%file' => $file->filename(),
778 '%source' => $settings->get_source_name(),
779 '%dest' => $destination->get_name(),
780 '!time' => $time['time'],
781 '!action' => !empty($settings->performed_action) ? $settings->performed_action : '',
782 );
0b503e8b 783 if ($destination && $destination->op('list files')) {
43fc2fb5 784 $params['!links'] = t('<a href="!restoreurl">Restore again</a>', array('!restoreurl' => url(BACKUP_MIGRATE_MENU_PATH . '/destination/restorefile/'. $destination_id ."/". $file_id)));
369df44f 785 }
b9df6ad2 786 backup_migrate_restore_succeed($message, $params, $settings);
369df44f 787 }
0b503e8b
RD
788 // Delete any temp files we've created.
789 backup_migrate_cleanup();
b9df6ad2
RD
790
791 // No errors. Return the file.
792 return $file;
369df44f
RD
793}
794
795/**
0b503e8b
RD
796 * Clean up when a backup operation fails.
797 */
798function backup_migrate_backup_fail($message, $params, $settings) {
799 backup_migrate_include('files', 'filters');
369df44f 800
0b503e8b
RD
801 _backup_migrate_message($message, $params, 'error');
802 backup_migrate_cleanup();
803 backup_migrate_filters_invoke_all('backup_fail', $settings, $message, $params);
804 return FALSE;
369df44f
RD
805}
806
807/**
0b503e8b 808 * Clean up when a backup operation suceeds.
369df44f 809 */
0b503e8b
RD
810function backup_migrate_backup_succeed($message, $params, $settings) {
811 backup_migrate_include('filters', 'files');
b9df6ad2 812 _backup_migrate_message($message, $params, 'success');
0b503e8b
RD
813 backup_migrate_cleanup();
814 backup_migrate_filters_invoke_all('backup_succeed', $settings, $message, $params);
815 return FALSE;
369df44f
RD
816}
817
b9df6ad2
RD
818/**
819 * Clean up when a restore operation fails.
820 */
821function backup_migrate_restore_fail($message, $params, $settings) {
822 backup_migrate_include('files', 'filters');
823 _backup_migrate_message($message, $params, 'error');
824 backup_migrate_cleanup();
825 backup_migrate_filters_invoke_all('restore_fail', $settings, $message, $params);
826 return FALSE;
827}
828
829/**
830 * Clean up when a restore operation suceeds.
831 */
832function backup_migrate_restore_succeed($message, $params, $settings) {
833 backup_migrate_include('filters', 'files');
834 _backup_migrate_message($message, $params, 'success');
835 backup_migrate_cleanup();
836 backup_migrate_filters_invoke_all('restore_succeed', $settings, $message, $params);
837 return FALSE;
838}
839
0b503e8b 840
369df44f 841/**
0b503e8b 842 * Cleanup after a success or failure.
369df44f 843 */
0b503e8b
RD
844function backup_migrate_cleanup() {
845 // Check that the cleanup function exists. If it doesn't then we probably didn't create any files to be cleaned up.
846 if (function_exists('_backup_migrate_temp_files_delete')) {
847 _backup_migrate_temp_files_delete();
369df44f 848 }
369df44f
RD
849}
850
0b503e8b 851
b9df6ad2
RD
852/**
853 * Shutdown callback. Called when the script terminates even if the script timed out.
854 */
855function backup_migrate_shutdown($settings) {
856 // If we ran out of time, set an error so the user knows what happened
857 if (_backup_migrate_check_timeout()) {
858 backup_migrate_cleanup();
859 backup_migrate_backup_fail('The operation timed out. Try increasing your PHP <a href="!url">max_execution_time setting</a>.', array('!url' => 'http://www.php.net/manual/en/info.configuration.php#ini.max-execution-time'), $settings);
860 // The session will have already been written and closed, so we need to write any changes directly.
f0a4737d 861 _drupal_session_write(session_id(), session_encode());
b9df6ad2 862 // Add a redirect or we'll just get whitescreened.
af6a0899 863 drupal_goto(BACKUP_MIGRATE_MENU_PATH);
b9df6ad2
RD
864 }
865}
866
867
868
0b503e8b
RD
869/* Actions/Workflow integration */
870
369df44f 871/**
0b503e8b 872 * Action to backup the drupal site. Requires actions.module.
0b503e8b
RD
873function action_backup_migrate_backup($op, $edit = array()) {
874 switch ($op) {
875 case 'do':
876 _backup_migrate_backup_with_defaults();
877 watchdog('action', 'Backed up database');
878 break;
369df44f 879
0b503e8b
RD
880 case 'metadata':
881 return array(
882 'description' => t('Backup the database with the default settings'),
883 'type' => t('Backup and Migrate'),
884 'batchable' => TRUE,
885 'configurable' => FALSE,
886 );
369df44f 887
0b503e8b 888 // Return an HTML config form for the action.
369df44f 889
0b503e8b
RD
890 case 'form':
891 return '';
892
893 // Validate the HTML form.
894
895 case 'validate':
896 return TRUE;
897
898 // Process the HTML form to store configuration.
899
900 case 'submit':
901 return '';
369df44f 902 }
369df44f 903}
b9df6ad2 904 */
369df44f 905
0b503e8b
RD
906/*
907 * Implementation of hook_action_info().
0b503e8b
RD
908function backup_migrate_action_info() {
909 return array(
910 'backup_migrate_action_backup' => array(
b9df6ad2
RD
911 'label' => t('Backup the database'),
912 'description' => t('Backup the database with the default settings.'),
0b503e8b
RD
913 ),
914 );
369df44f 915}
b9df6ad2 916 */
369df44f 917
0b503e8b
RD
918/*
919 * Action callback.
369df44f 920 */
0b503e8b
RD
921function backup_migrate_action_backup() {
922 _backup_migrate_backup_with_defaults();
369df44f
RD
923}
924
0b503e8b 925/* Utilities */
369df44f
RD
926
927/**
0b503e8b
RD
928 * Backup the database with the default settings.
929 */
930function _backup_migrate_backup_with_defaults($destination_id = "manual") {
b9df6ad2 931 backup_migrate_include('files', 'profiles');
0b503e8b
RD
932
933 $settings = _backup_migrate_profile_saved_default_profile();
934 $settings->destination_id = $destination_id;
b9df6ad2 935 $settings->source_id = 'db';
0b503e8b 936 backup_migrate_perform_backup($settings);
ff90901b
RD
937}
938
939/**
0b503e8b
RD
940 * Helper function to set a drupal message and watchdog message depending on whether the module is being run interactively.
941 */
942function _backup_migrate_message($message, $replace = array(), $type = 'status') {
b9df6ad2
RD
943 // Only set a message if there is a callback handler to handle the message.
944 if (($callback = _backup_migrate_message_callback()) && function_exists($callback)) {
945 $callback($message, $replace, $type);
ff90901b 946 }
369df44f 947
0b503e8b
RD
948 // Store the message in case it's needed (for the status notification filter for example).
949 _backup_migrate_messages($message, $replace, $type);
950}
ff90901b 951
369df44f 952/**
0b503e8b
RD
953 * Helper function to set a drupal message and watchdog message depending on whether the module is being run interactively.
954 */
955function _backup_migrate_messages($message = NULL, $replace = array(), $type = 'status') {
956 static $messages = array();
957 if ($message) {
958 $messages[] = array('message' => $message, 'replace' => $replace, 'type' => 'status');
959 }
960 return $messages;
369df44f
RD
961}
962
963/**
b9df6ad2 964 * Send a message to the browser. The normal type of message handling for interactive use.
369df44f 965 */
b9df6ad2
RD
966function _backup_migrate_message_browser($message, $replace, $type) {
967 // Log the message as well for admins.
968 _backup_migrate_message_log($message, $replace, $type);
969
970 // If there are links, we can display them in the browser.
971 if (!empty($replace['!links'])) {
972 $message .= " (!links)";
0b503e8b 973 }
b9df6ad2
RD
974 // Use drupal_set_message to display to the user.
975 drupal_set_message(t($message, $replace), str_replace('success', 'status', $type), FALSE);
976}
977
978/**
979 * Log message if we are in a non-interactive mode such as a cron run.
980 */
981function _backup_migrate_message_log($message, $replace, $type) {
982 // We only want to log the errors or successful completions.
983 if (in_array($type, array('error', 'success'))) {
984 watchdog('backup_migrate', $message, $replace, $type == 'error' ? WATCHDOG_ERROR : WATCHDOG_NOTICE);
985 }
986}
987
988/**
989 * Set or retrieve a message handler.
990 */
991function _backup_migrate_message_callback($callback = NULL) {
992 static $current_callback = '_backup_migrate_message_log';
993 if ($callback !== NULL) {
994 $current_callback = $callback;
995 }
996 return $current_callback;
369df44f
RD
997}
998
0b503e8b
RD
999function _backup_migrate_check_timeout() {
1000 static $timeout;
51b5a20e
RD
1001
1002 // Max execution of 0 means unlimited.
1003 if (ini_get('max_execution_time') == 0) {
1004 return false;
1005 }
0b503e8b
RD
1006 // Figure out when we should stop execution.
1007 if (!$timeout) {
1008 $timeout = (!empty($_SERVER['REQUEST_TIME']) ? $_SERVER['REQUEST_TIME'] : time()) + ini_get('max_execution_time') - variable_get('backup_migrate_timeout_buffer', 5);
1009 }
1010 return (time() > $timeout);
1011}
b9df6ad2 1012
921679f9
RD
1013/**
1014 * Convert an associated array to an ini format string.
1015 */
1016function _backup_migrate_array_to_ini($data, $prefix = '') {
1017 $content = "";
1018 foreach ($data as $key => $val) {
1019 if ($prefix) {
1020 $key = $prefix . '[' . $key .']';
1021 }
1022 if (is_array($val)) {
1023 $content .= _backup_migrate_array_to_ini($val, $key);
1024 }
1025 else {
1026 $content .= $key . " = \"". $val ."\"\n";
1027 }
1028 }
1029 return $content;
1030}
df85be61
RD
1031
1032/**
1033 * Execute a command line command. Returns false if the function failed.
1034 */
1035function backup_migrate_exec($command, $args = array()) {
1036 if (!function_exists('exec') || ini_get('safe_mode')) {
1037 return FALSE;
1038 }
1039
1040 // Escape the arguments
1041 foreach ($args as $key => $arg) {
1042 $args[$key] = escapeshellarg($arg);
1043 }
1044 $command = strtr($command, $args);
1045 $output = $result = NULL;
1046
1047 // Run the command.
1048 exec($command . ' 2>&1', $output, $result);
1049
1050 return $result == 0;
1051}
1052