PluginProbe
UpdraftCentral Dashboard / 0.8.13
UpdraftCentral Dashboard v0.8.13
0.8.33 0.7.2 0.7.3 0.7.4 0.8.0 0.8.1 0.8.10 0.8.11 0.8.12 0.8.13 0.8.14 0.8.15 0.8.16 0.8.17 0.8.18 0.8.19 0.8.2 0.8.20 0.8.21 0.8.22 0.8.23 0.8.24 0.8.25 0.8.26 0.8.27 All 51 releases
updraftcentral / site-management.php

site-management.php in UpdraftCentral Dashboard 0.8.13, at site-management.php

1,632 lines 57.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 // @codingStandardsIgnoreStart
3 /*
4 Plugin Name: UpdraftCentral Dashboard
5 Plugin URI: https://updraftcentral.com
6 Description: Manage your WordPress sites from a central dashboard
7 Version: 0.8.13
8 Text Domain: updraftcentral
9 Domain Path: /languages
10 Author: David Anderson + Team Updraft
11 Author URI: https://www.simbahosting.co.uk/s3/shop/
12 Requires at least: 4.4
13 Tested up to: 5.3
14 License: MIT
15
16 Copyright: 2015- David Anderson
17 */
18 // @codingStandardsIgnoreEnd
19 if (!defined('ABSPATH')) die('Access denied.');
20
21 define('UD_CENTRAL_DIR', dirname(__FILE__));
22 define('UD_CENTRAL_URL', plugins_url('', __FILE__));
23
24 // The name of the plugin sub-directory/file.
25 define('UD_CENTRAL_PLUGIN_NAME', basename(UD_CENTRAL_DIR).'/'.basename(__FILE__));
26
27 if (!defined('UPDRAFTCENTRAL_TABLE_PREFIX')) define('UPDRAFTCENTRAL_TABLE_PREFIX', 'updraftcentral_');
28
29 if (!class_exists('UpdraftCentral')) :
30 class UpdraftCentral {
31 const VERSION = '0.8.13';
32
33 // Minimum PHP version required to run this plugin
34 const PHP_REQUIRED = '5.3';
35
36 // Minimum WP version required to run this plugin
37 const WP_REQUIRED = '4.4';
38
39 protected static $_instance = null;
40
41 protected static $_logger_instance = null;
42
43 // This gets filled from the constant, for more convenient access
44 public $version;
45
46 // An instance of UpdraftCentral_User
47 public $user;
48
49 // An instance of UpdraftCentral_Site_Meta
50 public $site_meta;
51
52 private $inited = false;
53
54 private $notices = array();
55
56 public $table_prefix;
57
58 private $template_directories;
59
60 private $semaphore;
61
62 public $export_settings_version = '1';
63
64 private $start_time;
65
66 /**
67 * Creates an instance of this class. Singleton Pattern
68 *
69 * @return object Instance of this class
70 */
71 public static function instance() {
72 if (empty(self::$_instance)) {
73 self::$_instance = new self();
74 }
75
76 return self::$_instance;
77 }
78
79 /**
80 * UpdraftCentral constructor.
81 *
82 * Does initial checks. Adds necessary hooks, filters and shortcode. Loads modules
83 *
84 * @return self
85 */
86 public function __construct() {
87
88 $this->version = self::VERSION;
89
90 // The shortcode will handle + provide output if running on an insufficient PHP/WP version; hence, this goes before the check/return.
91 add_shortcode('updraft_central', array($this, 'shortcode'));
92
93 if (version_compare(PHP_VERSION, self::PHP_REQUIRED, '<')) {
94 add_action('all_admin_notices', array($this, 'admin_notice_insufficient_php'));
95 $abort = true;
96 }
97
98 include ABSPATH.WPINC.'/version.php';
99 if (version_compare($wp_version, self::WP_REQUIRED, '<')) {
100 add_action('all_admin_notices', array($this, 'admin_notice_insufficient_wp'));
101 $abort = true;
102 }
103
104 if (!empty($abort)) return;
105
106 $this->table_prefix = defined('UPDRAFTCENTRAL_TABLE_PREFIX') ? UPDRAFTCENTRAL_TABLE_PREFIX : 'updraftcentral_';
107
108 add_action('plugins_loaded', array($this, 'plugins_loaded'));
109 add_action('init', array($this, 'wp_init'));
110 register_activation_hook(__FILE__, array($this, 'activation_hook'));
111 register_deactivation_hook(__FILE__, array($this, 'deactivation_hook'));
112
113 if (is_admin()) {
114 add_action('admin_menu', array($this, 'admin_menu'));
115 // Add settings link in plugin list
116 $plugin = plugin_basename(__FILE__);
117 add_filter('plugin_action_links_'.$plugin, array($this, 'plugin_action_links'));
118 add_filter('network_admin_plugin_action_links_'.$plugin, array($this, 'plugin_action_links'));
119 }
120
121 // Possibly redirect on login back to the UC dashboard
122 add_action('woocommerce_login_redirect', array($this, 'woocommerce_login_redirect'));
123
124 add_action('updraftcentral_print_dashboard_notices', array($this, 'print_dashboard_notices'));
125
126 if (!empty($_POST['updraftcentral_action']) && 'receive_key' == $_POST['updraftcentral_action']) {
127 add_action('init', array($this, 'init_updraftcentral_action_receive_key'));
128 }
129
130 add_action('wp_ajax_updraftcentral_dashboard_ajax', array($this, 'updraftcentral_dashboard_ajax'));
131
132 // Needed in Site Meta changes (do_action('updraftcentral_inited') is located within this file)
133 add_action('updraftcentral_inited', array($this, 'updraftcentral_add_site_metadata_filter'));
134
135 add_action('updraftcentral_cron', array($this, 'process_cron'));
136 add_action('shutdown', array($this, 'schedule_event'));
137
138 add_action('delete_user', array($this, 'delete_user'));
139
140 // Add our custom cron schedule:
141 add_filter('cron_schedules', array($this, 'add_custom_cron_schedules'));
142
143 // Add WP personal Exporter
144 add_filter('wp_privacy_personal_data_exporters', array($this, 'plugin_register_exporters'));
145
146 // Allow both bundled and external modules to hook into various parts of the dashboard
147 $this->load_modules();
148 }
149
150 /**
151 * Register the data exporter
152 *
153 * @param array $exporters
154 * @return array modified exporters
155 */
156 public function plugin_register_exporters($exporters) {
157 $exporters[] = array(
158 'exporter_friendly_name' => __('Export UpdraftCentral registered sites', 'updraftcentral'),
159 'callback' => array($this, 'export_registered_sites'),
160 );
161 return $exporters;
162 }
163
164 /**
165 * Runs upon the WP delete_user action. We then delete their data.
166 *
167 * @param Integer $user_id - user being deleted
168 */
169 public function delete_user($user_id) {
170 try {
171 $user = $this->get_user_object($user_id);
172 if (is_a($user, 'UpdraftCentral_User')) {
173 $user->delete_all_sites();
174 }
175 } catch (Exception $e) {
176 error_log("UpdraftCentral::delete_user($user_id): exception: ".get_class($e).": ".$e->getMessage());
177 // @codingStandardsIgnoreLine
178 } catch (Error $e) {
179 error_log("UpdraftCentral::delete_user($user_id): error: ".get_class($e).": ".$e->getMessage());
180 }
181 }
182
183 /**
184 * Inserts the custom "everyminute" schedule as cron option
185 *
186 * @param array $schedules Contains the current list of schedule options
187 * @return array
188 */
189 public function add_custom_cron_schedules($schedules) {
190 if (!isset($schedules['everyminute'])) {
191 $schedules['everyminute'] = array(
192 'interval' => 60,
193 'display' => __('Once every minute', 'updraftcentral')
194 );
195 }
196
197 return $schedules;
198 }
199
200 /**
201 * Initializes the semaphore object that will be used to lock
202 * the background data fetching process through cron
203 *
204 * @param string $lock_name The lock name to use for the current semaphore object
205 */
206 private function init_semaphore($lock_name) {
207 try {
208 if (empty($this->semaphore)) {
209 if (!class_exists('Updraft_Semaphore_1_0')) include_once UD_CENTRAL_DIR.'/classes/class-updraft-semaphore.php';
210
211 $this->semaphore = Updraft_Semaphore_1_0::factory();
212 $this->semaphore->lock_name = $lock_name;
213 $this->semaphore->ensure_semaphore_exists();
214 $this->semaphore->add_logger(self::get_logger());
215 }
216 } catch (Exception $e) {
217 UpdraftCentral()->log('UpdraftCentral: error initializing semaphore: '.$e->getMessage(), 'error');
218 }
219 }
220
221 /**
222 * Checks whether there are any available commands waiting to be process
223 *
224 * @return boolean - true when commands are available to process, false otherwise.
225 */
226 public function has_scheduled_commands() {
227 global $wp_filter;
228
229 if (empty($wp_filter['updraftcentral_scheduled_commands'])) return false;
230
231 return $wp_filter['updraftcentral_scheduled_commands']->has_filters();
232 }
233
234 /**
235 * Processes scheduled commands for the given user
236 *
237 * @param integer $user_id The current user ID that the command is associated with
238 *
239 * @return void
240 */
241 public function process_scheduled_commands($user_id) {
242 $user = $this->get_user_object($user_id);
243 if (!empty($user) && is_a($user, 'UpdraftCentral_User')) {
244
245 // Pull user sites
246 $sites = $user->load_user_sites();
247
248 // No point in continuing if the user currently don't have any sites to
249 // execute the commands.
250 if (empty($sites) || !is_array($sites)) return;
251
252 /**
253 * Commands when added using this filter should have the following structure:
254 *
255 * $scheduled_commands []= array(
256 * 'command' => ,
257 * 'data' => ,
258 * 'maximum_age' => ,
259 * 'is_long_running' =>
260 * );
261 */
262 $scheduled_commands = apply_filters('updraftcentral_scheduled_commands', array());
263 if (!empty($scheduled_commands)) {
264 $command_pipeline = array();
265 $short_commands = array();
266
267 // Default: 10 minutes (600 seconds) if UPDRAFTCENTRAL_DATA_MAXIMUM_AGE is not defined
268 $default_maximum_age = defined('UPDRAFTCENTRAL_DATA_MAXIMUM_AGE') ? UPDRAFTCENTRAL_DATA_MAXIMUM_AGE : 600;
269
270 foreach ($scheduled_commands as $task) {
271 $maximum_age = isset($task['maximum_age']) ? $task['maximum_age'] : $default_maximum_age;
272 $is_long_running = isset($task['is_long_running']) ? $task['is_long_running'] : false;
273
274 // Insert maximum_age (freshness of data) into the actual data parameter which will be
275 // referenced later in the subsequent process.
276 $task['data']['maximum_age'] = $maximum_age;
277
278 // If we don't have a valid command format then we continue with the next.
279 if (!preg_match('/^([a-z0-9]+)\.(.*)$/', $task['command'], $matches)) {
280 UpdraftCentral()->log('UpdraftCentral: "'.$task['command'].'" is not a valid command. Valid command format comes in the form of {command_class_identifier}.{command_action} (e.g. updates.get_updates, etc.)', 'error');
281 continue;
282 }
283
284 if (!$is_long_running) {
285 // We're preserving these non long running commands that will be wrapped
286 // and added as multiplexed commands later.
287 $short_commands[$task['command']] = $task['data'];
288 } elseif ($is_long_running) {
289 $command_pipeline[] = array(
290 'data' => array(
291 'command' => $task['command'],
292 'data' => $task['data']
293 )
294 );
295 }
296 }
297
298 // Add non long running commands into pipeline as multiplexed commands
299 if (!empty($short_commands)) {
300 $command_pipeline[] = array(
301 'data' => array(
302 'command' => 'core.execute_commands',
303 'data' => array(
304 'commands' => $short_commands,
305 'error_flag' => 3 /* Abort when all command fails - default */
306 )
307 )
308 );
309 }
310
311 if (!empty($command_pipeline)) {
312
313 // N.B. We temporarily store processed sites in the 'uc_cron_sites_processed' user meta
314 // in order not to re-processed them, especially when the "last_run" flag for the current user
315 // is not updated yet until all sites are processed.
316
317 $processed_sites = get_user_meta($user->user_id, 'uc_cron_sites_processed', true);
318 if (empty($processed_sites)) $processed_sites = array();
319
320 foreach ($sites as $site) {
321 // Additional check will ensure that we're not running the same process
322 // more than once with this current event run.
323 if (!in_array($site->site_id, $processed_sites)) {
324 // Compute for the elapsed time (in minutes) since we started.
325 $elapsed_time = (time() - $this->start_time) / 60;
326
327 // Maximum of 3 minutes (default) process time. If succeeding loop goes beyond the allowable
328 // time then the process will be terminated.
329 if ($elapsed_time > apply_filters('updraftcentral_max_process_time', 3)) break;
330
331 // Execute all available commands for the currently
332 // selected site
333 foreach ($command_pipeline as $data) {
334 $data['site_id'] = $site->site_id;
335
336 // Setting the "force_save" flag to true saves and caches the result to DB.
337 //
338 // N.B. This overrides the use of the "updraftcentral_cache_commands" filter where UDC only
339 // saves and cache response when any module add certain command(s) that they wish the response
340 // to be cached to DB. In this case, we're forcing the save since we're running it in cron.
341 $user->send_remote_command($data, true, array());
342 }
343
344 // By the time we reach here all commands for this particular site have been executed.
345 // Thus, we're going to add the site to the $processed_sites array. We cannot put this on top
346 // as we need to be sure that all commands are executed before we add this to the "$processed_sites" array.
347 array_push($processed_sites, $site->site_id);
348
349 // We need to set/update the 'uc_cron_sites_processed' user meta here since all process
350 // will be cut-off abruptly either after the max process time has expired (default 3 minutes)
351 // or the seamlock has been released therefore we need to set which sites have already been
352 // processed before the process is stopped so that other sites which are not yet processed
353 // will get a chance.
354 update_user_meta($user->user_id, 'uc_cron_sites_processed', $processed_sites);
355 }
356 }
357
358 // If the scheduled commands are executed to all user sites then we delete the user meta entry
359 // to give room for the next round of process (e.g. after 12 hours).
360 if (count($sites) == count($processed_sites)) {
361 delete_user_meta($user->user_id, 'uc_cron_sites_processed');
362 }
363 }
364 }
365 }
366 }
367
368 /**
369 * Process scheduled events (fetching UpdraftCentral data in the background) through cron
370 *
371 * @return void
372 */
373 public function process_cron() {
374
375 // Bypass processing if we don't have any existing commands
376 if (!$this->has_scheduled_commands()) return;
377
378 // Make sure we have a valid semaphore to work on by initializing it
379 // whenever needed or return if it was already been set.
380 $this->init_semaphore('updraftcentral_cron');
381
382 if (empty($this->semaphore)) {
383 UpdraftCentral()->log('Failed to initilize a semaphore object - exiting', 'error');
384 return;
385 }
386
387 $max_execution_time = ini_get('max_execution_time');
388
389 // Since the semaphore lock is for 3 minutes, we want to try to run for at least in that region; otherwise the queue may get longer whilst a lock is stuck. We go for 170 to allow a bit of margin for really slow database updates.
390 $time_limit = (defined('UPDRAFTCENTRAL_PROCESS_CRON_TIME_LIMIT') && UPDRAFTCENTRAL_PROCESS_CRON_TIME_LIMIT > 5) ? UPDRAFTCENTRAL_PROCESS_CRON_TIME_LIMIT : 170;
391 if ((defined('UPDRAFTCENTRAL_PROCESS_CRON_TIME_LIMIT') || $max_execution_time > 5) && $max_execution_time < $time_limit) set_time_limit($time_limit);
392
393 if (!$this->semaphore->lock()) {
394 UpdraftCentral()->log('Failed to gain semaphore lock ('.$this->semaphore->lock_name.') - An active background data fetching process appears to be running, if the other process crashed without removing the lock, then another can be started after 3 minutes)', 'error');
395 return;
396 }
397
398 try {
399
400 if (!class_exists('UpdraftCentral_User_Cron')) include_once UD_CENTRAL_DIR.'/classes/user-cron.php';
401 $user_cron = new UpdraftCentral_User_Cron();
402
403 $queue = $user_cron->get_process_queue();
404 if (!empty($queue)) {
405 $this->start_time = time();
406
407 foreach ($queue as $user_id) {
408 // Compute for the elapsed time (in minutes) since we started.
409 $elapsed_time = (time() - $this->start_time) / 60;
410
411 // Maximum of 3 minutes (default) process time. If succeeding loop goes beyond the allowable
412 // time then the process will be terminated.
413 if ($elapsed_time > apply_filters('updraftcentral_max_process_time', 3)) break;
414
415 // Process any scheduled commands (if there are any) for the given user.
416 $this->process_scheduled_commands($user_id);
417
418 // Update user's last (cron) run field
419 $user_cron->update_last_run($user_id);
420 }
421 }
422
423 } catch (Exception $e) {
424 UpdraftCentral()->log('UpdraftCentral: error when running cron event: '.$e->getMessage(), 'error');
425 }
426
427 // Release lock
428 $this->semaphore->unlock();
429 }
430
431 /**
432 * Initializes cron event for background data fetching
433 */
434 public function schedule_event() {
435 try {
436 $current_user = wp_get_current_user();
437 if (!function_exists('is_plugin_active')) include_once(ABSPATH.'wp-admin/includes/plugin.php');
438
439 // Make sure we have an entry in the "updraftcentral_user_cron" for the current user
440 // for executing the scheduled commands at a later time.
441 if (!class_exists('UpdraftCentral_User_Cron')) include_once UD_CENTRAL_DIR.'/classes/user-cron.php';
442 $user_cron = new UpdraftCentral_User_Cron();
443 $user_cron->maybe_insert_entry($current_user->ID);
444
445 // Register our cron if not yet registered and making sure that we're scheduling our event
446 // only if UpdraftCentral is active since we're clearing all events when this plugin is deactivated
447 // by the user, so that we won't be registering those already cleared events again by mistake.
448 //
449 // Updates: "updraftcentral_cron" is now global and runs accros the whole UpdraftCentral dashboard
450 // instead of associating it to the individual user.
451 if (!wp_next_scheduled('updraftcentral_cron') && is_plugin_active(UD_CENTRAL_PLUGIN_NAME)) {
452 wp_schedule_event(time(), 'everyminute', 'updraftcentral_cron');
453 }
454 } catch (Exception $e) {
455 UpdraftCentral()->log($e->getMessage(), 'error');
456 }
457 }
458
459
460 public function updraftcentral_add_site_metadata_filter() {
461 if (is_a($this->site_meta, 'UpdraftCentral_Site_Meta')) {
462 // We're short-circuiting the return by using our own implementation since we have a custom column "created"
463 // that would check for the maximum age of the content to return.
464 add_filter('get_site_metadata', array($this->site_meta, 'updraftcentral_get_site_metadata'), 10, 6);
465 add_filter('get_site_metadata_created', array($this->site_meta, 'updraftcentral_get_site_metadata_created'), 10, 3);
466 }
467 }
468
469 /**
470 * Compares installed and available version and takes necessary action
471 */
472 public function wp_init() {
473 include_once UD_CENTRAL_DIR.'/classes/activation.php';
474 UpdraftCentral_Activation::check_updates();
475 }
476
477 /**
478 * Prevents WooCommerce default redirection to My Accounts page
479 *
480 * @param string $redirect_to
481 * @return mixed
482 */
483 public function woocommerce_login_redirect($redirect_to) {
484 return empty($_POST['updraftcentral_redirect_on_wc_login']) ? $redirect_to : $_POST['updraftcentral_redirect_on_wc_login'];
485 }
486
487 /**
488 * Adds admin notice for insufficient php version.
489 *
490 * @return void
491 */
492 public function admin_notice_insufficient_php() {
493 $this->show_admin_warning('<strong>'.__('Higher PHP version required', 'updraftcentral').'</strong><br> '.sprintf(__('The %s plugin requires %s version %s or higher - your current version is only %s.', 'updraftcentral'), 'UpdraftCentral', 'PHP', self::PHP_REQUIRED, PHP_VERSION), 'error');
494 }
495
496 /**
497 * Adds admin notice for insufficient wp version
498 *
499 * @return void
500 */
501 public function admin_notice_insufficient_wp() {
502 include ABSPATH.WPINC.'/version.php';
503 $this->show_admin_warning('<strong>'.__('Higher WordPress version required', 'updraftcentral').'</strong><br> '.sprintf(__('The %s plugin requires %s version %s or higher - your current version is only %s.', 'updraftcentral'), 'UpdraftCentral', 'WordPress', self::WP_REQUIRED, $wp_version), 'error');
504 }
505
506 /**
507 * Warning message for admin notice
508 *
509 * @param string $message
510 * @param string $class
511 *
512 * @return void
513 */
514 public function show_admin_warning($message, $class = 'updated') {
515 echo '<div class="updraftcentral_message '.$class.'">'."<p>$message</p></div>";
516 }
517
518 /**
519 * Adds admin page for UpdraftCentral plugin
520 *
521 * @return void
522 */
523 public function admin_menu() {
524 add_menu_page('UpdraftCentral', 'UpdraftCentral', 'manage_options', 'updraft-central', array($this, 'wp_dashboard_page'), UD_CENTRAL_URL.'/images/dashicon.png', 56.8467664);
525 }
526
527 /**
528 * These are also available from JS via udclion.common_urls
529 *
530 * @return array
531 */
532 public function get_common_urls() {
533 return apply_filters('updraftcentral_common_urls', array(
534 'support_forum' => 'https://wordpress.org/support/plugin/updraftcentral',
535 'faqs' => 'https://updraftplus.com/updraftcentral-frequently-asked-questions/',
536 'idea_suggestion' => 'https://updraftplus.com/make-a-suggestion',
537 'first_link' => '<a href="http://updraftcentral.com">'.__('Home', 'updraftcentral').'</a>',
538 'how_to_install' => 'https://updraftplus.com/faqs/how-do-i-install-updraftcentral/',
539 'how_to_add_site' => 'https://updraftplus.com/updraftcentral-how-to-add-a-site/',
540 'paid_support' => 'https://updraftplus.com/paid-support-requests/',
541 'connection_checklist' => 'https://updraftplus.com/troubleshooting-updraftcentral-connection-issues/',
542 'connection_advanced_issues' => 'https://updraftplus.com/faqs/how-can-i-control-a-site-that-has-access-controls-e-g-brower-password-ip-address-restrictions/',
543 'get_licences' => false,
544 ));
545 }
546
547 /**
548 * Checks whether UpdraftCentral Premium is installed or not
549 *
550 * @param bool $also_require_active
551 * @return bool
552 */
553 public function is_premium_installed($also_require_active = false) {
554 if ($also_require_active) return class_exists('UpdraftCentral_Premium');
555 if (!function_exists('get_plugins')) include_once(ABSPATH.'wp-admin/includes/plugin.php');
556 $plugins = get_plugins();
557 $updraftcentral_premium_file = false;
558 foreach ($plugins as $key => $value) {
559 if ("updraftcentral-premium" == $value['TextDomain']) {
560 $updraftcentral_premium_file = $key;
561 break;
562 }
563 }
564 return $updraftcentral_premium_file ? true : false;
565 }
566
567 /**
568 * Includes dashboard page template and content
569 */
570 public function wp_dashboard_page() {
571 $extract_these = $this->get_common_urls();
572 $this->include_template('wp-admin/dashboard-page.php', false, $extract_these);
573 }
574
575 public function init_updraftcentral_action_receive_key() {
576 // @codingStandardsIgnoreLine
577 @header('Content-Type: application/json');
578
579 $response_array = array(
580 'mothership' => 'thatsus',
581 'mothership_info' => array('version' => self::VERSION),
582 );
583
584 if (empty($_POST['key'])) {
585 $response_array['code'] = 'key_invalid';
586 $response_array['message'] = 'Necessary data was not supplied';
587 } else {
588
589 global $wpdb;
590 $ud_rpc = $this->get_udrpc('central_host.updraftplus.com');
591
592 // Normally, key generation takes seconds, even on a slow machine. However, some Windows machines appear to have a setup in which it takes a minute or more. And then, if you're on a double-localhost setup on slow hardware - even worse. It doesn't hurt to just raise the maximum execution time.
593
594 $key_generation_time_limit = (defined('UPDRAFTCENTRAL_SET_TIME_LIMIT') && is_numeric(UPDRAFTCENTRAL_SET_TIME_LIMIT) && UPDRAFTCENTRAL_SET_TIME_LIMIT > 10) ? UPDRAFTCENTRAL_SET_TIME_LIMIT : 900;
595
596 // @codingStandardsIgnoreLine
597 @set_time_limit($key_generation_time_limit);
598
599 if (false != $ud_rpc->generate_new_keypair()) {
600 $response_array['key_public'] = $ud_rpc->get_key_remote();
601 $inserted = $this->wp_insert('site_temporary_keys', array('key_remote_public' => $_POST['key'], 'key_local_private' => $ud_rpc->get_key_local(), 'created' => time()), array('%s', '%s', '%d'));
602 if ($inserted) {
603 $response_array['key_id'] = $wpdb->insert_id;
604 } else {
605 $response_array['code'] = 'insert_error';
606 $response_array['message'] = 'A database error occurred when attempting to load the key';
607 }
608 } else {
609 $response_array['code'] = 'keygen_error';
610 $response_array['message'] = 'An error occurred when attempting to generate a new key-pair';
611 }
612
613 }
614 echo json_encode($response_array);
615 die;
616 }
617
618 /**
619 * Handles all ajax requests to this plugin.
620 *
621 * This single method handles and delegates all ajax requests in this plugin.
622 * By doing this, there is no need to add `wp_ajax_{your_action}` hook for every single actions
623 *
624 * @return void
625 */
626 public function updraftcentral_dashboard_ajax() {
627 if (empty($_REQUEST['subaction']) || empty($_REQUEST['nonce']) || empty($_REQUEST['component']) || !wp_verify_nonce($_REQUEST['nonce'], 'updraftcentral_dashboard_nonce')) die('Security check');
628
629 if ('dashboard' != $_REQUEST['component']) die;
630
631 $response = array();
632
633 if (!$this->init()) {
634 $response = array('responsetype' => 'error', 'code' => 'init_failure', 'message' => __('Error:', 'updraftcentral').' '.__('failed to initialise', 'updraftcentral'));
635 } else {
636
637 $post_data = stripslashes_deep($_POST);
638
639 if (isset($post_data['data']) && is_array($post_data['data']) && isset($post_data['data']['site_id'])) {
640 $site_id = (int) $post_data['data']['site_id'];
641 if (!in_array($site_id, array_keys($this->user->sites))) {
642 $response = array('responsetype' => 'error', 'code' => 'unauthorised', 'message' => __('Error:', 'updraftcentral').' '.__('you are not authorized to access this site', 'updraftcentral'));
643 }
644 }
645
646 // Remember, if doing any processing here, that the site has not yet been checked as to whether it is licenced
647
648 // Any data will be in $_REQUEST['data'];
649 switch ($_REQUEST['subaction']) {
650 default:
651 $response = apply_filters('updraftcentral_dashboard_ajaxaction_'.$_REQUEST['subaction'], $response, $post_data);
652 break;
653 }
654
655 }
656
657 if (empty($response)) {
658 $response['responsetype'] = 'error';
659 $response['code'] = 'empty';
660 $response['message'] = __('Error:', 'updraftcentral').' '.sprintf(__('This action (%s) could not be handled', 'updraftcentral'), $_REQUEST['subaction']);
661 } elseif (is_wp_error($response)) {
662 $new_response = array(
663 'responsetype' => 'error',
664 'code' => $response->get_error_code(),
665 'message' => __('Error:', 'updraftcentral').' '.$response->get_error_message().' ('.$response->get_error_code().')',
666 'data' => $response->get_error_data(),
667 );
668 $response = $new_response;
669 }
670
671 echo json_encode($response);
672
673 die;
674
675 }
676
677 /**
678 * This is shortcode function for [updraft_central] checks the user's access level, and then dispatches to the appropriate page in the /pages sub-directory
679 *
680 * @param array $atts
681 * @return string
682 */
683 public function shortcode($atts) {
684
685 // Short-circuit plugins that run do_shortcode out-of-context (e.g. Relevansii)
686 if (is_admin()) return '';
687
688 if (version_compare(PHP_VERSION, self::PHP_REQUIRED, '<')) {
689 return sprintf(__('The %s plugin requires %s version %s or higher - your current version is only %s.', 'updraftcentral'), 'UpdraftCentral', 'PHP', self::PHP_REQUIRED, PHP_VERSION);
690 }
691 include ABSPATH.WPINC.'/version.php';
692 if (version_compare($wp_version, self::WP_REQUIRED, '<')) {
693 return sprintf(__('The %s plugin requires %s version %s or higher - your current version is only %s.', 'updraftcentral'), 'UpdraftCentral', 'WordPress', self::WP_REQUIRED, $wp_version);
694 }
695
696 $atts = shortcode_atts(array(
697 'page' => 'dashboard',
698 'require_role' => 'administrator',
699 'require_cap' => false,
700 ), $atts, 'updraft_central');
701
702 // Security check - only valid characters
703 if (!preg_match('/^[_a-z]+$/', $atts['page'])) return;
704
705 ob_start();
706
707 if (!is_user_logged_in()) {
708 $this->include_template('dashboard/not-logged-in.php');
709 } else {
710
711 add_action('wp_footer', array($this, 'wp_footer'));
712
713 if (!$this->init()) {
714
715 // We want the notice styles
716 $this->load_dashboard_css();
717
718 // Get the header, which invokes the notice-printing actions
719 $this->include_template('dashboard/header.php');
720
721 // Print this, in case for some reason the notices don't display (or there weren't any)
722 echo 'Setup error';
723
724 } else {
725
726 // Check they are a customer (i.e. have customer role)
727
728 $require_role = empty($atts['require_role']) ? false : explode(',', str_replace(' ', '', $atts['require_role']));
729 $require_cap = empty($atts['require_cap']) ? false : explode(',', str_replace(' ', '', $atts['require_cap']));
730
731 if (!$this->access_role_check($atts['page'], $require_role, $require_cap)) {
732 $this->include_template('dashboard/not-authorised.php');
733 } elseif ('dashboard' == $atts['page']) {
734 include_once UD_CENTRAL_DIR.'/pages/dashboard.php';
735 }
736
737 }
738
739 }
740
741 return ob_get_clean();
742 }
743
744 /**
745 * Includes all module loader files from modules folder.
746 *
747 * @return void
748 */
749 private function load_modules() {
750
751 do_action('updraftcentral_load_modules');
752
753 if (is_dir(UD_CENTRAL_DIR.'/modules') && $dir_handle = opendir(UD_CENTRAL_DIR.'/modules')) {
754 while (false !== ($e = readdir($dir_handle))) {
755 if (is_dir(UD_CENTRAL_DIR.'/modules/'.$e) && file_exists(UD_CENTRAL_DIR.'/modules/'.$e.'/loader.php') && apply_filters('updraftcentral_load_module', true, $e, UD_CENTRAL_DIR.'/modules')) {
756 include_once UD_CENTRAL_DIR.'/modules/'.$e.'/loader.php';
757 }
758 }
759 // @codingStandardsIgnoreLine
760 @closedir($dir_handle);
761 }
762
763 do_action('updraftcentral_loaded_modules');
764
765 }
766
767 /**
768 * Sorts navigation items
769 *
770 * @param string $a - Navigation item to compare
771 * @param string $b - Navigation item to compare
772 * @return int - Result of comparison
773 */
774 public function sort_navigation_items($a, $b) {
775 if (!is_array($a) || !isset($a['sort_order']) || !is_numeric($a['sort_order'])) return 1;
776 if (!is_array($b) || !isset($b['sort_order']) || !is_numeric($b['sort_order'])) return -1;
777 if ($a['sort_order'] < $b['sort_order']) return -1;
778 if ($a['sort_order'] > $b['sort_order']) return 1;
779
780 return 0;
781 }
782
783 /**
784 * Register and enqueues needed js files
785 *
786 * @retun void
787 */
788 public function load_dashboard_js() {
789 // @codingStandardsIgnoreLine
790 $enqueue_version = @constant('WP_DEBUG') ? self::VERSION.'.'.time() : self::VERSION;
791
792 $min_or_not = (defined('SCRIPT_DEBUG') && SCRIPT_DEBUG) ? '' : '.min';
793
794 // https://github.com/alexei/sprintf.js
795 wp_register_script('sprintf', UD_CENTRAL_URL.'/js/sprintf/sprintf'.$min_or_not.'.js', array(), '20151204');
796
797 // https://github.com/digitalbazaar/forge
798 wp_register_script('forge', UD_CENTRAL_URL.'/js/forge-js/forge.min.js', array(), '0.7.0');
799
800 wp_register_script('class-udrpc', UD_CENTRAL_URL.'/js/class-udrpc'.$min_or_not.'.js', array('forge'), '0.3.3');
801
802 /*
803 // https://github.com/google/caja/
804 wp_register_script('caja-html4-defs', UD_CENTRAL_URL.'/js/caja/html4-defs.js', array(), '20151215');
805 wp_register_script('caja-uri', UD_CENTRAL_URL.'/js/caja/uri.js', array(), '20151215');
806 wp_register_script('google-caja-sanitizer', UD_CENTRAL_URL.'/js/caja/sanitizer.js', array('caja-html4-defs', 'caja-uri'), '20151215');
807 */
808
809 wp_register_script('tether', UD_CENTRAL_URL.'/js/tether/tether'.$min_or_not.'.js', array(), '1.4.0');
810
811 wp_register_script('popperjs', UD_CENTRAL_URL.'/js/popper.js/popper'.$min_or_not.'.js', array(), '1.16.1');
812
813 // starting from version 4.0.0, Bootstrap components (tooltips & dropdowns) require Popper JS
814 // https://cdn.rawgit.com/twbs/bootstrap/v4-dev/dist/js/bootstrap(.min).js
815 wp_register_script('bootstrap4', UD_CENTRAL_URL.'/js/bootstrap/bootstrap'.$min_or_not.'.js', array('jquery', 'tether'), '4.4.1');
816
817 // https://github.com/makeusabrew/bootbox/releases/download/v(version)/bootbox(.min).js / http://bootboxjs.com/#download
818 wp_register_script('bootbox', UD_CENTRAL_URL.'/js/bootbox/bootbox'.$min_or_not.'.js', array('bootstrap4'), '5.4.0');
819
820 // https://github.com/iyogeshjoshi/google-caja-sanitizer/
821 wp_register_script('google-caja-sanitizer', UD_CENTRAL_URL.'/js/caja/sanitizer'.$min_or_not.'.js', array(), '20150315');
822
823 // Handlebars - http://www.handlebarsjs.com - https://github.com/wycats/handlebars.js
824 // The "run-time" build handles only pre-compiled templates
825 // Visit http://builds.handlebarsjs.com.s3.amazonaws.com/bucket-listing.html?sort=lastmod&sortdir=desc to spot the Git ID for new versions
826 // http://builds.handlebarsjs.com.s3.amazonaws.com/handlebars.runtime.min-(Git ID).js - then rename it to match the non-minified version
827 // http://builds.handlebarsjs.com.s3.amazonaws.com/handlebars.runtime-v(version).js
828 // Rename them all to get rid of the version number from the file - ever-shifting filenames irritate when managing within SVN
829 // We used to put the handlebars version here - but then it needed manual updating, which didn't happen, and would result in problems where the old version was cached.
830 wp_register_script('handlebars', UD_CENTRAL_URL.'/js/handlebars/handlebars'.$min_or_not.'.js', array(), $enqueue_version);
831
832 // https://github.com/private-face/jquery.fullscreen
833 wp_register_script('jquery-fullscreen', UD_CENTRAL_URL.'/js/jquery-fullscreen/jquery.fullscreen'.$min_or_not.'.js', array('jquery'), '0.5.1');
834 wp_register_script('modernizr-custom', UD_CENTRAL_URL.'/js/modernizr/modernizr-custom'.$min_or_not.'.js', array(), '3.3.1');
835
836 wp_register_script('updraftcentral-queue', UD_CENTRAL_URL.'/js/queue.js', array(), $enqueue_version);
837 wp_register_script('d3-queue', UD_CENTRAL_URL.'/js/d3-queue/d3-queue'.$min_or_not.'.js', array(), '3.0.3');
838
839 $library_deps = array('jquery', 'jquery-fullscreen', 'sprintf', 'google-caja-sanitizer', 'bootbox', 'handlebars', 'forge');
840 wp_register_script('uc-library', UD_CENTRAL_URL.'/js/uc-library'.$min_or_not.'.js', $library_deps, $enqueue_version);
841
842 $dashboard_deps = array('jquery', 'popperjs', 'bootbox', 'jquery-fullscreen', 'sprintf', 'class-udrpc', 'google-caja-sanitizer', 'handlebars', 'modernizr-custom', 'updraftcentral-queue', 'd3-queue', 'uc-library', 'jquery-ui-sortable');
843
844 include ABSPATH.WPINC.'/version.php';
845 global $wpdb;
846
847 if (function_exists('curl_version')) {
848 $curl_version = curl_version();
849 $curl_version = $curl_version['version'];
850 if (!function_exists('curl_exec')) $curl_version .= '/Disabled';
851 } else {
852 $curl_version = '-';
853 }
854
855 $pass_to_js = array(
856 'udc_version' => self::VERSION,
857 'php_version' => PHP_VERSION,
858 'wp_version' => $wp_version,
859 'mysql_version' => $wpdb->db_version(),
860 'curl_version' => $curl_version,
861 'home_url' => home_url(),
862 'handlebars' => $this->get_handlebars_data(),
863 'show_licence_counts' => apply_filters('updraftcentral_show_licence_counts', false),
864 'user_defined_timeout' => $this->user->get_user_defined_timeout(),
865 'shortcut_status' => $this->user->get_keyboard_shortcut_status(),
866 );
867
868 if (!empty($pass_to_js['handlebars']['enqueue'])) {
869 $dashboard_deps[] = 'updraftcentral-handlebars-compiled';
870 wp_register_script('updraftcentral-handlebars-compiled', $pass_to_js['handlebars']['enqueue']['url'], array('handlebars'), filemtime($pass_to_js['handlebars']['enqueue']['file']));
871 unset($pass_to_js['handlebars']['enqueue']);
872 }
873
874 // Our dashboard framework
875 wp_register_script('updraftcentral-dashboard', UD_CENTRAL_URL.'/js/dashboard'.$min_or_not.'.js', $dashboard_deps, $enqueue_version);
876
877 $localize = array_merge(
878 array('common_urls' => $this->get_common_urls()),
879 $pass_to_js,
880 include(UD_CENTRAL_DIR.'/dashboard-translations.php'),
881 include(UD_CENTRAL_DIR.'/keyboard-mappings.php')
882 );
883
884 wp_localize_script('updraftcentral-dashboard', 'udclion', apply_filters('updraftcentral_udrclion', $localize));
885
886 do_action('updraftcentral_load_dashboard_js', $enqueue_version);
887
888 wp_enqueue_script('jquery-ui-tabs', array('jquery', 'jquery-ui'));
889 }
890
891 /**
892 * Gets template directory
893 *
894 * @return mixed
895 */
896 public function get_templates_dir() {
897 return apply_filters('updraftcentral_templates_dir', wp_normalize_path(UD_CENTRAL_DIR.'/templates'));
898 }
899
900 /**
901 * Gets templates URL
902 *
903 * @return mixed
904 */
905 public function get_templates_url() {
906 return apply_filters('updraftcentral_templates_url', UD_CENTRAL_URL.'/templates');
907 }
908
909 /**
910 * Stores all template directories in an array
911 *
912 * @return void
913 */
914 private function register_template_directories() {
915
916 $template_directories = array();
917
918 $templates_dir = $this->get_templates_dir();
919
920 if ($dh = opendir($templates_dir)) {
921 while (($file = readdir($dh)) !== false) {
922 if ('.' == $file || '..' == $file) continue;
923 if (is_dir($templates_dir.'/'.$file)) {
924 $template_directories[$file] = $templates_dir.'/'.$file;
925 }
926 }
927 closedir($dh);
928 }
929
930 // This is the optimal hook for most extensions to hook into
931 $this->template_directories = apply_filters('updraftcentral_template_directories', $template_directories);
932
933 }
934
935 /**
936 * Compile and return all handlebars templates
937 *
938 * @return array A list of handlebars templates to compile and/or scripts to enqueue, plus a base URL
939 */
940 private function get_handlebars_data() {
941
942 $templates_dir = $this->get_templates_dir();
943 $templates_url = $this->get_templates_url();
944
945 $handlebars = array('base' => $templates_url);
946
947 $handlebars_compile = array();
948
949 $compiled_file = apply_filters('updraftcentral_handlebars_templates_compiled_file', $templates_dir.'/handlebars-compiled.js');
950
951 if ((!defined('UPDRAFTCENTRAL_DEV_ENVIRONMENT') || !UPDRAFTCENTRAL_DEV_ENVIRONMENT) && file_exists($compiled_file)) {
952 $handlebars['enqueue'] = apply_filters('updraftcentral_handlebars_templates_enqueue', array('url' => $templates_url.'/handlebars-compiled.js', 'file' => $compiled_file));
953 }
954
955 if ((defined('UPDRAFTCENTRAL_DEV_ENVIRONMENT') && UPDRAFTCENTRAL_DEV_ENVIRONMENT) || !file_exists($compiled_file)) {
956 foreach ($this->template_directories as $prefix => $directory) {
957 $templates = $this->register_handlebars_templates($prefix, $directory);
958 $handlebars_compile = array_merge($handlebars_compile, $templates);
959 }
960 }
961
962 $handlebars['compile'] = apply_filters('updraftcentral_handlebars_compile', $handlebars_compile);
963
964 return $handlebars;
965 }
966
967 /**
968 * This method is for telling UpdraftCentral to recursively scan the indicated directory, and to add all found handlebars templates (i.e. ones ending in .handlebars.html) to the list of templates to (potentially) compile
969 *
970 * @param string $prefix
971 * @param string $directory
972 * @return array
973 */
974 public function register_handlebars_templates($prefix, $directory) {
975
976 $handlebars_compile = array();
977
978 try {
979
980 $directory_iterator = new RecursiveDirectoryIterator($directory);
981 $iterator = new RecursiveIteratorIterator($directory_iterator);
982 $regex = new RegexIterator($iterator, '/^.+\.handlebars\.html$/i', RecursiveRegexIterator::GET_MATCH);
983
984 foreach ($regex as $files) {
985 foreach ($files as $file) {
986 $basename = substr($file, 1 + strlen($directory));
987 $template_name = $prefix.'-'.str_replace('/', '-', wp_normalize_path(substr($basename, 0, -strlen('.handlebars.html'))));
988 $get_template_from = apply_filters('updraftcentral_handlebars_template_file', $file, $template_name);
989 $handlebars_compile[$template_name] = apply_filters('updraftcentral_handlebars_template_contents', file_get_contents($get_template_from), $template_name, $get_template_from);
990 }
991
992 }
993 } catch (Exception $e) {
994 error_log('UpdraftCentral: error when scanning for handlebars templates: '.$e->getMessage());
995 }
996
997 return $handlebars_compile;
998 }
999
1000 /**
1001 * Enqueues required stylesheets
1002 *
1003 * @return void
1004 */
1005 public function load_dashboard_css() {
1006 // @codingStandardsIgnoreLine
1007 $enqueue_version = @constant('WP_DEBUG') ? self::VERSION.'.'.time() : self::VERSION;
1008
1009 wp_enqueue_style('updraftcentral-dashboard-css', UD_CENTRAL_URL.'/css/dashboard.css', array('dashicons'), $enqueue_version);
1010
1011 wp_enqueue_style('updraftcentral-mobile-css', UD_CENTRAL_URL.'/css/mobile.css', array('updraftcentral-dashboard-css'), $enqueue_version);
1012
1013 // Temporary file for developers to add CSS in without stepping on the toes of people working on styling
1014 wp_enqueue_style('updraftcentral-dashboard-temp-css', UD_CENTRAL_URL.'/css/dashboard-temp.css', array('updraftcentral-dashboard-css'), $enqueue_version);
1015
1016 // Loads Google fonts.
1017 wp_enqueue_style('updraftcentral-google-fonts-source-sans-pro', 'https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,600,700', $enqueue_version);
1018
1019 // Temp new design
1020 wp_enqueue_style('updraftcentral-new-design', UD_CENTRAL_URL.'/css/dashboard-design-temp.css', array('updraftcentral-dashboard-css'), $enqueue_version);
1021
1022 do_action('updraftcentral_load_dashboard_css', $enqueue_version);
1023
1024 }
1025
1026 /**
1027 * Enqueues Bootstrap stylesheet and dequeues old PageLines
1028 *
1029 * @return void
1030 */
1031 public function wp_footer() {
1032 // @codingStandardsIgnoreLine
1033 $bootstrap_file = @constant('SCRIPT_DEBUG') ? 'bootstrap' : 'bootstrap.min';
1034 // https://cdn.rawgit.com/twbs/bootstrap/v4-dev/dist/css/$bootstrap_file
1035 wp_enqueue_style('bootstrap4', UD_CENTRAL_URL."/css/bootstrap/${bootstrap_file}.css", array(), '4.4.1');
1036
1037 // Old testing sandbox used PageLines; no longer; but perhaps someone else will be doing
1038 wp_dequeue_script('pagelines-bootstrap-all');
1039
1040 }
1041
1042 /**
1043 * Sets up text domain for plugin upon the WordPress action plugins_loaded
1044 *
1045 * @return void
1046 */
1047 public function plugins_loaded() {
1048 load_plugin_textdomain('updraftcentral', false, basename(UD_CENTRAL_DIR).'/languages');
1049 }
1050
1051 /**
1052 * Verify whether the currently logged-in WP user is allowed to access the specified page (or any pages)
1053 *
1054 * @param Boolean|String $page - The page
1055 * @param Boolean|String $require_role - Require the user to have a particular role
1056 * @param Boolean $require_cap - Require the user to have a particular capability
1057 *
1058 * @return Boolean - the result
1059 */
1060 public function access_role_check($page = false, $require_role = false, $require_cap = false) {
1061
1062 $current_user = wp_get_current_user();
1063 $user_roles = $current_user->roles;
1064
1065 $allowed = false;
1066
1067 if (is_super_admin()) $allowed = true;
1068
1069 // With this option, *any* matching role grants access
1070 if (is_array($require_role) && !empty($require_role)) {
1071 foreach ($require_role as $rr) {
1072 if (in_array(trim(strtolower($rr)), $user_roles)) {
1073 $allowed = true;
1074 break;
1075 }
1076 }
1077 } else {
1078 $allowed = true;
1079 }
1080
1081 // With this option, *all* specified capabilities are required
1082 if ($allowed && is_array($require_cap) && !empty($require_cap)) {
1083 foreach ($require_cap as $rc) {
1084 if (!current_user_can($rc)) {
1085 $allowed = false;
1086 break;
1087 }
1088 }
1089 }
1090
1091 return apply_filters('updraftcentral_access_role_check', $allowed, $current_user, $page, $require_role, $require_cap);
1092 }
1093
1094 /**
1095 * Runs upon plugin activation to check requirements and create tables
1096 *
1097 * @return void
1098 */
1099 public function activation_hook() {
1100 include_once UD_CENTRAL_DIR.'/classes/activation.php';
1101 UpdraftCentral_Activation::install();
1102 }
1103
1104 /**
1105 * Runs upon plugin deactivation to do housekeeping or cleanup
1106 *
1107 * @return void
1108 */
1109 public function deactivation_hook() {
1110 if (!function_exists('_get_cron_array')) include ABSPATH.WPINC.'/cron.php';
1111
1112 $crons = _get_cron_array();
1113 if (!empty($crons)) {
1114 foreach ($crons as $timestamp => $cron) {
1115 foreach ($cron as $key => $value) {
1116 if (preg_match('#^updraftcentral#', $key)) {
1117 foreach ($value as $serialized_key => $schedule) {
1118 wp_clear_scheduled_hook($key, $schedule['args']);
1119 }
1120 }
1121 }
1122 }
1123 }
1124 }
1125
1126 /**
1127 * Stores notices in an array
1128 *
1129 * @param string $content Notice message to log
1130 * @param string $level Notice level e.g. error
1131 * @param bool $unique_id Unique ID of notice message
1132 * @param array $options Options for notice message. eg. can close?
1133 * @return void
1134 */
1135 public function log_notice($content, $level = 'error', $unique_id = false, $options = array()) {
1136 if (apply_filters('updraftcentral_log_notice', true, $content, $level, $unique_id)) {
1137
1138 $defaults = array(
1139 'show_dismiss' => 'true'
1140 );
1141
1142 $options = wp_parse_args($options, $defaults);
1143
1144 $log_this = apply_filters('updraftcentral_log_notice_content', array('level' => $level, 'content' => $content, 'options' => $options), $level, $unique_id);
1145
1146 if ($unique_id) {
1147 $this->notices[$unique_id] = $log_this;
1148 } else {
1149 $this->notices[] = $log_this;
1150 }
1151 }
1152 }
1153
1154 /**
1155 * Remove a previously added notice
1156 *
1157 * @param string $notice_id
1158 * @return void
1159 */
1160 public function remove_notice($notice_id) {
1161 if (isset($this->notices[$notice_id])) {
1162 unset($this->notices[$notice_id]);
1163 }
1164 }
1165
1166 /**
1167 * Includes a notices template and displays logged notices using that template
1168 *
1169 * @return void
1170 */
1171 public function print_dashboard_notices() {
1172 // We only want one notice area
1173 static $printed_notice_container = false;
1174 if ($printed_notice_container) return;
1175 $this->include_template('dashboard/notices.php', false, array('notices' => $this->notices));
1176 $printed_notice_container = true;
1177 }
1178
1179 /**
1180 * Initalizes plugin functionality
1181 *
1182 * Sets up an array of template directories to use, current user
1183 * Includes Site meta and options class
1184 *
1185 * @return bool Returns true upon successful initialisation, else false.
1186 */
1187 private function init() {
1188 if ($this->inited) return true;
1189
1190 if (!is_user_logged_in()) return false;
1191
1192 $this->register_template_directories();
1193
1194 $user = wp_get_current_user();
1195
1196 try {
1197 $this->user = $this->get_user_object($user->ID);
1198 } catch (Exception $e) {
1199 $failure = true;
1200 $this->log_notice($e->getMessage().' ('.get_class($e).')', 'error');
1201 }
1202
1203 if (!class_exists('UpdraftCentral_Site_Meta')) include_once UD_CENTRAL_DIR.'/classes/site-meta.php';
1204
1205 if (!class_exists('UpdraftCentral_Options')) include_once UD_CENTRAL_DIR.'/classes/updraftcentral-options.php';
1206
1207 try {
1208 $this->site_meta = new UpdraftCentral_Site_Meta($this->table_prefix);
1209 } catch (Exception $e) {
1210 $failure = true;
1211 $this->log_notice($e->getMessage().' ('.get_class($e).')', 'error');
1212 }
1213
1214 if (empty($failure)) {
1215 $this->inited = true;
1216 do_action('updraftcentral_inited');
1217 }
1218
1219 return $this->inited;
1220 }
1221
1222 /**
1223 * Get user object based on id
1224 *
1225 * @param int $user_id - User ID
1226 * @return UpdraftCentral_User - UpdraftCentral user object
1227 */
1228 public function get_user_object($user_id) {
1229 if (!class_exists('UpdraftCentral_User')) include_once UD_CENTRAL_DIR.'/classes/user.php';
1230
1231 return new UpdraftCentral_User($user_id);
1232 }
1233
1234 /**
1235 * Gets an RPC object, and sets some defaults on it that we always want
1236 *
1237 * @param string $indicator_name
1238 * @return UpdraftPlus_Remote_Communications
1239 */
1240 public function get_udrpc($indicator_name = 'central.updraftplus.com') {
1241 // Include composer autoload.php to get libraries
1242 include_once UD_CENTRAL_DIR.'/vendor/autoload.php';
1243
1244 // Check if UpdraftPlus_Remote_Communications is present before including class-udrpc.php
1245 if (!class_exists('UpdraftPlus_Remote_Communications')) include_once UD_CENTRAL_DIR.'/classes/class-udrpc.php';
1246
1247 $ud_rpc = new UpdraftPlus_Remote_Communications($indicator_name);
1248 $ud_rpc->set_can_generate(true);
1249
1250 return $ud_rpc;
1251 }
1252
1253 /**
1254 * Adds settings and UDC site links to plugin links in WordPress Dashboard Plugins Page
1255 *
1256 * @param string $links - Existing links to be used in plugin dashboard page
1257 * @return mixed $links - Updated links array
1258 */
1259 public function plugin_action_links($links) {
1260
1261 $link = '<a href="'.admin_url('admin.php?page=updraft-central').'">'.__('Settings', 'updraftcentral').'</a>';
1262 array_unshift($links, $link);
1263
1264 $link2 = '<a href="http://updraftcentral.com">'.__('UpdraftCentral website', 'updraftcentral').'</a>';
1265
1266 array_unshift($links, $link2);
1267
1268 return $links;
1269 }
1270
1271 /**
1272 * Includes templates from mentioned path. Either returns it or echos it
1273 *
1274 * @param string $path - Path of template file to be included
1275 * @param bool $return_instead_of_echo - Option to echo or return template
1276 * @param array $extract_these - Data available to template
1277 * @return string
1278 */
1279 public function include_template($path, $return_instead_of_echo = false, $extract_these = array()) {
1280 if ($return_instead_of_echo) ob_start();
1281
1282 if (preg_match('#^([^/]+)/(.*)$#', $path, $matches)) {
1283 $prefix = $matches[1];
1284 $suffix = $matches[2];
1285 if (isset($this->template_directories[$prefix])) {
1286 $template_file = $this->template_directories[$prefix].'/'.$suffix;
1287 }
1288 }
1289
1290 // @codingStandardsIgnoreStart
1291 // Not yet used
1292 // public function wp_get_var($table, $where) {
1293 // global $wpdb;
1294 // $var = $wpdb->get_var("SELECT * FROM ".$wpdb->base_prefix.$this->table_prefix.$table." WHERE ".$where);
1295 // if (null === $var && !empty($wpdb->last_error)) return new WP_Error('database_getvar_error', $wpdb->last_error);
1296 // return $var;
1297 // }
1298 // @codingStandardsIgnoreEnd
1299
1300 if (!isset($template_file)) {
1301 $template_file = UD_CENTRAL_DIR.'/templates/'.$path;
1302 }
1303
1304 $template_file = apply_filters('updraftcentral_template', $template_file, $path);
1305
1306 do_action('updraftcentral_before_template', $path, $template_file, $return_instead_of_echo, $extract_these);
1307
1308 if (!file_exists($template_file)) {
1309 error_log("UpdraftCentral: template not found: $template_file");
1310 echo __('Error:', 'updraftcentral').' '.__('template not found', 'updraftcentral')." ($path)";
1311 } else {
1312 extract($extract_these);
1313 $updraft_central = $this;
1314 include $template_file;
1315 }
1316
1317 do_action('updraftcentral_after_template', $path, $template_file, $return_instead_of_echo, $extract_these);
1318
1319 if ($return_instead_of_echo) return ob_get_clean();
1320 }
1321
1322 /**
1323 * Selects a row of data and returns it
1324 *
1325 * @see WPDB::get_row()
1326 * @param string $table - Table name to be retrieved
1327 * @param string $where - Condition to match with retrieved data
1328 * @return array | WP_Error - Array of retrieved row data or a WP_Error object
1329 */
1330 public function wp_get_row($table, $where) {
1331 global $wpdb;
1332 $row = $wpdb->get_row('SELECT * FROM '.$wpdb->base_prefix.$this->table_prefix.$table.' WHERE '.$where);
1333 if (null === $row && !empty($wpdb->last_error)) return new WP_Error('database_get_row_error', $wpdb->last_error);
1334
1335 return $row;
1336 }
1337
1338 /**
1339 * Performs a database delete operation. A thin layer on WPDB::delete().
1340 *
1341 * @see WPDB::delete()
1342 * @param String $table - the table to delete from (without any of our prefixes)
1343 * @param Array $where - A named array of WHERE clauses (in column/value pairs)
1344 * @return Integer|WP_Error - the number of rows deleted, or a WP_Error object
1345 */
1346 public function wp_delete($table, $where) {
1347 global $wpdb;
1348 $deleted = $wpdb->delete($wpdb->base_prefix.$this->table_prefix.$table, $where);
1349 if (false === $deleted) return new WP_Error('database_delete_error', $wpdb->last_error);
1350
1351 return $deleted;
1352 }
1353
1354 /**
1355 * Performs a database insert operation. A thin layer on WPDB::insert().
1356 *
1357 * @see WPDB::insert()
1358 * @param String $table - the table to insert into (without any of our prefixes)
1359 * @param Array $data - A named array of column/value pairs
1360 * @param Array|String|null $format - An array of formats to be mapped to each of the value in $data. If string, that format will be used for all of the values in $data.
1361 * @return Integer|WP_Error - the number of rows inserted, or a WP_Error object
1362 */
1363 public function wp_insert($table, $data, $format = null) {
1364 global $wpdb;
1365 $inserted = $wpdb->insert($wpdb->base_prefix.$this->table_prefix.$table, $data, $format);
1366 if (false === $inserted) return new WP_Error('database_add_error', $wpdb->last_error);
1367
1368 return $inserted;
1369 }
1370
1371 /**
1372 * Performs a database update operation. A thin layer on WPDB::update().
1373 *
1374 * @see WPDB::insert()
1375 * @param String $table - the table to insert into (without any of our prefixes)
1376 * @param Array $data - A named array of column/value pairs
1377 * @param Array $where - A named array of WHERE clauses (in column/value pairs)
1378 * @param Array|String|null $format - An array of formats to be mapped to each of the value in $data. If string, that format will be used for all of the values in $data.
1379 * @param Array|String|null $where_format - An array of formats to be mapped to each of the value in $where. If string, that format will be used for all of the values in $data.
1380 * @return Integer|WP_Error - the number of rows updated, or a WP_Error object
1381 */
1382 public function wp_update($table, $data, $where, $format = null, $where_format = null) {
1383 global $wpdb;
1384 $updated = $wpdb->update($wpdb->base_prefix.$this->table_prefix.$table, $data, $where, $format, $where_format);
1385 if (false === $updated) return new WP_Error('database_update_error', $wpdb->last_error);
1386
1387 return $updated;
1388 }
1389
1390 /**
1391 * Does not have to (and should not be relied upon to) be able to infallibly detect
1392 *
1393 * @param string $url
1394 * @return boolean
1395 */
1396 public function url_looks_internal($url) {
1397 $url_host = strtolower(parse_url($url, PHP_URL_HOST));
1398 if (0 === strpos($url_host, 'localhost') || strpos($url_host, '127.') === 0 || strpos($url_host, '10.') === 0 || '::1' == $url_host || substr($url_host, -10, 10) == '.localhost' || substr($url_host, -4, 4) == '.dev' || '.localdomain' == substr($url_host, -12, 12)) return true;
1399
1400 return false;
1401 }
1402
1403 /**
1404 * Return instance of Updraft_Logger
1405 *
1406 * @return Updraft_Logger
1407 */
1408 public static function get_logger() {
1409 if (empty(self::$_logger_instance)) {
1410 if (!class_exists('Updraft_Logger')) include_once UD_CENTRAL_DIR.'/classes/class-updraft-logger.php';
1411 $updraft_logger = new Updraft_Logger();
1412
1413 // Loggers must implement the "Updraft_Logger_Interface"
1414 // interface to be added as valid loggers
1415 $loggers = apply_filters('updraftcentral_loggers', $updraft_logger->get_loggers());
1416 if (!empty($loggers)) {
1417 foreach ($loggers as $logger) {
1418 $updraft_logger->add_logger($logger);
1419 }
1420 }
1421
1422 // Making sure that we have at least 1 logger to use in case all else fails.
1423 $updraft_loggers = $updraft_logger->get_loggers();
1424 if (empty($updraft_loggers)) {
1425 // Add PHP Logger as the default logger if no logger is available
1426 if (!class_exists('Updraft_PHP_Logger')) include_once UD_CENTRAL_DIR.'/classes/class-updraft-php-logger.php';
1427 $logger = new Updraft_PHP_Logger();
1428 $updraft_logger->add_logger($logger);
1429 }
1430
1431 self::$_logger_instance = $updraft_logger;
1432 }
1433
1434 return self::$_logger_instance;
1435 }
1436
1437 /**
1438 * Message to capture or log
1439 *
1440 * @param string $message Message to insert into the log.
1441 * @param string $level The type of message this current log contain (e.g. info, warning, debug, error)
1442 * @param array $context Context of the log.
1443 */
1444 public function log($message, $level = 'debug', $context = array()) {
1445 // For now, we set the $level to 'debug' as default when logging (mostly for debugging purposes).
1446 // The level can be in the form of 'info', 'warning', etc. Please refer to the 'Updraft_Log_Levels'
1447 // class for a complete list of level definitions.
1448 self::get_logger()->log($level, $message, $context);
1449 }
1450
1451 /**
1452 * Checks whether the submitted data is a valid json-encoded string
1453 *
1454 * @param Mixed $data The data to validate
1455 * @return Boolean - True when $data is a valid json string, False otherwise.
1456 */
1457 public function is_json($data) {
1458 return is_string($data) && is_array(json_decode($data, true)) ? true : false;
1459 }
1460
1461 /**
1462 * JSON encodes data whenever applicable
1463 *
1464 * @param Mixed $data The data to process
1465 * @return Mixed
1466 */
1467 public function maybe_json_encode($data) {
1468 // We only encode data that is currently not json-encoded and
1469 // those that has 'object' or 'array' data types. Otherwise, we'll return
1470 // them as is.
1471 if (in_array(gettype($data), array('object', 'array'))) {
1472 $data = json_encode($data);
1473 }
1474
1475 return $data;
1476 }
1477
1478 /**
1479 * JSON decodes data to its original form whenever applicable
1480 *
1481 * @param Mixed $data The data to process
1482 * @return Mixed
1483 */
1484 public function maybe_json_decode($data) {
1485 // Decode data to its original form only when the given $data
1486 // is a valid json string.
1487 if (is_array($data)) {
1488 foreach ($data as $key => $value) {
1489 if ($this->is_json($value)) {
1490 $data[$key] = json_decode($value, true);
1491 }
1492 }
1493 } else {
1494 if ($this->is_json($data)) {
1495 $data = json_decode($data, true);
1496 }
1497 }
1498
1499 return $data;
1500 }
1501
1502 /**
1503 * This is part of the wp_privacy_personal_data_erasers
1504 * THis will export the list of sites registered by the user
1505 *
1506 * @param string $email_address The email of user sites to be exported
1507 * @return array
1508 */
1509 public function export_registered_sites($email_address) {
1510 // Get user ID by emails
1511 $user_id = get_user_by('email', $email_address);
1512
1513 // Check to make sure a user has returned
1514 if ($user_id && $user_id->ID) {
1515 // Get user
1516 $user = $this->get_user_object($user_id->ID);
1517
1518 // Get user sites
1519 $sites = $user->load_user_sites();
1520
1521 // Check if the user has added any sites
1522 if (!empty($sites)) {
1523 // Get each site information and add it to the export array
1524 foreach ($sites as $site_id => $site) {
1525 // Add this group of items to the exporters data array.
1526 $export_items[] = array(
1527 'group_id' => "registered-sites",
1528 'group_label' => __('UpdraftCentral Registered sites', 'updraftcentral'),
1529 'item_id' => "registered-sites-{$user_id->ID}",
1530 'data' => $data = array(
1531 array(
1532 'name' => __('Remote user login', 'updraftcentral'),
1533 'value' => $site->remote_user_login
1534 ),
1535 array(
1536 'name' => __('Remote user ID', 'updraftcentral'),
1537 'value' => $site->remote_user_id
1538 ),
1539 array(
1540 'name' => __('Description', 'updraftcentral'),
1541 'value' => $site->description
1542 ),
1543 array(
1544 'name' => __('URL', 'updraftcentral'),
1545 'value' => $site->url
1546 ),
1547 )
1548 );
1549
1550 // Get site meta info
1551 if (!class_exists('UpdraftCentral_Site_Meta')) include_once UD_CENTRAL_DIR.'/classes/site-meta.php';
1552
1553 // Call site meta class
1554 $get_site_meta = new UpdraftCentral_Site_Meta($this->table_prefix);
1555
1556 // Add site meta data to the sites
1557 $sites_meta = $get_site_meta->get_site_meta($site->site_id, 'site_tag', false, false, true);
1558
1559
1560 if (!empty($sites_meta)) {
1561 foreach ($sites_meta as $meta) {
1562 $export_items[] = array(
1563 'data' => $data = array(
1564 array(
1565 'name' => __('Meta ID', 'updraftcentral'),
1566 'value' => $meta->meta_id
1567 ),
1568 array(
1569 'name' => __('Meta key', 'updraftcentral'),
1570 'value' => $meta->meta_key
1571 ),
1572 array(
1573 'name' => __('Meta value', 'updraftcentral'),
1574 'value' => $meta->meta_value
1575 ),
1576 array(
1577 'name' => __('Created', 'updraftmanager'),
1578 'value' => $meta->updraftcentral
1579 ),
1580 )
1581 );
1582 }
1583 }
1584 }
1585 } else {
1586 $export_items[] = array(
1587 'group_id' => "registered-sites",
1588 'group_label' => __('registered sites', 'updraftcentral'),
1589 'item_id' => "registered-sites-{$user_id->ID}",
1590 'data' => $data = array(
1591 array(
1592 'name' => __('No sites found', 'updraftcentral'),
1593 'value' => 'No Sites Found'
1594 ),
1595 )
1596 );
1597 }
1598 } else {
1599 $export_items[] = array(
1600 'group_id' => "registered-sites",
1601 'group_label' => __('registered sites', 'updraftcentral'),
1602 'item_id' => "registered-sites-{$user_id->ID}",
1603 'data' => $data = array(
1604 array(
1605 'name' => __('Not Found', 'updraftcentral'),
1606 'value' => 'User Not Found'
1607 ),
1608 )
1609 );
1610 }
1611
1612 // Return once completed
1613 return array(
1614 'data' => $export_items,
1615 'done' => true,
1616 );
1617 }
1618 }
1619
1620 endif;
1621
1622 /**
1623 * Creates an instance of UpdraftCentral class
1624 *
1625 * @return object
1626 */
1627 function UpdraftCentral() {
1628 return UpdraftCentral::instance();
1629 }
1630
1631 $GLOBALS['updraft_central'] = UpdraftCentral();
1632