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

backup-module.php in UpdraftPlus: WP Backup & Migration Plugin 1.16.5, at methods/backup-module.php

672 lines 23.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 if (!defined('UPDRAFTPLUS_DIR')) die('No direct access allowed.');
4
5 abstract class UpdraftPlus_BackupModule {
6
7 private $_options;
8
9 private $_instance_id;
10
11 private $_storage;
12
13 /**
14 * Store options (within this class) for this remote storage module. There is also a parameter for saving to the permanent storage (i.e. database).
15 *
16 * @param array $options array of options to store
17 * @param boolean $save whether or not to also save the options to the database
18 * @param null|String $instance_id optionally set the instance ID for this instance at the same time. This is required if you have not already set an instance ID with set_instance_id()
19 * @return void|Boolean If saving to DB, then the result of the DB save operation is returned.
20 */
21 public function set_options($options, $save = false, $instance_id = null) {
22
23 $this->_options = $options;
24
25 // Remove any previously-stored storage object, because this is usually tied to the options
26 if (!empty($this->_storage)) unset($this->_storage);
27
28 if ($instance_id) $this->set_instance_id($instance_id);
29
30 if ($save) return $this->save_options();
31
32 }
33
34 /**
35 * Saves the current options to the database. This is a private function; external callers should use set_options().
36 *
37 * @throws Exception if trying to save options without indicating an instance_id, or if the remote storage module does not have the multi-option capability
38 */
39 private function save_options() {
40
41 if (!$this->supports_feature('multi_options')) {
42 throw new Exception('save_options() can only be called on a storage method which supports multi_options (this module, '.$this->get_id().', does not)');
43 }
44
45 if (!$this->_instance_id) {
46 throw new Exception('save_options() requires an instance ID, but was called without setting one (either directly or via set_instance_id())');
47 }
48
49 global $updraftplus;
50
51 $current_db_options = UpdraftPlus_Storage_Methods_Interface::update_remote_storage_options_format($this->get_id());
52
53 if (is_wp_error($current_db_options)) {
54 throw new Exception('save_options(): options fetch/update failed ('.$current_db_options->get_error_code().': '.$current_db_options->get_error_message().')');
55 }
56
57 $current_db_options['settings'][$this->_instance_id] = $this->_options;
58
59 return UpdraftPlus_Options::update_updraft_option('updraft_'.$this->get_id(), $current_db_options);
60
61 }
62
63 /**
64 * Retrieve default options for this remote storage module.
65 * This method would normally be over-ridden by the child.
66 *
67 * @return Array - an array of options
68 */
69 public function get_default_options() {
70 return array();
71 }
72
73 /**
74 * Retrieve a list of supported features for this storage method
75 * This method should be over-ridden by methods supporting new
76 * features.
77 *
78 * Keys are strings, and values are booleans.
79 *
80 * Currently known features:
81 *
82 * - multi_options : indicates that the remote storage module
83 * can handle its options being in the Feb-2017 multi-options
84 * format. N.B. This only indicates options handling, not any
85 * other multi-destination options.
86 *
87 * - multi_servers : not implemented yet: indicates that the
88 * remote storage module can handle multiple servers at backup
89 * time. This should not be specified without multi_options.
90 * multi_options without multi_servers is fine - it will just
91 * cause only the first entry in the options array to be used.
92 *
93 * - config_templates : not implemented yet: indicates that
94 * the remote storage module can output its configuration in
95 * Handlebars format via the get_configuration_template() method.
96 *
97 * @return Array - an array of supported features (any features not
98 * mentioned are assumed to not be supported)
99 */
100 public function get_supported_features() {
101 return array();
102 }
103
104 /**
105 * This method should only be called if the feature 'multi storage' is supported. In that case, it returns a template with information about the remote storage. The code below is a placeholder, and methods supporting the feature should always over-ride it.
106 *
107 * @return String - HTML template
108 */
109 public function get_pre_configuration_template() {
110 return $this->get_id().": called, but not implemented in the child class (coding error)";
111 }
112
113 /**
114 * This method should only be called if the feature 'config templates' is supported. In that case, it returns a template with appropriate placeholders for specific settings. The code below is a placeholder, and methods supporting the feature should always over-ride it.
115 *
116 * @return String - HTML template
117 */
118 public function get_configuration_template() {
119 return $this->get_id().": called, but not implemented in the child class (coding error)";
120 }
121
122 /**
123 * This method will set the stored storage object to that indicated
124 *
125 * @param Object $storage - the storage client
126 */
127 public function set_storage($storage) {
128 $this->_storage = $storage;
129 }
130
131 /**
132 * This method will return the stored storage client
133 *
134 * @return Object - the stored remote storage client
135 */
136 public function get_storage() {
137 if (!empty($this->_storage)) return $this->_storage;
138 }
139
140 /**
141 * Outputs id and name fields, as if currently within an input tag
142 *
143 * This assumes standardised options handling (i.e. that the options array is updraft_(method-id))
144 *
145 * @param Array|String $field - the field identifiers
146 * @param Boolean $return_instead_of_echo - tells the method if it should return the output or echo it to page
147 */
148 public function output_settings_field_name_and_id($field, $return_instead_of_echo = false) {
149
150 $method_id = $this->get_id();
151
152 $instance_id = $this->supports_feature('config_templates') ? '{{instance_id}}' : $this->_instance_id;
153
154 $id = '';
155 $name = '';
156
157 if (is_array($field)) {
158 foreach ($field as $key => $value) {
159 $id .= '_'.$value;
160 $name .= '['.$value.']';
161 }
162 } else {
163 $id = '_'.$field;
164 $name = '['.$field.']';
165 }
166
167 $output = "id=\"updraft_${method_id}${id}_${instance_id}\" name=\"updraft_${method_id}[settings][${instance_id}]${name}\" ";
168
169 if ($return_instead_of_echo) {
170 return $output;
171 } else {
172 echo $output;
173 }
174 }
175
176 /**
177 * Get the CSS ID
178 *
179 * @param String $field - the field identifier to return a CSS ID for
180 *
181 * @return String
182 */
183 public function get_css_id($field) {
184 $method_id = $this->get_id();
185 $instance_id = $this->supports_feature('config_templates') ? '{{instance_id}}' : $this->_instance_id;
186 return "updraft_${method_id}_${field}_${instance_id}";
187 }
188
189 /**
190 * Get handlebarsjs template
191 * This deals with any boiler-plate, prior to calling config_print()
192 *
193 * @uses self::config_print()
194 * @uses self::get_configuration_template()
195 *
196 * return handlebarsjs template or html
197 */
198 public function get_template() {
199 ob_start();
200 // Allow methods to not use this hidden field, if they do not output any settings (to prevent their saved settings being over-written by just this hidden field)
201 if ($this->print_shared_settings_fields()) {
202 ?><tr class="<?php echo $this->get_css_classes(); ?>"><input type="hidden" name="updraft_<?php echo $this->get_id();?>[version]" value="1"></tr><?php
203 }
204
205 if ($this->supports_feature('config_templates')) {
206 ?>
207 {{#if first_instance}}
208 <?php
209
210 $this->get_pre_configuration_template();
211
212 if ($this->supports_feature('multi_storage')) {
213 do_action('updraftplus_config_print_add_multi_storage', $this->get_id(), $this);
214 }
215
216 ?>
217 {{/if}}
218 <?php
219 do_action('updraftplus_config_print_before_storage', $this->get_id(), $this);
220
221 if ($this->supports_feature('multi_storage')) {
222 do_action('updraftplus_config_print_add_instance_label', $this->get_id(), $this);
223 }
224
225 $template = ob_get_clean();
226 $template .= $this->get_configuration_template();
227 } else {
228 do_action('updraftplus_config_print_before_storage', $this->get_id(), $this);
229 // N.B. These are mutually exclusive: config_print() is not used if config_templates is supported. So, even during transition, the UpdraftPlus_BackupModule instance only needs to support one of the two, not both.
230 $this->config_print();
231 $template = ob_get_clean();
232 }
233 return $template;
234 }
235
236 /**
237 * Modifies handerbar template options. Other child class can extend it.
238 *
239 * @param array $opts
240 * @return array - Modified handerbar template options
241 */
242 public function transform_options_for_template($opts) {
243 return $opts;
244 }
245
246 /**
247 * Gives settings keys which values should not passed to handlebarsjs context.
248 * The settings stored in UD in the database sometimes also include internal information that it would be best not to send to the front-end (so that it can't be stolen by a man-in-the-middle attacker)
249 *
250 * @return array - Settings array keys which should be filtered
251 */
252 public function filter_frontend_settings_keys() {
253 return array();
254 }
255
256 /**
257 * Over-ride this to allow methods to not use the hidden version field, if they do not output any settings (to prevent their saved settings being over-written by just this hidden field
258 *
259 * @return [boolean] - return true to output the version field or false to not output the field
260 */
261 public function print_shared_settings_fields() {
262 return true;
263 }
264
265 /**
266 * Prints out the configuration section for a particular module. This is now (Sep 2017) considered deprecated; things are being ported over to get_configuration_template(), indicated via the feature 'config_templates'.
267 */
268 public function config_print() {
269 echo $this->get_id().": module neither declares config_templates support, nor has a config_print() method (coding bug)";
270 }
271
272 /**
273 * Supplies the list of keys for options to be saved in the backup job.
274 */
275 public function get_credentials() {
276 $keys = array('updraft_ssl_disableverify', 'updraft_ssl_nossl', 'updraft_ssl_useservercerts');
277 if (!$this->supports_feature('multi_servers')) $keys[] = 'updraft_'.$this->get_id();
278 return $keys;
279 }
280
281 /**
282 * Returns a space-separated list of CSS classes suitable for rows in the configuration section
283 *
284 * @param Boolean $include_instance - a boolean value to indicate if we want to include the instance_id in the css class, we may not want to include the instance if it's for a UI element that we don't want to be removed along with other UI elements that do include a instance id.
285 *
286 * @returns String - the list of CSS classes
287 */
288 public function get_css_classes($include_instance = true) {
289 $classes = 'updraftplusmethod '.$this->get_id();
290 if (!$include_instance) return $classes;
291 if ($this->supports_feature('multi_options')) {
292 if ($this->supports_feature('config_templates')) {
293 $classes .= ' '.$this->get_id().'-{{instance_id}}';
294 } else {
295 $classes .= ' '.$this->get_id().'-'.$this->_instance_id;
296 }
297 }
298 return $classes;
299 }
300
301 /**
302 *
303 * Returns HTML for a row for a test button
304 *
305 * @param String $title - The text to be used in the button
306 *
307 * @returns String - The HTML to be inserted into the settings page
308 */
309 protected function get_test_button_html($title) {
310 ob_start();
311 $instance_id = $this->supports_feature('config_templates') ? '{{instance_id}}' : $this->_instance_id;
312 ?>
313 <tr class="<?php echo $this->get_css_classes(); ?>">
314 <th></th>
315 <td><p><button id="updraft-<?php echo $this->get_id();?>-test-<?php echo $instance_id;?>" type="button" class="button-primary updraft-test-button updraft-<?php echo $this->get_id();?>-test" data-instance_id="<?php echo $instance_id;?>" data-method="<?php echo $this->get_id();?>" data-method_label="<?php echo esc_attr($title);?>"><?php printf(__('Test %s Settings', 'updraftplus'), $title);?></button></p></td>
316 </tr>
317 <?php
318 return ob_get_clean();
319 }
320
321 /**
322 * Get the backup method identifier for this class
323 *
324 * @return String - the identifier
325 */
326 public function get_id() {
327 $class = get_class($this);
328 // UpdraftPlus_BackupModule_
329 return substr($class, 25);
330 }
331
332 /**
333 * Get the backup method description for this class
334 *
335 * @return String - the identifier
336 */
337 public function get_description() {
338 global $updraftplus;
339
340 $methods = $updraftplus->backup_methods;
341
342 $id = $this->get_id();
343
344 return $methods[$id];
345 }
346
347 /**
348 * Sets the instance ID - for supporting multi_options
349 *
350 * @param String $instance_id - the instance ID
351 */
352 public function set_instance_id($instance_id) {
353 $this->_instance_id = $instance_id;
354 }
355
356 /**
357 * Sets the instance ID - for supporting multi_options
358 *
359 * @returns String the instance ID
360 */
361 public function get_instance_id() {
362 return $this->_instance_id;
363 }
364
365 /**
366 * Check whether this storage module supports a mentioned feature
367 *
368 * @param String $feature - the feature concerned
369 *
370 * @returns Boolean
371 */
372 public function supports_feature($feature) {
373 return in_array($feature, $this->get_supported_features());
374 }
375
376 /**
377 * Retrieve options for this remote storage module.
378 * N.B. The option name instance_id is reserved and should not be used.
379 *
380 * @uses get_default_options
381 *
382 * @return Array - array of options. This will include default values for any options not set.
383 */
384 public function get_options() {
385
386 global $updraftplus;
387
388 $supports_multi_options = $this->supports_feature('multi_options');
389
390 if (is_array($this->_options)) {
391 // First, prioritise any options that were explicitly set. This is the eventual goal for all storage modules.
392 $options = $this->_options;
393
394 } elseif (is_callable(array($this, 'get_opts'))) {
395 // Next, get any options available via a legacy / over-ride method.
396
397 if ($supports_multi_options) {
398 // This is forbidden, because get_opts() is legacy and is for methods that do not support multi-options. Supporting multi-options leads to the array format being updated, which will then break get_opts().
399 die('Fatal error: method '.$this->get_id().' both supports multi_options and provides a get_opts method');
400 }
401
402 $options = $this->get_opts();
403
404 } else {
405
406 // Next, look for job options (which in turn, falls back to saved settings if no job options were set)
407
408 $options = $updraftplus->get_job_option('updraft_'.$this->get_id());
409 if (!is_array($options)) $options = array();
410
411 if ($supports_multi_options) {
412
413 if (!isset($options['version'])) {
414 $options_full = UpdraftPlus_Storage_Methods_Interface::update_remote_storage_options_format($this->get_id());
415
416 if (is_wp_error($options_full)) {
417 $updraftplus->log("Options retrieval failure: ".$options_full->get_error_code().": ".$options_full->get_error_message()." (".json_encode($options_full->get_error_data()).")");
418 return array();
419 }
420
421 } else {
422 $options_full = $options;
423 }
424
425 // UpdraftPlus_BackupModule::get_options() is for getting the current instance's options. So, this branch (going via the job option) is a legacy route, and hence we just give back the first one. The non-legacy route is to call the set_options() method externally.
426 $options = reset($options_full['settings']);
427
428 if (false === $options) {
429 $updraftplus->log("Options retrieval failure (no options set)");
430 return array();
431 }
432 $instance_id = key($options_full['settings']);
433 $this->set_options($options, false, $instance_id);
434
435 }
436
437 }
438
439 $options = apply_filters(
440 'updraftplus_backupmodule_get_options',
441 wp_parse_args($options, $this->get_default_options()),
442 $this
443 );
444
445 return $options;
446
447 }
448
449 /**
450 * Set job data that is local to this storage instance
451 * (i.e. the key does not need to be unique across instances)
452 *
453 * @uses UpdraftPlus::jobdata_set()
454 *
455 * @param String $key - the key for the job data
456 * @param Mixed $value - the data to be stored
457 */
458 public function jobdata_set($key, $value) {
459
460 $instance_key = $this->get_id().'-'.($this->_instance_id ? $this->_instance_id : 'no_instance');
461
462 global $updraftplus;
463
464 $instance_data = $updraftplus->jobdata_get($instance_key);
465
466 if (!is_array($instance_data)) $instance_data = array();
467
468 $instance_data[$key] = $value;
469
470 $updraftplus->jobdata_set($instance_key, $instance_data);
471
472 }
473
474 /**
475 * Get job data that is local to this storage instance
476 * (i.e. the key does not need to be unique across instances)
477 *
478 * @uses UpdraftPlus::jobdata_get()
479 *
480 * @param String $key - the key for the job data
481 * @param Mixed $default - the default to return if nothing was set
482 * @param String|Null $legacy_key - the previous name of the key, prior to instance-specific job data (so that upgrades across versions whilst a backup is in progress can still find its data). In future, support for this can be removed.
483 */
484 public function jobdata_get($key, $default = null, $legacy_key = null) {
485
486 $instance_key = $this->get_id().'-'.($this->_instance_id ? $this->_instance_id : 'no_instance');
487
488 global $updraftplus;
489
490 $instance_data = $updraftplus->jobdata_get($instance_key);
491
492 if (is_array($instance_data) && isset($instance_data[$key])) return $instance_data[$key];
493
494 return is_string($legacy_key) ? $updraftplus->jobdata_get($legacy_key, $default) : $default;
495
496 }
497
498 /**
499 * Delete job data that is local to this storage instance
500 * (i.e. the key does not need to be unique across instances)
501 *
502 * @uses UpdraftPlus::jobdata_set()
503 *
504 * @param String $key - the key for the job data
505 * @param String|Null $legacy_key - the previous name of the key, prior to instance-specific job data (so that upgrades across versions whilst a backup is in progress can still find its data)
506 */
507 public function jobdata_delete($key, $legacy_key = null) {
508
509 $instance_key = $this->get_id().'-'.($this->_instance_id ? $this->_instance_id : 'no_instance');
510
511 global $updraftplus;
512
513 $instance_data = $updraftplus->jobdata_get($instance_key);
514
515 if (is_array($instance_data) && isset($instance_data[$key])) {
516 unset($instance_data[$key]);
517 $updraftplus->jobdata_set($instance_key, $instance_data);
518 }
519
520 if (is_string($legacy_key)) $updraftplus->jobdata_delete($legacy_key);
521
522 }
523
524 /**
525 * This method will either return or echo the constructed auth link for the remote storage method
526 *
527 * @param Boolean $echo_instead_of_return - a boolean to indicate if the authentication link should be echo or returned
528 * @param Boolean $template_instead_of_notice - a boolean to indicate if the authentication link is for a template or a notice
529 * @return Void|String - returns a string or nothing depending on the parameters
530 */
531 public function get_authentication_link($echo_instead_of_return = true, $template_instead_of_notice = true) {
532 if (!$echo_instead_of_return) {
533 ob_start();
534 }
535
536 $account_warning = '';
537 $id = $this->get_id();
538 $description = $this->get_description();
539
540 if ($this->output_account_warning()) {
541 $account_warning = __('Ensure you are logged into the correct account before continuing.', 'updraftplus');
542 }
543
544 if ($template_instead_of_notice) {
545 $instance_id = "{{instance_id}}";
546 $text = sprintf(__("<strong>After</strong> you have saved your settings (by clicking 'Save Changes' below), then come back here once and click this link to complete authentication with %s.", 'updraftplus'), $description);
547 } else {
548 $instance_id = $this->get_instance_id();
549 $text = sprintf(__('Follow this link to authorize access to your %s account (you will not be able to backup to %s without it).', 'updraftplus'), $description, $description);
550 }
551
552 echo $account_warning . ' <a class="updraft_authlink" href="'.UpdraftPlus_Options::admin_page_url().'?&action=updraftmethod-'.$id.'-auth&page=updraftplus&updraftplus_'.$id.'auth=doit&updraftplus_instance='.$instance_id.'" data-instance_id="'.$instance_id.'" data-remote_method="'.$id.'">'.$text.'</a>';
553
554 if (!$echo_instead_of_return) {
555 return ob_get_clean();
556 }
557 }
558
559 /**
560 * Check the authentication is valid before proceeding to call the authentication method
561 */
562 public function action_authenticate_storage() {
563 if (isset($_GET['updraftplus_'.$this->get_id().'auth']) && 'doit' == $_GET['updraftplus_'.$this->get_id().'auth'] && !empty($_GET['updraftplus_instance'])) {
564 $this->authenticate_storage((string) $_GET['updraftplus_instance']);
565 }
566 }
567
568 /**
569 * Authenticate the remote storage and save settings
570 *
571 * @param String $instance_id - The remote storage instance id
572 */
573 public function authenticate_storage($instance_id) {
574 if (method_exists($this, 'do_authenticate_storage')) {
575 $this->do_authenticate_storage($instance_id);
576 } else {
577 error_log($this->get_id().": module does not have an authenticate storage method (coding bug)");
578 }
579 }
580
581 /**
582 * This method will either return or echo the constructed deauth link for the remote storage method
583 *
584 * @param boolean $echo_instead_of_return - a boolean to indicate if the deauthentication link should be echo or returned
585 * @return Void|String - returns a string or nothing depending on the parameters
586 */
587 public function get_deauthentication_link($echo_instead_of_return = true) {
588 if (!$echo_instead_of_return) {
589 ob_start();
590 }
591
592 $id = $this->get_id();
593 $description = $this->get_description();
594
595 echo ' <a class="updraft_deauthlink" href="'.UpdraftPlus_Options::admin_page_url().'?action=updraftmethod-'.$id.'-auth&page=updraftplus&updraftplus_'.$id.'auth=deauth&nonce='.wp_create_nonce($id.'_deauth_nonce').'&updraftplus_instance={{instance_id}}" data-instance_id="{{instance_id}}" data-remote_method="'.$id.'">'.sprintf(__("Follow this link to remove these settings for %s.", 'updraftplus'), $description).'</a>';
596
597 if (!$echo_instead_of_return) {
598 return ob_get_clean();
599 }
600 }
601
602 /**
603 * Check the deauthentication is valid before proceeding to call the deauthentication method
604 */
605 public function action_deauthenticate_storage() {
606 if (isset($_GET['updraftplus_'.$this->get_id().'auth']) && 'deauth' == $_GET['updraftplus_'.$this->get_id().'auth'] && !empty($_GET['nonce']) && !empty($_GET['updraftplus_instance']) && wp_verify_nonce($_GET['nonce'], $this->get_id().'_deauth_nonce')) {
607 $this->deauthenticate_storage($_GET['updraftplus_instance']);
608 }
609 }
610
611 /**
612 * Deauthenticate the remote storage and remove the saved settings
613 *
614 * @param String $instance_id - The remote storage instance id
615 */
616 public function deauthenticate_storage($instance_id) {
617 if (method_exists($this, 'do_deauthenticate_storage')) {
618 $this->do_deauthenticate_storage($instance_id);
619 }
620 $opts = $this->get_default_options();
621 $this->set_options($opts, true, $instance_id);
622 }
623
624 /**
625 * Over-ride this to allow methods to output extra information about using the correct account for OAuth storage methods
626 *
627 * @return Boolean - return false so that no extra information is output
628 */
629 public function output_account_warning() {
630 return false;
631 }
632
633 /**
634 * This function is a wrapper and will call $updraftplus->log(), the backup modules should use this so we can add information to the log lines to do with the remote storage and instance settings.
635 *
636 * @param string $line - the log line
637 * @param string $level - the log level: notice, warning, error. If suffixed with a hypen and a destination, then the default destination is changed too.
638 * @param boolean $uniq_id - each of these will only be logged once
639 * @param boolean $skip_dblog - if true, then do not write to the database
640 *
641 * @return void
642 */
643 public function log($line, $level = 'notice', $uniq_id = false, $skip_dblog = false) {
644 global $updraftplus;
645
646 $prefix = $this->get_storage_label();
647
648 $updraftplus->log("$prefix: $line", $level, $uniq_id = false, $skip_dblog = false);
649 }
650
651 /**
652 * This function will build and return the remote storage instance label
653 *
654 * @return string - the remote storage instance label
655 */
656 private function get_storage_label() {
657
658 $opts = $this->get_options();
659 $label = isset($opts['instance_label']) ? $opts['instance_label'] : '';
660
661 $description = $this->get_description();
662
663 if (!empty($label)) {
664 $prefix = (false !== strpos($label, $description)) ? $label : "$description: $label";
665 } else {
666 $prefix = $description;
667 }
668
669 return $prefix;
670 }
671 }
672