* Get HTML suitable for the admin area for the status of the last backup
public function last_backup_html() {
$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 = "<span style=\"color:".(($updraft_last_backup['success']) ? 'green' : 'black').";\">".$print_time.'</span>';
$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 = "<span style=\"color:".(($updraft_last_backup['success']) ? 'green' : 'black').";\">$inc_time</span> (".sprintf(__('incremental backup; base backup: %s', 'updraftplus'), $print_time).')';
$last_backup_text .= '<br>';
// 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) ? "<span style=\"color:orange;\">" : "<span style=\"color:red;\">";
if ('warning' == $level) {
$message = sprintf(__("Warning: %s", 'updraftplus'), make_clickable(htmlspecialchars($message)));
$message = htmlspecialchars($message);
$last_backup_text .= $message;
$last_backup_text .= '</span><br>';
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 .= "<a href=\"?page=updraftplus&action=downloadlog&updraftplus_backup_nonce=".$updraft_last_backup['backup_nonce']."\" class=\"updraft-log-link\" onclick=\"event.preventDefault(); updraft_popuplog('".$updraft_last_backup['backup_nonce']."');\">".__('Download log file', 'updraftplus')."</a>";
$last_backup_text = "<span style=\"color:blue;\">".__('No backup has been completed', 'updraftplus')."</span>";
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') {
if ($updraftplus->is_restricted_hosting('only_one_backup_per_month')) {
'manual' => _x('Manual', 'i.e. Non-automatic', 'updraftplus'),
'monthly' => __('Monthly', 'updraftplus')
'manual' => _x('Manual', 'i.e. Non-automatic', 'updraftplus'),
'everyhour' => __('Every hour', 'updraftplus'),
'every2hours' => sprintf(__('Every %s hours', 'updraftplus'), '2'),
'every4hours' => sprintf(__('Every %s hours', 'updraftplus'), '4'),
'every8hours' => sprintf(__('Every %s hours', 'updraftplus'), '8'),
'twicedaily' => sprintf(__('Every %s hours', 'updraftplus'), '12'),
'daily' => __('Daily', 'updraftplus'),
'weekly' => __('Weekly', 'updraftplus'),
'fortnightly' => __('Fortnightly', 'updraftplus'),
'monthly' => __('Monthly', 'updraftplus'),
if ('files' == $what_for) unset($intervals['everyhour']);
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 = '<span style="color:green;">'.__('Backup directory specified is writable, which is good.', 'updraftplus').'</span>';
$dir_info = '<span style="color:red;">';
if (!is_dir($updraft_dir)) {
$dir_info .= __('Backup directory specified does <b>not</b> exist.', 'updraftplus');
$dir_info .= __('Backup directory specified exists, but is <b>not</b> writable.', 'updraftplus');
$dir_info .= '<span class="updraft-directory-not-writable-blurb"><span class="directory-permissions"><a class="updraft_create_backup_dir" href="'.UpdraftPlus_Options::admin_page_url().'?page=updraftplus&action=updraft_create_backup_dir&nonce='.wp_create_nonce('create_backup_dir').'">'.__('Follow this link to attempt to create the directory and set the permissions', 'updraftplus').'</a></span>, '.__('or, to reset this option', 'updraftplus').' <a href="'.UpdraftPlus::get_current_clean_url().'" class="updraft_backup_dir_reset">'.__('press here', 'updraftplus').'</a>. '.__('If that is unsuccessful check the permissions on your server or change it to another directory that is writable by your web server process.', 'updraftplus').'</span>';
* 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(
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
if (!$really_is_writable) echo "jQuery('.backupdirrow').show();\n";
if (!empty($active_service)) {
if (is_array($active_service)) {
foreach ($active_service as $serv) {
echo "jQuery('.".esc_js($serv)."').show();\n";
echo "jQuery('.".esc_js($active_service)."').show();\n";
echo "jQuery('.none').show();\n";
foreach ($updraftplus->backup_methods as $method => $description) {
// already done: require_once(UPDRAFTPLUS_DIR.'/methods/'.$method.'.php');
$call_method = "UpdraftPlus_BackupModule_$method";
if (method_exists($call_method, 'config_print_javascript_onready')) {
$method_objects[$method]->config_print_javascript_onready();
$ret = ob_get_contents();
* Return the HTML for the files selector widget
* @param String $prefix Prefix for the ID
* @param Boolean $show_exclusion_options True or False for exclusion options
* @param Boolean|String $include_more $include_more can be (bool) or (string)"sometimes"
public function files_selector_widgetry($prefix = '', $show_exclusion_options = true, $include_more = true) {
$for_updraftcentral = defined('UPDRAFTCENTRAL_COMMAND') && UPDRAFTCENTRAL_COMMAND;
$backupable_entities = $updraftplus->get_backupable_file_entities(true, true);
// 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 .= '<label '.(('others' == $key) ? 'title="'.sprintf(__('Your wp-content directory server path: %s', 'updraftplus'), WP_CONTENT_DIR).'" ' : '').' for="'.$prefix.'updraft_include_'.$key.'" class="updraft_checkbox"><input class="updraft_include_entity" id="'.$prefix.'updraft_include_'.$key.'" '.$data_toggle_exclude_field.' type="checkbox" name="updraft_include_'.$key.'" value="1" '.$included.'> '.(('others' == $key) ? __('Any other directories found inside wp-content', 'updraftplus') : htmlspecialchars($info['description'])).'</label>';
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 .= "<div id=\"".$exclude_container_class."\" $display class=\"updraft_exclude_container\">";
$ret .= '<label class="updraft-exclude-label" for="'.$prefix.'updraft_include_'.$key.'_exclude">'.__('Exclude these from', 'updraftplus').' '.htmlspecialchars($info['description']).':</label> <span class="updraft-fs-italic">'.__('(the asterisk character matches zero or more characters)', 'updraftplus').'</span>';
$exclude_input_type = $for_updraftcentral ? "text" : "hidden";
$exclude_input_extra_attr = $for_updraftcentral ? 'title="'.__('If entering multiple files/directories, then separate them with commas. For entities at the top level, you can use a * at the start or end of the entry as a wildcard.', 'updraftplus').'" size="54"' : '';
$ret .= '<input type="'.$exclude_input_type.'" id="'.$prefix.'updraft_include_'.$key.'_exclude" name="updraft_include_'.$key.'_exclude" '.$exclude_input_extra_attr.' value="'.htmlspecialchars($include_exclude).'" />';
if (!$for_updraftcentral) {
$backupable_file_entities = $updraftplus->get_backupable_file_entities();
$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(
'include_exclude' => $include_exclude,
'show_exclusion_options' => $show_exclusion_options,
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.'"' : '';
$ret .= "<label for=\"".$prefix."updraft_include_$key\"".((isset($info['htmltitle'])) ? ' title="'.htmlspecialchars($info['htmltitle']).'"' : '')." class=\"updraft_checkbox\"><input class=\"updraft_include_entity\" $data_toggle_exclude_field id=\"".$prefix."updraft_include_$key\" type=\"checkbox\" name=\"updraft_include_$key\" value=\"1\" $included /> ".htmlspecialchars($info['description']);
$ret .= apply_filters("updraftplus_config_option_include_$key", '', $prefix, $for_updraftcentral);
* 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 = "<div class=\"error updraftplusmethod $extraclass\"><p>$text</p></div>";
$ret .= "<div class=\"notice error below-h2\"><p>$text</p></div>";
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) {
if (!function_exists("curl_init") || !function_exists('curl_exec')) {
$ret .= $this->show_double_warning('<strong>'.__('Warning', 'updraftplus').':</strong> '.sprintf(__("Your web server's PHP installation does not included a <strong>required</strong> (for %s) module (%s). Please contact your web hosting provider's support and ask for them to enable it.", 'updraftplus'), $service, 'Curl').' ', $extraclass, false);
$curl_version = curl_version();
$curl_ssl_supported= ($curl_version['features'] & CURL_VERSION_SSL);
if (!$curl_ssl_supported) {
$ret .= '<p><strong>'.__('Warning', 'updraftplus').':</strong> '.sprintf(__("Your web server's PHP/Curl installation does not support https access. Communications with %s will be unencrypted. Ask your web host to install Curl/SSL in order to gain the ability for encryption (via an add-on).", 'updraftplus'), $service).'</p>';
$ret .= $this->show_double_warning('<p><strong>'.__('Warning', 'updraftplus').':</strong> '.sprintf(__("Your web server's PHP/Curl installation does not support https access. We cannot access %s without this support. Please contact your web hosting provider's support. %s <strong>requires</strong> Curl+https. Please do not file any support requests; there is no alternative.", 'updraftplus'), $service, $service).'</p>', $extraclass, false);
$ret .= '<p><em>'.sprintf(__("Good news: Your site's communications with %s can be encrypted. If you see any errors to do with encryption, then look in the 'Expert Settings' for more help.", 'updraftplus'), $service).'</em></p>';
if ($echo_instead_of_return) {
* 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) {
$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 = "<h2 title=\"$key\">$pretty_date</h2>";
if (!empty($backup['label'])) $rawbackup .= '<span class="raw-backup-info">'.$backup['label'].'</span>';
if (null === $job_data) $job_data = empty($nonce) ? array() : $updraftplus->jobdata_getarray($nonce);
if (!$only_remote_sent) {
$rawbackup .= '<input type="checkbox" name="always_keep_this_backup" id="always_keep_this_backup" data-backup_key="'.$key.'" '.(empty($backup['always_keep']) ? '' : 'checked ').'><label for="always_keep_this_backup">'.__('Only allow this backup to be deleted manually (i.e. keep it even if retention limits are hit).', 'updraftplus').'</label>';
$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);
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 .= '<strong>'.__('Uploaded to:', 'updraftplus').'</strong> ';
foreach ($services as $serv) {
if ('none' == $serv || '' == $serv) {
} elseif (isset($updraftplus->backup_methods[$serv])) {
$show_services .= $show_services ? ', '.$updraftplus->backup_methods[$serv] : $updraftplus->backup_methods[$serv];
$show_services .= $show_services ? ', '.$serv : $serv;
if ('' == $show_services && $add_none) $show_services .= __('None', 'updraftplus');
$rawbackup .= $show_services;
if (false !== $total_size) {
$rawbackup .= '</p><strong>'.__('Total backup size:', 'updraftplus').'</strong> '.UpdraftPlus_Manipulation_Functions::convert_numeric_size_to_text($total_size).'<p>';
$rawbackup .= '</p><hr><p><pre>'.print_r($backup, true).'</pre></p>';
if (!empty($job_data) && is_array($job_data)) {
$rawbackup .= '<p><pre>'.htmlspecialchars(print_r($job_data, true)).'</pre></p>';
return esc_attr($rawbackup);
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'];
$desc_source = __('unknown source', 'updraftplus');
$dbt = empty($backup['meta_foreign']) ? esc_attr(__('Database', 'updraftplus')) : esc_attr(sprintf(__('Database (created by %s)', 'updraftplus'), $desc_source));
$dbt = __('External database', 'updraftplus').' ('.substr($bkey, 2).')';
$ret .= $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 vaules within $backup
* @param String $entities Entities to be added
* @param String $esc_pretty_date Whether the button needs to escape the pretty date format
* @return String - the resulting HTML
public function download_buttons($backup, $key, $accept, &$entities, $esc_pretty_date) {
$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;
if (empty($backup['meta_foreign'])) {
$sdescrip = preg_replace('/ \(.*\)$/', '', $info['description']);
if (strlen($sdescrip) > 20 && isset($info['shortdescription'])) $sdescrip = $info['shortdescription'];
$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']).' ';
$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]);
$whatfiles = $backup[$type];
foreach ($whatfiles as $findex => $bfile) {
$set_contents .= ('' == $set_contents) ? $findex : ",$findex";
if ($findex != $expected_index) $index_missing = true;
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, '%UP_backups_total_file_size%');
if ($index_missing) $ide .= ' '.__('You appear to be 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/';
$ret .= $this->download_button($type, $key, 0, null, $ide, $sdescrip, $esc_pretty_date, $set_contents);
$ret = str_replace('%UP_backups_total_file_size%', UpdraftPlus_Manipulation_Functions::convert_numeric_size_to_text($total_file_size), $ret);
public function date_label($pretty_date, $key, $backup, $jobdata, $nonce, $simple_format = false) {
$pretty_date = $simple_format ? $pretty_date : '<div class="clear-right">'.$pretty_date.'</div>';
$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'])) {
$ret .= ' '.__('(Not finished)', 'updraftplus');
$ret .= apply_filters('updraftplus_msg_unfinishedbackup', "<br><span title=\"".esc_attr(__('If you are seeing more backups than you expect, then it is probably because the deletion of old backup sets does not happen until a fresh backup completes.', 'updraftplus'))."\">".__('(Not finished)', 'updraftplus').'</span>', $jobdata, $nonce);