PluginProbe
UpdraftPlus: WP Backup & Migration Plugin / 1.13.6
UpdraftPlus: WP Backup & Migration Plugin v1.13.6
1.26.7 1.26.6 1.26.5 1.26.4 1.26.3 1.9.19 1.9.25 1.9.26 1.9.30 1.9.31 1.9.32 1.9.4 1.9.40 1.9.41 1.9.42 1.9.43 1.9.44 1.9.45 1.9.46 1.9.5 1.9.50 1.9.51 1.9.60 1.9.62 1.9.63 All 371 releases
updraftplus / admin.php

admin.php in UpdraftPlus: WP Backup & Migration Plugin 1.13.6, at admin.php

4,450 lines 200.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 if (!defined ('UPDRAFTPLUS_DIR')) die('No direct access allowed');
4
5 // Admin-area code lives here. This gets called in admin_menu, earlier than admin_init
6
7 global $updraftplus_admin;
8 if (!is_a($updraftplus_admin, 'UpdraftPlus_Admin')) $updraftplus_admin = new UpdraftPlus_Admin();
9
10 class UpdraftPlus_Admin {
11
12 public $logged = array();
13
14 private $template_directories;
15
16 public function __construct() {
17 $this->admin_init();
18 }
19
20 private function wp_normalize_path($path) {
21 // wp_normalize_path is not present before WP 3.9
22 if (function_exists('wp_normalize_path')) return wp_normalize_path($path);
23 // Taken from WP 4.6
24 $path = str_replace('\\', '/', $path);
25 $path = preg_replace('|(?<=.)/+|', '/', $path);
26 if (':' === substr($path, 1, 1)) {
27 $path = ucfirst($path);
28 }
29 return $path;
30 }
31
32 public function get_templates_dir() {
33 return apply_filters('updraftplus_templates_dir', $this->wp_normalize_path(UPDRAFTPLUS_DIR.'/templates'));
34 }
35
36 public function get_templates_url() {
37 return apply_filters('updraftplus_templates_url', UPDRAFTPLUS_DIR.'/templates');
38 }
39
40 private function register_template_directories() {
41
42 $template_directories = array();
43
44 $templates_dir = $this->get_templates_dir();
45
46 if ($dh = opendir($templates_dir)) {
47 while (($file = readdir($dh)) !== false) {
48 if ('.' == $file || '..' == $file) continue;
49 if (is_dir($templates_dir.'/'.$file)) {
50 $template_directories[$file] = $templates_dir.'/'.$file;
51 }
52 }
53 closedir($dh);
54 }
55
56 // This is the optimal hook for most extensions to hook into
57 $this->template_directories = apply_filters('updraftplus_template_directories', $template_directories);
58
59 }
60
61 public function include_template($path, $return_instead_of_echo = false, $extract_these = array()) {
62 if ($return_instead_of_echo) ob_start();
63
64 if (preg_match('#^([^/]+)/(.*)$#', $path, $matches)) {
65 $prefix = $matches[1];
66 $suffix = $matches[2];
67 if (isset($this->template_directories[$prefix])) {
68 $template_file = $this->template_directories[$prefix].'/'.$suffix;
69 }
70 }
71
72 if (!isset($template_file)) {
73 $template_file = UPDRAFTPLUS_DIR.'/templates/'.$path;
74 }
75
76 $template_file = apply_filters('updraftplus_template', $template_file, $path);
77
78 do_action('updraftplus_before_template', $path, $template_file, $return_instead_of_echo, $extract_these);
79
80 if (!file_exists($template_file)) {
81 error_log("UpdraftPlus: template not found: $template_file");
82 echo __('Error:', 'updraftplus').' '.__('template not found', 'updraftplus')." ($path)";
83 } else {
84 extract($extract_these);
85 global $updraftplus, $wpdb;
86 $updraftplus_admin = $this;
87 include $template_file;
88 }
89
90 do_action('updraftplus_after_template', $path, $template_file, $return_instead_of_echo, $extract_these);
91
92 if ($return_instead_of_echo) return ob_get_clean();
93 }
94
95 /**
96 * Add actions for any needed dashboard notices for remote storage services
97 *
98 * @param String|Array $services - a list of services, or single service
99 */
100 private function setup_all_admin_notices_global($services) {
101
102 global $updraftplus;
103
104 if ('googledrive' === $services || (is_array($services) && in_array('googledrive', $services))) {
105 $settings = $updraftplus->update_remote_storage_options_format('googledrive');
106
107 if (is_wp_error($settings)) {
108 if (!isset($this->storage_module_option_errors)) $this->storage_module_option_errors = '';
109 $this->storage_module_option_errors .= "Google Drive (".$settings->get_error_code()."): ".$settings->get_error_message();
110 add_action('all_admin_notices', array($this, 'show_admin_warning_multiple_storage_options'));
111 $updraftplus->log_wp_error($settings, true, true);
112 } elseif (!empty($settings['settings'])) {
113 foreach ($settings['settings'] as $instance_id => $storage_options) {
114 if ((defined('UPDRAFTPLUS_CUSTOM_GOOGLEDRIVE_APP') && UPDRAFTPLUS_CUSTOM_GOOGLEDRIVE_APP) || !empty($storage_options['clientid'])) {
115 if (!empty($storage_options['clientid'])) {
116 $clientid = $storage_options['clientid'];
117 $token = empty($storage_options['token']) ? '' : $storage_options['token'];
118 }
119 if (!empty($clientid) && '' == $token) add_action('all_admin_notices', array($this, 'show_admin_warning_googledrive'));
120 unset($clientid);
121 unset($token);
122 } else {
123 if (empty($storage_options['user_id'])) add_action('all_admin_notices', array($this, 'show_admin_warning_googledrive'));
124 }
125 }
126 }
127 }
128 if ('googlecloud' === $services || (is_array($services) && in_array('googlecloud', $services))) {
129 $settings = $updraftplus->update_remote_storage_options_format('googlecloud');
130
131 if (is_wp_error($settings)) {
132 if (!isset($this->storage_module_option_errors)) $this->storage_module_option_errors = '';
133 $this->storage_module_option_errors .= "Google Cloud (".$settings->get_error_code()."): ".$settings->get_error_message();
134 add_action('all_admin_notices', array($this, 'show_admin_warning_multiple_storage_options'));
135 $updraftplus->log_wp_error($settings, true, true);
136 } elseif (!empty($settings['settings'])) {
137 foreach ($settings['settings'] as $instance_id => $storage_options) {
138 $clientid = $storage_options['clientid'];
139 $token = (empty($storage_options['token'])) ? '' : $storage_options['token'];
140
141 if (!empty($clientid) && empty($token)) add_action('all_admin_notices', array($this,'show_admin_warning_googlecloud'));
142 }
143 }
144 }
145
146 if ('dropbox' === $services || (is_array($services) && in_array('dropbox', $services))) {
147 $settings = $updraftplus->update_remote_storage_options_format('dropbox');
148
149 if (is_wp_error($settings)) {
150 if (!isset($this->storage_module_option_errors)) $this->storage_module_option_errors = '';
151 $this->storage_module_option_errors .= "Dropbox (".$settings->get_error_code()."): ".$settings->get_error_message();
152 add_action('all_admin_notices', array($this, 'show_admin_warning_multiple_storage_options'));
153 $updraftplus->log_wp_error($settings, true, true);
154 } elseif (!empty($settings['settings'])) {
155 foreach ($settings['settings'] as $instance_id => $storage_options) {
156 if (empty($storage_options['tk_access_token'])) {
157 add_action('all_admin_notices', array($this, 'show_admin_warning_dropbox'));
158 }
159 }
160 }
161 }
162
163 if ('onedrive' === $services || (is_array($services) && in_array('onedrive', $services))) {
164 $settings = $updraftplus->update_remote_storage_options_format('onedrive');
165
166 if (is_wp_error($settings)) {
167 if (!isset($this->storage_module_option_errors)) $this->storage_module_option_errors = '';
168 $this->storage_module_option_errors .= "OneDrive (".$settings->get_error_code()."): ".$settings->get_error_message();
169 add_action('all_admin_notices', array($this, 'show_admin_warning_multiple_storage_options'));
170 $updraftplus->log_wp_error($settings, true, true);
171 } elseif (!empty($settings['settings'])) {
172 foreach ($settings['settings'] as $instance_id => $storage_options) {
173 if((defined('UPDRAFTPLUS_CUSTOM_ONEDRIVE_APP') && UPDRAFTPLUS_CUSTOM_ONEDRIVE_APP)){
174 if(!empty($storage_options['clientid']) && !empty($storage_options['secret']) && empty($storage_options['refresh_token'])) {
175 add_action('all_admin_notices', array($this,'show_admin_warning_onedrive'));
176 } elseif (empty($storage_options['refresh_token'])) {
177 add_action('all_admin_notices', array($this,'show_admin_warning_onedrive'));
178 }
179 } else{
180 if(empty($storage_options['refresh_token']))add_action('all_admin_notices', array($this,'show_admin_warning_onedrive'));
181 }
182 }
183 }
184 }
185
186 if ('updraftvault' === $services || (is_array($services) && in_array('updraftvault', $services))) {
187 $settings = $updraftplus->update_remote_storage_options_format('updraftvault');
188
189 if (is_wp_error($settings)) {
190 if (!isset($this->storage_module_option_errors)) $this->storage_module_option_errors = '';
191 $this->storage_module_option_errors .= "UpdraftVault (".$settings->get_error_code()."): ".$settings->get_error_message();
192 add_action('all_admin_notices', array($this, 'show_admin_warning_multiple_storage_options'));
193 $updraftplus->log_wp_error($settings, true, true);
194 } elseif (!empty($settings['settings'])) {
195 foreach ($settings['settings'] as $instance_id => $storage_options) {
196 if (empty($storage_options['token']) && empty($storage_options['email'])) {
197 add_action('all_admin_notices', array($this,'show_admin_warning_updraftvault'));
198 }
199 }
200 }
201 }
202
203 if ($this->disk_space_check(1048576*35) === false) add_action('all_admin_notices', array($this, 'show_admin_warning_diskspace'));
204 }
205
206 private function setup_all_admin_notices_udonly($service, $override = false){
207 global $wp_version;
208
209 if (UpdraftPlus_Options::user_can_manage() && defined('DISABLE_WP_CRON') && DISABLE_WP_CRON && (!defined('UPDRAFTPLUS_DISABLE_WP_CRON_NOTICE') || !UPDRAFTPLUS_DISABLE_WP_CRON_NOTICE)) {
210 add_action('all_admin_notices', array($this, 'show_admin_warning_disabledcron'));
211 }
212
213 if (UpdraftPlus_Options::get_updraft_option('updraft_debug_mode')) {
214 @ini_set('display_errors',1);
215 @error_reporting(E_ALL & ~E_NOTICE & ~E_DEPRECATED);
216 add_action('all_admin_notices', array($this, 'show_admin_debug_warning'));
217 }
218
219 if (null === UpdraftPlus_Options::get_updraft_option('updraft_interval')) {
220 add_action('all_admin_notices', array($this, 'show_admin_nosettings_warning'));
221 $this->no_settings_warning = true;
222 }
223
224 # Avoid false positives, by attempting to raise the limit (as happens when we actually do a backup)
225 @set_time_limit(UPDRAFTPLUS_SET_TIME_LIMIT);
226 $max_execution_time = (int)@ini_get('max_execution_time');
227 if ($max_execution_time>0 && $max_execution_time<20) {
228 add_action('all_admin_notices', array($this, 'show_admin_warning_execution_time'));
229 }
230
231 // LiteSpeed has a generic problem with terminating cron jobs
232 if (isset($_SERVER['SERVER_SOFTWARE']) && strpos($_SERVER['SERVER_SOFTWARE'], 'LiteSpeed') !== false) {
233 if (!is_file(ABSPATH.'.htaccess') || !preg_match('/noabort/i', file_get_contents(ABSPATH.'.htaccess'))) {
234 add_action('all_admin_notices', array($this, 'show_admin_warning_litespeed'));
235 }
236 }
237
238 if (version_compare($wp_version, '3.2', '<')) add_action('all_admin_notices', array($this, 'show_admin_warning_wordpressversion'));
239 }
240
241 /*
242 private function reset_all_updraft_admin_notices() {
243
244 $actions_to_remove = array('show_admin_warning_googledrive', 'show_admin_warning_googlecloud', 'show_admin_warning_dropbox', 'show_admin_warning_onedrive', 'show_admin_warning_updraftvault', 'show_admin_warning_diskspace', 'show_admin_warning_disabledcron', 'show_admin_debug_warning', 'show_admin_warning_execution_time', 'show_admin_warning_litespeed', 'show_admin_warning_wordpressversion');
245
246 foreach ($actions_to_remove as $action) {
247 remove_action('all_admin_notices', $action);
248 }
249
250 }
251 */
252
253 //Used to output the information for the next scheduled backup
254 //**// moved to function for the ajax saves
255 public function next_scheduled_backups_output() {
256 // UNIX timestamp
257 $next_scheduled_backup = wp_next_scheduled('updraft_backup');
258 if ($next_scheduled_backup) {
259 // Convert to GMT
260 $next_scheduled_backup_gmt = gmdate('Y-m-d H:i:s', $next_scheduled_backup);
261 // Convert to blog time zone
262 $next_scheduled_backup = get_date_from_gmt($next_scheduled_backup_gmt, 'D, F j, Y H:i');
263 // $next_scheduled_backup = date_i18n('D, F j, Y H:i', $next_scheduled_backup);
264 } else {
265 $next_scheduled_backup = __('Nothing currently scheduled', 'updraftplus');
266 $files_not_scheduled = true;
267 }
268
269 $next_scheduled_backup_database = wp_next_scheduled('updraft_backup_database');
270 if (UpdraftPlus_Options::get_updraft_option('updraft_interval_database',UpdraftPlus_Options::get_updraft_option('updraft_interval')) == UpdraftPlus_Options::get_updraft_option('updraft_interval')) {
271 if (isset($files_not_scheduled)) {
272 $next_scheduled_backup_database = $next_scheduled_backup;
273 $database_not_scheduled = true;
274 } else {
275 $next_scheduled_backup_database = __("At the same time as the files backup", 'updraftplus');
276 $next_scheduled_backup_database_same_time = true;
277 }
278 } else {
279 if ($next_scheduled_backup_database) {
280 // Convert to GMT
281 $next_scheduled_backup_database_gmt = gmdate('Y-m-d H:i:s', $next_scheduled_backup_database);
282 // Convert to blog time zone
283 $next_scheduled_backup_database = get_date_from_gmt($next_scheduled_backup_database_gmt, 'D, F j, Y H:i');
284 // $next_scheduled_backup_database = date_i18n('D, F j, Y H:i', $next_scheduled_backup_database);
285 } else {
286 $next_scheduled_backup_database = __('Nothing currently scheduled', 'updraftplus');
287 $database_not_scheduled = true;
288 }
289 }
290 ?>
291 <tr>
292 <?php if (isset($files_not_scheduled) && isset($database_not_scheduled)) { ?>
293 <td colspan="2" class="not-scheduled"><?php _e('Nothing currently scheduled', 'updraftplus'); ?></td>
294 <?php } else { ?>
295 <td class="updraft_scheduled"><?php echo empty($next_scheduled_backup_database_same_time) ? __('Files', 'updraftplus') : __('Files and database', 'updraftplus'); ?>:</td><td class="updraft_all-files"><?php echo $next_scheduled_backup; ?></td>
296 </tr>
297 <?php if (empty($next_scheduled_backup_database_same_time)) { ?>
298 <tr>
299 <td class="updraft_scheduled"><?php _e('Database', 'updraftplus');?>: </td><td class="updraft_all-files"><?php echo $next_scheduled_backup_database; ?></td>
300 </tr>
301 <?php } ?>
302 <?php
303 }
304 }
305
306 private function admin_init() {
307
308 add_action('core_upgrade_preamble', array($this, 'core_upgrade_preamble'));
309 add_action('admin_action_upgrade-plugin', array($this, 'admin_action_upgrade_pluginortheme'));
310 add_action('admin_action_upgrade-theme', array($this, 'admin_action_upgrade_pluginortheme'));
311
312 add_action('admin_head', array($this,'admin_head'));
313 add_filter((is_multisite() ? 'network_admin_' : '').'plugin_action_links', array($this, 'plugin_action_links'), 10, 2);
314 add_action('wp_ajax_updraft_download_backup', array($this, 'updraft_download_backup'));
315 add_action('wp_ajax_updraft_ajax', array($this, 'updraft_ajax_handler'));
316 add_action('wp_ajax_updraft_ajaxrestore', array($this, 'updraft_ajaxrestore'));
317 add_action('wp_ajax_nopriv_updraft_ajaxrestore', array($this, 'updraft_ajaxrestore'));
318
319 add_action('wp_ajax_plupload_action', array($this, 'plupload_action'));
320 add_action('wp_ajax_plupload_action2', array($this, 'plupload_action2'));
321
322 add_action('wp_before_admin_bar_render', array($this, 'wp_before_admin_bar_render'));
323
324 // Add a new Ajax action for saving settings
325 add_action('wp_ajax_updraft_savesettings', array($this, 'updraft_ajax_savesettings'));
326
327 // Ajax for settings import and export
328 add_action('wp_ajax_updraft_importsettings', array($this, 'updraft_ajax_importsettings'));
329
330 // UpdraftPlus templates
331 $this->register_template_directories();
332
333 global $updraftplus, $wp_version, $pagenow;
334 add_filter('updraftplus_dirlist_others', array($updraftplus, 'backup_others_dirlist'));
335 add_filter('updraftplus_dirlist_uploads', array($updraftplus, 'backup_uploads_dirlist'));
336
337 // First, the checks that are on all (admin) pages:
338
339 $service = UpdraftPlus_Options::get_updraft_option('updraft_service');
340
341 if (UpdraftPlus_Options::user_can_manage()) {
342
343 $this->print_restore_in_progress_box_if_needed();
344
345 // Main dashboard page advert
346 // Since our nonce is printed, make sure they have sufficient credentials
347 if ($pagenow == 'index.php' && current_user_can('update_plugins') && (!file_exists(UPDRAFTPLUS_DIR.'/udaddons') || (defined('UPDRAFTPLUS_FORCE_DASHNOTICE') && UPDRAFTPLUS_FORCE_DASHNOTICE))) {
348
349 $dismissed_until = UpdraftPlus_Options::get_updraft_option('updraftplus_dismisseddashnotice', 0);
350
351 $backup_dir = $updraftplus->backups_dir_location();
352 // N.B. Not an exact proxy for the installed time; they may have tweaked the expert option to move the directory
353 $installed = @filemtime($backup_dir.'/index.html');
354 $installed_for = time() - $installed;
355
356 if (($installed && time() > $dismissed_until && $installed_for > 28*86400 && !defined('UPDRAFTPLUS_NOADS_B')) || (defined('UPDRAFTPLUS_FORCE_DASHNOTICE') && UPDRAFTPLUS_FORCE_DASHNOTICE)) {
357 add_action('all_admin_notices', array($this, 'show_admin_notice_upgradead'));
358 }
359 }
360
361 //Moved out for use with Ajax saving
362 $this->setup_all_admin_notices_global($service);
363 }
364
365 // Next, the actions that only come on the UpdraftPlus page
366 if ($pagenow != UpdraftPlus_Options::admin_page() || empty($_REQUEST['page']) || 'updraftplus' != $_REQUEST['page']) return;
367 $this->setup_all_admin_notices_udonly($service);
368
369 add_action('admin_enqueue_scripts', array($this, 'admin_enqueue_scripts'), 99999);
370 }
371
372 /**
373 * Sets up what is needed to allow an in-page backup to be run. Will enqueue scripts and output appropriate HTML (so, should be run when at a suitable place). Not intended for use on the UpdraftPlus settings page.
374 *
375 * @param string $title Text to use for the title of the modal
376 * @param callable $callback Callable function to output the contents of the updraft_inpage_prebackup element - i.e. what shows in the modal before a backup beings.
377 *
378 */
379 public function add_backup_scaffolding($title, $callback) {
380 $this->admin_enqueue_scripts();
381 ?>
382 <script>
383 // TODO: This is not the best way.
384 var updraft_credentialtest_nonce='<?php echo wp_create_nonce('updraftplus-credentialtest-nonce');?>';
385 </script>
386 <div id="updraft-poplog" >
387 <pre id="updraft-poplog-content" style="white-space: pre-wrap;"></pre>
388 </div>
389
390 <div id="updraft-backupnow-inpage-modal" title="UpdraftPlus - <?php echo $title; ?>">
391
392 <div id="updraft_inpage_prebackup" style="float:left; clear:both;">
393 <?php call_user_func($callback); ?>
394 </div>
395
396 <div id="updraft_inpage_backup" style="float:left; clear:both;">
397
398 <h2><?php echo $title;?></h2>
399
400 <div id="updraft_backup_started" class="updated" style="display:none; max-width: 560px; font-size:100%; line-height: 100%; padding:6px; clear:left;"></div>
401
402 <?php $this->render_active_jobs_and_log_table(true, false); ?>
403
404 </div>
405
406 </div>
407 <?php
408 }
409
410 public function updraft_ajaxrestore() {
411 // TODO: All needs testing with restricted filesystem permissions. Those credentials need to be POST-ed too - currently not.
412 // TODO
413 // error_log(serialize($_POST));
414
415 if (empty($_POST['subaction']) || 'restore' != $_POST['subaction']) {
416 echo json_encode(array('e' => 'Illegitimate data sent (0)'));
417 die();
418 }
419
420 if (empty($_POST['restorenonce'])) {
421 echo json_encode(array('e' => 'Illegitimate data sent (1)'));
422 die();
423 }
424
425 $restore_nonce = (string)$_POST['restorenonce'];
426
427 if (empty($_POST['ajaxauth'])) {
428 echo json_encode(array('e' => 'Illegitimate data sent (2)'));
429 die();
430 }
431
432 global $updraftplus;
433
434 $ajax_auth = get_site_option('updraft_ajax_restore_'.$restore_nonce);
435
436 if (!$ajax_auth) {
437 echo json_encode(array('e' => 'Illegitimate data sent (3)'));
438 die();
439 }
440
441 if (!preg_match('/^([0-9a-f]+):(\d+)/i', $ajax_auth, $matches)) {
442 echo json_encode(array('e' => 'Illegitimate data sent (4)'));
443 die();
444 }
445
446 $nonce_time = $matches[2];
447 $auth_code_sent = $matches[1];
448 if (time() > $nonce_time + 600) {
449 echo json_encode(array('e' => 'Illegitimate data sent (5)'));
450 die();
451 }
452
453 // TODO: Deactivate the auth code whilst the operation is underway
454
455 $last_one = empty($_POST['lastone']) ? false : true;
456
457 @set_time_limit(UPDRAFTPLUS_SET_TIME_LIMIT);
458
459 $updraftplus->backup_time_nonce($restore_nonce);
460 $updraftplus->logfile_open($restore_nonce);
461
462 $timestamp = empty($_POST['timestamp']) ? false : (int)$_POST['timestamp'];
463 $multisite = empty($_POST['multisite']) ? false : (bool)$_POST['multisite'];
464 $created_by_version = empty($_POST['created_by_version']) ? false : (int)$_POST['created_by_version'];
465
466 // TODO: We need to know about first_one (not yet sent), as well as last_one
467
468 // TODO: Verify the values of these
469 $type = empty($_POST['type']) ? false : (int)$_POST['type'];
470 $backupfile = empty($_POST['backupfile']) ? false : (string)$_POST['backupfile'];
471
472 $updraftplus->log("Deferred restore resumption: $type: $backupfile (timestamp=$timestamp, last_one=$last_one)");
473
474
475
476 $backupable_entities = $updraftplus->get_backupable_file_entities(true);
477
478 if (!isset($backupable_entities[$type])) {
479 echo json_encode(array('e' => 'Illegitimate data sent (6 - no such entity)', 'data' => $type));
480 die();
481 }
482
483
484 if ($last_one) {
485 // Remove the auth nonce from the DB to prevent abuse
486 delete_site_option('updraft_ajax_restore_'.$restore_nonce);
487 } else {
488 // Reset the counter after a successful operation
489 update_site_option('updraft_ajax_restore_'.$restore_nonce, $auth_code_sent.':'.time());
490 }
491
492 echo json_encode(array('e' => 'TODO', 'd' => $_POST));
493 die;
494 }
495
496 public function wp_before_admin_bar_render() {
497 global $wp_admin_bar;
498
499 if (!UpdraftPlus_Options::user_can_manage()) return;
500 if (defined('UPDRAFTPLUS_ADMINBAR_DISABLE') && UPDRAFTPLUS_ADMINBAR_DISABLE) return;
501
502 if (false == apply_filters('updraftplus_settings_page_render', true)) return;
503
504 $option_location = UpdraftPlus_Options::admin_page_url();
505
506 $args = array(
507 'id' => 'updraft_admin_node',
508 'title' => apply_filters('updraftplus_admin_node_title', 'UpdraftPlus')
509 );
510 $wp_admin_bar->add_node($args);
511
512 $args = array(
513 'id' => 'updraft_admin_node_status',
514 'title' => __('Current Status', 'updraftplus').' / '.__('Backup Now', 'updraftplus'),
515 'parent' => 'updraft_admin_node',
516 'href' => $option_location.'?page=updraftplus&tab=status'
517 );
518 $wp_admin_bar->add_node($args);
519
520 $args = array(
521 'id' => 'updraft_admin_node_backups',
522 'title' => __('Existing Backups', 'updraftplus'),
523 'parent' => 'updraft_admin_node',
524 'href' => $option_location.'?page=updraftplus&tab=backups'
525 );
526 $wp_admin_bar->add_node($args);
527
528 $args = array(
529 'id' => 'updraft_admin_node_settings',
530 'title' => __('Settings', 'updraftplus'),
531 'parent' => 'updraft_admin_node',
532 'href' => $option_location.'?page=updraftplus&tab=settings'
533 );
534 $wp_admin_bar->add_node($args);
535
536 $args = array(
537 'id' => 'updraft_admin_node_expert_content',
538 'title' => __('Advanced Tools', 'updraftplus'),
539 'parent' => 'updraft_admin_node',
540 'href' => $option_location.'?page=updraftplus&tab=expert'
541 );
542 $wp_admin_bar->add_node($args);
543
544 $args = array(
545 'id' => 'updraft_admin_node_addons',
546 'title' => __('Extensions', 'updraftplus'),
547 'parent' => 'updraft_admin_node',
548 'href' => $option_location.'?page=updraftplus&tab=addons'
549 );
550 $wp_admin_bar->add_node($args);
551
552 global $updraftplus;
553 if (!$updraftplus->have_addons) {
554 $args = array(
555 'id' => 'updraft_admin_node_premium',
556 'title' => 'UpdraftPlus Premium',
557 'parent' => 'updraft_admin_node',
558 'href' => apply_filters('updraftplus_com_link','https://updraftplus.com/shop/updraftplus-premium/')
559 );
560 $wp_admin_bar->add_node($args);
561 }
562 }
563
564 // // Defeat other plugins/themes which dump their jQuery UI CSS onto our settings page
565 // public function style_loader_tag($link, $handle) {
566 // if ('jquery-ui' != $handle || false === strpos) return $link;
567 // return "<link rel='stylesheet' id='$handle-css' $title href='$href' type='text/css' media='$media' />\n";
568 // }
569
570 public function show_admin_notice_upgradead() {
571 $this->include_template('wp-admin/notices/thanks-for-using-main-dash.php');
572 }
573
574 private function ensure_sufficient_jquery_and_enqueue() {
575 global $updraftplus, $wp_version;
576
577 $enqueue_version = (defined('SCRIPT_DEBUG') && SCRIPT_DEBUG) ? $updraftplus->version.'.'.time() : $updraftplus->version;
578 $min_or_not = (defined('SCRIPT_DEBUG') && SCRIPT_DEBUG) ? '' : '.min';
579
580 if (version_compare($wp_version, '3.3', '<')) {
581 // Require a newer jQuery (3.2.1 has 1.6.1, so we go for something not too much newer). We use .on() in a way that is incompatible with < 1.7
582 wp_deregister_script('jquery');
583 $jquery_enqueue_version = (defined('SCRIPT_DEBUG') && SCRIPT_DEBUG) ? '1.7.2'.'.'.time() : '1.7.2';
584 wp_register_script('jquery', 'https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery'.$min_or_not.'.js', false, $jquery_enqueue_version, false);
585 wp_enqueue_script('jquery');
586 // No plupload until 3.3
587 wp_enqueue_script('updraftplus-admin', UPDRAFTPLUS_URL.'/includes/updraft-admin'.$min_or_not.'.js', array('jquery', 'jquery-ui-dialog'), $enqueue_version, true);
588 } else {
589 wp_enqueue_script('updraftplus-admin', UPDRAFTPLUS_URL.'/includes/updraft-admin'.$min_or_not.'.js', array('jquery', 'jquery-ui-dialog', 'plupload-all'), $enqueue_version);
590 }
591
592 }
593
594 // This is also called directly from the auto-backup add-on
595 public function admin_enqueue_scripts() {
596
597 global $updraftplus, $wp_locale;
598
599 $enqueue_version = (defined('SCRIPT_DEBUG') && SCRIPT_DEBUG) ? $updraftplus->version.'.'.time() : $updraftplus->version;
600 $min_or_not = (defined('SCRIPT_DEBUG') && SCRIPT_DEBUG) ? '' : '.min';
601
602 // Defeat other plugins/themes which dump their jQuery UI CSS onto our settings page
603 wp_deregister_style('jquery-ui');
604 $jquery_ui_css_enqueue_version = (defined('SCRIPT_DEBUG') && SCRIPT_DEBUG) ? '1.11.4'.'.'.time() : '1.11.4';
605 wp_enqueue_style('jquery-ui', UPDRAFTPLUS_URL.'/includes/jquery-ui.custom'.$min_or_not.'.css', array(), $jquery_ui_css_enqueue_version);
606
607 wp_enqueue_style('updraft-admin-css', UPDRAFTPLUS_URL.'/css/admin'.$min_or_not.'.css', array(), $enqueue_version);
608 // add_filter('style_loader_tag', array($this, 'style_loader_tag'), 10, 2);
609
610 $this->ensure_sufficient_jquery_and_enqueue();
611 $jquery_blockui_enqueue_version = (defined('SCRIPT_DEBUG') && SCRIPT_DEBUG) ? '2.70.0'.'.'.time() : '2.70.0';
612 wp_enqueue_script('jquery-blockui', UPDRAFTPLUS_URL.'/includes/jquery.blockUI'.$min_or_not.'.js', array('jquery'), $jquery_blockui_enqueue_version);
613
614 wp_enqueue_script('jquery-labelauty', UPDRAFTPLUS_URL.'/includes/labelauty/jquery-labelauty'.$min_or_not.'.js', array('jquery'), $enqueue_version);
615 wp_enqueue_style('jquery-labelauty', UPDRAFTPLUS_URL.'/includes/labelauty/jquery-labelauty'.$min_or_not.'.css', array(), $enqueue_version);
616 $serialize_js_enqueue_version = (defined('SCRIPT_DEBUG') && SCRIPT_DEBUG) ? '2.8.1'.'.'.time() : '2.8.1';
617 wp_enqueue_script('jquery.serializeJSON', UPDRAFTPLUS_URL.'/includes/jquery.serializeJSON/jquery.serializejson'.$min_or_not.'.js', array('jquery'), $serialize_js_enqueue_version);
618
619 $this->enqueue_jstree();
620
621 do_action('updraftplus_admin_enqueue_scripts');
622
623 $day_selector = '';
624 for ($day_index = 0; $day_index <= 6; $day_index++) {
625 // $selected = ($opt == $day_index) ? 'selected="selected"' : '';
626 $selected = '';
627 $day_selector .= "\n\t<option value='" . $day_index . "' $selected>" . $wp_locale->get_weekday($day_index) . '</option>';
628 }
629
630 $mday_selector = '';
631 for ($mday_index = 1; $mday_index <= 28; $mday_index++) {
632 // $selected = ($opt == $mday_index) ? 'selected="selected"' : '';
633 $selected = '';
634 $mday_selector .= "\n\t<option value='" . $mday_index . "' $selected>" . $mday_index . '</option>';
635 }
636
637 wp_localize_script('updraftplus-admin', 'updraftlion', array(
638 'sendonlyonwarnings' => __('Send a report only when there are warnings/errors', 'updraftplus'),
639 'wholebackup' => __('When the Email storage method is enabled, also send the entire backup', 'updraftplus'),
640 'emailsizelimits' => esc_attr(sprintf(__('Be aware that mail servers tend to have size limits; typically around %s Mb; backups larger than any limits will likely not arrive.', 'updraftplus'), '10-20')),
641 'rescanning' => __('Rescanning (looking for backups that you have uploaded manually into the internal backup store)...', 'updraftplus'),
642 'rescanningremote' => __('Rescanning remote and local storage for backup sets...', 'updraftplus'),
643 'enteremailhere' => esc_attr(__('To send to more than one address, separate each address with a comma.', 'updraftplus')),
644 'excludedeverything' => __('If you exclude both the database and the files, then you have excluded everything!', 'updraftplus'),
645 'nofileschosen' => __('You have chosen to backup files, but no file entities have been selected', 'updraftplus'),
646 'notableschosen' => __('You have chosen to backup a database, but no tables have been selected', 'updraftplus'),
647 'restoreproceeding' => __('The restore operation has begun. Do not press stop or close your browser until it reports itself as having finished.', 'updraftplus'),
648 'unexpectedresponse' => __('Unexpected response:', 'updraftplus'),
649 'servererrorcode' => __('The web server returned an error code (try again, or check your web server logs)', 'updraftplus'),
650 'newuserpass' => __("The new user's RackSpace console password is (this will not be shown again):", 'updraftplus'),
651 'trying' => __('Trying...', 'updraftplus'),
652 'fetching' => __('Fetching...', 'updraftplus'),
653 'calculating' => __('calculating...', 'updraftplus'),
654 'begunlooking' => __('Begun looking for this entity', 'updraftplus'),
655 'stilldownloading' => __('Some files are still downloading or being processed - please wait.', 'updraftplus'),
656 'processing' => __('Processing files - please wait...', 'updraftplus'),
657 'emptyresponse' => __('Error: the server sent an empty response.', 'updraftplus'),
658 'warnings' => __('Warnings:', 'updraftplus'),
659 'errors' => __('Errors:', 'updraftplus'),
660 'jsonnotunderstood' => __('Error: the server sent us a response which we did not understand.', 'updraftplus'),
661 'errordata' => __('Error data:', 'updraftplus'),
662 'error' => __('Error:', 'updraftplus'),
663 'errornocolon' => __('Error', 'updraftplus'),
664 'existing_backups' => __('Existing Backups', 'updraftplus'),
665 'fileready' => __('File ready.', 'updraftplus'),
666 'actions' => __('Actions', 'updraftplus'),
667 'deletefromserver' => __('Delete from your web server', 'updraftplus'),
668 'downloadtocomputer' => __('Download to your computer', 'updraftplus'),
669 'browse_contents' => __('Browse contents', 'updraftplus'),
670 'notunderstood' => __('Download error: the server sent us a response which we did not understand.', 'updraftplus'),
671 'requeststart' => __('Requesting start of backup...', 'updraftplus'),
672 'phpinfo' => __('PHP information', 'updraftplus'),
673 'delete_old_dirs' => __('Delete Old Directories', 'updraftplus'),
674 'raw' => __('Raw backup history', 'updraftplus'),
675 'notarchive' => __('This file does not appear to be an UpdraftPlus backup archive (such files are .zip or .gz files which have a name like: backup_(time)_(site name)_(code)_(type).(zip|gz)).', 'updraftplus').' '.__('However, UpdraftPlus archives are standard zip/SQL files - so if you are sure that your file has the right format, then you can rename it to match that pattern.', 'updraftplus'),
676 'notarchive2' => '<p>'.__('This file does not appear to be an UpdraftPlus backup archive (such files are .zip or .gz files which have a name like: backup_(time)_(site name)_(code)_(type).(zip|gz)).', 'updraftplus').'</p> '.apply_filters('updraftplus_if_foreign_then_premium_message', '<p><a href="'.apply_filters('updraftplus_com_link', "https://updraftplus.com/shop/updraftplus-premium/").'">'.__('If this is a backup created by a different backup plugin, then UpdraftPlus Premium may be able to help you.', 'updraftplus').'</a></p>'),
677 'makesure' => __('(make sure that you were trying to upload a zip file previously created by UpdraftPlus)', 'updraftplus'),
678 'uploaderror' => __('Upload error:', 'updraftplus'),
679 'notdba' => __('This file does not appear to be an UpdraftPlus encrypted database archive (such files are .gz.crypt files which have a name like: backup_(time)_(site name)_(code)_db.crypt.gz).', 'updraftplus'),
680 'uploaderr' => __('Upload error', 'updraftplus'),
681 'followlink' => __('Follow this link to attempt decryption and download the database file to your computer.', 'updraftplus'),
682 'thiskey' => __('This decryption key will be attempted:', 'updraftplus'),
683 'unknownresp' => __('Unknown server response:', 'updraftplus'),
684 'ukrespstatus' => __('Unknown server response status:', 'updraftplus'),
685 'uploaded' => __('The file was uploaded.', 'updraftplus'),
686 'backupnow' => __('Backup Now', 'updraftplus'),
687 'cancel' => __('Cancel', 'updraftplus'),
688 'deletebutton' => __('Delete', 'updraftplus'),
689 'createbutton' => __('Create', 'updraftplus'),
690 'youdidnotselectany' => __('You did not select any components to restore. Please select at least one, and then try again.', 'updraftplus'),
691 'proceedwithupdate' => __('Proceed with update', 'updraftplus'),
692 'close' => __('Close', 'updraftplus'),
693 'restore' => __('Restore', 'updraftplus'),
694 'downloadlogfile' => __('Download log file', 'updraftplus'),
695 'automaticbackupbeforeupdate' => __('Automatic backup before update', 'updraftplus'),
696 'unsavedsettings' => __('You have made changes to your settings, and not saved.', 'updraftplus'),
697 'saving' => __('Saving...', 'updraftplus'),
698 'connect' => __('Connect', 'updraftplus'),
699 'connecting' => __('Connecting...', 'updraftplus'),
700 'disconnect' => __('Disconnect', 'updraftplus'),
701 'disconnecting' => __('Disconnecting...', 'updraftplus'),
702 'counting' => __('Counting...', 'updraftplus'),
703 'updatequotacount' => __('Update quota count', 'updraftplus'),
704 'addingsite' => __('Adding...', 'updraftplus'),
705 'addsite' => __('Add site', 'updraftplus'),
706 // 'resetting' => __('Resetting...', 'updraftplus'),
707 'creating_please_allow' => __('Creating...', 'updraftplus').(function_exists('openssl_encrypt') ? '' : ' ('.__('your PHP install lacks the openssl module; as a result, this can take minutes; if nothing has happened by then, then you should either try a smaller key size, or ask your web hosting company how to enable this PHP module on your setup.', 'updraftplus').')'),
708 'sendtosite' => __('Send to site:', 'updraftplus'),
709 'checkrpcsetup' => sprintf(__('You should check that the remote site is online, not firewalled, does not have security modules that may be blocking access, has UpdraftPlus version %s or later active and that the keys have been entered correctly.', 'updraftplus'), '2.10.3'),
710 'pleasenamekey' => __('Please give this key a name (e.g. indicate the site it is for):', 'updraftplus'),
711 'key' => __('Key', 'updraftplus'),
712 'nokeynamegiven' => sprintf(__("Failure: No %s was given.",'updraftplus'), __('key name', 'updraftplus')),
713 'deleting' => __('Deleting...', 'updraftplus'),
714 'enter_mothership_url' => __('Please enter a valid URL', 'updraftplus'),
715 'delete_response_not_understood' => __("We requested to delete the file, but could not understand the server's response", 'updraftplus'),
716 'testingconnection' => __('Testing connection...', 'updraftplus'),
717 'send' => __('Send', 'updraftplus'),
718 'migratemodalheight' => class_exists('UpdraftPlus_Addons_Migrator') ? 555 : 300,
719 'migratemodalwidth' => class_exists('UpdraftPlus_Addons_Migrator') ? 770 : 500,
720 'download' => _x('Download', '(verb)', 'updraftplus'),
721 'browse_download_link' => apply_filters('updraftplus_browse_download_link', '<a id="updraft_zip_download_notice" href="'.apply_filters('updraftplus_com_link', "https://updraftplus.com/landing/updraftplus-premium").'">'.__("With UpdraftPlus Premium, you can directly download individual files from here.", "updraftplus").'</a>'),
722 'unsavedsettingsbackup' => __('You have made changes to your settings, and not saved.', 'updraftplus')."\n".__('You should save your changes to ensure that they are used for making your backup.', 'updraftplus'),
723 'unsaved_settings_export' => __('You have made changes to your settings, and not saved.', 'updraftplus')."\n".__('Your export file will be of your displayed settings, not your saved ones.', 'updraftplus'),
724 'dayselector' => $day_selector,
725 'mdayselector' => $mday_selector,
726 'day' => __('day', 'updraftplus'),
727 'inthemonth' => __('in the month', 'updraftplus'),
728 'days' => __('day(s)', 'updraftplus'),
729 'hours' => __('hour(s)', 'updraftplus'),
730 'weeks' => __('week(s)', 'updraftplus'),
731 'forbackupsolderthan' => __('For backups older than', 'updraftplus'),
732 'ud_url' => UPDRAFTPLUS_URL,
733 'processing' => __('Processing...', 'updraftplus'),
734 'pleasefillinrequired' => __('Please fill in the required information.', 'updraftplus'),
735 'test_settings' => __('Test %s Settings', 'updraftplus'),
736 'testing_settings' => __('Testing %s Settings...', 'updraftplus'),
737 'settings_test_result' => __('%s settings test result:', 'updraftplus'),
738 'nothing_yet_logged' => __('Nothing yet logged', 'updraftplus'),
739 'import_select_file' => __('You have not yet selected a file to import.', 'updraftplus'),
740 'import_invalid_json_file' => __('Error: The chosen file is corrupt. Please choose a valid UpdraftPlus export file.', 'updraftplus'),
741 'updraft_settings_url' => UpdraftPlus_Options::admin_page_url().'?page=updraftplus',
742 'network_site_url' => network_site_url(),
743 'importing' => __('Importing...', 'updraftplus'),
744 'importing_data_from' => __('This will import data from:', 'updraftplus'),
745 'exported_on' => __('Which was exported on:', 'updraftplus'),
746 'continue_import' => __('Do you want to carry out the import?', 'updraftplus'),
747 'complete' => __('Complete', 'updraftplus'),
748 'remote_delete_limit' => defined('UPDRAFTPLUS_REMOTE_DELETE_LIMIT') ? UPDRAFTPLUS_REMOTE_DELETE_LIMIT : 15,
749 'remote_files_deleted' => __('remote files deleted', 'updraftplus'),
750 'http_code' => __('HTTP code:', 'updraftplus'),
751 'makesure2' => __('The file failed to upload. Please check the following:', 'updraftplus')."\n\n - ".__('Any settings in your .htaccess or web.config file that affects the maximum upload or post size.', 'updraftplus')."\n - ".__('The available memory on the server.', 'updraftplus')."\n - ".__('That you are attempting to upload a zip file previously created by UpdraftPlus.', 'updraftplus')."\n\n".__('Further information may be found in the browser JavaScript console, and the server PHP error logs.', 'updraftplus'),
752 'zip_file_contents' => __('Browsing zip file', 'updraftplus'),
753 'zip_file_contents_info' => __('Select a file to view information about it', 'updraftplus'),
754 'search' => __('Search', 'updraftplus'),
755 'download_timeout' => __('Unable to download file. This could be caused by a timeout. It would be best to download the zip to your computer.', 'updraftplus'),
756 'loading_log_file' => __('Loading log file', 'updraftplus'),
757 'updraftplus_version' => $updraftplus->version
758 ) );
759 }
760
761 // Despite the name, this fires irrespective of what capabilities the user has (even none - so be careful)
762 public function core_upgrade_preamble() {
763 // They need to be able to perform backups, and to perform updates
764 if (!UpdraftPlus_Options::user_can_manage() || (!current_user_can('update_core') && !current_user_can('update_plugins') && !current_user_can('update_themes'))) return;
765
766 if (!class_exists('UpdraftPlus_Addon_Autobackup')) {
767 if (defined('UPDRAFTPLUS_NOADS_B')) return;
768 }
769
770 ?>
771 <?php
772 if (!class_exists('UpdraftPlus_Addon_Autobackup')) {
773 if (!class_exists('UpdraftPlus_Notices')) require_once(UPDRAFTPLUS_DIR.'/includes/updraftplus-notices.php');
774 global $updraftplus_notices;
775 echo apply_filters('updraftplus_autobackup_blurb', $updraftplus_notices->do_notice('autobackup', 'autobackup', true));
776 } else {
777 echo '<div class="updraft-ad-container updated">';
778 echo '<h3 style="margin-top: 2px;">'. __('Be safe with an automatic backup', 'updraftplus').'</h3>';
779 echo apply_filters('updraftplus_autobackup_blurb', '');
780 echo '</div>';
781 }
782 ?>
783 <script>
784 jQuery(document).ready(function() {
785 jQuery('.updraft-ad-container').appendTo('.wrap p:first');
786 });
787 </script>
788 <?php
789 }
790
791 public function admin_head() {
792
793 global $pagenow;
794
795 if ($pagenow != UpdraftPlus_Options::admin_page() || !isset($_REQUEST['page']) || 'updraftplus' != $_REQUEST['page'] || !UpdraftPlus_Options::user_can_manage()) return;
796
797 $chunk_size = min(wp_max_upload_size()-1024, 1048576*2);
798
799 # The multiple_queues argument is ignored in plupload 2.x (WP3.9+) - http://make.wordpress.org/core/2014/04/11/plupload-2-x-in-wordpress-3-9/
800 # max_file_size is also in filters as of plupload 2.x, but in its default position is still supported for backwards-compatibility. Likewise, our use of filters.extensions below is supported by a backwards-compatibility option (the current way is filters.mime-types.extensions
801
802 $plupload_init = array(
803 'runtimes' => 'html5,flash,silverlight,html4',
804 'browse_button' => 'plupload-browse-button',
805 'container' => 'plupload-upload-ui',
806 'drop_element' => 'drag-drop-area',
807 'file_data_name' => 'async-upload',
808 'multiple_queues' => true,
809 'max_file_size' => '100Gb',
810 'chunk_size' => $chunk_size.'b',
811 'url' => admin_url('admin-ajax.php', 'relative'),
812 'multipart' => true,
813 'multi_selection' => true,
814 'urlstream_upload' => true,
815 // additional post data to send to our ajax hook
816 'multipart_params' => array(
817 '_ajax_nonce' => wp_create_nonce('updraft-uploader'),
818 'action' => 'plupload_action'
819 )
820 );
821 // 'flash_swf_url' => includes_url('js/plupload/plupload.flash.swf'),
822 // 'silverlight_xap_url' => includes_url('js/plupload/plupload.silverlight.xap'),
823
824 // We want to receive -db files also...
825 // if (1) {
826 // $plupload_init['filters'] = array(array('title' => __('Allowed Files'), 'extensions' => 'zip,tar,gz,bz2,crypt,sql,txt'));
827 // } else {
828 // }
829
830 # WP 3.9 updated to plupload 2.0 - https://core.trac.wordpress.org/ticket/25663
831 if (is_file(ABSPATH.WPINC.'/js/plupload/Moxie.swf')) {
832 $plupload_init['flash_swf_url'] = includes_url('js/plupload/Moxie.swf');
833 } else {
834 $plupload_init['flash_swf_url'] = includes_url('js/plupload/plupload.flash.swf');
835 }
836
837 if (is_file(ABSPATH.WPINC.'/js/plupload/Moxie.xap')) {
838 $plupload_init['silverlight_xap_url'] = includes_url('js/plupload/Moxie.xap');
839 } else {
840 $plupload_init['silverlight_xap_url'] = includes_url('js/plupload/plupload.silverlight.swf');
841 }
842
843 ?><script>
844 var updraft_credentialtest_nonce='<?php echo wp_create_nonce('updraftplus-credentialtest-nonce');?>';
845 var updraftplus_settings_nonce='<?php echo wp_create_nonce('updraftplus-settings-nonce');?>';
846 var updraft_siteurl = '<?php echo esc_js(site_url('', 'relative'));?>';
847 var updraft_plupload_config=<?php echo json_encode($plupload_init); ?>;
848 var updraft_download_nonce='<?php echo wp_create_nonce('updraftplus_download');?>';
849 var updraft_accept_archivename = <?php echo apply_filters('updraftplus_accept_archivename_js', "[]");?>;
850 <?php
851 $plupload_init['browse_button'] = 'plupload-browse-button2';
852 $plupload_init['container'] = 'plupload-upload-ui2';
853 $plupload_init['drop_element'] = 'drag-drop-area2';
854 $plupload_init['multipart_params']['action'] = 'plupload_action2';
855 $plupload_init['filters'] = array(array('title' => __('Allowed Files'), 'extensions' => 'crypt'));
856 ?>
857 var updraft_plupload_config2=<?php echo json_encode($plupload_init); ?>;
858 var updraft_downloader_nonce = '<?php wp_create_nonce("updraftplus_download"); ?>'
859 <?php
860 $overdue = $this->howmany_overdue_crons();
861 if ($overdue >= 4) { ?>
862 jQuery(document).ready(function(){
863 setTimeout(function(){updraft_check_overduecrons();}, 11000);
864 });
865 <?php } ?>
866 </script>
867 <?php
868 }
869
870
871 private function disk_space_check($space) {
872 global $updraftplus;
873 $updraft_dir = $updraftplus->backups_dir_location();
874 $disk_free_space = @disk_free_space($updraft_dir);
875 if ($disk_free_space == false) return -1;
876 return ($disk_free_space > $space) ? true : false;
877 }
878
879 # Adds the settings link under the plugin on the plugin screen.
880 public function plugin_action_links($links, $file) {
881 if (is_array($links) && $file == 'updraftplus/updraftplus.php'){
882 $settings_link = '<a href="'.UpdraftPlus_Options::admin_page_url().'?page=updraftplus">'.__("Settings", "updraftplus").'</a>';
883 array_unshift($links, $settings_link);
884 // $settings_link = '<a href="http://david.dw-perspective.org.uk/donate">'.__("Donate","UpdraftPlus").'</a>';
885 // array_unshift($links, $settings_link);
886 $settings_link = '<a href="'.apply_filters('updraftplus_com_link', "https://updraftplus.com/").'">'.__("Add-Ons / Pro Support","updraftplus").'</a>';
887 array_unshift($links, $settings_link);
888 }
889 return $links;
890 }
891
892 public function admin_action_upgrade_pluginortheme() {
893 if (isset($_GET['action']) && ($_GET['action'] == 'upgrade-plugin' || $_GET['action'] == 'upgrade-theme') && !class_exists('UpdraftPlus_Addon_Autobackup') && !defined('UPDRAFTPLUS_NOADS_B')) {
894
895 if ($_GET['action'] == 'upgrade-plugin') {
896 if (!current_user_can('update_plugins')) return;
897 } else {
898 if (!current_user_can('update_themes')) return;
899 }
900
901 $dismissed_until = UpdraftPlus_Options::get_updraft_option('updraftplus_dismissedautobackup', 0);
902 if ($dismissed_until > time()) return;
903
904 if ('upgrade-plugin' == $_GET['action']) {
905 $title = __('Update Plugin');
906 $parent_file = 'plugins.php';
907 $submenu_file = 'plugins.php';
908 } else {
909 $title = __('Update Theme');
910 $parent_file = 'themes.php';
911 $submenu_file = 'themes.php';
912 }
913
914 require_once(ABSPATH.'wp-admin/admin-header.php');
915
916 if (!class_exists('UpdraftPlus_Notices')) require_once(UPDRAFTPLUS_DIR.'/includes/updraftplus-notices.php');
917 global $updraftplus_notices;
918 $updraftplus_notices->do_notice('autobackup', 'autobackup');
919 }
920 }
921
922 public function show_admin_warning($message, $class = "updated") {
923 echo '<div class="updraftmessage '.$class.'">'."<p>$message</p></div>";
924 }
925
926 //
927 public function show_admin_warning_multiple_storage_options() {
928 $this->show_admin_warning('<strong>UpdraftPlus:</strong> '.__('An error occurred when fetching storage module options: ', 'updraftplus').htmlspecialchars($this->storage_module_option_errors), 'error');
929 }
930
931 public function show_admin_warning_unwritable(){
932 $unwritable_mess = htmlspecialchars(__("The 'Backup Now' button is disabled as your backup directory is not writable (go to the 'Settings' tab and find the relevant option).", 'updraftplus'));
933 $this->show_admin_warning($unwritable_mess, "error");
934 }
935
936 public function show_admin_nosettings_warning() {
937 $this->show_admin_warning('<strong>'.__('Welcome to UpdraftPlus!', 'updraftplus').'</strong> '.__('To make a backup, just press the Backup Now button.', 'updraftplus').' <a href="#" id="updraft-navtab-settings2">'.__('To change any of the default settings of what is backed up, to configure scheduled backups, to send your backups to remote storage (recommended), and more, go to the settings tab.', 'updraftplus').'</a>', 'updated notice is-dismissible');
938 }
939
940 public function show_admin_warning_execution_time() {
941 $this->show_admin_warning('<strong>'.__('Warning', 'updraftplus').':</strong> '.sprintf(__('The amount of time allowed for WordPress plugins to run is very low (%s seconds) - you should increase it to avoid backup failures due to time-outs (consult your web hosting company for more help - it is the max_execution_time PHP setting; the recommended value is %s seconds or more)', 'updraftplus'), (int)@ini_get('max_execution_time'), 90));
942 }
943
944 public function show_admin_warning_disabledcron() {
945 $this->show_admin_warning('<strong>'.__('Warning', 'updraftplus').':</strong> '.__('The scheduler is disabled in your WordPress install, via the DISABLE_WP_CRON setting. No backups can run (even &quot;Backup Now&quot;) unless either you have set up a facility to call the scheduler manually, or until it is enabled.', 'updraftplus').' <a href="'.apply_filters('updraftplus_com_link', "https://updraftplus.com/faqs/my-scheduled-backups-and-pressing-backup-now-does-nothing-however-pressing-debug-backup-does-produce-a-backup/#disablewpcron/").'">'.__('Go here for more information.', 'updraftplus').'</a>', 'updated updraftplus-disable-wp-cron-warning');
946 }
947
948 public function show_admin_warning_diskspace() {
949 $this->show_admin_warning('<strong>'.__('Warning', 'updraftplus').':</strong> '.sprintf(__('You have less than %s of free disk space on the disk which UpdraftPlus is configured to use to create backups. UpdraftPlus could well run out of space. Contact your the operator of your server (e.g. your web hosting company) to resolve this issue.', 'updraftplus'),'35 MB'));
950 }
951
952 public function show_admin_warning_wordpressversion() {
953 $this->show_admin_warning('<strong>'.__('Warning', 'updraftplus').':</strong> '.sprintf(__('UpdraftPlus does not officially support versions of WordPress before %s. It may work for you, but if it does not, then please be aware that no support is available until you upgrade WordPress.', 'updraftplus'), '3.2'));
954 }
955
956 public function show_admin_warning_litespeed() {
957 $this->show_admin_warning('<strong>'.__('Warning', 'updraftplus').':</strong> '.sprintf(__('Your website is hosted using the %s web server.', 'updraftplus'),'LiteSpeed').' <a href="'.apply_filters('updraftplus_com_link', "https://updraftplus.com/faqs/i-am-having-trouble-backing-up-and-my-web-hosting-company-uses-the-litespeed-webserver/").'">'.__('Please consult this FAQ if you have problems backing up.', 'updraftplus').'</a>');
958 }
959
960 public function show_admin_debug_warning() {
961 $this->show_admin_warning('<strong>'.__('Notice', 'updraftplus').':</strong> '.__('UpdraftPlus\'s debug mode is on. You may see debugging notices on this page not just from UpdraftPlus, but from any other plugin installed. Please try to make sure that the notice you are seeing is from UpdraftPlus before you raise a support request.', 'updraftplus').'</a>');
962 }
963
964 public function show_admin_warning_overdue_crons($howmany) {
965 $ret = '<div class="updraftmessage updated"><p>';
966 $ret .= '<strong>'.__('Warning', 'updraftplus').':</strong> '.sprintf(__('WordPress has a number (%d) of scheduled tasks which are overdue. Unless this is a development site, this probably means that the scheduler in your WordPress install is not working.', 'updraftplus'), $howmany).' <a href="'.apply_filters('updraftplus_com_link', "https://updraftplus.com/faqs/scheduler-wordpress-installation-working/").'">'.__('Read this page for a guide to possible causes and how to fix it.', 'updraftplus').'</a>';
967 $ret .= '</p></div>';
968 return $ret;
969 }
970
971 //checking remote storage
972 public function show_admin_warning_dropbox() {
973 $this->show_admin_warning('<strong>'.__('UpdraftPlus notice:', 'updraftplus').'</strong> <a class="updraft_authlink" href="'.UpdraftPlus_Options::admin_page_url().'?page=updraftplus&action=updraftmethod-dropbox-auth&updraftplus_dropboxauth=doit">'.sprintf(__('Follow this link to authorize access to your %s account (you will not be able to back up to %s without it).', 'updraftplus'), 'Dropbox', 'Dropbox').'</a>', 'updated updraft_authenticate_dropbox');
974 }
975
976 public function show_admin_warning_onedrive() {
977 $this->show_admin_warning('<strong>'.__('UpdraftPlus notice:', 'updraftplus').'</strong> <a class="updraft_authlink" href="'.UpdraftPlus_Options::admin_page_url().'?page=updraftplus&action=updraftmethod-onedrive-auth&updraftplus_onedriveauth=doit">'.sprintf(__('Follow this link to authorize access to your %s account (you will not be able to back up to %s without it).', 'updraftplus'), 'OneDrive', 'OneDrive').'</a>', 'updated updraft_authenticate_onedrive');
978 }
979
980 public function show_admin_warning_updraftvault() {
981 $this->show_admin_warning('<strong>'.__('UpdraftPlus notice:', 'updraftplus').'</strong> '.sprintf(__('%s has been chosen for remote storage, but you are not currently connected.', 'updraftplus'), 'UpdraftPlus Vault').' '.__('Go to the remote storage settings in order to connect.', 'updraftplus'), 'updated');
982 }
983
984 public function show_admin_warning_googledrive() {
985 $this->show_admin_warning('<strong>'.__('UpdraftPlus notice:', 'updraftplus').'</strong> <a class="updraft_authlink" href="'.UpdraftPlus_Options::admin_page_url().'?page=updraftplus&action=updraftmethod-googledrive-auth&updraftplus_googleauth=doit">'.sprintf(__('Follow this link to authorize access to your %s account (you will not be able to back up to %s without it).', 'updraftplus'), 'Google Drive', 'Google Drive').'</a>', 'updated updraft_authenticate_googledrive');
986 }
987
988 public function show_admin_warning_googlecloud() {
989 $this->show_admin_warning('<strong>'.__('UpdraftPlus notice:', 'updraftplus').'</strong> <a class="updraft_authlink" href="'.UpdraftPlus_Options::admin_page_url().'?page=updraftplus&action=updraftmethod-googlecloud-auth&updraftplus_googleauth=doit">'.sprintf(__('Follow this link to authorize access to your %s account (you will not be able to back up to %s without it).', 'updraftplus'), 'Google Cloud', 'Google Cloud').'</a>', 'updated updraft_authenticate_googlecloud');
990 }
991
992 // This options filter removes ABSPATH off the front of updraft_dir, if it is given absolutely and contained within it
993 public function prune_updraft_dir_prefix($updraft_dir) {
994 if ('/' == substr($updraft_dir, 0, 1) || "\\" == substr($updraft_dir, 0, 1) || preg_match('/^[a-zA-Z]:/', $updraft_dir)) {
995 $wcd = trailingslashit(WP_CONTENT_DIR);
996 if (strpos($updraft_dir, $wcd) === 0) {
997 $updraft_dir = substr($updraft_dir, strlen($wcd));
998 }
999 # Legacy
1000 // if (strpos($updraft_dir, ABSPATH) === 0) {
1001 // $updraft_dir = substr($updraft_dir, strlen(ABSPATH));
1002 // }
1003 }
1004 return $updraft_dir;
1005 }
1006
1007 public function updraft_download_backup() {
1008
1009 if (empty($_REQUEST['_wpnonce']) || !wp_verify_nonce($_REQUEST['_wpnonce'], 'updraftplus_download')) die;
1010
1011 if (empty($_REQUEST['timestamp']) || !is_numeric($_REQUEST['timestamp']) || empty($_REQUEST['type'])) exit;
1012
1013 $findex = empty($_REQUEST['findex']) ? 0 : (int)$_REQUEST['findex'];
1014 $stage = empty($_REQUEST['stage']) ? '' : $_REQUEST['stage'];
1015 $file_path = empty($_REQUEST['filepath']) ? '' : $_REQUEST['filepath'];
1016
1017 // This call may not actually return, depending upon what mode it is called in
1018 $result = $this->do_updraft_download_backup($findex, $_REQUEST['type'], $_REQUEST['timestamp'], $stage, false, $file_path);
1019
1020 // In theory, if a response was already sent, then Connection: close has been issued, and a Content-Length. However, in https://updraftplus.com/forums/topic/pclzip_err_bad_format-10-invalid-archive-structure/ a browser ignores both of these, and then picks up the second output and complains.
1021 if (empty($result['already_closed'])) echo json_encode($result);
1022
1023 die();
1024 }
1025
1026 // This function may die(), depending on the request being made in $stage
1027 public function do_updraft_download_backup($findex, $type, $timestamp, $stage, $close_connection_callable = false, $file_path = '') {
1028
1029 @set_time_limit(UPDRAFTPLUS_SET_TIME_LIMIT);
1030
1031 global $updraftplus;
1032
1033 // This is a bit ugly; these variables get placed back into $_POST (where they may possibly have come from), so that UpdraftPlus::log() can detect exactly where to log the download status.
1034 $_POST['findex'] = $findex;
1035 $_POST['type'] = $type;
1036 $_POST['timestamp'] = $timestamp;
1037
1038 // Check that it is a known entity type; if not, die
1039 if ('db' != substr($type, 0, 2)) {
1040 $backupable_entities = $updraftplus->get_backupable_file_entities(true);
1041 foreach ($backupable_entities as $t => $info) {
1042 if ($type == $t) $type_match = true;
1043 }
1044 if (empty($type_match)) return array('result' => 'error', 'code' => 'no_such_type');
1045 }
1046
1047 // We already know that no possible entities have an MD5 clash (even after 2 characters)
1048 // Also, there's nothing enforcing a requirement that nonces are hexadecimal
1049 $job_nonce = dechex($timestamp).$findex.substr(md5($type), 0, 3);
1050
1051 // You need a nonce before you can set job data. And we certainly don't yet have one.
1052 $updraftplus->backup_time_nonce($job_nonce);
1053
1054 $debug_mode = UpdraftPlus_Options::get_updraft_option('updraft_debug_mode');
1055
1056 // Set the job type before logging, as there can be different logging destinations
1057 $updraftplus->jobdata_set('job_type', 'download');
1058 $updraftplus->jobdata_set('job_time_ms', $updraftplus->job_time_ms);
1059
1060 // Retrieve the information from our backup history
1061 $backup_history = $updraftplus->get_backup_history();
1062 // Base name
1063 $file = $backup_history[$timestamp][$type];
1064
1065 // Deal with multi-archive sets
1066 if (is_array($file)) $file = $file[$findex];
1067
1068 if (strpos($file_path, '..') !== false) {
1069 error_log("UpdraftPlus_Admin::do_updraft_download_backup : invalid file_path: $file_path");
1070 return array('result' => __('Error: invalid path', 'updraftplus'));
1071 }
1072
1073 if (!empty($file_path)) $file = $file_path;
1074
1075 // Where it should end up being downloaded to
1076 $fullpath = $updraftplus->backups_dir_location().'/'.$file;
1077
1078 if (!empty($file_path) && strpos(realpath($fullpath), realpath($updraftplus->backups_dir_location())) === false) {
1079 error_log("UpdraftPlus_Admin::do_updraft_download_backup : invalid fullpath: $fullpath");
1080 return array('result' => __('Error: invalid path', 'updraftplus'));
1081 }
1082
1083 if (2 == $stage) {
1084 $updraftplus->spool_file($fullpath);
1085 // We only want to remove if it was a temp file from the zip browser
1086 if (!empty($file_path)) @unlink($fullpath);
1087 // Do not return - we do not want the caller to add any output
1088 die;
1089 }
1090
1091 if ('delete' == $stage) {
1092 @unlink($fullpath);
1093 $updraftplus->log("The file has been deleted ($file)");
1094 return array('result' => 'deleted');
1095 }
1096
1097 // TODO: FIXME: Failed downloads may leave log files forever (though they are small)
1098 if ($debug_mode) $updraftplus->logfile_open($updraftplus->nonce);
1099
1100 set_error_handler(array($updraftplus, 'php_error'), E_ALL & ~E_STRICT);
1101
1102 $updraftplus->log("Requested to obtain file: timestamp=$timestamp, type=$type, index=$findex");
1103
1104 $itext = empty($findex) ? '' : $findex;
1105 $known_size = isset($backup_history[$timestamp][$type.$itext.'-size']) ? $backup_history[$timestamp][$type.$itext.'-size'] : 0;
1106
1107 $services = isset($backup_history[$timestamp]['service']) ? $backup_history[$timestamp]['service'] : false;
1108 if (is_string($services)) $services = array($services);
1109
1110 $updraftplus->jobdata_set('service', $services);
1111
1112 // Fetch it from the cloud, if we have not already got it
1113
1114 $needs_downloading = false;
1115
1116 if (!file_exists($fullpath)) {
1117 //if the file doesn't exist and they're using one of the cloud options, fetch it down from the cloud.
1118 $needs_downloading = true;
1119 $updraftplus->log('File does not yet exist locally - needs downloading');
1120 } elseif ($known_size > 0 && filesize($fullpath) < $known_size) {
1121 $updraftplus->log("The file was found locally (".filesize($fullpath).") but did not match the size in the backup history ($known_size) - will resume downloading");
1122 $needs_downloading = true;
1123 } elseif ($known_size > 0) {
1124 $updraftplus->log('The file was found locally and matched the recorded size from the backup history ('.round($known_size/1024,1).' KB)');
1125 } else {
1126 $updraftplus->log('No file size was found recorded in the backup history. We will assume the local one is complete.');
1127 $known_size = filesize($fullpath);
1128 }
1129
1130 // The AJAX responder that updates on progress wants to see this
1131 $updraftplus->jobdata_set('dlfile_'.$timestamp.'_'.$type.'_'.$findex, "downloading:$known_size:$fullpath");
1132
1133 if ($needs_downloading) {
1134
1135 // Update the "last modified" time to dissuade any other instances from thinking that no downloaders are active
1136 @touch($fullpath);
1137
1138 $msg = array(
1139 'result' => 'needs_download',
1140 'request' => array(
1141 'type' => $type,
1142 'timestamp' => $timestamp,
1143 'findex' => $findex
1144 )
1145 );
1146
1147 if ($close_connection_callable && is_callable($close_connection_callable)) {
1148 call_user_func($close_connection_callable, $msg);
1149 } else {
1150 $updraftplus->close_browser_connection(json_encode($msg));
1151 }
1152
1153 $is_downloaded = false;
1154 add_action('http_request_args', array($updraftplus, 'modify_http_options'));
1155 foreach ($services as $service) {
1156 if ($is_downloaded) continue;
1157 $download = $this->download_file($file, $service);
1158 if (is_readable($fullpath) && $download !== false) {
1159 clearstatcache();
1160 $updraftplus->log('Remote fetch was successful (file size: '.round(filesize($fullpath)/1024,1).' KB)');
1161 $is_downloaded = true;
1162 } else {
1163 clearstatcache();
1164 if (0 === @filesize($fullpath)) @unlink($fullpath);
1165 $updraftplus->log('Remote fetch failed');
1166 }
1167 }
1168 remove_action('http_request_args', array($updraftplus, 'modify_http_options'));
1169 }
1170
1171 // Now, be ready to spool the thing to the browser
1172 if (is_file($fullpath) && is_readable($fullpath)) {
1173
1174 // That message is then picked up by the AJAX listener
1175 $updraftplus->jobdata_set('dlfile_'.$timestamp.'_'.$type.'_'.$findex, 'downloaded:'.filesize($fullpath).":$fullpath");
1176
1177 $result = 'downloaded';
1178
1179 } else {
1180
1181 $updraftplus->jobdata_set('dlfile_'.$timestamp.'_'.$type.'_'.$findex, 'failed');
1182 $updraftplus->jobdata_set('dlerrors_'.$timestamp.'_'.$type.'_'.$findex, $updraftplus->errors);
1183 $updraftplus->log('Remote fetch failed. File '.$fullpath.' did not exist or was unreadable. If you delete local backups then remote retrieval may have failed.');
1184
1185 $result = 'download_failed';
1186 }
1187
1188 restore_error_handler();
1189
1190 @fclose($updraftplus->logfile_handle);
1191 if (!$debug_mode) @unlink($updraftplus->logfile_name);
1192
1193 // The browser connection was possibly already closed, but not necessarily
1194 return array('result' => $result, 'already_closed' => $needs_downloading);
1195
1196 }
1197
1198 /**
1199 * Downloads a specified file into UD's directory
1200 *
1201 * @param String $file - The name of the file
1202 * @param String $service - The identifier of the service to download from. You cannot pass multiple services.
1203 *
1204 * @return Boolean - Whether the operation succeeded. Inherited from the storage module's download() method. N.B. At the time of writing it looks like not all modules necessarily return true upon success; but false can be relied upon for detecting failure.
1205 */
1206 private function download_file($file, $service) {
1207
1208 global $updraftplus;
1209
1210 @set_time_limit(UPDRAFTPLUS_SET_TIME_LIMIT);
1211
1212 $updraftplus->log("Requested file from remote service: $service: $file");
1213
1214 $method_include = UPDRAFTPLUS_DIR.'/methods/'.$service.'.php';
1215 if (file_exists($method_include)) require_once($method_include);
1216
1217 $objname = "UpdraftPlus_BackupModule_${service}";
1218 if (method_exists($objname, "download")) {
1219
1220 try {
1221 $remote_obj = new $objname;
1222 return $remote_obj->download($file);
1223 } catch (Exception $e) {
1224 $log_message = 'Exception ('.get_class($e).') occurred during download: '.$e->getMessage().' (Code: '.$e->getCode().', line '.$e->getLine().' in '.$e->getFile().')';
1225 $updraftplus->log($log_message);
1226 error_log($log_message);
1227 $updraftplus->log(sprintf(__('A PHP exception (%s) has occurred: %s', 'updraftplus'), get_class($e), $e->getMessage()), 'error');
1228 return false;
1229 // @codingStandardsIgnoreLine
1230 } catch (Error $e) {
1231 $log_message = 'PHP Fatal error ('.get_class($e).') has occurred. Error Message: '.$e->getMessage().' (Code: '.$e->getCode().', line '.$e->getLine().' in '.$e->getFile().')';
1232 $updraftplus->log($log_message);
1233 error_log($log_message);
1234 $updraftplus->log(sprintf(__('A PHP fatal error (%s) has occurred: %s', 'updraftplus'), get_class($e), $e->getMessage()), 'error');
1235 return false;
1236 }
1237 } else {
1238 $updraftplus->log("Automatic backup restoration is not available with the method: $service.");
1239 $updraftplus->log("$file: ".sprintf(__("The backup archive for this file could not be found. The remote storage method in use (%s) does not allow us to retrieve files. To perform any restoration using UpdraftPlus, you will need to obtain a copy of this file and place it inside UpdraftPlus's working folder", 'updraftplus'), $service)." (".$this->prune_updraft_dir_prefix($updraftplus->backups_dir_location()).")", 'error');
1240 return false;
1241 }
1242
1243 }
1244
1245 // This is used as a callback
1246 public function _updraftplus_background_operation_started($msg) {
1247 global $updraftplus;
1248 // The extra spaces are because of a bug seen on one server in handling of non-ASCII characters; see HS#11739
1249 $updraftplus->close_browser_connection(json_encode($msg).' ');
1250 }
1251
1252 public function updraft_ajax_handler() {
1253
1254 global $updraftplus;
1255
1256 $nonce = empty($_REQUEST['nonce']) ? '' : $_REQUEST['nonce'];
1257
1258 if (!wp_verify_nonce($nonce, 'updraftplus-credentialtest-nonce') || empty($_REQUEST['subaction'])) die('Security check');
1259
1260 // Mitigation in case the nonce leaked to an unauthorised user
1261 if ('dismissautobackup' == $_REQUEST['subaction']) {
1262 if (!current_user_can('update_plugins') && !current_user_can('update_themes')) return;
1263 } elseif ('dismissexpiry' == $_REQUEST['subaction'] || 'dismissdashnotice' == $_REQUEST['subaction']) {
1264 if (!current_user_can('update_plugins')) return;
1265 } else {
1266 if (!UpdraftPlus_Options::user_can_manage()) return;
1267 }
1268
1269 $subaction = $_REQUEST['subaction'];
1270
1271 // All others use _POST
1272 $data_in_get = array('get_log', 'get_fragment');
1273
1274 // UpdraftPlus_WPAdmin_Commands extends UpdraftPlus_Commands - i.e. all commands are in there
1275 if (!class_exists('UpdraftPlus_WPAdmin_Commands')) require_once(UPDRAFTPLUS_DIR.'/includes/class-wpadmin-commands.php');
1276 $commands = new UpdraftPlus_WPAdmin_Commands($this);
1277
1278 if (method_exists($commands, $subaction)) {
1279
1280 $data = in_array($subaction, $data_in_get) ? $_GET : $_POST;
1281 // TODO: Once all commands come through here and through updraft_send_command(), the data should always come from this attribute (once updraft_send_command() is modified appropriately).
1282 if (isset($data['action_data'])) $data = $data['action_data'];
1283 $results = call_user_func(array($commands, $subaction), $data);
1284
1285 if (is_wp_error($results)) {
1286 $results = array(
1287 'result' => false,
1288 'error_code' => $results->get_error_code(),
1289 'error_message' => $results->get_error_message(),
1290 'error_data' => $results->get_error_data(),
1291 );
1292 }
1293
1294 if (is_string($results)) {
1295 // A handful of legacy methods, and some which are directly the source for iframes, for which JSON is not appropriate.
1296 echo $results;
1297 } else {
1298 echo json_encode($results);
1299 }
1300 die;
1301
1302 }
1303
1304 // Below are all the commands not ported over into class-commands.php or class-wpadmin-commands.php
1305
1306 if ('activejobs_list' == $subaction) {
1307
1308 // N.B. Also called from autobackup.php
1309 // TODO: This should go into UpdraftPlus_Commands, once the add-ons have been ported to use updraft_send_command()
1310 echo json_encode($this->get_activejobs_list($_GET));
1311
1312 } elseif ('httpget' == $_REQUEST['subaction']) {
1313
1314 // httpget
1315 $curl = empty($_REQUEST['curl']) ? false : true;
1316 echo $this->http_get($_REQUEST['uri'], $curl);
1317
1318 } elseif ('doaction' == $_REQUEST['subaction'] && !empty($_REQUEST['subsubaction']) && 'updraft_' == substr($_REQUEST['subsubaction'], 0, 8)) {
1319
1320 // These generally echo and die - they will need further work to port to one of the command classes. Some may already have equivalents in UpdraftPlus_Commands, if they are used from UpdraftCentral.
1321 do_action($_REQUEST['subsubaction']);
1322 } else {
1323 // These can be removed after a couple of releases
1324 include(UPDRAFTPLUS_DIR.'/includes/deprecated-actions.php');
1325 }
1326
1327 die;
1328
1329 }
1330
1331 /**
1332 * Run a credentials test for the indicated remote storage module
1333 *
1334 * @param Array $test_settings - the test parameters, including the method itself indicated in the key 'method'
1335 * @param Boolean $return_instead_of_echo - whether to return or echo the results. N.B. More than just the results to echo will be returned
1336 *
1337 * @return Array|Void - the results, if they are being returned (rather than echoed). Keys: 'output' (the output), 'data' (other data)
1338 */
1339 public function do_credentials_test($test_settings, $return_instead_of_echo = false) {
1340
1341 $method = (!empty($test_settings['method']) && preg_match("/^[a-z0-9]+$/", $test_settings['method'])) ? $test_settings['method'] : "";
1342
1343 $objname = "UpdraftPlus_BackupModule_$method";
1344
1345 $this->logged = array();
1346 # TODO: Add action for WP HTTP SSL stuff
1347 set_error_handler(array($this, 'get_php_errors'), E_ALL & ~E_STRICT);
1348
1349 if (!class_exists($objname)) include_once(UPDRAFTPLUS_DIR."/methods/$method.php");
1350
1351 $ret = '';
1352 $data = null;
1353
1354 # TODO: Add action for WP HTTP SSL stuff
1355 if (method_exists($objname, "credentials_test")) {
1356 $obj = new $objname;
1357 if ($return_instead_of_echo) ob_start();
1358 $data = $obj->credentials_test($test_settings);
1359 if ($return_instead_of_echo) $ret .= ob_get_clean();
1360 }
1361
1362 if (count($this->logged) >0) {
1363 $ret .= "\n\n".__('Messages:', 'updraftplus')."\n";
1364 foreach ($this->logged as $err) {
1365 $ret .= "* $err\n";
1366 }
1367 if (!$return_instead_of_echo) echo $ret;
1368 }
1369 restore_error_handler();
1370
1371 if ($return_instead_of_echo) return array('output' => $ret, 'data' => $data);
1372
1373 }
1374
1375 // Relevant options (array keys): backup_timestamp, delete_remote, [remote_delete_limit]
1376 public function delete_set($opts) {
1377
1378 global $updraftplus;
1379
1380 $backups = $updraftplus->get_backup_history();
1381 $timestamps = (string)$opts['backup_timestamp'];
1382
1383 $remote_delete_limit = (isset($opts['remote_delete_limit']) && $opts['remote_delete_limit'] > 0) ? (int)$opts['remote_delete_limit'] : PHP_INT_MAX;
1384
1385 $timestamps = explode(',', $timestamps);
1386 $delete_remote = empty($opts['delete_remote']) ? false : true;
1387
1388 // You need a nonce before you can set job data. And we certainly don't yet have one.
1389 $updraftplus->backup_time_nonce();
1390 // Set the job type before logging, as there can be different logging destinations
1391 $updraftplus->jobdata_set('job_type', 'delete');
1392 $updraftplus->jobdata_set('job_time_ms', $updraftplus->job_time_ms);
1393
1394 if (UpdraftPlus_Options::get_updraft_option('updraft_debug_mode')) {
1395 $updraftplus->logfile_open($updraftplus->nonce);
1396 set_error_handler(array($updraftplus, 'php_error'), E_ALL & ~E_STRICT);
1397 }
1398
1399 $updraft_dir = $updraftplus->backups_dir_location();
1400 $backupable_entities = $updraftplus->get_backupable_file_entities(true, true);
1401
1402 $local_deleted = 0;
1403 $remote_deleted = 0;
1404 $sets_removed = 0;
1405
1406 foreach ($timestamps as $i => $timestamp) {
1407
1408 if (!isset($backups[$timestamp])) {
1409 return array('result' => 'error', 'message' => __('Backup set not found', 'updraftplus'));
1410 }
1411
1412 $nonce = isset($backups[$timestamp]['nonce']) ? $backups[$timestamp]['nonce'] : '';
1413
1414 $delete_from_service = array();
1415
1416 if ($delete_remote) {
1417 // Locate backup set
1418 if (isset($backups[$timestamp]['service'])) {
1419 $services = is_string($backups[$timestamp]['service']) ? array($backups[$timestamp]['service']) : $backups[$timestamp]['service'];
1420 if (is_array($services)) {
1421 foreach ($services as $service) {
1422 if ($service && $service != 'none' && $service != 'email') $delete_from_service[] = $service;
1423 }
1424 }
1425 }
1426 }
1427
1428 $files_to_delete = array();
1429 foreach ($backupable_entities as $key => $ent) {
1430 if (isset($backups[$timestamp][$key])) {
1431 $files_to_delete[$key] = $backups[$timestamp][$key];
1432 }
1433 }
1434 // Delete DB
1435 foreach ($backups[$timestamp] as $key => $value){
1436 if ('db' == strtolower(substr($key, 0, 2)) && '-size' != substr($key, -5, 5)) {
1437 $files_to_delete[$key] = $backups[$timestamp][$key];
1438 }
1439 }
1440
1441 // Also delete the log
1442 if ($nonce && !UpdraftPlus_Options::get_updraft_option('updraft_debug_mode')) {
1443 $files_to_delete['log'] = "log.$nonce.txt";
1444 }
1445
1446 add_action('http_request_args', array($updraftplus, 'modify_http_options'));
1447
1448 foreach ($files_to_delete as $key => $files) {
1449
1450 if (is_string($files)) {
1451 $was_string = true;
1452 $files = array($files);
1453 } else {
1454 $was_string = false;
1455 }
1456
1457 foreach ($files as $file) {
1458 if (is_file($updraft_dir.'/'.$file) && @unlink($updraft_dir.'/'.$file)) $local_deleted++;
1459 }
1460
1461 if ('log' != $key && count($delete_from_service) > 0) {
1462 foreach ($delete_from_service as $service) {
1463 if ('email' == $service) continue;
1464 if (file_exists(UPDRAFTPLUS_DIR."/methods/$service.php")) require_once(UPDRAFTPLUS_DIR."/methods/$service.php");
1465 $objname = "UpdraftPlus_BackupModule_".$service;
1466 $deleted = -1;
1467 if (class_exists($objname)) {
1468 # TODO: Re-use the object (i.e. prevent repeated connection setup/teardown)
1469 $remote_obj = new $objname;
1470
1471 foreach ($files as $index => $file) {
1472 if ($remote_deleted == $remote_delete_limit) {
1473 return $this->remove_backup_set_cleanup(false, $backups, $local_deleted, $remote_deleted, $sets_removed);
1474 }
1475
1476 $deleted = $remote_obj->delete($file);
1477
1478 if ($deleted === -1) {
1479 //echo __('Did not know how to delete from this cloud service.', 'updraftplus');
1480 } elseif ($deleted !== false) {
1481 $remote_deleted++;
1482 }
1483
1484 $itext = $index ? (string)$index : '';
1485 if ($was_string) {
1486 unset($backups[$timestamp][$key]);
1487 if ('db' == strtolower(substr($key, 0, 2))) unset($backups[$timestamp][$key][$index.'-size']);
1488 } else {
1489 unset($backups[$timestamp][$key][$index]);
1490 unset($backups[$timestamp][$key.$itext.'-size']);
1491 if (empty($backups[$timestamp][$key])) unset($backups[$timestamp][$key]);
1492 }
1493 if (isset($backups[$timestamp]['checksums']) && is_array($backups[$timestamp]['checksums'])) {
1494 foreach (array_keys($backups[$timestamp]['checksums']) as $algo) {
1495 unset($backups[$timestamp]['checksums'][$algo][$key.$index]);
1496 }
1497 }
1498
1499 // If we don't save the array back, then the above section will fire again for the same files - and the remote storage will be requested to delete already-deleted files, which then means no time is actually saved by the browser-backend loop method.
1500 UpdraftPlus_Options::update_updraft_option('updraft_backup_history', $backups);
1501
1502 }
1503 }
1504 }
1505 }
1506 }
1507
1508 unset($backups[$timestamp]);
1509 UpdraftPlus_Options::update_updraft_option('updraft_backup_history', $backups);
1510 $sets_removed++;
1511 }
1512
1513
1514 return $this->remove_backup_set_cleanup(true, $backups, $local_deleted, $remote_deleted, $sets_removed);
1515
1516 }
1517
1518 public function remove_backup_set_cleanup($delete_complete, $backups, $local_deleted, $remote_deleted, $sets_removed) {
1519
1520 global $updraftplus;
1521
1522 remove_action('http_request_args', array($updraftplus, 'modify_http_options'));
1523
1524 UpdraftPlus_Options::update_updraft_option('updraft_backup_history', $backups);
1525
1526 $updraftplus->log("Local files deleted: $local_deleted. Remote files deleted: $remote_deleted");
1527
1528 if ($delete_complete) {
1529 $set_message = __('Backup sets removed:', 'updraftplus');
1530 $local_message = __('Local files deleted:', 'updraftplus');
1531 $remote_message = __('Remote files deleted:', 'updraftplus');
1532
1533 if (UpdraftPlus_Options::get_updraft_option('updraft_debug_mode')) {
1534 restore_error_handler();
1535 }
1536
1537 return array('result' => 'success', 'set_message' => $set_message, 'local_message' => $local_message, 'remote_message' => $remote_message, 'backup_sets' => $sets_removed, 'backup_local' => $local_deleted, 'backup_remote' => $remote_deleted);
1538 } else {
1539
1540 return array('result' => 'continue', 'backup_local' => $local_deleted, 'backup_remote' => $remote_deleted, 'backup_sets' => $sets_removed);
1541 }
1542 }
1543
1544 public function get_history_status($rescan, $remotescan) {
1545
1546 global $updraftplus;
1547
1548 if ($rescan) $messages = $updraftplus->rebuild_backup_history($remotescan);
1549 $backup_history = UpdraftPlus_Options::get_updraft_option('updraft_backup_history');
1550 $backup_history = (is_array($backup_history)) ? $backup_history : array();
1551 $output = $this->existing_backup_table($backup_history);
1552 $data = array();
1553
1554 if (!empty($messages) && is_array($messages)) {
1555 $noutput = '<div style="margin-left: 100px; margin-top: 10px;"><ul style="list-style: disc inside;">';
1556 foreach ($messages as $msg) {
1557 $noutput .= '<li>'.(empty($msg['desc']) ? '' : $msg['desc'].': ').'<em>'.$msg['message'].'</em></li>';
1558 if (!empty($msg['data'])) {
1559 if (!empty($msg['desc'])) {
1560 $data['desc'] = $msg['data'];
1561 } else {
1562 // At the time of authorship, this code branch is not known to be used
1563 $data[] = $msg['data'];
1564 }
1565 }
1566 }
1567 $noutput .= '</ul></div>';
1568 $output = $noutput.$output;
1569 }
1570
1571 $logs_exist = (false !== strpos($output, 'downloadlog'));
1572 if (!$logs_exist) {
1573 list($mod_time, $log_file, $nonce) = $updraftplus->last_modified_log();
1574 if ($mod_time) $logs_exist = true;
1575 }
1576
1577 return apply_filters('updraftplus_get_history_status_result', array(
1578 'n' => sprintf(__('Existing Backups', 'updraftplus').' (%d)', count($backup_history)),
1579 't' => $output,
1580 'data' => $data,
1581 'cksum' => md5($output),
1582 'logs_exist' => $logs_exist,
1583 ));
1584 }
1585
1586 public function get_disk_space_used($entity) {
1587 global $updraftplus;
1588 if ('updraft' == $entity) {
1589 return $this->recursive_directory_size($updraftplus->backups_dir_location());
1590 } else {
1591 $backupable_entities = $updraftplus->get_backupable_file_entities(true, false);
1592 if ('all' == $entity) {
1593 $total_size = 0;
1594 foreach ($backupable_entities as $entity => $data) {
1595 # Might be an array
1596 $basedir = $backupable_entities[$entity];
1597 $dirs = apply_filters('updraftplus_dirlist_'.$entity, $basedir);
1598 $size = $this->recursive_directory_size($dirs, $updraftplus->get_exclude($entity), $basedir, 'numeric');
1599 if (is_numeric($size) && $size>0) $total_size += $size;
1600 }
1601 return $updraftplus->convert_numeric_size_to_text($total_size);
1602 } elseif (!empty($backupable_entities[$entity])) {
1603 # Might be an array
1604 $basedir = $backupable_entities[$entity];
1605 $dirs = apply_filters('updraftplus_dirlist_'.$entity, $basedir);
1606 return $this->recursive_directory_size($dirs, $updraftplus->get_exclude($entity), $basedir);
1607 }
1608 }
1609 return __('Error', 'updraftplus');
1610 }
1611
1612 public function activejobs_delete($job_id) {
1613
1614 if (preg_match("/^[0-9a-f]{12}$/", $job_id)) {
1615
1616 global $updraftplus;
1617 $cron = get_option('cron');
1618 $found_it = false;
1619
1620 $updraft_dir = $updraftplus->backups_dir_location();
1621 if (file_exists($updraft_dir.'/log.'.$job_id.'.txt')) touch($updraft_dir.'/deleteflag-'.$job_id.'.txt');
1622
1623 foreach ($cron as $time => $job) {
1624 if (isset($job['updraft_backup_resume'])) {
1625 foreach ($job['updraft_backup_resume'] as $hook => $info) {
1626 if (isset($info['args'][1]) && $info['args'][1] == $job_id) {
1627 $args = $cron[$time]['updraft_backup_resume'][$hook]['args'];
1628 wp_unschedule_event($time, 'updraft_backup_resume', $args);
1629 if (!$found_it) return array('ok' => 'Y', 'c' => 'deleted', 'm' => __('Job deleted', 'updraftplus'));
1630 $found_it = true;
1631 }
1632 }
1633 }
1634 }
1635 }
1636
1637 if (!$found_it) return array('ok' => 'N', 'c' => 'not_found', 'm' => __('Could not find that job - perhaps it has already finished?', 'updraftplus'));
1638
1639 }
1640
1641 // Input: an array of items
1642 // Each item is in the format: <base>,<timestamp>,<type>(,<findex>)
1643 // The 'base' is not for us: we just pass it straight back
1644 public function get_download_statuses($downloaders) {
1645 global $updraftplus;
1646 $download_status = array();
1647 foreach ($downloaders as $downloader) {
1648 # prefix, timestamp, entity, index
1649 if (preg_match('/^([^,]+),(\d+),([-a-z]+|db[0-9]+),(\d+)$/', $downloader, $matches)) {
1650 $findex = (empty($matches[4])) ? '0' : $matches[4];
1651 $updraftplus->nonce = dechex($matches[2]).$findex.substr(md5($matches[3]), 0, 3);
1652 $updraftplus->jobdata_reset();
1653 $status = $this->download_status($matches[2], $matches[3], $matches[4]);
1654 if (is_array($status)) {
1655 $status['base'] = $matches[1];
1656 $status['timestamp'] = $matches[2];
1657 $status['what'] = $matches[3];
1658 $status['findex'] = $findex;
1659 $download_status[] = $status;
1660 }
1661 }
1662 }
1663 return $download_status;
1664 }
1665
1666 public function get_activejobs_list($request) {
1667
1668 global $updraftplus;
1669
1670 $download_status = empty($request['downloaders']) ? array(): $this->get_download_statuses(explode(':', $request['downloaders']));
1671
1672 if (!empty($request['oneshot'])) {
1673 $job_id = get_site_option('updraft_oneshotnonce', false);
1674 // print_active_job() for one-shot jobs that aren't in cron
1675 $active_jobs = (false === $job_id) ? '' : $this->print_active_job($job_id, true);
1676 } elseif (!empty($request['thisjobonly'])) {
1677 // print_active_jobs() is for resumable jobs where we want the cron info to be included in the output
1678 $active_jobs = $this->print_active_jobs($request['thisjobonly']);
1679 } else {
1680 $active_jobs = $this->print_active_jobs();
1681 }
1682
1683 $logupdate_array = array();
1684 if (!empty($request['log_fetch'])) {
1685 if (isset($request['log_nonce'])) {
1686 $log_nonce = $request['log_nonce'];
1687 $log_pointer = isset($request['log_pointer']) ? absint($request['log_pointer']) : 0;
1688 $logupdate_array = $this->fetch_log($log_nonce, $log_pointer);
1689 }
1690 }
1691
1692 return array(
1693 // We allow the front-end to decide what to do if there's nothing logged - we used to (up to 1.11.29) send a pre-defined message
1694 'l' => htmlspecialchars(UpdraftPlus_Options::get_updraft_option('updraft_lastmessage', '')),
1695 'j' => $active_jobs,
1696 'ds' => $download_status,
1697 'u' => $logupdate_array
1698 );
1699
1700 }
1701
1702 public function request_backupnow($request, $close_connection_callable = false) {
1703 global $updraftplus;
1704
1705 $backupnow_nocloud = (empty($request['backupnow_nocloud'])) ? false : true;
1706 $event = (!empty($request['backupnow_nofiles'])) ? 'updraft_backupnow_backup_database' : ((!empty($request['backupnow_nodb'])) ? 'updraft_backupnow_backup' : 'updraft_backupnow_backup_all');
1707
1708 // The call to backup_time_nonce() allows us to know the nonce in advance, and return it
1709 $nonce = $updraftplus->backup_time_nonce();
1710
1711 $msg = array(
1712 'nonce' => $nonce,
1713 'm' => '<strong>'.__('Start backup', 'updraftplus').':</strong> '.htmlspecialchars(__('OK. You should soon see activity in the "Last log message" field below.', 'updraftplus'))
1714 );
1715
1716 if ($close_connection_callable && is_callable($close_connection_callable)) {
1717 call_user_func($close_connection_callable, $msg);
1718 } else {
1719 $updraftplus->close_browser_connection(json_encode($msg));
1720 }
1721
1722 $options = array('nocloud' => $backupnow_nocloud, 'use_nonce' => $nonce);
1723 if (!empty($request['onlythisfileentity']) && is_string($request['onlythisfileentity'])) {
1724 // Something to see in the 'last log' field when it first appears, before the backup actually starts
1725 $updraftplus->log(__('Start backup', 'updraftplus'));
1726 $options['restrict_files_to_override'] = explode(',', $request['onlythisfileentity']);
1727 }
1728
1729 if (!empty($request['extradata'])) {
1730 $options['extradata'] = $request['extradata'];
1731 }
1732
1733 do_action($event, apply_filters('updraft_backupnow_options', $options, $request));
1734 }
1735
1736 /**
1737 * Get the contents of a log file
1738 *
1739 * @param String $backup_nonce - the backup id; or empty, for the most recently modified
1740 * @param Integer $log_pointer - the byte count to fetch from
1741 * @param String $output_format - the format to return in; allowed as 'html' (which will escape HTML entities in what is returned) and 'raw'
1742 *
1743 * @return String
1744 */
1745 public function fetch_log($backup_nonce = '', $log_pointer = 0, $output_format = 'html') {
1746 global $updraftplus;
1747
1748 if (empty($backup_nonce)) {
1749 list($mod_time, $log_file, $nonce) = $updraftplus->last_modified_log();
1750 } else {
1751 $nonce = $backup_nonce;
1752 }
1753
1754 if (!preg_match('/^[0-9a-f]+$/', $nonce)) die('Security check');
1755
1756 $log_content = '';
1757 $new_pointer = $log_pointer;
1758
1759 if (!empty($nonce)) {
1760 $updraft_dir = $updraftplus->backups_dir_location();
1761
1762 $potential_log_file = $updraft_dir."/log.".$nonce.".txt";
1763
1764 if (is_readable($potential_log_file)){
1765
1766 $templog_array = array();
1767 $log_file = fopen($potential_log_file, "r");
1768 if ($log_pointer > 0) fseek($log_file, $log_pointer);
1769
1770 while (($buffer = fgets($log_file, 4096)) !== false) {
1771 $templog_array[] = $buffer;
1772 }
1773 if (!feof($log_file)) {
1774 $templog_array[] = __('Error: unexpected file read fail', 'updraftplus');
1775 }
1776
1777 $new_pointer = ftell($log_file);
1778 $log_content = implode("", $templog_array);
1779
1780
1781 } else {
1782 $log_content .= __('The log file could not be read.', 'updraftplus');
1783 }
1784
1785 } else {
1786 $log_content .= __('The log file could not be read.', 'updraftplus');
1787 }
1788
1789 if ('html' == $output_format) $log_content = htmlspecialchars($log_content);
1790
1791 $ret_array = array(
1792 'log' => $log_content,
1793 'nonce' => $nonce,
1794 'pointer' => $new_pointer
1795 );
1796
1797 return $ret_array;
1798 }
1799
1800 public function howmany_overdue_crons() {
1801 $how_many_overdue = 0;
1802 if (function_exists('_get_cron_array') || (is_file(ABSPATH.WPINC.'/cron.php') && include_once(ABSPATH.WPINC.'/cron.php') && function_exists('_get_cron_array'))) {
1803 $crons = _get_cron_array();
1804 if (is_array($crons)) {
1805 $timenow = time();
1806 foreach ($crons as $jt => $job) {
1807 if ($jt < $timenow) {
1808 $how_many_overdue++;
1809 }
1810 }
1811 }
1812 }
1813 return $how_many_overdue;
1814 }
1815
1816 public function get_php_errors($errno, $errstr, $errfile, $errline) {
1817 global $updraftplus;
1818 if (0 == error_reporting()) return true;
1819 $logline = $updraftplus->php_error_to_logline($errno, $errstr, $errfile, $errline);
1820 if (false !== $logline) $this->logged[] = $logline;
1821 # Don't pass it up the chain (since it's going to be output to the user always)
1822 return true;
1823 }
1824
1825 private function download_status($timestamp, $type, $findex) {
1826 global $updraftplus;
1827 $response = array('m' => $updraftplus->jobdata_get('dlmessage_'.$timestamp.'_'.$type.'_'.$findex).'<br>');
1828 if ($file = $updraftplus->jobdata_get('dlfile_'.$timestamp.'_'.$type.'_'.$findex)) {
1829 if ('failed' == $file) {
1830 $response['e'] = __('Download failed', 'updraftplus').'<br>';
1831 $response['failed'] = true;
1832 $errs = $updraftplus->jobdata_get('dlerrors_'.$timestamp.'_'.$type.'_'.$findex);
1833 if (is_array($errs) && !empty($errs)) {
1834 $response['e'] .= '<ul class="disc">';
1835 foreach ($errs as $err) {
1836 if (is_array($err)) {
1837 $response['e'] .= '<li>'.htmlspecialchars($err['message']).'</li>';
1838 } else {
1839 $response['e'] .= '<li>'.htmlspecialchars($err).'</li>';
1840 }
1841 }
1842 $response['e'] .= '</ul>';
1843 }
1844 } elseif (preg_match('/^downloaded:(\d+):(.*)$/', $file, $matches) && file_exists($matches[2])) {
1845 $response['p'] = 100;
1846 $response['f'] = $matches[2];
1847 $response['s'] = (int)$matches[1];
1848 $response['t'] = (int)$matches[1];
1849 $response['m'] = __('File ready.', 'updraftplus');
1850 if ('db' != substr($type, 0, 2)) $response['can_show_contents'] = true;
1851 } elseif (preg_match('/^downloading:(\d+):(.*)$/', $file, $matches) && file_exists($matches[2])) {
1852 // Convert to bytes
1853 $response['f'] = $matches[2];
1854 $total_size = (int)max($matches[1], 1);
1855 $cur_size = filesize($matches[2]);
1856 $response['s'] = $cur_size;
1857 $file_age = time() - filemtime($matches[2]);
1858 if ($file_age > 20) $response['a'] = time() - filemtime($matches[2]);
1859 $response['t'] = $total_size;
1860 $response['m'] .= __("Download in progress", 'updraftplus').' ('.round($cur_size/1024).' / '.round(($total_size/1024)).' KB)';
1861 $response['p'] = round(100*$cur_size/$total_size);
1862 } else {
1863 $response['m'] .= __('No local copy present.', 'updraftplus');
1864 $response['p'] = 0;
1865 $response['s'] = 0;
1866 $response['t'] = 1;
1867 }
1868 }
1869 return $response;
1870 }
1871
1872 public function upload_dir($uploads) {
1873 global $updraftplus;
1874 $updraft_dir = $updraftplus->backups_dir_location();
1875 if (is_writable($updraft_dir)) $uploads['path'] = $updraft_dir;
1876 return $uploads;
1877 }
1878
1879 // We do actually want to over-write
1880 public function unique_filename_callback($dir, $name, $ext) {
1881 return $name.$ext;
1882 }
1883
1884 public function sanitize_file_name($filename) {
1885 // WordPress 3.4.2 on multisite (at least) adds in an unwanted underscore
1886 return preg_replace('/-db(.*)\.gz_\.crypt$/', '-db$1.gz.crypt', $filename);
1887 }
1888
1889 public function plupload_action() {
1890 // check ajax nonce
1891
1892 global $updraftplus;
1893 @set_time_limit(UPDRAFTPLUS_SET_TIME_LIMIT);
1894
1895 if (!UpdraftPlus_Options::user_can_manage()) exit;
1896 check_ajax_referer('updraft-uploader');
1897
1898 $updraft_dir = $updraftplus->backups_dir_location();
1899 if (!@$updraftplus->really_is_writable($updraft_dir)) {
1900 echo json_encode(array('e' => sprintf(__("Backup directory (%s) is not writable, or does not exist.", 'updraftplus'), $updraft_dir).' '.__('You will find more information about this in the Settings section.', 'updraftplus')));
1901 exit;
1902 }
1903
1904 add_filter('upload_dir', array($this, 'upload_dir'));
1905 add_filter('sanitize_file_name', array($this, 'sanitize_file_name'));
1906 // handle file upload
1907
1908 $farray = array('test_form' => true, 'action' => 'plupload_action');
1909
1910 $farray['test_type'] = false;
1911 $farray['ext'] = 'x-gzip';
1912 $farray['type'] = 'application/octet-stream';
1913
1914 if (!isset($_POST['chunks'])) {
1915 $farray['unique_filename_callback'] = array($this, 'unique_filename_callback');
1916 }
1917
1918 $status = wp_handle_upload(
1919 $_FILES['async-upload'],
1920 $farray
1921 );
1922 remove_filter('upload_dir', array($this, 'upload_dir'));
1923 remove_filter('sanitize_file_name', array($this, 'sanitize_file_name'));
1924
1925 if (isset($status['error'])) {
1926 echo json_encode(array('e' => $status['error']));
1927 exit;
1928 }
1929
1930 // If this was the chunk, then we should instead be concatenating onto the final file
1931 if (isset($_POST['chunks']) && isset($_POST['chunk']) && preg_match('/^[0-9]+$/',$_POST['chunk'])) {
1932 $final_file = basename($_POST['name']);
1933 if (!rename($status['file'], $updraft_dir.'/'.$final_file.'.'.$_POST['chunk'].'.zip.tmp')) {
1934 @unlink($status['file']);
1935 echo json_encode(array('e' => sprintf(__('Error: %s', 'updraftplus'), __('This file could not be uploaded', 'updraftplus'))));
1936 exit;
1937 }
1938 $status['file'] = $updraft_dir.'/'.$final_file.'.'.$_POST['chunk'].'.zip.tmp';
1939
1940 // Final chunk? If so, then stich it all back together
1941 if ($_POST['chunk'] == $_POST['chunks']-1) {
1942 if ($wh = fopen($updraft_dir.'/'.$final_file, 'wb')) {
1943 for ($i=0 ; $i<$_POST['chunks']; $i++) {
1944 $rf = $updraft_dir.'/'.$final_file.'.'.$i.'.zip.tmp';
1945 if ($rh = fopen($rf, 'rb')) {
1946 while ($line = fread($rh, 32768)) fwrite($wh, $line);
1947 fclose($rh);
1948 @unlink($rf);
1949 }
1950 }
1951 fclose($wh);
1952 $status['file'] = $updraft_dir.'/'.$final_file;
1953 if ('.tar' == substr($final_file, -4, 4)) {
1954 if (file_exists($status['file'].'.gz')) unlink($status['file'].'.gz');
1955 if (file_exists($status['file'].'.bz2')) unlink($status['file'].'.bz2');
1956 } elseif ('.tar.gz' == substr($final_file, -7, 7)) {
1957 if (file_exists(substr($status['file'], 0, strlen($status['file'])-3))) unlink(substr($status['file'], 0, strlen($status['file'])-3));
1958 if (file_exists(substr($status['file'], 0, strlen($status['file'])-3).'.bz2')) unlink(substr($status['file'], 0, strlen($status['file'])-3).'.bz2');
1959 } elseif ('.tar.bz2' == substr($final_file, -8, 8)) {
1960 if (file_exists(substr($status['file'], 0, strlen($status['file'])-4))) unlink(substr($status['file'], 0, strlen($status['file'])-4));
1961 if (file_exists(substr($status['file'], 0, strlen($status['file'])-4).'.gz')) unlink(substr($status['file'], 0, strlen($status['file'])-3).'.gz');
1962 }
1963 }
1964 }
1965
1966 }
1967
1968 $response = array();
1969 if (!isset($_POST['chunks']) || (isset($_POST['chunk']) && $_POST['chunk'] == $_POST['chunks']-1)) {
1970 $file = basename($status['file']);
1971 if (!preg_match('/^log\.[a-f0-9]{12}\.txt/i', $file) && !preg_match('/^backup_([\-0-9]{15})_.*_([0-9a-f]{12})-([\-a-z]+)([0-9]+)?(\.(zip|gz|gz\.crypt))?$/i', $file, $matches)) {
1972 $accept = apply_filters('updraftplus_accept_archivename', array());
1973 if (is_array($accept)) {
1974 foreach ($accept as $acc) {
1975 if (preg_match('/'.$acc['pattern'].'/i', $file)) $accepted = $acc['desc'];
1976 }
1977 }
1978 if (!empty($accepted)) {
1979 $response['dm'] = sprintf(__('This backup was created by %s, and can be imported.', 'updraftplus'), $accepted);
1980 } else {
1981 @unlink($status['file']);
1982 echo json_encode(array('e' => sprintf(__('Error: %s', 'updraftplus'),__('Bad filename format - this does not look like a file created by UpdraftPlus', 'updraftplus'))));
1983 exit;
1984 }
1985 } else {
1986 $backupable_entities = $updraftplus->get_backupable_file_entities(true);
1987 $type = isset($matches[3]) ? $matches[3] : '';
1988 if (!preg_match('/^log\.[a-f0-9]{12}\.txt/', $file) && 'db' != $type && !isset($backupable_entities[$type])) {
1989 @unlink($status['file']);
1990 echo json_encode(array('e' => sprintf(__('Error: %s', 'updraftplus'),sprintf(__('This looks like a file created by UpdraftPlus, but this install does not know about this type of object: %s. Perhaps you need to install an add-on?', 'updraftplus'), htmlspecialchars($type)))));
1991 exit;
1992 }
1993 }
1994 }
1995
1996 // send the uploaded file url in response
1997 $response['m'] = $status['url'];
1998 echo json_encode($response);
1999 exit;
2000 }
2001
2002 # Database decrypter
2003 public function plupload_action2() {
2004
2005 @set_time_limit(UPDRAFTPLUS_SET_TIME_LIMIT);
2006 global $updraftplus;
2007
2008 if (!UpdraftPlus_Options::user_can_manage()) exit;
2009 check_ajax_referer('updraft-uploader');
2010
2011 $updraft_dir = $updraftplus->backups_dir_location();
2012 if (!is_writable($updraft_dir)) exit;
2013
2014 add_filter('upload_dir', array($this, 'upload_dir'));
2015 add_filter('sanitize_file_name', array($this, 'sanitize_file_name'));
2016 // handle file upload
2017
2018 $farray = array('test_form' => true, 'action' => 'plupload_action2');
2019
2020 $farray['test_type'] = false;
2021 $farray['ext'] = 'crypt';
2022 $farray['type'] = 'application/octet-stream';
2023
2024 if (isset($_POST['chunks'])) {
2025 // $farray['ext'] = 'zip';
2026 // $farray['type'] = 'application/zip';
2027 } else {
2028 $farray['unique_filename_callback'] = array($this, 'unique_filename_callback');
2029 }
2030
2031 $status = wp_handle_upload(
2032 $_FILES['async-upload'],
2033 $farray
2034 );
2035 remove_filter('upload_dir', array($this, 'upload_dir'));
2036 remove_filter('sanitize_file_name', array($this, 'sanitize_file_name'));
2037
2038 if (isset($status['error'])) {
2039 echo 'ERROR:'.$status['error'];
2040 exit;
2041 }
2042
2043 // If this was the chunk, then we should instead be concatenating onto the final file
2044 if (isset($_POST['chunks']) && isset($_POST['chunk']) && preg_match('/^[0-9]+$/',$_POST['chunk'])) {
2045 $final_file = basename($_POST['name']);
2046 rename($status['file'], $updraft_dir.'/'.$final_file.'.'.$_POST['chunk'].'.zip.tmp');
2047 $status['file'] = $updraft_dir.'/'.$final_file.'.'.$_POST['chunk'].'.zip.tmp';
2048
2049 // Final chunk? If so, then stich it all back together
2050 if ($_POST['chunk'] == $_POST['chunks']-1) {
2051 if ($wh = fopen($updraft_dir.'/'.$final_file, 'wb')) {
2052 for ($i=0 ; $i<$_POST['chunks']; $i++) {
2053 $rf = $updraft_dir.'/'.$final_file.'.'.$i.'.zip.tmp';
2054 if ($rh = fopen($rf, 'rb')) {
2055 while ($line = fread($rh, 32768)) fwrite($wh, $line);
2056 fclose($rh);
2057 @unlink($rf);
2058 }
2059 }
2060 fclose($wh);
2061 $status['file'] = $updraft_dir.'/'.$final_file;
2062 }
2063 }
2064
2065 }
2066
2067 if (!isset($_POST['chunks']) || (isset($_POST['chunk']) && $_POST['chunk'] == $_POST['chunks']-1)) {
2068 $file = basename($status['file']);
2069 if (!preg_match('/^backup_([\-0-9]{15})_.*_([0-9a-f]{12})-db([0-9]+)?\.(gz\.crypt)$/i', $file)) {
2070
2071 @unlink($status['file']);
2072 echo 'ERROR:'.__('Bad filename format - this does not look like an encrypted database file created by UpdraftPlus', 'updraftplus');
2073
2074 exit;
2075 }
2076 }
2077
2078 // send the uploaded file url in response
2079 // echo 'OK:'.$status['url'];
2080 echo 'OK:'.$file;
2081 exit;
2082 }
2083
2084 public function settings_header() {
2085 $this->include_template('wp-admin/settings/header.php');
2086 }
2087
2088 public function settings_output() {
2089
2090 if (false == ($render = apply_filters('updraftplus_settings_page_render', true))) {
2091 do_action('updraftplus_settings_page_render_abort', $render);
2092 return;
2093 }
2094
2095 do_action('updraftplus_settings_page_init');
2096
2097 global $updraftplus;
2098
2099 /*
2100 we use request here because the initial restore is triggered by a POSTed form. we then may need to obtain credentials
2101 for the WP_Filesystem. to do this WP outputs a form, but we don't pass our parameters via that. So the values are
2102 passed back in as GET parameters.
2103 */
2104
2105 if (isset($_REQUEST['action']) && (($_REQUEST['action'] == 'updraft_restore' && isset($_REQUEST['backup_timestamp'])) || ('updraft_restore_continue' == $_REQUEST['action'] && !empty($_REQUEST['restoreid'])))) {
2106
2107 $is_continuation = ('updraft_restore_continue' == $_REQUEST['action']) ? true : false;
2108
2109 if ($is_continuation) {
2110 $restore_in_progress = get_site_option('updraft_restore_in_progress');
2111 if ($restore_in_progress != $_REQUEST['restoreid']) {
2112 $abort_restore_already = true;
2113 $updraftplus->log(__('Sufficient information about the in-progress restoration operation could not be found.', 'updraftplus').' (restoreid_mismatch)', 'error', 'restoreid_mismatch');
2114 } else {
2115
2116 $restore_jobdata = $updraftplus->jobdata_getarray($restore_in_progress);
2117 if (is_array($restore_jobdata) && isset($restore_jobdata['job_type']) && 'restore' == $restore_jobdata['job_type'] && isset($restore_jobdata['second_loop_entities']) && !empty($restore_jobdata['second_loop_entities']) && isset($restore_jobdata['job_time_ms']) && isset($restore_jobdata['backup_timestamp'])) {
2118 $backup_timestamp = $restore_jobdata['backup_timestamp'];
2119 $continuation_data = $restore_jobdata;
2120 } else {
2121 $abort_restore_already = true;
2122 $updraftplus->log(__('Sufficient information about the in-progress restoration operation could not be found.', 'updraftplus').' (restoreid_nojobdata)', 'error', 'restoreid_nojobdata');
2123 }
2124 }
2125
2126 } else {
2127 $backup_timestamp = $_REQUEST['backup_timestamp'];
2128 $continuation_data = null;
2129 }
2130
2131 if (empty($abort_restore_already)) {
2132 $backup_success = $this->restore_backup($backup_timestamp, $continuation_data);
2133 } else {
2134 $backup_success = false;
2135 }
2136
2137 if (empty($updraftplus->errors) && $backup_success === true) {
2138 // TODO: Deal with the case of some of the work having been deferred
2139 // If we restored the database, then that will have out-of-date information which may confuse the user - so automatically re-scan for them.
2140 $updraftplus->rebuild_backup_history();
2141 echo '<p><strong>';
2142 $updraftplus->log_e('Restore successful!');
2143 echo '</strong></p>';
2144 $updraftplus->log("Restore successful");
2145 $s_val = 1;
2146 if (!empty($this->entities_to_restore) && is_array($this->entities_to_restore)) {
2147 foreach ($this->entities_to_restore as $k => $v) {
2148 if ('db' != $v) $s_val = 2;
2149 }
2150 }
2151 $pval = ($updraftplus->have_addons) ? 1 : 0;
2152
2153 echo '<strong>'.__('Actions', 'updraftplus').':</strong> <a href="'.UpdraftPlus_Options::admin_page_url().'?page=updraftplus&updraft_restore_success='.$s_val.'&pval='.$pval.'">'.__('Return to UpdraftPlus Configuration', 'updraftplus').'</a>';
2154 return;
2155 } elseif (is_wp_error($backup_success)) {
2156 echo '<p>';
2157 $updraftplus->log_e('Restore failed...');
2158 echo '</p>';
2159 $updraftplus->log_wp_error($backup_success);
2160 $updraftplus->log("Restore failed");
2161 $updraftplus->list_errors();
2162 echo '<strong>'.__('Actions', 'updraftplus').':</strong> <a href="'.UpdraftPlus_Options::admin_page_url().'?page=updraftplus">'.__('Return to UpdraftPlus Configuration', 'updraftplus').'</a>';
2163 return;
2164 } elseif (false === $backup_success) {
2165 # This means, "not yet - but stay on the page because we may be able to do it later, e.g. if the user types in the requested information"
2166 echo '<p>';
2167 $updraftplus->log_e('Restore failed...');
2168 echo '</p>';
2169 $updraftplus->log("Restore failed");
2170 $updraftplus->list_errors();
2171 echo '<strong>'.__('Actions', 'updraftplus').':</strong> <a href="'.UpdraftPlus_Options::admin_page_url().'?page=updraftplus">'.__('Return to UpdraftPlus Configuration', 'updraftplus').'</a>';
2172 return;
2173 }
2174 }
2175
2176 if (isset($_REQUEST['action']) && 'updraft_delete_old_dirs' == $_REQUEST['action']) {
2177 $nonce = (empty($_REQUEST['_wpnonce'])) ? "" : $_REQUEST['_wpnonce'];
2178 if (!wp_verify_nonce($nonce, 'updraftplus-credentialtest-nonce')) die('Security check');
2179 $this->delete_old_dirs_go();
2180 return;
2181 }
2182
2183 if (!empty($_REQUEST['action']) && 'updraftplus_broadcastaction' == $_REQUEST['action'] && !empty($_REQUEST['subaction'])) {
2184 $nonce = (empty($_REQUEST['nonce'])) ? "" : $_REQUEST['nonce'];
2185 if (!wp_verify_nonce($nonce, 'updraftplus-credentialtest-nonce')) die('Security check');
2186 do_action($_REQUEST['subaction']);
2187 return;
2188 }
2189
2190 if (isset($_GET['error'])) {
2191 // This is used by Microsoft OneDrive authorisation failures (May 15). I am not sure what may have been using the 'error' GET parameter otherwise - but it is harmless.
2192 if (!empty($_GET['error_description'])) {
2193 $this->show_admin_warning(htmlspecialchars($_GET['error_description']).' ('.htmlspecialchars($_GET['error']).')', 'error');
2194 } else {
2195 $this->show_admin_warning(htmlspecialchars($_GET['error']), 'error');
2196 }
2197 }
2198
2199 if (isset($_GET['message'])) $this->show_admin_warning(htmlspecialchars($_GET['message']));
2200
2201 if (isset($_GET['action']) && $_GET['action'] == 'updraft_create_backup_dir' && isset($_GET['nonce']) && wp_verify_nonce($_GET['nonce'], 'create_backup_dir')) {
2202 $created = $this->create_backup_dir();
2203 if (is_wp_error($created)) {
2204 echo '<p>'.__('Backup directory could not be created', 'updraftplus').'...<br>';
2205 echo '<ul class="disc">';
2206 foreach ($created->get_error_messages() as $key => $msg) {
2207 echo '<li>'.htmlspecialchars($msg).'</li>';
2208 }
2209 echo '</ul></p>';
2210 } elseif ($created !== false) {
2211 echo '<p>'.__('Backup directory successfully created.', 'updraftplus').'</p><br>';
2212 }
2213 echo '<b>'.__('Actions', 'updraftplus').':</b> <a href="'.UpdraftPlus_Options::admin_page_url().'?page=updraftplus">'.__('Return to UpdraftPlus Configuration', 'updraftplus').'</a>';
2214 return;
2215 }
2216
2217 echo '<div id="updraft_backup_started" class="updated updraft-hidden" style="display:none;"></div>';
2218
2219 if (isset($_POST['action']) && 'updraft_wipesettings' == $_POST['action']) {
2220 $this->updraft_wipe_settings();
2221 }
2222
2223 // This opens a div
2224 $this->settings_header();
2225 ?>
2226
2227 <div id="updraft-hidethis">
2228 <p>
2229 <strong><?php _e('Warning:', 'updraftplus'); ?> <?php _e("If you can still read these words after the page finishes loading, then there is a JavaScript or jQuery problem in the site.", 'updraftplus'); ?></strong>
2230
2231 <?php if (false !== strpos(basename(UPDRAFTPLUS_URL), ' ')) { ?>
2232 <strong><?php _e('The UpdraftPlus directory in wp-content/plugins has white-space in it; WordPress does not like this. You should rename the directory to wp-content/plugins/updraftplus to fix this problem.', 'updraftplus');?></strong>
2233 <?php } else { ?>
2234 <a href="<?php echo apply_filters('updraftplus_com_link', "https://updraftplus.com/do-you-have-a-javascript-or-jquery-error/");?>"><?php _e('Go here for more information.', 'updraftplus'); ?></a>
2235 <?php } ?>
2236 </p>
2237 </div>
2238
2239 <?php
2240
2241 $include_deleteform_div = true;
2242
2243 // Opens a div, which needs closing later
2244 if (isset($_GET['updraft_restore_success'])) {
2245
2246 if (get_template() === 'optimizePressTheme' || is_plugin_active('optimizePressPlugin') || is_plugin_active_for_network('optimizePressPlugin')){
2247 $this->show_admin_warning("<a href='https://optimizepress.zendesk.com/hc/en-us/articles/203699826-Update-URL-References-after-moving-domain' target='_blank'>" . __("OptimizePress 2.0 encodes its contents, so search/replace does not work.","updraftplus") . ' ' . __("To fix this problem go here.","updraftplus") . "</a>", "notice notice-warning");
2248 }
2249 $success_advert = (isset($_GET['pval']) && 0 == $_GET['pval'] && !$updraftplus->have_addons) ? '<p>'.__('For even more features and personal support, check out ', 'updraftplus').'<strong><a href="'.apply_filters("updraftplus_com_link",'https://updraftplus.com/shop/updraftplus-premium/').'" target="_blank">UpdraftPlus Premium</a>.</strong></p>' : "";
2250
2251 echo "<div class=\"updated backup-restored\"><span><strong>".__('Your backup has been restored.', 'updraftplus').'</strong></span><br>';
2252 // Unnecessary - will be advised of this below
2253 // if (2 == $_GET['updraft_restore_success']) echo ' '.__('Your old (themes, uploads, plugins, whatever) directories have been retained with "-old" appended to their name. Remove them when you are satisfied that the backup worked properly.');
2254 echo $success_advert;
2255 $include_deleteform_div = false;
2256
2257 }
2258
2259 // $this->print_restore_in_progress_box_if_needed();
2260
2261 if ($this->scan_old_dirs(true)) $this->print_delete_old_dirs_form(true, $include_deleteform_div);
2262
2263 // Close the div opened by the earlier section
2264 if (isset($_GET['updraft_restore_success'])) echo '</div>';
2265
2266 if(empty($success_advert) && empty($this->no_settings_warning)) {
2267
2268 if (!class_exists('UpdraftPlus_Notices')) require_once(UPDRAFTPLUS_DIR.'/includes/updraftplus-notices.php');
2269 global $updraftplus_notices;
2270 $updraftplus_notices->do_notice();
2271 }
2272
2273 if (!$updraftplus->memory_check(64)) {
2274 // HS8390 - A case where UpdraftPlus::memory_check_current() returns -1
2275 $memory_check_current = $updraftplus->memory_check_current();
2276 if ($memory_check_current > 0) { ?>
2277 <div class="updated memory-limit"><?php _e('Your PHP memory limit (set by your web hosting company) is very low. UpdraftPlus attempted to raise it but was unsuccessful. This plugin may struggle with a memory limit of less than 64 Mb - especially if you have very large files uploaded (though on the other hand, many sites will be successful with a 32Mb limit - your experience may vary).', 'updraftplus');?> <?php _e('Current limit is:', 'updraftplus');?> <?php echo $updraftplus->memory_check_current(); ?> MB</div>
2278 <?php }
2279 }
2280
2281
2282 if (!empty($updraftplus->errors)) {
2283 echo '<div class="error updraft_list_errors">';
2284 $updraftplus->list_errors();
2285 echo '</div>';
2286 }
2287
2288 $backup_history = UpdraftPlus_Options::get_updraft_option('updraft_backup_history');
2289 if (empty($backup_history)) {
2290 $updraftplus->rebuild_backup_history();
2291 $backup_history = UpdraftPlus_Options::get_updraft_option('updraft_backup_history');
2292 }
2293 $backup_history = is_array($backup_history) ? $backup_history : array();
2294 ?>
2295
2296 <?php
2297
2298 $tabflag = 1;
2299
2300 if (isset($_REQUEST['tab'])){
2301 switch($_REQUEST['tab']) {
2302 case 'status': $tabflag = 1; break;
2303 case 'backups': $tabflag = 2; break;
2304 case 'settings': $tabflag = 3; break;
2305 case 'expert': $tabflag = 4; break;
2306 case 'addons': $tabflag = 5; break;
2307 default : $tabflag = 1;
2308 }
2309 }
2310
2311 $this->include_template('wp-admin/settings/tab-bar.php', false, array('backup_history' => $backup_history, 'tabflag' => $tabflag));
2312
2313 $updraft_dir = $updraftplus->backups_dir_location();
2314 $backup_disabled = $updraftplus->really_is_writable($updraft_dir) ? '' : 'disabled="disabled"';
2315 ?>
2316
2317 <div id="updraft-poplog" >
2318 <pre id="updraft-poplog-content"></pre>
2319 </div>
2320
2321 <?php $this->include_template('wp-admin/settings/tab-status.php', false, array('tabflag' => $tabflag, 'backup_disabled' => $backup_disabled)); ?>
2322
2323 <div id="updraft-navtab-backups-content" <?php if (2 != $tabflag) echo 'class="updraft-hidden"'; ?> style="<?php if (2 != $tabflag) echo 'display:none;'; ?>">
2324 <?php
2325 $is_opera = (false !== strpos($_SERVER['HTTP_USER_AGENT'], 'Opera') || false !== strpos($_SERVER['HTTP_USER_AGENT'], 'OPR/'));
2326 $tmp_opts = array('include_opera_warning' => $is_opera);
2327 $this->settings_downloading_and_restoring($backup_history, false, $tmp_opts);
2328 $this->include_template('wp-admin/settings/delete-and-restore-modals.php');
2329 ?>
2330 </div>
2331
2332 <div id="updraft-navtab-settings-content" <?php if (3 != $tabflag) echo 'class="updraft-hidden"'; ?> style="<?php if (3 != $tabflag) echo 'display:none;'; ?>">
2333 <h2 class="updraft_settings_sectionheading"><?php _e('Backup Contents And Schedule', 'updraftplus');?></h2>
2334 <?php UpdraftPlus_Options::options_form_begin(); ?>
2335 <?php $this->settings_formcontents(); ?>
2336 </form>
2337 </div>
2338
2339 <div id="updraft-navtab-expert-content"<?php if (4 != $tabflag) echo ' class="updraft-hidden"'; ?> style="<?php if (4 != $tabflag) echo 'display:none;'; ?>">
2340 <?php $this->settings_advanced_tools(); ?>
2341 </div>
2342
2343 <div id="updraft-navtab-addons-content"<?php if (5 != $tabflag) echo ' class="updraft-hidden"'; ?> style="<?php if (5 != $tabflag) echo 'display:none;'; ?>">
2344
2345 <?php
2346 $tab_addons = $this->include_template('wp-admin/settings/tab-addons.php', true, array('tabflag' => $tabflag));
2347
2348 echo apply_filters('updraftplus_addonstab_content', $tab_addons);
2349
2350 ?>
2351
2352 </div>
2353
2354 <?php
2355 // settings_header() opens a div
2356 echo '</div>';
2357 }
2358
2359 private function print_restore_in_progress_box_if_needed() {
2360 $restore_in_progress = get_site_option('updraft_restore_in_progress');
2361 if (!empty($restore_in_progress)) {
2362 global $updraftplus;
2363 $restore_jobdata = $updraftplus->jobdata_getarray($restore_in_progress);
2364 if (is_array($restore_jobdata) && !empty($restore_jobdata)) {
2365 // Only print if within the last 24 hours; and only after 2 minutes
2366 if (isset($restore_jobdata['job_type']) && 'restore' == $restore_jobdata['job_type'] && isset($restore_jobdata['second_loop_entities']) && !empty($restore_jobdata['second_loop_entities']) && isset($restore_jobdata['job_time_ms']) && (time() - $restore_jobdata['job_time_ms'] > 120 || (defined('UPDRAFTPLUS_RESTORE_PROGRESS_ALWAYS_SHOW') && UPDRAFTPLUS_RESTORE_PROGRESS_ALWAYS_SHOW)) && time() - $restore_jobdata['job_time_ms'] < 86400 && (empty($_REQUEST['action']) || ('updraft_restore' != $_REQUEST['action'] && 'updraft_restore_continue' != $_REQUEST['action']))) {
2367 $restore_jobdata['jobid'] = $restore_in_progress;
2368 $this->restore_in_progress_jobdata = $restore_jobdata;
2369 add_action('all_admin_notices', array($this, 'show_admin_restore_in_progress_notice'));
2370 }
2371 }
2372 }
2373 }
2374
2375 public function show_admin_restore_in_progress_notice() {
2376
2377 if (isset($_REQUEST['action']) && 'updraft_restore_abort' == $_REQUEST['action'] && !empty($_REQUEST['restoreid'])) {
2378 delete_site_option('updraft_restore_in_progress');
2379 return;
2380 }
2381
2382 $restore_jobdata = $this->restore_in_progress_jobdata;
2383 $seconds_ago = time() - (int)$restore_jobdata['job_time_ms'];
2384 $minutes_ago = floor($seconds_ago/60);
2385 $seconds_ago = $seconds_ago - $minutes_ago*60;
2386 $time_ago = sprintf(__("%s minutes, %s seconds", 'updraftplus'), $minutes_ago, $seconds_ago);
2387 ?><div class="updated show_admin_restore_in_progress_notice">
2388 <span class="unfinished-restoration"><strong><?php echo 'UpdraftPlus: '.__('Unfinished restoration', 'updraftplus'); ?> </strong></span><br>
2389 <p><?php printf(__('You have an unfinished restoration operation, begun %s ago.', 'updraftplus'), $time_ago);?></p>
2390 <form method="post" action="<?php echo UpdraftPlus_Options::admin_page_url().'?page=updraftplus'; ?>">
2391 <?php wp_nonce_field('updraftplus-credentialtest-nonce'); ?>
2392 <input id="updraft_restore_continue_action" type="hidden" name="action" value="updraft_restore_continue">
2393 <input type="hidden" name="restoreid" value="<?php echo $restore_jobdata['jobid'];?>" value="<?php echo esc_attr($restore_jobdata['jobid']);?>">
2394 <button onclick="jQuery('#updraft_restore_continue_action').val('updraft_restore_continue'); jQuery(this).parent('form').submit();" type="submit" class="button-primary"><?php _e('Continue restoration', 'updraftplus'); ?></button>
2395 <button onclick="jQuery('#updraft_restore_continue_action').val('updraft_restore_abort'); jQuery(this).parent('form').submit();" class="button-secondary"><?php _e('Dismiss', 'updraftplus');?></button>
2396 </form><?php
2397 echo "</div>";
2398
2399 }
2400
2401 public function backupnow_modal_contents() {
2402
2403 $ret = $this->backup_now_widgetry();
2404
2405 // $ret .= '<p>'.__('Does nothing happen when you attempt backups?', 'updraftplus').' <a href="https://updraftplus.com/faqs/my-scheduled-backups-and-pressing-backup-now-does-nothing-however-pressing-debug-backup-does-produce-a-backup/">'.__('Go here for help.', 'updraftplus').'</a></p>';
2406
2407 return $ret;
2408 }
2409
2410 private function backup_now_widgetry() {
2411
2412 $ret = '';
2413
2414 $ret .= '<p><input type="checkbox" id="backupnow_includedb" checked="checked"> <label for="backupnow_includedb">'.__("Include the database in the backup", 'updraftplus').'</label> ';
2415
2416 $ret .= '(<a href="#" id="backupnow_database_showmoreoptions">...</a>)<br>';
2417
2418 $ret .= '<div id="backupnow_database_moreoptions" class="updraft-hidden" style="display:none;">';
2419
2420 $premium_link = apply_filters('updraftplus_com_link','https://updraftplus.com/landing/updraftplus-premium');
2421
2422 $free_ret = '<em>'.__('All WordPress tables will be backed up.', 'updraftplus').' <a href="'.$premium_link.'">'. __('With UpdraftPlus Premium, you can choose to backup non-WordPress tables, backup only specified tables, and backup other databases too.', 'updraftplus').'</a></em>';
2423
2424 $ret .= apply_filters('updraft_backupnow_database_showmoreoptions', $free_ret, '') . '</p>';
2425
2426 $ret .= '</div>';
2427
2428 $ret .= '<p><input type="checkbox" id="backupnow_includefiles" checked="checked"> <label for="backupnow_includefiles">'.__("Include any files in the backup", 'updraftplus').'</label> (<a href="#" id="backupnow_includefiles_showmoreoptions">...</a>)<br>';
2429
2430 $ret .= '<div id="backupnow_includefiles_moreoptions" class="updraft-hidden" style="display:none;"><em>'.__('Your saved settings also affect what is backed up - e.g. files excluded.', 'updraftplus').'</em><br>'.$this->files_selector_widgetry('backupnow_files_', false, 'sometimes').'</div></p>';
2431
2432 $ret .= '<span id="backupnow_remote_container">'.$this->backup_now_remote_message().'</span>';
2433
2434 $ret .= apply_filters('updraft_backupnow_modal_afteroptions', '', '');
2435
2436 return $ret;
2437 }
2438
2439 // Also used by the auto-backups add-on
2440 public function render_active_jobs_and_log_table($wide_format = false, $print_active_jobs = true) {
2441 ?>
2442 <table class="form-table" id="updraft_activejobs_table">
2443
2444 <?php $active_jobs = ($print_active_jobs) ? $this->print_active_jobs() : '';?>
2445 <tr id="updraft_activejobsrow" class="<?php
2446 if (!$active_jobs && !$wide_format) { echo 'hidden'; }
2447 if ($wide_format) { echo ".minimum-height"; }
2448 ?>">
2449 <?php if ($wide_format) { ?>
2450 <td id="updraft_activejobs" colspan="2">
2451 <?php echo $active_jobs;?>
2452 </td>
2453 <?php } else { ?>
2454 <th><?php _e('Backups in progress:', 'updraftplus');?></th>
2455 <td id="updraft_activejobs"><?php echo $active_jobs;?></td>
2456 <?php } ?>
2457 </tr>
2458
2459 <tr id="updraft_lastlogmessagerow">
2460 <?php if ($wide_format) {
2461 // Hide for now - too ugly
2462 ?>
2463 <td colspan="2" class="last-message"><strong><?php _e('Last log message', 'updraftplus');?>:</strong><br>
2464 <span id="updraft_lastlogcontainer"><?php echo htmlspecialchars(UpdraftPlus_Options::get_updraft_option('updraft_lastmessage', __('(Nothing yet logged)', 'updraftplus'))); ?></span><br>
2465 <?php $this->most_recently_modified_log_link(); ?>
2466 </td>
2467 <?php } else { ?>
2468 <th><?php _e('Last log message', 'updraftplus');?>:</th>
2469 <td>
2470 <span id="updraft_lastlogcontainer"><?php echo htmlspecialchars(UpdraftPlus_Options::get_updraft_option('updraft_lastmessage', __('(Nothing yet logged)', 'updraftplus'))); ?></span><br>
2471 <?php $this->most_recently_modified_log_link(); ?>
2472 </td>
2473 <?php } ?>
2474 </tr>
2475
2476 <?php
2477 # Currently disabled - not sure who we want to show this to
2478 if (1==0 && !defined('UPDRAFTPLUS_NOADS_B')) {
2479 $feed = $updraftplus->get_updraftplus_rssfeed();
2480 if (is_a($feed, 'SimplePie')) {
2481 echo '<tr><th style="vertical-align:top;">'.__('Latest UpdraftPlus.com news:', 'updraftplus').'</th><td class="updraft_simplepie">';
2482 echo '<ul class="disc;">';
2483 foreach ($feed->get_items(0, 5) as $item) {
2484 echo '<li>';
2485 echo '<a href="'.esc_attr($item->get_permalink()).'">';
2486 echo htmlspecialchars($item->get_title());
2487 # D, F j, Y H:i
2488 echo "</a> (".htmlspecialchars($item->get_date('j F Y')).")";
2489 echo '</li>';
2490 }
2491 echo '</ul></td></tr>';
2492 }
2493 }
2494 ?>
2495 </table>
2496 <?php
2497 }
2498
2499 private function most_recently_modified_log_link() {
2500
2501 global $updraftplus;
2502 list($mod_time, $log_file, $nonce) = $updraftplus->last_modified_log();
2503
2504 ?>
2505 <a href="?page=updraftplus&amp;action=downloadlatestmodlog&amp;wpnonce=<?php echo wp_create_nonce('updraftplus_download') ?>" <?php if (!$mod_time) echo 'style="display:none;"'; ?> class="updraft-log-link" onclick="event.preventDefault(); updraft_popuplog('');"><?php _e('Download most recently modified log file', 'updraftplus');?></a>
2506 <?php
2507 }
2508
2509 public function settings_downloading_and_restoring($backup_history = array(), $return_result = false, $options = array()) {
2510 return $this->include_template('wp-admin/settings/downloading-and-restoring.php', $return_result, array('backup_history' => $backup_history, 'options' => $options));
2511 }
2512
2513 public function settings_debugrow($head, $content) {
2514 echo "<tr class=\"updraft_debugrow\"><th>$head</th><td>$content</td></tr>";
2515 }
2516
2517 public function settings_advanced_tools($return_instead_of_echo = false, $pass_through = array()) {
2518 return $this->include_template('wp-admin/advanced/advanced-tools.php', $return_instead_of_echo, $pass_through);
2519 }
2520
2521 private function print_delete_old_dirs_form($include_blurb = true, $include_div = true) {
2522 if ($include_blurb) {
2523 if ($include_div) {
2524 echo '<div id="updraft_delete_old_dirs_pagediv" class="updated delete-old-directories">';
2525 }
2526 echo '<p>'.__('Your WordPress install has old directories from its state before you restored/migrated (technical information: these are suffixed with -old). You should press this button to delete them as soon as you have verified that the restoration worked.', 'updraftplus').'</p>';
2527 }
2528 ?>
2529 <form method="post" action="<?php echo esc_url(add_query_arg(array('error' => false, 'updraft_restore_success' => false, 'action' => false, 'page' => 'updraftplus'))); ?>">
2530 <?php wp_nonce_field('updraftplus-credentialtest-nonce'); ?>
2531 <input type="hidden" name="action" value="updraft_delete_old_dirs">
2532 <input type="submit" class="button-primary" value="<?php echo esc_attr(__('Delete Old Directories', 'updraftplus'));?>">
2533 </form>
2534 <?php
2535 if ($include_blurb && $include_div) echo '</div>';
2536 }
2537
2538 public function get_cron($job_id = false) {
2539
2540 $cron = get_option('cron');
2541 if (!is_array($cron)) $cron = array();
2542 if (false === $job_id) return $cron;
2543
2544 foreach ($cron as $time => $job) {
2545 if (isset($job['updraft_backup_resume'])) {
2546 foreach ($job['updraft_backup_resume'] as $hook => $info) {
2547 if (isset($info['args'][1]) && $job_id == $info['args'][1]) {
2548 global $updraftplus;
2549 $jobdata = $updraftplus->jobdata_getarray($job_id);
2550 return (!is_array($jobdata)) ? false : array($time, $jobdata);
2551 }
2552 }
2553 }
2554 }
2555 }
2556
2557 // A value for $this_job_only also causes something to always be returned (to allow detection of the job having started on the front-end)
2558 private function print_active_jobs($this_job_only = false) {
2559 $cron = $this->get_cron();
2560 // $found_jobs = 0;
2561 $ret = '';
2562
2563 foreach ($cron as $time => $job) {
2564 if (isset($job['updraft_backup_resume'])) {
2565 foreach ($job['updraft_backup_resume'] as $hook => $info) {
2566 if (isset($info['args'][1])) {
2567 // $found_jobs++;
2568 $job_id = $info['args'][1];
2569 if (false === $this_job_only || $job_id == $this_job_only) {
2570 $ret .= $this->print_active_job($job_id, false, $time, $info['args'][0]);
2571 }
2572 }
2573 }
2574 }
2575 }
2576
2577 // A value for $this_job_only implies that output is required
2578 if (false !== $this_job_only && !$ret) {
2579 $ret = $this->print_active_job($this_job_only);
2580 if ('' == $ret) {
2581 // The presence of the exact ID matters to the front-end - indicates that the backup job has at least begun
2582 $ret = '<div class="active-jobs updraft_finished" id="updraft-jobid-'.$this_job_only.'"><em>'.__('The backup has finished running', 'updraftplus').'</em> - <a class="updraft-log-link" data-jobid="'.$this_job_only.'">'.__('View Log', 'updraftplus').'</a></div>';
2583 }
2584 }
2585
2586 // if (0 == $found_jobs) $ret .= '<p><em>'.__('(None)', 'updraftplus').'</em></p>';
2587 return $ret;
2588 }
2589
2590 private function print_active_job($job_id, $is_oneshot = false, $time = false, $next_resumption = false) {
2591
2592 $ret = '';
2593
2594 global $updraftplus;
2595 $jobdata = $updraftplus->jobdata_getarray($job_id);
2596
2597 if (false == apply_filters('updraftplus_print_active_job_continue', true, $is_oneshot, $next_resumption, $jobdata)) return '';
2598
2599 #if (!is_array($jobdata)) $jobdata = array();
2600 if (!isset($jobdata['backup_time'])) return '';
2601
2602 $backupable_entities = $updraftplus->get_backupable_file_entities(true, true);
2603
2604 $began_at = (isset($jobdata['backup_time'])) ? get_date_from_gmt(gmdate('Y-m-d H:i:s', (int)$jobdata['backup_time']), 'D, F j, Y H:i') : '?';
2605
2606 $jobstatus = empty($jobdata['jobstatus']) ? 'unknown' : $jobdata['jobstatus'];
2607 $stage = 0;
2608 switch ($jobstatus) {
2609 # Stage 0
2610 case 'begun':
2611 $curstage = __('Backup begun', 'updraftplus');
2612 break;
2613 # Stage 1
2614 case 'filescreating':
2615 $stage = 1;
2616 $curstage = __('Creating file backup zips', 'updraftplus');
2617 if (!empty($jobdata['filecreating_substatus']) && isset($backupable_entities[$jobdata['filecreating_substatus']['e']]['description'])) {
2618
2619 $sdescrip = preg_replace('/ \(.*\)$/', '', $backupable_entities[$jobdata['filecreating_substatus']['e']]['description']);
2620 if (strlen($sdescrip) > 20 && isset($jobdata['filecreating_substatus']['e']) && is_array($jobdata['filecreating_substatus']['e']) && isset($backupable_entities[$jobdata['filecreating_substatus']['e']]['shortdescription'])) $sdescrip = $backupable_entities[$jobdata['filecreating_substatus']['e']]['shortdescription'];
2621 $curstage .= ' ('.$sdescrip.')';
2622 if (isset($jobdata['filecreating_substatus']['i']) && isset($jobdata['filecreating_substatus']['t'])) {
2623 $stage = min(2, 1 + ($jobdata['filecreating_substatus']['i']/max($jobdata['filecreating_substatus']['t'],1)));
2624 }
2625 }
2626 break;
2627 case 'filescreated':
2628 $stage = 2;
2629 $curstage = __('Created file backup zips', 'updraftplus');
2630 break;
2631
2632 # Stage 4
2633 case 'clouduploading':
2634 $stage = 4;
2635 $curstage = __('Uploading files to remote storage', 'updraftplus');
2636 if (isset($jobdata['uploading_substatus']['t']) && isset($jobdata['uploading_substatus']['i'])) {
2637 $t = max((int)$jobdata['uploading_substatus']['t'], 1);
2638 $i = min($jobdata['uploading_substatus']['i']/$t, 1);
2639 $p = min($jobdata['uploading_substatus']['p'], 1);
2640 $pd = $i + $p/$t;
2641 $stage = 4 + $pd;
2642 $curstage .= ' '.sprintf(__('(%s%%, file %s of %s)', 'updraftplus'), floor(100*$pd), $jobdata['uploading_substatus']['i']+1, $t);
2643 }
2644 break;
2645 case 'pruning':
2646 $stage = 5;
2647 $curstage = __('Pruning old backup sets', 'updraftplus');
2648 break;
2649 case 'resumingforerrors':
2650 $stage = -1;
2651 $curstage = __('Waiting until scheduled time to retry because of errors', 'updraftplus');
2652 break;
2653 # Stage 6
2654 case 'finished':
2655 $stage = 6;
2656 $curstage = __('Backup finished', 'updraftplus');
2657 break;
2658 default:
2659
2660 # Database creation and encryption occupies the space from 2 to 4. Databases are created then encrypted, then the next databae is created/encrypted, etc.
2661 if ('dbcreated' == substr($jobstatus, 0, 9)) {
2662 $jobstatus = 'dbcreated';
2663 $whichdb = substr($jobstatus, 9);
2664 if (!is_numeric($whichdb)) $whichdb = 0;
2665 $howmanydbs = max((empty($jobdata['backup_database']) || !is_array($jobdata['backup_database'])) ? 1 : count($jobdata['backup_database']), 1);
2666 $perdbspace = 2/$howmanydbs;
2667
2668 $stage = min(4, 2 + ($whichdb+2)*$perdbspace);
2669
2670 $curstage = __('Created database backup', 'updraftplus');
2671
2672 } elseif ('dbcreating' == substr($jobstatus, 0, 10)) {
2673 $whichdb = substr($jobstatus, 10);
2674 if (!is_numeric($whichdb)) $whichdb = 0;
2675 $howmanydbs = (empty($jobdata['backup_database']) || !is_array($jobdata['backup_database'])) ? 1 : count($jobdata['backup_database']);
2676 $perdbspace = 2/$howmanydbs;
2677 $jobstatus = 'dbcreating';
2678
2679 $stage = min(4, 2 + $whichdb*$perdbspace);
2680
2681 $curstage = __('Creating database backup', 'updraftplus');
2682 if (!empty($jobdata['dbcreating_substatus']['t'])) {
2683 $curstage .= ' ('.sprintf(__('table: %s', 'updraftplus'), $jobdata['dbcreating_substatus']['t']).')';
2684 if (!empty($jobdata['dbcreating_substatus']['i']) && !empty($jobdata['dbcreating_substatus']['a'])) {
2685 $substage = max(0.001, ($jobdata['dbcreating_substatus']['i'] / max($jobdata['dbcreating_substatus']['a'],1)));
2686 $stage += $substage * $perdbspace * 0.5;
2687 }
2688 }
2689 } elseif ('dbencrypting' == substr($jobstatus, 0, 12)) {
2690 $whichdb = substr($jobstatus, 12);
2691 if (!is_numeric($whichdb)) $whichdb = 0;
2692 $howmanydbs = (empty($jobdata['backup_database']) || !is_array($jobdata['backup_database'])) ? 1 : count($jobdata['backup_database']);
2693 $perdbspace = 2/$howmanydbs;
2694 $stage = min(4, 2 + $whichdb*$perdbspace + $perdbspace*0.5);
2695 $jobstatus = 'dbencrypting';
2696 $curstage = __('Encrypting database', 'updraftplus');
2697 } elseif ('dbencrypted' == substr($jobstatus, 0, 11)) {
2698 $whichdb = substr($jobstatus, 11);
2699 if (!is_numeric($whichdb)) $whichdb = 0;
2700 $howmanydbs = (empty($jobdata['backup_database']) || !is_array($jobdata['backup_database'])) ? 1 : count($jobdata['backup_database']);
2701 $jobstatus = 'dbencrypted';
2702 $perdbspace = 2/$howmanydbs;
2703 $stage = min(4, 2 + $whichdb*$perdbspace + $perdbspace);
2704 $curstage = __('Encrypted database', 'updraftplus');
2705 } else {
2706 $curstage = __('Unknown', 'updraftplus');
2707 }
2708 }
2709
2710 $runs_started = (empty($jobdata['runs_started'])) ? array() : $jobdata['runs_started'];
2711 $time_passed = (empty($jobdata['run_times'])) ? array() : $jobdata['run_times'];
2712 $last_checkin_ago = -1;
2713 if (is_array($time_passed)) {
2714 foreach ($time_passed as $run => $passed) {
2715 if (isset($runs_started[$run])) {
2716 $time_ago = microtime(true) - ($runs_started[$run] + $time_passed[$run]);
2717 if ($time_ago < $last_checkin_ago || $last_checkin_ago == -1) $last_checkin_ago = $time_ago;
2718 }
2719 }
2720 }
2721
2722 $next_res_after = (int)$time-time();
2723 $next_res_txt = ($is_oneshot) ? '' : ' - '.sprintf(__("next resumption: %d (after %ss)", 'updraftplus'), $next_resumption, $next_res_after). ' ';
2724 $last_activity_txt = ($last_checkin_ago >= 0) ? ' - '.sprintf(__('last activity: %ss ago', 'updraftplus'), floor($last_checkin_ago)).' ' : '';
2725
2726 if (($last_checkin_ago < 50 && $next_res_after>30) || $is_oneshot) {
2727 $show_inline_info = $last_activity_txt;
2728 $title_info = $next_res_txt;
2729 } else {
2730 $show_inline_info = $next_res_txt;
2731 $title_info = $last_activity_txt;
2732 }
2733
2734 // Existence of the 'updraft-jobid-(id)' id is checked for in other places, so do not modify this
2735 $ret .= '<div class="job-id" id="updraft-jobid-'.$job_id.'"><span class="updraft_jobtimings next-resumption';
2736
2737 if (!empty($jobdata['is_autobackup'])) $ret .= ' isautobackup';
2738
2739 $ret .= '" data-jobid="'.$job_id.'" data-lastactivity="'.(int)$last_checkin_ago.'" data-nextresumption="'.$next_resumption.'" data-nextresumptionafter="'.$next_res_after.'" title="'.esc_attr(sprintf(__('Job ID: %s', 'updraftplus'), $job_id)).$title_info.'">'.$began_at.'</span> ';
2740
2741 $ret .= $show_inline_info;
2742 $ret .= '- <a data-jobid="'.$job_id.'" href="'.UpdraftPlus_Options::admin_page_url().'?page=updraftplus&action=downloadlog&updraftplus_backup_nonce='.$job_id.'" class="updraft-log-link">'.__('show log', 'updraftplus').'</a>';
2743
2744 if (!$is_oneshot) $ret .=' - <a href="#" data-jobid="'.$job_id.'" title="'.esc_attr(__('Note: the progress bar below is based on stages, NOT time. Do not stop the backup simply because it seems to have remained in the same place for a while - that is normal.', 'updraftplus')).'" class="updraft_jobinfo_delete">'.__('stop', 'updraftplus').'</a>';
2745
2746 $ret .= apply_filters('updraft_printjob_beforewarnings', '', $jobdata, $job_id);
2747
2748 if (!empty($jobdata['warnings']) && is_array($jobdata['warnings'])) {
2749 $ret .= '<ul class="disc">';
2750 foreach ($jobdata['warnings'] as $warning) {
2751 $ret .= '<li>'.sprintf(__('Warning: %s', 'updraftplus'), make_clickable(htmlspecialchars($warning))).'</li>';
2752 }
2753 $ret .= '</ul>';
2754 }
2755
2756 $ret .= '<div class="curstage">';
2757 $ret .= htmlspecialchars($curstage);
2758 //we need to add this data-progress attribute in order to be able to update the progress bar in UDC
2759 $ret .= '<div class="updraft_percentage" data-info="'.esc_attr($curstage).'" data-progress="'.(($stage>0) ? (ceil((100/6)*$stage)) : '0').'" style="height: 100%; width:'.(($stage>0) ? (ceil((100/6)*$stage)) : '0').'%"></div>';
2760 $ret .= '</div></div>';
2761
2762 $ret .= '</div>';
2763
2764 return $ret;
2765
2766 }
2767
2768 private function delete_old_dirs_go($show_return = true) {
2769 echo ($show_return) ? '<h1>UpdraftPlus - '.__('Remove old directories', 'updraftplus').'</h1>' : '<h2>'.__('Remove old directories', 'updraftplus').'</h2>';
2770
2771 if ($this->delete_old_dirs()) {
2772 echo '<p>'.__('Old directories successfully removed.', 'updraftplus').'</p><br>';
2773 } else {
2774 echo '<p>',__('Old directory removal failed for some reason. You may want to do this manually.', 'updraftplus').'</p><br>';
2775 }
2776 if ($show_return) echo '<b>'.__('Actions', 'updraftplus').':</b> <a href="'.UpdraftPlus_Options::admin_page_url().'?page=updraftplus">'.__('Return to UpdraftPlus Configuration', 'updraftplus').'</a>';
2777 }
2778
2779 //deletes the -old directories that are created when a backup is restored.
2780 private function delete_old_dirs() {
2781 global $wp_filesystem, $updraftplus;
2782 $credentials = request_filesystem_credentials(wp_nonce_url(UpdraftPlus_Options::admin_page_url()."?page=updraftplus&action=updraft_delete_old_dirs", 'updraftplus-credentialtest-nonce'));
2783 WP_Filesystem($credentials);
2784 if ($wp_filesystem->errors->get_error_code()) {
2785 foreach ($wp_filesystem->errors->get_error_messages() as $message)
2786 show_message($message);
2787 exit;
2788 }
2789 // From WP_CONTENT_DIR - which contains 'themes'
2790 $ret = $this->delete_old_dirs_dir($wp_filesystem->wp_content_dir());
2791
2792 $updraft_dir = $updraftplus->backups_dir_location();
2793 if ($updraft_dir) {
2794 $ret4 = ($updraft_dir) ? $this->delete_old_dirs_dir($updraft_dir, false) : true;
2795 } else {
2796 $ret4 = true;
2797 }
2798
2799 // $ret2 = $this->delete_old_dirs_dir($wp_filesystem->abspath());
2800 $plugs = untrailingslashit($wp_filesystem->wp_plugins_dir());
2801 if ($wp_filesystem->is_dir($plugs.'-old')) {
2802 print "<strong>".__('Delete', 'updraftplus').": </strong>plugins-old: ";
2803 if (!$wp_filesystem->delete($plugs.'-old', true)) {
2804 $ret3 = false;
2805 print "<strong>".__('Failed', 'updraftplus')."</strong><br>";
2806 } else {
2807 $ret3 = true;
2808 print "<strong>".__('OK', 'updraftplus')."</strong><br>";
2809 }
2810 } else {
2811 $ret3 = true;
2812 }
2813
2814 return $ret && $ret3 && $ret4;
2815 }
2816
2817 private function delete_old_dirs_dir($dir, $wpfs = true) {
2818
2819 $dir = trailingslashit($dir);
2820
2821 global $wp_filesystem, $updraftplus;
2822
2823 if ($wpfs) {
2824 $list = $wp_filesystem->dirlist($dir);
2825 } else {
2826 $list = scandir($dir);
2827 }
2828 if (!is_array($list)) return false;
2829
2830 $ret = true;
2831 foreach ($list as $item) {
2832 $name = (is_array($item)) ? $item['name'] : $item;
2833 if ("-old" == substr($name, -4, 4)) {
2834 //recursively delete
2835 print "<strong>".__('Delete', 'updraftplus').": </strong>".htmlspecialchars($name).": ";
2836
2837 if ($wpfs) {
2838 if (!$wp_filesystem->delete($dir.$name, true)) {
2839 $ret = false;
2840 echo "<strong>".__('Failed', 'updraftplus')."</strong><br>";
2841 } else {
2842 echo "<strong>".__('OK', 'updraftplus')."</strong><br>";
2843 }
2844 } else {
2845 if ($updraftplus->remove_local_directory($dir.$name)) {
2846 echo "<strong>".__('OK', 'updraftplus')."</strong><br>";
2847 } else {
2848 $ret = false;
2849 echo "<strong>".__('Failed', 'updraftplus')."</strong><br>";
2850 }
2851 }
2852 }
2853 }
2854 return $ret;
2855 }
2856
2857 // The aim is to get a directory that is writable by the webserver, because that's the only way we can create zip files
2858 private function create_backup_dir() {
2859
2860 global $wp_filesystem, $updraftplus;
2861
2862 if (false === ($credentials = request_filesystem_credentials(UpdraftPlus_Options::admin_page().'?page=updraftplus&action=updraft_create_backup_dir&nonce='.wp_create_nonce('create_backup_dir')))) {
2863 return false;
2864 }
2865
2866 if (!WP_Filesystem($credentials)) {
2867 // our credentials were no good, ask the user for them again
2868 request_filesystem_credentials(UpdraftPlus_Options::admin_page().'?page=updraftplus&action=updraft_create_backup_dir&nonce='.wp_create_nonce('create_backup_dir'), '', true);
2869 return false;
2870 }
2871
2872 $updraft_dir = $updraftplus->backups_dir_location();
2873
2874 $default_backup_dir = $wp_filesystem->find_folder(dirname($updraft_dir)).basename($updraft_dir);
2875
2876 $updraft_dir = ($updraft_dir) ? $wp_filesystem->find_folder(dirname($updraft_dir)).basename($updraft_dir) : $default_backup_dir;
2877
2878 if (!$wp_filesystem->is_dir($default_backup_dir) && !$wp_filesystem->mkdir($default_backup_dir, 0775)) {
2879 $wperr = new WP_Error;
2880 if ($wp_filesystem->errors->get_error_code()) {
2881 foreach ($wp_filesystem->errors->get_error_messages() as $message) {
2882 $wperr->add('mkdir_error', $message);
2883 }
2884 return $wperr;
2885 } else {
2886 return new WP_Error('mkdir_error', __('The request to the filesystem to create the directory failed.', 'updraftplus'));
2887 }
2888 }
2889
2890 if ($wp_filesystem->is_dir($default_backup_dir)) {
2891
2892 if ($updraftplus->really_is_writable($updraft_dir)) return true;
2893
2894 @$wp_filesystem->chmod($default_backup_dir, 0775);
2895 if ($updraftplus->really_is_writable($updraft_dir)) return true;
2896
2897 @$wp_filesystem->chmod($default_backup_dir, 0777);
2898
2899 if ($updraftplus->really_is_writable($updraft_dir)) {
2900 echo '<p>'.__('The folder was created, but we had to change its file permissions to 777 (world-writable) to be able to write to it. You should check with your hosting provider that this will not cause any problems', 'updraftplus').'</p>';
2901 return true;
2902 } else {
2903 @$wp_filesystem->chmod($default_backup_dir, 0775);
2904 $show_dir = (0 === strpos($default_backup_dir, ABSPATH)) ? substr($default_backup_dir, strlen(ABSPATH)) : $default_backup_dir;
2905 return new WP_Error('writable_error', __('The folder exists, but your webserver does not have permission to write to it.', 'updraftplus').' '.__('You will need to consult with your web hosting provider to find out how to set permissions for a WordPress plugin to write to the directory.', 'updraftplus').' ('.$show_dir.')');
2906 }
2907 }
2908
2909 return true;
2910 }
2911
2912 //scans the content dir to see if any -old dirs are present
2913 private function scan_old_dirs($print_as_comment = false) {
2914 global $updraftplus;
2915 $dirs = scandir(untrailingslashit(WP_CONTENT_DIR));
2916 if (!is_array($dirs)) $dirs = array();
2917 $dirs_u = @scandir($updraftplus->backups_dir_location());
2918 if (!is_array($dirs_u)) $dirs_u = array();
2919 foreach (array_merge($dirs, $dirs_u) as $dir) {
2920 if (preg_match('/-old$/', $dir)) {
2921 if ($print_as_comment) echo '<!--'.htmlspecialchars($dir).'-->';
2922 return true;
2923 }
2924 }
2925 # No need to scan ABSPATH - we don't backup there
2926 if (is_dir(untrailingslashit(WP_PLUGIN_DIR).'-old')) {
2927 if ($print_as_comment) echo '<!--'.htmlspecialchars(untrailingslashit(WP_PLUGIN_DIR).'-old').'-->';
2928 return true;
2929 }
2930 return false;
2931 }
2932
2933 /**
2934 * Outputs html for a storage method using the parameters passed in, this version should be removed when all remote storages use the multi version
2935 * @param [String] $classes - a list of classes to be used when
2936 * @param [String] $header - the table header content
2937 * @param [String] $contents - the table contents
2938 */
2939 public function storagemethod_row($method, $header, $contents) {
2940 ?>
2941 <tr class="updraftplusmethod <?php echo $method;?>">
2942 <th><?php echo $header;?></th>
2943 <td><?php echo $contents;?></td>
2944 </tr>
2945 <?php
2946 }
2947
2948 /**
2949 * Outputs html for a storage method using the parameters passed in, this version of the method is compatible with multi storage options
2950 * @param [String] $classes - a list of classes to be used when
2951 * @param [String] $header - the table header content
2952 * @param [String] $contents - the table contents
2953 */
2954 public function storagemethod_row_multi($classes, $header, $contents) {
2955 ?>
2956 <tr class="<?php echo $classes;?>">
2957 <th><?php echo $header;?></th>
2958 <td><?php echo $contents;?></td>
2959 </tr>
2960 <?php
2961 }
2962
2963 public function last_backup_html() {
2964
2965 global $updraftplus;
2966
2967 $updraft_last_backup = UpdraftPlus_Options::get_updraft_option('updraft_last_backup');
2968
2969 if ($updraft_last_backup) {
2970
2971 // Convert to GMT, then to blog time
2972 $backup_time = (int)$updraft_last_backup['backup_time'];
2973
2974 $print_time = get_date_from_gmt(gmdate('Y-m-d H:i:s', $backup_time), 'D, F j, Y H:i');
2975 // $print_time = date_i18n('D, F j, Y H:i', $backup_time);
2976
2977 if (empty($updraft_last_backup['backup_time_incremental'])) {
2978 $last_backup_text = "<span style=\"color:".(($updraft_last_backup['success']) ? 'green' : 'black').";\">".$print_time.'</span>';
2979 } else {
2980 $inc_time = get_date_from_gmt(gmdate('Y-m-d H:i:s', $updraft_last_backup['backup_time_incremental']), 'D, F j, Y H:i');
2981 // $inc_time = date_i18n('D, F j, Y H:i', $updraft_last_backup['backup_time_incremental']);
2982 $last_backup_text = "<span style=\"color:".(($updraft_last_backup['success']) ? 'green' : 'black').";\">$inc_time</span> (".sprintf(__('incremental backup; base backup: %s', 'updraftplus'), $print_time).')';
2983 }
2984
2985 $last_backup_text .= '<br>';
2986
2987 // Show errors + warnings
2988 if (is_array($updraft_last_backup['errors'])) {
2989 foreach ($updraft_last_backup['errors'] as $err) {
2990 $level = (is_array($err)) ? $err['level'] : 'error';
2991 $message = (is_array($err)) ? $err['message'] : $err;
2992 $last_backup_text .= ('warning' == $level) ? "<span style=\"color:orange;\">" : "<span style=\"color:red;\">";
2993 if ('warning' == $level) {
2994 $message = sprintf(__("Warning: %s", 'updraftplus'), make_clickable(htmlspecialchars($message)));
2995 } else {
2996 $message = htmlspecialchars($message);
2997 }
2998 $last_backup_text .= $message;
2999 $last_backup_text .= '</span><br>';
3000 }
3001 }
3002
3003 // Link log
3004 if (!empty($updraft_last_backup['backup_nonce'])) {
3005 $updraft_dir = $updraftplus->backups_dir_location();
3006
3007 $potential_log_file = $updraft_dir."/log.".$updraft_last_backup['backup_nonce'].".txt";
3008 if (is_readable($potential_log_file)) $last_backup_text .= "<a href=\"?page=updraftplus&action=downloadlog&updraftplus_backup_nonce=".$updraft_last_backup['backup_nonce']."\" class=\"updraft-log-link\" onclick=\"event.preventDefault(); updraft_popuplog('".$updraft_last_backup['backup_nonce']."');\">".__('Download log file', 'updraftplus')."</a>";
3009 }
3010
3011 } else {
3012 $last_backup_text = "<span style=\"color:blue;\">".__('No backup has been completed', 'updraftplus')."</span>";
3013 }
3014
3015 return $last_backup_text;
3016
3017 }
3018
3019 public function get_intervals() {
3020 return apply_filters('updraftplus_backup_intervals', array(
3021 'manual' => _x("Manual", 'i.e. Non-automatic', 'updraftplus'),
3022 'every4hours' => sprintf(__("Every %s hours", 'updraftplus'), '4'),
3023 'every8hours' => sprintf(__("Every %s hours", 'updraftplus'), '8'),
3024 'twicedaily' => sprintf(__("Every %s hours", 'updraftplus'), '12'),
3025 'daily' => __("Daily", 'updraftplus'),
3026 'weekly' => __("Weekly", 'updraftplus'),
3027 'fortnightly' => __("Fortnightly", 'updraftplus'),
3028 'monthly' => __("Monthly", 'updraftplus')
3029 ));
3030 }
3031
3032 public function really_writable_message($really_is_writable, $updraft_dir){
3033 if ($really_is_writable) {
3034 $dir_info = '<span style="color:green;">'.__('Backup directory specified is writable, which is good.', 'updraftplus').'</span>';
3035 } else {
3036 $dir_info = '<span style="color:red;">';
3037 if (!is_dir($updraft_dir)) {
3038 $dir_info .= __('Backup directory specified does <b>not</b> exist.', 'updraftplus');
3039 } else {
3040 $dir_info .= __('Backup directory specified exists, but is <b>not</b> writable.', 'updraftplus');
3041 }
3042 $dir_info .= '<span class="updraft-directory-not-writable-blurb"><span class="directory-permissions"><a class="updraft_create_backup_dir" href="'.UpdraftPlus_Options::admin_page_url().'?page=updraftplus&action=updraft_create_backup_dir&nonce='.wp_create_nonce('create_backup_dir').'">'.__('Follow this link to attempt to create the directory and set the permissions', 'updraftplus').'</a></span>, '.__('or, to reset this option', 'updraftplus').' <a href="#" class="updraft_backup_dir_reset">'.__('click here', 'updraftplus').'</a>. '.__('If that is unsuccessful check the permissions on your server or change it to another directory that is writable by your web server process.', 'updraftplus').'</span>';
3043 }
3044 return $dir_info;
3045 }
3046
3047 public function settings_formcontents($options = array()) {
3048 $this->include_template('wp-admin/settings/form-contents.php', false, array('options' => $options));
3049 }
3050
3051 public function get_settings_js($method_objects, $really_is_writable, $updraft_dir, $active_service) {
3052
3053 global $updraftplus;
3054
3055 ob_start();
3056 ?>
3057 jQuery(document).ready(function() {
3058 <?php
3059 if (!$really_is_writable) echo "jQuery('.backupdirrow').show();\n";
3060 ?>
3061 <?php
3062 if (!empty($active_service)) {
3063 if (is_array($active_service)) {
3064 foreach ($active_service as $serv) {
3065 echo "jQuery('.${serv}').show();\n";
3066 }
3067 } else {
3068 echo "jQuery('.${active_service}').show();\n";
3069 }
3070 } else {
3071 echo "jQuery('.none').show();\n";
3072 }
3073 foreach ($updraftplus->backup_methods as $method => $description) {
3074 // already done: require_once(UPDRAFTPLUS_DIR.'/methods/'.$method.'.php');
3075 $call_method = "UpdraftPlus_BackupModule_$method";
3076 if (method_exists($call_method, 'config_print_javascript_onready')) {
3077 $method_objects[$method]->config_print_javascript_onready();
3078 }
3079 }
3080 ?>
3081 });
3082 <?php
3083 $ret = ob_get_contents();
3084 ob_end_clean();
3085 return $ret;
3086 }
3087
3088 // $include_more can be (bool) or (string)"sometimes"
3089 public function files_selector_widgetry($prefix = '', $show_exclusion_options = true, $include_more = true) {
3090
3091 $ret = '';
3092
3093 global $updraftplus;
3094 $backupable_entities = $updraftplus->get_backupable_file_entities(true, true);
3095 # The true (default value if non-existent) here has the effect of forcing a default of on.
3096 $include_more_paths = UpdraftPlus_Options::get_updraft_option('updraft_include_more_path');
3097 foreach ($backupable_entities as $key => $info) {
3098 $included = (UpdraftPlus_Options::get_updraft_option("updraft_include_$key", apply_filters("updraftplus_defaultoption_include_".$key, true))) ? 'checked="checked"' : "";
3099 if ('others' == $key || 'uploads' == $key) {
3100
3101 $data_toggle_exclude_field = $show_exclusion_options ? 'data-toggle_exclude_field="'.$key.'"' : '';
3102
3103 $ret .= '<input class="updraft_include_entity" id="'.$prefix.'updraft_include_'.$key.'" '.$data_toggle_exclude_field.' type="checkbox" name="updraft_include_'.$key.'" value="1" '.$included.'> <label '.(('others' == $key) ? 'title="'.sprintf(__('Your wp-content directory server path: %s', 'updraftplus'), WP_CONTENT_DIR).'" ' : '').' for="'.$prefix.'updraft_include_'.$key.'">'.(('others' == $key) ? __('Any other directories found inside wp-content', 'updraftplus') : htmlspecialchars($info['description'])).'</label><br>';
3104
3105 if ($show_exclusion_options) {
3106 $include_exclude = UpdraftPlus_Options::get_updraft_option('updraft_include_'.$key.'_exclude', ('others' == $key) ? UPDRAFT_DEFAULT_OTHERS_EXCLUDE : UPDRAFT_DEFAULT_UPLOADS_EXCLUDE);
3107
3108 $display = ($included) ? '' : 'class="updraft-hidden" style="display:none;"';
3109
3110 $ret .= "<div id=\"".$prefix."updraft_include_".$key."_exclude\" $display>";
3111
3112 $ret .= '<label for="'.$prefix.'updraft_include_'.$key.'_exclude">'.__('Exclude these:', 'updraftplus').'</label>';
3113
3114 $ret .= '<input title="'.__('If entering multiple files/directories, then separate them with commas. For entities at the top level, you can use a * at the start or end of the entry as a wildcard.', 'updraftplus').'" type="text" id="'.$prefix.'updraft_include_'.$key.'_exclude" name="updraft_include_'.$key.'_exclude" size="54" value="'.htmlspecialchars($include_exclude).'" />';
3115
3116 $ret .= '<br></div>';
3117 }
3118
3119 } else {
3120
3121 if ($key != 'more' || true === $include_more || ('sometimes' === $include_more && !empty($include_more_paths))) {
3122
3123 $data_toggle_exclude_field = $show_exclusion_options ? 'data-toggle_exclude_field="'.$key.'"' : '';
3124
3125 $ret .= "<input class=\"updraft_include_entity\" $data_toggle_exclude_field id=\"".$prefix."updraft_include_$key\" type=\"checkbox\" name=\"updraft_include_$key\" value=\"1\" $included /><label for=\"".$prefix."updraft_include_$key\"".((isset($info['htmltitle'])) ? ' title="'.htmlspecialchars($info['htmltitle']).'"' : '')."> ".htmlspecialchars($info['description']);
3126
3127 $ret .= "</label><br>";
3128 $ret .= apply_filters("updraftplus_config_option_include_$key", '', $prefix);
3129 }
3130 }
3131 }
3132
3133 return $ret;
3134 }
3135
3136 public function show_double_warning($text, $extraclass = '', $echo = true) {
3137
3138 $ret = "<div class=\"error updraftplusmethod $extraclass\"><p>$text</p></div>";
3139 $ret .= "<p class=\"double-warning\">$text</p>";
3140
3141 if ($echo) echo $ret;
3142 return $ret;
3143
3144 }
3145
3146 public function optionfilter_split_every($value) {
3147 $value = absint($value);
3148 if ($value < UPDRAFTPLUS_SPLIT_MIN) $value = UPDRAFTPLUS_SPLIT_MIN;
3149 return $value;
3150 }
3151
3152 /**
3153 * Check if curl exists; if not, print or return appropriate error messages
3154 *
3155 * @param String $service - the service description (used only for user-visible messages - so, use the description)
3156 * @param Boolean $has_fallback - set as true if the lack of Curl only affects the ability to connect over SSL
3157 * @param String $extraclass - an extra CSS class for any resulting message, passed on to show_double_warning()
3158 * @param Boolean $echo_instead_of_return - whether the result should be echoed or returned
3159 *
3160 * @returns String|Void - any resulting message, if $echo_instead_of_return was set
3161 */
3162 public function curl_check($service, $has_fallback = false, $extraclass = '', $echo_instead_of_return = true) {
3163
3164 $ret = '';
3165
3166 // Check requirements
3167 if (!function_exists("curl_init") || !function_exists('curl_exec')) {
3168
3169 $ret .= $this->show_double_warning('<strong>'.__('Warning', 'updraftplus').':</strong> '.sprintf(__("Your web server's PHP installation does not included a <strong>required</strong> (for %s) module (%s). Please contact your web hosting provider's support and ask for them to enable it.", 'updraftplus'), $service, 'Curl').' ', $extraclass, false);
3170
3171 } else {
3172 $curl_version = curl_version();
3173 $curl_ssl_supported= ($curl_version['features'] & CURL_VERSION_SSL);
3174 if (!$curl_ssl_supported) {
3175 if ($has_fallback) {
3176 $ret .= '<p><strong>'.__('Warning', 'updraftplus').':</strong> '.sprintf(__("Your web server's PHP/Curl installation does not support https access. Communications with %s will be unencrypted. Ask your web host to install Curl/SSL in order to gain the ability for encryption (via an add-on).",'updraftplus'), $service).'</p>';
3177 } else {
3178 $ret .= $this->show_double_warning('<p><strong>'.__('Warning', 'updraftplus').':</strong> '.sprintf(__("Your web server's PHP/Curl installation does not support https access. We cannot access %s without this support. Please contact your web hosting provider's support. %s <strong>requires</strong> Curl+https. Please do not file any support requests; there is no alternative.",'updraftplus'), $service, $service).'</p>', $extraclass, false);
3179 }
3180 } else {
3181 $ret .= '<p><em>'.sprintf(__("Good news: Your site's communications with %s can be encrypted. If you see any errors to do with encryption, then look in the 'Expert Settings' for more help.", 'updraftplus'),$service).'</em></p>';
3182 }
3183 }
3184 if ($echo_instead_of_return) {
3185 echo $ret;
3186 } else {
3187 return $ret;
3188 }
3189 }
3190
3191 # If $basedirs is passed as an array, then $directorieses must be too
3192 # Note: Reason $directorieses is being used because $directories is used within the foreach-within-a-foreach further down
3193 private function recursive_directory_size($directorieses, $exclude = array(), $basedirs = '', $format='text') {
3194
3195
3196 $size = 0;
3197
3198 if (is_string($directorieses)) {
3199 $basedirs = $directorieses;
3200 $directorieses = array($directorieses);
3201 }
3202
3203 if (is_string($basedirs)) $basedirs = array($basedirs);
3204
3205 foreach ($directorieses as $ind => $directories) {
3206 if (!is_array($directories)) $directories=array($directories);
3207
3208 $basedir = empty($basedirs[$ind]) ? $basedirs[0] : $basedirs[$ind];
3209
3210 foreach ($directories as $dir) {
3211 if (is_file($dir)) {
3212 $size += @filesize($dir);
3213 } else {
3214 $suffix = ('' != $basedir) ? ((0 === strpos($dir, $basedir.'/')) ? substr($dir, 1+strlen($basedir)) : '') : '';
3215 $size += $this->recursive_directory_size_raw($basedir, $exclude, $suffix);
3216 }
3217 }
3218
3219 }
3220
3221 if ('numeric' == $format) return $size;
3222
3223 global $updraftplus;
3224 return $updraftplus->convert_numeric_size_to_text($size);
3225
3226 }
3227
3228 private function recursive_directory_size_raw($prefix_directory, &$exclude = array(), $suffix_directory = '') {
3229
3230 $directory = $prefix_directory.('' == $suffix_directory ? '' : '/'.$suffix_directory);
3231 $size = 0;
3232 if (substr($directory, -1) == '/') $directory = substr($directory,0,-1);
3233
3234 if (!file_exists($directory) || !is_dir($directory) || !is_readable($directory)) return -1;
3235 if (file_exists($directory.'/.donotbackup')) return 0;
3236
3237 if ($handle = opendir($directory)) {
3238 while (($file = readdir($handle)) !== false) {
3239 if ($file != '.' && $file != '..') {
3240 $spath = ('' == $suffix_directory) ? $file : $suffix_directory.'/'.$file;
3241 if (false !== ($fkey = array_search($spath, $exclude))) {
3242 unset($exclude[$fkey]);
3243 continue;
3244 }
3245 $path = $directory.'/'.$file;
3246 if (is_file($path)) {
3247 $size += filesize($path);
3248 } elseif (is_dir($path)) {
3249 $handlesize = $this->recursive_directory_size_raw($prefix_directory, $exclude, $suffix_directory.('' == $suffix_directory ? '' : '/').$file);
3250 if ($handlesize >= 0) { $size += $handlesize; }# else { return -1; }
3251 }
3252 }
3253 }
3254 closedir($handle);
3255 }
3256
3257 return $size;
3258
3259 }
3260
3261 private function raw_backup_info($backup_history, $key, $nonce) {
3262
3263 global $updraftplus;
3264
3265 $backup = $backup_history[$key];
3266
3267 $pretty_date = get_date_from_gmt(gmdate('Y-m-d H:i:s', (int)$key), 'M d, Y G:i');
3268
3269 $rawbackup = "<h2 title=\"$key\">$pretty_date</h2>";
3270
3271 if (!empty($backup['label'])) $rawbackup .= '<span class="raw-backup-info">'.$backup['label'].'</span>';
3272
3273 $rawbackup .= '<hr><p>';
3274
3275 $backupable_entities = $updraftplus->get_backupable_file_entities(true, true);
3276
3277 if (!empty($nonce)) {
3278 $jd = $updraftplus->jobdata_getarray($nonce);
3279 } else {
3280 $jd = array();
3281 }
3282
3283 $checksums = $updraftplus->which_checksums();
3284
3285 foreach ($backupable_entities as $type => $info) {
3286 if (!isset($backup[$type])) continue;
3287
3288 $rawbackup .= $updraftplus->printfile($info['description'], $backup, $type, $checksums, $jd, true);
3289
3290 // $rawbackup .= '<h3>'.$info['description'].'</h3>';
3291 // $files = is_string($backup[$type]) ? array($backup[$type]) : $backup[$type];
3292 // foreach ($files as $index => $file) {
3293 // $rawbackup .= $file.'<br>';
3294 // }
3295 }
3296
3297 $total_size = 0;
3298 foreach ($backup as $ekey => $files) {
3299 if ('db' == strtolower(substr($ekey, 0, 2)) && '-size' != substr($ekey, -5, 5)) {
3300 $rawbackup .= $updraftplus->printfile(__('Database', 'updraftplus'), $backup, $ekey, $checksums, $jd, true);
3301 }
3302 if (!isset($backupable_entities[$ekey]) && ('db' != substr($ekey, 0, 2) || '-size' == substr($ekey, -5, 5))) continue;
3303 if (is_string($files)) $files = array($files);
3304 foreach ($files as $findex => $file) {
3305 $size_key = (0 == $findex) ? $ekey.'-size' : $ekey.$findex.'-size';
3306 $total_size = (false === $total_size || !isset($backup[$size_key]) || !is_numeric($backup[$size_key])) ? false : $total_size + $backup[$size_key];
3307 }
3308 }
3309
3310 $services = empty($backup['service']) ? array('none') : $backup['service'];
3311 if (!is_array($services)) $services = array('none');
3312
3313 $rawbackup .= '<strong>'.__('Uploaded to:', 'updraftplus').'</strong> ';
3314
3315 $show_services = '';
3316 foreach ($services as $serv) {
3317 if ('none' == $serv || '' == $serv) {
3318 $add_none = true;
3319 } elseif (isset($updraftplus->backup_methods[$serv])) {
3320 $show_services .= ($show_services) ? ', '.$updraftplus->backup_methods[$serv] : $updraftplus->backup_methods[$serv];
3321 } else {
3322 $show_services .= ($show_services) ? ', '.$serv : $serv;
3323 }
3324 }
3325 if ('' == $show_services && $add_none) $show_services .= __('None', 'updraftplus');
3326
3327 $rawbackup .= $show_services;
3328
3329 if ($total_size !== false) {
3330 $rawbackup .= '</p><strong>'.__('Total backup size:', 'updraftplus').'</strong> '.$updraftplus->convert_numeric_size_to_text($total_size).'<p>';
3331 }
3332
3333
3334
3335 $rawbackup .= '</p><hr><p><pre>'.print_r($backup, true).'</p></pre>';
3336
3337 if (!empty($jd) && is_array($jd)) {
3338 $rawbackup .= '<p><pre>'.print_r($jd, true).'</pre></p>';
3339 }
3340
3341 return esc_attr($rawbackup);
3342 }
3343
3344 public function existing_backup_table($backup_history = false) {
3345
3346 global $updraftplus;
3347
3348 if (false === $backup_history) $backup_history = UpdraftPlus_Options::get_updraft_option('updraft_backup_history');
3349
3350 if (!is_array($backup_history) || empty($backup_history)) return "<p><em>".__('You have not yet made any backups.', 'updraftplus')."</em></p>";
3351
3352 $pass_values = array(
3353 'backup_history' => $backup_history,
3354 'updraft_dir' => $updraftplus->backups_dir_location(),
3355 'backupable_entities' => $updraftplus->get_backupable_file_entities(true, true)
3356 );
3357
3358 return $this->include_template('wp-admin/settings/existing-backups-table.php', true, $pass_values);
3359
3360 }
3361
3362 private function download_db_button($bkey, $key, $esc_pretty_date, $backup, $accept = array()) {
3363
3364 if (!empty($backup['meta_foreign']) && isset($accept[$backup['meta_foreign']])) {
3365 $desc_source = $accept[$backup['meta_foreign']]['desc'];
3366 } else {
3367 $desc_source = __('unknown source', 'updraftplus');
3368 }
3369
3370 $ret = '';
3371
3372 if ('db' == $bkey) {
3373 $dbt = empty($backup['meta_foreign']) ? esc_attr(__('Database', 'updraftplus')) : esc_attr(sprintf(__('Database (created by %s)', 'updraftplus'), $desc_source));
3374 } else {
3375 $dbt = __('External database', 'updraftplus').' ('.substr($bkey, 2).')';
3376 }
3377
3378 $ret .= $this->download_button($bkey, $key, 0, null, '', $dbt, $esc_pretty_date, '0');
3379
3380 return $ret;
3381 }
3382
3383 // Go through each of the file entities
3384 public function download_buttons($backup, $key, $accept, &$entities, $esc_pretty_date) {
3385 global $updraftplus;
3386 $ret = '';
3387 $backupable_entities = $updraftplus->get_backupable_file_entities(true, true);
3388
3389 $first_entity = true;
3390
3391 foreach ($backupable_entities as $type => $info) {
3392 if (!empty($backup['meta_foreign']) && 'wpcore' != $type) continue;
3393
3394 $ide = '';
3395 if ('wpcore' == $type) $wpcore_restore_descrip = $info['description'];
3396 if (empty($backup['meta_foreign'])) {
3397 $sdescrip = preg_replace('/ \(.*\)$/', '', $info['description']);
3398 if (strlen($sdescrip) > 20 && isset($info['shortdescription'])) $sdescrip = $info['shortdescription'];
3399 } else {
3400 $info['description'] = 'WordPress';
3401
3402 if (isset($accept[$backup['meta_foreign']])) {
3403 $desc_source = $accept[$backup['meta_foreign']]['desc'];
3404 $ide .= sprintf(__('Backup created by: %s.', 'updraftplus'), $accept[$backup['meta_foreign']]['desc']).' ';
3405 } else {
3406 $desc_source = __('unknown source', 'updraftplus');
3407 $ide .= __('Backup created by unknown source (%s) - cannot be restored.', 'updraftplus').' ';
3408 }
3409
3410 $sdescrip = (empty($accept[$backup['meta_foreign']]['separatedb'])) ? sprintf(__('Files and database WordPress backup (created by %s)', 'updraftplus'), $desc_source) : sprintf(__('Files backup (created by %s)', 'updraftplus'), $desc_source);
3411 if ('wpcore' == $type) $wpcore_restore_descrip = $sdescrip;
3412 }
3413 if (isset($backup[$type])) {
3414 if (!is_array($backup[$type])) $backup[$type]=array($backup[$type]);
3415 $howmanyinset = count($backup[$type]);
3416 $expected_index = 0;
3417 $index_missing = false;
3418 $set_contents = '';
3419 $entities .= "/$type=";
3420 $whatfiles = $backup[$type];
3421 ksort($whatfiles);
3422 foreach ($whatfiles as $findex => $bfile) {
3423 $set_contents .= ($set_contents == '') ? $findex : ",$findex";
3424 if ($findex != $expected_index) $index_missing = true;
3425 $expected_index++;
3426 }
3427 $entities .= $set_contents.'/';
3428 if (!empty($backup['meta_foreign'])) {
3429 $entities .= '/plugins=0//themes=0//uploads=0//others=0/';
3430 }
3431 $printing_first = true;
3432 foreach ($whatfiles as $findex => $bfile) {
3433
3434 $pdescrip = ($findex > 0) ? $sdescrip.' ('.($findex+1).')' : $sdescrip;
3435 if ($printing_first) {
3436 $ide .= __('Press here to download or browse', 'updraftplus').' '.strtolower($info['description']);
3437 } else {
3438 $ret .= '<div class="updraft-hidden" style="display:none;">';
3439 }
3440 if (count($backup[$type]) >0) {
3441 if ($printing_first) $ide .= ' '.sprintf(__('(%d archive(s) in set).', 'updraftplus'), $howmanyinset);
3442 }
3443 if ($index_missing) {
3444 if ($printing_first) $ide .= ' '.__('You appear to be missing one or more archives from this multi-archive set.', 'updraftplus');
3445 }
3446
3447 if (!$first_entity) {
3448 // $ret .= ', ';
3449 } else {
3450 $first_entity = false;
3451 }
3452
3453 $ret .= $this->download_button($type, $key, $findex, $info, $ide, $pdescrip, $esc_pretty_date, $set_contents);
3454
3455 if (!$printing_first) {
3456 $ret .= '</div>';
3457 } else {
3458 $printing_first = false;
3459 }
3460 }
3461 }
3462 }
3463 return $ret;
3464 }
3465
3466 public function date_label($pretty_date, $key, $backup, $jobdata, $nonce, $simple_format = false) {
3467 // $ret = apply_filters('updraftplus_showbackup_date', '<strong>'.$pretty_date.'</strong>', $backup, $jobdata, (int)$key);
3468
3469 $pretty_date = $simple_format ? $pretty_date : '<div class="clear-right">'.$pretty_date.'</div>';
3470
3471 $ret = apply_filters('updraftplus_showbackup_date', $pretty_date, $backup, $jobdata, (int)$key, $simple_format);
3472 if (is_array($jobdata) && !empty($jobdata['resume_interval']) && (empty($jobdata['jobstatus']) || 'finished' != $jobdata['jobstatus'])) {
3473 if ($simple_format) {
3474 $ret .= ' '.__('(Not finished)', 'updraftplus');
3475 } else {
3476 $ret .= apply_filters('updraftplus_msg_unfinishedbackup', "<br><span title=\"".esc_attr(__('If you are seeing more backups than you expect, then it is probably because the deletion of old backup sets does not happen until a fresh backup completes.', 'updraftplus'))."\">".__('(Not finished)', 'updraftplus').'</span>', $jobdata, $nonce);
3477 }
3478 }
3479 return $ret;
3480 }
3481
3482 public function download_button($type, $backup_timestamp, $findex, $info, $title, $pdescrip, $esc_pretty_date, $set_contents) {
3483
3484 $ret = '';
3485
3486 $wp_nonce = wp_create_nonce('updraftplus_download');
3487
3488 // updraft_downloader(base, backup_timestamp, what, whicharea, set_contents, prettydate, async)
3489 $ret .= '<button data-wp_nonce="'.esc_attr($wp_nonce).'" data-backup_timestamp="'.esc_attr($backup_timestamp).'" data-what="'.esc_attr($type).'" data-set_contents="'.esc_attr($set_contents).'" data-prettydate="'.esc_attr($esc_pretty_date).'" type="button" class="updraft_download_button '."uddownloadform_${type}_${backup_timestamp}_${findex}".'" title="'.$title.'">'.$pdescrip.'</button>';
3490 // onclick="'."return updraft_downloader('uddlstatus_', '$backup_timestamp', '$type', '.ud_downloadstatus', '$set_contents', '$esc_pretty_date', true)".'"
3491
3492 return $ret;
3493 }
3494
3495 public function restore_button($backup, $key, $pretty_date, $entities = '') {
3496 $ret = '<div class="restore-button">';
3497
3498 if ($entities) {
3499 $show_data = $pretty_date;
3500 if (isset($backup['native']) && false == $backup['native']) {
3501 $show_data .= ' '.__('(backup set imported from remote location)', 'updraftplus');
3502 }
3503
3504 $ret .= '<button data-showdata="'.esc_attr($show_data).'" data-backup_timestamp="'.$key.'" data-entities="'.esc_attr($entities).'" title="'.__('After pressing this button, you will be given the option to choose which components you wish to restore', 'updraftplus').'" type="button" style="float:left; clear:none;" class="button-primary choose-components-button">'.__('Restore', 'updraftplus').'</button>';
3505 }
3506 $ret .= "</div>\n";
3507 return $ret;
3508 }
3509
3510 public function delete_button($key, $nonce, $backup) {
3511 $sval = ((isset($backup['service']) && $backup['service'] != 'email' && $backup['service'] != 'none')) ? '1' : '0';
3512 return '<div class="updraftplus-remove" style="float: left; clear: none;" data-hasremote="'.$sval.'">
3513 <a data-hasremote="'.$sval.'" data-nonce="'.$nonce.'" data-key="'.$key.'" class="no-decoration updraft-delete-link" href="#" title="'.esc_attr(__('Delete this backup set', 'updraftplus')).'">'.__('Delete', 'updraftplus').'</a>
3514 </div>';
3515 }
3516
3517 public function log_button($backup) {
3518 global $updraftplus;
3519 $updraft_dir = $updraftplus->backups_dir_location();
3520 $ret = '';
3521 if (isset($backup['nonce']) && preg_match("/^[0-9a-f]{12}$/",$backup['nonce']) && is_readable($updraft_dir.'/log.'.$backup['nonce'].'.txt')) {
3522 $nval = $backup['nonce'];
3523 // $lt = esc_attr(__('View Log', 'updraftplus'));
3524 $lt = __('View Log', 'updraftplus');
3525 $url = esc_attr(UpdraftPlus_Options::admin_page()."?page=updraftplus&action=downloadlog&amp;updraftplus_backup_nonce=$nval");
3526 $ret .= <<<ENDHERE
3527 <div style="clear:none;" class="updraft-viewlogdiv">
3528 <a class="no-decoration updraft-log-link" href="$url" data-jobid="$nval">
3529 $lt
3530 </a>
3531 <!--
3532 <form action="$url" method="get">
3533 <input type="hidden" name="action" value="downloadlog" />
3534 <input type="hidden" name="page" value="updraftplus" />
3535 <input type="hidden" name="updraftplus_backup_nonce" value="$nval" />
3536 <input type="submit" value="$lt" class="updraft-log-link" onclick="event.preventDefault(); updraft_popuplog('$nval');" />
3537 </form>
3538 -->
3539 </div>
3540 ENDHERE;
3541 return $ret;
3542 } else {
3543 // return str_replace(' ', '&nbsp;', '('.__('No backup log)', 'updraftplus').')');
3544 }
3545 }
3546
3547 /**
3548 * Carry out the restore process
3549 *
3550 * @param Integer $timestamp - identifying the backup to be restored
3551 * @param Array|Null $continuation_data - for continuing a multi-stage restore (code believed to be incomplete)
3552 *
3553 * @return Boolean|WP_Error - WP_Error indicates a terminal failure; false indicates not-yet complete (not necessarily terminal); true indicates complete.
3554 */
3555 private function restore_backup($timestamp, $continuation_data = null) {
3556
3557 @set_time_limit(UPDRAFTPLUS_SET_TIME_LIMIT);
3558
3559 global $wp_filesystem, $updraftplus;
3560 $backup_history = UpdraftPlus_Options::get_updraft_option('updraft_backup_history');
3561 if (!isset($backup_history[$timestamp]) || !is_array($backup_history[$timestamp])) {
3562 echo '<p>'.__('This backup does not exist in the backup history - restoration aborted. Timestamp:', 'updraftplus')." $timestamp</p><br>";
3563 return new WP_Error('does_not_exist', __('Backup does not exist in the backup history', 'updraftplus'));
3564 }
3565
3566 // request_filesystem_credentials passes on fields just via hidden name/value pairs.
3567 // Build array of parameters to be passed via this
3568 $extra_fields = array();
3569 if (isset($_POST['updraft_restore']) && is_array($_POST['updraft_restore'])) {
3570 foreach ($_POST['updraft_restore'] as $entity) {
3571 $_POST['updraft_restore_'.$entity] = 1;
3572 $extra_fields[] = 'updraft_restore_'.$entity;
3573 }
3574 }
3575
3576 if (is_array($continuation_data)) {
3577 foreach ($continuation_data['second_loop_entities'] as $type => $files) {
3578 $_POST['updraft_restore_'.$type] = 1;
3579 if (!in_array('updraft_restore_'.$type, $extra_fields)) $extra_fields[] = 'updraft_restore_'.$type;
3580 }
3581 if (!empty($continuation_data['restore_options'])) $restore_options = $continuation_data['restore_options'];
3582 }
3583
3584 // Now make sure that updraft_restorer_ option fields get passed along to request_filesystem_credentials
3585 foreach ($_POST as $key => $value) {
3586 if (0 === strpos($key, 'updraft_restorer_')) $extra_fields[] = $key;
3587 }
3588
3589 $credentials = request_filesystem_credentials(UpdraftPlus_Options::admin_page()."?page=updraftplus&action=updraft_restore&backup_timestamp=$timestamp", '', false, false, $extra_fields);
3590 WP_Filesystem($credentials);
3591 if ($wp_filesystem->errors->get_error_code()) {
3592 echo '<p><em><a href="'.apply_filters('updraftplus_com_link', "https://updraftplus.com/faqs/asked-ftp-details-upon-restorationmigration-updates/").'">'.__('Why am I seeing this?', 'updraftplus').'</a></em></p>';
3593 foreach ($wp_filesystem->errors->get_error_messages() as $message) show_message($message);
3594 exit;
3595 }
3596
3597 // If we make it this far then WP_Filesystem has been instantiated and is functional
3598
3599 # Set up logging
3600 $updraftplus->backup_time_nonce();
3601 $updraftplus->jobdata_set('job_type', 'restore');
3602 $updraftplus->jobdata_set('job_time_ms', $updraftplus->job_time_ms);
3603 $updraftplus->logfile_open($updraftplus->nonce);
3604
3605 # Provide download link for the log file
3606
3607 # TODO: Automatic purging of old log files
3608 # TODO: Provide option to auto-email the log file
3609
3610 echo '<h1>'.__('UpdraftPlus Restoration: Progress', 'updraftplus').'</h1><div id="updraft-restore-progress">';
3611
3612 $this->show_admin_warning('<a target="_blank" href="?action=downloadlog&page=updraftplus&updraftplus_backup_nonce='.htmlspecialchars($updraftplus->nonce).'">'.__('Follow this link to download the log file for this restoration (needed for any support requests).', 'updraftplus').'</a>');
3613
3614 $updraft_dir = trailingslashit($updraftplus->backups_dir_location());
3615 $foreign_known = apply_filters('updraftplus_accept_archivename', array());
3616
3617 $service = (isset($backup_history[$timestamp]['service'])) ? $backup_history[$timestamp]['service'] : false;
3618 if (!is_array($service)) $service = array($service);
3619
3620 // Now, need to turn any updraft_restore_<entity> fields (that came from a potential WP_Filesystem form) back into parts of the _POST array (which we want to use)
3621 if (empty($_POST['updraft_restore']) || (!is_array($_POST['updraft_restore']))) $_POST['updraft_restore'] = array();
3622
3623 $backup_set = $backup_history[$timestamp];
3624 $entities_to_restore = array();
3625 foreach ($_POST['updraft_restore'] as $entity) {
3626 if (empty($backup_set['meta_foreign'])) {
3627 $entities_to_restore[$entity] = $entity;
3628 } else {
3629 if ('db' == $entity && !empty($foreign_known[$backup_set['meta_foreign']]) && !empty($foreign_known[$backup_set['meta_foreign']]['separatedb'])) {
3630 $entities_to_restore[$entity] = 'db';
3631 } else {
3632 $entities_to_restore[$entity] = 'wpcore';
3633 }
3634 }
3635 }
3636
3637 foreach ($_POST as $key => $value) {
3638 if (0 === strpos($key, 'updraft_restore_')) {
3639 $nkey = substr($key, 16);
3640 if (!isset($entities_to_restore[$nkey])) {
3641 $_POST['updraft_restore'][] = $nkey;
3642 if (empty($backup_set['meta_foreign'])) {
3643 $entities_to_restore[$nkey] = $nkey;
3644 } else {
3645 if ('db' == $entity && !empty($foreign_known[$backup_set['meta_foreign']]['separatedb'])) {
3646 $entities_to_restore[$nkey] = 'db';
3647 } else {
3648 $entities_to_restore[$nkey] = 'wpcore';
3649 }
3650 }
3651 }
3652 }
3653 }
3654
3655 if (0 == count($_POST['updraft_restore'])) {
3656 echo '<p>'.__('ABORT: Could not find the information on which entities to restore.', 'updraftplus').'</p>';
3657 echo '<p>'.__('If making a request for support, please include this information:', 'updraftplus').' '.count($_POST).' : '.htmlspecialchars(serialize($_POST)).'</p>';
3658 return new WP_Error('missing_info', 'Backup information not found');
3659 }
3660
3661 $this->entities_to_restore = $entities_to_restore;
3662
3663 set_error_handler(array($updraftplus, 'php_error'), E_ALL & ~E_STRICT);
3664
3665 /*
3666 $_POST['updraft_restore'] is typically something like: array(0=>'db', 1=>'plugins', 2=>'themes'), etc.
3667 i.e. array ('db', 'plugins', themes')
3668 */
3669
3670 if (empty($restore_options)) {
3671 // Gather the restore optons into one place - code after here should read the options, and not the HTTP layer
3672 $restore_options = array();
3673 if (!empty($_POST['updraft_restorer_restore_options'])) {
3674 parse_str(stripslashes($_POST['updraft_restorer_restore_options']), $restore_options);
3675 }
3676 $restore_options['updraft_restorer_replacesiteurl'] = empty($_POST['updraft_restorer_replacesiteurl']) ? false : true;
3677 $restore_options['updraft_encryptionphrase'] = empty($_POST['updraft_encryptionphrase']) ? '' : (string)stripslashes($_POST['updraft_encryptionphrase']);
3678 $restore_options['updraft_restorer_wpcore_includewpconfig'] = empty($_POST['updraft_restorer_wpcore_includewpconfig']) ? false : true;
3679 $updraftplus->jobdata_set('restore_options', $restore_options);
3680 }
3681
3682 // Restore in the most helpful order
3683 uksort($backup_set, array($this, 'sort_restoration_entities'));
3684
3685 // Now log
3686 $copy_restore_options = $restore_options;
3687 if (!empty($copy_restore_options['updraft_encryptionphrase'])) $copy_restore_options['updraft_encryptionphrase'] = '***';
3688 $updraftplus->log("Restore job started. Entities to restore: ".implode(', ', array_flip($entities_to_restore)).'. Restore options: '.json_encode($copy_restore_options));
3689
3690 $backup_set['timestamp'] = $timestamp;
3691
3692 $backupable_entities = $updraftplus->get_backupable_file_entities(true, true);
3693
3694 // Allow add-ons to adjust the restore directory (but only in the case of restore - otherwise, they could just use the filter built into UpdraftPlus::get_backupable_file_entities)
3695 $backupable_entities = apply_filters('updraft_backupable_file_entities_on_restore', $backupable_entities, $restore_options, $backup_set);
3696
3697 // We use a single object for each entity, because we want to store information about the backup set
3698 require_once(UPDRAFTPLUS_DIR.'/restorer.php');
3699
3700 global $updraftplus_restorer;
3701
3702 $updraftplus_restorer = new Updraft_Restorer(new Updraft_Restorer_Skin, $backup_set, false, $restore_options);
3703
3704 $second_loop = array();
3705
3706 echo "<h2>".__('Final checks', 'updraftplus').'</h2>';
3707
3708 if (empty($backup_set['meta_foreign'])) {
3709 $entities_to_download = $entities_to_restore;
3710 } else {
3711 if (!empty($foreign_known[$backup_set['meta_foreign']]['separatedb'])) {
3712 $entities_to_download = array();
3713 if (in_array('db', $entities_to_restore)) {
3714 $entities_to_download['db'] = 1;
3715 }
3716 if (count($entities_to_restore) > 1 || !in_array('db', $entities_to_restore)) {
3717 $entities_to_download['wpcore'] = 1;
3718 }
3719 } else {
3720 $entities_to_download = array('wpcore' => 1);
3721 }
3722 }
3723
3724 // First loop: make sure that files are present + readable; and populate array for second loop
3725 foreach ($backup_set as $type => $files) {
3726 // All restorable entities must be given explicitly, as we can store other arbitrary data in the history array
3727 if (!isset($backupable_entities[$type]) && 'db' != $type) continue;
3728 if (isset($backupable_entities[$type]['restorable']) && $backupable_entities[$type]['restorable'] == false) continue;
3729
3730 if (!isset($entities_to_download[$type])) continue;
3731 if ('wpcore' == $type && is_multisite() && 0 === $updraftplus_restorer->ud_backup_is_multisite) {
3732 echo "<p>$type: <strong>";
3733 $updraftplus->log(__('Skipping restoration of WordPress core when importing a single site into a multisite installation. If you had anything necessary in your WordPress directory then you will need to re-add it manually from the zip file.', 'updraftplus'), 'notice-restore');
3734 #TODO
3735 #$updraftplus->log_e('Skipping restoration of WordPress core when importing a single site into a multisite installation. If you had anything necessary in your WordPress directory then you will need to re-add it manually from the zip file.');
3736 echo "</strong></p>";
3737 continue;
3738 }
3739
3740 if (is_string($files)) $files = array($files);
3741
3742 foreach ($files as $ind => $file) {
3743
3744 $fullpath = $updraft_dir.$file;
3745 $updraftplus->log(sprintf(__("Looking for %s archive: file name: %s", 'updraftplus'), $type, $file), 'notice-restore');
3746
3747 if (is_array($continuation_data) && isset($continuation_data['second_loop_entities'][$type]) && !in_array($file, $continuation_data['second_loop_entities'][$type])) {
3748 echo __('Skipping: this archive was already restored.', 'updraftplus')."<br>";
3749 // Set the marker so that the existing directory isn't moved out of the way
3750 $updraftplus_restorer->been_restored[$type] = true;
3751 continue;
3752 }
3753
3754 add_action('http_request_args', array($updraftplus, 'modify_http_options'));
3755 foreach ($service as $serv) {
3756 if (!is_readable($fullpath)) {
3757 $sd = (empty($updraftplus->backup_methods[$serv])) ? $serv : $updraftplus->backup_methods[$serv];
3758 $updraftplus->log(__("File is not locally present - needs retrieving from remote storage",'updraftplus')." ($sd)", 'notice-restore');
3759 $this->download_file($file, $serv);
3760 if (!is_readable($fullpath)) {
3761 $updraftplus->log(__("Error", 'updraftplus'), 'notice-restore');
3762 } else {
3763 $updraftplus->log(__("OK", 'updraftplus'), 'notice-restore');
3764 }
3765 }
3766 }
3767 remove_action('http_request_args', array($updraftplus, 'modify_http_options'));
3768
3769 $index = ($ind == 0) ? '' : $ind;
3770 // If a file size is stored in the backup data, then verify correctness of the local file
3771 if (isset($backup_history[$timestamp][$type.$index.'-size'])) {
3772 $fs = $backup_history[$timestamp][$type.$index.'-size'];
3773 $print_message = __("Archive is expected to be size:",'updraftplus')." ".round($fs/1024, 1)." KB: ";
3774 $as = @filesize($fullpath);
3775 if ($as == $fs) {
3776 $updraftplus->log($print_message.__('OK', 'updraftplus'), 'notice-restore');
3777 } else {
3778 $updraftplus->log($print_message.__('Error:', 'updraftplus')." ".__('file is size:', 'updraftplus')." ".round($as/1024)." ($fs, $as)", 'warning-restore');
3779 }
3780 } else {
3781 $updraftplus->log(__("The backup records do not contain information about the proper size of this file.",'updraftplus'), 'notice-restore');
3782 }
3783 if (!is_readable($fullpath)) {
3784 $updraftplus->log(__('Could not find one of the files for restoration', 'updraftplus')." ($file)", 'warning-restore');
3785 $updraftplus->log("$file: ".__('Could not find one of the files for restoration', 'updraftplus'), 'error');
3786 echo '</div>';
3787 restore_error_handler();
3788 return false;
3789 }
3790 }
3791
3792 if (empty($updraftplus_restorer->ud_foreign)) {
3793 $types = array($type);
3794 } else {
3795 if ('db' != $type || empty($foreign_known[$updraftplus_restorer->ud_foreign]['separatedb'])) {
3796 $types = array('wpcore');
3797 } else {
3798 $types = array('db');
3799 }
3800 }
3801
3802 foreach ($types as $check_type) {
3803 $info = (isset($backupable_entities[$check_type])) ? $backupable_entities[$check_type] : array();
3804 $val = $updraftplus_restorer->pre_restore_backup($files, $check_type, $info, $continuation_data);
3805 if (is_wp_error($val)) {
3806 $updraftplus->log_wp_error($val);
3807 foreach ($val->get_error_messages() as $msg) {
3808 $updraftplus->log(__('Error:', 'updraftplus').' '.$msg, 'warning-restore');
3809 }
3810 foreach ($val->get_error_codes() as $code) {
3811 if ('already_exists' == $code) $this->print_delete_old_dirs_form(false);
3812 }
3813 echo '</div>'; //close the updraft_restore_progress div even if we error
3814 restore_error_handler();
3815 return $val;
3816 } elseif (false === $val) {
3817 echo '</div>'; //close the updraft_restore_progress div even if we error
3818 restore_error_handler();
3819 return false;
3820 }
3821 }
3822
3823 foreach ($entities_to_restore as $entity => $via) {
3824 if ($via == $type) {
3825 if ('wpcore' == $via && 'db' == $entity && count($files) > 1) {
3826 $second_loop[$entity] = apply_filters('updraftplus_select_wpcore_file_with_db', $files, $updraftplus_restorer->ud_foreign);
3827 } else {
3828 $second_loop[$entity] = $files;
3829 }
3830 }
3831 }
3832
3833 }
3834
3835 $updraftplus_restorer->delete = (UpdraftPlus_Options::get_updraft_option('updraft_delete_local')) ? true : false;
3836 if ('none' === $service || 'email' === $service || empty($service) || (is_array($service) && 1 == count($service) && (in_array('none', $service) || in_array('', $service) || in_array('email', $service))) || !empty($updraftplus_restorer->ud_foreign)) {
3837 if ($updraftplus_restorer->delete) $updraftplus->log_e('Will not delete any archives after unpacking them, because there was no cloud storage for this backup');
3838 $updraftplus_restorer->delete = false;
3839 }
3840
3841 if (!empty($updraftplus_restorer->ud_foreign)) $updraftplus->log("Foreign backup; created by: ".$updraftplus_restorer->ud_foreign);
3842
3843 // Second loop: now actually do the restoration
3844 uksort($second_loop, array($this, 'sort_restoration_entities'));
3845
3846 // If continuing, then prune those already done
3847 if (is_array($continuation_data)) {
3848 foreach ($second_loop as $type => $files) {
3849 if (isset($continuation_data['second_loop_entities'][$type])) $second_loop[$type] = $continuation_data['second_loop_entities'][$type];
3850 }
3851 }
3852
3853 $updraftplus->jobdata_set('second_loop_entities', $second_loop);
3854 $updraftplus->jobdata_set('backup_timestamp', $timestamp);
3855 // use a site option, as otherwise on multisite when all the array of options is updated via UpdraftPlus_Options::update_site_option(), it will over-write any restored UD options from the backup
3856 update_site_option('updraft_restore_in_progress', $updraftplus->nonce);
3857
3858 foreach ($second_loop as $type => $files) {
3859 # Types: uploads, themes, plugins, others, db
3860 $info = (isset($backupable_entities[$type])) ? $backupable_entities[$type] : array();
3861
3862 echo ('db' == $type) ? "<h2>".__('Database', 'updraftplus')."</h2>" : "<h2>".$info['description']."</h2>";
3863 $updraftplus->log("Entity: ".$type);
3864
3865 if (is_string($files)) $files = array($files);
3866 foreach ($files as $fkey => $file) {
3867 $last_one = (1 == count($second_loop) && 1 == count($files));
3868 try {
3869 $val = $updraftplus_restorer->restore_backup($file, $type, $info, $last_one);
3870 } catch (Exception $e) {
3871 $log_message = 'Exception ('.get_class($e).') occurred during restore: '.$e->getMessage().' (Code: '.$e->getCode().', line '.$e->getLine().' in '.$e->getFile().')';
3872 error_log($log_message);
3873 $display_log_message = sprintf(__('A PHP exception (%s) has occurred: %s', 'updraftplus'), get_class($e), $e->getMessage());
3874 $updraftplus->log($log_message);
3875 $updraftplus->log($display_log_message, 'notice-restore');
3876 die();
3877 // @codingStandardsIgnoreLine
3878 } catch (Error $e) {
3879 $log_message = 'PHP Fatal error ('.get_class($e).') has occurred. Error Message: '.$e->getMessage().' (Code: '.$e->getCode().', line '.$e->getLine().' in '.$e->getFile().')';
3880 error_log($log_message);
3881 $display_log_message = sprintf(__('A PHP fatal error (%s) has occurred: %s', 'updraftplus'), get_class($e), $e->getMessage());
3882 $updraftplus->log($log_message);
3883 $updraftplus->log($display_log_message, 'notice-restore');
3884 die();
3885 }
3886 if (is_wp_error($val)) {
3887 $codes = $val->get_error_codes();
3888 if (is_array($codes) && in_array('not_found', $codes) && !empty($updraftplus_restorer->ud_foreign) && apply_filters('updraftplus_foreign_allow_missing_entity', false, $type, $updraftplus_restorer->ud_foreign)) {
3889 $updraftplus->log("Entity to move not found in this zip - but this is possible with this foreign backup type");
3890 } else {
3891
3892 $updraftplus->log_e($val);
3893 foreach ($val->get_error_messages() as $msg) {
3894 $updraftplus->log(__('Error message', 'updraftplus').': '.$msg, 'notice-restore');
3895 }
3896 $codes = $val->get_error_codes();
3897 if (is_array($codes)) {
3898 foreach ($codes as $code) {
3899 $data = $val->get_error_data($code);
3900 if (!empty($data)) {
3901 $pdata = (is_string($data)) ? $data : serialize($data);
3902 $updraftplus->log(__('Error data:', 'updraftplus').' '.$pdata, 'warning-restore');
3903 if (false !== strpos($pdata, 'PCLZIP_ERR_BAD_FORMAT (-10)')) {
3904 echo '<a href="'.apply_filters('updraftplus_com_link', "https://updraftplus.com/faqs/error-message-pclzip_err_bad_format-10-invalid-archive-structure-mean/").'"><strong>'.__('Please consult this FAQ for help on what to do about it.', 'updraftplus').'</strong></a><br>';
3905 }
3906 }
3907 }
3908 }
3909 echo '</div>'; //close the updraft_restore_progress div even if we error
3910 restore_error_handler();
3911 return $val;
3912 }
3913 } elseif (false === $val) {
3914 echo '</div>'; //close the updraft_restore_progress div even if we error
3915 restore_error_handler();
3916 return false;
3917 }
3918 unset($files[$fkey]);
3919 $second_loop[$type] = $files;
3920 $updraftplus->jobdata_set('second_loop_entities', $second_loop);
3921 $updraftplus->jobdata_set('backup_timestamp', $timestamp);
3922
3923 do_action('updraft_restored_archive', $file, $type, $val, $fkey, $timestamp);
3924
3925 }
3926 unset($second_loop[$type]);
3927 update_site_option('updraft_restore_in_progress', $updraftplus->nonce);
3928 $updraftplus->jobdata_set('second_loop_entities', $second_loop);
3929 $updraftplus->jobdata_set('backup_timestamp', $timestamp);
3930 }
3931
3932 // All done - remove
3933 delete_site_option('updraft_restore_in_progress');
3934
3935 foreach (array('template', 'stylesheet', 'template_root', 'stylesheet_root') as $opt) {
3936 add_filter('pre_option_'.$opt, array($this, 'option_filter_'.$opt));
3937 }
3938
3939 # Clear any cached pages after the restore
3940 $updraftplus_restorer->clear_cache();
3941
3942 if (!function_exists('validate_current_theme')) require_once(ABSPATH.WPINC.'/themes');
3943
3944 # Have seen a case where the current theme in the DB began with a capital, but not on disk - and this breaks migrating from Windows to a case-sensitive system
3945 $template = get_option('template');
3946 if (!empty($template) && $template != WP_DEFAULT_THEME && $template != strtolower($template)) {
3947
3948 $theme_root = get_theme_root($template);
3949 $theme_root2 = get_theme_root(strtolower($template));
3950
3951 if (!file_exists("$theme_root/$template/style.css") && file_exists("$theme_root/".strtolower($template)."/style.css")) {
3952 $updraftplus->log_e("Theme directory (%s) not found, but lower-case version exists; updating database option accordingly", $template);
3953 update_option('template', strtolower($template));
3954 }
3955
3956 }
3957
3958 if (!validate_current_theme()) {
3959 echo '<strong>';
3960 $updraftplus->log_e("The current theme was not found; to prevent this stopping the site from loading, your theme has been reverted to the default theme");
3961 echo '</strong>';
3962 }
3963
3964 echo '</div>'; //close the updraft_restore_progress div
3965
3966 restore_error_handler();
3967 return true;
3968 }
3969
3970 public function option_filter_template($val) { global $updraftplus; return $updraftplus->option_filter_get('template'); }
3971
3972 public function option_filter_stylesheet($val) { global $updraftplus; return $updraftplus->option_filter_get('stylesheet'); }
3973
3974 public function option_filter_template_root($val) { global $updraftplus; return $updraftplus->option_filter_get('template_root'); }
3975
3976 public function option_filter_stylesheet_root($val) { global $updraftplus; return $updraftplus->option_filter_get('stylesheet_root'); }
3977
3978 public function sort_restoration_entities($a, $b) {
3979 if ($a == $b) return 0;
3980 // Put the database first
3981 // Put wpcore after plugins/uploads/themes (needed for restores of foreign all-in-one formats)
3982 if ('db' == $a || 'wpcore' == $b) return -1;
3983 if ('db' == $b || 'wpcore' == $a) return 1;
3984 // After wpcore, next last is others
3985 if ('others' == $b) return -1;
3986 if ('others' == $a) return 1;
3987 // And then uploads - this is only because we want to make sure uploads is after plugins, so that we know before we get to the uploads whether the version of UD which might have to unpack them can do this new-style or not.
3988 if ('uploads' == $b) return -1;
3989 if ('uploads' == $a) return 1;
3990 return strcmp($a, $b);
3991 }
3992
3993 public function return_array($input) {
3994 if (!is_array($input)) $input = array();
3995 return $input;
3996 }
3997
3998 public function updraft_ajax_savesettings() {
3999 global $updraftplus;
4000
4001 if (empty($_POST) || empty($_POST['subaction']) || 'savesettings' != $_POST['subaction'] || !isset($_POST['nonce']) || !is_user_logged_in() || !UpdraftPlus_Options::user_can_manage() || !wp_verify_nonce($_POST['nonce'], 'updraftplus-settings-nonce')) die('Security check');
4002
4003 if (empty($_POST['settings']) || !is_string($_POST['settings'])) die('Invalid data');
4004
4005 parse_str(stripslashes($_POST['settings']), $posted_settings);
4006 // We now have $posted_settings as an array
4007 if (!empty($_POST['updraftplus_version'])) $posted_settings['updraftplus_version'] = $_POST['updraftplus_version'];
4008
4009 echo json_encode($this->save_settings($posted_settings));
4010
4011 die;
4012 }
4013
4014 public function updraft_ajax_importsettings() {
4015 global $updraftplus;
4016
4017 if (empty($_POST) || empty($_POST['subaction']) || 'importsettings' != $_POST['subaction'] || !isset($_POST['nonce']) || !is_user_logged_in() || !UpdraftPlus_Options::user_can_manage() || !wp_verify_nonce($_POST['nonce'], 'updraftplus-settings-nonce')) die('Security check');
4018
4019 if (empty($_POST['settings']) || !is_string($_POST['settings'])) die('Invalid data');
4020
4021 $this->import_settings($_POST);
4022 }
4023
4024 /**
4025 * This method handles the imported json settings it will convert them into a readable format for the existing save settings function, it will also update some of the options to match the new remote storage options format (Apr 2017)
4026 *
4027 * @param Array $settings - The settings from the imported json file
4028 */
4029 public function import_settings($settings) {
4030 global $updraftplus;
4031
4032 // A bug in UD releases around 1.12.40 - 1.13.3 meant that it was saved in URL-string format, instead of JSON
4033 $perhaps_not_yet_parsed = json_decode(stripslashes($settings['settings']), true);
4034
4035 if (!is_array($perhaps_not_yet_parsed)) {
4036 parse_str($perhaps_not_yet_parsed, $posted_settings);
4037 } else {
4038 $posted_settings = $perhaps_not_yet_parsed;
4039 }
4040
4041 if (!empty($settings['updraftplus_version'])) $posted_settings['updraftplus_version'] = $settings['updraftplus_version'];
4042
4043 // Handle the settings name change of WebDAV and SFTP (Apr 2017) if someone tries to import an old settings to this version
4044 if (isset($posted_settings['updraft_webdav_settings'])) {
4045 $posted_settings['updraft_webdav'] = $posted_settings['updraft_webdav_settings'];
4046 unset($posted_settings['updraft_webdav_settings']);
4047 }
4048
4049 if (isset($posted_settings['updraft_sftp_settings'])) {
4050 $posted_settings['updraft_sftp'] = $posted_settings['updraft_sftp_settings'];
4051 unset($posted_settings['updraft_sftp_settings']);
4052 }
4053
4054 // We also need to wrap some of the options in the new style settings array otherwise later on we will lose the settings if this information is missing
4055 if (empty($posted_settings['updraft_webdav']['settings'])) $posted_settings['updraft_webdav'] = $updraftplus->wrap_remote_storage_options($posted_settings['updraft_webdav']);
4056 if (empty($posted_settings['updraft_googledrive']['settings'])) $posted_settings['updraft_googledrive'] = $updraftplus->wrap_remote_storage_options($posted_settings['updraft_googledrive']);
4057 if (empty($posted_settings['updraft_googlecloud']['settings'])) $posted_settings['updraft_googlecloud'] = $updraftplus->wrap_remote_storage_options($posted_settings['updraft_googlecloud']);
4058 if (empty($posted_settings['updraft_onedrive']['settings'])) $posted_settings['updraft_onedrive'] = $updraftplus->wrap_remote_storage_options($posted_settings['updraft_onedrive']);
4059 if (empty($posted_settings['updraft_azure']['settings'])) $posted_settings['updraft_azure'] = $updraftplus->wrap_remote_storage_options($posted_settings['updraft_azure']);
4060 if (empty($posted_settings['updraft_dropbox']['settings'])) $posted_settings['updraft_dropbox'] = $updraftplus->wrap_remote_storage_options($posted_settings['updraft_dropbox']);
4061
4062 echo json_encode($this->save_settings($posted_settings));
4063
4064 die;
4065 }
4066
4067 private function backup_now_remote_message() {
4068 global $updraftplus;
4069
4070 $service = $updraftplus->just_one(UpdraftPlus_Options::get_updraft_option('updraft_service'));
4071 if (is_string($service)) $service = array($service);
4072 if (!is_array($service)) $service = array();
4073
4074 $no_remote_configured = (empty($service) || array('none') === $service || array('') === $service) ? true : false;
4075
4076 if ($no_remote_configured) {
4077 return '<input type="checkbox" disabled="disabled" id="backupnow_includecloud"> <em>'.sprintf(__("Backup won't be sent to any remote storage - none has been saved in the %s", 'updraftplus'), '<a href="'.UpdraftPlus_Options::admin_page_url().'?page=updraftplus&amp;tab=settings" id="updraft_backupnow_gotosettings">'.__('settings', 'updraftplus')).'</a>. '.__('Not got any remote storage?', 'updraftplus').' <a href="'.apply_filters('updraftplus_com_link', "https://updraftplus.com/landing/vault/").'">'.__("Check out UpdraftPlus Vault.", 'updraftplus').'</a></em>';
4078 } else {
4079 return '<input type="checkbox" id="backupnow_includecloud" checked="checked"> <label for="backupnow_includecloud">'.__("Send this backup to remote storage", 'updraftplus').'</label>';
4080 }
4081 }
4082
4083 /**
4084 * This method works through the passed in settings array and saves the settings to the database clearing old data and setting up a return array with content to update the page via ajax
4085 * @param [Array] $settings - an array of settings taking from the admin page ready to be saved to the database
4086 * @return [Array] - an array response containing the status of the update along with content to be used to update the admin page.
4087 */
4088 public function save_settings($settings) {
4089
4090 global $updraftplus;
4091
4092 // Make sure that settings filters are registered
4093 UpdraftPlus_Options::admin_init();
4094
4095 $more_files_path_updated = false;
4096
4097 if (isset($settings['updraftplus_version']) && $updraftplus->version == $settings['updraftplus_version']) {
4098
4099 $return_array = array('saved' => true);
4100
4101 $add_to_post_keys = array('updraft_interval', 'updraft_interval_database', 'updraft_starttime_files', 'updraft_starttime_db', 'updraft_startday_files', 'updraft_startday_db');
4102
4103 //If database and files are on same schedule, override the db day/time settings
4104 if (isset($settings['updraft_interval_database']) && isset($settings['updraft_interval_database']) && $settings['updraft_interval_database'] == $settings['updraft_interval'] && isset($settings['updraft_starttime_files'])) {
4105 $settings['updraft_starttime_db'] = $settings['updraft_starttime_files'];
4106 $settings['updraft_startday_db'] = $settings['updraft_startday_files'];
4107 }
4108 foreach ($add_to_post_keys as $key) {
4109 // For add-ons that look at $_POST to find saved settings, add the relevant keys to $_POST so that they find them there
4110 if (isset($settings[$key])) {
4111 $_POST[$key] = $settings[$key];
4112 }
4113 }
4114
4115 // Check if updraft_include_more_path is set, if it is then we need to update the page, if it's not set but there's content already in the database that is cleared down below so again we should update the page.
4116 $more_files_path_updated = false;
4117
4118 // i.e. If an option has been set, or if it was currently active in the settings
4119 if (isset($settings['updraft_include_more_path']) || UpdraftPlus_Options::get_updraft_option('updraft_include_more_path')) {
4120 $more_files_path_updated = true;
4121 }
4122
4123 // Wipe the extra retention rules, as they are not saved correctly if the last one is deleted
4124 UpdraftPlus_Options::update_updraft_option('updraft_retain_extrarules', array());
4125 UpdraftPlus_Options::update_updraft_option('updraft_email', array());
4126 UpdraftPlus_Options::update_updraft_option('updraft_report_warningsonly', array());
4127 UpdraftPlus_Options::update_updraft_option('updraft_report_wholebackup', array());
4128 UpdraftPlus_Options::update_updraft_option('updraft_extradbs', array());
4129 UpdraftPlus_Options::update_updraft_option('updraft_include_more_path', array());
4130
4131 $relevant_keys = $updraftplus->get_settings_keys();
4132
4133 if (method_exists('UpdraftPlus_Options', 'mass_options_update')) {
4134 $original_settings = $settings;
4135 $settings = UpdraftPlus_Options::mass_options_update($settings);
4136 $mass_updated = true;
4137 }
4138
4139 foreach ($settings as $key => $value) {
4140
4141 if (in_array($key, $relevant_keys)) {
4142 if ($key == 'updraft_service' && is_array($value)){
4143 foreach ($value as $subkey => $subvalue){
4144 if ($subvalue == '0') unset($value[$subkey]);
4145 }
4146 }
4147
4148 // This flag indicates that either the stored database option was changed, or that the supplied option was changed before being stored. It isn't comprehensive - it's only used to update some UI elements with invalid input.
4149 $updated = empty($mass_updated) ? (is_string($value) && $value != UpdraftPlus_Options::get_updraft_option($key)) : (is_string($value) && (!isset($original_settings[$key]) || $original_settings[$key] != $value));
4150
4151 $db_updated = empty($mass_updated) ? UpdraftPlus_Options::update_updraft_option($key, $value) : true;
4152
4153 // Add information on what has changed to array to loop through to update links etc.
4154 // Restricting to strings for now, to prevent any unintended leakage (since this is just used for UI updating)
4155 if ($updated) {
4156 $value = UpdraftPlus_Options::get_updraft_option($key);
4157 if (is_string($value)) $return_array['changed'][$key] = $value;
4158 }
4159
4160 } else {
4161 // When last active, it was catching: option_page, action, _wpnonce, _wp_http_referer, updraft_s3_endpoint, updraft_dreamobjects_endpoint. The latter two are empty; probably don't need to be in the page at all.
4162 //error_log("Non-UD key when saving from POSTed data: ".$key);
4163 }
4164 }
4165 } else {
4166 $return_array = array('saved' => false, 'error_message' => sprintf(__('UpdraftPlus seems to have been updated to version (%s), which is different to the version running when this settings page was loaded. Please reload the settings page before trying to save settings.', 'updraftplus'), $updraftplus->version));
4167 }
4168
4169 // Checking for various possible messages
4170 $updraft_dir = $updraftplus->backups_dir_location(false);
4171 $really_is_writable = $updraftplus->really_is_writable($updraft_dir);
4172 $dir_info = $this->really_writable_message($really_is_writable, $updraft_dir);
4173 $button_title = esc_attr(__('This button is disabled because your backup directory is not writable (see the settings).', 'updraftplus'));
4174
4175 $return_array['backup_now_message'] = $this->backup_now_remote_message();
4176
4177 $return_array['backup_dir'] = array('writable' => $really_is_writable, 'message' => $dir_info, 'button_title' => $button_title);
4178
4179 // Check if $more_files_path_updated is true, is so then there's a change and we should update the backup modal
4180 if ($more_files_path_updated) {
4181 $return_array['updraft_include_more_path'] = $this->files_selector_widgetry('backupnow_files_', false, 'sometimes');
4182 }
4183
4184 //Because of the single AJAX call, we need to remove the existing UD messages from the 'all_admin_notices' action
4185 remove_all_actions('all_admin_notices');
4186
4187 //Moving from 2 to 1 ajax call
4188 ob_start();
4189
4190 $service = UpdraftPlus_Options::get_updraft_option('updraft_service');
4191
4192 $this->setup_all_admin_notices_global($service);
4193 $this->setup_all_admin_notices_udonly($service);
4194
4195 do_action('all_admin_notices');
4196
4197 if (!$really_is_writable) { //Check if writable
4198 $this->show_admin_warning_unwritable();
4199 }
4200
4201 if ($return_array['saved']) { //
4202 $this->show_admin_warning(__('Your settings have been saved.', 'updraftplus'), 'updated fade');
4203 } else {
4204 if (isset($return_array['error_message'])) {
4205 $this->show_admin_warning($return_array['error_message'], 'error');
4206 } else {
4207 $this->show_admin_warning(__('Your settings failed to save. Please refresh the settings page and try again', 'updraftplus'), 'error');
4208 }
4209 }
4210
4211 $messages_output = ob_get_contents();
4212
4213 ob_clean();
4214
4215 // Backup schedule output
4216 $this->next_scheduled_backups_output();
4217
4218 $scheduled_output = ob_get_clean();
4219
4220 $return_array['messages'] = $messages_output;
4221 $return_array['scheduled'] = $scheduled_output;
4222
4223 //*** Add the updated options to the return message, so we can update on screen ***\\
4224
4225
4226 return $return_array;
4227
4228 }
4229
4230 /**
4231 * A method to remove UpdraftPlus settings from the options table.
4232 * @param boolean $wipe_all_settings Set to true as default as we want to remove all options, set to false if calling from UpdraftCentral, as we do not want to remove the UpdraftCentral key or we will lose connection to the site.
4233 */
4234 public function updraft_wipe_settings($wipe_all_settings = true) {
4235
4236 global $updraftplus;
4237
4238 $settings = $updraftplus->get_settings_keys();
4239
4240 // if this is false the UDC has called it we don't want to remove the UDC key other wise we will lose connection to the remote site.
4241 if (false == $wipe_all_settings) {
4242 $key = array_search('updraft_central_localkeys', $settings);
4243 unset($settings[$key]);
4244 }
4245
4246 foreach ($settings as $s) UpdraftPlus_Options::delete_updraft_option($s);
4247
4248 // These aren't in get_settings_keys() because they are always in the options table, regardless of context
4249 global $wpdb;
4250 $wpdb->query("DELETE FROM $wpdb->options WHERE (option_name LIKE 'updraftplus_unlocked_%' OR option_name LIKE 'updraftplus_locked_%' OR option_name LIKE 'updraftplus_last_lock_time_%' OR option_name LIKE 'updraftplus_semaphore_%' OR option_name LIKE 'updraft_jobdata_%' OR option_name LIKE 'updraft_last_scheduled_%' )");
4251
4252 $site_options = array('updraft_oneshotnonce');
4253 foreach ($site_options as $s) delete_site_option($s);
4254
4255 $this->show_admin_warning(__("Your settings have been wiped.", 'updraftplus'));
4256
4257 return true;
4258 }
4259
4260 //this get the details for updraft vault and to be used globally
4261 public function get_updraftvault() {
4262 require_once(UPDRAFTPLUS_DIR.'/methods/updraftvault.php');
4263 $vault = new UpdraftPlus_BackupModule_updraftvault();
4264 return $vault;
4265 }
4266
4267 // http_get will allow the HTTP Fetch execute available in advanced tools
4268 public function http_get($uri = null, $curl = false) {
4269
4270 if (!preg_match('/^https?/', $uri)) return json_encode(array('e' => 'Non-http URL specified'));
4271
4272 if ($curl) {
4273 if (!function_exists('curl_exec')) {
4274 return json_encode(array('e' => 'No Curl installed'));
4275 die;
4276 }
4277 $ch = curl_init();
4278 curl_setopt($ch, CURLOPT_URL, $uri);
4279 curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
4280 curl_setopt($ch, CURLOPT_FAILONERROR, true);
4281 curl_setopt($ch, CURLOPT_HEADER, false);
4282 curl_setopt($ch, CURLOPT_VERBOSE, true);
4283 curl_setopt($ch, CURLOPT_STDERR, $output=fopen('php://temp', "w+"));
4284 $response = curl_exec($ch);
4285 $error = curl_error($ch);
4286 $getinfo = curl_getinfo($ch);
4287 curl_close($ch);
4288
4289 rewind($output);
4290 $verb = stream_get_contents($output);
4291
4292 $resp = array();
4293 if (false === $response) {
4294 $resp['e'] = htmlspecialchars($error);
4295 }
4296 $resp['r'] = (empty($response)) ? '' : htmlspecialchars(substr($response, 0, 2048));
4297
4298 if (!empty($verb)) $resp['r'] = htmlspecialchars($verb)."\n\n".$resp['r'];
4299
4300 //extra info returned for Central
4301 $resp['verb'] = $verb;
4302 $resp['response'] = $response;
4303 $resp['status'] = $getinfo;
4304
4305 return json_encode($resp);
4306 // echo json_encode(array('r' => htmlspecialchars(substr($response, 0, 2048))));
4307 } else {
4308 $response = wp_remote_get($uri, array('timeout' => 10));
4309 if (is_wp_error($response)) {
4310 return json_encode(array('e' => htmlspecialchars($response->get_error_message())));
4311 }
4312 return json_encode(
4313 array(
4314 'r' => wp_remote_retrieve_response_code($response).': '.htmlspecialchars(substr(wp_remote_retrieve_body($response), 0, 2048)),
4315 'code' => wp_remote_retrieve_response_code($response),
4316 'html_response' => htmlspecialchars(substr(wp_remote_retrieve_body($response), 0, 2048)),
4317 'response' => $response
4318 )
4319 );
4320 }
4321 }
4322
4323 //This will bring back all the details for raw backup and file list
4324 public function show_raw_backups($no_pre_tags = false){
4325 global $updraftplus;
4326
4327 $response = array();
4328
4329 $response['html'] = '<h3 id="ud-debuginfo-rawbackups">'.__('Known backups (raw)', 'updraftplus').'</h3><pre>';
4330 ob_start();
4331 var_dump($updraftplus->get_backup_history());
4332 $response["html"] .= ob_get_clean();
4333 $response['html'] .= '</pre>';
4334
4335 $response['html'] .= '<h3 id="ud-debuginfo-files">'.__('Files', 'updraftplus').'</h3><pre>';
4336 $updraft_dir = $updraftplus->backups_dir_location();
4337 $raw_output = array();
4338 $d = dir($updraft_dir);
4339 while (false !== ($entry = $d->read())) {
4340 $fp = $updraft_dir.'/'.$entry;
4341 $mtime = filemtime($fp);
4342 if (is_dir($fp)) {
4343 $size = ' d';
4344 } elseif (is_link($fp)) {
4345 $size = ' l';
4346 } elseif (is_file($fp)) {
4347 $size = sprintf("%8.1f", round(filesize($fp)/1024, 1)).' '.gmdate('r', $mtime);
4348 } else {
4349 $size = ' ?';
4350 }
4351 if (preg_match('/^log\.(.*)\.txt$/', $entry, $lmatch)) $entry = '<a target="_top" href="?action=downloadlog&amp;page=updraftplus&amp;updraftplus_backup_nonce='.htmlspecialchars($lmatch[1]).'">'.$entry.'</a>';
4352 $raw_output[$mtime] = empty($raw_output[$mtime]) ? sprintf("%s %s\n", $size, $entry) : $raw_output[$mtime].sprintf("%s %s\n", $size, $entry);
4353 }
4354 @$d->close();
4355 krsort($raw_output, SORT_NUMERIC);
4356 foreach ($raw_output as $line){ $response['html'] .= $line; }
4357 $response['html'] .= '</pre>';
4358
4359 $response['html'] .= '<h3 id="ud-debuginfo-options">'.__('Options (raw)', 'updraftplus').'</h3>';
4360 $opts = $updraftplus->get_settings_keys();
4361 asort($opts);
4362 // <tr><th>'.__('Key', 'updraftplus').'</th><th>'.__('Value', 'updraftplus').'</th></tr>
4363 $response['html'] .= '<table><thead></thead><tbody>';
4364 foreach ($opts as $opt) {
4365 $response['html'] .= '<tr><td>'.htmlspecialchars($opt).'</td><td>'.htmlspecialchars(print_r(UpdraftPlus_Options::get_updraft_option($opt), true)).'</td>';
4366 }
4367 $response['html'] .= '</tbody></table>';
4368
4369 ob_start();
4370 do_action('updraftplus_showrawinfo');
4371 $response['html'] .= ob_get_clean();
4372
4373 if (true == $no_pre_tags) {
4374 $response['html'] = str_replace('<pre>','',$response['html']);
4375 $response['html'] = str_replace('</pre>','',$response['html']);
4376 }
4377
4378 return $response;
4379 }
4380
4381 // This will call any wp_action
4382 public function call_wp_action($data = null, $close_connection_callable = false) {
4383 global $updraftplus;
4384
4385 ob_start();
4386
4387 $res = '<em>Request received: </em>';
4388
4389 if (preg_match('/^([^:]+)+:(.*)$/', stripslashes($data['wpaction']), $matches)) {
4390 $action = $matches[1];
4391 if (null === ($args = json_decode($matches[2], true))) {
4392 $res .= "The parameters (should be JSON) could not be decoded";
4393 $action = false;
4394 } else {
4395 if (is_string($args)) $args = array($args);
4396 $res .= "Will despatch action: ".htmlspecialchars($action).", parameters: ".htmlspecialchars(implode(',', $args));
4397 }
4398 } else {
4399 $action = $data['wpaction'];
4400 $res .= "Will despatch action: ".htmlspecialchars($action).", no parameters";
4401 }
4402
4403 $ret = ob_get_clean();
4404
4405 //need to add this as the close browser should only work for UDP
4406 if ($close_connection_callable) {
4407 if (is_callable($close_connection_callable)) {
4408 call_user_func($close_connection_callable, array('r' => $res));
4409 } else {
4410 $updraftplus->close_browser_connection(json_encode(array('r' => $res)));
4411 }
4412 }
4413
4414 if (!empty($action)) {
4415 if (!empty($args)) {
4416 ob_start();
4417 $returned = do_action_ref_array($action, $args);
4418 $output = ob_get_clean();
4419 $res .= " - do_action_ref_array Trigger ";
4420 } else {
4421 ob_start();
4422 do_action($action);
4423 $output = ob_get_contents();
4424 ob_end_clean();
4425 $res .= " - do_action Trigger ";
4426 }
4427 }
4428 $response['response'] = $res;
4429 $response['log'] = $output;
4430
4431 //Check if response is empty
4432 if (!empty($returned)) $response['status'] = $returned;
4433
4434 return $response;
4435 }
4436
4437 public function enqueue_jstree() {
4438
4439 static $already_enqueued = false;
4440 if ($already_enqueued) return;
4441
4442 $already_enqueued = true;
4443 $jstree_enqueue_version = (defined('SCRIPT_DEBUG') && SCRIPT_DEBUG) ? '3.3'.'.'.time() : '3.3';
4444 $min_or_not = (defined('SCRIPT_DEBUG') && SCRIPT_DEBUG) ? '' : '.min';
4445 // Include jstree components
4446 wp_enqueue_script('jstree', UPDRAFTPLUS_URL.'/includes/jstree/jstree'.$min_or_not.'.js', array('jquery'), $jstree_enqueue_version);
4447 wp_enqueue_style('jstree', UPDRAFTPLUS_URL.'/includes/jstree/themes/default/style'.$min_or_not.'.css', array(), $jstree_enqueue_version);
4448 }
4449 }
4450