PluginProbe
RabbitLoader / 3.0.5
RabbitLoader v3.0.5
4.0.2 4.0.1 4.0 3.2.0 3.1.1 3.1.0 3.0.5 3.0.3 3.0.4 2.17.1 2.17.2 2.17.3 2.17.4 2.17.5 2.17.6 2.17.7 2.18.0 2.18.1 2.18.2 2.18.3 2.18.4 2.18.5 2.18.6 2.18.7 2.18.8 All 129 releases
rabbit-loader / inc / admin.php

admin.php in RabbitLoader 3.0.5, at inc/admin.php

979 lines 41.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 class RabbitLoader_21_Admin
4 {
5
6 public static $rabbitloader_cache_warnings = false;
7 public static $admin_notice_shown = false;
8
9 const PURGE_POST_CHANGE = "PURGE_POST";
10 const PURGE_THEME_CHANGE = "PURGE_THEME";
11 const PURGE_PLUG_CHANGE = "PURGE_PLUGIN";
12 const PURGE_MANUAL_USER = "PURGE_USER";
13 const SURVEY_DIS_PERMA = 999;
14
15 public static function addActions()
16 {
17 add_action('admin_notices', 'RabbitLoader_21_Admin::admin_notices');
18 add_action('admin_init', 'RabbitLoader_21_Admin::admin_init');
19 add_action('network_admin_notices', 'RabbitLoader_21_Admin::admin_notices');
20 add_action('admin_menu', 'RabbitLoader_21_Admin::leftMenuOption');
21 add_action('admin_enqueue_scripts', function () {
22 $is_rl_page = RabbitLoader_21_Util_Core::isRLPage();
23 $dependencies = ['jquery'];
24 if ($is_rl_page) {
25 wp_enqueue_script('rabbitloader-react', 'no', [], false);
26 wp_enqueue_script('rabbitloader-react-dom', 'no', [], false);
27 $dependencies[] = 'rabbitloader-react';
28 $dependencies[] = 'rabbitloader-react-dom';
29 }
30
31 wp_enqueue_script('rabbitloader-index', RABBITLOADER_PLUG_URL . 'admin/js/index.js', $dependencies, RABBITLOADER_PLUG_VERSION);
32 if ($is_rl_page) {
33 $guard_config = wp_json_encode([
34 'adminAjax' => admin_url('admin-ajax.php'),
35 'isConnected' => self::isPluginActivated(),
36 ], JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT);
37 if ($guard_config !== false) {
38 wp_add_inline_script('rabbitloader-index', 'window.RLConflictGuardConfig = ' . $guard_config . ';', 'before');
39 }
40
41 wp_enqueue_script('rabbitloader-conflict-guard', RABBITLOADER_PLUG_URL . 'admin/js/conflict-guard.js', ['rabbitloader-index'], RABBITLOADER_PLUG_VERSION, true);
42 $proof_config = wp_json_encode(self::getConnectProofConfig(), JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT);
43 if ($proof_config !== false) {
44 wp_add_inline_script('rabbitloader-index', 'window.RLConnectProofConfig = ' . $proof_config . ';', 'before');
45 }
46 wp_enqueue_script('rabbitloader-connect-proof', RABBITLOADER_PLUG_URL . 'admin/js/connect-proof.js', ['rabbitloader-index'], RABBITLOADER_PLUG_VERSION, true);
47 }
48 if ($is_rl_page && self::isPluginActivated()) {
49 $boot_data = wp_json_encode(self::getAdminBootData(), JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT);
50 if ($boot_data !== false) {
51 wp_add_inline_script('rabbitloader-index', 'window.rabbitloader_local_vars = ' . $boot_data . ';', 'before');
52 }
53 }
54 });
55
56 $purge_func = function () {
57 RL21UtilWP::verifyAjaxNonce();
58 $response = [
59 'result' => false,
60 'lpc' => 0 //local purge count
61 ];
62 if (!empty($_POST['post_id'])) {
63 $post_id = RabbitLoader_21_Util_Core::get_param('post_id', true);
64 RL21UtilWP::onPostChange($post_id);
65 $response['result'] = true;
66 } else {
67 RL21UtilWP::onPostChange(RL21UtilWP::POST_ID_ALL);
68 $response['result'] = true;
69 }
70 RL21UtilWP::execute_purge($response['lpc']);
71 delete_transient('rabbitloader_trans_overview_data');
72 RabbitLoader_21_Core::sendJsonResponse($response);
73 };
74
75 add_action('wp_ajax_rabbitloader_ajax_purge', $purge_func);
76 add_action('wp_ajax_nopriv_rabbitloader_ajax_purge', $purge_func);
77
78 $mode_change_func = function () {
79 RL21UtilWP::verifyAjaxNonce();
80
81 if (!RabbitLoader_21_Admin::canAccessModeAjax()) {
82 wp_send_json_error(null, 403);
83 return;
84 }
85
86 $private_mode = !empty($_POST['private_mode']);
87 RabbitLoader_21_Admin::updateModeValue($private_mode);
88 $response = [
89 'result' => true,
90 'private_mode' => $private_mode,
91 'me_mode' => $private_mode,
92 'mode' => $private_mode ? 'me' : 'everyone'
93 ];
94
95 RabbitLoader_21_Core::sendJsonResponse($response);
96 };
97
98 add_action('wp_ajax_rabbitloader_mode_change', $mode_change_func);
99 add_action('wp_ajax_nopriv_rabbitloader_mode_change', $mode_change_func);
100
101 $mode_get_func = function () {
102 if (empty($_POST['rl_nonce'])) {
103 $request_nonce = RabbitLoader_21_Util_Core::get_param('rl_nonce', true);
104 if (!empty($request_nonce)) {
105 $_POST['rl_nonce'] = $request_nonce;
106 }
107 }
108
109 if (empty($_POST['token'])) {
110 $request_token = RabbitLoader_21_Util_Core::get_param('token', true);
111 if (!empty($request_token)) {
112 $_POST['token'] = $request_token;
113 }
114 }
115
116 RL21UtilWP::verifyAjaxNonce();
117
118 if (!RabbitLoader_21_Admin::canAccessModeAjax()) {
119 wp_send_json_error(null, 403);
120 return;
121 }
122
123 RabbitLoader_21_Core::getWpUserOption($user_options);
124 $private_mode = !empty($user_options['private_mode_val']);
125 $response = [
126 'result' => true,
127 'private_mode' => $private_mode,
128 'me_mode' => $private_mode,
129 'mode' => $private_mode ? 'me' : 'everyone'
130 ];
131
132 RabbitLoader_21_Core::sendJsonResponse($response);
133 };
134
135 add_action('wp_ajax_rabbitloader_mode_get', $mode_get_func);
136 add_action('wp_ajax_nopriv_rabbitloader_mode_get', $mode_get_func);
137 add_action('wp_ajax_rabbitloader_get_exclusions', function () {
138 RL21UtilWP::verifyAjaxNonce();
139
140 if (!current_user_can('manage_options')) {
141 wp_send_json_error(null, 403);
142 return;
143 }
144
145 $response = self::getExcludePatternsState();
146 RabbitLoader_21_Core::sendJsonResponse($response);
147 });
148 add_action('wp_ajax_rabbitloader_add_exclusion', function () {
149 $json_body = json_decode(file_get_contents('php://input'), true);
150 if (is_array($json_body)) {
151 foreach ($json_body as $key => $value) {
152 $_POST[$key] = $value;
153 }
154 }
155
156 RL21UtilWP::verifyAjaxNonce();
157
158 if (!current_user_can('manage_options')) {
159 wp_send_json_error(null, 403);
160 return;
161 }
162
163 $incoming_patterns = [];
164 $single_pattern = '';
165
166 if (isset($_POST['pattern'])) {
167 $single_pattern = wp_unslash($_POST['pattern']);
168 } elseif (isset($_GET['pattern'])) {
169 $single_pattern = wp_unslash($_GET['pattern']);
170 }
171
172 if (!empty($single_pattern)) {
173 $incoming_patterns[] = $single_pattern;
174 }
175
176 if (!empty($_POST['patterns']) && is_array($_POST['patterns'])) {
177 foreach ($_POST['patterns'] as $pattern) {
178 $incoming_patterns[] = $pattern;
179 }
180 }
181
182 $normalized_patterns = self::normalizeExcludePatterns($incoming_patterns);
183 if (empty($normalized_patterns)) {
184 wp_send_json_error(['message' => 'At least one valid exclusion pattern is required.'], 400);
185 return;
186 }
187
188 $response = self::addExcludePatterns($normalized_patterns);
189 RabbitLoader_21_Core::sendJsonResponse($response);
190 });
191 add_action('wp_ajax_rabbitloader_remove_exclusion', function () {
192 $json_body = json_decode(file_get_contents('php://input'), true);
193 if (is_array($json_body)) {
194 foreach ($json_body as $key => $value) {
195 $_POST[$key] = $value;
196 }
197 }
198
199 RL21UtilWP::verifyAjaxNonce();
200
201 if (!current_user_can('manage_options')) {
202 wp_send_json_error(null, 403);
203 return;
204 }
205
206 $incoming_patterns = [];
207 $single_pattern = '';
208
209 if (isset($_POST['pattern'])) {
210 $single_pattern = wp_unslash($_POST['pattern']);
211 } elseif (isset($_GET['pattern'])) {
212 $single_pattern = wp_unslash($_GET['pattern']);
213 }
214
215 if (!empty($single_pattern)) {
216 $incoming_patterns[] = $single_pattern;
217 }
218
219 if (!empty($_POST['patterns']) && is_array($_POST['patterns'])) {
220 foreach ($_POST['patterns'] as $pattern) {
221 $incoming_patterns[] = $pattern;
222 }
223 }
224
225 $normalized_patterns = self::normalizeExcludePatterns($incoming_patterns);
226 if (empty($normalized_patterns)) {
227 wp_send_json_error(['message' => 'At least one valid exclusion pattern is required.'], 400);
228 return;
229 }
230
231 $response = self::removeExcludePatterns($normalized_patterns);
232 RabbitLoader_21_Core::sendJsonResponse($response);
233 });
234 add_action('wp_ajax_rabbitloader_conflicts', function () {
235 if (!current_user_can('manage_options')) {
236 wp_send_json_error(null, 403);
237 return;
238 }
239
240 $context = sanitize_text_field(RabbitLoader_21_Util_Core::get_param('context'));
241 $isActivationFlow = strcmp($context, 'connected') !== 0;
242 $response = RabbitLoader_21_Conflicts::getConflictState($isActivationFlow);
243 RabbitLoader_21_Core::sendJsonResponse($response);
244 });
245 add_action('wp_ajax_rabbitloader_admin_boot', function () {
246 if (!current_user_can('manage_options')) {
247 wp_send_json_error(null, 403);
248 return;
249 }
250
251 RabbitLoader_21_Core::sendJsonResponse(self::getAdminBootData());
252 });
253 add_action('wp_ajax_rabbitloader_connect_proof', function () {
254 RL21UtilWP::verifyAjaxNonce();
255
256 if (!current_user_can('manage_options')) {
257 wp_send_json_error(null, 403);
258 return;
259 }
260
261 $challenge_id = isset($_POST['challenge_id']) ? sanitize_text_field(wp_unslash($_POST['challenge_id'])) : '';
262 $challenge_nonce = isset($_POST['challenge_nonce']) ? sanitize_text_field(wp_unslash($_POST['challenge_nonce'])) : '';
263 $site_url = isset($_POST['site_url']) ? esc_url_raw(wp_unslash($_POST['site_url'])) : '';
264
265 if (empty($challenge_id) || empty($challenge_nonce) || empty($site_url)) {
266 wp_send_json_error(['message' => 'Missing reconnect challenge data.'], 400);
267 return;
268 }
269
270 $site_host = self::normalizeConnectProofHost($site_url);
271 $home_host = self::normalizeConnectProofHost(home_url());
272 if (empty($site_host) || empty($home_host) || $site_host !== $home_host) {
273 wp_send_json_error(['message' => 'Reconnect challenge site mismatch.'], 400);
274 return;
275 }
276
277 $response = wp_remote_post(RabbitLoader_21_Core::getRLDomainV2() . 'domain/v2/reconnect-challenge/redeem', [
278 'timeout' => 15,
279 'headers' => [
280 'Content-Type' => 'application/json',
281 'Accept' => 'application/json',
282 ],
283 'body' => wp_json_encode([
284 'challenge_id' => $challenge_id,
285 'challenge_nonce' => $challenge_nonce,
286 'site_url' => home_url(),
287 'plugin_version' => RABBITLOADER_PLUG_VERSION,
288 'wp_admin_url' => admin_url('admin.php?page=rabbitloader'),
289 ]),
290 ]);
291
292 if (is_wp_error($response)) {
293 wp_send_json_error(['message' => 'Could not redeem reconnect challenge.'], 502);
294 return;
295 }
296
297 $status = (int) wp_remote_retrieve_response_code($response);
298 $body = json_decode(wp_remote_retrieve_body($response), true);
299 if ($status !== 200 || empty($body['result']) || empty($body['redeem_token'])) {
300 wp_send_json_error(['message' => 'Reconnect challenge was not accepted.'], $status >= 400 ? $status : 400);
301 return;
302 }
303
304 $result = [
305 'result' => true,
306 'challenge_id' => $challenge_id,
307 'redeem_token' => sanitize_text_field($body['redeem_token']),
308 'expires_at' => isset($body['expires_at']) ? intval($body['expires_at']) : 0,
309 ];
310 RabbitLoader_21_Core::sendJsonResponse($result);
311 });
312 add_action('wp_ajax_rabbitloader_save_keys', function () {
313 $json_body = json_decode(file_get_contents('php://input'), true);
314 if (is_array($json_body)) {
315 foreach ($json_body as $key => $value) {
316 $_POST[$key] = $value;
317 }
318 }
319
320 RL21UtilWP::verifyAjaxNonce();
321
322 if (!current_user_can('manage_options')) {
323 wp_send_json_error(null, 403);
324 return;
325 }
326
327 $conflict_state = RabbitLoader_21_Conflicts::getConflictState(true);
328 if (!empty($conflict_state['activation_blocked'])) {
329 $conflict_state['result'] = false;
330 if (empty($conflict_state['message'])) {
331 $conflict_state['message'] = RL21UtilWP::__('Resolve the blocking RabbitLoader conflict(s) before connecting this site.');
332 }
333
334 status_header(409);
335 RabbitLoader_21_Core::sendJsonResponse($conflict_state);
336 }
337
338 $encoded_token = RabbitLoader_21_Util_Core::get_param('rl-token', true);
339 $decoded_token = !empty($encoded_token) ? base64_decode($encoded_token, true) : false;
340 if (empty($decoded_token)) {
341 wp_send_json_error(['message' => 'Invalid token'], 400);
342 return;
343 }
344
345 $tokens = json_decode($decoded_token, true);
346 if (empty($tokens['api_token'])) {
347 wp_send_json_error(['message' => 'Invalid token'], 400);
348 return;
349 }
350
351 $urlparts = parse_url(home_url());
352 RabbitLoader_21_Core::update_api_tokens($tokens['api_token'], $urlparts['host'], $tokens['did'], '');
353 if (isset($tokens['cdn_prefix'])) {
354 update_option('rabbitloader_cdn_prefix', $tokens['cdn_prefix'], true);
355 }
356
357 do_action('rl_site_connected');
358
359 $response = self::getAdminBootData();
360 $response['result'] = true;
361 RabbitLoader_21_Core::sendJsonResponse($response);
362 });
363 add_action('wp_ajax_rabbitloader_disconnect', function () {
364 RL21UtilWP::verifyAjaxNonce();
365
366 if (!current_user_can('manage_options')) {
367 wp_send_json_error(null, 403);
368 return;
369 }
370
371 self::disconnectPlugin();
372
373 $response = [
374 'result' => true,
375 'admin_ajax' => admin_url('admin-ajax.php'),
376 'rl_nonce' => wp_create_nonce('rl-ajax-nonce'),
377 'rl_acct' => false,
378 'is_connected' => false,
379 'api_token' => '',
380 'did' => '',
381 'domain' => '',
382 'plan_title' => '',
383 'home_page_url_id' => ''
384 ];
385 RabbitLoader_21_Core::sendJsonResponse($response);
386 });
387 add_action('wp_ajax_rabbitloader_ajax_cron', function () {
388 //self::deferred_exe();
389 RL21UtilWP::verifyAjaxNonce();
390 });
391 add_action('wp_ajax_rabbitloader_warmup_urls', function () {
392 RL21UtilWP::verifyAjaxNonce();
393 $response = [
394 'poffset' => intval($_POST['poffset']),
395 'posts_per_page' => intval($_POST['posts_per_page']),
396 ];
397 RabbitLoader_21_Core::get_recent_posts($response['poffset'], $response['posts_per_page'], $response['published_count'], $response['permalinks']);
398 RabbitLoader_21_Core::sendJsonResponse($response);
399 });
400 // add_action('wp_ajax_rabbitloader_ajax_survey_dismissed', function () {
401 // RL21UtilWP::verifyAjaxNonce();
402 // self::survey_dismissed(self::SURVEY_DIS_PERMA);
403 // });
404 add_action('rl_site_connected', function () {
405 self::rl_site_connected();
406 });
407 add_action('plugins_loaded', 'RabbitLoader_AD_AD::on_plugins_loaded');
408 //listeners for taxonomy changes
409
410 add_action('wp_print_scripts', function () {
411 if (RabbitLoader_21_Util_Core::isRLPage()) {
412 wp_dequeue_script('monsterinsights-vue-frontend'); //vue JS conflict with apexcharts window.SVG().addTo fails
413 }
414 }, 100);
415 }
416
417 public static function init() {}
418
419 private static function canAccessModeAjax()
420 {
421 if (!empty($_POST['token'])) {
422 return true;
423 }
424
425 return current_user_can('manage_options');
426 }
427
428 private static function normalizeExcludePatterns($patterns)
429 {
430 if (!is_array($patterns)) {
431 $patterns = [$patterns];
432 }
433
434 $normalized = [];
435 foreach ($patterns as $pattern) {
436 if (!is_scalar($pattern)) {
437 continue;
438 }
439
440 $pattern = sanitize_text_field((string) $pattern);
441 $pattern = preg_replace('/[\r\n\t]+/', '', $pattern);
442 $pattern = trim($pattern);
443
444 if ($pattern === '' || strpos($pattern, "\0") !== false) {
445 continue;
446 }
447
448 $normalized[$pattern] = true;
449 }
450
451 return array_keys($normalized);
452 }
453
454 private static function getExcludePatternsState()
455 {
456 RabbitLoader_21_Core::getWpUserOption($user_options);
457 $patterns = self::normalizeExcludePatterns(explode("\n", $user_options['exclude_patterns']));
458
459 return [
460 'result' => true,
461 'patterns' => $patterns,
462 'exclude_patterns' => implode("\n", $patterns),
463 'count' => count($patterns),
464 ];
465 }
466
467 private static function addExcludePatterns($patterns)
468 {
469 RabbitLoader_21_Core::getWpUserOption($user_options);
470 $existing_patterns = self::normalizeExcludePatterns(explode("\n", $user_options['exclude_patterns']));
471 $existing_map = [];
472 foreach ($existing_patterns as $pattern) {
473 $existing_map[$pattern] = true;
474 }
475
476 $added_patterns = [];
477 foreach ($patterns as $pattern) {
478 if (isset($existing_map[$pattern])) {
479 continue;
480 }
481
482 $existing_map[$pattern] = true;
483 $existing_patterns[] = $pattern;
484 $added_patterns[] = $pattern;
485 }
486
487 $user_options['exclude_patterns'] = implode("\n", $existing_patterns);
488 RabbitLoader_21_Core::updateUserOption($user_options);
489
490 if (!empty($added_patterns)) {
491 RL21UtilWP::onPostChange(RL21UtilWP::POST_ID_ALL);
492 }
493
494 return [
495 'result' => true,
496 'message' => empty($added_patterns) ? 'Exclusion pattern already exists.' : 'Exclusion pattern(s) added successfully.',
497 'added_patterns' => $added_patterns,
498 'patterns' => $existing_patterns,
499 'exclude_patterns' => $user_options['exclude_patterns'],
500 'count' => count($existing_patterns),
501 ];
502 }
503
504 private static function removeExcludePatterns($patterns)
505 {
506 RabbitLoader_21_Core::getWpUserOption($user_options);
507 $existing_patterns = self::normalizeExcludePatterns(explode("\n", $user_options['exclude_patterns']));
508 $remove_map = [];
509 foreach ($patterns as $pattern) {
510 $remove_map[$pattern] = true;
511 }
512
513 $remaining_patterns = [];
514 $removed_patterns = [];
515 foreach ($existing_patterns as $pattern) {
516 if (isset($remove_map[$pattern])) {
517 $removed_patterns[] = $pattern;
518 continue;
519 }
520
521 $remaining_patterns[] = $pattern;
522 }
523
524 $user_options['exclude_patterns'] = implode("\n", $remaining_patterns);
525 RabbitLoader_21_Core::updateUserOption($user_options);
526
527 if (!empty($removed_patterns)) {
528 RL21UtilWP::onPostChange(RL21UtilWP::POST_ID_ALL);
529 }
530
531 return [
532 'result' => true,
533 'message' => empty($removed_patterns) ? 'Exclusion pattern not found.' : 'Exclusion pattern(s) removed successfully.',
534 'removed_patterns' => $removed_patterns,
535 'patterns' => $remaining_patterns,
536 'exclude_patterns' => $user_options['exclude_patterns'],
537 'count' => count($remaining_patterns),
538 ];
539 }
540
541 private static function updateModeValue($private_mode)
542 {
543 RabbitLoader_21_Core::getWpUserOption($user_options);
544 $user_options['private_mode_val'] = !empty($private_mode);
545 $user_options['private_mode_ts'] = date('c');
546 RabbitLoader_21_Core::updateUserOption($user_options);
547
548 try {
549 // remove public pages cache, main purpose is to purge TPV
550 RabbitLoader_21_TP::purge_all($tp_purge_count);
551 } catch (\Throwable $e) {
552 RabbitLoader_21_Core::on_exception($e);
553 }
554 }
555
556 protected static function disconnectPlugin()
557 {
558 $disconnect_context = [
559 'did' => RabbitLoader_21_Core::getWpOptVal('did'),
560 'domain' => RabbitLoader_21_Core::getWpOptVal('domain'),
561 'token_present' => !empty(RabbitLoader_21_Core::getWpOptVal('api_token')),
562 ];
563 error_log('RabbitLoader manual disconnect test: sending heartbeat ' . wp_json_encode($disconnect_context));
564
565 $post_data = ['disconnect' => 1];
566 $http = RabbitLoader_21_Core::callPOSTAPI('domain/heartbeat', $post_data, $apiError, $apiMessage);
567 $response_body = !empty($http['body']) && is_array($http['body']) ? $http['body'] : [];
568 $response_context = [
569 'status' => empty($http['response']['code']) ? 0 : (int) $http['response']['code'],
570 'result' => array_key_exists('result', $response_body) ? (bool) $response_body['result'] : null,
571 'message' => empty($response_body['message']) ? (isset($apiMessage) ? (string) $apiMessage : '') : (string) $response_body['message'],
572 'api_error' => (bool) $apiError,
573 'token_present_after_call' => !empty(RabbitLoader_21_Core::getWpOptVal('api_token')),
574 ];
575 error_log('RabbitLoader manual disconnect test: heartbeat response ' . wp_json_encode($response_context));
576
577 if ($apiError) {
578 error_log('RabbitLoader: backend manual disconnect notification failed.');
579 }
580 error_log('RabbitLoader manual disconnect test: continuing to clear local credentials');
581 RabbitLoader_21_Core::update_api_tokens('', '', '', 'user action disconnect');
582 delete_transient('rabbitloader_trans_overview_data');
583 }
584
585 public static function leftMenuOption()
586 {
587 self::get_warnings($notification_count, false);
588 add_menu_page(
589 'RabbitLoader',
590 $notification_count ? sprintf('RabbitLoader <span class="awaiting-mod">%d</span>', $notification_count) : 'RabbitLoader',
591 'manage_options',
592 'rabbitloader',
593 'RabbitLoader_21_Tab_Init::echoPluginPage',
594 dirname(plugin_dir_url(__FILE__)) . '/images/icon_16.png',
595 //'',
596 20
597 );
598
599 if (RabbitLoader_21_Util_Core::isRLPage()) {
600 add_action('admin_head', 'admin_styles', 10, 1);
601 function admin_styles($a)
602 {
603 echo '<link rel="stylesheet" href="' . RABBITLOADER_PLUG_URL . 'admin/css/bootstrap.v5.1.3.min.css' . '" type="text/css" media="all" />
604 <link rel="stylesheet" href="' . RABBITLOADER_PLUG_URL . 'admin/css/style.css?v=' . RABBITLOADER_PLUG_VERSION . '" type="text/css" media="all" />
605 <link rel="stylesheet" href="' . RABBITLOADER_PLUG_URL . 'admin/css/mfe-patches.css?v=' . RABBITLOADER_PLUG_VERSION . '" type="text/css" media="all" />';
606 }
607 }
608 add_action('admin_head', function () {
609 echo '<link rel="stylesheet" href="' . RABBITLOADER_PLUG_URL . 'admin/css/style-common.css?v=' . RABBITLOADER_PLUG_VERSION . '" type="text/css" media="all" />';
610 }, 10, 1);
611 }
612
613
614 protected static function isPluginActivated()
615 {
616 $api_token = RabbitLoader_21_Core::getWpOptVal('api_token');
617 $did = RabbitLoader_21_Core::getWpOptVal('did');
618
619 return !empty($api_token) && preg_match('/^[a-f0-9]{24}$/i', $did) === 1;
620 }
621
622 protected static function getAdminBootData()
623 {
624 $overview = RabbitLoader_21_Tab_Init::getOverviewData();
625 $is_connected = self::isPluginActivated();
626
627 return [
628 'admin_ajax' => admin_url('admin-ajax.php'),
629 'rl_nonce' => wp_create_nonce('rl-ajax-nonce'),
630 'rl_acct' => $is_connected,
631 'is_connected' => $is_connected,
632 'api_token' => RabbitLoader_21_Core::getWpOptVal('api_token'),
633 'did' => RabbitLoader_21_Core::getWpOptVal('did'),
634 'domain' => RabbitLoader_21_Core::getWpOptVal('domain'),
635 'plan_title' => isset($overview['plan_title']) ? $overview['plan_title'] : '',
636 'home_page_url_id' => isset($overview['home_page_url_id']) ? $overview['home_page_url_id'] : ''
637 ];
638 }
639
640 protected static function getConnectProofConfig()
641 {
642 $allowed_origins = RabbitLoader_21_Util_Core::isDev()
643 ? ['https://dash.rabbitloader.local', 'http://localhost:3000', 'https://dash.rabbitloader.com']
644 : ['https://dash.rabbitloader.com'];
645
646 return [
647 'admin_ajax' => admin_url('admin-ajax.php'),
648 'rl_nonce' => wp_create_nonce('rl-ajax-nonce'),
649 'home_url' => home_url(),
650 'wp_admin_url' => admin_url('admin.php?page=rabbitloader'),
651 'plugin_version' => RABBITLOADER_PLUG_VERSION,
652 'can_manage_options' => current_user_can('manage_options'),
653 'allowed_dashboard_origins' => $allowed_origins,
654 ];
655 }
656
657 protected static function normalizeConnectProofHost($url)
658 {
659 $host = wp_parse_url($url, PHP_URL_HOST);
660 if (empty($host)) {
661 return '';
662 }
663
664 $host = strtolower($host);
665 return preg_replace('/^www\./', '', $host);
666 }
667
668 public static function admin_notices()
669 {
670
671 //self::survey();
672
673 try {
674 $page = RabbitLoader_21_Util_Core::get_param('page');
675
676 if (self::$admin_notice_shown || ($page == 'rabbitloader')) {
677 return;
678 }
679
680 self::$admin_notice_shown = true;
681
682 $plug_url = admin_url("admin.php?page=rabbitloader");
683
684
685 if (!self::isPluginActivated()) {
686 echo '
687 <div class="notice notice-error is-dismissible"><p>';
688 printf(RL21UtilWP::__('RabbitLoader is disconnected. Your pages are not optimized. <a href="%s">Click here to connect</a>'), $plug_url);
689 echo '</p></div>';
690 } else {
691 self::get_warnings($notification_count, false);
692
693 if ($notification_count > 0) {
694 echo '<div class="notice notice-error is-dismissible"><p>';
695 printf(RL21UtilWP::_n('RabbitLoader has %d warning which is affecting your website\'s optimizations. <a href="%s">check details</a>', 'RabbitLoader has %d warnings which may affect your website\'s optimizations. <a href="%s">check details</a>', $notification_count), $notification_count, $plug_url);
696 echo '</p></div>';
697 }
698 }
699 } catch (Throwable $e) {
700 RabbitLoader_21_Core::on_exception($e);
701 } catch (Exception $e) {
702 RabbitLoader_21_Core::on_exception($e);
703 }
704 }
705
706 private static function survey()
707 {
708 $showSurvey = false;
709 $integration_start_time = RabbitLoader_21_Core::getWpOptVal('token_update_ts');
710 if (!empty($integration_start_time)) {
711 $maxTimeSec = 1 * 3600; //after 1hour
712 $elapsedTimeSec = time() - $integration_start_time;
713 $showSurvey = $elapsedTimeSec > $maxTimeSec;
714 }
715
716 if (!$showSurvey) {
717 return;
718 }
719
720 $user_id = get_current_user_id();
721 if (empty($user_id)) {
722 return;
723 }
724 //delete_user_meta($user_id, 'rl_survey_dismissed');
725 $dismiss_time = intval(get_user_meta($user_id, 'rl_survey_dismissed', true));
726 if (self::SURVEY_DIS_PERMA === $dismiss_time) {
727 //permanently dismissed
728 return;
729 } else if (!empty($dismiss_time) && ($dismiss_time > strtotime('-7 days'))) {
730 //dismiss within last 7 days
731 return;
732 }
733
734 $remindLaterURL = esc_url(add_query_arg('rl_survey_dismissed', time()));
735 $remindNeverURL = esc_url(add_query_arg('rl_survey_dismissed', self::SURVEY_DIS_PERMA));
736 echo '<div class="notice notice-info is-dismissible rl_survey_notice" style="background: #f4f4f4; color: #1d2327; border-width: 1px; border-style: solid; border-color: #1d2327; padding: 1rem 1rem; border-radius: 5px;"><div style="float:left; padding-right:1rem;"><img src="' . RABBITLOADER_PLUG_URL . '/assets/icon-dark.svg" width="100" /></div>';
737 echo '<p class="p1">';
738 RL21UtilWP::_e("Enjoying RabbitLoader? 🚀");
739 echo '</p>';
740 echo '<p>';
741 RL21UtilWP::_e('Share your thoughts in a quick 10-second anonymous survey. Your feedback helps us hop forward! 🐇');
742 echo '</p>';
743 echo '<p class="p" style="margin-top: 1.5rem;"><button id="rl_show_survey" class="rl-btn rl-btn-primary mt-2 mb-sm-0">';
744 RL21UtilWP::_e('Yes, Continue');
745 echo '</button> <a href="' . $remindLaterURL . '" class="rl-btn" style="color:#0076CE;">';
746 RL21UtilWP::_e('Ask me later');
747 echo '</a><a href="' . $remindNeverURL . '" class="rl-btn" style="color:#0076CE;">';
748 RL21UtilWP::_e('I already did');
749 echo '</a></p>';
750 echo '</div>';
751 }
752
753 // private static function survey_dismissed($forceTime)
754 // {
755 // $user_id = get_current_user_id();
756 // if (empty($user_id)) {
757 // wp_send_json_error(null, 403);
758 // }
759 // if (isset($_GET['rl_survey_dismissed'])) {
760 // delete_user_meta($user_id, 'rl_survey_dismissed');
761 // update_user_meta($user_id, 'rl_survey_dismissed', intval($_GET['rl_survey_dismissed']), false);
762 // }
763 // if ($forceTime) {
764 // delete_user_meta($user_id, 'rl_survey_dismissed');
765 // update_user_meta($user_id, 'rl_survey_dismissed', $forceTime, false);
766 // }
767 // }
768 public static function admin_init()
769 {
770 // if (isset($_GET['rl_survey_dismissed'])) {
771 // self::survey_dismissed(0);
772 // }
773 }
774
775 protected static function get_warnings(&$count, $print)
776 {
777
778 if (self::$rabbitloader_cache_warnings === false) {
779
780 self::$rabbitloader_cache_warnings = [];
781
782 $otherConflictPluginMessages = RabbitLoader_21_Conflicts::getMessages(false);
783 foreach ($otherConflictPluginMessages as $plugMessage) {
784 self::$rabbitloader_cache_warnings[] = $plugMessage;
785 }
786
787 $adv_cache_msg = RL21UtilWP::__("The file /wp-content/advanced-cache.php is not writable. Please make sure that the PHP script has write access to the /wp-content/ directory and refresh this page to make RabbitLoader work efficiently.");
788
789 if (!defined('RABBITLOADER_AC_ACTIVE') || (RABBITLOADER_PLUG_VERSION != RABBITLOADER_AC_PLUG_VERSION) || (LOGGED_IN_COOKIE != RABBITLOADER_AC_LOGGED_IN_COOKIE)) {
790 $aac_code = self::activate_advanced_cache();
791 if ($aac_code === 4) {
792 self::$rabbitloader_cache_warnings[] = $adv_cache_msg;
793 }
794 }
795
796 if ((!defined("WP_CACHE") || !WP_CACHE)) {
797 if (RL21UtilWP::is_flywheel()) {
798 self::$rabbitloader_cache_warnings[] = sprintf(RL21UtilWP::__('Please enable WP_CACHE from the Flywheel settings <a href="%s">check details</a>'), "https://rabbitloader.com/kb/settings-for-flywheel/");
799 } else if (!self::update_wp_config_const('WP_CACHE', 'true')) {
800 self::$rabbitloader_cache_warnings[] = RL21UtilWP::__("The file /wp-config.php is not writable. Please make sure the file is writable or set WP_CACHE value to true to make RabbitLoader work efficiently.");
801 }
802 }
803
804 RabbitLoader_21_Core::getWpOption($rl_wp_options);
805 if (!empty($rl_wp_options['rl_hb_messages'])) {
806 foreach ($rl_wp_options['rl_hb_messages'] as $message) {
807 if (!empty($message['fd'])) {
808 self::$rabbitloader_cache_warnings[] = RL21UtilWP::__($message['fd']);
809 }
810 }
811 }
812 if (!empty($rl_wp_options['rl_latest_plugin_v'])) {
813 if (version_compare(RABBITLOADER_PLUG_VERSION, $rl_wp_options['rl_latest_plugin_v']) == -1) {
814 self::$rabbitloader_cache_warnings[] = RL21UtilWP::__("You are using an outdated version of RabbitLoader plugin. Please update it for a better experience.");
815 }
816 }
817 }
818
819 $count = count(self::$rabbitloader_cache_warnings);
820
821 if ($print) {
822 foreach (self::$rabbitloader_cache_warnings as $message) {
823 // echo '<div class="alert alert-danger" role="alert">';
824 // _e($message, RABBITLOADER_TEXT_DOMAIN);
825 // echo '</div>';
826 echo '<div class="notice notice-error"><p><b>';
827 RL21UtilWP::_e('Warning');
828 echo ': </b>';
829 _e($message, RABBITLOADER_TEXT_DOMAIN);
830 echo '</div>';
831 }
832 }
833 }
834
835 public static function activate_advanced_cache()
836 {
837
838 try {
839 if (!empty(RabbitLoader_21_Conflicts::getMessages(false))) {
840 return 1;
841 }
842
843 // if (!RabbitLoader_21_Core::htaccessExists()) {
844 // return 2;
845 // }
846
847 $adv_cache_sample = RABBITLOADER_PLUG_DIR . "advanced-cache.php";
848 $file_updated = false;
849 if (file_exists($adv_cache_sample)) {
850 $adv_cache_contents = file_get_contents($adv_cache_sample);
851 $adv_cache_contents = str_replace("%%RABBITLOADER_AC_ABSPATH%%", ABSPATH, $adv_cache_contents);
852 $adv_cache_contents = str_replace("%%RABBITLOADER_AC_PLUG_DIR%%", RABBITLOADER_PLUG_DIR, $adv_cache_contents);
853 $adv_cache_contents = str_replace("%%RABBITLOADER_AC_LOGGED_IN_COOKIE%%", LOGGED_IN_COOKIE, $adv_cache_contents);
854 $adv_cache_contents = str_replace("%%RABBITLOADER_AC_CACHE_DIR%%", RABBITLOADER_CACHE_DIR, $adv_cache_contents);
855 $adv_cache_contents = str_replace("%%RABBITLOADER_AC_PLUG_VERSION%%", RABBITLOADER_PLUG_VERSION, $adv_cache_contents);
856 $adv_cache_contents = str_replace("%%RABBITLOADER_AC_PLUG_ENV%%", RABBITLOADER_PLUG_ENV, $adv_cache_contents);
857
858 $advanced_cache_file = WP_CONTENT_DIR . DIRECTORY_SEPARATOR . 'advanced-cache.php';
859 $file_updated = RabbitLoader_21_Util_Core::fpc($advanced_cache_file, $adv_cache_contents, WP_DEBUG);
860 }
861
862 if ($file_updated) {
863 self::update_wp_config_const('WP_CACHE', 'true');
864 return 3;
865 } else {
866 return 4;
867 }
868 } catch (Throwable $e) {
869 RabbitLoader_21_Core::on_exception($e);
870 }
871 }
872
873 private static function update_wp_config_const($const_name, $const_val)
874 {
875 $wp_config_path = RL21UtilWP::get_wp_config();
876 //check if config file is writable
877 if (empty($wp_config_path) || !is_writable($wp_config_path)) {
878 //echo 'rl_not_writable__';
879 return;
880 }
881
882 $lines = file($wp_config_path);
883 $last_line = count($lines) - 1;
884
885 $new_file = array();
886 $const_added = false;
887 foreach ($lines as $current_line => $line_content) {
888 //check if constant is already defined
889 if (preg_match("/define\(\s*'{$const_name}'/i", $line_content)) {
890 $const_added = true;
891 $new_file[] = "if (!defined('{$const_name}')) { define( '{$const_name}', {$const_val} );}\n\n";
892 continue; //dont't break here, its a complete file rewrite
893 }
894
895 $thatsAllLine = (preg_match("/\/\* That's all, stop editing!.*/i", $line_content)); //constants should be before this line.
896 $isLast = ($thatsAllLine && !defined($const_name)) || ($last_line == $current_line);
897
898 // If we reach the end and no define - add it.
899 if (empty($const_added) && $isLast) {
900 $const_added = true;
901 $new_file[] = "if (!defined('{$const_name}')) { define( '{$const_name}', {$const_val} );}\n\n";
902 }
903
904 $new_file[] = $line_content;
905 }
906 $file_contents = implode("", $new_file);
907 return RabbitLoader_21_Util_Core::fpc($wp_config_path, $file_contents, WP_DEBUG);
908 }
909
910 public static function plugin_deactivate($network_wide = false, $notifyBackend = true)
911 {
912 if ($notifyBackend) {
913 RabbitLoader_21_Core::notify_backend_disconnect('plugin deactivate', $apiError, $apiMessage);
914 }
915
916 try {
917 self::update_wp_config_const('WP_CACHE', 'false');
918 $advanced_cache_file = WP_CONTENT_DIR . DIRECTORY_SEPARATOR . 'advanced-cache.php';
919 if (file_exists($advanced_cache_file)) { //during uninstall RABBITLOADER_AC_ACTIVE will not be there
920 $adv_cache_contents = "";
921 $file_updated = RabbitLoader_21_Util_Core::fpc($advanced_cache_file, $adv_cache_contents, WP_DEBUG);
922 }
923 } catch (Throwable $e) {
924 RabbitLoader_21_Core::on_exception($e);
925 }
926 }
927
928 public static function plugin_uninstall()
929 {
930 self::plugin_deactivate(false, false);
931
932 RabbitLoader_21_Core::notify_backend_disconnect('plugin uninstall', $apiError, $apiMessage);
933
934 try {
935 RabbitLoader_21_Core::cleanAllCachedFiles();
936 RabbitLoader_21_Core::delete_log_file(RabbitLoader_21_Core::LOCAL_CONFIG_FILE);
937 } catch (Exception $e) {
938 }
939 $ourOptions = array('rabbitloader_field_apikey', 'rabbitloader_field_apisecret', 'rabbitloader_field_domain', 'rl_optimizer_engine_version', 'rabbit_loader_wp_options', 'rabbit_loader_user_options', 'rabbitloader_cdn_prefix');
940
941 foreach ($ourOptions as $optionName) {
942 delete_option($optionName);
943 }
944 }
945
946 private static function rl_site_connected()
947 {
948 $api_token = RabbitLoader_21_Core::getWpOptVal('api_token');
949 $domain = RabbitLoader_21_Core::getWpOptVal('domain');
950 $did = RabbitLoader_21_Core::getWpOptVal('did');
951 RabbitLoader_21_Core::callPOSTAPI('domain/heartbeat', [], $apiError, $apiMessage);
952 if ($apiError) {
953 RabbitLoader_21_Core::update_api_tokens($api_token, $domain, $did, 'connected; heartbeat notification failed');
954 error_log('RabbitLoader: backend connection heartbeat failed after saving credentials.');
955 }
956
957 try {
958 RabbitLoader_21_TP::purge_all($tp_purge_count);
959 } catch (\Throwable $e) {
960 RabbitLoader_21_Core::on_exception($e);
961 }
962 }
963
964 public static function settings_link($links)
965 {
966 $url_settings = esc_url(add_query_arg(
967 'page',
968 'rabbitloader',
969 get_admin_url() . 'admin.php'
970 ));
971 $link = "<a href='$url_settings'>" . __('Settings') . '</a>';
972 array_push($links, $link);
973 $url_kb = esc_url('https://rabbitloader.com/kb/');
974 $link = "<a target='_blank' href='$url_kb'>" . __('Knowledge Base') . '</a>';
975 array_push($links, $link);
976 return $links;
977 }
978 }
979