PluginProbe ʕ •ᴥ•ʔ
UpdraftPlus: WP Backup & Migration Plugin / 1.26.4
UpdraftPlus: WP Backup & Migration Plugin v1.26.4
1.26.4 1.26.3 1.9.19 1.9.25 1.9.26 1.9.30 1.9.31 1.9.32 1.9.4 1.9.40 1.9.41 1.9.42 1.9.43 1.9.44 1.9.45 1.9.46 1.9.5 1.9.50 1.9.51 1.9.60 1.9.62 1.9.63 1.9.64 1.11.12 1.4.8 1.11.15 1.4.9 1.11.17 1.5.16 1.11.18 1.5.20 1.11.2 1.5.21 1.11.20 1.5.22 1.11.23 1.5.5 1.11.24 1.5.6 1.11.25 1.5.7 1.11.26 1.5.8 1.11.27 1.5.9 1.11.28 1.6.1 1.11.3 1.6.17 1.11.4 1.6.2 1.11.5 1.6.46 1.11.8 1.7.0 1.11.9 1.7.1 1.12.0 1.7.18 1.12.1 1.7.20 1.12.12 1.7.3 1.12.13 1.7.34 1.12.15 1.7.35 1.12.17 1.7.39 1.12.2 1.7.40 1.12.20 1.7.41 1.12.23 1.8.1 1.12.24 1.8.11 1.12.25 1.8.12 1.12.28 1.8.13 1.12.29 1.8.2 1.12.30 1.8.5 1.12.32 1.8.8 1.12.34 1.9.0 1.12.35 1.9.13 1.12.37 1.9.15 1.12.39 1.9.17 1.12.4 1.12.40 1.12.6 1.13.1 1.13.11 1.13.12 1.13.15 1.13.16 1.13.2 1.13.3 1.13.4 1.13.5 1.13.6 1.13.7 1.13.8 1.13.9 1.14.10 1.14.11 1.14.12 1.14.13 1.14.2 1.14.3 1.14.4 1.14.5 1.14.7 1.14.9 1.15.0 1.15.2 1.15.3 1.15.5 1.15.6 1.15.7 1.16.0 1.16.10 1.16.11 1.16.12 1.16.13 1.16.14 1.16.15 1.16.16 1.16.17 1.16.20 1.16.21 1.16.22 1.16.23 1.16.24 1.16.25 1.16.26 1.16.28 1.16.29 1.16.32 1.16.34 1.16.35 1.16.36 1.16.37 1.16.4 1.16.40 1.16.41 1.16.42 1.16.43 1.16.44 1.16.45 1.16.46 1.16.47 1.16.48 1.16.49 1.16.5 1.16.50 1.16.51 1.16.53 1.16.55 1.16.56 1.16.59 1.16.6 1.16.60 1.16.61 1.16.62 1.16.63 1.16.64 1.16.65 1.16.66 1.16.67 1.16.68 1.16.69 1.16.7 1.16.8 1.16.9 1.2.0 1.2.1 1.2.10 1.2.11 1.2.12 1.2.14 1.2.15 1.2.16 1.2.17 1.2.19 1.2.2 1.2.20 1.2.24 1.2.25 1.2.26 1.2.27 1.2.28 1.2.29 1.2.3 1.2.30 1.2.31 1.2.33 1.2.35 1.2.36 1.2.38 1.2.39 1.2.4 1.2.40 1.2.41 1.2.42 1.2.43 1.2.44 1.2.45 1.2.46 1.2.5 1.2.7 1.2.8 1.2.9 1.22.1 1.22.10 1.22.11 1.22.12 1.22.14 1.22.15 1.22.16 1.22.17 1.22.18 1.22.19 1.22.20 1.22.21 1.22.22 1.22.23 1.22.24 1.22.3 1.22.4 1.22.5 1.22.6 1.22.7 1.22.8 1.22.9 1.23.1 1.23.10 1.23.11 1.23.12 1.23.13 1.23.15 1.23.16 1.23.2 1.23.3 1.23.4 1.23.5 1.23.6 1.23.7 1.23.8 1.23.9 1.24.1 1.24.10 1.24.11 1.24.12 1.24.2 trunk 1.24.3 0.7.4 1.24.4 0.7.7 1.24.5 0.8.28 1.24.6 0.8.29 1.24.7 0.8.30 1.24.8 0.8.31 1.24.9 0.8.32 1.25.1 0.8.33 1.25.2 0.8.36 1.25.3 0.8.37 1.25.5 0.8.50 1.25.6 0.8.51 1.25.7 0.9.1 1.25.8 0.9.10 1.25.9 0.9.11 1.26.1 0.9.12 1.26.2 0.9.2 1.3.10 0.9.20 1.3.12 0.9.21 1.3.14 0.9.22 1.3.15 1.0.10 1.3.17 1.0.11 1.3.18 1.0.12 1.3.19 1.0.15 1.3.2 1.0.16 1.3.20 1.0.18 1.3.22 1.0.20 1.3.23 1.0.3 1.3.24 1.0.4 1.3.25 1.0.5 1.3.3 1.0.6 1.3.4 1.0.7 1.3.6 1.0.8 1.3.7 1.0.9 1.3.8 1.1.0 1.3.9 1.1.10 1.4.0 1.1.11 1.4.10 1.1.12 1.4.11 1.1.13 1.4.12 1.1.14 1.4.13 1.1.15 1.4.14 1.1.16 1.4.15 1.1.17 1.4.2 1.1.2 1.4.27 1.1.3 1.4.28 1.1.5 1.4.29 1.1.6 1.4.30 1.1.8 1.4.4 1.1.9 1.4.48 1.10.1 1.4.5 1.10.3 1.4.6 1.11.1 1.4.7
updraftplus / includes / class-wpadmin-commands.php
updraftplus / includes Last commit date
Dropbox2 3 weeks ago Google 3 weeks ago blockui 3 weeks ago checkout-embed 3 weeks ago cloudfiles 3 weeks ago handlebars 1 month ago images 9 years ago jquery-ui.dialog.extended 3 weeks ago jquery.serializeJSON 5 years ago jstree 1 year ago labelauty 3 weeks ago pcloud 3 weeks ago select2 1 year ago tether 6 years ago tether-shepherd 7 years ago updraftclone 3 weeks ago S3.php 3 weeks ago S3compat.php 3 weeks ago cacert.pem 2 years ago class-backup-history.php 1 month ago class-commands.php 3 weeks ago class-database-utility.php 1 month ago class-filesystem-functions.php 1 month ago class-http-error-descriptions.php 2 years ago class-job-scheduler.php 3 years ago class-manipulation-functions.php 1 month ago class-partialfileservlet.php 3 weeks ago class-remote-send.php 3 weeks ago class-search-replace.php 1 month ago class-semaphore.php 3 weeks ago class-storage-methods-interface.php 1 month ago class-updraft-dashboard-news.php 1 month ago class-updraft-semaphore.php 4 years ago class-updraftcentral-updraftplus-commands.php 3 years ago class-updraftplus-deactivation.php 1 month ago class-updraftplus-encryption.php 1 month ago class-wpadmin-commands.php 1 month ago class-zip.php 1 month ago ftp.class.php 2 months ago get-cpanel-quota-usage.pl 12 years ago google-extensions.php 1 month ago jquery-ui.custom-v1.11.4-1-26-4.min.css 3 weeks ago jquery-ui.custom-v1.11.4-1-26-4.min.css.map 3 weeks ago jquery-ui.custom-v1.11.4.css 3 years ago jquery-ui.custom-v1.12.1-1-26-4.min.css 3 weeks ago jquery-ui.custom-v1.12.1-1-26-4.min.css.map 3 weeks ago jquery-ui.custom-v1.12.1.css 3 years ago migrator-lite.php 1 month ago updraft-admin-common-1-26-4.min.js 3 weeks ago updraft-admin-common.js 3 weeks ago updraft-restorer-skin-compatibility.php 6 years ago updraft-restorer-skin.php 3 years ago updraftcentral.php 1 year ago updraftplus-clone.php 1 year ago updraftplus-login.php 7 months ago updraftplus-notices.php 1 month ago updraftplus-tour.php 1 month ago updraftvault.php 3 years ago
class-wpadmin-commands.php
979 lines
1 <?php
2 //phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery -- we try to reduce overhead by bypassing WP APIs and other extra layers; Some custom complex queries tailored specifically to our needs, giving us full control over the SQL commands and data manipulation
3 // phpcs:disable WordPress.PHP.DevelopmentFunctions.error_log_print_r -- print_r is intentionally used to convert an array into a readable string or for controlled logging purposes.
4 // phpcs:disable WordPress.DB.DirectDatabaseQuery.NoCaching -- some query operations need to always receive the most up-to-date or actual data directly from the database, reducing the risk of serving stale information.
5 // phpcs:disable WordPress.PHP.DevelopmentFunctions.error_log_set_error_handler -- we use the set_error_handler() function to provide a flexible way of handling PHP errors according to our needs; we centralises error handling in one place and customises certain errors based on their severity and context.
6 // phpcs:disable Squiz.PHP.DiscouragedFunctions.Discouraged -- some functions, like set_time_limit() and ini_set(), are used to temporarily change PHP configuration values based on the script's needs (e.g., processing large datasets or performing long operations).
7 if (!defined('UPDRAFTPLUS_DIR')) die('No access.');
8
9 /*
10 See class-commands.php for explanation about how these classes work.
11 */
12
13 if (!class_exists('UpdraftPlus_Commands')) updraft_try_include_file('includes/class-commands.php', 'require_once');
14
15 /**
16 * An extension, because commands available via wp-admin are a super-set of those which are available through all mechanisms
17 */
18 class UpdraftPlus_WPAdmin_Commands extends UpdraftPlus_Commands {
19
20 private $_uc_helper;
21
22 private $_updraftplus_admin;
23
24 private $_updraftplus;
25
26 /**
27 * Constructor
28 *
29 * @param string $uc_helper The 'helper' needs to provide the method _updraftplus_background_operation_started
30 */
31 public function __construct($uc_helper) {
32 $this->_uc_helper = $uc_helper;
33 global $updraftplus_admin, $updraftplus;
34 $this->_updraftplus_admin = $updraftplus_admin;
35 $this->_updraftplus = $updraftplus;
36 parent::__construct($uc_helper);
37 }
38
39 /**
40 * Forces a resumption of a backup where the resumption is overdue (so apparently cron is not working)
41 *
42 * @param Array $info - keys 'job_id' and 'resumption'
43 *
44 * @return Array - if there is an error. Otherwise, dies.
45 */
46 public function forcescheduledresumption($info) {
47
48 // Casting $resumption to int is absolutely necessary, as the WP cron system uses a hashed serialisation of the parameters for identifying jobs. Different type => different hash => does not match
49 $resumption = (int) $info['resumption'];
50 $job_id = $info['job_id'];
51 $get_cron = $this->_updraftplus_admin->get_cron($job_id);
52 if (!is_array($get_cron)) {
53 return array('r' => false);
54 } else {
55 $this->_updraftplus->log("Forcing resumption: job id=$job_id, resumption=$resumption");
56 wp_clear_scheduled_hook('updraft_backup_resume', array($resumption, $job_id));
57 $this->_updraftplus->close_browser_connection(json_encode(array('r' => true)));
58 $this->_updraftplus->jobdata_set_from_array($get_cron[1]);
59 $this->_updraftplus->backup_resume($resumption, $job_id);
60 // We don't want to return. The close_browser_connection call already returned a result.
61 die;
62 }
63 }
64
65 /**
66 * Calls a WordPress action and dies
67 *
68 * @param Array $data - must have at least the key 'wpaction' with a string value
69 *
70 * @return WP_Error if no command was included
71 */
72 public function call_wordpress_action($data) {
73
74 if (empty($data['wpaction'])) return new WP_Error('error', '', 'no command sent');
75
76 $response = $this->_updraftplus_admin->call_wp_action($data, array($this->_uc_helper, '_updraftplus_background_operation_started'));// phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- Unused variable is for future use.
77
78 die;
79
80 // return array('response' => $response['response'], 'status' => $response['status'], 'log' => $response['log'] );
81 }
82
83 public function updraftcentral_delete_key($params) {
84 global $updraftcentral_main;
85 if (!is_a($updraftcentral_main, 'UpdraftCentral_Main')) {
86 return array('error' => 'UpdraftCentral_Main object not found');
87 }
88
89 return $updraftcentral_main->delete_key($params['key_id']);
90 }
91
92 public function updraftcentral_get_log($params) {
93 global $updraftcentral_main;
94 if (!is_a($updraftcentral_main, 'UpdraftCentral_Main')) {
95 return array('error' => 'UpdraftCentral_Main object not found');
96 }
97 return call_user_func(array($updraftcentral_main, 'get_log'), $params);
98 }
99
100 public function updraftcentral_create_key($params) {
101 global $updraftcentral_main;
102 if (!is_a($updraftcentral_main, 'UpdraftCentral_Main')) {
103 return array('error' => 'UpdraftCentral_Main object not found');
104 }
105 return call_user_func(array($updraftcentral_main, 'create_key'), $params);
106 }
107
108 public function restore_alldownloaded($params) {
109
110 $backups = UpdraftPlus_Backup_History::get_history();
111 $updraft_dir = $this->_updraftplus->backups_dir_location();
112
113 $timestamp = (int) $params['timestamp'];
114 if (!isset($backups[$timestamp])) {
115 return array('m' => '', 'w' => '', 'e' => __('No such backup set exists', 'updraftplus'));
116 }
117
118 $mess = array();
119 parse_str(stripslashes($params['restoreopts']), $res);
120
121 if (isset($res['updraft_restore'])) {
122
123 $error_levels = version_compare(PHP_VERSION, '8.4.0', '>=') ? E_ALL : E_ALL & ~E_STRICT;
124 set_error_handler(array($this->_updraftplus_admin, 'get_php_errors'), $error_levels);
125
126 $elements = array_flip($res['updraft_restore']);
127
128 $warn = array();
129 $err = array();
130
131 if (function_exists('set_time_limit')) @set_time_limit(UPDRAFTPLUS_SET_TIME_LIMIT);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise because of the function.
132 $max_execution_time = (int) @ini_get('max_execution_time');// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise because of the function.
133
134 if ($max_execution_time>0 && $max_execution_time<61) {
135 /* translators: %s: Maximum execution time in seconds */
136 $warn[] = sprintf(__('The PHP setup on this webserver allows only %s seconds for PHP to run, and does not allow this limit to be raised.', 'updraftplus'), $max_execution_time).' '.
137 __('If you have a lot of data to import, and if the restore operation times out, then you will need to ask your web hosting company for ways to raise this limit (or attempt the restoration piece-by-piece).', 'updraftplus');
138 }
139
140 if (isset($backups[$timestamp]['native']) && false == $backups[$timestamp]['native']) {
141 $warn[] = __('This backup set was not known by UpdraftPlus to be created by the current WordPress installation, but was either found in remote storage, or was sent from a remote site.', 'updraftplus').' '.__('You should make sure that this really is a backup set intended for use on this website, before you restore (rather than a backup set of an unrelated website).', 'updraftplus');
142 }
143
144 if (isset($elements['db'])) {
145
146 // Analyse the header of the database file + display results
147 list ($mess2, $warn2, $err2, $info) = $this->_updraftplus->analyse_db_file($timestamp, $res);
148 $mess = array_merge($mess, $mess2);
149 $warn = array_merge($warn, $warn2);
150 $err = array_merge($err, $err2);
151 foreach ($backups[$timestamp] as $bid => $bval) {
152 if ('db' != $bid && 'db' == substr($bid, 0, 2) && '-size' != substr($bid, -5, 5)) {
153 $warn[] = __('Only the WordPress database can be restored; you will need to deal with the external database manually.', 'updraftplus');
154 break;
155 }
156 }
157 }
158
159 $backupable_entities = $this->_updraftplus->get_backupable_file_entities(true, true);
160 $backupable_plus_db = $backupable_entities;
161 $backupable_plus_db['db'] = array('path' => 'path-unused', 'description' => __('Database', 'updraftplus'));
162
163 if (!empty($backups[$timestamp]['meta_foreign'])) {
164 $foreign_known = apply_filters('updraftplus_accept_archivename', array());
165 if (!is_array($foreign_known) || empty($foreign_known[$backups[$timestamp]['meta_foreign']])) {
166 /* translators: %s: Backup source information */
167 $err[] = sprintf(__('Backup created by unknown source (%s) - cannot be restored.', 'updraftplus'), $backups[$timestamp]['meta_foreign']);
168 } else {
169 // For some reason, on PHP 5.5 passing by reference in a single array stopped working with apply_filters_ref_array (though not with do_action_ref_array).
170 $backupable_plus_db = apply_filters_ref_array("updraftplus_importforeign_backupable_plus_db", array($backupable_plus_db, array($foreign_known[$backups[$timestamp]['meta_foreign']], &$mess, &$warn, &$err)));
171 }
172 }
173
174 foreach ($backupable_plus_db as $type => $entity_info) {
175 if (!isset($elements[$type]) || (isset($entity_info['restorable']) && !$entity_info['restorable'])) continue;
176 $whatwegot = $backups[$timestamp][$type];
177 if (is_string($whatwegot)) $whatwegot = array($whatwegot);
178 $expected_index = 0;
179 $missing = '';
180 ksort($whatwegot);
181 $outof = false;
182 foreach ($whatwegot as $index => $file) {
183 if (preg_match('/\d+of(\d+)\.zip/', $file, $omatch)) {
184 $outof = max($omatch[1], 1);
185 }
186 while ($expected_index < $index) {
187 $missing .= ('' == $missing) ? (1+$expected_index) : ",".(1+$expected_index);
188 $expected_index++;
189 }
190 if (!file_exists($updraft_dir.'/'.$file)) {
191 /* translators: %s: File path */
192 $err[] = sprintf(__('File not found (you need to upload it): %s', 'updraftplus'), $updraft_dir.'/'.$file);
193 } elseif (filesize($updraft_dir.'/'.$file) == 0) {
194 /* translators: %s: File name */
195 $err[] = sprintf(__('File was found, but is zero-sized (you need to re-upload it): %s', 'updraftplus'), $file);
196 } else {
197 $itext = (0 == $index) ? '' : $index;
198 if (!empty($backups[$timestamp][$type.$itext.'-size']) && filesize($updraft_dir.'/'.$file) != $backups[$timestamp][$type.$itext.'-size']) {
199 if (empty($warn['doublecompressfixed'])) {
200 /* translators: 1: File name, 2: Actual file size, 3: Expected file size */
201 $warn[] = sprintf(__('File (%1$s) was found, but has a different size (%2$s) from what was expected (%3$s) - it may be corrupt.', 'updraftplus'), $file, filesize($updraft_dir.'/'.$file), $backups[$timestamp][$type.$itext.'-size']);
202 }
203 }
204 do_action_ref_array("updraftplus_checkzip_$type", array($updraft_dir.'/'.$file, &$mess, &$warn, &$err));
205 }
206 $expected_index++;
207 }
208 do_action_ref_array("updraftplus_checkzip_end_$type", array(&$mess, &$warn, &$err));
209 // Detect missing archives where they are missing from the end of the set
210 if ($outof>0 && $expected_index < $outof) {
211 for ($j = $expected_index; $j<$outof; $j++) {
212 $missing .= ('' == $missing) ? (1+$j) : ",".(1+$j);
213 }
214 }
215 if ('' != $missing) {
216 /* translators: %s: List of missing archives with description */
217 $warn[] = sprintf(__("This multi-archive backup set appears to have the following archives missing: %s", 'updraftplus'), $missing.' ('.$entity_info['description'].')');
218 }
219 }
220
221 // Check this backup set has a incremental_sets array e.g may have been created before this array was introduced
222 if (isset($backups[$timestamp]['incremental_sets'])) {
223 if (isset($elements['db']) && 1 === count($elements)) {
224 // Don't show the incremental dropdown if the user only selects 'database'
225 } else {
226 $incremental_sets = array_keys($backups[$timestamp]['incremental_sets']);
227 // Check if there are more than one timestamp in the incremental set
228 if (1 < count($incremental_sets)) {
229 $incremental_select_html = '<div class="udp-notice updraft-restore-option"><label>'.__('This backup set contains incremental backups of your files; please select the time you wish to restore your files to', 'updraftplus').': </label>';
230 $incremental_select_html .= '<select name="updraft_incremental_restore_point" id="updraft_incremental_restore_point">';
231 $incremental_sets = array_reverse($incremental_sets);
232 $first_timestamp = $incremental_sets[0];
233
234 foreach ($incremental_sets as $set_timestamp) {
235 $pretty_date = get_date_from_gmt(gmdate('Y-m-d H:i:s', (int) $set_timestamp), 'M d, Y G:i');
236 $esc_pretty_date = esc_attr($pretty_date);
237 $incremental_select_html .= '<option value="'.$set_timestamp.'" '.selected($set_timestamp, $first_timestamp, false).'>'.$esc_pretty_date.'</option>';
238 }
239
240 $incremental_select_html .= '</select>';
241 $incremental_select_html .= '</div>';
242 $info['addui'] = empty($info['addui']) ? $incremental_select_html : $info['addui'].'<br>'.$incremental_select_html;
243 }
244 }
245 }
246
247 if (0 == count($err) && 0 == count($warn)) {
248 $mess_first = __('The backup archive files have been successfully processed.', 'updraftplus').' '.__('Now press Restore to proceed.', 'updraftplus');
249 } elseif (0 == count($err)) {
250 $mess_first = __('The backup archive files have been processed, but with some warnings.', 'updraftplus').' '.__('If all is well, then press Restore to proceed.', 'updraftplus').' '.__('Otherwise, cancel and correct any problems first.', 'updraftplus');
251 } else {
252 $mess_first = __('The backup archive files have been processed, but with some errors.', 'updraftplus').' '.__('You will need to cancel and correct any problems before retrying.', 'updraftplus');
253 }
254
255 if (count($this->_updraftplus_admin->logged) >0) {
256 foreach ($this->_updraftplus_admin->logged as $lwarn) $warn[] = $lwarn;
257 }
258 restore_error_handler();
259
260 // Get the info if it hasn't already come from the DB scan
261 if (!isset($info) || !is_array($info)) $info = array();
262
263 // Not all characters can be json-encoded, and we don't need this potentially-arbitrary user-supplied info.
264 unset($info['label']);
265
266 if (!isset($info['created_by_version']) && !empty($backups[$timestamp]['created_by_version'])) $info['created_by_version'] = $backups[$timestamp]['created_by_version'];
267
268 if (!isset($info['multisite']) && !empty($backups[$timestamp]['is_multisite'])) $info['multisite'] = $backups[$timestamp]['is_multisite'];
269
270 do_action_ref_array('updraftplus_restore_all_downloaded_postscan', array($backups, $timestamp, $elements, &$info, &$mess, &$warn, &$err));
271
272 if (0 == count($err) && 0 == count($warn)) {
273 $mess_first = __('The backup archive files have been successfully processed.', 'updraftplus').' '.__('Now press Restore again to proceed.', 'updraftplus');
274 } elseif (0 == count($err)) {
275 $mess_first = __('The backup archive files have been processed, but with some warnings.', 'updraftplus').' '.__('If all is well, then now press Restore again to proceed.', 'updraftplus').' '.__('Otherwise, cancel and correct any problems first.', 'updraftplus');
276 } else {
277 $mess_first = __('The backup archive files have been processed, but with some errors.', 'updraftplus').' '.__('You will need to cancel and correct any problems before retrying.', 'updraftplus');
278 }
279
280 $warn_result = '';
281 foreach ($warn as $warning) {
282 if (!$warn_result) $warn_result = '<ul id="updraft_restore_warnings">';
283 $warn_result .= '<li>'.$warning.'</li>';
284 }
285 if ($warn_result) $warn_result .= '</ul>';
286
287 return array('m' => '<p>'.$mess_first.'</p>'.implode('<br>', $mess), 'w' => $warn_result, 'e' => implode('<br>', $err), 'i' => json_encode($info));
288 }
289
290 }
291
292 /**
293 * The purpose of this is to detect brokenness caused by extra line feeds in plugins/themes - before it breaks other AJAX operations and leads to support requests
294 *
295 * @return string
296 */
297 public function ping() {
298 return 'pong';
299 }
300
301 /**
302 * This function is called via ajax and will update the autobackup notice dismiss time
303 *
304 * @return array - an empty array
305 */
306 public function dismissautobackup() {
307 UpdraftPlus_Options::update_updraft_option('updraftplus_dismissedautobackup', time() + 84*86400);
308 return array();
309 }
310
311 /**
312 * This function is called via ajax and will update the general notice dismiss time
313 *
314 * @return array - an empty array
315 */
316 public function dismiss_notice() {
317 UpdraftPlus_Options::update_updraft_option('dismissed_general_notices_until', time() + 84*86400);
318 return array();
319 }
320
321 /**
322 * This function is called via ajax and will update the review notice dismiss time
323 *
324 * @param array $data - an array that contains the dismiss notice for time
325 *
326 * @return array - an empty array
327 */
328 public function dismiss_review_notice($data) {
329 if (empty($data['dismiss_forever'])) {
330 UpdraftPlus_Options::update_updraft_option('dismissed_review_notice', time() + 84*86400);
331 } else {
332 UpdraftPlus_Options::update_updraft_option('dismissed_review_notice', 100 * (365.25 * 86400));
333 }
334 return array();
335 }
336
337 /**
338 * This function is called via ajax and will update the season notice dismiss time
339 *
340 * @return array - an empty array
341 */
342 public function dismiss_season() {
343 UpdraftPlus_Options::update_updraft_option('dismissed_season_notices_until', time() + 366*86400);
344 return array();
345 }
346
347 /**
348 * This function is called via ajax and will update the clone php notice dismiss time
349 *
350 * @return array - an empty array
351 */
352 public function dismiss_clone_php_notice() {
353 UpdraftPlus_Options::update_updraft_option('dismissed_clone_php_notices_until', time() + 180 * 86400);
354 return array();
355 }
356
357 /**
358 * Update and set dismiss_phpseclib_notice option name to true
359 *
360 * @return array - an associative array containing a key named 'success' with 1 value which indicates the successful of updating the option
361 */
362 public function dismiss_phpseclib_notice() {
363 UpdraftPlus_Options::update_updraft_option('updraft_dismiss_phpseclib_notice', true);
364 return array('success' => 1);
365 }
366
367 /**
368 * This function is called via ajax and will update the WooCommerce clone notice dismiss time
369 *
370 * @return array - an empty array
371 */
372 public function dismiss_clone_wc_notice() {
373 UpdraftPlus_Options::update_updraft_option('dismissed_clone_wc_notices_until', time() + 90 * 86400);
374 return array();
375 }
376
377 public function set_autobackup_default($params) {
378 $default = empty($params['default']) ? 0 : 1;
379 UpdraftPlus_Options::update_updraft_option('updraft_autobackup_default', $default);
380 return array();
381 }
382
383 public function dismissexpiry() {
384 UpdraftPlus_Options::update_updraft_option('updraftplus_dismissedexpiry', time() + 14*86400);
385 return array();
386 }
387
388 public function dismissdashnotice() {
389 UpdraftPlus_Options::update_updraft_option('updraftplus_dismisseddashnotice', time() + 366*86400);
390 return array();
391 }
392
393 public function rawbackuphistory() {
394 // This is used for iframe source; hence, returns a string
395 $show_raw_data = $this->_updraftplus_admin->show_raw_backups();
396 return $show_raw_data['html'];
397 }
398
399 /**
400 * N.B. Not exactly the same as the phpinfo method in the UpdraftCentral core class
401 * Returns a string, as it is directly fetched as the source of an iframe
402 *
403 * @return String - returns the resulting HTML
404 */
405 public function phpinfo() {
406
407 ob_start();
408
409 if (function_exists('phpinfo')) phpinfo(INFO_ALL ^ (INFO_CREDITS | INFO_LICENSE)); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.prevent_path_disclosure_phpinfo -- we call the phpinfo() function to display PHP information in the advanced tools.
410
411 echo '<h3 id="ud-debuginfo-constants">'.esc_html__('Constants', 'updraftplus').'</h3>';
412 $opts = @get_defined_constants();// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise because of the function.
413 ksort($opts);
414 echo '<table><thead></thead><tbody>';
415 foreach ($opts as $key => $opt) {
416 // Administrators can already read these in other ways, but we err on the side of caution
417 if (is_string($opt) && false !== stripos($opt, 'api_key')) $opt = '***';
418 echo '<tr><td>'.esc_html($key).'</td><td>'.esc_html(print_r($opt, true)).'</td>';
419 }
420 echo '</tbody></table>';
421
422 $ret = ob_get_contents();
423 ob_end_clean();
424
425 return $ret;
426
427 }
428
429 /**
430 * Return messages if there are more than 4 overdue cron jobs
431 *
432 * @return Array - the messages are stored in an associative array and are indexed with key 'm'
433 */
434 public function check_overdue_crons() {
435 $messages = array();
436 $how_many_overdue = $this->_updraftplus_admin->howmany_overdue_crons();
437 if ($how_many_overdue >= 4) {
438 $messages['m'] = array();
439 $messages['m'][] = $this->_updraftplus_admin->show_admin_warning_overdue_crons($how_many_overdue);
440 if (defined('DISABLE_WP_CRON') && DISABLE_WP_CRON && (!defined('UPDRAFTPLUS_DISABLE_WP_CRON_NOTICE') || !UPDRAFTPLUS_DISABLE_WP_CRON_NOTICE)) $messages['m'][] = $this->_updraftplus_admin->show_admin_warning_disabledcron();
441 }
442 return $messages;
443 }
444
445 public function whichdownloadsneeded($params) {
446 // The purpose of this is to look at the list of indicated downloads, and indicate which are not already fully downloaded. i.e. Which need further action.
447 $send_back = array();
448 $backup = UpdraftPlus_Backup_History::get_history($params['timestamp']);
449 $updraft_dir = $this->_updraftplus->backups_dir_location();
450 $backupable_entities = $this->_updraftplus->get_backupable_file_entities();
451
452 if (empty($backup)) return array('result' => 'asyouwere');
453
454 if (isset($params['updraftplus_clone']) && empty($params['downloads'])) {
455 $entities = array('db', 'plugins', 'themes', 'uploads', 'others');
456 foreach ($entities as $entity) {
457
458 foreach ($backup as $key => $data) {
459 if ($key != $entity) continue;
460
461 $set_contents = '';
462 $entity_array = array();
463 $entity_array[] = $key;
464
465 if ('db' == $key) {
466 $set_contents = "0";
467 } else {
468 foreach (array_keys($data) as $findex) {
469 $set_contents .= ('' == $set_contents) ? $findex : ",$findex";
470 }
471 }
472
473 $entity_array[] = $set_contents;
474 $params['downloads'][] = $entity_array;
475 }
476 }
477 }
478
479 foreach ($params['downloads'] as $i => $download) {
480 if (is_array($download) && 2 == count($download) && isset($download[0]) && isset($download[1])) {
481 $entity = $download[0];
482 if (('db' == $entity || isset($backupable_entities[$entity])) && isset($backup[$entity])) {
483 $indexes = explode(',', $download[1]);
484 $retain_string = '';
485 foreach ($indexes as $index) {
486 $retain = true; // default
487 $findex = (0 == $index) ? '' : (string) $index;
488 $files = $backup[$entity];
489 if (!is_array($files)) $files = array($files);
490 $size_key = $entity.$findex.'-size';
491 if (isset($files[$index]) && isset($backup[$size_key])) {
492 $file = $updraft_dir.'/'.$files[$index];
493 if (file_exists($file) && filesize($file) >= $backup[$size_key]) {
494 $retain = false;
495 }
496 }
497 if ($retain) {
498 $retain_string .= ('' === $retain_string) ? $index : ','.$index;
499 $send_back[$i][0] = $entity;
500 $send_back[$i][1] = $retain_string;
501 }
502 }
503 } else {
504 $send_back[$i][0] = $entity;
505 $send_back[$i][1] = $download[$i][1];
506 }
507 } else {
508 // Format not understood. Just send it back as-is.
509 $send_back[$i] = $download[$i];
510 }
511 }
512 // Finally, renumber the keys (to usual PHP style - 0, 1, ...). Otherwise, in order to preserve the indexes, json_encode() will create an object instead of an array in the case where $send_back only has one element (and is indexed with an index > 0)
513 $send_back = array_values($send_back);
514 return array('downloads' => $send_back);
515 }
516
517 /**
518 * This is an handler function that checks what entity has been specified in the $params and calls the required method
519 *
520 * @param [array] $params this is an array of parameters sent via ajax it can include various things depending on what has called this method, this method only cares about the entity parameter which is used to call the correct method and return tree nodes based on that
521 * @return [array] returns an array of jstree nodes
522 */
523 public function get_jstree_directory_nodes($params) {
524
525 if ('filebrowser' == $params['entity']) {
526 $node_array = $this->_updraft_jstree_directory($params);
527 } elseif ('zipbrowser' == $params['entity']) {
528 $node_array = $this->_updraft_jstree_zip($params);
529 } else {
530 $node_array = apply_filters('updraftplus_jstree_'.$params['entity'], array(), $params);
531 }
532 return empty($node_array['error']) ? array('nodes' => $node_array) : $node_array;
533 }
534
535 /**
536 * This creates an array of nodes, built from either ABSPATH or the given directory ready to be returned to the jstree object.
537 *
538 * @param [array] $params this is an array of parameters sent via ajax it can include the following:
539 * node - this is a jstree node object containing information about the selected node
540 * path - this is a path if provided this will be used to build the tree otherwise ABSPATH is used
541 * drop_directory - this is a boolean that if set to true will drop one directory level off the path this is used so that you can move above the current root directory
542 * @return [array] returns an array of jstree nodes
543 */
544 private function _updraft_jstree_directory($params) {
545 $node_array = array();
546
547 // # is the root node if it's the root node then this is the first call so create a parent node otherwise it's a child node and we should get the path from the node id
548 if ('#' == $params['node']['id']) {
549 $path = ABSPATH;
550
551 if (!empty($params['path']) && is_dir($params['path']) && is_readable($params['path'])) $path = $params['path'];
552 $one_dir_up = dirname($path);
553
554 if (!empty($params['drop_directory']) && true == $params['drop_directory'] && is_readable($one_dir_up)) $path = $one_dir_up;
555 if (empty($params['skip_root_node'])) {
556 $node_array[] = array(
557 'text' => basename($path),
558 'children' => true,
559 'id' => $path,
560 'icon' => 'jstree-folder',
561 'state' => array(
562 'opened' => true
563 )
564 );
565 }
566 } else {
567 $path = $params['node']['id'];
568 }
569
570 $page = empty($params['page']) ? '' : $params['page'];
571
572 if ($dh = opendir($path)) {
573 $path = rtrim($path, DIRECTORY_SEPARATOR);
574
575 $skip_paths = array(".", "..");
576
577 while (($value = readdir($dh)) !== false) {
578 if (!in_array($value, $skip_paths)) {
579 if (is_dir($path . DIRECTORY_SEPARATOR . $value)) {
580 $node_array[] = array(
581 'text' => $value,
582 'children' => true,
583 'id' => UpdraftPlus_Manipulation_Functions::wp_normalize_path($path . DIRECTORY_SEPARATOR . $value),
584 'icon' => 'jstree-folder'
585 );
586 } elseif (empty($params['directories_only']) && 'restore' != $page && is_file($path . DIRECTORY_SEPARATOR . $value)) {
587 $node_array[] = array(
588 'text' => $value,
589 'children' => false,
590 'id' => UpdraftPlus_Manipulation_Functions::wp_normalize_path($path . DIRECTORY_SEPARATOR . $value),
591 'type' => 'file',
592 'icon' => 'jstree-file'
593 );
594 }
595 }
596 }
597 } else {
598 /* translators: %s: Directory path */
599 $node_array['error'] = sprintf(__('Failed to open directory: %s.', 'updraftplus'), $path).' '.
600 __('This is normally caused by file permissions.', 'updraftplus');
601 }
602
603 return $node_array;
604 }
605
606 /**
607 * This creates an array of nodes, built from a unzipped zip file structure.
608 *
609 * @param [array] $params this is an array of parameters sent via ajax it can include the following:
610 * node - this is a jstree node object containing information about the selected node
611 * timestamp - this is the backup timestamp and is used to get the backup archive
612 * type - this is the type of backup and is used to get the backup archive
613 * findex - this is the index used to get the correct backup archive if theres more than one of a single archive type
614 * @return [array] returns an array of jstree nodes
615 */
616 private function _updraft_jstree_zip($params) {
617
618 $updraftplus = $this->_updraftplus;
619
620 $node_array = array();
621
622 $zip_object = $updraftplus->get_zip_object_name();
623
624 // Retrieve the information from our backup history
625 $backup_history = UpdraftPlus_Backup_History::get_history();
626
627 if (!isset($backup_history[$params['timestamp']][$params['type']])) {
628 return array('error' => __('Backup set not found', 'updraftplus'));
629 }
630
631 // Base name
632 $file = $backup_history[$params['timestamp']][$params['type']];
633
634 // Get date in human readable form
635 $pretty_date = get_date_from_gmt(gmdate('Y-m-d H:i:s', (int) $params['timestamp']), 'M d, Y G:i');
636
637 $backupable_entities = $updraftplus->get_backupable_file_entities(true, true);
638
639 // Check the file type and set the name in a more friendly way
640 $archive_name = isset($backupable_entities[$params['type']]['description']) ? $backupable_entities[$params['type']]['description'] : $params['type'];
641
642 if (substr($params['type'], 0, 2) === 'db') $archive_name = __('Extra database', 'updraftplus') . ' ' . substr($params['type'], 3, 1);
643 if ('db' == $params['type']) $archive_name = __('Database', 'updraftplus');
644 if ('more' == $params['type']) $archive_name = $backupable_entities[$params['type']]['shortdescription'];
645 if ('wpcore' == $params['type']) $archive_name = __('WordPress Core', 'updraftplus');
646
647 $archive_set = ($params['findex'] + 1) . '/' . sizeof($file);
648
649 if ('1/1' == $archive_set) $archive_set = '';
650
651 $parent_name = $archive_name . ' ' . __('archive', 'updraftplus') . ' ' . $archive_set . ' ' . $pretty_date;
652
653 // Deal with multi-archive sets
654 if (is_array($file)) $file = $file[$params['findex']];
655
656 // Where it should end up being downloaded to
657 $fullpath = $updraftplus->backups_dir_location().'/'.$file;
658
659 if (file_exists($fullpath) && is_readable($fullpath) && filesize($fullpath)>0) {
660
661 $node_array[] = array(
662 'text' => $parent_name,
663 'parent' => '#',
664 'id' => $parent_name,
665 'icon' => 'jstree-folder',
666 'state' => array('opened' => true),
667 'li_attr' => array('path' => $parent_name)
668 );
669
670 $zip = new $zip_object;
671
672 $zip_opened = $zip->open($fullpath);
673
674 if (true !== $zip_opened) {
675 return array('error' => 'UpdraftPlus: opening zip (' . $fullpath . '): failed to open this zip file (object='.$zip_object.', code: '.$zip_opened.')');
676 } else {
677
678 $numfiles = $zip->numFiles;
679
680 if (false === $numfiles) return array('error' => 'UpdraftPlus: reading zip: '.$zip->last_error);
681
682 for ($i=0; $i < $numfiles; $i++) {
683 $si = $zip->statIndex($i);
684
685 // Fix for windows being unable to build jstree due to different directory separators being used
686 $si['name'] = str_replace("/", DIRECTORY_SEPARATOR, $si['name']);
687
688 // if it's a dot then we don't want to append this as it will break the ids and the tree structure
689 if ('.' == dirname($si['name'])) {
690 $node_id = $parent_name;
691 } else {
692 $node_id = $parent_name . DIRECTORY_SEPARATOR . dirname($si['name']) . DIRECTORY_SEPARATOR;
693 }
694
695 $extension = substr(strrchr($si['name'], "."), 1);
696
697 if (0 == $si['size'] && empty($extension)) {
698 $node_array[] = array(
699 'text' => basename($si['name']),
700 'parent' => $node_id,
701 'id' => $parent_name . DIRECTORY_SEPARATOR . $si['name'],
702 'icon' => 'jstree-folder',
703 'li_attr' => array(
704 'path' => $parent_name . DIRECTORY_SEPARATOR . $si['name']
705 )
706 );
707 } else {
708 $node_array[] = array(
709 'text' => basename($si['name']),
710 'parent' => $node_id,
711 'id' => $parent_name . DIRECTORY_SEPARATOR . $si['name'],
712 'type' => 'file',
713 'icon' => 'jstree-file',
714 'li_attr' => array(
715 'path' => $parent_name . DIRECTORY_SEPARATOR . $si['name'],
716 'size' => UpdraftPlus_Manipulation_Functions::convert_numeric_size_to_text($si['size'])
717 )
718 );
719 }
720 }
721
722 // check if this is an upload archive if it is add a 'uploads' folder so that the children can attach to it
723 if ('uploads' == $params['type']) $node_array[] = array(
724 'text' => 'uploads',
725 'parent' => $parent_name,
726 'id' => $parent_name . DIRECTORY_SEPARATOR . 'uploads' . DIRECTORY_SEPARATOR,
727 'icon' => 'jstree-folder',
728 'li_attr' => array(
729 'path' => $parent_name . DIRECTORY_SEPARATOR . 'uploads' . DIRECTORY_SEPARATOR
730 )
731 );
732
733 @$zip->close();// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise because of the method.
734 }
735 }
736
737 return $node_array;
738 }
739
740 /**
741 * Return information on the zipfile download
742 *
743 * @param Array $params - details on the download; keys: type, findex, path, timestamp
744 *
745 * @return Array
746 */
747 public function get_zipfile_download($params) {
748 return apply_filters('updraftplus_command_get_zipfile_download', array('error' => 'UpdraftPlus: command (get_zipfile_download) not installed (are you missing an add-on?)'), $params);
749 }
750
751 /**
752 * Dismiss the notice which will if .htaccess have any old migrated site reference.
753 *
754 * @return Boolean Return true if migration notice is dismissed
755 */
756 public function dismiss_migration_notice_for_old_site_reference() {
757 delete_site_option('updraftplus_migrated_site_domain');
758 return true;
759 }
760
761 /**
762 * When character set and collate both are unsupported at restoration time and if user change anyone substitution dropdown from both, Other substitution select box value should be change respectively. To achieve this functionality, Ajax calls comes here.
763 *
764 * @param Array $params this is an array of parameters sent via ajax it can include the following:
765 * collate_change_on_charset_selection_data - It is data in serialize form which is need for choose other dropdown option value. It contains below elements data:
766 * db_supported_collations - All collations supported by current database. This is result of 'SHOW COLLATION' query
767 * db_unsupported_collate_unique - Unsupported collates unique array
768 * db_collates_found - All collates found in database backup file
769 * event_source_elem - Dropdown elemtn id which trigger the ajax request
770 * updraft_restorer_charset - Charset dropdown selected value option
771 * updraft_restorer_collate - Collate dropdown selected value option
772 *
773 * @return array - $action_data which contains following data:
774 * is_action_required - 1 or 0 Whether or not change other dropdown value
775 * elem_id - Dropdown element id which value need to change. The other dropdown element id
776 * elem_val - Dropdown element value which should be selected for other drodown
777 */
778 public function collate_change_on_charset_selection($params) {
779 $collate_change_on_charset_selection_data = json_decode(UpdraftPlus_Manipulation_Functions::wp_unslash($params['collate_change_on_charset_selection_data']), true);
780 $updraft_restorer_collate = $params['updraft_restorer_collate'];
781 $updraft_restorer_charset = $params['updraft_restorer_charset'];
782
783 $db_supported_collations = $collate_change_on_charset_selection_data['db_supported_collations'];
784 $db_unsupported_collate_unique = $collate_change_on_charset_selection_data['db_unsupported_collate_unique'];
785 $db_collates_found = $collate_change_on_charset_selection_data['db_collates_found'];
786
787 $action_data = array(
788 'is_action_required' => 0,
789 );
790 // No need to change other dropdown value
791 if (isset($db_supported_collations[$updraft_restorer_collate]->Charset) && $updraft_restorer_charset == $db_supported_collations[$updraft_restorer_collate]->Charset) {
792 return $action_data;
793 }
794 $similar_type_collate = $this->_updraftplus->get_similar_collate_related_to_charset($db_supported_collations, $db_unsupported_collate_unique, $updraft_restorer_charset);
795 if (empty($similar_type_collate)) {
796 $similar_type_collate = $this->_updraftplus->get_similar_collate_based_on_ocuurence_count($db_collates_found, $db_supported_collations, $updraft_restorer_collate);
797 }
798 // Default collation for changed character set
799 if (empty($similar_type_collate)) {
800 $charset_row = $GLOBALS['wpdb']->get_row($GLOBALS['wpdb']->prepare("SHOW CHARACTER SET LIKE '%s'", $updraft_restorer_charset));
801 if (null !== $charset_row && !empty($charset_row->{'Default collation'})) {
802 $similar_type_collate = $charset_row->{'Default collation'};
803 }
804 }
805 if (empty($similar_type_collate)) {
806 foreach ($db_supported_collations as $db_supported_collation => $db_supported_collation_info) {
807 if (isset($db_supported_collation_info->Charset) && $updraft_restorer_charset == $db_supported_collation_info->Charset) {
808 $similar_type_collate = $db_supported_collation;
809 break;
810 }
811 }
812 }
813 if (!empty($similar_type_collate)) {
814 $action_data['is_action_required'] = 1;
815 $action_data['similar_type_collate'] = $similar_type_collate;
816 }
817 return $action_data;
818 }
819
820 /**
821 * Set the Tour status
822 *
823 * @param array $params - the $_REQUEST. We're looking for 'current_step'
824 * @return bool
825 */
826 public function set_tour_status($params) {
827 return class_exists('UpdraftPlus_Tour') ? UpdraftPlus_Tour::get_instance()->set_tour_status($params) : false;
828 }
829
830 /**
831 * Resets the tour status
832 *
833 * @return bool
834 */
835 public function reset_tour_status() {
836 return class_exists('UpdraftPlus_Tour') ? UpdraftPlus_Tour::get_instance()->reset_tour_status() : false;
837 }
838
839 /**
840 * Return the database information
841 *
842 * @param bool $raw_data_only If true, returns array of data; if false, returns HTML
843 * @return array If $raw_data_only is true, returns array with 'tables' and 'size'. If false, returns array with 'size' and 'html'
844 */
845 public function db_size($raw_data_only = false) {
846 global $wpdb;
847
848 $db_table_res = $wpdb->get_results('SHOW TABLE STATUS', ARRAY_A);
849 $db_table_size = 0;
850 $db_size_info = array();
851
852 if ($wpdb->num_rows > 0) {
853 $key_field_name = UpdraftPlus_Manipulation_Functions::backquote('Key');
854
855 foreach ($db_table_res as $row) {
856 // Try search from transient
857 $rows_count = get_transient('wpo_'.$row['Name'].'_count');
858 if (false === $rows_count) {
859 // If not found, try search primary key first
860 $table_name = UpdraftPlus_Manipulation_Functions::backquote($row['Name']);
861 // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- $table_name and $key_field_name are SQL identifiers; identifiers cannot be parameterized with $wpdb->prepare(), $table_name and $key_field_name are safe.
862 $primary_key = $wpdb->get_row($wpdb->prepare("SHOW COLUMNS FROM $table_name WHERE $key_field_name = %s", 'PRI'), ARRAY_A);
863 if ($primary_key) {
864 // Count rows by primary key
865 $primary_key_field = UpdraftPlus_Manipulation_Functions::backquote($primary_key['Field']);
866 // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- $table_name and $primary_key_field are SQL identifiers; identifiers cannot be parameterized with $wpdb->prepare(), $table_name and $primary_key_field are safe.
867 $rows_count = $wpdb->get_var("SELECT COUNT($primary_key_field) FROM ".$table_name);
868 }
869
870 // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- $table_name is SQL identifiers; identifiers cannot be parameterized with $wpdb->prepare(), $table_name is are safe.
871 if (is_null($rows_count) || false === $rows_count) $rows_count = $wpdb->get_var("SELECT COUNT(*) FROM ".$table_name);
872 }
873
874 $data_length = isset($row['Data_length']) ? (int) $row['Data_length'] : 0;
875 $index_length = isset($row['Index_length']) ? (int) $row['Index_length'] : 0;
876
877 $db_size_info[] = array(
878 'name' => $row['Name'],
879 'records' => $rows_count,
880 'data_length' => $data_length,
881 'index_length' => $index_length,
882 'type' => $row['Engine']
883 );
884
885 $db_table_size += $data_length + $index_length;
886 }
887 }
888
889 $total_size_formatted = size_format((int) $db_table_size, 2);
890
891 // Return raw data if requested
892 if ($raw_data_only) {
893 return array(
894 'tables' => $db_size_info,
895 'size' => $total_size_formatted
896 );
897 }
898
899 // Otherwise, construct HTML from the collected data
900 $db_table_html = '';
901 foreach ($db_size_info as $table_info) {
902 $db_table_html .= '<tr>';
903 $db_table_html .= sprintf('<td>%s</td>', esc_html($table_info['name']));
904 $db_table_html .= sprintf('<td>%s</td>', esc_html($table_info['records']));
905 $db_table_html .= sprintf('<td>%s</td>', esc_html($table_info['data_size']));
906 $db_table_html .= sprintf('<td>%s</td>', esc_html($table_info['index_size']));
907 $db_table_html .= sprintf('<td>%s</td>', esc_html($table_info['type']));
908 $db_table_html .= '</tr>';
909 }
910
911 return array(
912 'size' => $total_size_formatted,
913 'html' => $db_table_html
914 );
915 }
916
917 /**
918 * Retrieves the scheduled UpdraftPlus cron events.
919 *
920 * This function fetches all cron events and filters those related to UpdraftPlus.
921 * It then formats the schedule information, calculates the time difference,
922 * and returns a structured array of cron details.
923 *
924 * @return array[] An array of cron event details, where each event contains:
925 * - `overdue` (int): Whether the event is overdue (1) or not (0).
926 * - `hook` (string): The name of the cron hook.
927 * - `name` (string): The display name of the schedule.
928 * - `time` (string): The formatted execution time.
929 * - `interval` (string): The time remaining or overdue duration.
930 */
931 public function get_cron_events() {
932 $data = array();
933
934 $schedules = wp_get_schedules();
935 $cron = _get_cron_array();
936
937 $date_format = get_option('date_format');
938 $time_format = get_option('time_format');
939
940 // Loop through the cron schedules
941 foreach ($cron as $timestamp => $cron_hooks) {
942 foreach ($cron_hooks as $hook => $events) {
943 if (!preg_match('/^updraft(_backup(_database|_resume|_increments)?|plus_clean_temporary_files)$/', $hook)) continue;
944
945 sort($events);
946
947 $schedule_name = $schedules[$events[0]['schedule']];
948 // wp_date() is available on WP 5.3+, it performs locale translation.
949 $formatted_date = function_exists('wp_date') ? wp_date($date_format. ' ' .$time_format, $timestamp) : get_date_from_gmt(gmdate('Y-m-d H:i:s', $timestamp), $date_format. ' ' .$time_format);
950
951 $difference = $timestamp - current_time('timestamp', true);
952 $difference_in_seconds = abs($difference);
953 $overdue = $difference < 0 ? 1 : 0;
954
955 $hours = floor($difference_in_seconds / 3600);
956 $minutes = floor(($difference_in_seconds % 3600) / 60);
957
958 if ($overdue) {
959 /* translators: 1: Number of hours, 2: Number of minutes */
960 $interval = sprintf(__('%1$d hours and %2$d minutes ago', 'updraftplus'), $hours, $minutes);
961 } else {
962 /* translators: 1: Number of hours, 2: Number of minutes */
963 $interval = sprintf(__('%1$d hours and %2$d minutes', 'updraftplus'), $hours, $minutes);
964 }
965
966 $data[] = array(
967 'overdue' => $overdue,
968 'hook' => $hook,
969 'name' => $schedule_name['display'],
970 'time' => $formatted_date,
971 'interval' => $interval
972 );
973 }
974 }
975
976 return $data;
977 }
978 }
979