'.esc_html__('Backup directory could not be created', 'updraftplus').'...
';
echo '
style="">
$is_opera);
$this->include_template('wp-admin/settings/tab-backups.php', false, array('backup_history' => $backup_history, 'options' => $tmp_opts));
$this->include_template('wp-admin/settings/upload-backups-modal.php');
?>
style="">
include_template('wp-admin/settings/migrator-no-migrator.php');
}
?>
style="">
include_template('wp-admin/settings/tab-addons.php', true, array('tabflag' => $tabflag));
echo apply_filters('updraftplus_addonstab_content', $tab_addons);// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- this should be ignored because the variable contains html script tag
?>
settings_footer();
}
/**
* Get main tabs array
*
* @return Array Array which have key as a tab key and value as tab label
*/
private function get_main_tabs_array() {
return apply_filters(
'updraftplus_main_tabs',
array(
'backups' => __('Backup / Restore', 'updraftplus'),
'migrate' => __('Migrate / Clone', 'updraftplus'),
'settings' => __('Settings', 'updraftplus'),
'expert' => __('Advanced Tools', 'updraftplus'),
'addons' => __('Premium / Extensions', 'updraftplus'),
)
);
}
/**
* Potentially register an action for showing restore progress
*/
private function print_restore_in_progress_box_if_needed() {
global $updraftplus;
$check_restore_progress = $updraftplus->check_restore_progress();
// Check to see if the restore is still in progress
if (is_array($check_restore_progress) && true == $check_restore_progress['status']) {
$restore_jobdata = $check_restore_progress['restore_jobdata'];
$restore_jobdata['jobid'] = $check_restore_progress['restore_in_progress'];
$this->restore_in_progress_jobdata = $restore_jobdata;
add_action('all_admin_notices', array($this, 'show_admin_restore_in_progress_notice'));
add_action('admin_print_footer_scripts', array($this, 'print_unfinished_restoration_dialog_scripts'));
add_action('admin_print_styles', array($this, 'print_unfinished_restoration_dialog_styles'));
}
}
/**
* This function is called via the command class, it will get the resume restore notice to be shown when a restore is taking place over AJAX
*
* @param string $job_id - the id of the job
*
* @return WP_Error|string - can return a string containing html or a WP_Error
*/
public function get_restore_resume_notice($job_id) {
global $updraftplus;
if (empty($job_id)) return new WP_Error('missing_parameter', 'Missing parameters.');
$restore_jobdata = $updraftplus->jobdata_getarray($job_id);
if (!is_array($restore_jobdata) && empty($restore_jobdata)) return new WP_Error('missing_jobdata', 'Job data not found.');
$restore_jobdata['jobid'] = $job_id;
$this->restore_in_progress_jobdata = $restore_jobdata;
$html = $this->show_admin_restore_in_progress_notice(true);
if (empty($html)) return new WP_Error('job_aborted', 'Job aborted.');
return $html;
}
/**
* If added, then runs upon the WP action all_admin_notices, or can be called via get_restore_resume_notice() for when a restore is running over AJAX
*
* @param Boolean $return_instead_of_echo - indicates if we want to add the tfa UI
* @param Boolean $exclude_js - indicates if we want to exclude the js in the returned html
*
* @return void|string - can return a string containing html or echo the html to page
*/
public function show_admin_restore_in_progress_notice($return_instead_of_echo = false) {
if (isset($_REQUEST['action']) && 'updraft_restore_abort' === $_REQUEST['action'] && !empty($_REQUEST['job_id'])) {
delete_site_option('updraft_restore_in_progress');
return;
}
$restore_jobdata = $this->restore_in_progress_jobdata;
$seconds_ago = time() - (int) $restore_jobdata['job_time_ms'];
$minutes_ago = floor($seconds_ago/60);
$seconds_ago = $seconds_ago - $minutes_ago*60;
$time_ago = sprintf(__("%s minutes, %s seconds", 'updraftplus'), $minutes_ago, $seconds_ago);
$html = '';
}
echo '
'.esc_html(__('Your WordPress install has old folders from its state before you restored/migrated (technical information: these are suffixed with -old).', 'updraftplus').' '.__('You should press this button to delete them as soon as you have verified that the restoration worked.', 'updraftplus')).'
';
}
?>
';
}
/**
* Return cron status information about a specified in-progress job
*
* @param Boolean|String $job_id - the job to get information about; or, if not specified, all jobs
*
* @return Array|Boolean - the requested information, or false if it was not found. Format differs depending on whether info on all jobs, or a single job, was requested.
*/
public function get_cron($job_id = false) {
$cron = get_option('cron');
if (!is_array($cron)) $cron = array();
if (false === $job_id) return $cron;
foreach ($cron as $time => $job) {
if (!isset($job['updraft_backup_resume'])) continue;
foreach ($job['updraft_backup_resume'] as $info) {
if (isset($info['args'][1]) && $job_id == $info['args'][1]) {
global $updraftplus;
$jobdata = $updraftplus->jobdata_getarray($job_id);
return is_array($jobdata) ? array($time, $jobdata) : false;
}
}
}
}
/**
* Gets HTML describing the active jobs
*
* @param Boolean $this_job_only A value for $this_job_only also causes something non-empty to always be returned (to allow detection of the job having started on the front-end)
*
* @return String - the HTML
*/
private function print_active_jobs($this_job_only = false) {
$cron = $this->get_cron();
$ret = '';
foreach ($cron as $time => $job) {
if (!isset($job['updraft_backup_resume'])) continue;
foreach ($job['updraft_backup_resume'] as $info) {
if (isset($info['args'][1])) {
$job_id = $info['args'][1];
if (false === $this_job_only || $job_id == $this_job_only) {
$ret .= $this->print_active_job($job_id, false, $time, $info['args'][0]);
}
}
}
}
// A value for $this_job_only implies that output is required
if (false !== $this_job_only && !$ret) {
$ret = $this->print_active_job($this_job_only);
if ('' == $ret) {
global $updraftplus;
$log_file = $updraftplus->get_logfile_name($this_job_only);
// if the file exists, the backup was booted. Check if the information about completion is found in the log, or if it was modified at least 2 minutes ago.
if (file_exists($log_file) && ($updraftplus->found_backup_complete_in_logfile($this_job_only) || (time() - filemtime($log_file)) > 120)) {
// The presence of the exact ID matters to the front-end - indicates that the backup job has at least begun
$ret = '
';
}
}
}
return $ret;
}
/**
* Print the HTML for a particular job
*
* @param String $job_id - the job identifier/nonce
* @param Boolean $is_oneshot - whether this backup should be 'one shot', i.e. no resumptions
* @param Boolean|Integer $time
* @param Integer $next_resumption
*
* @return String
*/
private function print_active_job($job_id, $is_oneshot = false, $time = false, $next_resumption = false) {
$ret = '';
global $updraftplus;
$jobdata = $updraftplus->jobdata_getarray($job_id);
if (false == apply_filters('updraftplus_print_active_job_continue', true, $is_oneshot, $next_resumption, $jobdata)) return '';
if (!isset($jobdata['backup_time'])) return '';
$backupable_entities = $updraftplus->get_backupable_file_entities(true, true);
$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') : '?';
$backup_label = !empty($jobdata['label']) ? $jobdata['label'] : '';
$remote_sent = (!empty($jobdata['service']) && ((is_array($jobdata['service']) && in_array('remotesend', $jobdata['service'])) || 'remotesend' === $jobdata['service'])) ? true : false;
$jobstatus = empty($jobdata['jobstatus']) ? 'unknown' : $jobdata['jobstatus'];
$stage = 0;
switch ($jobstatus) {
// Stage 0
case 'begun':
$curstage = __('Backup begun', 'updraftplus');
break;
// Stage 1
case 'filescreating':
$stage = 1;
$curstage = __('Creating file backup zips', 'updraftplus');
if (!empty($jobdata['filecreating_substatus']) && isset($backupable_entities[$jobdata['filecreating_substatus']['e']]['description'])) {
$sdescrip = preg_replace('/ \(.*\)$/', '', $backupable_entities[$jobdata['filecreating_substatus']['e']]['description']);
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'];
$curstage .= ' ('.$sdescrip.')';
if (isset($jobdata['filecreating_substatus']['i']) && isset($jobdata['filecreating_substatus']['t'])) {
$stage = min(2, 1 + ($jobdata['filecreating_substatus']['i']/max($jobdata['filecreating_substatus']['t'], 1)));
}
}
break;
case 'filescreated':
$stage = 2;
$curstage = __('Created file backup zips', 'updraftplus');
break;
// Stage 4
case 'clonepolling':
$stage = 4;
$curstage = __('Clone server being provisioned and booted (can take several minutes)', 'updraftplus');
break;
case 'partialclouduploading':
case 'clouduploading':
$stage = 'clouduploading' == $jobstatus ? 4 : 2;
$curstage = __('Uploading files to remote storage', 'updraftplus');
if ($remote_sent) $curstage = __('Sending files to remote site', 'updraftplus');
if (isset($jobdata['uploading_substatus']['t']) && isset($jobdata['uploading_substatus']['i'])) {
$t = max((int) $jobdata['uploading_substatus']['t'], 1);
$i = min($jobdata['uploading_substatus']['i']/$t, 1);
$p = min($jobdata['uploading_substatus']['p'], 1);
$pd = $i + $p/$t;
$stage = 'clouduploading' == $jobstatus ? $stage + $pd : $stage;
$curstage .= ' ('.floor(100*$pd).'%, '.sprintf(__('file %d of %d', 'updraftplus'), (int) $jobdata['uploading_substatus']['i']+1, $t).')';
}
break;
case 'pruning':
$stage = 5;
$curstage = __('Pruning old backup sets', 'updraftplus');
break;
case 'resumingforerrors':
$stage = -1;
$curstage = __('Waiting until scheduled time to retry because of errors', 'updraftplus');
break;
// Stage 6
case 'finished':
$stage = 6;
$curstage = __('Backup finished', 'updraftplus');
break;
default:
// Database creation and encryption occupies the space from 2 to 4. Databases are created then encrypted, then the next database is created/encrypted, etc.
if ('dbcreated' == substr($jobstatus, 0, 9)) {
$jobstatus = 'dbcreated';
$whichdb = substr($jobstatus, 9);
if (!is_numeric($whichdb)) $whichdb = 0;
$howmanydbs = max((empty($jobdata['backup_database']) || !is_array($jobdata['backup_database'])) ? 1 : count($jobdata['backup_database']), 1);
$perdbspace = 2/$howmanydbs;
$stage = min(4, 2 + ($whichdb+2)*$perdbspace);
$curstage = __('Created database backup', 'updraftplus');
} elseif ('dbcreating' == substr($jobstatus, 0, 10)) {
$whichdb = substr($jobstatus, 10);
if (!is_numeric($whichdb)) $whichdb = 0;
$howmanydbs = (empty($jobdata['backup_database']) || !is_array($jobdata['backup_database'])) ? 1 : count($jobdata['backup_database']);
$perdbspace = 2/$howmanydbs;
$jobstatus = 'dbcreating';
$stage = min(4, 2 + $whichdb*$perdbspace);
$curstage = __('Creating database backup', 'updraftplus');
if (!empty($jobdata['dbcreating_substatus']['t'])) {
$curstage .= ' ('.sprintf(__('table: %s', 'updraftplus'), $jobdata['dbcreating_substatus']['t']).')';
if (!empty($jobdata['dbcreating_substatus']['i']) && !empty($jobdata['dbcreating_substatus']['a'])) {
$substage = max(0.001, ($jobdata['dbcreating_substatus']['i'] / max($jobdata['dbcreating_substatus']['a'], 1)));
$stage += $substage * $perdbspace * 0.5;
}
}
} elseif ('dbencrypting' == substr($jobstatus, 0, 12)) {
$whichdb = substr($jobstatus, 12);
if (!is_numeric($whichdb)) $whichdb = 0;
$howmanydbs = (empty($jobdata['backup_database']) || !is_array($jobdata['backup_database'])) ? 1 : count($jobdata['backup_database']);
$perdbspace = 2/$howmanydbs;
$stage = min(4, 2 + $whichdb*$perdbspace + $perdbspace*0.5);
$jobstatus = 'dbencrypting';
$curstage = __('Encrypting database', 'updraftplus');
} elseif ('dbencrypted' == substr($jobstatus, 0, 11)) {
$whichdb = substr($jobstatus, 11);
if (!is_numeric($whichdb)) $whichdb = 0;
$howmanydbs = (empty($jobdata['backup_database']) || !is_array($jobdata['backup_database'])) ? 1 : count($jobdata['backup_database']);
$jobstatus = 'dbencrypted';
$perdbspace = 2/$howmanydbs;
$stage = min(4, 2 + $whichdb*$perdbspace + $perdbspace);
$curstage = __('Encrypted database', 'updraftplus');
} else {
$curstage = __('Unknown', 'updraftplus');
}
}
$runs_started = empty($jobdata['runs_started']) ? array() : $jobdata['runs_started'];
$time_passed = empty($jobdata['run_times']) ? array() : $jobdata['run_times'];
$last_checkin_ago = -1;
if (is_array($time_passed)) {
foreach ($time_passed as $run => $passed) {
if (isset($runs_started[$run])) {
$time_ago = microtime(true) - ($runs_started[$run] + $time_passed[$run]);
if ($time_ago < $last_checkin_ago || -1 == $last_checkin_ago) $last_checkin_ago = $time_ago;
}
}
}
$next_res_after = (int) $time-time();
$next_res_txt = $is_oneshot ? '' : sprintf(__('next resumption: %d', 'updraftplus'), $next_resumption).($next_resumption ? ' '.sprintf(__('(after %ss)', 'updraftplus'), $next_res_after) : '').' ';
$last_activity_txt = ($last_checkin_ago >= 0) ? sprintf(__('last activity: %ss ago', 'updraftplus'), floor($last_checkin_ago)).' ' : '';
if (($last_checkin_ago < 50 && $next_res_after>30) || $is_oneshot) {
$show_inline_info = $last_activity_txt;
$title_info = $next_res_txt;
} else {
$show_inline_info = $next_res_txt;
$title_info = $last_activity_txt;
}
$ret .= '
';
$ret .= '
'.(!empty($backup_label) ? esc_html($backup_label) : $began_at).
'
';
$ret .= '
';
// Existence of the 'updraft-jobid-(id)' id is checked for in other places, so do not modify this
$ret .= '
';
if ($clone_url) $ret .= '
';
$ret .= apply_filters('updraft_printjob_beforewarnings', '', $jobdata, $job_id);
if (!empty($jobdata['warnings']) && is_array($jobdata['warnings'])) {
$ret .= '
';
foreach ($jobdata['warnings'] as $warning) {
$ret .= '- '.sprintf(__('Warning: %s', 'updraftplus'), make_clickable(htmlspecialchars($warning))).'
';
}
$ret .= '
';
}
$ret .= '
';
// $ret .= '
'.htmlspecialchars($curstage).'';
$ret .= htmlspecialchars($curstage);
// we need to add this data-progress attribute in order to be able to update the progress bar in UDC
$ret .= '
';
$ret .= '
';
$ret .= '
';
$ret .= '
';
return $ret;
}
private function delete_old_dirs_go($show_return = true) {
echo $show_return ? '
UpdraftPlus - '.esc_html__('Remove old folders', 'updraftplus').'
' : '
'.esc_html__('Remove old directories', 'updraftplus').'
';
if ($this->delete_old_dirs()) {
echo '
'.esc_html__('Old folders successfully removed.', 'updraftplus').'
';
} else {
echo '
'.esc_html__('Old folder removal failed for some reason.', 'updraftplus').' '.esc_html__('You may want to do this manually.', 'updraftplus').'
';
}
if ($show_return) echo '
'.esc_html__('Actions', 'updraftplus').': '.esc_html__('Return to UpdraftPlus configuration', 'updraftplus').'';
}
/**
* Deletes the -old directories and wp-config-pre-ud-restore-backup.php that are created when a backup is restored.
*
* @return Boolean. Can also exit (something we ought to probably review)
*/
private function delete_old_dirs() {
global $wp_filesystem, $updraftplus;
$credentials = request_filesystem_credentials(wp_nonce_url(UpdraftPlus_Options::admin_page_url()."?page=updraftplus&action=updraft_delete_old_dirs", 'updraftplus-credentialtest-nonce', 'updraft_delete_old_dirs_nonce'));
$wpfs = WP_Filesystem($credentials);
if (!empty($wp_filesystem->errors) && $wp_filesystem->errors->get_error_code()) {
foreach ($wp_filesystem->errors->get_error_messages() as $message) show_message($message);
exit;
}
if (!$wpfs) exit;
// From WP_CONTENT_DIR - which contains 'themes'
$ret = $this->delete_old_dirs_dir($wp_filesystem->wp_content_dir());
$updraft_dir = $updraftplus->backups_dir_location();
if ($updraft_dir) {
$ret4 = $updraft_dir ? $this->delete_old_dirs_dir($updraft_dir, false) : true;
} else {
$ret4 = true;
}
$plugs = untrailingslashit($wp_filesystem->wp_plugins_dir());
if ($wp_filesystem->is_dir($plugs.'-old')) {
echo "
".esc_html__('Delete', 'updraftplus').": plugins-old: ";
if (!$wp_filesystem->delete($plugs.'-old', true)) {
$ret3 = false;
echo "
".esc_html__('Failed', 'updraftplus')."";
echo esc_html($updraftplus->log_permission_failure_message($wp_filesystem->wp_content_dir(), 'Delete '.$plugs.'-old'));
} else {
$ret3 = true;
echo "
".esc_html__('OK', 'updraftplus')."";
}
} else {
$ret3 = true;
}
$ret2 = true;
if ($wp_filesystem->is_file(ABSPATH.'wp-config-pre-ud-restore-backup.php')) {
echo "
".esc_html__('Delete', 'updraftplus').": wp-config-pre-ud-restore-backup.php: ";
if ($wp_filesystem->delete(ABSPATH.'wp-config-pre-ud-restore-backup.php')) {
echo "
".esc_html__('OK', 'updraftplus')."";
} else {
$ret2 = false;
echo "
".esc_html__('Failed', 'updraftplus')."";
}
}
return $ret && $ret2 && $ret3 && $ret4;
}
private function delete_old_dirs_dir($dir, $wpfs = true) {
$dir = trailingslashit($dir);
global $wp_filesystem, $updraftplus;
if ($wpfs) {
$list = $wp_filesystem->dirlist($dir);
} else {
$list = scandir($dir);
}
if (!is_array($list)) return false;
$ret = true;
foreach ($list as $item) {
$name = (is_array($item)) ? $item['name'] : $item;
if ("-old" == substr($name, -4, 4)) {
// recursively delete
print "
".esc_html__('Delete', 'updraftplus').": ".esc_html(basename($dir).'/'.$name).": ";
if ($wpfs) {
if (!$wp_filesystem->delete($dir.$name, true)) {
$ret = false;
echo "
".esc_html__('Failed', 'updraftplus')."";
echo wp_kses_post($updraftplus->log_permission_failure_message($dir, 'Delete '.$dir.$name));
} else {
echo "
".esc_html__('OK', 'updraftplus')."";
}
} else {
if (UpdraftPlus_Filesystem_Functions::remove_local_directory($dir.$name)) {
echo "
".esc_html__('OK', 'updraftplus')."";
} else {
$ret = false;
echo "
".esc_html__('Failed', 'updraftplus')."";
echo wp_kses_post($updraftplus->log_permission_failure_message($dir, 'Delete '.$dir.$name));
}
}
}
}
return $ret;
}
/**
* The aim is to get a directory that is writable by the webserver, because that's the only way we can create zip files
*
* @return Boolean|WP_Error true if successful, otherwise false or a WP_Error
*/
private function create_backup_dir() {
global $wp_filesystem, $updraftplus;
if (false === ($credentials = request_filesystem_credentials(UpdraftPlus_Options::admin_page().'?page=updraftplus&action=updraft_create_backup_dir&nonce='.wp_create_nonce('create_backup_dir')))) {
return false;
}
if (!WP_Filesystem($credentials)) {
// our credentials were no good, ask the user for them again
request_filesystem_credentials(UpdraftPlus_Options::admin_page().'?page=updraftplus&action=updraft_create_backup_dir&nonce='.wp_create_nonce('create_backup_dir'), '', true);
return false;
}
$updraft_dir = $updraftplus->backups_dir_location();
$default_backup_dir = $wp_filesystem->find_folder(dirname($updraft_dir)).basename($updraft_dir);
$updraft_dir = ($updraft_dir) ? $wp_filesystem->find_folder(dirname($updraft_dir)).basename($updraft_dir) : $default_backup_dir;
if (!$wp_filesystem->is_dir($default_backup_dir) && !$wp_filesystem->mkdir($default_backup_dir, 0775)) {
$wperr = new WP_Error;
if ($wp_filesystem->errors->get_error_code()) {
foreach ($wp_filesystem->errors->get_error_messages() as $message) {
$wperr->add('mkdir_error', $message);
}
return $wperr;
} else {
return new WP_Error('mkdir_error', __('The request to the filesystem to create the directory failed.', 'updraftplus'));
}
}
if ($wp_filesystem->is_dir($default_backup_dir)) {
if (UpdraftPlus_Filesystem_Functions::really_is_writable($updraft_dir)) return true;
@$wp_filesystem->chmod($default_backup_dir, 0775);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise because of the method.
if (UpdraftPlus_Filesystem_Functions::really_is_writable($updraft_dir)) return true;
@$wp_filesystem->chmod($default_backup_dir, 0777);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise because of the method.
if (UpdraftPlus_Filesystem_Functions::really_is_writable($updraft_dir)) {
echo '
'.esc_html__('The folder was created, but we had to change its file permissions to 777 (world-writable) to be able to write to it.', 'updraftplus').' '.esc_html__('You should check with your hosting provider that this will not cause any problems', 'updraftplus').'
';
return true;
} else {
@$wp_filesystem->chmod($default_backup_dir, 0775);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise because of the method.
$show_dir = (0 === strpos($default_backup_dir, ABSPATH)) ? substr($default_backup_dir, strlen(ABSPATH)) : $default_backup_dir;
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.')');
}
}
return true;
}
/**
* scans the content dir to see if any -old dirs are present
*
* @param Boolean $print_as_comment Echo information in an HTML comment
* @return Boolean
*/
private function scan_old_dirs($print_as_comment = false) {
global $updraftplus;
$dirs = scandir(untrailingslashit(WP_CONTENT_DIR));
if (!is_array($dirs)) $dirs = array();
foreach ($dirs as $dir) {
if (preg_match('/-old$/', $dir)) {
if ($print_as_comment) echo '';
return true;
}
}
$backups_dir_location = $updraftplus->backups_dir_location();
$dirs = @scandir($backups_dir_location);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise because of the function.
if (!is_array($dirs)) $dirs = array();
foreach ($dirs as $dir) {
if (preg_match('/-old$/', $dir)) {
if ($print_as_comment) echo '';
return true;
}
}
// No need to scan ABSPATH - we don't backup there
if (is_dir(untrailingslashit(WP_PLUGIN_DIR).'-old')) {
if ($print_as_comment) echo '';
return true;
}
if (is_file(ABSPATH.'wp-config-pre-ud-restore-backup.php')) {
if ($print_as_comment) echo '';
return true;
}
return false;
}
/**
* Outputs html for a storage method using the parameters passed in, this version should be removed when all remote storages use the multi version
*
* @param String $method a list of methods to be used when
* @param String $header the table header content
* @param String $contents the table contents
*/
public function storagemethod_row($method, $header, $contents) {
?>
|
|
|
|
'.$header.' |
'.$contents.' |
';
}
/**
* Get HTML suitable for the admin area for the status of the last backup
*
* @return String
*/
public function last_backup_html() {
global $updraftplus;
$updraft_last_backup = UpdraftPlus_Options::get_updraft_option('updraft_last_backup');
if ($updraft_last_backup) {
// Convert to GMT, then to blog time
$backup_time = (int) $updraft_last_backup['backup_time'];
$print_time = get_date_from_gmt(gmdate('Y-m-d H:i:s', $backup_time), 'D, F j, Y H:i');
if (empty($updraft_last_backup['backup_time_incremental'])) {
$last_backup_text = "
".$print_time.'';
} else {
$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');
$last_backup_text = "
$inc_time (".sprintf(__('incremental backup; base backup: %s', 'updraftplus'), $print_time).')';
}
$last_backup_text .= '
';
// Show errors + warnings
if (is_array($updraft_last_backup['errors'])) {
foreach ($updraft_last_backup['errors'] as $err) {
$level = (is_array($err)) ? $err['level'] : 'error';
$message = (is_array($err)) ? $err['message'] : $err;
$last_backup_text .= ('warning' == $level) ? "
" : "";
if ('warning' == $level) {
$message = sprintf(__("Warning: %s", 'updraftplus'), make_clickable(htmlspecialchars($message)));
} else {
$message = htmlspecialchars($message);
}
$last_backup_text .= $message;
$last_backup_text .= '
';
}
}
// Link log
if (!empty($updraft_last_backup['backup_nonce'])) {
$updraft_dir = $updraftplus->backups_dir_location();
$potential_log_file = $updraft_dir."/log.".$updraft_last_backup['backup_nonce'].".txt";
if (is_readable($potential_log_file)) $last_backup_text .= "".__('Download log file', 'updraftplus')."";
}
} else {
$last_backup_text = "".__('No backup has been completed', 'updraftplus')."";
}
return $last_backup_text;
}
/**
* Get a list of backup intervals
*
* @param String $what_for - 'files' or 'db'
*
* @return Array - keys are used as identifiers in the UI drop-down; values are user-displayed text describing the interval
*/
public function get_intervals($what_for = 'db') {
global $updraftplus;
$intervals = wp_get_schedules();
if ($updraftplus->is_restricted_hosting('only_one_backup_per_month')) {
$intervals = array_intersect_key($intervals, array('monthly' => array()));
} else {
if ('db' != $what_for) unset($intervals['everyhour']);
}
$intervals = array_intersect_key(updraftplus_list_cron_schedules(), $intervals); // update schedule descriptions for the UI of the backup schedule drop-down and rearrange schedule order
foreach ($intervals as $interval => $data) {
$intervals[$interval] = $data['display'];
}
$intervals = array('manual' => _x('Manual', 'i.e. Non-automatic', 'updraftplus')) + $intervals;
return apply_filters('updraftplus_backup_intervals', $intervals, $what_for);
}
public function really_writable_message($really_is_writable, $updraft_dir) {
if ($really_is_writable) {
$dir_info = ''.__('Backup directory specified is writable, which is good.', 'updraftplus').'';
} else {
$dir_info = '';
if (!is_dir($updraft_dir)) {
$dir_info .= __('Backup directory specified does not exist.', 'updraftplus');
} else {
$dir_info .= __('Backup directory specified exists, but is not writable.', 'updraftplus');
}
$dir_info .= ''.__('Follow this link to attempt to create the directory and set the permissions', 'updraftplus').', '.__('or, to reset this option', 'updraftplus').' '.__('press here', 'updraftplus').'. '.__('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').'';
}
return $dir_info;
}
/**
* Directly output the settings form (suitable for the admin area)
*
* @param Array $options current options (passed on to the template)
*/
public function settings_formcontents($options = array()) {
$this->include_template('wp-admin/settings/form-contents.php', false, array(
'options' => $options
));
if (!(defined('UPDRAFTCENTRAL_COMMAND') && UPDRAFTCENTRAL_COMMAND)) {
$this->include_template('wp-admin/settings/exclude-modal.php', false);
}
}
public function get_settings_js($method_objects, $really_is_writable, $updraft_dir, $active_service) {// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found -- Filter use
global $updraftplus;
ob_start();
?>
jQuery(function() {
backup_methods as $method => $description) {
// already done: updraft_try_include_file('methods/'.$method.'.php', 'require_once');
$call_method = "UpdraftPlus_BackupModule_$method";
if (method_exists($call_method, 'config_print_javascript_onready')) {
$method_objects[$method]->config_print_javascript_onready();
}
}
?>
});
get_backupable_file_entities(true, true);
if (!function_exists('get_mu_plugins')) include_once(ABSPATH.'wp-admin/includes/plugin.php');
$mu_plugins = get_mu_plugins();
// The true (default value if non-existent) here has the effect of forcing a default of on.
$include_more_paths = UpdraftPlus_Options::get_updraft_option('updraft_include_more_path');
foreach ($backupable_entities as $key => $info) {
$included = (UpdraftPlus_Options::get_updraft_option("updraft_include_$key", apply_filters("updraftplus_defaultoption_include_".$key, true))) ? 'checked="checked"' : "";
if ('others' == $key || 'uploads' == $key) {
$data_toggle_exclude_field = $show_exclusion_options ? 'data-toggle_exclude_field="'.$key.'"' : '';
$ret .= '';
if ($show_exclusion_options) {
$include_exclude = UpdraftPlus_Options::get_updraft_option('updraft_include_'.$key.'_exclude', ('others' == $key) ? UPDRAFT_DEFAULT_OTHERS_EXCLUDE : UPDRAFT_DEFAULT_UPLOADS_EXCLUDE);
$display = ($included) ? '' : 'class="updraft-hidden" style="display:none;"';
$exclude_container_class = $prefix.'updraft_include_'.$key.'_exclude';
if (!$for_updraftcentral) $exclude_container_class .= '_container';
$ret .= "";
$ret .= ' '.__('(the asterisk character matches zero or more characters)', 'updraftplus').'';
$exclude_input_type = $for_updraftcentral ? "text" : "hidden";
$exclude_input_extra_attr = $for_updraftcentral ? 'title="'.__('If entering multiple files/directories, then separate them with commas.', 'updraftplus').' '.__('For entities at the top level, you can use a * at the start or end of the entry as a wildcard.', 'updraftplus').'" size="54"' : '';
$ret .= '';
if (!$for_updraftcentral) {
global $updraftplus;
$backupable_file_entities = $updraftplus->get_backupable_file_entities();
if ('uploads' == $key) {
$path = UpdraftPlus_Manipulation_Functions::wp_normalize_path($backupable_file_entities['uploads']);
} elseif ('others' == $key) {
$path = UpdraftPlus_Manipulation_Functions::wp_normalize_path($backupable_file_entities['others']);
}
$ret .= $this->include_template('wp-admin/settings/file-backup-exclude.php', true, array(
'key' => $key,
'include_exclude' => $include_exclude,
'path' => $path,
'show_exclusion_options' => $show_exclusion_options,
));
}
$ret .= '
';
}
} else {
if ('more' != $key || true === $include_more || ('sometimes' === $include_more && !empty($include_more_paths))) {
$data_toggle_exclude_field = $show_exclusion_options ? 'data-toggle_exclude_field="'.$key.'"' : '';
$force_disabled = '';
if ('mu-plugins' == $key && !$mu_plugins) {
$force_disabled = 'data-force_disabled="1"';
$info['description'] .= ' ('.__('none present', 'updraftplus').')';
}
$ret .= "";
$ret .= apply_filters("updraftplus_config_option_include_$key", '', $prefix, $for_updraftcentral);
}
}
}
return $ret;
}
/**
* Output or echo HTML for an error condition relating to a remote storage method
*
* @param String $text - the text of the message; this should already be escaped (no more is done)
* @param String $extraclass - a CSS class for the resulting DOM node
* @param Integer $echo - if set, then the results will be echoed as well as returned
*
* @return String - the results
*/
public function show_double_warning($text, $extraclass = '', $echo = true) {
$ret = "";
$ret .= "";
if ($echo) echo wp_kses_post($ret);
return $ret;
}
public function optionfilter_split_every($value) {
return max(absint($value), UPDRAFTPLUS_SPLIT_MIN);
}
/**
* Check if curl exists; if not, print or return appropriate error messages
*
* @param String $service the service description (used only for user-visible messages - so, use the description)
* @param Boolean $has_fallback set as true if the lack of Curl only affects the ability to connect over SSL
* @param String $extraclass an extra CSS class for any resulting message, passed on to show_double_warning()
* @param Boolean $echo_instead_of_return whether the result should be echoed or returned
* @return String any resulting message, if $echo_instead_of_return was set
*/
public function curl_check($service, $has_fallback = false, $extraclass = '', $echo_instead_of_return = true) {
$ret = '';
// Check requirements
if (!function_exists("curl_init") || !function_exists('curl_exec')) {
$ret .= $this->show_double_warning(''.__('Warning', 'updraftplus').': '.sprintf(__("Your web server's PHP installation does not included a required (for %s) module (%s).", 'updraftplus'), $service, 'Curl').' '.__("Please contact your web hosting provider's support and ask for them to enable it.", 'updraftplus').' ', $extraclass, false);
} else {
$curl_version = curl_version();
$curl_ssl_supported= ($curl_version['features'] & CURL_VERSION_SSL);
if (!$curl_ssl_supported) {
if ($has_fallback) {
$ret .= ''.__('Warning', 'updraftplus').': '.__("Your web server's PHP/Curl installation does not support https access.", 'updraftplus').' '.sprintf(__("Communications with %s will be unencrypted.", 'updraftplus'), $service).' '.__("Ask your web host to install Curl/SSL in order to gain the ability for encryption (via an add-on).", 'updraftplus').'
';
} else {
$ret .= $this->show_double_warning(''.__('Warning', 'updraftplus').': '.__("Your web server's PHP/Curl installation does not support https access.", 'updraftplus').' '.sprintf(__("We cannot access %s without this support.", 'updraftplus'), $service).' '.__("Please contact your web hosting provider's support.", 'updraftplus').' '.sprintf(__("%s requires Curl+https.", 'updraftplus'), $service).' '.__("Please do not file any support requests; there is no alternative.", 'updraftplus').'
', $extraclass, false);
}
} else {
$ret .= ''.sprintf(__("Good news: Your site's communications with %s can be encrypted.", 'updraftplus'), $service).' '.__("If you see any errors to do with encryption, then look in the 'Expert Settings' for more help.", 'updraftplus').'
';
}
}
if ($echo_instead_of_return) {
echo wp_kses_post($ret);
} else {
return $ret;
}
}
/**
* Get backup information in HTML format for a specific backup
*
* @param Array $backup_history all backups history
* @param String $key backup timestamp
* @param String $nonce backup nonce (job ID)
* @param Array|Null $job_data if an array, then use this as the job data (if null, then it will be fetched directly)
*
* @return string HTML-formatted backup information
*/
public function raw_backup_info($backup_history, $key, $nonce, $job_data = null) {
global $updraftplus;
$backup = $backup_history[$key];
$only_remote_sent = !empty($backup['service']) && (array('remotesend') === $backup['service'] || 'remotesend' === $backup['service']);
$pretty_date = get_date_from_gmt(gmdate('Y-m-d H:i:s', (int) $key), 'M d, Y G:i');
$rawbackup = "$pretty_date
";
if (!empty($backup['label'])) $rawbackup .= ''.$backup['label'].'';
if (null === $job_data) $job_data = empty($nonce) ? array() : $updraftplus->jobdata_getarray($nonce);
if (!$only_remote_sent) {
$rawbackup .= '
';
$rawbackup .= '';
}
$rawbackup .= '
';
$backupable_entities = $updraftplus->get_backupable_file_entities(true, true);
$checksums = $updraftplus->which_checksums();
foreach ($backupable_entities as $type => $info) {
if (!isset($backup[$type])) continue;
$rawbackup .= $updraftplus->printfile($info['description'], $backup, $type, $checksums, $job_data, true);
}
$total_size = 0;
foreach ($backup as $ekey => $files) {
if ('db' == strtolower(substr($ekey, 0, 2)) && '-size' != substr($ekey, -5, 5)) {
$rawbackup .= $updraftplus->printfile(__('Database', 'updraftplus'), $backup, $ekey, $checksums, $job_data, true);
}
if (!isset($backupable_entities[$ekey]) && ('db' != substr($ekey, 0, 2) || '-size' == substr($ekey, -5, 5))) continue;
if (is_string($files)) $files = array($files);
foreach ($files as $findex => $file) {
$size_key = (0 == $findex) ? $ekey.'-size' : $ekey.$findex.'-size';
$total_size = (false === $total_size || !isset($backup[$size_key]) || !is_numeric($backup[$size_key])) ? false : $total_size + $backup[$size_key];
}
}
$services = empty($backup['service']) ? array('none') : $backup['service'];
if (!is_array($services)) $services = array('none');
$rawbackup .= ''.__('Uploaded to:', 'updraftplus').' ';
$show_services = '';
foreach ($services as $serv) {
if ('none' == $serv || '' == $serv) {
$add_none = true;
} elseif (isset($updraftplus->backup_methods[$serv])) {
$show_services .= $show_services ? ', '.$updraftplus->backup_methods[$serv] : $updraftplus->backup_methods[$serv];
} else {
$show_services .= $show_services ? ', '.$serv : $serv;
}
}
if ('' == $show_services && $add_none) $show_services .= __('None', 'updraftplus');
$rawbackup .= $show_services;
if (false !== $total_size) {
$rawbackup .= '
'.__('Total backup size:', 'updraftplus').' '.UpdraftPlus_Manipulation_Functions::convert_numeric_size_to_text($total_size).'';
}
$rawbackup .= '
'.print_r($backup, true).'
';
if (!empty($job_data) && is_array($job_data)) {
$rawbackup .= ''.htmlspecialchars(print_r($job_data, true)).'
';
}
return esc_attr($rawbackup);
}
/**
* Display button to download the DB backup in the existing backups table.
*
* @param string $bkey Backup type.
* @param string $key Backup timestamp (epoch time).
* @param string $esc_pretty_date Escaped pretty date.
* @param array $backup Backup Instance.
* @param array $accept Accepted archive names.
* @return void - Display download button for DB backup.
*/
private function download_db_button($bkey, $key, $esc_pretty_date, $backup, $accept = array()) {
if (!empty($backup['meta_foreign']) && isset($accept[$backup['meta_foreign']])) {
$desc_source = $accept[$backup['meta_foreign']]['desc'];
} else {
$desc_source = __('unknown source', 'updraftplus');
}
if ('db' == $bkey) {
$dbt = empty($backup['meta_foreign']) ? esc_attr(__('Database', 'updraftplus')) : esc_attr(sprintf(__('Database (created by %s)', 'updraftplus'), $desc_source));
} else {
$dbt = __('External database', 'updraftplus').' ('.substr($bkey, 2).')';
}
$this->download_button($bkey, $key, 0, null, '', $dbt, $esc_pretty_date, '0');
}
/**
* Go through each of the file entities
*
* @param Array $backup An array of meta information
* @param Integer $key Backup timestamp (epoch time)
* @param Array $accept An array of values to be accepted from values within $backup
* @param String $entities Entities to be added
* @param String $esc_pretty_date Escaped pretty date
* @return Void - Display download buttons in existing backups table
*/
public function download_buttons($backup, $key, $accept, &$entities, $esc_pretty_date) {
global $updraftplus;
$backupable_entities = $updraftplus->get_backupable_file_entities(true, true);
$first_entity = true;// phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- template
foreach ($backupable_entities as $type => $info) {
if (!empty($backup['meta_foreign']) && 'wpcore' != $type) continue;
$ide = '';
if (empty($backup['meta_foreign'])) {
$sdescrip = preg_replace('/ \(.*\)$/', '', $info['description']);
if (strlen($sdescrip) > 20 && isset($info['shortdescription'])) $sdescrip = $info['shortdescription'];
} else {
$info['description'] = 'WordPress';
if (isset($accept[$backup['meta_foreign']])) {
$desc_source = $accept[$backup['meta_foreign']]['desc'];
$ide .= sprintf(__('Backup created by: %s.', 'updraftplus'), $accept[$backup['meta_foreign']]['desc']).' ';
} else {
$desc_source = __('unknown source', 'updraftplus');
$ide .= __('Backup created by unknown source (%s) - cannot be restored.', 'updraftplus').' ';
}
$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);
}
if (isset($backup[$type])) {
if (!is_array($backup[$type])) $backup[$type] = array($backup[$type]);
$howmanyinset = count($backup[$type]);
$expected_index = 0;
$index_missing = false;
$set_contents = '';
$entities .= "/$type=";
$whatfiles = $backup[$type];
ksort($whatfiles);
$total_file_size = 0;
foreach ($whatfiles as $findex => $bfile) {
$set_contents .= ('' == $set_contents) ? $findex : ",$findex";
if ($findex != $expected_index) $index_missing = true;
$expected_index++;
if ($howmanyinset > 0) {
if (!empty($backup[$type.(($findex > 0) ? $findex : '')."-size"]) && $findex < $howmanyinset) $total_file_size += $backup[$type.(($findex > 0) ? $findex : '')."-size"];
}
}
$ide = __('Press here to download or browse', 'updraftplus').' '.strtolower($info['description']);
$ide .= ' '.sprintf(__('(%d archive(s) in set, total %s).', 'updraftplus'), $howmanyinset, UpdraftPlus_Manipulation_Functions::convert_numeric_size_to_text($total_file_size));
if ($index_missing) $ide .= ' '.__('You are missing one or more archives from this multi-archive set.', 'updraftplus');
$entities .= $set_contents.'/';
if (!empty($backup['meta_foreign'])) {
$entities .= '/plugins=0//themes=0//uploads=0//others=0/';
}
$this->download_button($type, $key, 0, null, $ide, $sdescrip, $esc_pretty_date, $set_contents);
}
}
}
/**
* Return HTML for 'Backup date' column in existing backups data table.
*
* @param string $pretty_date Pretty date.
* @param string $key Backup timestamp (epoch time).
* @param array $backup Backup instance.
* @param array $jobdata Job data.
* @param string $nonce Nonce.
* @param bool $simple_format Whether to display simple format or not. Default false.
* @return string - Return HTML for date label.
*/
public function date_label($pretty_date, $key, $backup, $jobdata, $nonce, $simple_format = false) {
$pretty_date = $simple_format ? $pretty_date : ''.$pretty_date.'
';
$ret = apply_filters('updraftplus_showbackup_date', $pretty_date, $backup, $jobdata, (int) $key, $simple_format);
if (is_array($jobdata) && !empty($jobdata['resume_interval']) && (empty($jobdata['jobstatus']) || 'finished' != $jobdata['jobstatus'])) {
if ($simple_format) {
$ret .= ' '.__('(Not finished)', 'updraftplus');
} else {
$ret .= apply_filters('updraftplus_msg_unfinishedbackup', "
".__('(Not finished)', 'updraftplus').'', $jobdata, $nonce);
}
}
return $ret;
}
/**
* Display download button for backups in existing backups table.
*
* @param string $type Backup type.
* @param string $backup_timestamp Backup timestamp (epoch time).
* @param int $findex File index.
* @param string $info Additional Info.
* @param string $title Title attribute contents.
* @param string $pdescrip Button text.
* @param string $esc_pretty_date Escaped pretty date.
* @param string $set_contents Custom data attribute called set.
* @return void - Display download button for backups.
*/
public function download_button($type, $backup_timestamp, $findex, $info, $title, $pdescrip, $esc_pretty_date, $set_contents) {// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found -- Filter use
$wp_nonce = wp_create_nonce('updraftplus_download');
// updraft_downloader(base, backup_timestamp, what, whicharea, set_contents, prettydate, async)
echo '';
// onclick="'."return updraft_downloader('uddlstatus_', '$backup_timestamp', '$type', '.ud_downloadstatus', '$set_contents', '$esc_pretty_date', true)".'"
}
/**
* Display 'Restore' button for backup in existing backups table.
*
* @param array $backup Backup instance.
* @param string $key Backup timestamp (epoch time).
* @param string $pretty_date Pretty date.
* @param string $entities Entities backed-up in this backup.
* @return void - Display 'Restore' button.
*/
public function restore_button($backup, $key, $pretty_date, $entities = '') {
?>
jobdata_getarray($nonce);
if (!empty($jobdata) && 'finished' != $jobdata['jobstatus']) return '';
// Check that the user has remote storage setup.
$services = (array) $updraftplus->just_one($updraftplus->get_canonical_service_list());
if (empty($services)) return '';
$show_upload = false;
// Check that the backup has not already been sent to remote storage before.
if (empty($backup['service']) || array('none') == $backup['service'] || array('') == $backup['service'] || 'none' == $backup['service']) {
$show_upload = true;
// If it has been uploaded then check if there are any new remote storage options that it has not yet been sent to.
} elseif (!empty($backup['service']) && array('none') != $backup['service'] && array('') != $backup['service'] && 'none' != $backup['service']) {
foreach ($services as $key => $value) {
if (is_string($backup['service'])) $backup['service'] = array($backup['service']);
if (in_array($value, $backup['service'])) unset($services[$key]);
}
if (!empty($services)) $show_upload = true;
}
if ($show_upload) {
$backup_local = $this->check_backup_is_complete($backup, false, false, true);
if ($backup_local) {
$service_list = '';
$service_list_display = '';
$is_first_service = true;
foreach ($services as $key => $service) {
if (!$is_first_service) {
$service_list .= ',';
$service_list_display .= ', ';
}
$service_list .= $service;
$service_list_display .= $updraftplus->backup_methods[$service];
$is_first_service = false;
}
?>
backups_dir_location();
if (isset($backup['nonce']) && preg_match("/^[0-9a-f]{12}$/", $backup['nonce']) && is_readable($updraft_dir.'/log.'.$backup['nonce'].'.txt')) {
$nval = $backup['nonce'];
$url = UpdraftPlus_Options::admin_page()."?page=updraftplus&action=downloadlog&updraftplus_backup_nonce=$nval";
?>
'', 'plugins' => '', 'themes' => '', 'uploads' => '', 'others' => '');
} else {
$entities = $updraftplus->get_backupable_file_entities(true, true);
// Add the database to the entities array ready to loop over
$entities['db'] = '';
foreach ($entities as $key => $info) {
if (!UpdraftPlus_Options::get_updraft_option("updraft_include_$key", false)) {
unset($entities[$key]);
}
}
}
$updraft_dir = trailingslashit($updraftplus->backups_dir_location());
foreach ($entities as $type => $info) {
if ($full_backup) {
if (UpdraftPlus_Options::get_updraft_option("updraft_include_$type", false) && !isset($backup[$type])) return false;
}
if (!isset($backup[$type])) return false;
if ($local) {
// Cast this to an array so that a warning is not thrown when we encounter a Database.
foreach ((array) $backup[$type] as $value) {
if (!file_exists($updraft_dir . DIRECTORY_SEPARATOR . $value)) return false;
}
}
}
return true;
}
/**
* This function will set up the backup job data for when we are uploading a local backup to remote storage. It changes the initial jobdata so that UpdraftPlus knows about what files it's uploading and so that it skips directly to the upload stage.
*
* @param array $jobdata - the initial job data that we want to change
* @param array $options - options sent from the front end includes backup timestamp and nonce
*
* @return array - the modified jobdata
*/
public function upload_local_backup_jobdata($jobdata, $options) {
global $updraftplus;
if (!is_array($jobdata)) return $jobdata;
$backup_history = UpdraftPlus_Backup_History::get_history();
$services = !empty($options['services']) ? $options['services'] : array();
$backup = $backup_history[$options['use_timestamp']];
/*
The initial job data is not set up in a key value array instead it is set up so key "x" is the name of the key and then key "y" is the value.
e.g array[0] = 'backup_name' array[1] = 'my_backup'
*/
$jobstatus_key = array_search('jobstatus', $jobdata) + 1;
$backup_time_key = array_search('backup_time', $jobdata) + 1;
$backup_database_key = array_search('backup_database', $jobdata) + 1;
$backup_files_key = array_search('backup_files', $jobdata) + 1;
$service_key = array_search('service', $jobdata) + 1;
$db_backups = $jobdata[$backup_database_key];
$db_backup_info = $updraftplus->update_database_jobdata($db_backups, $backup);
$file_backups = $updraftplus->update_files_jobdata($backup);
// Next we need to build the services array using the remote storage destinations the user has selected to upload this backup set to
$selected_services = array();
if (is_array($services)) {
foreach ($services as $storage_info) {
$selected_services[] = $storage_info['value'];
}
} else {
$selected_services = array($services);
}
$jobdata[$jobstatus_key] = 'clouduploading';
$jobdata[$backup_time_key] = $options['use_timestamp'];
$jobdata[$backup_files_key] = 'finished';
$jobdata[] = 'backup_files_array';
$jobdata[] = $file_backups;
$jobdata[] = 'blog_name';
$jobdata[] = $db_backup_info['blog_name'];
$jobdata[$backup_database_key] = $db_backup_info['db_backups'];
$jobdata[] = 'local_upload';
$jobdata[] = true;
if (!empty($selected_services)) $jobdata[$service_key] = $selected_services;
return $jobdata;
}
/**
* This function allows us to change the backup name, this is needed when uploading a local database backup to remote storage when the backup has come from another site.
*
* @param string $backup_name - the current name of the backup file
* @param string $use_time - the current timestamp we are using
* @param string $blog_name - the blog name of the current site
*
* @return string - the new filename or the original if the blog name from the job data is not set
*/
public function upload_local_backup_name($backup_name, $use_time, $blog_name) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found -- Filter use
global $updraftplus;
$backup_blog_name = $updraftplus->jobdata_get('blog_name', '');
if ('' != $blog_name && '' != $backup_blog_name) {
return str_replace($blog_name, $backup_blog_name, $backup_name);
}
return $backup_name;
}
/**
* This function starts the updraftplus restore process it processes $_REQUEST
* (keys: updraft_*, meta_foreign, backup_timestamp and job_id)
*
* @return void
*/
public function prepare_restore() {
global $updraftplus;
// on restore start job_id is empty but if we needed file system permissions or this is a resumption then we have already started a job so reuse it
$restore_job_id = empty($_REQUEST['job_id']) ? false : stripslashes($_REQUEST['job_id']);
if (false !== $restore_job_id && !preg_match('/^[0-9a-f]+$/', $restore_job_id)) die('Invalid request (restore_job_id).');
if (isset($_REQUEST['action']) && 'updraft_ajaxrestore_continue' === $_REQUEST['action']) { // unlike updraft_ajaxrestore which requires nonce to start a new restoration, updraft_ajaxrestore_continue doesn't require nonces at all so additional checks are required
$restore_in_progress = get_site_option('updraft_restore_in_progress');
if (empty($restore_in_progress) || !$restore_job_id || $restore_job_id !== $restore_in_progress) die; // continuation requires a job ID, and if it's not presented then just abort without showing anything
}
// Set up nonces, log files etc.
$updraftplus->initiate_restore_job($restore_job_id);
// If this is the start of a restore then get the restore data from the posted data and put it into jobdata.
if (isset($_REQUEST['action']) && 'updraft_restore' == $_REQUEST['action']) {
if (empty($restore_job_id)) {
$jobdata_to_save = array();
foreach ($_REQUEST as $key => $value) {
if (false !== strpos($key, 'updraft_') || 'backup_timestamp' == $key || 'meta_foreign' == $key) {
if ('updraft_restorer_restore_options' == $key) parse_str(stripslashes($value), $value);
$jobdata_to_save[$key] = $value;
}
}
$selective_restore_types = array(
'tables',
'plugins',
'themes',
);
foreach ($selective_restore_types as $type) {
if (isset($jobdata_to_save['updraft_restorer_restore_options']['updraft_restore_'.$type.'_options']) && !empty($jobdata_to_save['updraft_restorer_restore_options']['updraft_restore_'.$type.'_options'])) {
$restore_entities_options = $jobdata_to_save['updraft_restorer_restore_options']['updraft_restore_'.$type.'_options'];
$include_unspecified_entities = false;
$entities_to_restore = array();
$entities_to_skip = array();
foreach ($restore_entities_options as $entity) {
if ('udp_all_other_'.$type == $entity) {
$include_unspecified_entities = true;
} elseif (substr($entity, 0, strlen('udp-skip-'.$type)) == 'udp-skip-'.$type) {
$entities_to_skip[] = substr($entity, strlen('udp-skip-'.$type) + 1);
} else {
$entities_to_restore[] = $entity;
}
}
$jobdata_to_save['updraft_restorer_restore_options']['include_unspecified_'.$type] = $include_unspecified_entities;
$jobdata_to_save['updraft_restorer_restore_options'][$type.'_to_restore'] = $entities_to_restore;
$jobdata_to_save['updraft_restorer_restore_options'][$type.'_to_skip'] = $entities_to_skip;
unset($jobdata_to_save['updraft_restorer_restore_options']['updraft_restore_'.$type.'_options']);
}
}
$updraftplus->jobdata_set_multi($jobdata_to_save);
// 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
update_site_option('updraft_restore_in_progress', $updraftplus->nonce);
}
}
// If this is the start of an ajax restore then end execution here so it can then be booted over ajax
if (isset($_REQUEST['updraftplus_ajax_restore']) && 'start_ajax_restore' == $_REQUEST['updraftplus_ajax_restore']) {
// return to prevent any more code from running
return $this->prepare_ajax_restore();
} elseif (isset($_REQUEST['updraftplus_ajax_restore']) && 'continue_ajax_restore' == $_REQUEST['updraftplus_ajax_restore']) {
// If we enter here then in order to restore we needed to require the filesystem credentials we should save these before returning back to the browser and load them back after the AJAX call, this prevents us asking for the filesystem credentials again
$filesystem_credentials = array(
'hostname' => '',
'username' => '',
'password' => '',
'connection_type' => '',
'upgrade' => '',
);
$credentials_found = false;
foreach ($_REQUEST as $key => $value) {
if (array_key_exists($key, $filesystem_credentials)) {
$filesystem_credentials[$key] = stripslashes($value);
$credentials_found = true;
}
}
if ($credentials_found) $updraftplus->jobdata_set('filesystem_credentials', $filesystem_credentials);
// return to prevent any more code from running
return $this->prepare_ajax_restore();
}
if (!empty($_REQUEST['updraftplus_ajax_restore'])) add_filter('updraftplus_logline', array($this, 'updraftplus_logline'), 10, 5);
$is_continuation = ('updraft_ajaxrestore_continue' == $_REQUEST['action']) ? true : false;
if ($is_continuation) {
$restore_in_progress = get_site_option('updraft_restore_in_progress');
if ($restore_in_progress != $_REQUEST['job_id']) {
$abort_restore_already = true;
$updraftplus->log(__('Sufficient information about the in-progress restoration operation could not be found.', 'updraftplus') . ' (job_id_mismatch)', 'error', 'job_id_mismatch');
} else {
$restore_jobdata = $updraftplus->jobdata_getarray($restore_in_progress);
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'])) {
$backup_timestamp = $restore_jobdata['backup_timestamp'];
$continuation_data = $restore_jobdata;
$continuation_data['updraftplus_ajax_restore'] = 'continue_ajax_restore';
} else {
$abort_restore_already = true;
$updraftplus->log(__('Sufficient information about the in-progress restoration operation could not be found.', 'updraftplus') . ' (job_id_nojobdata)', 'error', 'job_id_nojobdata');
}
}
} elseif (isset($_REQUEST['updraftplus_ajax_restore']) && 'do_ajax_restore' == $_REQUEST['updraftplus_ajax_restore']) {
$backup_timestamp = $updraftplus->jobdata_get('backup_timestamp');
$continuation_data = array('updraftplus_ajax_restore' => 'do_ajax_restore');
} else {
$backup_timestamp = (int) $_REQUEST['backup_timestamp'];
$continuation_data = null;
}
$filesystem_credentials = $updraftplus->jobdata_get('filesystem_credentials', array());
if (!empty($filesystem_credentials)) {
$continuation_data['updraftplus_ajax_restore'] = 'continue_ajax_restore';
// If the filesystem credentials are not empty then we now need to load these back into $_POST so that WP_Filesystem can access them
foreach ($filesystem_credentials as $key => $value) {
$_POST[$key] = $value;
}
}
if (empty($abort_restore_already)) {
$backup_success = $this->restore_backup($backup_timestamp, $continuation_data);
} else {
$backup_success = false;
}
if (empty($updraftplus->errors) && true === $backup_success) {
// TODO: Deal with the case of some of the work having been deferred
echo '';
$updraftplus->log_e('Restore successful!');
echo '
';
$updraftplus->log('Restore successful');
$s_val = 1;
if (!empty($this->entities_to_restore) && is_array($this->entities_to_restore)) {
foreach ($this->entities_to_restore as $v) {
if ('db' != $v) $s_val = 2;
}
}
$pval = $updraftplus->have_addons ? 1 : 0;
echo ''.esc_html__('Actions', 'updraftplus').': '.esc_html__('Return to UpdraftPlus configuration', 'updraftplus').'';
return;
} elseif (is_wp_error($backup_success)) {
echo '';
$updraftplus->log_e('Restore failed...');
echo '
';
$updraftplus->log_wp_error($backup_success);
$updraftplus->log('Restore failed');
echo '';
$updraftplus->list_errors();
echo '
';
echo ''.esc_html__('Actions', 'updraftplus').': '.esc_html__('Return to UpdraftPlus configuration', 'updraftplus').'';
return;
} elseif (false === $backup_success) {
// 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"
echo '';
$updraftplus->log_e('Restore failed...');
echo '
';
$updraftplus->log("Restore failed");
echo '';
$updraftplus->list_errors();
echo '
';
echo '' . __('Actions', 'updraftplus') . ': ' . __('Return to UpdraftPlus configuration', 'updraftplus') . '';
return;
}
}
/**
* This function will load the required ajax and output any relevant html for the ajax restore
*
* @return void
*/
private function prepare_ajax_restore() {
global $updraftplus;
$debug = $updraftplus->use_unminified_scripts();
$enqueue_version = $debug ? $updraftplus->version . '.' . time() : $updraftplus->version;
$updraft_min_or_not = $updraftplus->get_updraftplus_file_version();
$ajax_action = isset($_REQUEST['updraftplus_ajax_restore']) && 'continue_ajax_restore' == $_REQUEST['updraftplus_ajax_restore'] && 'updraft_restore' != $_REQUEST['action'] ? 'updraft_ajaxrestore_continue' : 'updraft_ajaxrestore';
// get the entities info
$jobdata = $updraftplus->jobdata_getarray($updraftplus->nonce);
$restore_components = $jobdata['updraft_restore'];
usort($restore_components, array('UpdraftPlus_Manipulation_Functions', 'sort_restoration_entities'));
$backupable_entities = $updraftplus->get_backupable_file_entities(true, true);
$pretty_date = get_date_from_gmt(gmdate('Y-m-d H:i:s', (int) $jobdata['backup_timestamp']), 'M d, Y G:i');
wp_enqueue_script('updraft-admin-restore', UPDRAFTPLUS_URL . '/js/updraft-admin-restore' . $updraft_min_or_not . '.js', array(), $enqueue_version);
$updraftplus->log("Restore setup, now closing connection and starting restore over AJAX.");
echo '';
echo '
';
echo '
'.esc_html__('Warning: If you can still read these words after the page finishes loading, then there is a JavaScript or jQuery problem in the site.', 'updraftplus').' '.esc_html__('This may prevent the restore procedure from being able to proceed.', 'updraftplus').'';
echo ' '.esc_html__('Go here for more information.', 'updraftplus').'
';
echo '
';
echo '
'.esc_html__('UpdraftPlus Restoration', 'updraftplus').' - '.esc_html__('Backup', 'updraftplus').' '.esc_html($pretty_date).'
';
echo '
';
if ($debug) echo '
';
echo '
';
echo '
';
echo '
';
echo '
';
echo '
'.sprintf(esc_html__('The restore operation has begun (%s).', 'updraftplus'), $updraftplus->nonce).' '.esc_html__('Do not close this page until it reports itself as having finished.', 'updraftplus').'
';
echo '
'.esc_html__('Restoration progress:', 'updraftplus').'
';
echo '
';
echo '
';
echo '- '.esc_html__('Verifying', 'updraftplus').'
';
foreach ($restore_components as $restore_component) {
// Set Database description
if ('db' == $restore_component && !isset($backupable_entities[$restore_component]['description'])) $backupable_entities[$restore_component]['description'] = __('Database', 'updraftplus');
if (!isset($backupable_entities[$restore_component])) {
die('Abort: invalid data');
}
echo '- '.(isset($backupable_entities[$restore_component]['description']) ? esc_html($backupable_entities[$restore_component]['description']) : esc_html($restore_component)).'
';
}
echo '- '.esc_html__('Cleaning', 'updraftplus').'
';
echo '- '.esc_html__('Finished', 'updraftplus').'
';
echo '
'; // end ul.updraft_restore_components_list
// Provide download link for the log file
echo '
'.esc_html__('Follow this link to download the log file for this restoration (needed for any support requests).', 'updraftplus').'
';
echo '
'; // end .updraft_restore_main--components
echo '
';
echo '
'.esc_html__('Activity log', 'updraftplus').'
';
echo '
';
echo '
'; // end .updraft_restore_main--activity
echo '
';
echo '
'; // end .updraft_restore_main
echo '
'; // end .updraft_restore_container
}
/**
* Processes the jobdata to build an array of entities to restore.
*
* @param Array $backup_set - information on the backup to restore
*
* @return Array - the entities to restore built from the restore jobdata
*/
private function get_entities_to_restore_from_jobdata($backup_set) {
global $updraftplus;
$updraft_restore = $updraftplus->jobdata_get('updraft_restore');
if (empty($updraft_restore) || (!is_array($updraft_restore))) $updraft_restore = array();
$entities_to_restore = array();
$foreign_known = apply_filters('updraftplus_accept_archivename', array());
foreach ($updraft_restore as $entity) {
if (empty($backup_set['meta_foreign'])) {
$entities_to_restore[$entity] = $entity;
} else {
if ('db' == $entity && !empty($foreign_known[$backup_set['meta_foreign']]) && !empty($foreign_known[$backup_set['meta_foreign']]['separatedb'])) {
$entities_to_restore[$entity] = 'db';
} else {
$entities_to_restore[$entity] = 'wpcore';
}
}
}
return $entities_to_restore;
}
/**
* Processes the jobdata to build an array of restoration options
*
* @return Array - the restore options built from the restore jobdata
*/
private function get_restore_options_from_jobdata() {
global $updraftplus;
$restore_options = $updraftplus->jobdata_get('updraft_restorer_restore_options');
$updraft_encryptionphrase = $updraftplus->jobdata_get('updraft_encryptionphrase');
$include_wpconfig = $updraftplus->jobdata_get('updraft_restorer_wpcore_includewpconfig');
$restore_options['updraft_encryptionphrase'] = empty($updraft_encryptionphrase) ? '' : $updraft_encryptionphrase;
$restore_options['updraft_restorer_wpcore_includewpconfig'] = !empty($include_wpconfig);
$restore_options['updraft_incremental_restore_point'] = empty($restore_options['updraft_incremental_restore_point']) ? -1 : (int) $restore_options['updraft_incremental_restore_point'];
return $restore_options;
}
/**
* Carry out the restore process within the WP admin dashboard, using data from $_POST
*
* @param Integer $timestamp Identifying the backup to be restored
* @param Array|null $continuation_data For continuing a multi-stage restore; this is the saved jobdata for the job; in this method the keys used are second_loop_entities, restore_options; but it is also passed on to Updraft_Restorer::perform_restore()
* @return Boolean|WP_Error - a WP_Error indicates a terminal failure; false indicates not-yet complete (not necessarily terminal); true indicates complete.
*/
private function restore_backup($timestamp, $continuation_data = null) {
global $updraftplus, $updraftplus_restorer;
$second_loop_entities = empty($continuation_data['second_loop_entities']) ? array() : $continuation_data['second_loop_entities'];
// If this is a resumption and we still need to restore the database we should rebuild the backup history to ensure the database is in there.
if (!empty($second_loop_entities['db'])) UpdraftPlus_Backup_History::rebuild();
$backup_set = UpdraftPlus_Backup_History::get_history($timestamp);
if (empty($backup_set)) {
echo ''.esc_html__('This backup does not exist in the backup history - restoration aborted.', 'updraftplus').' '.esc_html__('Timestamp:', 'updraftplus').' '.esc_html($timestamp).'
';
return new WP_Error('does_not_exist', __('Backup does not exist in the backup history', 'updraftplus')." ($timestamp)");
}
$backup_set['timestamp'] = $timestamp;
$url_parameters = array(
'backup_timestamp' => $timestamp,
'job_id' => $updraftplus->nonce
);
if (!empty($continuation_data['updraftplus_ajax_restore'])) {
$url_parameters['updraftplus_ajax_restore'] = 'continue_ajax_restore';
$updraftplus->output_to_browser(''); // Start timer
// Force output buffering off so that we get log lines sent to the browser as they come not all at once at the end of the ajax restore
// zlib creates an output buffer, and waits for the entire page to be generated before it can send it to the client try to turn it off
@ini_set("zlib.output_compression", '0');// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise because of the function.
// Turn off PHP output buffering for NGINX
header('X-Accel-Buffering: no');
header('Content-Encoding: none');
while (ob_get_level()) {
ob_end_flush();
}
if (version_compare(PHP_VERSION, '8.0.0', '>=')) {
$flush = true; // PHP 8.0.0 or newer, use bool
} else {
$flush = 1; // PHP older than 8.0.0, use int
}
ob_implicit_flush($flush);
}
$updraftplus->log("Ensuring WP_Filesystem is setup for a restore");
// This will print HTML and die() if necessary
UpdraftPlus_Filesystem_Functions::ensure_wp_filesystem_set_up_for_restore($url_parameters);
$updraftplus->log("WP_Filesystem is setup and ready for a restore");
$entities_to_restore = $this->get_entities_to_restore_from_jobdata($backup_set);
if (empty($entities_to_restore)) {
$restore_jobdata = $updraftplus->jobdata_getarray($updraftplus->nonce);
echo ''.esc_html__('ABORT: Could not find the information on which entities to restore.', 'updraftplus').'
'.esc_html__('If making a request for support, please include this information:', 'updraftplus').' '.count($restore_jobdata).' : '.esc_html(serialize($restore_jobdata)).'
';
return new WP_Error('missing_info', 'Backup information not found');
}
// This is used in painting the admin page after a successful restore
$this->entities_to_restore = $entities_to_restore;
$error_levels = version_compare(PHP_VERSION, '8.4.0', '>=') ? E_ALL : E_ALL & ~E_STRICT;
// This will be removed by Updraft_Restorer::post_restore_clean_up()
set_error_handler(array($updraftplus, 'php_error'), $error_levels);
// Set $restore_options, either from the continuation data, or from $_POST
if (!empty($continuation_data['restore_options'])) {
$restore_options = $continuation_data['restore_options'];
} else {
// Gather the restore options into one place - code after here should read the options
$restore_options = $this->get_restore_options_from_jobdata();
$updraftplus->jobdata_set('restore_options', $restore_options);
}
add_action('updraftplus_restoration_title', array($this, 'restoration_title'));
$updraftplus->log_restore_update(array('type' => 'state', 'stage' => 'started', 'data' => array()));
// We use a single object for each entity, because we want to store information about the backup set
$updraftplus_restorer = new Updraft_Restorer(new Updraft_Restorer_Skin, $backup_set, false, $restore_options, $continuation_data);
$restore_result = $updraftplus_restorer->perform_restore($entities_to_restore, $restore_options);
$updraftplus_restorer->post_restore_clean_up($restore_result);
$pval = $updraftplus->have_addons ? 1 : 0;
$sval = (true === $restore_result) ? 1 : 0;
$pages = get_pages(array('number' => 2));
$page_urls = array(
'home' => get_home_url(),
);
foreach ($pages as $page_info) {
$page_urls[$page_info->post_name] = get_page_link($page_info->ID);
}
$updraftplus->log_restore_update(
array(
'type' => 'state',
'stage' => 'finished',
'data' => array(
'actions' => array(
__('Return to UpdraftPlus configuration', 'updraftplus') => UpdraftPlus_Options::admin_page_url() . '?page=updraftplus&updraft_restore_success=' . $sval . '&pval=' . $pval
),
'urls' => $page_urls,
)
)
);
return $restore_result;
}
/**
* Called when the restore process wants to print a title
*
* @param String $title - title
*/
public function restoration_title($title) {
echo ''.esc_html($title).'
';
}
/**
* Logs a line from the restore process, being called from UpdraftPlus::log().
* Hooks the WordPress filter updraftplus_logline
* In future, this can get more sophisticated. For now, things are funnelled through here, giving the future possibility.
*
* @param String $line - the line to be logged
* @param String $nonce - the job ID of the restore job
* @param String $level - the level of the log notice
* @param String|Boolean $uniq_id - a unique ID for the log if it should only be logged once; or false otherwise
* @param String $destination - the type of job ongoing. If it is not 'restore', then we will skip the logging.
*
* @return String|Boolean - the filtered value. If set to false, then UpdraftPlus::log() will stop processing the log line.
*/
public function updraftplus_logline($line, $nonce, $level, $uniq_id, $destination) {// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found -- Unused parameter is present because the method is used as a WP filter.
if ('progress' != $destination || (defined('WP_CLI') && WP_CLI) || false === $line || false === strpos($line, 'RINFO:')) return $line;
global $updraftplus;
$updraftplus->output_to_browser($line);
// Indicate that we have completely handled all logging needed
return false;
}
/**
* Ensure that what is returned is an array. Used as a WP options filter.
*
* @param Array $input - input
*
* @return Array
*/
public function return_array($input) {
return is_array($input) ? $input : array();
}
/**
* Called upon the WP action wp_ajax_updraft_savesettings. Will die().
*/
public function updraft_ajax_savesettings() {
try {
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');
if (empty($_POST['settings']) || !is_string($_POST['settings'])) die('Invalid data');
parse_str(stripslashes($_POST['settings']), $posted_settings);
// We now have $posted_settings as an array
if (!empty($_POST['updraftplus_version'])) $posted_settings['updraftplus_version'] = $_POST['updraftplus_version'];
echo json_encode($this->save_settings($posted_settings));
} catch (Exception $e) {
$log_message = 'PHP Fatal Exception error ('.get_class($e).') has occurred during save settings. Error Message: '.$e->getMessage().' (Code: '.$e->getCode().', line '.$e->getLine().' in '.$e->getFile().')';
error_log($log_message);
echo json_encode(array(
'fatal_error' => true,
'fatal_error_message' => $log_message
));
// @codingStandardsIgnoreLine
} catch (Error $e) {
$log_message = 'PHP Fatal error ('.get_class($e).') has occurred during save settings. Error Message: '.$e->getMessage().' (Code: '.$e->getCode().', line '.$e->getLine().' in '.$e->getFile().')';
error_log($log_message);
echo json_encode(array(
'fatal_error' => true,
'fatal_error_message' => $log_message
));
}
die;
}
public function updraft_ajax_importsettings() {
try {
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');
if (empty($_POST['settings']) || !is_string($_POST['settings'])) die('Invalid data');
$this->import_settings($_POST);
} catch (Exception $e) {
$log_message = 'PHP Fatal Exception error ('.get_class($e).') has occurred during import settings. Error Message: '.$e->getMessage().' (Code: '.$e->getCode().', line '.$e->getLine().' in '.$e->getFile().')';
error_log($log_message);
echo json_encode(array(
'fatal_error' => true,
'fatal_error_message' => $log_message
));
// @codingStandardsIgnoreLine
} catch (Error $e) {
$log_message = 'PHP Fatal error ('.get_class($e).') has occurred during import settings. Error Message: '.$e->getMessage().' (Code: '.$e->getCode().', line '.$e->getLine().' in '.$e->getFile().')';
error_log($log_message);
echo json_encode(array(
'fatal_error' => true,
'fatal_error_message' => $log_message
));
}
}
/**
* 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)
*
* @param Array $settings - The settings from the imported json file
*/
public function import_settings($settings) {
// A bug in UD releases around 1.12.40 - 1.13.3 meant that it was saved in URL-string format, instead of JSON
$perhaps_not_yet_parsed = json_decode(stripslashes($settings['settings']), true);
if (!is_array($perhaps_not_yet_parsed)) {
parse_str($perhaps_not_yet_parsed, $posted_settings);
} else {
$posted_settings = $perhaps_not_yet_parsed;
}
if (!empty($settings['updraftplus_version'])) $posted_settings['updraftplus_version'] = $settings['updraftplus_version'];
// Handle the settings name change of WebDAV and SFTP (Apr 2017) if someone tries to import an old settings to this version
if (isset($posted_settings['updraft_webdav_settings'])) {
$posted_settings['updraft_webdav'] = $posted_settings['updraft_webdav_settings'];
unset($posted_settings['updraft_webdav_settings']);
}
if (isset($posted_settings['updraft_sftp_settings'])) {
$posted_settings['updraft_sftp'] = $posted_settings['updraft_sftp_settings'];
unset($posted_settings['updraft_sftp_settings']);
}
// 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
if (empty($posted_settings['updraft_webdav']['settings'])) $posted_settings['updraft_webdav'] = UpdraftPlus_Storage_Methods_Interface::wrap_remote_storage_options($posted_settings['updraft_webdav']);
if (empty($posted_settings['updraft_googledrive']['settings'])) $posted_settings['updraft_googledrive'] = UpdraftPlus_Storage_Methods_Interface::wrap_remote_storage_options($posted_settings['updraft_googledrive']);
if (empty($posted_settings['updraft_googlecloud']['settings'])) $posted_settings['updraft_googlecloud'] = UpdraftPlus_Storage_Methods_Interface::wrap_remote_storage_options($posted_settings['updraft_googlecloud']);
if (empty($posted_settings['updraft_onedrive']['settings'])) $posted_settings['updraft_onedrive'] = UpdraftPlus_Storage_Methods_Interface::wrap_remote_storage_options($posted_settings['updraft_onedrive']);
if (empty($posted_settings['updraft_azure']['settings'])) $posted_settings['updraft_azure'] = UpdraftPlus_Storage_Methods_Interface::wrap_remote_storage_options($posted_settings['updraft_azure']);
if (empty($posted_settings['updraft_dropbox']['settings'])) $posted_settings['updraft_dropbox'] = UpdraftPlus_Storage_Methods_Interface::wrap_remote_storage_options($posted_settings['updraft_dropbox']);
echo json_encode($this->save_settings($posted_settings));
die;
}
/**
* This function will get a list of remote storage methods with valid connection details and create a HTML list of checkboxes
*
* @return String - HTML checkbox list of remote storage methods with valid connection details
*/
private function backup_now_remote_message() {
global $updraftplus;
$services = (array) $updraftplus->just_one($updraftplus->get_canonical_service_list());
$all_services = UpdraftPlus_Storage_Methods_Interface::get_storage_objects_and_ids($services);
$active_remote_storage_list = '';
foreach ($all_services as $method => $sinfo) {
if ('email' == $method) {
$possible_emails = $updraftplus->just_one_email(UpdraftPlus_Options::get_updraft_option('updraft_email'));
if (!empty($possible_emails)) {
$active_remote_storage_list .= '
';
}
} elseif (empty($sinfo['object']) || empty($sinfo['instance_settings']) || !is_callable(array($sinfo['object'], 'options_exist'))) {
continue;
}
$instance_count = 1;
foreach ($sinfo['instance_settings'] as $instance => $opt) {
if ($sinfo['object']->options_exist($opt)) {
$instance_count_label = (1 == $instance_count) ? '' : ' ('.$instance_count.')';
$label = empty($opt['instance_label']) ? $sinfo['object']->get_description() . $instance_count_label : $opt['instance_label'];
if (!isset($opt['instance_enabled'])) $opt['instance_enabled'] = 1;
$checked = empty($opt['instance_enabled']) ? '' : 'checked="checked"';
$active_remote_storage_list .= '
';
$instance_count++;
}
}
}
$service = $updraftplus->just_one(UpdraftPlus_Options::get_updraft_option('updraft_service'));
if (is_string($service)) $service = array($service);
if (!is_array($service)) $service = array();
$no_remote_configured = (empty($service) || array('none') === $service || array('') === $service) ? true : false;
if ($no_remote_configured && empty($active_remote_storage_list)) {
return ' '.
/* translators: %s: "settings" which is the name of a tab on which remote storage settings are configured */
sprintf(__("Backup won't be sent to any remote storage - none has been saved in the %s", 'updraftplus'), ''.
__('settings', 'updraftplus')).'. '.__('Not got any remote storage?', 'updraftplus').' '.__("Check out UpdraftVault.", 'updraftplus').'';
}
if (empty($active_remote_storage_list)) {
$active_remote_storage_list = ''.__('No remote storage locations with valid options found.', 'updraftplus').'
';
}
return ' (...)
'. __('The following remote storage options are configured.', 'updraftplus').'
'.$active_remote_storage_list.'
';
}
/**
* 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
*
* @param array $settings An array of settings taking from the admin page ready to be saved to the database
* @return array An array response containing the status of the update along with content to be used to update the admin page.
*/
public function save_settings($settings) {
global $updraftplus;
// Make sure that settings filters are registered
UpdraftPlus_Options::admin_init();
$more_files_path_updated = false;
if (isset($settings['updraftplus_version']) && $updraftplus->version == $settings['updraftplus_version']) {
$return_array = array('saved' => true);
$add_to_post_keys = array('updraft_interval', 'updraft_interval_database', 'updraft_interval_increments', 'updraft_starttime_files', 'updraft_starttime_db', 'updraft_startday_files', 'updraft_startday_db');
// If database and files are on same schedule, override the db day/time settings
if (isset($settings['updraft_interval_database']) && isset($settings['updraft_interval_database']) && $settings['updraft_interval_database'] == $settings['updraft_interval'] && isset($settings['updraft_starttime_files'])) {
$settings['updraft_starttime_db'] = $settings['updraft_starttime_files'];
$settings['updraft_startday_db'] = $settings['updraft_startday_files'];
}
foreach ($add_to_post_keys as $key) {
// For add-ons that look at $_POST to find saved settings, add the relevant keys to $_POST so that they find them there
if (isset($settings[$key])) {
$_POST[$key] = $settings[$key];
}
}
// 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.
$more_files_path_updated = false;
// i.e. If an option has been set, or if it was currently active in the settings
if (isset($settings['updraft_include_more_path']) || UpdraftPlus_Options::get_updraft_option('updraft_include_more_path')) {
$more_files_path_updated = true;
}
// Wipe the extra retention rules, as they are not saved correctly if the last one is deleted
UpdraftPlus_Options::update_updraft_option('updraft_retain_extrarules', array());
UpdraftPlus_Options::update_updraft_option('updraft_email', array());
UpdraftPlus_Options::update_updraft_option('updraft_report_warningsonly', array());
UpdraftPlus_Options::update_updraft_option('updraft_report_wholebackup', array());
UpdraftPlus_Options::update_updraft_option('updraft_extradbs', array());
UpdraftPlus_Options::update_updraft_option('updraft_include_more_path', array());
$relevant_keys = $updraftplus->get_settings_keys();
if (isset($settings['updraft_auto_updates']) && in_array('updraft_auto_updates', $relevant_keys)) {
$updraftplus->set_automatic_updates($settings['updraft_auto_updates']);
unset($settings['updraft_auto_updates']); // unset the key and its value to prevent being processed the second time
}
if (method_exists('UpdraftPlus_Options', 'mass_options_update')) {
$original_settings = $settings;
$settings = UpdraftPlus_Options::mass_options_update($settings);
$mass_updated = true;
}
foreach ($settings as $key => $value) {
if (in_array($key, $relevant_keys)) {
if ('updraft_service' == $key && is_array($value)) {
foreach ($value as $subkey => $subvalue) {
if ('0' == $subvalue) unset($value[$subkey]);
}
}
// 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.
$updated = empty($mass_updated) ? (is_string($value) && UpdraftPlus_Options::get_updraft_option($key) != $value) : (is_string($value) && (!isset($original_settings[$key]) || $original_settings[$key] != $value));
if (empty($mass_updated)) UpdraftPlus_Options::update_updraft_option($key, $value);
// Add information on what has changed to array to loop through to update links etc.
// Restricting to strings for now, to prevent any unintended leakage (since this is just used for UI updating)
if ($updated) {
$value = UpdraftPlus_Options::get_updraft_option($key);
if (is_string($value)) $return_array['changed'][$key] = $value;
}
// @codingStandardsIgnoreLine
} else {
// This section is ignored by CI otherwise it will complain the ELSE is empty.
// 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.
// error_log("Non-UD key when saving from POSTed data: ".$key);
}
}
} else {
$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.', 'updraftplus'), $updraftplus->version).' '.__('Please reload the settings page before trying to save settings.', 'updraftplus'));
}
// Checking for various possible messages
$updraft_dir = $updraftplus->backups_dir_location(false);
$really_is_writable = UpdraftPlus_Filesystem_Functions::really_is_writable($updraft_dir);
$dir_info = $this->really_writable_message($really_is_writable, $updraft_dir);
$button_title = esc_attr(__('This button is disabled because your backup directory is not writable (see the settings).', 'updraftplus'));
$return_array['backup_now_message'] = $this->backup_now_remote_message();
$return_array['backup_dir'] = array('writable' => $really_is_writable, 'message' => $dir_info, 'button_title' => $button_title);
// Check if $more_files_path_updated is true, is so then there's a change and we should update the backup modal
if ($more_files_path_updated) {
$return_array['updraft_include_more_path'] = $this->files_selector_widgetry('backupnow_files_', false, 'sometimes');
}
// Because of the single AJAX call, we need to remove the existing UD messages from the 'all_admin_notices' action
remove_all_actions('all_admin_notices');
// Moving from 2 to 1 ajax call
ob_start();
$service = UpdraftPlus_Options::get_updraft_option('updraft_service');
$this->setup_all_admin_notices_global($service);
$this->setup_all_admin_notices_udonly($service);
do_action('all_admin_notices');
if (!$really_is_writable) { // Check if writable
$this->show_admin_warning_unwritable();
}
if ($return_array['saved']) { //
$this->show_admin_warning(__('Your settings have been saved.', 'updraftplus'), 'updated fade');
} else {
if (isset($return_array['error_message'])) {
$this->show_admin_warning($return_array['error_message'], 'error');
} else {
$this->show_admin_warning(__('Your settings failed to save.', 'updraftplus').' '.__('Please refresh the settings page and try again', 'updraftplus'), 'error');
}
}
$messages_output = ob_get_contents();
ob_clean();
// Backup schedule output
$this->next_scheduled_backups_output('line');
$scheduled_output = ob_get_clean();
$return_array['messages'] = $messages_output;
$return_array['scheduled'] = $scheduled_output;
$return_array['files_scheduled'] = $this->next_scheduled_files_backups_output(true);
$return_array['database_scheduled'] = $this->next_scheduled_database_backups_output(true);
// Add the updated options to the return message, so we can update on screen
return $return_array;
}
/**
* Authenticate remote storage instance
*
* @param array - $data It consists of below key elements:
* $remote_method - Remote storage service
* $instance_id - Remote storage instance id
* @return array An array response containing the status of the authentication
*/
public function auth_remote_method($data) {
global $updraftplus;
$response = array();
if (isset($data['remote_method']) && isset($data['instance_id'])) {
$response['result'] = 'success';
$remote_method = $data['remote_method'];
$instance_id = $data['instance_id'];
$storage_objects_and_ids = UpdraftPlus_Storage_Methods_Interface::get_storage_objects_and_ids(array($remote_method));
try {
$storage_objects_and_ids[$remote_method]['object']->authenticate_storage($instance_id);
} catch (Exception $e) {
$response['result'] = 'error';
$response['message'] = $updraftplus->backup_methods[$remote_method] . ' ' . __('authentication error', 'updraftplus') . ' ' . $e->getMessage();
}
} else {
$response['result'] = 'error';
$response['message'] = __('Remote storage method and instance id are required for authentication.', 'updraftplus');
}
return $response;
}
/**
* Deauthenticate remote storage instance
*
* @param array - $data It consists of below key elements:
* $remote_method - Remote storage service
* $instance_id - Remote storage instance id
* @return array An array response containing the status of the deauthentication
*/
public function deauth_remote_method($data) {
global $updraftplus;
$response = array();
if (isset($data['remote_method']) && isset($data['instance_id'])) {
$response['result'] = 'success';
$remote_method = $data['remote_method'];
$instance_id = $data['instance_id'];
$storage_objects_and_ids = UpdraftPlus_Storage_Methods_Interface::get_storage_objects_and_ids(array($remote_method));
try {
$storage_objects_and_ids[$remote_method]['object']->deauthenticate_storage($instance_id);
} catch (Exception $e) {
$response['result'] = 'error';
$response['message'] = $updraftplus->backup_methods[$remote_method] . ' deauthentication error ' . $e->getMessage();
}
} else {
$response['result'] = 'error';
$response['message'] = 'Remote storage method and instance id are required for deauthentication.';
}
return $response;
}
/**
* A method to remove UpdraftPlus settings from the options table.
*
* @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.
* @return boolean
*/
public function wipe_settings($wipe_all_settings = true) {
global $updraftplus;
$settings = $updraftplus->get_settings_keys();
// 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.
if (false == $wipe_all_settings) {
$key = array_search('updraft_central_localkeys', $settings);
unset($settings[$key]);
}
foreach ($settings as $s) UpdraftPlus_Options::delete_updraft_option($s);
if (is_multisite()) $updraftplus->wipe_state_data(true, 'sitemeta');
$updraftplus->wipe_state_data(true);
$site_options = array('updraft_oneshotnonce');
foreach ($site_options as $s) delete_site_option($s);
$updraftplus->schedule_backup('manual');
$updraftplus->schedule_backup_database('manual');
$this->show_admin_warning(__("Your settings have been wiped.", 'updraftplus'));
return true;
}
/**
* This get the details for updraft vault and to be used globally
*
* @param string $instance_id - the instance_id of the current instance being used
* @return object - the UpdraftVault option setup to use the passed in instance id or if one wasn't passed then use the default set of options
*/
public function get_updraftvault($instance_id = '') {
$storage_objects_and_ids = UpdraftPlus_Storage_Methods_Interface::get_storage_objects_and_ids(array('updraftvault'));
if (isset($storage_objects_and_ids['updraftvault']['instance_settings'][$instance_id])) {
$opts = $storage_objects_and_ids['updraftvault']['instance_settings'][$instance_id];
$vault = $storage_objects_and_ids['updraftvault']['object'];
$vault->set_options($opts, false, $instance_id);
} else {
updraft_try_include_file('methods/updraftvault.php', 'include_once');
$vault = new UpdraftPlus_BackupModule_updraftvault();
}
return $vault;
}
/**
* http_get will allow the HTTP Fetch execute available in advanced tools
*
* @param String $uri Specific URL passed to curl
* @param Boolean $curl True or False if cURL is to be used
* @return String - JSON encoded results
*/
public function http_get($uri = null, $curl = false) {
if (!preg_match('/^https?/', $uri)) return json_encode(array('e' => 'Non-http URL specified'));
if ($curl) {
if (!function_exists('curl_exec')) {
return json_encode(array('e' => 'No Curl installed'));
die;
}
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $uri);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_FAILONERROR, true);
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_VERBOSE, true);
curl_setopt($ch, CURLOPT_STDERR, $output = fopen('php://temp', "w+"));
$response = curl_exec($ch);
$error = curl_error($ch);
$getinfo = curl_getinfo($ch);
curl_close($ch);
rewind($output);
$verb = stream_get_contents($output);
$resp = array();
if (false === $response) {
$resp['e'] = htmlspecialchars($error);
}
$resp['r'] = (empty($response)) ? '' : htmlspecialchars(substr($response, 0, 2048));
if (!empty($verb)) $resp['r'] = htmlspecialchars($verb)."\n\n".$resp['r'];
// Extra info returned for Central
$resp['verb'] = $verb;
$resp['response'] = $response;
$resp['status'] = $getinfo;
return json_encode($resp);
} else {
$response = wp_remote_get($uri, array('timeout' => 10));
if (is_wp_error($response)) {
return json_encode(array('e' => htmlspecialchars($response->get_error_message())));
}
return json_encode(
array(
'r' => wp_remote_retrieve_response_code($response).': '.htmlspecialchars(substr(wp_remote_retrieve_body($response), 0, 2048)),
'code' => wp_remote_retrieve_response_code($response),
'html_response' => htmlspecialchars(substr(wp_remote_retrieve_body($response), 0, 2048)),
'response' => $response
)
);
}
}
/**
* This will return all the details for raw backup and file list, in HTML format
*
* @param Boolean $no_pre_tags - if set, then tags will be removed from the output
*
* @return String
*/
public function show_raw_backups($no_pre_tags = false) {
global $updraftplus;
$response = array();
$response['html'] = ''.__('Known backups (raw)', 'updraftplus').'
';
ob_start();
$history = UpdraftPlus_Backup_History::get_history();
var_dump($history);
$response["html"] .= ob_get_clean();
$response['html'] .= '
';
$response['html'] .= ''.__('Files', 'updraftplus').'
';
$updraft_dir = $updraftplus->backups_dir_location();
$raw_output = array();
$d = dir($updraft_dir);
while (false !== ($entry = $d->read())) {
$fp = $updraft_dir.'/'.$entry;
$mtime = filemtime($fp);
if (is_dir($fp)) {
$size = ' d';
} elseif (is_link($fp)) {
$size = ' l';
} elseif (is_file($fp)) {
$size = sprintf("%8.1f", round(filesize($fp)/1024, 1)).' '.gmdate('r', $mtime);
} else {
$size = ' ?';
}
if (preg_match('/^log\.(.*)\.txt$/', $entry, $lmatch)) $entry = ''.$entry.'';
$raw_output[$mtime] = empty($raw_output[$mtime]) ? sprintf("%s %s\n", $size, $entry) : $raw_output[$mtime].sprintf("%s %s\n", $size, $entry);
}
@$d->close();// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise because of the method.
krsort($raw_output, SORT_NUMERIC);
foreach ($raw_output as $line) {
$response['html'] .= $line;
}
$response['html'] .= '';
$response['html'] .= ''.__('Options (raw)', 'updraftplus').'
';
$opts = $updraftplus->get_settings_keys();
asort($opts);
// | '.__('Key', 'updraftplus').' | '.__('Value', 'updraftplus').' |
$response['html'] .= '';
foreach ($opts as $opt) {
$response['html'] .= '| '.htmlspecialchars($opt).' | '.htmlspecialchars(print_r(UpdraftPlus_Options::get_updraft_option($opt), true)).' | ';
}
// Get the option saved by yahnis-elsts/plugin-update-checker
$response['html'] .= '
| external_updates-updraftplus | '.htmlspecialchars(print_r(get_site_option('external_updates-updraftplus'), true)).' | ';
$response['html'] .= '
';
ob_start();
do_action('updraftplus_showrawinfo');
$response['html'] .= ob_get_clean();
if (true == $no_pre_tags) {
$response['html'] = str_replace('', '', $response['html']);
$response['html'] = str_replace('', '', $response['html']);
}
return $response;
}
/**
* This will call any wp_action
*
* @param Array|Null $data The array of data with the values for wpaction
* @param Callable|Boolean $close_connection_callable A callable to call to close the browser connection, or true for a default suitable for internal use, or false for none
* @return Array - results
*/
public function call_wp_action($data = null, $close_connection_callable = false) {
global $updraftplus;
ob_start();
$res = 'Request received: ';
if (preg_match('/^([^:]+)+:(.*)$/', $data['wpaction'], $matches)) {
$action = $matches[1];
if (null === ($args = json_decode($matches[2], true))) {
$res .= "The parameters (should be JSON) could not be decoded";
$action = false;
} else {
if (is_string($args)) $args = array($args);
$res .= "Will despatch action: ".htmlspecialchars($action).", parameters: ".htmlspecialchars(implode(',', $args));
}
} else {
$action = $data['wpaction'];
$res .= "Will despatch action: ".htmlspecialchars($action).", no parameters";
}
ob_get_clean();
// Need to add this as the close browser should only work for UDP
if ($close_connection_callable) {
if (is_callable($close_connection_callable)) {
call_user_func($close_connection_callable, array('r' => $res));
} else {
$updraftplus->close_browser_connection(json_encode(array('r' => $res)));
}
}
if (!empty($action)) {
if (!empty($args)) {
ob_start();
$returned = do_action_ref_array($action, $args);
$output = ob_get_clean();
$res .= " - do_action_ref_array Trigger ";
} else {
ob_start();
do_action($action);
$output = ob_get_contents();
ob_end_clean();
$res .= " - do_action Trigger ";
}
}
$response = array();
$response['response'] = $res;
$response['log'] = $output;
// Check if response is empty
if (!empty($returned)) $response['status'] = $returned;
return $response;
}
/**
* Enqueue JSTree JavaScript and CSS, taking into account whether it is already enqueued, and current debug settings
*/
public function enqueue_jstree() {
global $updraftplus;
static $already_enqueued = false;
if ($already_enqueued) return;
$already_enqueued = true;
$jstree_enqueue_version = $updraftplus->use_unminified_scripts() ? '3.3.12-rc.0'.'.'.time() : '3.3.12-rc.0';
$min_or_not = $updraftplus->use_unminified_scripts() ? '' : '.min';
wp_enqueue_script('jstree', UPDRAFTPLUS_URL.'/includes/jstree/jstree'.$min_or_not.'.js', array('jquery'), $jstree_enqueue_version);
wp_enqueue_style('jstree', UPDRAFTPLUS_URL.'/includes/jstree/themes/default/style'.$min_or_not.'.css', array(), $jstree_enqueue_version);
}
/**
* Detects byte-order mark at the start of common files and change waning message texts
*
* @return string|boolean BOM warning text or false if not bom characters detected
*/
public function get_bom_warning_text() {
$files_to_check = array(
ABSPATH.'wp-config.php',
get_template_directory().DIRECTORY_SEPARATOR.'functions.php',
);
if (is_child_theme()) {
$files_to_check[] = get_stylesheet_directory().DIRECTORY_SEPARATOR.'functions.php';
}
$corrupted_files = array();
foreach ($files_to_check as $file) {
if (!file_exists($file)) continue;
if (false === ($fp = fopen($file, 'r'))) continue;
if (false === ($file_data = fread($fp, 8192)));
fclose($fp);
$substr_file_data = array();
for ($substr_length = 2; $substr_length <= 5; $substr_length++) {
$substr_file_data[$substr_length] = substr($file_data, 0, $substr_length);
}
// Detect UTF-7, UTF-8, UTF-16 (BE), UTF-16 (LE), UTF-32 (BE) & UTF-32 (LE) Byte order marks (BOM)
$bom_decimal_representations = array(
array(43, 47, 118, 56), // UTF-7 (Hexadecimal: 2B 2F 76 38)
array(43, 47, 118, 57), // UTF-7 (Hexadecimal: 2B 2F 76 39)
array(43, 47, 118, 43), // UTF-7 (Hexadecimal: 2B 2F 76 2B)
array(43, 47, 118, 47), // UTF-7 (Hexadecimal: 2B 2F 76 2F)
array(43, 47, 118, 56, 45), // UTF-7 (Hexadecimal: 2B 2F 76 38 2D)
array(239, 187, 191), // UTF-8 (Hexadecimal: 2B 2F 76 38 2D)
array(254, 255), // UTF-16 (BE) (Hexadecimal: FE FF)
array(255, 254), // UTF-16 (LE) (Hexadecimal: FF FE)
array(0, 0, 254, 255), // UTF-32 (BE) (Hexadecimal: 00 00 FE FF)
array(255, 254, 0, 0), // UTF-32 (LE) (Hexadecimal: FF FE 00 00)
);
foreach ($bom_decimal_representations as $bom_decimal_representation) {
$no_of_chars = count($bom_decimal_representation);
array_unshift($bom_decimal_representation, 'C*');
$binary = call_user_func_array('pack', $bom_decimal_representation);
if ($binary == $substr_file_data[$no_of_chars]) {
$corrupted_files[] = $file;
break;
}
}
}
if (empty($corrupted_files)) {
return false;
} else {
$corrupted_files_count = count($corrupted_files);
return ''.__('Warning', 'updraftplus').': '.sprintf(_n('The file %s has a "byte order mark" (BOM) at its beginning.', 'The files %s have a "byte order mark" (BOM) at their beginning.', $corrupted_files_count, 'updraftplus'), ''.implode(', ', $corrupted_files).'').' '.__('Follow this link for more information', 'updraftplus').'';
}
}
/**
* Gets an instance of the "UpdraftPlus_UpdraftCentral_Cloud" class which will be
* used to login or register the user to the UpdraftCentral cloud
*
* @return object
*/
public function get_updraftcentral_cloud() {
if (!class_exists('UpdraftPlus_UpdraftCentral_Cloud')) updraft_try_include_file('includes/updraftcentral.php', 'include_once');
return new UpdraftPlus_UpdraftCentral_Cloud();
}
/**
* This function will build and return the UpdraftPlus temporary clone ui widget
*
* @param boolean $include_testing_ui - a boolean to indicate if testing-only UI elements should be shown (N.B. they can only work if the user also has testing permissions)
* @param array $supported_wp_versions - an array of supported WordPress versions
* @param array $supported_packages - an array of supported clone packages
* @param array $supported_regions - an array of supported clone regions
* @param string $nearest_region - the user's nearest region
* @param array $supported_packages_label - an array of supported clone packages label
*
* @return string - the clone UI widget
*/
public function updraftplus_clone_ui_widget($include_testing_ui, $supported_wp_versions, $supported_packages, $supported_regions, $nearest_region = '', $supported_packages_label = array()) {
global $updraftplus;
$output = '';
$output .= ''.sprintf(__('%s version:', 'updraftplus'), 'PHP').' ';
$output .= $this->output_select_data($this->clone_php_versions, 'php');
$output .= '
';
$output .= '';
$output .= ' '.sprintf(__('%s version:', 'updraftplus'), 'WordPress').' ';
$output .= $this->output_select_data($this->get_wordpress_versions($supported_wp_versions), 'wp');
$output .= '
';
$output .= '';
$output .= ' '.__('Clone region:', 'updraftplus').' ';
$output .= $this->output_select_data($supported_regions, 'region', $nearest_region);
$output .= '
';
$backup_history = UpdraftPlus_Backup_History::get_history();
foreach ($backup_history as $key => $backup) {
$backup_complete = $this->check_backup_is_complete($backup, false, true, false);
$remote_sent = !empty($backup['service']) && ((is_array($backup['service']) && in_array('remotesend', $backup['service'])) || 'remotesend' === $backup['service']);
if (!$backup_complete || $remote_sent) unset($backup_history[$key]);
}
$output .= '';
$output .= ' '.__('Clone:', 'updraftplus').' ';
$output .= '';
$output .= '
';
$output .= '';
$output .= ' '.__('Clone package', 'updraftplus').' ('.__('more info', 'updraftplus').'): ';
$output .= '';
$output .= '
';
if ((defined('UPDRAFTPLUS_UPDRAFTCLONE_DEVELOPMENT') && UPDRAFTPLUS_UPDRAFTCLONE_DEVELOPMENT) || $include_testing_ui) {
$output .= '';
$output .= ' UpdraftClone Branch: ';
$output .= '';
$output .= '
';
$output .= '';
$output .= ' UpdraftPlus Branch: ';
$output .= '';
$output .= '
';
$output .= '';
}
$output .= '';
$output .= '';
$output .= '';
$output .= '
';
$output = apply_filters('updraftplus_clone_additional_ui', $output);
return $output;
}
/**
* This function will output a select input using the passed in values.
*
* @param array $data - the keys and values for the select
* @param string $name - the name of the items in the select input
* @param string $selected - the value we want selected by default
*
* @return string - the output of the select input
*/
public function output_select_data($data, $name, $selected = '') {
$name_version = empty($selected) ? $this->get_current_version($name) : $selected;
$output = '';
return $output;
}
/**
* This function will output the clones network information
*
* @param string $url - the clone URL
*
* @return string - the clone network information
*/
public function updraftplus_clone_info($url) {
global $updraftplus;
if (!empty($url)) {
$content = '';
$content .= ''.__('You can find your temporary clone information in your updraftplus.com account here.', 'updraftplus').'
';
} else {
$content = '' . __('Your clone has started, network information is not yet available but will be displayed here and at your updraftplus.com account once it is ready.', 'updraftplus') . '
';
$content .= '' . __('You can find your temporary clone information in your updraftplus.com account here.', 'updraftplus') . '
';
}
return $content;
}
/**
* This function will build and return an array of major WordPress versions, the array is built by calling the WordPress version API once every 24 hours and adding any new entries to our existing array of versions.
*
* @param array $supported_wp_versions - an array of supported WordPress versions
*
* @return array - an array of WordPress major versions
*/
private function get_wordpress_versions($supported_wp_versions) {
if (empty($supported_wp_versions)) $supported_wp_versions[] = $this->get_current_version('wp');
$key = array_search($this->get_current_version('wp'), $supported_wp_versions);
if ($key) {
$supported_wp_versions = array_slice($supported_wp_versions, $key);
}
$version_array = $supported_wp_versions;
return $version_array;
}
/**
* This function will get the current version the server is running for the passed in item e.g WordPress or PHP
*
* @param string $name - the thing we want to get the version for e.g WordPress or PHP
*
* @return string - returns the current version of the passed in item
*/
public function get_current_version($name) {
$version = '';
if ('php' == $name) {
$parts = explode(".", PHP_VERSION);
$version = $parts[0] . "." . $parts[1];
} elseif ('wp' == $name) {
global $updraftplus;
$wp_version = $updraftplus->get_wordpress_version();
$parts = explode(".", $wp_version);
$version = $parts[0] . "." . $parts[1];
}
return $version;
}
/**
* Show which remote storage settings are partially setup error, or if manual auth is supported show the manual auth UI
*/
public function show_admin_warning_if_remote_storage_with_partial_settings() {
if ((isset($_REQUEST['page']) && 'updraftplus' == $_REQUEST['page']) || (defined('DOING_AJAX') && DOING_AJAX)) {
$enabled_services = UpdraftPlus_Storage_Methods_Interface::get_enabled_storage_objects_and_ids(array_keys($this->storage_service_with_partial_settings));
foreach ($this->storage_service_with_partial_settings as $method => $method_name) {
if (empty($enabled_services[$method]['object']) || empty($enabled_services[$method]['instance_settings']) || !$enabled_services[$method]['object']->supports_feature('manual_authentication')) {
$this->show_admin_warning(sprintf(__('The following remote storage (%s) have only been partially configured, manual authorization is not supported with this remote storage, please try again and if the problem persists contact support.', 'updraftplus'), $method), 'error');
} else {
$this->show_admin_warning($enabled_services[$method]['object']->get_manual_authorisation_template(), 'error');
}
}
} else {
$this->show_admin_warning('UpdraftPlus: '.sprintf(__('The following remote storage (%s) have only been partially configured, if you are having problems you can try to manually authorise at the UpdraftPlus settings page.', 'updraftplus'), implode(', ', $this->storage_service_with_partial_settings)).' '.__('Return to UpdraftPlus configuration', 'updraftplus').'', 'error');
}
}
/**
* Show remote storage settings are empty warning
*/
public function show_admin_warning_if_remote_storage_setting_are_empty() {
if ((isset($_REQUEST['page']) && 'updraftplus' == $_REQUEST['page']) || (defined('DOING_AJAX') && DOING_AJAX)) {
$this->show_admin_warning(sprintf(__('You have requested saving to remote storage (%s), but without entering any settings for that storage.', 'updraftplus'), implode(', ', $this->storage_service_without_settings)), 'error');
} else {
$this->show_admin_warning('UpdraftPlus: '.sprintf(__('You have requested saving to remote storage (%s), but without entering any settings for that storage.', 'updraftplus'), implode(', ', $this->storage_service_without_settings)).' '.__('Return to UpdraftPlus configuration', 'updraftplus').'', 'error');
}
}
/**
* Show remote storage warning when one or more cloud storage options are selected but the add-ons are not installed
*/
public function show_admin_warning_if_remote_storage_without_addons() {
global $updraftplus;
$storage_service_without_addons = implode(', ', $this->storage_service_without_addons_settings);
$notice_label1 = sprintf(__('You have selected storage options which are not part of your version of %s.', 'updraftplus'), 'UpdraftPlus');
$notice_label2 = sprintf(__('To backup to %s, please upgrade to %s.', 'updraftplus'), $storage_service_without_addons, 'UpdraftPlus Premium');
$notice_label3 = sprintf(__('Where are my %s backups stored?', 'updraftplus'), 'UpdraftPlus');
$notice_label4 = ''.sprintf(__('Return to %s configuration', 'updraftplus'), 'UpdraftPlus').'';
$notice_label5 = sprintf(__('To see which remote storage locations are included in free and premium, please see here: %s', 'updraftplus'), ''.$notice_label3.'');
if ((isset($_REQUEST['page']) && 'updraftplus' == $_REQUEST['page']) || (defined('DOING_AJAX') && DOING_AJAX)) {
$this->show_admin_warning($notice_label1.' '.$notice_label2.' '.$notice_label5, 'error');
} else {
$this->show_admin_warning('UpdraftPlus: '.$notice_label1.' '.$notice_label2.' '.$notice_label5.' '.$notice_label4, 'error');
}
}
/**
* Receive Heartbeat data and respond.
*
* Processes data received via a Heartbeat request, and returns additional data to pass back to the front end.
*
* @param array $response - Heartbeat response data to pass back to front end.
* @param array $data - Data received from the front end (unslashed).
*/
public function process_status_in_heartbeat($response, $data) {
if (!UpdraftPlus_Options::user_can_manage() || !is_array($response) || empty($data['updraftplus'])) return $response;
try {
$response['updraftplus'] = $this->get_activejobs_list(UpdraftPlus_Manipulation_Functions::wp_unslash($data['updraftplus']));
} catch (Exception $e) {
$log_message = 'PHP Fatal Exception error ('.get_class($e).') has occurred during get active job list. Error Message: '.$e->getMessage().' (Code: '.$e->getCode().', line '.$e->getLine().' in '.$e->getFile().')';
error_log($log_message);
$response['updraftplus'] = array(
'fatal_error' => true,
'fatal_error_message' => $log_message
);
// @codingStandardsIgnoreLine
} catch (Error $e) {
$log_message = 'PHP Fatal error ('.get_class($e).') has occurred during get active job list. Error Message: '.$e->getMessage().' (Code: '.$e->getCode().', line '.$e->getLine().' in '.$e->getFile().')';
error_log($log_message);
$response['updraftplus'] = array(
'fatal_error' => true,
'fatal_error_message' => $log_message
);
}
if (isset($data['updraftplus']['updraft_credentialtest_nonce'])) {
if (!wp_verify_nonce($data['updraftplus']['updraft_credentialtest_nonce'], 'updraftplus-credentialtest-nonce')) {
$response['updraftplus']['updraft_credentialtest_nonce'] = wp_create_nonce('updraftplus-credentialtest-nonce');
}
}
$response['updraftplus']['time_now'] = get_date_from_gmt(gmdate('Y-m-d H:i:s'), 'D, F j, Y H:i');
return $response;
}
/**
* Show warning about restriction implied by the hosting company (can only perform a full backup once per month, incremental backup should not go above one per day)
*/
public function show_admin_warning_one_backup_per_month() {
global $updraftplus;
$hosting_company = $updraftplus->get_hosting_info();
$txt1 = sprintf(__('Your website is hosted with %s (%s).', 'updraftplus'), $hosting_company['name'], $hosting_company['website']);
$txt2 = sprintf(__('%s permits UpdraftPlus to perform only one backup per month.', 'updraftplus'), $hosting_company['name']).' '.__('Thus, we recommend you choose a full backup when performing a manual backup and to use that option when creating a scheduled backup.', 'updraftplus');
$txt3 = __('Due to the restriction, some settings can be automatically adjusted, disabled or not available.', 'updraftplus');
$this->show_plugin_page_admin_warning(''.__('Warning', 'updraftplus').': '.$txt1.' '.$txt2.' '.$txt3, 'update-nag notice notice-warning', true);
}
/**
* Find out if the current request is a backup download request, and proceed with the download if it is
*/
public function maybe_download_backup_from_email() {
global $pagenow;
if (UpdraftPlus_Options::user_can_manage() && (!defined('DOING_AJAX') || !DOING_AJAX) && UpdraftPlus_Options::admin_page() === $pagenow && isset($_REQUEST['page']) && 'updraftplus' === $_REQUEST['page'] && isset($_REQUEST['action']) && 'updraft_download_backup' === $_REQUEST['action']) {
$findexes = empty($_REQUEST['findex']) ? array(0) : $_REQUEST['findex'];
$timestamp = empty($_REQUEST['timestamp']) ? '' : $_REQUEST['timestamp'];
$nonce = empty($_REQUEST['nonce']) ? '' : $_REQUEST['nonce'];
$type = empty($_REQUEST['type']) ? '' : $_REQUEST['type'];
if (empty($timestamp) || empty($nonce) || empty($type)) wp_die(esc_html__('The download link is broken, you may have clicked the link from untrusted source', 'updraftplus'), '', array('back_link' => true));
$backup_history = UpdraftPlus_Backup_History::get_history();
if (!isset($backup_history[$timestamp]['nonce']) || $backup_history[$timestamp]['nonce'] !== $nonce) wp_die(esc_html__("The download link is broken or the backup file is no longer available", 'updraftplus'), '', array('back_link' => true));
$this->do_updraft_download_backup($findexes, $type, $timestamp, 2, false, '');
exit; // we don't need anything else but an exit
}
}
/**
* Print the phpseclib-notice-related scripts
*/
public function print_phpseclib_notice_scripts() {
static $printed = false;
if ($printed) return;
$printed = true;
?>
> An associative array where the keys are HTML tag names
* and the values are arrays of allowed attributes for
* each tag. The attribute arrays use attribute names
* as keys and `true` as the value to indicate the attribute
* is allowed.
*/
public function kses_allow_tags() {
return array(
'div' => array(
'id' => true,
'class' => true,
'style' => true,
),
'h3' => array(
'style' => true,
),
'input' => array(
'id' => true,
'class' => true,
'type' => true,
'name' => true,
'value' => true,
'checked' => true,
'style' => true,
'min' => true,
'step' => true,
),
'select' => array(
'id' => true,
'class' => true,
'name' => true,
'value' => true,
'style' => true,
),
'option' => array(
'value' => true,
'selected' => true,
),
'label' => array(
'id' => true,
'class' => true,
'style' => true,
'for' => true,
),
'br' => array(),
'em' => array(),
'a' => array(
'id' => true,
'class' => true,
'style' => true,
'href' => true,
'target' => true,
'onclick' => true,
'data-type' => true,
'data-incremental' => true,
),
'span' => array(
'id' => true,
'class' => true,
'style' => true,
),
'img' => array(
'id' => true,
'class' => true,
'style' => true,
'src' => true,
'width' => true,
'height' => true,
'alt' => true,
),
'p' => array(
'id' => true,
'class' => true,
'style' => true,
),
);
}
/**
* Print the unfinished restoration dialog scripts
*/
public function print_unfinished_restoration_dialog_scripts() {
?>