PluginProbe
UpdraftCentral Dashboard / 0.7.0
UpdraftCentral Dashboard v0.7.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.7.0, at classes/user.php

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