PluginProbe
UpdraftCentral Dashboard / 0.8.8
UpdraftCentral Dashboard v0.8.8
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 / classes / user.php

user.php in UpdraftCentral Dashboard 0.8.8, at classes/user.php

1,489 lines 53.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 if (!defined('UD_CENTRAL_DIR')) die('Security check');
4
5 if (!class_exists('UpdraftCentral_User')) :
6
7 class UpdraftCentral_User {
8
9 private $rc;
10
11 public $user_id = null;
12
13 public $sites = null;
14
15 public $sites_meta = null;
16
17 private $php_events;
18
19 private $licence_manager = null;
20
21 public function __construct($user_id) {
22
23 $this->user_id = (int) $user_id;
24
25 // We only check for the login state if the request is not coming from
26 // a cron event call
27 if (!defined('DOING_CRON') || !DOING_CRON) {
28 if (!is_user_logged_in()) throw new Exception('The current visitor is not logged in');
29 }
30
31 global $wpdb;
32 $this->rc = UpdraftCentral();
33 $this->sites_table = $wpdb->base_prefix.$this->rc->table_prefix.'sites';
34 $this->sitemeta_table = $wpdb->base_prefix.$this->rc->table_prefix.'sitemeta';
35
36 add_filter('updraftcentral_dashboard_ajaxaction_newsite', array($this, 'dashboard_ajaxaction_newsite'), 10, 2);
37 add_filter('updraftcentral_dashboard_ajaxaction_export_settings', array($this, 'dashboard_ajaxaction_export_settings'), 10, 2);
38 add_filter('updraftcentral_dashboard_ajaxaction_edit_site_configuration', array($this, 'dashboard_ajaxaction_edit_site_configuration'), 10, 2);
39 add_filter('updraftcentral_dashboard_ajaxaction_edit_site_connection_method', array($this, 'dashboard_ajaxaction_edit_site_connection_method'), 10, 2);
40 add_filter('updraftcentral_dashboard_ajaxaction_delete_site', array($this, 'dashboard_ajaxaction_delete_site'), 10, 2);
41 add_filter('updraftcentral_dashboard_ajaxaction_sites_html', array($this, 'dashboard_ajaxaction_sites_html'));
42 add_filter('updraftcentral_dashboard_ajaxaction_site_rpc', array($this, 'dashboard_ajaxaction_site_rpc'), 10, 2);
43 add_filter('updraftcentral_dashboard_ajaxaction_manage_site_order', array($this, 'dashboard_ajaxaction_manage_site_order'), 10, 2);
44
45 add_filter('updraftcentral_load_user_sites', array($this, 'load_user_sites_filter'));
46 add_filter('updraftcentral_dashboard_ajaxaction_manage_site_meta', array($this, 'dashboard_ajaxaction_manage_site_meta'), 10, 2);
47
48 // Manage dashboard shortcuts through ajax request
49 add_filter('updraftcentral_dashboard_ajaxaction_shortcuts', array($this, 'dashboard_ajaxaction_shortcuts'), 10, 2);
50
51 // Handles module visibility
52 add_filter('updraftcentral_main_navigation_items', array($this, 'main_navigation_items'));
53 add_filter('updraftcentral_dashboard_ajaxaction_module_visibility', array($this, 'dashboard_ajaxaction_module_visibility'), 10, 2);
54 add_filter('updraftcentral_dashboard_ajaxaction_reset_modules_visibility', array($this, 'dashboard_ajaxaction_reset_modules_visibility'), 10, 2);
55
56 // Save timeout settings
57 add_filter('updraftcentral_dashboard_ajaxaction_save_timeout', array($this, 'dashboard_ajaxaction_save_timeout'), 10, 2);
58
59 // Load licence manager - this needs loading before the sites themselves are
60 if (!class_exists('UpdraftCentral_Licence_Manager')) include_once UD_CENTRAL_DIR.'/classes/licence-manager.php';
61
62 // Allow developers to implement their own licence management
63 $licence_manager_class = apply_filters('updraftcentral_licence_manager_class', 'UpdraftCentral_Licence_Manager');
64
65 $this->licence_manager = new $licence_manager_class($this, $this->rc);
66
67 $this->load_user_sites();
68
69 }
70
71 /**
72 * Saves user-defined timeout settings
73 *
74 * @param array $response A response array where we insert our request response
75 * @param array $post_data Parameters passed as an additional argument(s) to the request
76 * @return array
77 */
78 public function dashboard_ajaxaction_save_timeout($response, $post_data) {
79 $response['responsetype'] = 'ok';
80 $response['message'] = 'success';
81
82 $timeout = $post_data['data']['timeout'];
83 if (!empty($timeout)) {
84 update_user_meta($this->user_id, 'updraftcentral_dashboard_user_defined_timeout', $timeout);
85 }
86
87 return $response;
88 }
89
90 /**
91 * Gets the user defined timeout settings
92 *
93 * @param integer $default_timeout The default timeout when no user defined timeout is set. Defaults to 30 seconds.
94 * @return integer
95 */
96 public function get_user_defined_timeout($default_timeout = 30) {
97 if (!empty($this->user_id)) {
98 $timeout = get_user_meta($this->user_id, 'updraftcentral_dashboard_user_defined_timeout', true);
99 if (!empty($timeout)) return (int) $timeout;
100 }
101
102 return $default_timeout;
103 }
104
105 /**
106 * Retrieves all available tags for this user, filterable through site id and/or tag name
107 *
108 * @param integer $site_id Optional. The ID of the site where the tags is to be pulled from
109 * @param string $tag_name Optional. The name of the tag to search for
110 *
111 * @return array|string|null
112 */
113 public function get_site_tags($site_id = 0, $tag_name = '') {
114
115 if (!$data = wp_cache_get($this->user_id, 'updraftcentral_tags')) {
116 $all_tags = $site_tags = $site_tags_by_name = array();
117
118 $user_tags = $this->get_site_tags_from_db();
119 foreach ($user_tags as $tag) {
120 $site_tags[$tag->site_id][$tag->meta_id] = $tag->meta_value;
121
122 $key_name = strtolower($tag->meta_value);
123 $site_tags_by_name[$tag->site_id][$key_name] = $tag->meta_value;
124 $all_tags[$tag->meta_id] = $tag->meta_value;
125 }
126
127 $data = array(
128 'site_tags' => $site_tags,
129 'site_tags_by_name' => $site_tags_by_name,
130 'all_tags' => $all_tags
131 );
132 wp_cache_add($this->user_id, $data, 'updraftcentral_tags');
133 }
134
135 if (!empty($site_id) && empty($tag_name)) {
136 $site_tags = $data['site_tags'];
137 if (isset($site_tags[$site_id])) return $site_tags[$site_id];
138
139 } elseif (!empty($site_id) && !empty($tag_name)) {
140 $site_tags = $data['site_tags_by_name'];
141
142 $key_name = strtolower($tag_name);
143 if (isset($site_tags[$site_id]) && isset($site_tags[$site_id][$key_name])) return $site_tags[$site_id][$key_name];
144
145 } elseif (empty($site_id) && empty($tag_name)) {
146 return $data['all_tags'];
147 }
148
149 return null;
150 }
151
152 /**
153 * Force refresh of site tags by removing previously stored cache
154 * in order to give way for a newly pulled data from database.
155 *
156 * @return void
157 */
158 public function refresh_site_tags() {
159
160 wp_cache_delete($this->user_id, 'updraftcentral_tags');
161
162 // Load the latest or updated list of tags from DB and save result in cache.
163 $this->get_site_tags();
164 }
165
166 /**
167 * Retrieves all available tags for this user, filterable through site id and/or tag name
168 *
169 * @param integer $site_id Optional. The ID of the site where the tags is to be pulled from
170 * @param string $tag_name Optional. The name of the tag to search for
171 *
172 * @return array|object|null
173 */
174 private function get_site_tags_from_db($site_id = 0, $tag_name = '') {
175
176 global $wpdb;
177
178 $our_prefix = $wpdb->base_prefix.$this->rc->table_prefix;
179
180 $site_filter = empty($site_id) ? '' : ' AND s.`site_id` = '.$site_id;
181 $tag_filter = empty($tag_name) ? '' : ' AND m.`meta_value` = "'.esc_sql($tag_name).'"';
182
183 $tags = $wpdb->get_results('SELECT m.*, s.`user_id` FROM '.$our_prefix.'sitemeta AS m INNER JOIN '.$our_prefix.'sites AS s ON m.`site_id` = s.`site_id` WHERE m.`meta_key` = "site_tag" AND s.`user_id` = '.$this->user_id.$site_filter.$tag_filter.' ORDER BY m.`meta_value` ASC');
184
185 return $tags;
186 }
187
188 /**
189 * Saves user-defined shortcut keys and returns shortcut collection
190 *
191 * @param array $response A response array where we insert our request response
192 * @param array $post_data Parameters passed as an additional argument(s) to the request
193 * @return array
194 */
195 public function dashboard_ajaxaction_shortcuts($response, $post_data) {
196
197 $response['responsetype'] = 'ok';
198 $response['message'] = 'success';
199
200 $shortcuts = get_user_meta($this->user_id, 'updraftcentral_dashboard_shortcuts', true);
201 if (!is_array($shortcuts)) $shortcuts = array();
202
203 if (isset($post_data['data']['key'])) {
204 $shortcuts[$post_data['data']['name']] = $post_data['data']['key'];
205 } elseif (isset($post_data['data']['clear'])) {
206 $shortcuts = array();
207 }
208
209 update_user_meta($this->user_id, 'updraftcentral_dashboard_shortcuts', $shortcuts);
210
211 // Return current shortcuts collection
212 $response['shortcuts'] = $shortcuts;
213
214 return $response;
215
216 }
217
218 /**
219 * Process site meta management actions (add, delete, update and get) through ajax request
220 *
221 * @param array $response
222 * @param array $post_data - site meta parameters for the current action
223 * @return array
224 */
225 public function dashboard_ajaxaction_manage_site_meta($response, $post_data) {
226
227 try {
228
229 $response['responsetype'] = 'ok';
230 $response['message'] = '';
231
232 $site_meta = $this->rc->site_meta;
233
234 if (!empty($site_meta)) {
235 $data = $post_data['data'];
236
237 switch ($data['action']) {
238 case 'add':
239 $response['data'] = $site_meta->add_site_meta($data['site_id'], $data['meta_key'], $data['meta_value'], $data['unique']);
240 break;
241 case 'delete':
242 $response['data'] = $site_meta->delete_site_meta($data['site_id'], $data['meta_key'], $data['meta_value']);
243 break;
244 case 'get':
245 $response['data'] = $site_meta->get_site_meta($data['site_id'], $data['key'], $data['single']);
246 break;
247 case 'update':
248 $response['data'] = $site_meta->update_site_meta($data['site_id'], $data['meta_key'], $data['meta_value'], $data['prev_value']);
249 break;
250 default:
251 $response['message'] = 'The submitted site meta command was not recognized.';
252 break;
253 }
254 } else {
255 $response['message'] = 'Unable to pull the site meta instance.';
256 }
257 } catch (Exception $e) {
258 $response['responsetype'] = 'error';
259 $response['message'] = $e->getMessage();
260 // @codingStandardsIgnoreLine
261 } catch (Error $e) {
262 $response['responsetype'] = 'error';
263 $response['message'] = $e->getMessage();
264 }
265
266 return $response;
267 }
268
269 /**
270 * Used to update sort order in user meta
271 *
272 * @param array $response
273 * @param array $post_data - site_order is an indexed array of site id's in the sorted order
274 * @return array
275 */
276 public function dashboard_ajaxaction_manage_site_order($response, $post_data) {
277
278 if (isset($post_data['data']['site_order'])) {
279
280 $user_id = $this->user_id;
281 $response['responsetype'] = "ok";
282 $response['message'] = 'No change';
283
284 if (get_user_meta($user_id, 'updraftcentral_dashboard_site_order', true) !== $post_data['data']['site_order']) { // only update if needed (user dragged and dropped in same place)
285
286 if (update_user_meta($user_id, 'updraftcentral_dashboard_site_order', $post_data['data']['site_order'])) {
287 $response['message'] = 'success';
288 } else {
289 $response['message'] = 'fail';
290 }
291 }
292 } else {
293 $response['responsetype'] = "error";
294 $response['message'] = "Missing site order data";
295 }
296
297 return $response;
298 }
299
300 public function get_licence_manager() {
301 return $this->licence_manager;
302 }
303
304 /**
305 * TODO: 1) Catch PHP events on the mothership, pass them on and let them be console.logged
306 * 2) Pass on caught output from the remote side, and get it console.logged
307 *
308 * @param array $response
309 * @param array $post_data
310 * @return array
311 */
312 public function dashboard_ajaxaction_site_rpc($response, $post_data) {
313
314 return $this->send_remote_command($post_data, false, $response);
315
316 }
317
318 /**
319 * Sends UpdraftCentral's command to the remote website
320 *
321 * @param array $data An array container the command to execute along with its command parameters
322 * @param boolean $force_save Optional. A flag that indicates whether UpdraftCentral will save and cache the response from the remote website
323 * @param array $response Optional. A response container that gets populated with the remote website's response from the current request
324 */
325 public function send_remote_command($data, $force_save = false, $response = array()) {
326
327 // Allow other components to intercept and deal with the command
328 if (null !== ($response = apply_filters('updraftcentral_send_remote_command_shortcircuit', null, $this, $data, $response))) {
329 return $response;
330 }
331
332 try {
333
334 // Load site rpc
335 if (!class_exists('UpdraftCentral_Remote_Communications')) include_once UD_CENTRAL_DIR.'/classes/class-siterpc.php';
336 $site_rpc = new UpdraftCentral_Remote_Communications($this, $response, $data);
337
338 if ($validate_result = $site_rpc->validate_input()) {
339 // Send command to remote website.
340 $response = $site_rpc->send_message($force_save);
341 } else {
342 // Return error from validation process
343 $response = $validate_result;
344 }
345
346 } catch (Exception $e) {
347 $response['responsetype'] = 'error';
348 $response['message'] = $e->getMessage();
349 }
350
351 return $response;
352 }
353
354 public function deep_sanitize($input, $sanitize_function = 'htmlspecialchars') {
355 if (is_string($input)) return call_user_func($sanitize_function, $input);
356 if (is_array($input)) {
357 foreach ($input as $k => $v) {
358 $input[$k] = $this->deep_sanitize($v, $sanitize_function);
359 }
360 }
361
362 return $input;
363 }
364
365 public function send_message($ud_rpc, $message, $data = null, $timeout = 30) {
366 $this->php_events = array();
367
368 // Override http timeout argument based from the user defined timeout
369 $timeout = $this->get_user_defined_timeout($timeout);
370
371 if ('__updraftcentral_internal_preencrypted' == $message) {
372
373 $post_options = array(
374 'timeout' => $timeout,
375 'body' => $data,
376 );
377
378 $post_options = apply_filters('udrpc_post_options', $post_options, $message, $data, $timeout, $this);
379
380 try {
381 $post = $ud_rpc->http_post($post_options);
382 } catch (Exception $e) {
383 // Curl can return an error code 0, which causes WP_Error to return early, without recording the message. So, we prefix the code.
384 return new WP_Error('http_post_'.$e->getCode(), $e->getMessage());
385 }
386
387 if (is_wp_error($post)) return $post;
388
389 if (empty($post['response']) || empty($post['response']['code'])) return new WP_Error('empty_http_code', 'Unexpected HTTP response code');
390
391 if ($post['response']['code'] < 200 || $post['response']['code'] >= 300) return new WP_Error('unexpected_http_code', 'Unexpected HTTP response code ('.$post['response']['code'].')', $post);
392
393 if (empty($post['body'])) return new WP_Error('empty_response', 'Empty response from remote site');
394
395 return (string) $post['body'];
396
397 } else {
398 $response = $ud_rpc->send_message($message, $data, $timeout);
399 }
400
401 // TODO: Handle caught_output
402
403 if (is_array($response) && !empty($response['data']) && is_array($response['data']) && !empty($response['data']['php_events']) && !empty($response['data']['previous_data'])) {
404 // global $updraftplus;
405 $this->php_events = $response['data']['php_events'];
406 if (defined('WP_DEBUG') && WP_DEBUG) {
407 foreach ($response['data']['php_events'] as $logline) {
408 error_log('From remote side: '.$logline);
409 }
410 }
411 $response['data'] = $response['data']['previous_data'];
412 }
413
414 return $response;
415 }
416
417 public function dashboard_ajaxaction_sites_html($response) {
418 $response['responsetype'] = 'ok';
419 $response['sites_html'] = $this->get_sites_html();
420 $response['status_info'] = array(
421 'how_many_licences_in_use' => $this->licence_manager->how_many_licences_in_use(),
422 'how_many_licences_available' => $this->licence_manager->how_many_licences_available(),
423 );
424 $response['message'] = __('The site list has been refreshed.', 'updraftcentral');
425
426 return $response;
427 }
428
429 public function dashboard_ajaxaction_delete_site($response, $post_data) {
430 if (isset($post_data['data']) && is_array($post_data['data']) && !empty($post_data['data']['site_id'])) {
431
432 $deleted = $this->delete_site_by_id((int) $post_data['data']['site_id']);
433
434 if (is_wp_error($deleted)) {
435 $response = $deleted;
436 } else {
437 $response['responsetype'] = 'ok';
438 $response['status_info'] = array(
439 'how_many_licences_in_use' => $this->licence_manager->how_many_licences_in_use(),
440 'how_many_licences_available' => $this->licence_manager->how_many_licences_available(),
441 );
442 $response['sites_html'] = $this->get_sites_html();
443 $response['message'] = __('The site was successfully deleted from your dashboard.', 'updraftcentral');
444 }
445
446 } else {
447 $response['responsetype'] = 'error';
448 $response['code'] = 'missing_data';
449 $response['message'] = __('Missing information', 'updraftcentral');
450 }
451
452 return $response;
453 }
454
455 public function dashboard_ajaxaction_edit_site_connection_method($response, $post_data) {
456
457 if (isset($post_data['data']) && is_array($post_data['data']) && !empty($post_data['data']['site_id'])) {
458
459 $site_id = (int) $post_data['data']['site_id'];
460
461 $connection_method = isset($post_data['data']['connection_method']) ? (string) $post_data['data']['connection_method'] : 'direct_default_auth';
462
463 $updated = $this->rc->wp_update('sites',
464 array(
465 'connection_method' => $connection_method,
466 ),
467 array(
468 'user_id' => $this->user_id,
469 'site_id' => $site_id,
470 ),
471 array(
472 '%s',
473 ),
474 array(
475 '%d',
476 '%d',
477 )
478 );
479
480 if (is_numeric($updated)) {
481 $response['responsetype'] = 'ok';
482
483 $this->load_user_sites();
484 $response['sites_html'] = $this->get_sites_html();
485 $response['status_info'] = array(
486 'how_many_licences_in_use' => $this->licence_manager->how_many_licences_in_use(),
487 'how_many_licences_available' => $this->licence_manager->how_many_licences_available(),
488 );
489
490 $response['message'] = __('The site configuration was successfully edited.', 'updraftcentral');
491 } else {
492 $response = $updated;
493 }
494
495 } else {
496 $response['responsetype'] = 'error';
497 $response['code'] = 'missing_data';
498 $response['message'] = __('Missing information', 'updraftcentral');
499 }
500
501 return $response;
502
503 }
504
505 public function dashboard_ajaxaction_edit_site_configuration($response, $post_data) {
506
507 if (isset($post_data['data']) && is_array($post_data['data']) && !empty($post_data['data']['site_id']) && isset($post_data['data']['description'])) {
508
509 $site_id = (int) $post_data['data']['site_id'];
510
511 $connection_method = isset($post_data['data']['connection_method']) ? (string) $post_data['data']['connection_method'] : 'direct_default_auth';
512 $send_cors_headers = (isset($post_data['data']['send_cors_headers']) && $post_data['data']['send_cors_headers']) ? 1 : 0;
513
514 $updated = $this->rc->wp_update('sites',
515 array(
516 'description' => (string) $post_data['data']['description'],
517 'connection_method' => $connection_method,
518 'send_cors_headers' => $send_cors_headers,
519 ),
520 array(
521 'user_id' => $this->user_id,
522 'site_id' => $site_id,
523 ),
524 array(
525 '%s',
526 '%s',
527 '%d',
528 ),
529 array(
530 '%d',
531 '%d',
532 )
533 );
534
535 if (is_numeric($updated)) {
536 $response['responsetype'] = 'ok';
537
538 $extra_site_info_unparsed = empty($post_data['data']['extra_site_info']) ? false : $post_data['data']['extra_site_info'];
539 if (!$extra_site_info_unparsed) {
540 $extra_site_info = array();
541 } else {
542 parse_str($extra_site_info_unparsed, $extra_site_info);
543 }
544
545 if (!empty($extra_site_info)) {
546 foreach ($extra_site_info as $meta_key => $meta_value) {
547 $this->rc->site_meta->update_site_meta($site_id, $meta_key, $meta_value);
548 }
549 }
550
551 $this->load_user_sites();
552 $response['sites_html'] = $this->get_sites_html();
553 $response['status_info'] = array(
554 'how_many_licences_in_use' => $this->licence_manager->how_many_licences_in_use(),
555 'how_many_licences_available' => $this->licence_manager->how_many_licences_available(),
556 );
557
558 $response['message'] = __('The site configuration was successfully edited.', 'updraftcentral');
559 } else {
560
561 $response = $updated;
562 }
563
564 } else {
565 $response['responsetype'] = 'error';
566 $response['code'] = 'missing_data';
567 $response['message'] = __('Missing information', 'updraftcentral');
568 }
569
570 return $response;
571 }
572
573 /**
574 * Exports user's settings
575 *
576 * @param array $response Respose array to be filtered
577 * @param array $post_data The request data
578 * @return array
579 */
580 public function dashboard_ajaxaction_export_settings($response, $post_data) {
581
582 $posted_data = $post_data['data'];
583 if (empty($posted_data)) {
584 return;
585 }
586
587 $uc_version = UpdraftCentral()->version;
588 $user = wp_get_current_user();
589
590 // Other keys might be added in the future (e.g. in new modules or future tasks), thus,
591 // we're having the keys filterable here so that anyone can add their respective user meta keys
592 // to be included during the export process.
593 $uc_usermeta_keys = apply_filters('updraftcentral_usermeta_keys', array(
594 'updraftcentral_dashboard_last_loaded',
595 'updraftcentral_modules_visibility',
596 'updraftcentral_dashboard_shortcuts',
597 'updraftcentral_dashboard_user_defined_timeout',
598 'updraftcentral_dashboard_site_order',
599 ));
600
601 $data = array(
602 'updraftcentral_version' => $uc_version,
603 'user' => $user->display_name,
604 'user_email' => $user->user_email,
605 'site_name' => get_bloginfo('name'),
606 'site_url' => get_bloginfo('url'),
607 'date' => date('Y-m-d H:i:s'),
608 'version' => '1',
609 'sites' => $this->load_sites_for_export(),
610 'usermeta' => $this->get_updraftcentral_usermeta($uc_usermeta_keys, $user->ID),
611 );
612
613 try {
614
615 if (!empty($posted_data['encrypt']) && 1 === intval($posted_data['encrypt'])) {
616 if (!empty($posted_data['phrase'])) {
617 $passphrase = $posted_data['phrase'];
618 $data = $this->encrypt_data($data, $passphrase);
619 } else {
620 $response['responsetype'] = 'error';
621 $response['code'] = 'missing_encryption_phrase';
622 $response['message'] = __('Missing encryption phrase', 'updraftcentral');
623
624 return $response;
625 }
626 }
627
628 $blog_name = sanitize_title(get_bloginfo('name'));
629 $file_name = apply_filters('updraftcentral_export_file_name', 'updraftcentral-settings-'.$blog_name.'.json');
630
631 $response['data'] = array(
632 'json_data' => json_encode($data),
633 'file_name' => $file_name,
634 );
635
636 $response['responsetype'] = 'ok';
637 $response['code'] = 'export_settings';
638 $response['message'] = 'success';
639 return $response;
640
641 } catch (Exception $e) {
642 $response['responsetype'] = 'error';
643 $response['code'] = 'export_failed';
644 $response['message'] = $e->getMessage();
645
646 return $response;
647 }
648 }
649
650 /**
651 * Retrieves usermeta information based from an array of meta keys
652 *
653 * @param array $keys A collection of meta keys where the data is to be pulled from
654 * @param integer $user_id The ID of the current user
655 * @return array
656 */
657 private function get_updraftcentral_usermeta($keys, $user_id) {
658 global $wpdb;
659
660 if (empty($keys) || empty($user_id)) return array();
661
662 $meta_keys = "'".implode("','", $keys)."'";
663 $metas = $wpdb->get_results($wpdb->prepare("SELECT `meta_key`, `meta_value` FROM $wpdb->usermeta WHERE `user_id` = %d AND (`meta_key` IN (".$meta_keys.") OR `meta_key` LIKE 'updraftcentral_%')", $user_id), ARRAY_A);
664
665 if (!empty($metas)) {
666 $metas = array_map('maybe_unserialize', $metas);
667 }
668
669 return $metas;
670 }
671
672 /**
673 * Encrypts data using an encryption or pass phrase
674 *
675 * @param array $data The data to encrypt
676 * @param array $passphrase A phrase to be used to encrypt the data into ciphertext
677 * @param array $result_data The resulting array that will contained the encrypted data
678 * @return array
679 */
680 private function encrypt_data($data, $passphrase, &$result_data = array()) {
681 if (!empty($data)) {
682 foreach ($data as $key => $value) {
683 if (is_object($value)) {
684 // Here, we're converting the object into array before we proceed
685 // with the rest of the process to cover all data as possible during encryption.
686 //
687 // N.B. We're using deep conversion using the two json functions (json_encode and json_decode)
688 // rather than casting the object directly using (array) since we wanted to convert all
689 // object instances found within the $value down to the lowest level.
690 $value = json_decode(json_encode($value), true);
691 }
692
693 if (is_array($value)) {
694 $result_data[$key] = array();
695 $this->encrypt_data($value, $passphrase, $result_data[$key]);
696 } else {
697 $result_data[$key] = $this->encrypt_with_passphrase($value, $passphrase);
698 }
699 }
700 }
701
702 return $result_data;
703 }
704
705 /**
706 * Decrypts data using an encryption or pass phrase
707 *
708 * @param array $data The data to decrypt
709 * @param array $passphrase A phrase to be used to decrypt the data back into plaintext
710 * @param array $result_data The resulting array that will contained the decrypted data
711 * @return array
712 */
713 private function decrypt_data($data, $passphrase, &$result_data = array()) {
714 if (!empty($data)) {
715 foreach ($data as $key => $value) {
716 if (is_object($value)) {
717 // Here, we're converting the object into array before we proceed
718 // with the rest of the process to cover all data as possible during decryption.
719 //
720 // N.B. We're using deep conversion using the two json functions (json_encode and json_decode)
721 // rather than casting the object directly using (array) since we wanted to convert all
722 // object instances found within the $value down to the lowest level.
723 $value = json_decode(json_encode($value), true);
724 }
725
726 if (is_array($value)) {
727 $result_data[$key] = array();
728 $this->decrypt_data($value, $passphrase, $result_data[$key]);
729 } else {
730 $decrypt_result = $this->decrypt_with_passphrase($value, $passphrase);
731 if (!is_wp_error($decrypt_result)) {
732 $result_data[$key] = $decrypt_result;
733 } else {
734 return $decrypt_result->get_error_message();
735 }
736 }
737 }
738 }
739
740 return $result_data;
741 }
742
743 /**
744 * Make sure phpseclib classes are loaded
745 */
746 public function load_crypto() {
747 $pdir = UD_CENTRAL_DIR.'/vendor/phpseclib/phpseclib/phpseclib';
748 if (false === strpos(get_include_path(), $pdir)) set_include_path($pdir.PATH_SEPARATOR.get_include_path());
749 if (!class_exists('Crypt_Rijndael')) include_once 'Crypt/Rijndael.php';
750 if (!class_exists('Crypt_RSA')) include_once 'Crypt/RSA.php';
751 if (!class_exists('Crypt_Hash')) include_once 'Crypt/Hash.php';
752 }
753
754 /**
755 * Encrypts information using an encryption or pass phrase
756 *
757 * @param array $plaintext The information to encrypt
758 * @param array $passphrase A phrase to be used to encrypt the data into ciphertext
759 * @return array
760 */
761 public function encrypt_with_passphrase($plaintext, $passphrase) {
762
763 $this->load_crypto();
764
765 $crypto = new Crypt_Rijndael(CRYPT_MODE_CTR);
766 $hmac = ':';
767 if (!empty($passphrase)) {
768 $crypto->setKey($passphrase);
769
770 $hash = new Crypt_Hash('sha256');
771 $hash->setKey($passphrase);
772 $hmac = base64_encode($hash->hash($passphrase)).':';
773 }
774
775 $crypto_string = crypt_random_string($crypto->getBlockLength() >> 3);
776 $crypto->setIV($crypto_string);
777 $crypto->disablePadding();
778
779 $encrypted = base64_encode($crypto_string).':'.base64_encode($crypto->encrypt($plaintext));
780
781 // Here, we're keeping the actual boolean representation of the original data
782 // making sure that "true" or "false" boolean value are not converted into
783 // its numerical counterpart as "1" or "0" after decryption.
784 $boolean_str = array('false', 'true');
785 if (in_array(strtolower($plaintext), $boolean_str) || is_bool($plaintext)) {
786 $encrypted .= ':bool'.(is_string($plaintext) ? '/s' : '/b');
787 }
788 return base64_encode($hmac.$encrypted);
789 }
790
791 /**
792 * Decrypts information using an encryption or pass phrase
793 *
794 * @param array $ciphertext The information to decrypt
795 * @param array $passphrase A phrase to be used to decrypt the data back into plaintext
796 * @return array
797 */
798 public function decrypt_with_passphrase($ciphertext, $passphrase) {
799 $this->load_crypto();
800
801 list($hmac, $crypt_string, $ciphertext, $bool_flag) = explode(':', base64_decode($ciphertext));
802 $ciphertext = base64_decode($ciphertext);
803 $crypt_string = base64_decode($crypt_string);
804
805 $crypto = new Crypt_Rijndael(CRYPT_MODE_CTR);
806 if (!empty($passphrase)) {
807 $hash = new Crypt_Hash('sha256');
808 $hash->setKey($passphrase);
809 if (base64_decode($hmac) !== $hash->hash($passphrase)) {
810 return new WP_Error('updraftcentral_unauthorized', __('You are not authorized to view this information.', 'updraftcentral'));
811 }
812
813 $crypto->setKey($passphrase);
814 }
815
816 $crypto->setIV($crypt_string);
817 $crypto->disablePadding();
818
819 $plaintext = $crypto->decrypt($ciphertext);
820
821 $boolean_arr = array('false', 'true');
822 if (!empty($bool_flag) && in_array($bool_flag, array('bool/s', 'bool/b'))) {
823 if (is_numeric($plaintext)) $plaintext = $boolean_arr[intval($plaintext)];
824 if ('bool/b' === $bool_flag) $plaintext = filter_var($plaintext, FILTER_VALIDATE_BOOLEAN) ? true : false;
825 }
826
827 return $plaintext;
828 }
829
830 /**
831 * Loads all available sites along with their current sitemeta informations
832 * for the given user
833 *
834 * @return array
835 */
836 private function load_sites_for_export() {
837 global $wpdb;
838 $list = $wpdb->get_results('SELECT * FROM `'.$this->sites_table.'` WHERE `user_id`='.absint($this->user_id));
839
840 $sites = array();
841 if (is_array($list) && !empty($list)) {
842 foreach ($list as $site) {
843 $sitemeta = $wpdb->get_results('SELECT `meta_key`, `meta_value` FROM `'.$this->sitemeta_table.'` WHERE `site_id`='.absint($site->site_id));
844
845 $metas = array();
846 if (is_array($sitemeta) && !empty($sitemeta)) {
847 foreach ($sitemeta as $meta) {
848 array_push($metas, (array) $meta);
849 }
850 }
851
852 // Remove unwanted fields (we don't need this ID fields in the export file)
853 unset($site->site_id);
854 unset($site->user_id);
855
856 $site->sitemeta = $metas;
857 array_push($sites, (array) $site);
858 }
859 }
860
861 return $sites;
862 }
863
864 /**
865 * Adds a new site to the user's sites collection
866 *
867 * @param array $response
868 * @param array $post_data
869 * @param bool $render_sites - Controls whether to render the sites' HTML or not.
870 * @return array
871 */
872 public function dashboard_ajaxaction_newsite($response, $post_data, $render_sites = true) {
873
874 if (empty($post_data['data']) || !is_array($post_data['data']) || empty($post_data['data']['key'])) {
875 $response['responsetype'] = 'error';
876 $response['code'] = 'empty';
877 $response['message'] = __('Please enter the site key.', 'updraftcentral');
878 } else {
879
880 $site_key = $post_data['data']['key'];
881
882 $extra_site_info_unparsed = empty($post_data['data']['extra_site_info']) ? false : $post_data['data']['extra_site_info'];
883 if (!$extra_site_info_unparsed) {
884 $extra_site_info = array();
885 } else {
886 parse_str($extra_site_info_unparsed, $extra_site_info);
887 }
888
889 $ud_rpc = $this->rc->get_udrpc();
890
891 // A bundle has these keys: key, name_indicator, url
892 $decode_bundle = $ud_rpc->decode_portable_bundle($site_key, 'base64_with_count');
893
894 if (!is_array($decode_bundle) || !empty($decode_bundle['code'])) {
895 $response['responsetype'] = 'error';
896 $response['message'] = __('Error:', 'updraftcentral');
897 $response['code'] = empty($decode_bundle['code']) ? 'could_not_decode' : $decode_bundle['code'];
898 if (!empty($decode_bundle['code']) && 'invalid_wrong_length' == $decode_bundle['code']) {
899 $response['message'] .= ' '.__('The entered key was the wrong length - please try again.', 'updraftcentral');
900 } elseif (!empty($decode_bundle['code']) && 'invalid_corrupt' == $decode_bundle['code']) {
901 $response['message'] .= ' '.__('The entered key was corrupt - please try again.', 'updraftcentral').' ('.$decode_bundle['data'].')';
902 } elseif (empty($decode_bundle['key']) || empty($decode_bundle['url']) || empty($decode_bundle['name_indicator'])) {
903 $response['message'] .= ' '.__('The entered key was corrupt - please try again.', 'updraftcentral');
904 $response['data'] = $decode_bundle;
905 }
906 } elseif (empty($decode_bundle['key']) || empty($decode_bundle['url']) || empty($decode_bundle['user_id'])) {
907 $response['message'] = __('Error:', 'updraftcentral').' '.__('The entered key was corrupt - please try again.', 'updraftcentral');
908 $response['code'] = 'corrupt_key';
909 $response['data'] = $decode_bundle;
910 } else {
911
912 if (trailingslashit(network_site_url()) == $decode_bundle['url'] && !apply_filters('updraftcentral_allow_self_control', true)) {
913 $response['responsetype'] = 'error';
914 $response['code'] = 'this_site';
915 $response['message'] = __('Error:', 'updraftcentral').' '.__('The entered key does not belong to a remote site (it belongs to this one).', 'updraftcentral');
916 } elseif ($this->rc->url_looks_internal($decode_bundle['url']) && !$this->rc->url_looks_internal(site_url()) && !apply_filters('updraftcentral_allow_adding_internal_url', true, $decode_bundle['url'])) {
917 // The default is to allow it, because as long as your browser is running on the same machine as the site is on, it can work.
918 $response['responsetype'] = 'error';
919 $response['code'] = 'cant_add_localhost';
920 $response['message'] = __('Error:', 'updraftcentral').' '.__('The entered key belongs to a local development website - these cannot be controlled from this dashboard because it is not reachable from an external network.', 'updraftcentral');
921 } else {
922 // Was the key sent SSL to us directly?
923 $key = $decode_bundle['key'];
924 if (is_array($key) && !empty($key['key_hash']) && isset($key['key_id'])) {
925 global $wpdb;
926 // Allow them 3 hours to copy-and-paste their key
927 $wpdb->query('DELETE FROM '.$wpdb->base_prefix.$this->rc->table_prefix.'site_temporary_keys WHERE created<='.(int) (time() - 10800));
928 $key_info = $this->rc->wp_get_row('site_temporary_keys', $wpdb->prepare('key_id=%d', $key['key_id']));
929 if (is_object($key_info) && !empty($key_info->key_local_private) && !empty($key_info->key_remote_public)) {
930 $this->rc->wp_delete('site_temporary_keys', array('key_id' => $key['key_id']));
931 $key_hash = hash('sha256', $key_info->key_remote_public);
932
933 // @codingStandardsIgnoreLine
934 if ((function_exists('hash_equals') && hash_equals($key_hash, $key['key_hash'])) || (!function_exists('hash_equals') && $key_hash === $key['key_hash'])) {
935 $key_local_private = $key_info->key_local_private;
936 $key_remote_public = $key_info->key_remote_public;
937 } else {
938 $response['responsetype'] = 'error';
939 $response['code'] = 'wrong_hash';
940 $response['message'] = __('Error:', 'updraftcentral').' '.apply_filters('updraftcentral_wrong_hash_message', __('This key could not be added, as it appears to be corrupt - please try again.', 'updraftcentral'));
941
942 return $response;
943 }
944 } else {
945 $response['responsetype'] = 'error';
946 $response['code'] = 'no_key_found';
947 $response['message'] = __('Error:', 'updraftcentral').' '.apply_filters('updraftcentral_no_key_found_message', __('This key could not be added - it may be too long since you generated it; please try again.', 'updraftcentral'));
948
949 return $response;
950 }
951
952 } elseif (!empty($decode_bundle['mothership_firewalled'])) {
953
954 // Need to do direct AJAX from the browser to the mothership to send our key
955
956 $ud_rpc = $this->rc->get_udrpc('central_host.updraftplus.com');
957 if (false != $ud_rpc->generate_new_keypair()) {
958 $key_remote_public = $key;
959 $key_local_private = $ud_rpc->get_key_local();
960 } else {
961 $response['responsetype'] = 'error';
962 $response['code'] = 'keygen_error';
963 $response['message'] = 'An error occurred when attempting to generate a new key-pair';
964
965 return $response;
966 }
967
968 } else {
969 $key_remote_public = $key;
970 $key_local_private = false;
971 }
972
973 $remote_site_id = empty($decode_bundle['ms_id']) ? 0 : $decode_bundle['ms_id'];
974 $description = isset($decode_bundle['site_title']) ? (string) $decode_bundle['site_title'] : '';
975
976 $send_cors_headers = (isset($post_data['data']['send_cors_headers']) && !$post_data['data']['send_cors_headers']) ? 0 : 1;
977 $connection_method = isset($post_data['data']['connection_method']) ? (string) $post_data['data']['connection_method'] : 'direct_default_auth';
978
979 $site_url = $decode_bundle['url'];
980
981 // Supply a default for legacy format keys that didn't include the admin URL
982 $admin_url = empty($decode_bundle['admin_url']) ? trailingslashit($site_url).'wp-admin' : $decode_bundle['admin_url'];
983
984 $added = $this->add_site($site_url, $admin_url, $key_local_private, $key_remote_public, $decode_bundle['user_id'], $decode_bundle['user_login'], $decode_bundle['name_indicator'], $remote_site_id, $description, $connection_method, $send_cors_headers);
985
986 if (true === $added) {
987 $response['responsetype'] = 'ok';
988
989 global $wpdb;
990 $new_site_id = $wpdb->insert_id;
991
992 if (!empty($extra_site_info)) {
993 if (!is_array($this->sites_meta)) $this->sites_meta = array();
994 if (empty($this->sites_meta[$new_site_id])) $this->sites_meta[$new_site_id] = array();
995 foreach ($extra_site_info as $meta_key => $meta_value) {
996 if (!$meta_value) continue;
997 // Don't bother to save the default value on the initial adding of the site
998 if ('http_authentication_method' == $meta_key && 'basic' == $meta_value) continue;
999 $result = $this->rc->site_meta->add_site_meta($new_site_id, $meta_key, $meta_value);
1000 if (false !== $result) {
1001 $created = apply_filters("get_site_metadata_created", false, $new_site_id, $meta_key);
1002 if (!$created) {
1003 // Fallback if in case we fail to retrieve the "created" value from
1004 // the site metadata table. But most likely, if the add_site_meta succeeded
1005 // then we would have a value for the $created variable.
1006 $created = time();
1007 }
1008
1009 $meta = new stdClass();
1010 $meta->value = $meta_value;
1011 $meta->created = $created;
1012
1013 // We update the in-memory copy because this is used by get_sites_html()
1014 $this->sites_meta[$new_site_id][$meta_key] = $meta;
1015 }
1016 }
1017 }
1018
1019 // Return the new HTML widget to the front end
1020 $response['sites_html'] = $render_sites ? $this->get_sites_html() : '';
1021 $response['status_info'] = array(
1022 'how_many_licences_in_use' => $this->licence_manager->how_many_licences_in_use(),
1023 'how_many_licences_available' => $this->licence_manager->how_many_licences_available(),
1024 );
1025
1026 $response['message'] = __('The key was successfully added.', 'updraftcentral').' '.__('It is for interacting with the following site: ', 'updraftcentral').htmlspecialchars($decode_bundle['url']);
1027
1028 if (!empty($decode_bundle['mothership_firewalled_callback_url'])) {
1029 $response['key_needs_sending'] = array(
1030 'site_id' => $new_site_id,
1031 'url' => $decode_bundle['mothership_firewalled_callback_url'],
1032 'updraft_key_index' => $decode_bundle['updraft_key_index'],
1033 'remote_public_key' => $ud_rpc->get_key_remote(),
1034 );
1035 }
1036
1037 } else {
1038 $response['responsetype'] = 'error';
1039 $response['code'] = $added->get_error_code();
1040 $response['message'] = __('Error:', 'updraftcentral').' '.$added->get_error_message();
1041 }
1042
1043 }
1044 }
1045 }
1046
1047 return $response;
1048 }
1049
1050 public function load_user_sites_filter($sites) {
1051 $how_many_licences_available = $this->licence_manager->how_many_licences_available();
1052 $how_many_licences_in_use = count($sites);
1053
1054 if ($how_many_licences_available >= $how_many_licences_in_use || $how_many_licences_available < 0) return $sites;
1055
1056 $log_message = sprintf(__('You have more sites being managed (%d) than active licences (%d) - you will need to obtain more licences in order to manage all of your managed sites.', 'updraftcentral'), $how_many_licences_in_use, $how_many_licences_available);
1057
1058 $this->rc->log_notice($log_message, 'error', 'not_enough_licences');
1059
1060 $i = 0;
1061 foreach ($sites as $site_id => $site) {
1062 if ($i >= $how_many_licences_available) {
1063 $site->unlicensed = true;
1064 $sites[$site_id] = $site;
1065 }
1066 ++$i;
1067 }
1068
1069 return $sites;
1070 }
1071
1072 /**
1073 * Populate $this->sites and $this->sites_meta in accordance with the current user ($this->user_id)
1074 *
1075 * @return Array|WP_Error - either the same list of sites as will be in $this->sites, or a WP_Error if something went wrong
1076 */
1077 public function load_user_sites() {
1078 global $wpdb;
1079 $sites = $wpdb->get_results('SELECT * FROM '.$this->sites_table.' WHERE user_id='.absint($this->user_id));
1080
1081 $this->sites_meta = array();
1082
1083 $subsequent_site = false;
1084 if (is_array($sites) && !empty($sites)) {
1085 $sites_meta_sql = 'SELECT * FROM '.$this->sitemeta_table.' WHERE site_id IN (';
1086 foreach ($sites as $site) {
1087 if ($subsequent_site) {
1088 $sites_meta_sql .= ',';
1089 } else {
1090 $subsequent_site = true;
1091 }
1092 $sites_meta_sql .= absint($site->site_id);
1093 }
1094 $sites_meta_sql .= ')';
1095 $sites_meta = $wpdb->get_results($sites_meta_sql);
1096 } else {
1097 $sites_meta = array();
1098 }
1099
1100 if (is_array($sites_meta)) {
1101 foreach ($sites_meta as $meta_row) {
1102 if (isset($meta_row->site_id)) {
1103 // N.B. Since we're trying to include the 'created' column in the site metadata when loaded or pulled from
1104 // the database, therefore, we assign an anonymous object to encapsulate the value of the current meta_key along with
1105 // its created column.
1106
1107 $meta = new stdClass();
1108 $meta->value = $meta_row->meta_value;
1109 $meta->created = $meta_row->created;
1110
1111 $this->sites_meta[$meta_row->site_id][$meta_row->meta_key] = $meta;
1112 }
1113 }
1114 }
1115
1116 if (is_array($sites)) {
1117
1118 $processed_sites = array();
1119 foreach ($sites as $site) {
1120 $processed_sites[$site->site_id] = $site;
1121 }
1122 $this->sites = apply_filters('updraftcentral_load_user_sites', $processed_sites, $this, $this->licence_manager);
1123
1124 return $this->sites;
1125
1126 } elseif (is_wp_error($sites)) {
1127
1128 $this->rc->log_notice($sites);
1129 $this->sites = null;
1130
1131 return $sites;
1132 }
1133 }
1134
1135 /**
1136 * Get the HTML to render the site list in the dashboard
1137 *
1138 * @return String
1139 */
1140 public function get_sites_html() {
1141
1142 $ret = '';
1143
1144 // Get sites. Print a line for each of them.
1145 if (empty($this->sites) || !is_array($this->sites)) {
1146 $ret .= $this->rc->include_template('sites/none-set-up.php', true, array('common_urls' => $this->rc->get_common_urls()));
1147 } else {
1148
1149 // retrieve metadata if any exists
1150 $user_id = $this->user_id;
1151
1152 // ensure we have an array (in even of no metadata)
1153
1154 if (!$site_order_meta = get_user_meta($user_id, 'updraftcentral_dashboard_site_order', true)) {
1155 $site_order_meta = array();
1156 }
1157
1158 // Add existing sites if not in siteOrderMeta (i.e. any new added sites or handling no meta data)
1159
1160 foreach ($this->sites as $site) {
1161 if (!in_array($site->site_id, $site_order_meta)) {
1162 array_push($site_order_meta, $site->site_id);
1163 }
1164 }
1165
1166 // Render the site rows in site_order_meta sequence using site_id to reference sites object
1167
1168 foreach ($site_order_meta as $site_id_meta) {
1169
1170 // Ignore invalid or removed sites
1171
1172 if (!empty($this->sites[$site_id_meta])) {
1173
1174 $connection_method = isset($this->sites[$site_id_meta]->connection_method) ? (string) $this->sites[$site_id_meta]->connection_method : 'direct_default_auth';
1175 $send_cors_headers = (isset($this->sites[$site_id_meta]->send_cors_headers) && !$this->sites[$site_id_meta]->send_cors_headers) ? 0 : 1;
1176
1177 $site_data_attributes = 'data-site_url="' . esc_attr($this->sites[$site_id_meta]->url) . '" data-site_id="' . (int) $site_id_meta . '" data-key_name_indicator="' . esc_attr($this->sites[$site_id_meta]->key_name_indicator) . '" data-site_description="' . (($this->sites[$site_id_meta]->description) ? esc_attr($this->sites[$site_id_meta]->description) : esc_attr($this->sites[$site_id_meta]->url)) . '" data-remote_user_id="' . (int) $this->sites[$site_id_meta]->remote_user_id . '" data-remote_user_login="' . esc_attr($this->sites[$site_id_meta]->remote_user_login) . '"';
1178
1179 if (empty($this->sites[$site_id_meta]->admin_url)) {
1180 $admin_url = trailingslashit($this->sites[$site_id_meta]->url) . 'wp-admin';
1181 } else {
1182 $admin_url = $this->sites[$site_id_meta]->admin_url;
1183 }
1184 $site_data_attributes .= ' data-admin_url="' . esc_attr($admin_url) . '"';
1185
1186 $site_meta = empty($this->sites_meta[$site_id_meta]) ? array() : $this->sites_meta[$site_id_meta];
1187 if (!empty($site_meta)) {
1188 if (!empty($site_meta['http_username']->value)) {
1189 $http_password = empty($site_meta['http_password']->value) ? '' : $site_meta['http_password']->value;
1190 $site_data_attributes .= ' data-http_username="' . esc_attr($site_meta['http_username']->value) . '" data-http_password="' . esc_attr($http_password) . '"';
1191 if (!empty($site_meta['http_authentication_method']->value)) $site_data_attributes .= ' data-http_authentication_method="' . $site_meta['http_authentication_method']->value . '"';
1192 }
1193 }
1194
1195 if (empty($this->sites[$site_id_meta]->unlicensed)) {
1196 if ('via_mothership_encrypting' != $connection_method) {
1197 $site_data_attributes .= ' data-site_remote_public_key="' . esc_attr($this->sites[$site_id_meta]->key_remote_public) . '" data-site_local_private_key="' . esc_attr($this->sites[$site_id_meta]->key_local_private) . '"';
1198 }
1199 } else {
1200 $site_data_attributes .= ' data-site_unlicensed="1"';
1201 }
1202
1203 $site_data_attributes .= ' data-connection_method="' . esc_attr($connection_method) . '" data-send_cors_headers="' . $send_cors_headers . '"';
1204
1205 // Check whether this site was tagged as suspended
1206 $tagged = $this->get_site_tags($site_id_meta, 'Suspended');
1207 $suspended = !empty($tagged);
1208
1209 $site_data_attributes = apply_filters('updraftcentral_site_data_attributes', $site_data_attributes, $this->sites[$site_id_meta]);
1210
1211 $ret .= $this->rc->include_template('sites/site-row.php', true, array('site' => $this->sites[$site_id_meta], 'site_meta' => $site_meta, 'site_data_attributes' => $site_data_attributes, 'suspended' => $suspended));
1212 }
1213 }
1214 }
1215
1216 return $ret;
1217 }
1218
1219 /**
1220 * Returns false if not authorised at all; or a timestamp if it's authorised until a particular date
1221 *
1222 * @param int $site_id [description]
1223 * @return boolean|integer
1224 */
1225 public function authorised_for_site_until($site_id) {
1226
1227 if (!is_array($this->sites)) $this->load_user_sites();
1228
1229 if (is_array($this->sites)) {
1230 foreach ($this->sites as $site) {
1231 if ((int) $site_id == (int) $site->site_id && isset($site->licence_until)) {
1232 return apply_filters('updraftcentral_authorised_for_site_until', (int) $site->licence_until, $site_id, $this->sites);
1233 }
1234 }
1235 }
1236
1237 return apply_filters('updraftcentral_authorised_for_site_until', false, $site_id, $this->sites);
1238
1239 }
1240
1241 /**
1242 * Adding a site
1243 *
1244 * @param string $url
1245 * @param string $admin_url
1246 * @param string $key_local_private
1247 * @param string $key_remote_public
1248 * @param integer $remote_user_id
1249 * @param string $remote_user_login
1250 * @param string $key_name_indicator
1251 * @param integer $remote_site_id
1252 * @param string $description
1253 * @param string $connection_method
1254 * @param Boolean $send_cors_headers
1255 * @return Boolean|WP_Error - if a Boolean, then it will be true
1256 */
1257 public function add_site($url, $admin_url, $key_local_private, $key_remote_public, $remote_user_id, $remote_user_login, $key_name_indicator, $remote_site_id = 0, $description = '', $connection_method = 'direct_default_auth', $send_cors_headers = 1) {
1258
1259 if (!$this->user_can('add_site')) return new WP_Error('permission_denied', __('You do not have the permission to do this.', 'updraftcentral'), $this->user_id);
1260
1261 if (!$this->licence_manager->is_slot_available(array('url' => $url))) {
1262 return new WP_Error('no_licences_available', apply_filters('updraftcentral_no_licences_available_message', __('You have no licences available - to add a site, you will need to obtain some more.', 'updraftcentral')));
1263 }
1264
1265 $this->delete_site_by_url($url);
1266
1267 $added = $this->rc->wp_insert('sites',
1268 array(
1269 'user_id' => $this->user_id,
1270 'url' => $url,
1271 'admin_url' => $admin_url,
1272 'key_local_private' => $key_local_private,
1273 'key_remote_public' => $key_remote_public,
1274 'description' => $description,
1275 'connection_method' => $connection_method,
1276 'send_cors_headers' => $send_cors_headers,
1277 'sequence_id' => 0,
1278 'remote_user_id' => $remote_user_id,
1279 'remote_user_login' => $remote_user_login,
1280 'remote_site_id' => $remote_site_id,
1281 'key_name_indicator' => $key_name_indicator,
1282 ),
1283 array(
1284 '%d',
1285 '%s',
1286 '%s',
1287 '%s',
1288 '%s',
1289 '%s',
1290 '%s',
1291 '%d',
1292 '%d',
1293 '%d',
1294 '%s',
1295 '%d',
1296 '%s',
1297 )
1298 );
1299
1300 if (is_numeric($added)) {
1301 $result = true;
1302 } else {
1303 $result = $added;
1304 }
1305
1306 $this->load_user_sites();
1307
1308 return $result;
1309
1310 }
1311
1312 /**
1313 * Delete all site meta for a specified site
1314 *
1315 * @param Integer $site_id - the site ID
1316 *
1317 * @uses UpdraftCentral::wp_delete()
1318 *
1319 * @return Integer|WP_Error - the number of rows deleted, or a WP_Error object
1320 */
1321 public function delete_site_meta($site_id) {
1322 return $this->rc->wp_delete('sitemeta', array('site_id' => $site_id));
1323 }
1324
1325 /**
1326 * Delete all sites for the user
1327 */
1328 public function delete_all_sites() {
1329
1330 if (!is_array($this->sites)) return;
1331
1332 $counter = 1;
1333
1334 foreach ($this->sites as $site_id => $site) {
1335 $reload_user_sites = ($counter >= $this->sites);
1336 $this->delete_site_by_id($site_id, $reload_user_sites);
1337 $counter ++;
1338 }
1339
1340 }
1341
1342 public function delete_site_by_id($site_id, $reload_user_sites = true) {
1343 $result = $this->rc->wp_delete('sites', array('user_id' => $this->user_id, 'site_id' => $site_id));
1344 $this->delete_site_meta($site_id);
1345 if ($reload_user_sites) $this->load_user_sites();
1346
1347 return $result;
1348 }
1349
1350 public function delete_site_by_url($url) {
1351
1352 // We used to do a direct delete... but we need the site ID in order to be able to wipe the site meta
1353 // $result = $this->rc->wp_delete('sites', array('user_id' => $this->user_id, 'url' => $url));
1354
1355 $result = 0;
1356
1357 foreach ($this->sites as $site_id => $site) {
1358 if (strtolower($url) == strtolower($site->url)) {
1359 $result += $this->delete_site_by_id($site_id, false);
1360 }
1361 }
1362
1363 $this->load_user_sites();
1364
1365 return $result;
1366 }
1367
1368 /**
1369 * This just gives some potential for the future, currently - currently, there's nothing we're forbidding through this mechanism
1370 *
1371 * @param string $do_what
1372 * @return Boolean
1373 */
1374 public function user_can($do_what) {
1375 $result = false;
1376
1377 $old_user_id = get_current_user_id();
1378 if ($old_user_id != $this->user_id) wp_set_current_user($this->user_id);
1379
1380 $is_admin = apply_filters('updraftcentral_user_can_is_admin', current_user_can('manage_options'), $this);
1381
1382 if ($old_user_id != $this->user_id) wp_set_current_user($old_user_id);
1383
1384 switch ($do_what) {
1385 case 'add_site':
1386 $result = $is_admin;
1387 break;
1388 case 'delete_site':
1389 $result = $is_admin;
1390 break;
1391 }
1392
1393 return apply_filters('updraftcentral_user_can', $result, $do_what, $this);
1394 }
1395
1396 /**
1397 * Gets list of loaded modules by hooking into `updraftcentral_main_navigation_items` filter
1398 * and sets each available module's visibility as user_meta if not already set.
1399 *
1400 * @param array $loaded_modules An array of loaded modules
1401 * @return array An array of loaded modules
1402 */
1403 public function main_navigation_items($loaded_modules) {
1404
1405 if ('' === get_user_meta($this->user_id, 'updraftcentral_modules_visibility', true)) {
1406 $updraftcentral_modules_visibility = array();
1407 foreach ($loaded_modules as $id => $item) {
1408 if ('sites' !== $id) {
1409 $updraftcentral_modules_visibility[$id] = true;
1410 }
1411 }
1412 // @codingStandardsIgnoreLine
1413 add_user_meta($this->user_id, 'updraftcentral_modules_visibility', $updraftcentral_modules_visibility, true);
1414 }
1415
1416 return $loaded_modules;
1417 }
1418
1419 /**
1420 * Handles module visibility status when it is changed in frontend.
1421 *
1422 * It fires when ajax call is made with `module_visibility` action.
1423 * Hooks into `updraftcentral_dashboard_ajaxaction_module_visibility` filter.
1424 * Receives module_id and its visibility and updates module's visibility status in
1425 * database as user_meta and returns the result of ajax call
1426 *
1427 * @param array $response An array to be returned to ajax call
1428 * @param array $post_data An array of data from ajax call
1429 * @return array An array of data contains result of ajax call
1430 */
1431 public function dashboard_ajaxaction_module_visibility($response, $post_data) {
1432
1433 if (empty($post_data['data']) || !is_array($post_data['data'])) {
1434 $response['responsetype'] = 'error';
1435 $response['code'] = 'empty';
1436 $response['message'] = __('There was a error, please try again.', 'updraftcentral');
1437 } else {
1438 $module_id = $post_data['data']['module_id'];
1439 $visibility = $post_data['data']['visibility'];
1440 $updraftcentral_modules_visibility = get_user_meta($this->user_id, 'updraftcentral_modules_visibility', true);
1441 if ("true" === $visibility) {
1442 $updraftcentral_modules_visibility[$module_id] = true;
1443 } else {
1444 $updraftcentral_modules_visibility[$module_id] = false;
1445 }
1446 update_user_meta($this->user_id, 'updraftcentral_modules_visibility', $updraftcentral_modules_visibility);
1447
1448 $response['responsetype'] = 'ok';
1449 $response['message'] = __('Visibility changed successfully', 'updraftcentral');
1450 }
1451
1452 $response['data'] = $post_data;
1453
1454 return $response;
1455 }
1456
1457 /**
1458 * Resets all modules visibility status.
1459 *
1460 * It fires when ajax call is made with `reset_modules_visibility` action.
1461 * Hooks into `updraftcentral_dashboard_ajaxaction_reset_modules_visibility` filter.
1462 * Updates every module's visibility status in
1463 * database as user_meta and returns the result of ajax call
1464 *
1465 * @param array $response An array to be returned to ajax call
1466 * @param array $post_data An array of data from ajax call
1467 * @return array An array of data contains result of ajax call
1468 */
1469 public function dashboard_ajaxaction_reset_modules_visibility($response, $post_data) {
1470 if (empty($post_data['data']) || 'all' !== $post_data['data']) {
1471 $response['responsetype'] = 'error';
1472 $response['code'] = 'empty';
1473 $response['message'] = __('There was a error, please try again.' . $post_data['data'], 'updraftcentral');
1474 } else {
1475 $updraftcentral_modules_visibility = get_user_meta($this->user_id, 'updraftcentral_modules_visibility', true);
1476 foreach ($updraftcentral_modules_visibility as $module_id => $visibility) {
1477 $updraftcentral_modules_visibility[$module_id] = true;
1478 }
1479 update_user_meta($this->user_id, 'updraftcentral_modules_visibility', $updraftcentral_modules_visibility);
1480 $response['responsetype'] = 'ok';
1481 $response['message'] = __('Visibility changed successfully', 'updraftcentral');
1482 }
1483 $response['data'] = $post_data;
1484 return $response;
1485 }
1486 }
1487
1488 endif;
1489