More appropriate use of drupal_realpath
[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
50ae9afe
RD
233/**
234 * Implementation of hook_backup_migrate_destinations().
235 *
236 * Get the default NodeSquirrel destination.
237 */
238function backup_migrate_backup_migrate_schedules() {
239 $schedules = array();
240 if (variable_get('nodesquirrel_schedule', 60*60*24) && variable_get('nodesquirrel_secret_key', FALSE) != FALSE) {
241 $schedule = array(
242 'schedule_id' => 'nodesquirrel',
243 'name' => 'NodeSquirrel',
244 'source_id' => 'db',
245 'destination_id' => 'nodesquirrel',
246 'profile_id' => 'default',
247 'period' => variable_get('nodesquirrel_schedule', 60*60*24),
248 'enabled' => variable_get('nodesquirrel_secret_key', FALSE) != FALSE,
249 );
250 $schedules['nodesquirrel'] = backup_migrate_crud_create_item('schedule', $schedule);
251 }
252 return $schedules;
253}
254
255
0b503e8b
RD
256/* Menu Callbacks */
257
369df44f 258/**
0b503e8b 259 * A menu callback helper. Handles file includes and interactivity setting.
369df44f 260 */
0b503e8b
RD
261function backup_migrate_menu_callback($include, $function, $interactive = TRUE) {
262 if ($include) {
263 backup_migrate_include($include);
369df44f 264 }
b9df6ad2
RD
265 // Set the message handler based on interactivity setting.
266 _backup_migrate_message_callback($interactive ? '_backup_migrate_message_browser' : '_backup_migrate_message_log');
0b503e8b
RD
267 // Get the arguments with the first 3 removed.
268 $args = array_slice(func_get_args(), 3);
269 return call_user_func_array($function, $args);
369df44f
RD
270}
271
272/**
0b503e8b 273 * Include views .inc files as necessary.
369df44f 274 */
0b503e8b
RD
275function backup_migrate_include() {
276 static $used = array();
277 foreach (func_get_args() as $file) {
278 if (!isset($used[$file])) {
b9df6ad2 279 require_once DRUPAL_ROOT . '/' . drupal_get_path('module', 'backup_migrate') . "/includes/$file.inc";
0b503e8b
RD
280 }
281
282 $used[$file] = TRUE;
369df44f 283 }
369df44f
RD
284}
285
286
287/**
0b503e8b 288 * The menu callback for easy manual backups.
369df44f 289 */
0b503e8b 290function backup_migrate_ui_manual_backup_quick() {
b9df6ad2 291 $out = array();
0b503e8b 292 if (user_access('perform backup')) {
b9df6ad2 293 return drupal_get_form('backup_migrate_ui_manual_quick_backup_form');
0b503e8b
RD
294 }
295 else {
b9df6ad2 296 return t('You do not have permission to back up this site.');
0b503e8b
RD
297 }
298 return $out;
369df44f
RD
299}
300
0b503e8b
RD
301/**
302 * The menu callback for advanced manual backups.
303 */
304function backup_migrate_ui_manual_backup_advanced() {
305 backup_migrate_include('profiles');
b9df6ad2 306 $out = array();
43fc2fb5 307 $profile_id = arg(BACKUP_MIGRATE_MENU_DEPTH + 2);
0b503e8b 308 $profile = _backup_migrate_profile_saved_default_profile($profile_id);
b9df6ad2
RD
309
310 $out[] = drupal_get_form('backup_migrate_ui_manual_backup_load_profile_form', $profile);
311 $out[] = drupal_get_form('backup_migrate_ui_manual_backup_form', $profile);
0b503e8b 312 return $out;
369df44f
RD
313}
314
315/**
0b503e8b 316 * The backup/export load profile form.
369df44f 317 */
b9df6ad2 318function backup_migrate_ui_manual_backup_load_profile_form($form, &$form_state, $profile = NULL) {
369df44f 319 $form = array();
0b503e8b
RD
320 $profile_options = _backup_migrate_get_profile_form_item_options();
321 if (count($profile_options) > 0) {
322 $profile_options = array(0 => t('-- Select a Settings Profile --')) + $profile_options;
323 $form['profile'] = array(
324 "#title" => t("Settings Profile"),
325 "#collapsible" => TRUE,
326 "#collapsed" => FALSE,
327 "#prefix" => '<div class="container-inline">',
328 "#suffix" => '</div>',
329 "#tree" => FALSE,
330 "#description" => t("You can load a profile. Any changes you made below will be lost."),
331 );
332 $form['profile']['profile_id'] = array(
333 "#type" => "select",
334 "#title" => t("Load Settings"),
b9df6ad2 335 '#default_value' => is_object($profile) ? $profile->get_id() : 0,
0b503e8b
RD
336 "#options" => $profile_options,
337 );
338 $form['profile']['load_profile'] = array(
339 '#type' => 'submit',
340 '#value' => t('Load Profile'),
369df44f
RD
341 );
342 }
369df44f
RD
343 return $form;
344}
345
346/**
0b503e8b 347 * Submit the profile load form.
369df44f 348 */
0b503e8b
RD
349function backup_migrate_ui_manual_backup_load_profile_form_submit($form, &$form_state) {
350 if ($profile = backup_migrate_get_profile($form_state['values']['profile_id'])) {
b9df6ad2 351 variable_set("backup_migrate_profile_id", $profile->get_id());
af6a0899 352 $form_state['redirect'] = BACKUP_MIGRATE_MENU_PATH . '/export/advanced';
0b503e8b
RD
353 }
354 else {
355 variable_set("backup_migrate_profile_id", NULL);
369df44f 356 }
369df44f
RD
357}
358
359/**
0b503e8b 360 * The quick backup form.
ff90901b 361 */
b9df6ad2
RD
362function backup_migrate_ui_manual_quick_backup_form($form, &$form_state) {
363 backup_migrate_include('profiles', 'destinations');
0b503e8b 364 drupal_add_js(drupal_get_path('module', 'backup_migrate') .'/backup_migrate.js');
ff90901b 365
0b503e8b 366 $form = array();
ff90901b 367
0b503e8b
RD
368 $form['quickbackup'] = array(
369 '#type' => 'fieldset',
370 "#title" => t("Quick Backup"),
371 "#collapsible" => FALSE,
372 "#collapsed" => FALSE,
373 "#tree" => FALSE,
374 );
ff90901b 375
0b503e8b 376 $form['quickbackup']['source_id'] = _backup_migrate_get_source_pulldown(variable_get('backup_migrate_source_id', NULL));
ff90901b 377
50ae9afe
RD
378 // Pull the destination ID from the get param if it exists.
379 $destinations = _backup_migrate_get_destination_form_item_options('manual backup');
380 $destination_id = variable_get('backup_migrate_destination_id', 'download');
381 if (isset($_GET['destination_id']) && isset($destinations[$_GET['destination_id']])) {
382 $destination_id = $_GET['destination_id'];
383 }
0b503e8b
RD
384 $form['quickbackup']['destination_id'] = array(
385 "#type" => "select",
386 "#title" => t("Destination"),
50ae9afe
RD
387 "#options" => $destinations,
388 "#default_value" => $destination_id,
0b503e8b
RD
389 );
390 $profile_options = _backup_migrate_get_profile_form_item_options();
391 $form['quickbackup']['profile_id'] = array(
392 "#type" => "select",
393 "#title" => t("Settings Profile"),
394 '#default_value' => variable_get('backup_migrate_profile_id', NULL),
395 "#options" => $profile_options,
396 );
ff90901b 397
0b503e8b
RD
398 $form['quickbackup']['submit'] = array(
399 '#type' => 'submit',
400 '#value' => t('Backup now'),
401 '#weight' => 1,
ff90901b 402 );
0b503e8b
RD
403
404 $form['advanced'] = array(
405 '#type' => 'markup',
af6a0899 406 '#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
407 );
408
409
410 return $form;
ff90901b
RD
411}
412
0b503e8b 413/**
b9df6ad2
RD
414 * Validate the quick backup form.
415 */
416function backup_migrate_ui_manual_quick_backup_form_validate($form, &$form_state) {
417 if ($form_state['values']['source_id'] == $form_state['values']['destination_id']) {
418 form_set_error('destination_id', t('A source cannot be backed up to itself. Please pick a different destination for this backup.'));
419 }
420}
421
422/**
0b503e8b
RD
423 * Submit the quick backup form.
424 */
425function backup_migrate_ui_manual_quick_backup_form_submit($form, &$form_state) {
51b5a20e 426 backup_migrate_include('profiles', 'destinations');
0b503e8b
RD
427 if (user_access('perform backup')) {
428 // For a quick backup use the default settings.
429 $settings = _backup_migrate_profile_saved_default_profile($form_state['values']['profile_id']);
430
431 // Set the destination to the one chosen in the pulldown.
432 $settings->destination_id = $form_state['values']['destination_id'];
433 $settings->source_id = $form_state['values']['source_id'];
434
435 // Save the settings for next time.
436 variable_set("backup_migrate_source_id", $form_state['values']['source_id']);
437 variable_set("backup_migrate_destination_id", $form_state['values']['destination_id']);
438 variable_set("backup_migrate_profile_id", $form_state['values']['profile_id']);
439
440 // Do the backup.
441 backup_migrate_ui_manual_backup_perform($settings);
442 }
af6a0899 443 $form_state['redirect'] = BACKUP_MIGRATE_MENU_PATH;
ff90901b
RD
444}
445
446/**
0b503e8b 447 * Theme the quick backup form.
ff90901b 448 */
0b503e8b 449function theme_backup_migrate_ui_manual_quick_backup_form($form) {
b9df6ad2
RD
450 $form = $form['form'];
451
0b503e8b
RD
452 // Remove the titles so that the pulldowns can be displayed inline.
453 unset($form['quickbackup']['source_id']['#title']);
454 unset($form['quickbackup']['destination_id']['#title']);
455 unset($form['quickbackup']['profile_id']['#title']);
456
457 $replacements = array(
458 '!from' => drupal_render($form['quickbackup']['source_id']),
459 '!to' => drupal_render($form['quickbackup']['destination_id']),
460 '!profile' => drupal_render($form['quickbackup']['profile_id']),
461 '!submit' => drupal_render($form['quickbackup']['submit']),
462 );
463 $form['quickbackup']['markup'] = array(
464 '#type' => 'markup',
465 "#prefix" => '<div class="container-inline">',
466 "#suffix" => '</div>',
b9df6ad2 467 '#markup' => t('Backup from !from to !to using !profile !submit', $replacements),
ff90901b 468 );
0b503e8b
RD
469 unset($form['quickbackup']['source_id']);
470 unset($form['quickbackup']['destination_id']);
471 unset($form['quickbackup']['profile_id']);
472 unset($form['quickbackup']['submit']);
b9df6ad2
RD
473
474 return drupal_render_children($form);
ff90901b
RD
475}
476
477/**
0b503e8b 478 * The backup/export form.
369df44f 479 */
b9df6ad2
RD
480function backup_migrate_ui_manual_backup_form($form, &$form_state, $profile) {
481 drupal_add_js(drupal_get_path('module', 'backup_migrate') .'/backup_migrate.js', array('type' => 'module', 'scope' => 'footer'));
369df44f 482
0b503e8b 483 $form = array();
51b5a20e 484
b9df6ad2 485 $form += _backup_migrate_get_source_form('db');
0b503e8b 486 $form += _backup_migrate_ui_backup_settings_form($profile);
b9df6ad2 487
0b503e8b
RD
488 $form['profile_id'] = array(
489 "#type" => "value",
490 '#default_value' => $profile->get_id(),
491 );
492 $form['storage'] = array(
493 "#type" => "value",
494 '#default_value' => $profile->storage,
495 );
496 $form['destination'] = array(
497 "#type" => "fieldset",
498 "#title" => t("Backup Destination"),
499 "#collapsible" => TRUE,
500 "#collapsed" => FALSE,
501 "#tree" => FALSE,
502 "#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 503 '#weight' => 70,
0b503e8b
RD
504 );
505 $form['destination']['destination_id'] = array(
506 "#type" => "select",
507 "#title" => t("Destination"),
508 "#options" => _backup_migrate_get_destination_form_item_options('manual backup'),
509 "#default_value" => variable_get("backup_migrate_destination_id", "download"),
510 );
511 if (user_access('administer backup and migrate')) {
af6a0899 512 $form['destination']['destination_id']['#description'] = l(t("Create new destination"), BACKUP_MIGRATE_MENU_PATH . "/destination/add");
369df44f 513 }
369df44f 514
0b503e8b
RD
515 if (user_access('administer backup and migrate')) {
516 $form['save_settings'] = array(
517 "#type" => "checkbox",
518 "#title" => t('Save these settings.'),
519 "#default_value" => FALSE,
b9df6ad2 520 '#weight' => 80,
0b503e8b
RD
521 );
522 $form['save_options'] = array(
523 '#prefix' => '<div class="backup-migrate-save-options">',
524 '#suffix' => '</div>',
b9df6ad2 525 '#weight' => 90,
0b503e8b
RD
526 );
527 $name = array(
528 '#default_value' => $profile->get('name'),
529 '#type' => 'textfield',
530 '#title' => t('Save the settings as'),
531 );
369df44f 532
0b503e8b
RD
533 if ($profile->get_id()) {
534 $form['save_options']['create_new'] = array(
535 '#default_value' => $profile->get('name'),
536 '#type' => 'radios',
537 '#default_value' => 0,
538 '#options' => array(
539 0 => t("Replace the '%profile' profile", array('%profile' => $profile->get('name'))),
540 1 => t('Create new profile'),
541 ),
542 );
369df44f 543
0b503e8b
RD
544 $name["#title"] = t('Profile name');
545 $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 546 }
0b503e8b
RD
547 else {
548 $name["#title"] = t('Save the settings as');
af6a0899 549 $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
550 $name["#default_value"] = t('Untitled Profile');
551 }
552 $form['save_options']['name'] = $name;
553 $form['save_options'][] = array(
554 '#type' => 'submit',
555 '#value' => t('Save Without Backing Up'),
556 );
369df44f 557 }
51b5a20e
RD
558 $form['#validate'][] = 'backup_migrate_ui_manual_quick_backup_form_validate';
559 $form['#submit'][] = 'backup_migrate_ui_manual_backup_form_submit';
0b503e8b
RD
560
561 $form[] = array(
562 '#type' => 'submit',
b9df6ad2
RD
563 '#value' => t('Backup now'),
564 '#weight' => 100,
0b503e8b
RD
565 );
566 return $form;
369df44f
RD
567}
568
b9df6ad2 569
369df44f 570/**
0b503e8b 571 * Submit the form. Save the values as defaults if desired and output the backup file.
369df44f 572 */
0b503e8b
RD
573function backup_migrate_ui_manual_backup_form_submit($form, &$form_state) {
574 // Save the settings profile if the save box is checked.
b9df6ad2
RD
575// $form_state['values']['nodata_tables'] = array_filter((array)$form_state['values']['nodata_tables']);
576// $form_state['values']['exclude_tables'] = array_filter((array)$form_state['values']['exclude_tables']);
577
578 $profile = backup_migrate_crud_create_item('profile', $form_state['values']);
579
51b5a20e 580 // Save the settings profile if the save box is checked.
0b503e8b
RD
581 if ($form_state['values']['save_settings'] && user_access('administer backup and migrate')) {
582 if (@$form_state['values']['create_new']) {
51b5a20e 583 // Reset the id and storage so a new item will be saved.
b9df6ad2 584 $profile->set_id(NULL);
51b5a20e 585 $profile->storage = BACKUP_MIGRATE_STORAGE_NONE;
369df44f 586 }
0b503e8b
RD
587 $profile->save();
588 variable_set("backup_migrate_profile_id", $profile->get_id());
589 variable_set("backup_migrate_destination_id", $form_state['values']['destination_id']);
369df44f 590 }
0b503e8b
RD
591
592 // Perform the actual backup if that is what was selected.
b9df6ad2
RD
593 if ($form_state['values']['op'] == t('Backup now') && user_access('perform backup')) {
594 backup_migrate_ui_manual_backup_perform($profile);
369df44f 595 }
af6a0899 596 $form_state['redirect'] = BACKUP_MIGRATE_MENU_PATH . "/export/advanced";
369df44f
RD
597}
598
599/**
0b503e8b 600 * Perform an actual manual backup and tell the user of the progress.
369df44f 601 */
0b503e8b
RD
602
603function backup_migrate_ui_manual_backup_perform($settings) {
0b503e8b 604 // Peform the actual backup.
b9df6ad2 605 backup_migrate_perform_backup($settings);
369df44f
RD
606}
607
608/**
0b503e8b 609 * The restore/import upload page.
369df44f 610 */
0b503e8b
RD
611function backup_migrate_ui_manual_restore() {
612 return drupal_get_form('backup_migrate_ui_manual_restore_form');
369df44f
RD
613}
614
369df44f 615/**
0b503e8b 616 * The restore/import upload form.
369df44f 617 */
0b503e8b
RD
618function backup_migrate_ui_manual_restore_form() {
619 backup_migrate_include('filters', 'destinations');
369df44f 620
0b503e8b 621 $form = array();
369df44f 622
0b503e8b
RD
623 $sources = _backup_migrate_get_destination_form_item_options('source');
624 if (count($sources) > 1) {
625 $form['source_id'] = array(
626 "#type" => "select",
627 "#title" => t("Restore to"),
628 "#options" => _backup_migrate_get_destination_form_item_options('source'),
629 "#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 630 "#default_value" => 'db',
0b503e8b
RD
631 );
632 }
633 else {
634 $form['source_id'] = array(
635 "#type" => "value",
b9df6ad2 636 "#value" => 'db',
0b503e8b
RD
637 );
638 }
639
640 $form['backup_migrate_restore_upload'] = array(
641 '#title' => t('Upload a Backup File'),
642 '#type' => 'file',
643 '#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()))),
644 );
b9df6ad2 645 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
646
647 $form = array_merge_recursive($form, backup_migrate_filters_settings_form(backup_migrate_filters_settings_default('restore'), 'restore'));
648 // Add the advanced fieldset if there are any fields in it.
649 if (@$form['advanced']) {
650 $form['advanced']['#type'] = 'fieldset';
651 $form['advanced']['#title'] = t('Advanced Options');
652 $form['advanced']['#collapsed'] = true;
653 $form['advanced']['#collapsible'] = true;
654 }
655
656 $form['submit'] = array(
657 '#type' => 'submit',
b9df6ad2 658 '#value' => t('Restore now'),
0b503e8b
RD
659 );
660 if (user_access('access backup files')) {
661 $form[] = array(
662 '#type' => 'markup',
af6a0899 663 '#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
664 );
665 }
666 $form['#attributes'] = array('enctype' => 'multipart/form-data');
667 return $form;
369df44f
RD
668}
669
670/**
0b503e8b 671 * The restore submit. Do the restore.
369df44f 672 */
0b503e8b 673function backup_migrate_ui_manual_restore_form_submit($form, &$form_state) {
14f4d209 674 $validators = array('file_validate_extensions' => array('gz zip sql mysql bz bz2 aes'));
f0a4737d 675 if ($file = file_save_upload('backup_migrate_restore_upload', $validators)) {
0b503e8b 676 backup_migrate_include('destinations');
b9df6ad2 677 backup_migrate_perform_restore('upload', $file->uri, $form_state['values']);
0b503e8b 678 }
af6a0899 679 $form_state['redirect'] = BACKUP_MIGRATE_MENU_PATH . '/restore';
369df44f
RD
680}
681
682/**
0b503e8b
RD
683 * Convert an item to an 'exportable'.
684 */
b9df6ad2 685function backup_migrate_ui_export_form($form, &$form_state, $item) {
0b503e8b
RD
686 if ($item && function_exists('ctools_var_export')) {
687 $code = ctools_var_export($item);
688 $form = ctools_export_form($form_state, $code);
689 return $form;
369df44f 690 }
0b503e8b 691 return array();
369df44f
RD
692}
693
694/**
0b503e8b
RD
695 * Perform a backup with the given settings.
696 */
697function backup_migrate_perform_backup(&$settings) {
b9df6ad2
RD
698 backup_migrate_include('destinations', 'files', 'filters');
699 timer_start('backup_migrate_backup');
0b503e8b
RD
700
701 // If not in 'safe mode', increase the maximum execution time:
171be96c 702 if (!ini_get('safe_mode') && strpos(ini_get('disable_functions'), 'set_time_limit') === FALSE && ini_get('max_execution_time') != 0 && ini_get('max_execution_time') < variable_get('backup_migrate_backup_max_time', 1200)) {
0b503e8b 703 set_time_limit(variable_get('backup_migrate_backup_max_time', 1200));
369df44f 704 }
369df44f 705
67158da5 706 $timestamp = '';
0b503e8b 707 if ($settings->append_timestamp && $settings->timestamp_format) {
67158da5 708 $timestamp = format_date(time(), 'custom', $settings->timestamp_format);
0b503e8b 709 }
67158da5 710 $filename = _backup_migrate_construct_filename($settings->filename, $timestamp);
369df44f 711
a3f879bd 712 $file = new backup_file(array('filename' => $filename));
0b503e8b
RD
713 if (!$file) {
714 backup_migrate_backup_fail("Could not run backup because a temporary file could not be created.", array(), $settings);
715 return FALSE;
369df44f 716 }
0b503e8b 717
b9df6ad2
RD
718 // Register shutdown callback to deal with timeouts.
719 register_shutdown_function('backup_migrate_shutdown', $settings);
720
721 $file = backup_migrate_filters_backup($file, $settings);
0b503e8b
RD
722 if (!$file) {
723 if (_backup_migrate_check_timeout()) {
b9df6ad2 724 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 725 }
369df44f 726 else {
b9df6ad2 727 backup_migrate_backup_fail("Could not complete the backup.", array(), $settings);
369df44f 728 }
0b503e8b 729 return FALSE;
369df44f 730 }
369df44f 731
0b503e8b
RD
732 $file = backup_migrate_destination_save_file($file, $settings);
733 if (!$file) {
734 backup_migrate_backup_fail("Could not run backup because the file could not be saved to the destination.", array(), $settings);
735 return FALSE;
736 }
369df44f 737
b9df6ad2
RD
738 // Backup succeeded,
739 $time = timer_stop('backup_migrate_backup');
740 $message = '%source backed up successfully to %file in destination %dest in !time ms. !action';
741 $params = array(
a3f879bd 742 '%file' => $filename,
b9df6ad2
RD
743 '%dest' => $settings->get_destination_name(),
744 '%source' => $settings->get_source_name(),
745 '!time' => $time['time'],
746 '!action' => !empty($settings->performed_action) ? $settings->performed_action : '',
747 );
748 if (($destination = $settings->get_destination()) && ($links = $destination->get_file_links($file->file_id()))) {
749 $params['!links'] = implode(", ", $links);
750 }
751
752 backup_migrate_backup_succeed($message, $params, $settings);
0b503e8b 753 return $file;
6f203ddd
RD
754}
755
756/**
0b503e8b 757 * Restore from a file in the given destination.
6f203ddd 758 */
b9df6ad2 759function backup_migrate_perform_restore($destination_id, $file, $settings = array()) {
0b503e8b 760 backup_migrate_include('files', 'filters');
b9df6ad2 761 timer_start('backup_migrate_restore');
0b503e8b
RD
762
763 // If not in 'safe mode', increase the maximum execution time:
171be96c 764 if (!ini_get('safe_mode') && strpos(ini_get('disable_functions'), 'set_time_limit') === FALSE && ini_get('max_execution_time') != 0 && ini_get('max_execution_time') < variable_get('backup_migrate_restore_max_time', 1200)) {
b9df6ad2 765 set_time_limit(variable_get('backup_migrate_restore_max_time', 1200));
6f203ddd 766 }
369df44f 767
b9df6ad2
RD
768 // Make the settings into a default profile.
769 if (!is_object($settings)) {
770 $settings = backup_migrate_crud_create_item('profile', $settings);
771 $settings->source_id = empty($settings->source_id) ? 'db' : $settings->source_id;
369df44f 772 }
369df44f 773
b9df6ad2
RD
774 // Register shutdown callback.
775 register_shutdown_function('backup_migrate_shutdown', $settings);
776
777 if (!is_object($file)) {
778 // Load the file from the destination.
779 $file = backup_migrate_destination_get_file($destination_id, $file);
780 if (!$file) {
781 _backup_migrate_message("Could not restore because the file could not be loaded from the destination.", array(), 'error');
782 backup_migrate_cleanup();
783 return FALSE;
784 }
0b503e8b 785 }
43fc2fb5 786 $file_id = $file->file_id();
0b503e8b 787
b9df6ad2
RD
788 // Filter the file and perform the restore.
789 $file = backup_migrate_filters_restore($file, $settings);
0b503e8b
RD
790 if (!$file) {
791 if (_backup_migrate_check_timeout()) {
b9df6ad2 792 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 793 }
0b503e8b 794 else {
b9df6ad2 795 backup_migrate_restore_fail("Could not perform the restore.", array(), 'error');
0b503e8b
RD
796 }
797 backup_migrate_cleanup();
798 return FALSE;
369df44f 799 }
369df44f 800
b9df6ad2
RD
801 $time = timer_stop('backup_migrate_restore');
802 if ($file) {
803 $destination = backup_migrate_get_destination($destination_id);
804 $message = '%source restored from %dest file %file in !time ms. !action';
805 $params = array(
806 '%file' => $file->filename(),
807 '%source' => $settings->get_source_name(),
808 '%dest' => $destination->get_name(),
809 '!time' => $time['time'],
810 '!action' => !empty($settings->performed_action) ? $settings->performed_action : '',
811 );
0b503e8b 812 if ($destination && $destination->op('list files')) {
43fc2fb5 813 $params['!links'] = t('<a href="!restoreurl">Restore again</a>', array('!restoreurl' => url(BACKUP_MIGRATE_MENU_PATH . '/destination/restorefile/'. $destination_id ."/". $file_id)));
369df44f 814 }
b9df6ad2 815 backup_migrate_restore_succeed($message, $params, $settings);
369df44f 816 }
0b503e8b
RD
817 // Delete any temp files we've created.
818 backup_migrate_cleanup();
b9df6ad2
RD
819
820 // No errors. Return the file.
821 return $file;
369df44f
RD
822}
823
824/**
0b503e8b
RD
825 * Clean up when a backup operation fails.
826 */
827function backup_migrate_backup_fail($message, $params, $settings) {
828 backup_migrate_include('files', 'filters');
369df44f 829
0b503e8b
RD
830 _backup_migrate_message($message, $params, 'error');
831 backup_migrate_cleanup();
832 backup_migrate_filters_invoke_all('backup_fail', $settings, $message, $params);
833 return FALSE;
369df44f
RD
834}
835
836/**
0b503e8b 837 * Clean up when a backup operation suceeds.
369df44f 838 */
0b503e8b
RD
839function backup_migrate_backup_succeed($message, $params, $settings) {
840 backup_migrate_include('filters', 'files');
b9df6ad2 841 _backup_migrate_message($message, $params, 'success');
0b503e8b
RD
842 backup_migrate_cleanup();
843 backup_migrate_filters_invoke_all('backup_succeed', $settings, $message, $params);
844 return FALSE;
369df44f
RD
845}
846
b9df6ad2
RD
847/**
848 * Clean up when a restore operation fails.
849 */
850function backup_migrate_restore_fail($message, $params, $settings) {
851 backup_migrate_include('files', 'filters');
852 _backup_migrate_message($message, $params, 'error');
853 backup_migrate_cleanup();
854 backup_migrate_filters_invoke_all('restore_fail', $settings, $message, $params);
855 return FALSE;
856}
857
858/**
859 * Clean up when a restore operation suceeds.
860 */
861function backup_migrate_restore_succeed($message, $params, $settings) {
862 backup_migrate_include('filters', 'files');
863 _backup_migrate_message($message, $params, 'success');
864 backup_migrate_cleanup();
865 backup_migrate_filters_invoke_all('restore_succeed', $settings, $message, $params);
866 return FALSE;
867}
868
0b503e8b 869
369df44f 870/**
0b503e8b 871 * Cleanup after a success or failure.
369df44f 872 */
0b503e8b
RD
873function backup_migrate_cleanup() {
874 // Check that the cleanup function exists. If it doesn't then we probably didn't create any files to be cleaned up.
875 if (function_exists('_backup_migrate_temp_files_delete')) {
876 _backup_migrate_temp_files_delete();
369df44f 877 }
369df44f
RD
878}
879
0b503e8b 880
b9df6ad2
RD
881/**
882 * Shutdown callback. Called when the script terminates even if the script timed out.
883 */
884function backup_migrate_shutdown($settings) {
885 // If we ran out of time, set an error so the user knows what happened
886 if (_backup_migrate_check_timeout()) {
887 backup_migrate_cleanup();
888 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);
889 // The session will have already been written and closed, so we need to write any changes directly.
f0a4737d 890 _drupal_session_write(session_id(), session_encode());
b9df6ad2 891 // Add a redirect or we'll just get whitescreened.
af6a0899 892 drupal_goto(BACKUP_MIGRATE_MENU_PATH);
b9df6ad2
RD
893 }
894}
895
896
897
0b503e8b
RD
898/* Actions/Workflow integration */
899
369df44f 900/**
0b503e8b 901 * Action to backup the drupal site. Requires actions.module.
0b503e8b
RD
902function action_backup_migrate_backup($op, $edit = array()) {
903 switch ($op) {
904 case 'do':
905 _backup_migrate_backup_with_defaults();
906 watchdog('action', 'Backed up database');
907 break;
369df44f 908
0b503e8b
RD
909 case 'metadata':
910 return array(
911 'description' => t('Backup the database with the default settings'),
912 'type' => t('Backup and Migrate'),
913 'batchable' => TRUE,
914 'configurable' => FALSE,
915 );
369df44f 916
0b503e8b 917 // Return an HTML config form for the action.
369df44f 918
0b503e8b
RD
919 case 'form':
920 return '';
921
922 // Validate the HTML form.
923
924 case 'validate':
925 return TRUE;
926
927 // Process the HTML form to store configuration.
928
929 case 'submit':
930 return '';
369df44f 931 }
369df44f 932}
b9df6ad2 933 */
369df44f 934
0b503e8b
RD
935/*
936 * Implementation of hook_action_info().
0b503e8b
RD
937function backup_migrate_action_info() {
938 return array(
939 'backup_migrate_action_backup' => array(
b9df6ad2
RD
940 'label' => t('Backup the database'),
941 'description' => t('Backup the database with the default settings.'),
0b503e8b
RD
942 ),
943 );
369df44f 944}
b9df6ad2 945 */
369df44f 946
0b503e8b
RD
947/*
948 * Action callback.
369df44f 949 */
0b503e8b
RD
950function backup_migrate_action_backup() {
951 _backup_migrate_backup_with_defaults();
369df44f
RD
952}
953
0b503e8b 954/* Utilities */
369df44f
RD
955
956/**
0b503e8b
RD
957 * Backup the database with the default settings.
958 */
959function _backup_migrate_backup_with_defaults($destination_id = "manual") {
b9df6ad2 960 backup_migrate_include('files', 'profiles');
0b503e8b
RD
961
962 $settings = _backup_migrate_profile_saved_default_profile();
963 $settings->destination_id = $destination_id;
b9df6ad2 964 $settings->source_id = 'db';
0b503e8b 965 backup_migrate_perform_backup($settings);
ff90901b
RD
966}
967
968/**
0b503e8b
RD
969 * Helper function to set a drupal message and watchdog message depending on whether the module is being run interactively.
970 */
971function _backup_migrate_message($message, $replace = array(), $type = 'status') {
b9df6ad2
RD
972 // Only set a message if there is a callback handler to handle the message.
973 if (($callback = _backup_migrate_message_callback()) && function_exists($callback)) {
974 $callback($message, $replace, $type);
ff90901b 975 }
369df44f 976
0b503e8b
RD
977 // Store the message in case it's needed (for the status notification filter for example).
978 _backup_migrate_messages($message, $replace, $type);
979}
ff90901b 980
369df44f 981/**
0b503e8b
RD
982 * Helper function to set a drupal message and watchdog message depending on whether the module is being run interactively.
983 */
984function _backup_migrate_messages($message = NULL, $replace = array(), $type = 'status') {
985 static $messages = array();
986 if ($message) {
987 $messages[] = array('message' => $message, 'replace' => $replace, 'type' => 'status');
988 }
989 return $messages;
369df44f
RD
990}
991
992/**
b9df6ad2 993 * Send a message to the browser. The normal type of message handling for interactive use.
369df44f 994 */
b9df6ad2
RD
995function _backup_migrate_message_browser($message, $replace, $type) {
996 // Log the message as well for admins.
997 _backup_migrate_message_log($message, $replace, $type);
998
999 // If there are links, we can display them in the browser.
1000 if (!empty($replace['!links'])) {
1001 $message .= " (!links)";
0b503e8b 1002 }
b9df6ad2
RD
1003 // Use drupal_set_message to display to the user.
1004 drupal_set_message(t($message, $replace), str_replace('success', 'status', $type), FALSE);
1005}
1006
1007/**
1008 * Log message if we are in a non-interactive mode such as a cron run.
1009 */
1010function _backup_migrate_message_log($message, $replace, $type) {
1011 // We only want to log the errors or successful completions.
1012 if (in_array($type, array('error', 'success'))) {
1013 watchdog('backup_migrate', $message, $replace, $type == 'error' ? WATCHDOG_ERROR : WATCHDOG_NOTICE);
1014 }
1015}
1016
1017/**
1018 * Set or retrieve a message handler.
1019 */
1020function _backup_migrate_message_callback($callback = NULL) {
1021 static $current_callback = '_backup_migrate_message_log';
1022 if ($callback !== NULL) {
1023 $current_callback = $callback;
1024 }
1025 return $current_callback;
369df44f
RD
1026}
1027
0b503e8b
RD
1028function _backup_migrate_check_timeout() {
1029 static $timeout;
51b5a20e
RD
1030
1031 // Max execution of 0 means unlimited.
1032 if (ini_get('max_execution_time') == 0) {
1033 return false;
1034 }
0b503e8b
RD
1035 // Figure out when we should stop execution.
1036 if (!$timeout) {
1037 $timeout = (!empty($_SERVER['REQUEST_TIME']) ? $_SERVER['REQUEST_TIME'] : time()) + ini_get('max_execution_time') - variable_get('backup_migrate_timeout_buffer', 5);
1038 }
1039 return (time() > $timeout);
1040}
b9df6ad2 1041
921679f9
RD
1042/**
1043 * Convert an associated array to an ini format string.
1044 */
1045function _backup_migrate_array_to_ini($data, $prefix = '') {
1046 $content = "";
1047 foreach ($data as $key => $val) {
1048 if ($prefix) {
1049 $key = $prefix . '[' . $key .']';
1050 }
1051 if (is_array($val)) {
1052 $content .= _backup_migrate_array_to_ini($val, $key);
1053 }
1054 else {
1055 $content .= $key . " = \"". $val ."\"\n";
1056 }
1057 }
1058 return $content;
1059}
df85be61
RD
1060
1061/**
1062 * Execute a command line command. Returns false if the function failed.
1063 */
1064function backup_migrate_exec($command, $args = array()) {
1065 if (!function_exists('exec') || ini_get('safe_mode')) {
1066 return FALSE;
1067 }
1068
1069 // Escape the arguments
1070 foreach ($args as $key => $arg) {
1071 $args[$key] = escapeshellarg($arg);
1072 }
1073 $command = strtr($command, $args);
1074 $output = $result = NULL;
1075
1076 // Run the command.
1077 exec($command . ' 2>&1', $output, $result);
1078
1079 return $result == 0;
1080}
1081
aebba76e
RD
1082/**
1083 * Implements hook_mail().
1084 */
1085function backup_migrate_mail($key, &$message, $params) {
1086
1087 switch($key) {
1088 case 'backup_succeed':
1089 $message['subject'] = t('!site backup succeeded', array('!site' => variable_get('site_name', 'Drupal')));
1090 if ($params['messages']) {
1091 $message['body'][] = t("The site backup has completed successfully with the following messages:");
1092 $message['body'][] = t("!messages", array('!messages' => $params['messages']));
1093 }
1094 else {
1095 $message['body'][] = t("The site backup has completed successfully.");
1096 }
1097 break;
1098 case 'backup_fail':
1099 $message['subject'] = t('!site backup failed', array('!site' => variable_get('site_name', 'Drupal')));
1100 if ($params['messages']) {
1101 $message['body'][] = t("The site backup has failed with the following messages:");
1102 $message['body'][] = t("!messages", array('!messages' => $params['messages']));
1103 }
1104 else {
1105 $message['body'][] = t("The site backup has failed for an unknown reason.");
1106 }
1107 break;
1108 }
14f4d209 1109}