PluginProbe
UpdraftCentral Dashboard / 0.8.0
UpdraftCentral Dashboard v0.8.0
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.0, at classes/user.php

1,109 lines 41.0 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_edit_site_configuration', array($this, 'dashboard_ajaxaction_edit_site_configuration'), 10, 2);
38 add_filter('updraftcentral_dashboard_ajaxaction_edit_site_connection_method', array($this, 'dashboard_ajaxaction_edit_site_connection_method'), 10, 2);
39 add_filter('updraftcentral_dashboard_ajaxaction_delete_site', array($this, 'dashboard_ajaxaction_delete_site'), 10, 2);
40 add_filter('updraftcentral_dashboard_ajaxaction_sites_html', array($this, 'dashboard_ajaxaction_sites_html'));
41 add_filter('updraftcentral_dashboard_ajaxaction_site_rpc', array($this, 'dashboard_ajaxaction_site_rpc'), 10, 2);
42 add_filter('updraftcentral_dashboard_ajaxaction_manage_site_order', array($this, 'dashboard_ajaxaction_manage_site_order'), 10, 2);
43
44 add_filter('updraftcentral_load_user_sites', array($this, 'load_user_sites_filter'));
45 add_filter('updraftcentral_dashboard_ajaxaction_manage_site_meta', array($this, 'dashboard_ajaxaction_manage_site_meta'), 10, 2);
46
47 // Manage dashboard shortcuts through ajax request
48 add_filter('updraftcentral_dashboard_ajaxaction_shortcuts', array($this, 'dashboard_ajaxaction_shortcuts'), 10, 2);
49
50 // Handles module visibility
51 add_filter('updraftcentral_main_navigation_items', array($this, 'main_navigation_items'));
52 add_filter('updraftcentral_dashboard_ajaxaction_module_visibility', array($this, 'dashboard_ajaxaction_module_visibility'), 10, 2);
53 add_filter('updraftcentral_dashboard_ajaxaction_reset_modules_visibility', array($this, 'dashboard_ajaxaction_reset_modules_visibility'), 10, 2);
54
55 // Save timeout settings
56 add_filter('updraftcentral_dashboard_ajaxaction_save_timeout', array($this, 'dashboard_ajaxaction_save_timeout'), 10, 2);
57
58 // Load licence manager - this needs loading before the sites themselves are
59 if (!class_exists('UpdraftCentral_Licence_Manager')) include_once UD_CENTRAL_DIR.'/classes/licence-manager.php';
60
61 // Allow developers to implement their own licence management
62 $licence_manager_class = apply_filters('updraftcentral_licence_manager_class', 'UpdraftCentral_Licence_Manager');
63
64 $this->licence_manager = new $licence_manager_class($this, $this->rc);
65
66 $this->load_user_sites();
67
68 }
69
70 /**
71 * Saves user-defined timeout settings
72 *
73 * @param array $response A response array where we insert our request response
74 * @param array $post_data Parameters passed as an additional argument(s) to the request
75 * @return array
76 */
77 public function dashboard_ajaxaction_save_timeout($response, $post_data) {
78 $response['responsetype'] = 'ok';
79 $response['message'] = 'success';
80
81 $timeout = $post_data['data']['timeout'];
82 if (!empty($timeout)) {
83 update_user_meta($this->user_id, 'updraftcentral_dashboard_user_defined_timeout', $timeout);
84 }
85
86 return $response;
87 }
88
89 /**
90 * Gets the user defined timeout settings
91 *
92 * @param integer $default_timeout The default timeout when no user defined timeout is set. Defaults to 30 seconds.
93 * @return integer
94 */
95 public function get_user_defined_timeout($default_timeout = 30) {
96 if (!empty($this->user_id)) {
97 $timeout = get_user_meta($this->user_id, 'updraftcentral_dashboard_user_defined_timeout', true);
98 if (!empty($timeout)) return (int) $timeout;
99 }
100
101 return $default_timeout;
102 }
103
104 /**
105 * Retrieves all available tags for this user, filterable through site id and/or tag name
106 *
107 * @param integer $site_id Optional. The ID of the site where the tags is to be pulled from
108 * @param string $tag_name Optional. The name of the tag to search for
109 *
110 * @return array|object|null
111 */
112 public function get_site_tags($site_id = 0, $tag_name = '') {
113 global $wpdb;
114 $our_prefix = $wpdb->base_prefix.$this->rc->table_prefix;
115 $site_filter = !empty($site_id) ? ' AND s.`site_id` = '.$site_id : '';
116 $tag_filter = !empty($tag_name) ? ' AND m.`meta_value` = "'.$tag_name.'"' : '';
117
118 $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');
119
120 return $tags;
121 }
122
123 /**
124 * Saves user-defined shortcut keys and returns shortcut collection
125 *
126 * @param array $response A response array where we insert our request response
127 * @param array $post_data Parameters passed as an additional argument(s) to the request
128 * @return array
129 */
130 public function dashboard_ajaxaction_shortcuts($response, $post_data) {
131
132 $response['responsetype'] = 'ok';
133 $response['message'] = 'success';
134
135 $shortcuts = get_user_meta($this->user_id, 'updraftcentral_dashboard_shortcuts', true);
136 if (!is_array($shortcuts)) $shortcuts = array();
137
138 if (isset($post_data['data']['key'])) {
139 $shortcuts[$post_data['data']['name']] = $post_data['data']['key'];
140 } elseif (isset($post_data['data']['clear'])) {
141 $shortcuts = array();
142 }
143
144 update_user_meta($this->user_id, 'updraftcentral_dashboard_shortcuts', $shortcuts);
145
146 // Return current shortcuts collection
147 $response['shortcuts'] = $shortcuts;
148
149 return $response;
150
151 }
152
153 /**
154 * Process site meta management actions (add, delete, update and get) through ajax request
155 *
156 * @param array $response
157 * @param array $post_data - site meta parameters for the current action
158 * @return array
159 */
160 public function dashboard_ajaxaction_manage_site_meta($response, $post_data) {
161
162 try {
163
164 $response['responsetype'] = 'ok';
165 $response['message'] = '';
166
167 $site_meta = $this->rc->site_meta;
168
169 if (!empty($site_meta)) {
170 $data = $post_data['data'];
171
172 switch ($data['action']) {
173 case 'add':
174 $response['data'] = $site_meta->add_site_meta($data['site_id'], $data['meta_key'], $data['meta_value'], $data['unique']);
175 break;
176 case 'delete':
177 $response['data'] = $site_meta->delete_site_meta($data['site_id'], $data['meta_key'], $data['meta_value']);
178 break;
179 case 'get':
180 $response['data'] = $site_meta->get_site_meta($data['site_id'], $data['key'], $data['single']);
181 break;
182 case 'update':
183 $response['data'] = $site_meta->update_site_meta($data['site_id'], $data['meta_key'], $data['meta_value'], $data['prev_value']);
184 break;
185 default:
186 $response['message'] = 'The submitted site meta command was not recognized.';
187 break;
188 }
189 } else {
190 $response['message'] = 'Unable to pull the site meta instance.';
191 }
192 } catch (Exception $e) {
193 $response['responsetype'] = 'error';
194 $response['message'] = $e->getMessage();
195 // @codingStandardsIgnoreLine
196 } catch (Error $e) {
197 $response['responsetype'] = 'error';
198 $response['message'] = $e->getMessage();
199 }
200
201 return $response;
202 }
203
204 /**
205 * Used to update sort order in user meta
206 *
207 * @param array $response
208 * @param array $post_data - site_order is an indexed array of site id's in the sorted order
209 * @return array
210 */
211 public function dashboard_ajaxaction_manage_site_order($response, $post_data) {
212
213 if (isset($post_data['data']['site_order'])) {
214
215 $user_id = $this->user_id;
216 $response['responsetype'] = "ok";
217 $response['message'] = 'No change';
218
219 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)
220
221 if (update_user_meta($user_id, 'updraftcentral_dashboard_site_order', $post_data['data']['site_order'])) {
222 $response['message'] = 'success';
223 } else {
224 $response['message'] = 'fail';
225 }
226 }
227 } else {
228 $response['responsetype'] = "error";
229 $response['message'] = "Missing site order data";
230 }
231
232 return $response;
233 }
234
235 public function get_licence_manager() {
236 return $this->licence_manager;
237 }
238
239 /**
240 * TODO: 1) Catch PHP events on the mothership, pass them on and let them be console.logged
241 * 2) Pass on caught output from the remote side, and get it console.logged
242 *
243 * @param array $response
244 * @param array $post_data
245 * @return array
246 */
247 public function dashboard_ajaxaction_site_rpc($response, $post_data) {
248
249 return $this->send_remote_command($post_data, false, $response);
250
251 }
252
253 /**
254 * Sends UpdraftCentral's command to the remote website
255 *
256 * @param array $data An array container the command to execute along with its command parameters
257 * @param boolean $force_save Optional. A flag that indicates whether UpdraftCentral will save and cache the response from the remote website
258 * @param array $response Optional. A response container that gets populated with the remote website's response from the current request
259 */
260 public function send_remote_command($data, $force_save = false, $response = array()) {
261
262 try {
263
264 // Load site rpc
265 if (!class_exists('UpdraftCentral_Remote_Communications')) include_once UD_CENTRAL_DIR.'/classes/class-siterpc.php';
266 $site_rpc = new UpdraftCentral_Remote_Communications($this, $response, $data);
267
268 if ($validate_result = $site_rpc->validate_input()) {
269 // Send command to remote website.
270 $response = $site_rpc->send_message($force_save);
271 } else {
272 // Return error from validation process
273 $response = $validate_result;
274 }
275
276 } catch (Exception $e) {
277 $response['responsetype'] = 'error';
278 $response['message'] = $e->getMessage();
279 }
280
281 return $response;
282 }
283
284 public function deep_sanitize($input, $sanitize_function = 'htmlspecialchars') {
285 if (is_string($input)) return call_user_func($sanitize_function, $input);
286 if (is_array($input)) {
287 foreach ($input as $k => $v) {
288 $input[$k] = $this->deep_sanitize($v, $sanitize_function);
289 }
290 }
291
292 return $input;
293 }
294
295 public function send_message($ud_rpc, $message, $data = null, $timeout = 30) {
296 $this->php_events = array();
297
298 // Override http timeout argument based from the user defined timeout
299 $timeout = $this->get_user_defined_timeout($timeout);
300
301 if ('__updraftcentral_internal_preencrypted' == $message) {
302
303 $post_options = array(
304 'timeout' => $timeout,
305 'body' => $data,
306 );
307
308 $post_options = apply_filters('udrpc_post_options', $post_options, $message, $data, $timeout, $this);
309
310 try {
311 $post = $ud_rpc->http_post($post_options);
312 } catch (Exception $e) {
313 // Curl can return an error code 0, which causes WP_Error to return early, without recording the message. So, we prefix the code.
314 return new WP_Error('http_post_'.$e->getCode(), $e->getMessage());
315 }
316
317 if (is_wp_error($post)) return $post;
318
319 if (empty($post['response']) || empty($post['response']['code'])) return new WP_Error('empty_http_code', 'Unexpected HTTP response code');
320
321 if ($post['response']['code'] < 200 || $post['response']['code'] >= 300) return new WP_Error('unexpected_http_code', 'Unexpected HTTP response code ('.$post['response']['code'].')', $post);
322
323 if (empty($post['body'])) return new WP_Error('empty_response', 'Empty response from remote site');
324
325 return (string) $post['body'];
326
327 } else {
328 $response = $ud_rpc->send_message($message, $data, $timeout);
329 }
330
331 // TODO: Handle caught_output
332
333 if (is_array($response) && !empty($response['data']) && is_array($response['data']) && !empty($response['data']['php_events']) && !empty($response['data']['previous_data'])) {
334 // global $updraftplus;
335 $this->php_events = $response['data']['php_events'];
336 if (defined('WP_DEBUG') && WP_DEBUG) {
337 foreach ($response['data']['php_events'] as $logline) {
338 error_log('From remote side: '.$logline);
339 }
340 }
341 $response['data'] = $response['data']['previous_data'];
342 }
343
344 return $response;
345 }
346
347 public function dashboard_ajaxaction_sites_html($response) {
348 $response['responsetype'] = 'ok';
349 $response['sites_html'] = $this->get_sites_html();
350 $response['status_info'] = array(
351 'how_many_licences_in_use' => $this->licence_manager->how_many_licences_in_use(),
352 'how_many_licences_available' => $this->licence_manager->how_many_licences_available(),
353 );
354 $response['message'] = __('The site list has been refreshed.', 'updraftcentral');
355
356 return $response;
357 }
358
359 public function dashboard_ajaxaction_delete_site($response, $post_data) {
360 if (isset($post_data['data']) && is_array($post_data['data']) && !empty($post_data['data']['site_id'])) {
361
362 $deleted = $this->delete_site_by_id((int) $post_data['data']['site_id']);
363
364 if (is_wp_error($deleted)) {
365 $response = $deleted;
366 } else {
367 $response['responsetype'] = 'ok';
368 $response['status_info'] = array(
369 'how_many_licences_in_use' => $this->licence_manager->how_many_licences_in_use(),
370 'how_many_licences_available' => $this->licence_manager->how_many_licences_available(),
371 );
372 $response['sites_html'] = $this->get_sites_html();
373 $response['message'] = __('The site was successfully deleted from your dashboard.', 'updraftcentral');
374 }
375
376 } else {
377 $response['responsetype'] = 'error';
378 $response['code'] = 'missing_data';
379 $response['message'] = __('Missing information', 'updraftcentral');
380 }
381
382 return $response;
383 }
384
385 public function dashboard_ajaxaction_edit_site_connection_method($response, $post_data) {
386
387 if (isset($post_data['data']) && is_array($post_data['data']) && !empty($post_data['data']['site_id'])) {
388
389 $site_id = (int) $post_data['data']['site_id'];
390
391 $connection_method = isset($post_data['data']['connection_method']) ? (string) $post_data['data']['connection_method'] : 'direct_default_auth';
392
393 $updated = $this->rc->wp_update('sites',
394 array(
395 'connection_method' => $connection_method,
396 ),
397 array(
398 'user_id' => $this->user_id,
399 'site_id' => $site_id,
400 ),
401 array(
402 '%s',
403 ),
404 array(
405 '%d',
406 '%d',
407 )
408 );
409
410 if (is_numeric($updated)) {
411 $response['responsetype'] = 'ok';
412
413 $this->load_user_sites();
414 $response['sites_html'] = $this->get_sites_html();
415 $response['status_info'] = array(
416 'how_many_licences_in_use' => $this->licence_manager->how_many_licences_in_use(),
417 'how_many_licences_available' => $this->licence_manager->how_many_licences_available(),
418 );
419
420 $response['message'] = __('The site configuration was successfully edited.', 'updraftcentral');
421 } else {
422 $response = $updated;
423 }
424
425 } else {
426 $response['responsetype'] = 'error';
427 $response['code'] = 'missing_data';
428 $response['message'] = __('Missing information', 'updraftcentral');
429 }
430
431 return $response;
432
433 }
434
435 public function dashboard_ajaxaction_edit_site_configuration($response, $post_data) {
436
437 if (isset($post_data['data']) && is_array($post_data['data']) && !empty($post_data['data']['site_id']) && isset($post_data['data']['description'])) {
438
439 $site_id = (int) $post_data['data']['site_id'];
440
441 $connection_method = isset($post_data['data']['connection_method']) ? (string) $post_data['data']['connection_method'] : 'direct_default_auth';
442 $send_cors_headers = (isset($post_data['data']['send_cors_headers']) && $post_data['data']['send_cors_headers']) ? 1 : 0;
443
444 $updated = $this->rc->wp_update('sites',
445 array(
446 'description' => (string) $post_data['data']['description'],
447 'connection_method' => $connection_method,
448 'send_cors_headers' => $send_cors_headers,
449 ),
450 array(
451 'user_id' => $this->user_id,
452 'site_id' => $site_id,
453 ),
454 array(
455 '%s',
456 '%s',
457 '%d',
458 ),
459 array(
460 '%d',
461 '%d',
462 )
463 );
464
465 if (is_numeric($updated)) {
466 $response['responsetype'] = 'ok';
467
468 $extra_site_info_unparsed = empty($post_data['data']['extra_site_info']) ? false : $post_data['data']['extra_site_info'];
469 if (!$extra_site_info_unparsed) {
470 $extra_site_info = array();
471 } else {
472 parse_str($extra_site_info_unparsed, $extra_site_info);
473 }
474
475 if (!empty($extra_site_info)) {
476 foreach ($extra_site_info as $meta_key => $meta_value) {
477 $this->rc->site_meta->update_site_meta($site_id, $meta_key, $meta_value);
478 }
479 }
480
481 $this->load_user_sites();
482 $response['sites_html'] = $this->get_sites_html();
483 $response['status_info'] = array(
484 'how_many_licences_in_use' => $this->licence_manager->how_many_licences_in_use(),
485 'how_many_licences_available' => $this->licence_manager->how_many_licences_available(),
486 );
487
488 $response['message'] = __('The site configuration was successfully edited.', 'updraftcentral');
489 } else {
490
491 $response = $updated;
492 }
493
494 } else {
495 $response['responsetype'] = 'error';
496 $response['code'] = 'missing_data';
497 $response['message'] = __('Missing information', 'updraftcentral');
498 }
499
500 return $response;
501 }
502
503 public function dashboard_ajaxaction_newsite($response, $post_data) {
504
505 if (empty($post_data['data']) || !is_array($post_data['data']) || empty($post_data['data']['key'])) {
506 $response['responsetype'] = 'error';
507 $response['code'] = 'empty';
508 $response['message'] = __('Please enter the site key.', 'updraftcentral');
509 } else {
510
511 $site_key = $post_data['data']['key'];
512
513 $extra_site_info_unparsed = empty($post_data['data']['extra_site_info']) ? false : $post_data['data']['extra_site_info'];
514 if (!$extra_site_info_unparsed) {
515 $extra_site_info = array();
516 } else {
517 parse_str($extra_site_info_unparsed, $extra_site_info);
518 }
519
520 $ud_rpc = $this->rc->get_udrpc();
521
522 // A bundle has these keys: key, name_indicator, url
523 $decode_bundle = $ud_rpc->decode_portable_bundle($site_key, 'base64_with_count');
524
525 if (!is_array($decode_bundle) || !empty($decode_bundle['code'])) {
526 $response['responsetype'] = 'error';
527 $response['message'] = __('Error:', 'updraftcentral');
528 $response['code'] = empty($decode_bundle['code']) ? 'could_not_decode' : $decode_bundle['code'];
529 if (!empty($decode_bundle['code']) && 'invalid_wrong_length' == $decode_bundle['code']) {
530 $response['message'] .= ' '.__('The entered key was the wrong length - please try again.', 'updraftcentral');
531 } elseif (!empty($decode_bundle['code']) && 'invalid_corrupt' == $decode_bundle['code']) {
532 $response['message'] .= ' '.__('The entered key was corrupt - please try again.', 'updraftcentral').' ('.$decode_bundle['data'].')';
533 } elseif (empty($decode_bundle['key']) || empty($decode_bundle['url']) || empty($decode_bundle['name_indicator'])) {
534 $response['message'] .= ' '.__('The entered key was corrupt - please try again.', 'updraftcentral');
535 $response['data'] = $decode_bundle;
536 }
537 } elseif (empty($decode_bundle['key']) || empty($decode_bundle['url']) || empty($decode_bundle['user_id'])) {
538 $response['message'] = __('Error:', 'updraftcentral').' '.__('The entered key was corrupt - please try again.', 'updraftcentral');
539 $response['code'] = 'corrupt_key';
540 $response['data'] = $decode_bundle;
541 } else {
542
543 if (trailingslashit(network_site_url()) == $decode_bundle['url'] && !apply_filters('updraftcentral_allow_self_control', true)) {
544 $response['responsetype'] = 'error';
545 $response['code'] = 'this_site';
546 $response['message'] = __('Error:', 'updraftcentral').' '.__('The entered key does not belong to a remote site (it belongs to this one).', 'updraftcentral');
547 } 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'])) {
548 // 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.
549 $response['responsetype'] = 'error';
550 $response['code'] = 'cant_add_localhost';
551 $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');
552 } else {
553 // Was the key sent SSL to us directly?
554 $key = $decode_bundle['key'];
555 if (is_array($key) && !empty($key['key_hash']) && isset($key['key_id'])) {
556 global $wpdb;
557 // Allow them 3 hours to copy-and-paste their key
558 $wpdb->query('DELETE FROM '.$wpdb->base_prefix.$this->rc->table_prefix.'site_temporary_keys WHERE created<='.(int) (time() - 10800));
559 $key_info = $this->rc->wp_get_row('site_temporary_keys', $wpdb->prepare('key_id=%d', $key['key_id']));
560 if (is_object($key_info) && !empty($key_info->key_local_private) && !empty($key_info->key_remote_public)) {
561 $this->rc->wp_delete('site_temporary_keys', array('key_id' => $key['key_id']));
562 $key_hash = hash('sha256', $key_info->key_remote_public);
563
564 // @codingStandardsIgnoreLine
565 if ((function_exists('hash_equals') && hash_equals($key_hash, $key['key_hash'])) || (!function_exists('hash_equals') && $key_hash === $key['key_hash'])) {
566 $key_local_private = $key_info->key_local_private;
567 $key_remote_public = $key_info->key_remote_public;
568 } else {
569 $response['responsetype'] = 'error';
570 $response['code'] = 'wrong_hash';
571 $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'));
572
573 return $response;
574 }
575 } else {
576 $response['responsetype'] = 'error';
577 $response['code'] = 'no_key_found';
578 $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'));
579
580 return $response;
581 }
582
583 } elseif (!empty($decode_bundle['mothership_firewalled'])) {
584
585 // Need to do direct AJAX from the browser to the mothership to send our key
586
587 $ud_rpc = $this->rc->get_udrpc('central_host.updraftplus.com', true);
588 if (false != $ud_rpc->generate_new_keypair()) {
589 $key_remote_public = $key;
590 $key_local_private = $ud_rpc->get_key_local();
591 } else {
592 $response['responsetype'] = 'error';
593 $response['code'] = 'keygen_error';
594 $response['message'] = 'An error occurred when attempting to generate a new key-pair';
595
596 return $response;
597 }
598
599 } else {
600 $key_remote_public = $key;
601 $key_local_private = false;
602 }
603
604 $remote_site_id = empty($decode_bundle['ms_id']) ? 0 : $decode_bundle['ms_id'];
605 $description = isset($decode_bundle['site_title']) ? (string) $decode_bundle['site_title'] : '';
606
607 $send_cors_headers = (isset($post_data['data']['send_cors_headers']) && !$post_data['data']['send_cors_headers']) ? 0 : 1;
608 $connection_method = isset($post_data['data']['connection_method']) ? (string) $post_data['data']['connection_method'] : 'direct_default_auth';
609
610 $site_url = $decode_bundle['url'];
611
612 // Supply a default for legacy format keys that didn't include the admin URL
613 $admin_url = empty($decode_bundle['admin_url']) ? trailingslashit($site_url).'wp-admin' : $decode_bundle['admin_url'];
614
615 $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);
616
617 if (true === $added) {
618 $response['responsetype'] = 'ok';
619
620 global $wpdb;
621 $new_site_id = $wpdb->insert_id;
622
623 if (!empty($extra_site_info)) {
624 if (!is_array($this->sites_meta)) $this->sites_meta = array();
625 if (empty($this->sites_meta[$new_site_id])) $this->sites_meta[$new_site_id] = array();
626 foreach ($extra_site_info as $meta_key => $meta_value) {
627 if (!$meta_value) continue;
628 // Don't bother to save the default value on the initial adding of the site
629 if ('http_authentication_method' == $meta_key && 'basic' == $meta_value) continue;
630 $result = $this->rc->site_meta->add_site_meta($new_site_id, $meta_key, $meta_value);
631 if (false !== $result) {
632 $created = apply_filters("get_site_metadata_created", false, $new_site_id, $meta_key);
633 if (!$created) {
634 // Fallback if in case we fail to retrieve the "created" value from
635 // the site metadata table. But most likely, if the add_site_meta succeeded
636 // then we would have a value for the $created variable.
637 $created = time();
638 }
639
640 $meta = new stdClass();
641 $meta->value = $meta_value;
642 $meta->created = $created;
643
644 // We update the in-memory copy because this is used by get_sites_html()
645 $this->sites_meta[$new_site_id][$meta_key] = $meta;
646 }
647 }
648 }
649
650 // Return the new HTML widget to the front end
651 $response['sites_html'] = $this->get_sites_html();
652 $response['status_info'] = array(
653 'how_many_licences_in_use' => $this->licence_manager->how_many_licences_in_use(),
654 'how_many_licences_available' => $this->licence_manager->how_many_licences_available(),
655 );
656
657 $response['message'] = __('The key was successfully added.', 'updraftcentral').' '.__('It is for interacting with the following site: ', 'updraftcentral').htmlspecialchars($decode_bundle['url']);
658
659 if (!empty($decode_bundle['mothership_firewalled_callback_url'])) {
660 $response['key_needs_sending'] = array(
661 'site_id' => $new_site_id,
662 'url' => $decode_bundle['mothership_firewalled_callback_url'],
663 'updraft_key_index' => $decode_bundle['updraft_key_index'],
664 'remote_public_key' => $ud_rpc->get_key_remote(),
665 );
666 }
667
668 } else {
669 $response['responsetype'] = 'error';
670 $response['code'] = $added->get_error_code();
671 $response['message'] = __('Error:', 'updraftcentral').' '.$added->get_error_message();
672 }
673
674 }
675 }
676 }
677
678 return $response;
679 }
680
681 public function load_user_sites_filter($sites) {
682 $how_many_licences_available = $this->licence_manager->how_many_licences_available();
683 $how_many_licences_in_use = count($sites);
684
685 if ($how_many_licences_available >= $how_many_licences_in_use || $how_many_licences_available < 0) return $sites;
686
687 $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);
688
689 $this->rc->log_notice($log_message, 'error', 'not_enough_licences');
690
691 $i = 0;
692 foreach ($sites as $site_id => $site) {
693 if ($i >= $how_many_licences_available) {
694 $site->unlicensed = true;
695 $sites[$site_id] = $site;
696 }
697 ++$i;
698 }
699
700 return $sites;
701 }
702
703 /**
704 * Populate $this->sites and $this->sites_meta in accordance with the current user ($this->user_id)
705 *
706 * @return Array|WP_Error - either the same list of sites as will be in $this->sites, or a WP_Error if something went wrong
707 */
708 public function load_user_sites() {
709 global $wpdb;
710 $sites = $wpdb->get_results('SELECT * FROM '.$this->sites_table.' WHERE user_id='.absint($this->user_id));
711
712 $this->sites_meta = array();
713
714 $subsequent_site = false;
715 if (is_array($sites) && !empty($sites)) {
716 $sites_meta_sql = 'SELECT * FROM '.$this->sitemeta_table.' WHERE site_id IN (';
717 foreach ($sites as $site) {
718 if ($subsequent_site) {
719 $sites_meta_sql .= ',';
720 } else {
721 $subsequent_site = true;
722 }
723 $sites_meta_sql .= absint($site->site_id);
724 }
725 $sites_meta_sql .= ')';
726 $sites_meta = $wpdb->get_results($sites_meta_sql);
727 } else {
728 $sites_meta = array();
729 }
730
731 if (is_array($sites_meta)) {
732 foreach ($sites_meta as $meta_row) {
733 if (isset($meta_row->site_id)) {
734 // N.B. Since we're trying to include the 'created' column in the site metadata when loaded or pulled from
735 // the database, therefore, we assign an anonymous object to encapsulate the value of the current meta_key along with
736 // its created column.
737
738 $meta = new stdClass();
739 $meta->value = $meta_row->meta_value;
740 $meta->created = $meta_row->created;
741
742 $this->sites_meta[$meta_row->site_id][$meta_row->meta_key] = $meta;
743 }
744 }
745 }
746
747 if (is_array($sites)) {
748
749 $processed_sites = array();
750 foreach ($sites as $site) {
751 $processed_sites[$site->site_id] = $site;
752 }
753 $this->sites = apply_filters('updraftcentral_load_user_sites', $processed_sites, $this, $this->licence_manager);
754
755 return $this->sites;
756
757 } elseif (is_wp_error($sites)) {
758
759 $this->rc->log_notice($sites);
760 $this->sites = null;
761
762 return $sites;
763 }
764 }
765
766 public function get_sites_html() {
767
768 $ret = '';
769
770 // Get sites. Print a line for each of them.
771 if (empty($this->sites) || !is_array($this->sites)) {
772 $ret .= $this->rc->include_template('sites/none-set-up.php', true, array('common_urls' => $this->rc->get_common_urls()));
773 } else {
774
775 // retrieve metadata if any exists
776 $user_id = $this->user_id;
777
778 // ensure we have an array (in even of no metadata)
779
780 if (!$site_order_meta = get_user_meta($user_id, 'updraftcentral_dashboard_site_order', true)) {
781 $site_order_meta = array();
782 }
783
784 // Add existing sites if not in siteOrderMeta (i.e. any new added sites or handling no meta data)
785
786 foreach ($this->sites as $site) {
787 if (!in_array($site->site_id, $site_order_meta)) {
788 array_push($site_order_meta, $site->site_id);
789 }
790 }
791
792 // Render the site rows in site_order_meta sequence using site_id to reference sites object
793
794 foreach ($site_order_meta as $site_id_meta) {
795
796 // Ignore invalid or removed sites
797
798 if (!empty($this->sites[$site_id_meta])) {
799
800 $connection_method = isset($this->sites[$site_id_meta]->connection_method) ? (string) $this->sites[$site_id_meta]->connection_method : 'direct_default_auth';
801 $send_cors_headers = (isset($this->sites[$site_id_meta]->send_cors_headers) && !$this->sites[$site_id_meta]->send_cors_headers) ? 0 : 1;
802
803 $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) . '"';
804
805 if (empty($this->sites[$site_id_meta]->admin_url)) {
806 $admin_url = trailingslashit($this->sites[$site_id_meta]->url) . 'wp-admin';
807 } else {
808 $admin_url = $this->sites[$site_id_meta]->admin_url;
809 }
810 $site_data_attributes .= ' data-admin_url="' . esc_attr($admin_url) . '"';
811
812 $site_meta = empty($this->sites_meta[$site_id_meta]) ? array() : $this->sites_meta[$site_id_meta];
813 if (!empty($site_meta)) {
814 if (!empty($site_meta['http_username']->value)) {
815 $http_password = empty($site_meta['http_password']->value) ? '' : $site_meta['http_password']->value;
816 $site_data_attributes .= ' data-http_username="' . esc_attr($site_meta['http_username']->value) . '" data-http_password="' . esc_attr($http_password) . '"';
817 if (!empty($site_meta['http_authentication_method']->value)) $site_data_attributes .= ' data-http_authentication_method="' . $site_meta['http_authentication_method']->value . '"';
818 }
819 }
820
821 if (empty($this->sites[$site_id_meta]->unlicensed)) {
822 if ('via_mothership_encrypting' != $connection_method) {
823 $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) . '"';
824 }
825 } else {
826 $site_data_attributes .= ' data-site_unlicensed="1"';
827 }
828
829 $site_data_attributes .= ' data-connection_method="' . esc_attr($connection_method) . '" data-send_cors_headers="' . $send_cors_headers . '"';
830
831 $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));
832 }
833 }
834 }
835
836 return $ret;
837 }
838
839 /**
840 * Returns false if not authorised at all; or a timestamp if it's authorised until a particular date
841 *
842 * @param int $site_id [description]
843 * @return boolean|integer
844 */
845 public function authorised_for_site_until($site_id) {
846
847 if (!is_array($this->sites)) $this->load_user_sites();
848
849 if (is_array($this->sites)) {
850 foreach ($this->sites as $site) {
851 if ((int) $site_id == (int) $site->site_id && isset($site->licence_until)) {
852 return apply_filters('updraftcentral_authorised_for_site_until', (int) $site->licence_until, $site_id, $this->sites);
853 }
854 }
855 }
856
857 return apply_filters('updraftcentral_authorised_for_site_until', false, $site_id, $this->sites);
858
859 }
860
861 /**
862 * Adding a site
863 *
864 * @param string $url
865 * @param string $admin_url
866 * @param string $key_local_private
867 * @param string $key_remote_public
868 * @param integer $remote_user_id
869 * @param string $remote_user_login
870 * @param string $key_name_indicator
871 * @param integer $remote_site_id
872 * @param string $description
873 * @param string $connection_method
874 * @param Boolean $send_cors_headers
875 * @return Boolean|WP_Error - if a Boolean, then it will be true
876 */
877 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) {
878
879 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);
880
881 if (!$this->licence_manager->is_slot_available()) {
882 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')));
883 }
884
885 $this->delete_site_by_url($url);
886
887 $added = $this->rc->wp_insert('sites',
888 array(
889 'user_id' => $this->user_id,
890 'url' => $url,
891 'admin_url' => $admin_url,
892 'key_local_private' => $key_local_private,
893 'key_remote_public' => $key_remote_public,
894 'description' => $description,
895 'connection_method' => $connection_method,
896 'send_cors_headers' => $send_cors_headers,
897 'sequence_id' => 0,
898 'remote_user_id' => $remote_user_id,
899 'remote_user_login' => $remote_user_login,
900 'remote_site_id' => $remote_site_id,
901 'key_name_indicator' => $key_name_indicator,
902 ),
903 array(
904 '%d',
905 '%s',
906 '%s',
907 '%s',
908 '%s',
909 '%s',
910 '%s',
911 '%d',
912 '%d',
913 '%d',
914 '%s',
915 '%d',
916 '%s',
917 )
918 );
919
920 if (is_numeric($added)) {
921 $result = true;
922 } else {
923 $result = $added;
924 }
925
926 $this->load_user_sites();
927
928 return $result;
929
930 }
931
932 /**
933 * Delete all site meta for a specified site
934 *
935 * @param Integer $site_id - the site ID
936 *
937 * @uses UpdraftCentral::wp_delete()
938 *
939 * @return Integer|WP_Error - the number of rows deleted, or a WP_Error object
940 */
941 public function delete_site_meta($site_id) {
942 return $this->rc->wp_delete('sitemeta', array('site_id' => $site_id));
943 }
944
945 /**
946 * Delete all sites for the user
947 */
948 public function delete_all_sites() {
949
950 if (!is_array($this->sites)) return;
951
952 $counter = 1;
953
954 foreach ($this->sites as $site_id => $site) {
955 $reload_user_sites = ($counter >= $this->sites);
956 $this->delete_site_by_id($site_id, $reload_user_sites);
957 $counter ++;
958 }
959
960 }
961
962 public function delete_site_by_id($site_id, $reload_user_sites = true) {
963 $result = $this->rc->wp_delete('sites', array('user_id' => $this->user_id, 'site_id' => $site_id));
964 $this->delete_site_meta($site_id);
965 if ($reload_user_sites) $this->load_user_sites();
966
967 return $result;
968 }
969
970 public function delete_site_by_url($url) {
971
972 // We used to do a direct delete... but we need the site ID in order to be able to wipe the site meta
973 // $result = $this->rc->wp_delete('sites', array('user_id' => $this->user_id, 'url' => $url));
974
975 $result = 0;
976
977 foreach ($this->sites as $site_id => $site) {
978 if (strtolower($url) == strtolower($site->url)) {
979 $result += $this->delete_site_by_id($site_id, false);
980 }
981 }
982
983 $this->load_user_sites();
984
985 return $result;
986 }
987
988 /**
989 * This just gives some potential for the future, currently - currently, there's nothing we're forbidding through this mechanism
990 *
991 * @param string $do_what
992 * @return Boolean
993 */
994 public function user_can($do_what) {
995 $result = false;
996
997 $old_user_id = get_current_user_id();
998 if ($old_user_id != $this->user_id) wp_set_current_user($this->user_id);
999
1000 $is_admin = apply_filters('updraftcentral_user_can_is_admin', current_user_can('manage_options'), $this);
1001
1002 if ($old_user_id != $this->user_id) wp_set_current_user($old_user_id);
1003
1004 switch ($do_what) {
1005 case 'add_site':
1006 $result = $is_admin;
1007 break;
1008 case 'delete_site':
1009 $result = $is_admin;
1010 break;
1011 }
1012
1013 return apply_filters('updraftcentral_user_can', $result, $do_what, $this);
1014 }
1015
1016 /**
1017 * Gets list of loaded modules by hooking into `updraftcentral_main_navigation_items` filter
1018 * and sets each available module's visibility as user_meta if not already set.
1019 *
1020 * @param array $loaded_modules An array of loaded modules
1021 * @return array An array of loaded modules
1022 */
1023 public function main_navigation_items($loaded_modules) {
1024
1025 if ('' === get_user_meta($this->user_id, 'updraftcentral_modules_visibility', true)) {
1026 $updraftcentral_modules_visibility = array();
1027 foreach ($loaded_modules as $id => $item) {
1028 if ('sites' !== $id) {
1029 $updraftcentral_modules_visibility[$id] = true;
1030 }
1031 }
1032 // @codingStandardsIgnoreLine
1033 add_user_meta($this->user_id, 'updraftcentral_modules_visibility', $updraftcentral_modules_visibility, true);
1034 }
1035
1036 return $loaded_modules;
1037 }
1038
1039 /**
1040 * Handles module visibility status when it is changed in frontend.
1041 *
1042 * It fires when ajax call is made with `module_visibility` action.
1043 * Hooks into `updraftcentral_dashboard_ajaxaction_module_visibility` filter.
1044 * Receives module_id and its visibility and updates module's visibility status in
1045 * database as user_meta and returns the result of ajax call
1046 *
1047 * @param array $response An array to be returned to ajax call
1048 * @param array $post_data An array of data from ajax call
1049 * @return array An array of data contains result of ajax call
1050 */
1051 public function dashboard_ajaxaction_module_visibility($response, $post_data) {
1052
1053 if (empty($post_data['data']) || !is_array($post_data['data'])) {
1054 $response['responsetype'] = 'error';
1055 $response['code'] = 'empty';
1056 $response['message'] = __('There was a error, please try again.', 'updraftcentral');
1057 } else {
1058 $module_id = $post_data['data']['module_id'];
1059 $visibility = $post_data['data']['visibility'];
1060 $updraftcentral_modules_visibility = get_user_meta($this->user_id, 'updraftcentral_modules_visibility', true);
1061 if ("true" === $visibility) {
1062 $updraftcentral_modules_visibility[$module_id] = true;
1063 } else {
1064 $updraftcentral_modules_visibility[$module_id] = false;
1065 }
1066 update_user_meta($this->user_id, 'updraftcentral_modules_visibility', $updraftcentral_modules_visibility);
1067
1068 $response['responsetype'] = 'ok';
1069 $response['message'] = __('Visibility changed successfully', 'updraftcentral');
1070 }
1071
1072 $response['data'] = $post_data;
1073
1074 return $response;
1075 }
1076
1077 /**
1078 * Resets all modules visibility status.
1079 *
1080 * It fires when ajax call is made with `reset_modules_visibility` action.
1081 * Hooks into `updraftcentral_dashboard_ajaxaction_reset_modules_visibility` filter.
1082 * Updates every module's visibility status in
1083 * database as user_meta and returns the result of ajax call
1084 *
1085 * @param array $response An array to be returned to ajax call
1086 * @param array $post_data An array of data from ajax call
1087 * @return array An array of data contains result of ajax call
1088 */
1089 public function dashboard_ajaxaction_reset_modules_visibility($response, $post_data) {
1090 if (empty($post_data['data']) || 'all' !== $post_data['data']) {
1091 $response['responsetype'] = 'error';
1092 $response['code'] = 'empty';
1093 $response['message'] = __('There was a error, please try again.' . $post_data['data'], 'updraftcentral');
1094 } else {
1095 $updraftcentral_modules_visibility = get_user_meta($this->user_id, 'updraftcentral_modules_visibility', true);
1096 foreach ($updraftcentral_modules_visibility as $module_id => $visibility) {
1097 $updraftcentral_modules_visibility[$module_id] = true;
1098 }
1099 update_user_meta($this->user_id, 'updraftcentral_modules_visibility', $updraftcentral_modules_visibility);
1100 $response['responsetype'] = 'ok';
1101 $response['message'] = __('Visibility changed successfully', 'updraftcentral');
1102 }
1103 $response['data'] = $post_data;
1104 return $response;
1105 }
1106 }
1107
1108 endif;
1109