PluginProbe
BerqWP – All-In-One Optimization for Core Web Vitals, Cache, CDN, Images, CSS & JavaScript / 4.1.16
BerqWP – All-In-One Optimization for Core Web Vitals, Cache, CDN, Images, CSS & JavaScript v4.1.16
4.1.16 4.1.15 4.1.14 4.1.13 4.1.12 4.1.11 4.1.10 4.0.30 4.0.29 4.0.28 4.0.27 4.0.26 4.0.24 4.0.25 4.0.23 4.0.22 4.0.21 4.0.19 4.0.18 4.0.17 4.0.16 1.9.3 1.9.4 1.9.5 1.9.6 All 170 releases
searchpro / inc / class-berqwp.php

class-berqwp.php in BerqWP – All-In-One Optimization for Core Web Vitals, Cache, CDN, Images, CSS & JavaScript 4.1.16, at inc/class-berqwp.php

1,862 lines 60.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 if (!defined('ABSPATH'))
3 exit;
4
5 use BerqWP\BerqWP as BerqWPCloud;
6 use BerqWP\RateLimiter;
7
8 if (!class_exists('berqWP')) {
9 class berqWP
10 {
11
12 public $is_key_verified = false;
13 public $key_response = false;
14 public static $instance = null;
15
16 public $conflicting_plugins = [
17 'autoptimize/autoptimize.php', // Autoptimize
18 'wp-super-cache/wp-cache.php', // WP Super Cache
19 'w3-total-cache/w3-total-cache.php', // W3 Total Cache
20 'wp-fastest-cache/wpFastestCache.php', // WP Fastest Cache
21 'litespeed-cache/litespeed-cache.php', // LiteSpeed Cache
22 'cache-enabler/cache-enabler.php', // Cache Enabler
23 'hummingbird-performance/wp-hummingbird.php', // Hummingbird – Speed up, Cache, Optimize Your CSS and JS
24 'sg-cachepress/sg-cachepress.php', // SiteGround Optimizer (for those hosted on SiteGround)
25 'wp-rocket/wp-rocket.php', // WP Rocket
26 'breeze/breeze.php', // Breeze (Cloudways)
27 'comet-cache/comet-cache.php', // Comet Cache
28 'hyper-cache/plugin.php', // Hyper Cache
29 'simple-cache/simple-cache.php', // Simple Cache
30 'wp-optimize/wp-optimize.php', // WP-Optimize
31 'swift-performance-lite/performance.php', // Swift Performance Lite
32 'nitropack/nitropack.php', // NitroPack
33 'nitropack/main.php', // NitroPack
34 'jetpack-boost/jetpack-boost.php', // Jetpack Boost
35 'tenweb-speed-optimizer/tenweb_speed_optimizer.php', // 10Web Booster
36 'speed-booster-pack/speed-booster-pack.php', // Speed booster pack
37 'wp-speed-of-light/wp-speed-of-light.php', // WP speed of light
38 'speedycache/speedycache.php', // Speedy cache
39 'powered-cache/powered-cache.php', // Powered cache
40 'clearfy/clearfy.php', // Clearfy
41 'rabbit-loader/rabbit-loader.php',
42 'psn-pagespeed-ninja/pagespeedninja.php',
43 'jch-optimize/jch-optimize.php',
44 'cache-enabler/cache-enabler.php',
45 'core-web-vitals-pagespeed-booster/core-web-vitals-pagespeed-booster.php',
46 'surge/surge.php',
47 'speedien/speedien.php',
48 'wpspeed/wpspeed.php',
49 'debloat/debloat.php',
50 'perfmatters/perfmatters.php',
51 'phastpress/phastpress.php',
52 'wp-meteor/wp-meteor.php',
53 ];
54
55 function __construct()
56 {
57
58 add_action('init', [$this, 'initialize']);
59
60 // Save settings
61 add_action('admin_init', [$this, 'save_settings']);
62
63 // Sitemap for cache warmup
64 // add_action('wp', 'bwp_get_sitemap');
65 add_action('wp_loaded', 'bwp_get_sitemap', 999);
66
67 // BerqWP display logs
68 add_action('init', 'bwp_display_logs');
69
70 require_once optifer_PATH . '/api/register_apis.php';
71
72 add_action('admin_menu', [$this, 'register_menu']);
73 // add_action('init', [$this, 'berq_post_types'], 20);
74 add_action('berqwp_notices', [$this, 'notices']);
75
76 add_filter('plugin_action_links_searchpro/berqwp.php', [$this, 'plugin_settings_links']);
77
78 // add_action('wp_ajax_berqwp_fetch_remote_html', [$this, 'fetch_remote_html']);
79
80 add_action('wp_ajax_berqwp_refresh_cache_stats', [$this, 'refresh_cache_stats']);
81
82 add_action('wp_ajax_berqwp_get_optimized_pages', [$this, 'berqwp_get_optimized_pages']);
83 add_action('wp_ajax_berqwp_recently_optimized_pages', [$this, 'ajax_recently_optimized_pages']);
84
85 add_action('admin_enqueue_scripts', [$this, 'admin_scripts']);
86
87 // add_filter( 'action_scheduler_queue_runner_concurrent_batches', [$this, 'ashp_increase_concurrent_batches'] );
88
89 // add_filter('action_scheduler_retention_period', function ($period) {
90 // return DAY_IN_SECONDS;
91 // });
92
93 // add_filter('action_scheduler_default_cleaner_statuses', function ($statuses) {
94 // $statuses[] = 'failed';
95 // return $statuses;
96 // });
97
98 // add_filter('action_scheduler_cleanup_batch_size', function ($batch_size) {
99 // return 100;
100 // });
101
102 // Refresh license key
103 add_action('admin_post_bwp_refresh_license', [$this, 'handle_refresh_license_action']);
104
105 add_action('admin_post_switch_optimization_method_local', [$this, 'switch_optimization_method']);
106 add_action('admin_post_switch_optimization_method_cloud', [$this, 'switch_optimization_method']);
107
108 add_action('in_admin_header', [$this, 'remove_admin_notices']);
109
110 // Increase nonce life
111 add_filter('nonce_life', [$this, 'increase_nonce_life']);
112
113 // Page compression test
114 add_action('wp_ajax_berqwp_enable_page_compression', [$this, 'enable_page_compression']);
115 add_action('template_redirect', [$this, 'page_compression_test']);
116
117 // Run daily maintenance tasks
118 // add_action('init', [$this, 'schedule_daily_maintenance']);
119 // add_action('berqwp_daily_maintenance_hook', [$this, 'daily_maintenance']);
120
121 // Revoke License
122 add_action('init', [$this, 'revoke_license']);
123
124 // Create dropin plugin file
125 add_action('init', 'berqwp_setup_dropin');
126
127 // Update settings via API
128 add_action('init', 'bwp_update_configs_webhook');
129
130 // Active-status check from optimization server
131 add_action('init', 'bwp_check_status_webhook');
132
133 // Sync addons from cloud
134 add_action('init', [$this, 'sync_addons']);
135
136 // Debloat: WordPress feature toggles
137 if (get_option('berqwp_disable_emojis')) {
138 add_action('init', [$this, 'disable_emoji']);
139 }
140 add_action('init', [$this, 'apply_heartbeat_settings'], 1);
141 add_action('init', [$this, 'apply_debloat_settings'], 1);
142
143 // Database: manual cleanup AJAX actions
144 add_action('wp_ajax_berqwp_db_run_action', [$this, 'ajax_db_run_action']);
145
146 // Database: scheduled optimization via WP-Cron
147 add_filter('cron_schedules', [$this, 'add_monthly_cron_schedule']);
148 add_action('berqwp_db_scheduled_optimize', [$this, 'run_scheduled_db_optimize']);
149 add_action('berqwp_reschedule_db_cron', [$this, 'reschedule_db_cron']);
150 add_action('berqwp_activate_plugin', [$this, 'reschedule_db_cron']);
151 add_action('berqwp_deactivate_plugin', [$this, 'clear_db_cron']);
152
153 // Multisite support
154 if (function_exists('is_multisite') && is_multisite()) {
155 add_action('network_admin_menu', [$this, 'register_network_menu']);
156 add_action('network_admin_edit_berqwp_network_save', [$this, 'save_network_settings']);
157 add_action('wp_initialize_site', [$this, 'on_new_site_created'], 10, 1);
158 add_action('wp_delete_site', [$this, 'regenerate_blog_map']);
159 add_action('berqwp_activate_plugin', [$this, 'regenerate_blog_map']);
160 }
161 }
162
163 static function getInstance() {
164
165 if (self::$instance === null) {
166 $instance = new berqWP();
167 self::$instance = $instance;
168 return $instance;
169 }
170
171 return self::$instance;
172 }
173
174 function upgrade_notice() {
175
176 if (!berqwp_can_use_cloud()) {
177 return;
178 }
179
180 if (empty($this->key_response->product_ref)) {
181 return;
182 }
183
184 if ($this->key_response->product_ref == 'Free Account' && bwp_cached_pages_count() >= 10) {
185
186 bwp_notice('warning', 'Free Plan Limit Reached', "<p>You've reached the limit of 10 optimized pages for your free BerqWP account. Upgrade now to optimize unlimited pages and get the best performance for your entire site!</p>");
187 }
188 }
189
190 function switch_optimization_method() {
191 // Check if the user has the necessary nonce and the action matches
192 if (isset($_GET['action']) && $_GET['action'] === 'switch_optimization_method_local' && wp_verify_nonce(sanitize_text_field(wp_unslash($_GET['_wpnonce'] ?? '')), 'switch_optimization_method_local_action')) {
193
194
195 $berqconfigs = berqConfigs::getInstance();
196 $berqconfigs->update_configs(['optimization_method' => 'local']);
197
198 // Require flush cache
199 update_option('bwp_require_flush_cache', 1);
200
201 $redirect_url = wp_get_referer();
202
203 // Redirect back to the referring page after clearing the cache
204 wp_safe_redirect($redirect_url);
205 exit;
206 }
207
208 if (isset($_GET['action']) && $_GET['action'] === 'switch_optimization_method_cloud' && wp_verify_nonce(sanitize_text_field(wp_unslash($_GET['_wpnonce'] ?? '')), 'switch_optimization_method_cloud_action')) {
209
210 $berqconfigs = berqConfigs::getInstance();
211 $configs = $berqconfigs->get_configs();
212
213 if (empty($configs['secret'])) {
214 $berqconfigs->update_configs(['optimization_method' => '']);
215 } else {
216 $berqconfigs->update_configs(['optimization_method' => 'cloud']);
217 }
218
219 // Require flush cache
220 update_option('bwp_require_flush_cache', 1);
221
222 $redirect_url = wp_get_referer();
223
224 // Redirect back to the referring page after clearing the cache
225 wp_safe_redirect($redirect_url);
226 exit;
227 }
228 }
229
230 function sync_addons()
231 {
232 $license_key = berqwp_get_license_key();
233 if (get_option('berqwp_sync_addons') && !empty($this->key_response->product_ref) && $this->key_response->product_ref == 'AppSumo Deal' && !empty($license_key)) {
234 berqwp_sync_addons($license_key, home_url());
235 delete_option('berqwp_sync_addons');
236 }
237 }
238
239 function revoke_license()
240 {
241 if (isset($_GET['berqwp_revoke_license']) && !empty($_POST['key_hash'])) {
242 $hash = sanitize_text_field($_POST['key_hash']);
243
244 if ($hash == md5(berqwp_get_license_key())) {
245 berqwp_delete_license_key();
246 echo json_encode(['success' => true]);
247 exit;
248 }
249 }
250 }
251
252 function daily_maintenance()
253 {
254
255 // Perform connection test
256 bwp_check_connection(true);
257
258 $log_file = optifer_cache . 'berqwp.log';
259 if (file_exists($log_file)) {
260 @unlink($log_file);
261 }
262
263 $log_dir = optifer_cache . 'logs/';
264
265 // Check if directory exists
266 if (is_dir($log_dir)) {
267 // Get all files in the directory
268 $files = glob($log_dir . '*');
269
270 // Check if there are more than 10 files
271 if (count($files) > 10) {
272 // Sort files by modified time, newest first
273 usort($files, function ($a, $b) {
274 return filemtime($b) - filemtime($a);
275 });
276
277 // Get files beyond the first 10
278 $files_to_delete = array_slice($files, 10);
279
280 // Delete the extra files
281 foreach ($files_to_delete as $file) {
282 if (is_file($file)) {
283 @unlink($file);
284 }
285 }
286 }
287 }
288 }
289
290 function schedule_daily_maintenance()
291 {
292 if (!as_next_scheduled_action('berqwp_daily_maintenance_hook')) {
293 as_schedule_recurring_action(time(), DAY_IN_SECONDS, 'berqwp_daily_maintenance_hook');
294 }
295 }
296
297 function page_compression_test()
298 {
299 if (isset($_GET['berqwp_compression_test'])) {
300 $test_file_path = optifer_cache . 'gzip-compression-test.gz';
301 $accept_encoding = $_SERVER['HTTP_ACCEPT_ENCODING'] ?? '';
302 $supports_gzip = strpos($accept_encoding, 'gzip') !== false;
303
304 // if ($supports_gzip) {
305 header('Content-Type: text/html; charset=utf-8');
306 // header_remove('Content-Encoding');
307 // header('Content-Encoding: gzip');
308 // readgzfile($test_file_path);
309
310 header('Vary: Accept-Encoding');
311 header('Content-Encoding: gzip', true);
312 header('Content-Length: ' . filesize($test_file_path), true);
313 readfile($test_file_path);
314 // }
315 // header('Content-Type: text/html');
316 // header('Content-Encoding: gzip');
317 // readfile($test_file_path);
318 exit;
319 }
320 }
321
322 function enable_page_compression()
323 {
324
325 check_ajax_referer('wp_rest', 'nonce');
326
327 $url = home_url('/?berqwp_compression_test=' . time());
328 $berqconfigs = berqConfigs::getInstance();
329 $testfile = optifer_cache . 'gzip-compression-test.gz';
330 $html = gzencode('Hello World!');
331 @file_put_contents($testfile, $html);
332
333 sleep(5);
334
335 $response = wp_remote_get($url);
336
337 if (!empty($response) && !is_wp_error($response)) {
338 $html = wp_remote_retrieve_body($response);
339
340 if ($html == 'Hello World!') {
341 $berqconfigs->update_configs(['page_compression' => true]);
342 wp_send_json_success('Compression test passed.');
343 }
344 }
345
346 $berqconfigs->update_configs(['page_compression' => false]);
347 wp_send_json_error('Compression test failed.');
348
349 die(); // Always exit in AJAX functions
350 }
351
352 function increase_nonce_life($default_life)
353 {
354
355 // if (!is_user_logged_in() && bwp_pass_account_requirement()) {
356 if (!is_user_logged_in()) {
357 return 30 * DAY_IN_SECONDS;
358 }
359
360 return $default_life;
361 }
362
363 function remove_admin_notices()
364 {
365
366 if (current_user_can('manage_options')) {
367 $screen = get_current_screen();
368 if ($screen->id === 'toplevel_page_berqwp') {
369 remove_all_actions('user_admin_notices');
370 remove_all_actions('admin_notices');
371 remove_all_actions('all_admin_notices');
372 } else {
373 add_action('admin_notices', function () {
374 do_action('berqwp_notices');
375 }, 10);
376 }
377 }
378 }
379
380 function admin_scripts()
381 {
382
383 // settings page specific assets
384 if ( isset($_GET['page']) && $_GET['page'] == 'berqwp' ) {
385
386 wp_enqueue_style(
387 'bwp-google-fonts',
388 "https://fonts.googleapis.com/css2?family=Lato:wght@400;700&display=swap",
389 [],
390 BERQWP_VERSION
391 );
392
393 wp_enqueue_style(
394 'bwp-jquery-datatable',
395 optifer_URL . '/admin/css/dataTables.dataTables.min.css',
396 [],
397 BERQWP_VERSION
398 );
399
400 wp_enqueue_style(
401 'bwp-settings-style',
402 optifer_URL . '/admin/css/style.css',
403 [],
404 BERQWP_VERSION
405 );
406
407 }
408
409 wp_enqueue_style(
410 'bwp-global-styles', // Handle for the style
411 optifer_URL . 'admin/css/global.css', // URL to the CSS file
412 [], // Dependencies (array of handles)
413 BERQWP_VERSION // Version number
414 );
415 }
416
417 function handle_refresh_license_action()
418 {
419 // Check if the user has the necessary nonce and the action matches
420 if (isset($_GET['action']) && $_GET['action'] === 'bwp_refresh_license' && wp_verify_nonce(sanitize_text_field(wp_unslash($_GET['_wpnonce'] ?? '')), 'bwp_refresh_license_action')) {
421
422 // $transient_key = 'berq_lic_response_cache';
423 // $expire_transient_key = 'berq_lic_cache_expire';
424
425 // berqwp_delete_network_option($transient_key);
426 // berqwp_delete_network_option($expire_transient_key);
427
428 berqwp_delete_network_option('berqwp_license_cache');
429
430 // clear cache from cloud
431 bwp_request_purge_license_key_cache();
432
433 global $berqNotifications;
434 $berqNotifications->success('License key successfully refreshed.');
435
436 $redirect_url = add_query_arg('berq_refresh_license', '', wp_get_referer());
437
438 // Redirect back to the referring page after clearing the cache
439 wp_safe_redirect($redirect_url);
440 exit;
441 }
442 }
443
444 function berqwp_get_optimized_pages()
445 {
446 check_ajax_referer('berqwp_get_optimized_pages_nonce', 'nonce');
447 if (!current_user_can('manage_options')) {
448 wp_send_json_error('Unauthorized', 403);
449 return;
450 }
451
452 if (!isset($_POST['start']) || !isset($_POST['length'])) {
453 wp_send_json_error('Invalid parameters');
454 return;
455 }
456
457 $start = intval($_POST['start']); // Offset for the query
458 $length = intval($_POST['length']); // Number of records to fetch per request
459 $search = isset($_POST['search']) ? sanitize_text_field($_POST['search']) : ''; // Search term if present
460
461 $post_types = get_option('berqwp_optimize_post_types');
462 $optimized_pages = [];
463
464 // Build the query arguments
465 $args = array(
466 'post_type' => $post_types,
467 'posts_per_page' => $length, // Limit the number of posts per request
468 'offset' => $start, // Set the offset for pagination
469 'post_status' => array('publish'), // Only published pages
470 );
471
472 // Add search filtering, if applicable
473 if (!empty($search)) {
474 $args['s'] = $search; // Add the search parameter to the query
475 }
476
477 if ($start === 0 && empty($search)) {
478 $url = bwp_admin_home_url('/');
479
480 if (strpos($url, bwp_admin_home_url()) === false) {
481 $url = str_replace(home_url(), bwp_admin_home_url(), $url);
482 }
483
484 $slug = bwp_url_into_path($url);
485
486 $cache_directory = bwp_get_cache_dir();
487 $cache_key = md5($url);
488 // $cache_key = md5($slug);
489 $cache_file = $cache_directory . $cache_key . '.gz';
490
491 if (is_file($cache_file)) {
492 $status = '<span class="bwp-cache-tag completed">Completed</span>';
493
494 // if (bwp_is_partial_cache($url) === true) {
495 // $status = '<span class="bwp-cache-tag part-completed">Partial cache</span>';
496 // }
497 } else {
498 $status = '<span class="bwp-cache-tag">Pending</span>';
499 }
500
501 $parsed_url = parse_url($url);
502 $decoded_path = isset($parsed_url['path']) ? urldecode($parsed_url['path']) : '';
503 $decoded_query = isset($parsed_url['query']) ? urldecode($parsed_url['query']) : '';
504
505 $decoded_url = $parsed_url['scheme'] . '://' . $parsed_url['host'];
506 if (isset($parsed_url['port'])) {
507 $decoded_url .= ':' . $parsed_url['port'];
508 }
509 $decoded_url .= $decoded_path;
510 if ($decoded_query) {
511 $decoded_url .= '?' . $decoded_query;
512 }
513 if (isset($parsed_url['fragment'])) {
514 $decoded_url .= '#' . $parsed_url['fragment'];
515 }
516
517 $page_arr = [
518 'url' => $decoded_url,
519 'status' => $status,
520 'last_modified' => file_exists($cache_file) ? date('Y-m-d H:i:s', filemtime($cache_file)) : ''
521 ];
522
523 array_push($optimized_pages, $page_arr);
524 }
525
526 $query = new WP_Query($args);
527 $total_posts = $query->found_posts; // Get the total number of records
528
529 $valid_posts_query = new WP_Query([
530 'post_type' => $post_types,
531 'posts_per_page' => -1,
532 'fields' => 'ids',
533 'post_status' => ['publish'],
534 ]);
535 $valid_posts = array_filter($valid_posts_query->posts, function ($post_id) {
536 $url = get_permalink($post_id);
537 return bwp_can_optimize_page_url($url);
538 });
539 $total_posts = count($valid_posts);
540
541 if ($query->have_posts()) {
542 while ($query->have_posts()) {
543 $query->the_post();
544
545 $url = get_permalink();
546
547 if (strpos($url, bwp_admin_home_url()) === false) {
548 $url = str_replace(home_url(), bwp_admin_home_url(), $url);
549 }
550
551 if (bwp_admin_home_url('/') == $url) {
552 continue;
553 }
554
555 $slug = bwp_url_into_path($url);
556
557 if (!bwp_can_optimize_page_url($url)) {
558 continue;
559 }
560
561 $cache_directory = bwp_get_cache_dir();
562 $cache_key = md5($url);
563 // $cache_key = md5($slug);
564 $cache_file = $cache_directory . $cache_key . '.gz';
565
566 if (is_file($cache_file)) {
567 $status = '<span class="bwp-cache-tag completed">Completed</span>';
568
569 // if (bwp_is_partial_cache($url) === true) {
570 // $status = '<span class="bwp-cache-tag part-completed">Partial cache</span>';
571 // }
572 } else {
573 $status = '<span class="bwp-cache-tag">Pending</span>';
574 }
575
576 $parsed_url = parse_url($url);
577 $decoded_path = isset($parsed_url['path']) ? urldecode($parsed_url['path']) : '';
578 $decoded_query = isset($parsed_url['query']) ? urldecode($parsed_url['query']) : '';
579
580 $decoded_url = $parsed_url['scheme'] . '://' . $parsed_url['host'];
581 if (isset($parsed_url['port'])) {
582 $decoded_url .= ':' . $parsed_url['port'];
583 }
584 $decoded_url .= $decoded_path;
585 if ($decoded_query) {
586 $decoded_url .= '?' . $decoded_query;
587 }
588 if (isset($parsed_url['fragment'])) {
589 $decoded_url .= '#' . $parsed_url['fragment'];
590 }
591
592 $page_arr = [
593 'url' => $decoded_url,
594 'status' => $status,
595 'last_modified' => file_exists($cache_file) ? date('Y-m-d H:i:s', filemtime($cache_file)) : ''
596 ];
597
598 array_push($optimized_pages, $page_arr);
599 }
600 }
601
602 wp_reset_postdata();
603
604 // Send the response with the optimized pages and total entries
605 wp_send_json_success([
606 'optimized_pages' => $optimized_pages,
607 'total_entries' => $total_posts, // Total number of posts (unfiltered)
608 'records_filtered' => $total_posts // Adjust if filtered by search
609 ]);
610 }
611
612
613
614 function ajax_recently_optimized_pages()
615 {
616 check_ajax_referer('wp_rest', 'nonce');
617
618 $search = isset($_POST['search']) ? sanitize_text_field($_POST['search']) : '';
619 $start = max(0, intval($_POST['start'] ?? 0));
620 $length = max(1, intval($_POST['length'] ?? 10));
621
622 $log = json_decode(get_option('berqwp_recently_optimized', '[]'), true);
623 if (!is_array($log)) $log = [];
624
625 if (!empty($search)) {
626 $s = strtolower(rtrim($search, '/'));
627 $home = strtolower(rtrim(home_url(), '/'));
628 // Strip the site root from the search term so we compare paths only.
629 // e.g. "http://plugin-berqwp.local/" → "", "/about" → "/about"
630 $s_path = ltrim(str_starts_with($s, $home) ? substr($s, strlen($home)) : $s, '/');
631
632 $log = array_values(array_filter($log, function ($e) use ($s, $home, $s_path) {
633 $url = strtolower(rtrim($e['url'], '/'));
634 $url_path = ltrim(str_starts_with($url, $home) ? substr($url, strlen($home)) : $url, '/');
635 // Exact path match (covers homepage: both empty)
636 if ($url_path === $s_path) return true;
637 // Path starts with the search path (only if search is non-empty to avoid matching everything)
638 if ($s_path !== '' && str_starts_with($url_path, $s_path)) return true;
639 // Full URL substring fallback
640 return $s_path === '' ? $url === $home : stripos($url_path, $s_path) !== false;
641 }));
642
643 // Sort: exact path match first, prefix match second, substring last
644 usort($log, function ($a, $b) use ($home, $s_path) {
645 $pa = ltrim(str_starts_with(strtolower(rtrim($a['url'], '/')), $home)
646 ? substr(strtolower(rtrim($a['url'], '/')), strlen($home)) : strtolower(rtrim($a['url'], '/')), '/');
647 $pb = ltrim(str_starts_with(strtolower(rtrim($b['url'], '/')), $home)
648 ? substr(strtolower(rtrim($b['url'], '/')), strlen($home)) : strtolower(rtrim($b['url'], '/')), '/');
649 $ra = $pa === $s_path ? 0 : (str_starts_with($pa, $s_path) ? 1 : 2);
650 $rb = $pb === $s_path ? 0 : (str_starts_with($pb, $s_path) ? 1 : 2);
651 return $ra <=> $rb;
652 });
653 }
654
655 $total = count($log);
656 $page_log = array_slice($log, $start, $length);
657
658 wp_send_json_success([
659 'data' => $page_log,
660 'total' => $total,
661 'records_filtered' => $total,
662 ]);
663 }
664
665 function ashp_increase_concurrent_batches($concurrent_batches)
666 {
667 return $concurrent_batches * 2;
668 }
669
670 function refresh_cache_stats()
671 {
672
673 check_ajax_referer('wp_rest', 'nonce');
674
675 $post_types = get_option('berqwp_optimize_post_types');
676 $args = array(
677 'post_type' => $post_types,
678 'posts_per_page' => -1,
679 'fields' => 'ids',
680 'post_status' => 'publish'
681 );
682 $query = new WP_Query($args);
683 $pages_to_exclude = get_option('berq_exclude_urls', []);
684 $total_pages = (int) $query->found_posts - count($pages_to_exclude);
685 $optimized_pages = bwp_cached_pages_count();
686
687 if (get_option('show_on_front') !== 'page') {
688 $total_pages++;
689 }
690
691 if (empty($total_pages) || $total_pages <= 0) {
692 $cached_percentage = 0;
693 } else {
694 $cached_percentage = round(($optimized_pages / $total_pages) * 100, 2);
695 }
696 if ($cached_percentage > 100) {
697 $cached_percentage = 100;
698 }
699
700 if ($cached_percentage < 0) {
701 $cached_percentage = 0;
702 }
703
704 $server_queue = get_option('berqwp_server_queue', []);
705
706 wp_send_json_success(['cache_count' => $optimized_pages, 'cache_percentage' => $cached_percentage, 'total' => $total_pages, 'server_queue' => count($server_queue)]);
707
708 die(); // Always exit in AJAX functions
709 }
710
711 function fetch_remote_html()
712 {
713
714 check_ajax_referer('wp_rest', 'nonce');
715
716 $url = get_option('berqwp_enable_sandbox') ? bwp_admin_home_url('/?berqwp') : bwp_admin_home_url('/');
717
718 $response = wp_remote_get($url);
719 // $response = bwp_wp_remote_get($url);
720
721 if (is_array($response) && !is_wp_error($response)) {
722 $html = wp_remote_retrieve_body($response);
723 echo $html;
724 } else {
725 echo 'Error fetching HTML.';
726 }
727
728 die(); // Always exit in AJAX functions
729 }
730
731 function activate_license_from_multi_site()
732 {
733 if (berq_is_localhost()) {
734 return;
735 }
736
737 $berqwp_license_key_from_parent = constant('BERQWP_LICENSE_KEY');
738
739 if (!empty($berqwp_license_key_from_parent) && empty(berqwp_get_license_key())) {
740 $key = sanitize_text_field($berqwp_license_key_from_parent);
741 $key_response = $this->verify_license_key($key, 'slm_activate');
742
743
744 if (!empty($key_response) && $key_response->result == 'success') {
745 berqwp_update_license_key($key);
746
747 if (is_admin()) {
748 ?>
749 <div class="notice notice-success is-dismissible">
750 <?php esc_html_e('The BerqWP license has been activated for your parent multisite.', 'searchpro'); ?>
751 </div>
752 <?php
753 }
754 } elseif ($key_response->result == 'error') {
755 $error = $key_response->message;
756
757 if (is_admin()) {
758 ?>
759 <div class="notice notice-error is-dismissible">
760 <?php echo esc_html($error); ?>
761 </div>
762 <?php
763
764 }
765 }
766 }
767 }
768
769 function handle_upgrade() {
770
771 $berqconfigs = berqConfigs::getInstance();
772 $configs = $berqconfigs->get_configs();
773 $site_id = $configs['site_id'];
774 $license_key = berqwp_get_license_key();
775 global $berq_log;
776
777 if (!empty($configs['optimization_method']) && $configs['optimization_method'] == 'cloud' && empty($license_key)) {
778
779 $berqconfigs->update_configs([
780 'secret' => '',
781 'optimization_method' => 'local'
782 ]);
783 return;
784 }
785
786 $license_check_only = false;
787
788 if ( !empty($configs['secret']) && !empty($configs['optimization_method']) && $configs['optimization_method'] == 'cloud' && !empty($license_key) && empty(berqwp_get_network_option('berqwp_license_cache')) ) {
789 $license_check_only = true;
790 }
791
792 // user first time upgrades to v4
793 $first_upgrade = empty($configs['optimization_method']) && !empty($license_key);
794 $missing_secret = empty($configs['secret']) && !empty($configs['optimization_method']) && $configs['optimization_method'] == 'cloud' && !empty($license_key);
795
796 if ($first_upgrade || $missing_secret || !empty($license_check_only)) {
797
798 // $berq_log->info("Authenticating secret");
799
800 // $lic_response = wp_remote_post(BerqWPCloud::$endpoint."authenticate", [
801 // 'timeout' => 30,
802 // 'body' => $request_body
803 // ]);
804
805 // if (is_wp_error($lic_response)) {
806 // $berq_log->info("Couldn't reach BerqWP server.");
807 // return;
808 // }
809
810 // $lic_body = wp_remote_retrieve_body($lic_response);
811 // $lic_json = json_decode($lic_body);
812
813 // if (empty($lic_json)) {
814 // $berq_log->info("Authenticate secret invalid response");
815 // return;
816 // }
817
818 $lic_json = $this->authenticate_license($license_key, $license_check_only);
819
820 if (!empty($lic_json->lic) && $lic_json->lic->result == 'success' && ($lic_json->lic->message == 'License key activated' || $lic_json->lic->status == 'active')) {
821
822 if (!empty($lic_json->secret)) {
823 $berqconfigs->update_configs([
824 'secret' => $lic_json->secret,
825 'optimization_method' => 'cloud'
826 ]);
827 }
828
829 berqwp_update_network_option('berqwp_license_cache', $lic_json->lic);
830
831 $berq_log->info("Authentication success.");
832
833 // Fresh installation
834 if (get_option('berqwp_can_use_fluid_images') === false) {
835
836 if ($lic_json->lic->product_ref !== 'AppSumo Deal') {
837 update_option('berqwp_can_use_fluid_images', 1);
838 } else {
839 update_option('berqwp_can_use_fluid_images', 0);
840 update_option('berqwp_sync_addons', true);
841 }
842 }
843
844 } else {
845
846 $berq_log->info("Authentication failed.");
847 berqwp_remove_license_key();
848
849 $berqconfigs->update_configs([
850 'secret' => '',
851 'optimization_method' => 'local'
852 ]);
853
854 berqwp_delete_network_option('berqwp_license_cache');
855 }
856
857 }
858
859 if (!empty(berqwp_get_network_option('berqwp_license_cache'))) {
860 $this->is_key_verified = true;
861 $this->key_response = berqwp_get_network_option('berqwp_license_cache');
862 }
863
864 }
865
866 function initialize()
867 {
868
869 if (defined('DOING_CRON') && DOING_CRON) {
870 return;
871 }
872
873 if (defined('DOING_AJAX') && DOING_AJAX) {
874 return;
875 }
876
877 // Set default settings
878 require_once optifer_PATH . '/inc/initialize.php';
879
880 $this->handle_upgrade();
881
882 if (berqwp_can_use_cloud()) {
883 $license_key = berqwp_get_license_key();
884 $lic_json = $this->authenticate_license($license_key, true);
885
886 if (!empty($lic_json->lic) && $lic_json->lic->result == 'success' && ($lic_json->lic->message == 'License key activated' || $lic_json->lic->status == 'active')) {
887 berqwp_update_network_option('berqwp_license_cache', $lic_json->lic);
888 }
889 }
890
891 // Activate the license from parent site
892 // if (defined('BERQWP_LICENSE_KEY')) {
893 // $this->activate_license_from_multi_site();
894 // }
895
896 if (is_admin()) {
897 $this->berq_post_types();
898 }
899
900 // if (is_admin() && isset($_GET['bwp_get_ip'])) {
901 // $ip = file_get_contents('https://api.ipify.org');
902 // echo 'Server Public IP Address: ' . $ip;
903 // exit;
904 // }
905
906 // if (is_admin() && !empty(berqwp_get_license_key())) {
907 // $license_key = berqwp_get_license_key();
908
909 // global $berq_log;
910 // // $berq_log->info("License key check from initialize function.");
911
912 // $key_response = $this->verify_license_key($license_key);
913
914 // if (!empty($key_response) && $key_response->result == 'success' && $key_response->status == 'active') {
915 // $this->is_key_verified = true;
916 // $this->key_response = $key_response;
917
918 // // Fresh installation
919 // if (get_option('berqwp_can_use_fluid_images') === false) {
920
921 // if ($key_response->product_ref !== 'AppSumo Deal') {
922 // update_option('berqwp_can_use_fluid_images', 1);
923 // } else {
924 // update_option('berqwp_can_use_fluid_images', 0);
925 // update_option('berqwp_sync_addons', true);
926 // }
927 // }
928
929 // } else {
930 // $this->is_key_verified = false;
931
932 // if (!empty($key_response) && $key_response->result == 'error') {
933 // berqwp_delete_license_key();
934 // }
935 // }
936 // }
937
938 // redirect to berqwp admin page
939 if (get_transient('berqwp_redirect')) {
940 delete_transient('berqwp_redirect');
941 // Set the URL to redirect to after activation
942 $redirect_url = admin_url('admin.php?page=berqwp');
943
944 // Redirect after activation
945 wp_redirect($redirect_url);
946
947 // Make sure to exit after the redirect
948 exit;
949 }
950
951 // Deactivate conflicting plugins
952 if (is_admin() && isset($_POST['berqwp_plugins_deactivate']) && wp_verify_nonce($_POST['berqwp_plugins_deactivate'], 'berqwp_plugins_deactivate')) {
953 foreach ($this->conflicting_plugins as $plugin) {
954 // Deactivate each conflicting plugin
955 if (is_plugin_active($plugin)) {
956 deactivate_plugins($plugin);
957 }
958 }
959 header('location: ' . esc_url(admin_url('admin.php') . add_query_arg(['page' => 'berqwp', 'tab_id' => sanitize_text_field(wp_unslash($_GET['tab_id'] ?? ''))])));
960 exit;
961 }
962 }
963
964 function save_settings()
965 {
966
967 if (current_user_can('manage_options') && !empty($_GET['page']) && $_GET['page'] == 'berqwp' && isset($_GET['activate-free'])) {
968 $berqconfigs = berqConfigs::getInstance();
969 $configs = $berqconfigs->get_configs();
970 $site_id = $configs['site_id'];
971
972 if (empty($site_id)) {
973 $site_id = berqwp_generate_site_id();
974 }
975
976 $berqconfigs->update_configs([
977 'site_id' => $site_id,
978 'optimization_method' => 'local'
979 ]);
980
981 wp_remote_post(BerqWPCloud::$endpoint."free-user", [
982 'timeout' => 30,
983 'body' => [
984 'site_id' => $site_id,
985 'site_url' => get_option('home'),
986 ]
987 ]);
988
989 $location = get_admin_url() . 'admin.php?page=berqwp';
990 wp_safe_redirect($location);
991 exit;
992 }
993
994 require_once optifer_PATH . '/admin/save-settings.php';
995 }
996
997 function notices()
998 {
999
1000 $plugin_name = defined('BERQWP_PLUGIN_NAME') ? BERQWP_PLUGIN_NAME : 'BerqWP';
1001
1002 $this->upgrade_notice();
1003
1004 if (isset($_GET['berqwp_page_compression_enabled'])) {
1005 bwp_notice('success', 'Page Compression Enabled', "<p>Page compression has been successfully enabled on your website.</p>");
1006 }
1007
1008 if (isset($_GET['dismiss_feedback'])) {
1009 set_transient('bwp_hide_feedback_notice', true, DAY_IN_SECONDS * 14);
1010 }
1011
1012 if (isset($_GET['bwp_quit_feedback'])) {
1013 update_option('bwp_quit_feedback', true);
1014 }
1015
1016
1017 if (!empty($this->key_response) && $this->key_response->result == 'success' && $this->key_response->status == 'expired') {
1018 ?>
1019 <div class="notice notice-error">
1020 <p><strong>Error:</strong> <?php echo $plugin_name; ?> license key has expired. Please renew your subscription.</p>
1021 </div>
1022 <?php
1023 }
1024
1025 // Check connection
1026 // $check_rest = bwp_check_connection(false, !empty($_GET['bwp_connection_test']) === true);
1027 // if ($check_rest['status'] == 'error') {
1028 // bwp_notice('error', 'Website Unreachable: Connection Blocked', "<p>$plugin_name server is unable to access this website, please whitelist our server IP address. <a href='https://berqwp.com/help-center/get-started-with-berqwp/' target='_blank'>Find our server IP address here.</a></p>", [
1029 // [
1030 // 'href' => esc_attr(add_query_arg(['bwp_connection_test' => true], get_admin_url())),
1031 // 'text' => 'Check again',
1032 // 'classes' => '',
1033 // ]
1034 // ]);
1035 // }
1036
1037 // Check Permissions
1038 $cache_directory = WP_CONTENT_DIR . '/cache/berqwp/';
1039 $wp_config_file = defined('BERQWP_WP_CONFIG') ? BERQWP_WP_CONFIG : ABSPATH . 'wp-config.php';
1040
1041 if (!is_writable($cache_directory)) {
1042 bwp_notice('error', 'Cache directory is not writable', "<p>The $plugin_name cache directory at /wp-content/cache/berqwp/ is not writable. Please update the directory permissions to allow the plugin to store cached files.</p>", []);
1043 }
1044
1045 if (!defined('WP_CACHE') && !is_writable($wp_config_file)) {
1046 bwp_notice('warning', 'wp-config.php is not writable', "<p>The wp-config.php file is not writable. $plugin_name needs to write configuration settings to this file. Please adjust the file permissions or manually add the WP_CACHE constant and set it to true.</p>", []);
1047 }
1048
1049 if (defined('BERQWP_ADVANCED_CACHE_PATH')) {
1050 $adv_cache_path = BERQWP_ADVANCED_CACHE_PATH;
1051 } else {
1052 $adv_cache_path = WP_CONTENT_DIR . '/advanced-cache.php';
1053 }
1054
1055 // if (file_exists($adv_cache_path) && !is_writable($adv_cache_path)) {
1056 // bwp_notice('warning', 'advanced-cache.php is not writable', "<p>$plugin_name can't write to wp-content/advanced-cache.php — please check file permissions or re-save settings to regenerate it.</p>", []);
1057 // }
1058
1059 $berqconfigs = berqConfigs::getInstance();
1060 $configs = $berqconfigs->get_configs();
1061 $readyForCache = !empty($configs['optimization_method']);
1062
1063 if ($readyForCache && bwp_is_home_cached() && !get_transient('bwp_hide_feedback_notice') && !get_option('bwp_quit_feedback') && bwp_show_account()) {
1064 bwp_notice('info bwp_feedback', 'Loving BerqWP\'s performance?', '<p>Show some love and help us grow 👉 - <a href="https://wordpress.org/support/plugin/searchpro/reviews/#new-post" target="_blank">Rate BerqWP Plugin</a>. Your insights shape our journey.</p>', [
1065 [
1066 'href' => 'https://wordpress.org/support/plugin/searchpro/reviews/#new-post',
1067 'text' => '❤️ You deserve it',
1068 'classes' => '',
1069 'target' => '_blank',
1070 ],
1071 [
1072 'href' => get_admin_url() . 'admin.php?page=berqwp&bwp_quit_feedback',
1073 'text' => '👍 Already done',
1074 'classes' => '',
1075 'target' => '',
1076 ],
1077 [
1078 'href' => get_admin_url() . 'admin.php?page=berqwp&dismiss_feedback',
1079 'text' => 'Not Now',
1080 'classes' => '',
1081 'target' => '',
1082 ]
1083 ]);
1084
1085 // $notice = '<div class="bwp-notice bwp_feedback">';
1086 // $notice .= '<p>';
1087 // $notice .= __('🎉 <b>Loving BerqWP\'s performance? 🚀</b> Show some love and help us grow 👉 - <a href="https://wordpress.org/support/plugin/searchpro/reviews/#new-post" target="_blank">Rate BerqWP Plugin</a>. Your insights shape our journey.', 'searchpro');
1088 // $notice .= '<a href="'.get_admin_url().'admin.php?page=berqwp&dismiss_feedback" style="display: table;margin-left: 50px;color: #969595;display: table;">Dismiss</a>';
1089 // $notice .= '</p>';
1090 // $notice .= '</div>';
1091 // echo wp_kses_post($notice);
1092 }
1093
1094 // purge single page notice
1095 if (get_transient('berq_purge_page_notice')) {
1096 $flushed_page = esc_url(get_transient('berq_purge_page_notice'));
1097 delete_transient('berq_purge_page_notice');
1098 bwp_notice('success', '', "<p>Page cache has been flushed for $flushed_page.</p>", []);
1099 }
1100
1101 // purge all cache notice
1102 if (get_transient('berq_cache_cleared_notice')) {
1103 delete_transient('berq_cache_cleared_notice');
1104 bwp_notice('success', '', '<p>The cache has been cleared. Our automatic cache warm-up system will generate the cache. Alternatively, you can
1105 visit any page to create its cache immediately.</p>', []);
1106 }
1107
1108 // purge cdn cache notice
1109 if (get_transient('berq_purge_cdn_notice')) {
1110 delete_transient('berq_purge_cdn_notice');
1111 bwp_notice('success', '', '<p>The CDN and page cache have been flushed.</p>', []);
1112 }
1113
1114 // purge critical css cache notice
1115 if (get_transient('berq_purge_criticalcss_notice')) {
1116 delete_transient('berq_purge_criticalcss_notice');
1117 bwp_notice('success', '', '<p>Critical CSS cache has been flushed from the cloud.</p>', []);
1118 }
1119
1120 // cache warmup notice
1121 if (get_transient('berq_cache_warmup_notice')) {
1122 delete_transient('berq_cache_warmup_notice');
1123 bwp_notice('success', 'Cache Warmup Queued', '<p>The cache warmup has been added to the queue. Your website pages will be optimized in the background and ready soon.</p>', []);
1124 }
1125
1126 // force page cache notice
1127 if (get_transient('berq_force_cache_notice')) {
1128 $forced_page = esc_url(get_transient('berq_force_cache_notice'));
1129 delete_transient('berq_force_cache_notice');
1130 bwp_notice('success', '', "<p>The cache for page ($forced_page) has been queued with high priority and will be generated soon.</p>", []);
1131 }
1132
1133 if ($readyForCache && get_option('bwp_require_flush_cache', false)) {
1134 $cache_rebuild_btns = [
1135 [
1136 'href' => esc_attr(wp_nonce_url(admin_url('admin-post.php?action=clear_cache'), 'clear_cache_action')),
1137 'text' => 'Flush cache',
1138 'classes' => '',
1139 ]
1140 ];
1141
1142 if (berqwp_can_use_cloud()) {
1143 $cache_rebuild_btns[] = [
1144 'href' => esc_attr(wp_nonce_url(admin_url('admin-post.php?action=warmup_cache'), 'warmup_cache_action')),
1145 'text' => 'Warmup cache',
1146 'classes' => '',
1147 ];
1148 }
1149
1150 bwp_notice('warning', 'Cache Flush Required', '<p>To apply the changes, please flush the cache.</p>', $cache_rebuild_btns);
1151 }
1152
1153 if (berq_is_localhost() && berqwp_can_use_cloud()) {
1154 bwp_notice('warning', 'Localhost environment detected:', "<p>$plugin_name requires a live, publicly accessible website to function.</p>", []);
1155 }
1156
1157 $plugins_to_deactivate = '';
1158
1159 foreach ($this->conflicting_plugins as $plugin) {
1160 if (is_plugin_active($plugin)) {
1161 $plugins_to_deactivate .= '<li><b>' . basename(dirname($plugin)) . '</b></li>';
1162 }
1163 }
1164
1165 if (!empty($plugins_to_deactivate)) {
1166 echo "<style>.berqwp-plugin-conflict ul {
1167 list-style: disc;
1168 margin-left: 20px;
1169 }.berqwp-plugin-conflict form {
1170 padding: 10px;
1171 }
1172 .berqwp-plugin-conflict {
1173 display: grid;
1174 grid-template-columns: auto min-content;
1175 }</style>";
1176 echo '<div class="bwp-notice notice notice-error berqwp-plugin-conflict">';
1177 echo wp_kses_post(__('<p><strong>BerqWP Plugin Conflict:</strong> The following plugins have a same nature as BerqWP plugin. Having multiple plugins of the same type can cause unexpected results.</p>', 'searchpro'));
1178 ?>
1179 <form action="<?php echo esc_url(admin_url('admin.php') . add_query_arg(['page' => 'berqwp', 'tab_id' => sanitize_text_field(wp_unslash($_GET['tab_id'] ?? ''))])); ?>" method="post">
1180
1181 <?php
1182 $my_nonce = wp_create_nonce('berqwp_plugins_deactivate');
1183 echo '<input type="hidden" name="berqwp_plugins_deactivate" value="' . esc_attr($my_nonce) . '" />';
1184 ?>
1185
1186 <input type="submit" class="button-secondary alignright" value="Deactivate Conflicting Plugins">
1187 </form>
1188 <?php
1189 echo wp_kses_post("<ul>$plugins_to_deactivate</ul>");
1190 echo '</div>';
1191 }
1192 }
1193
1194 function berq_post_types()
1195 {
1196 // Get post type names
1197 $post_type_names = get_post_types(['public' => true, 'exclude_from_search' => false], 'names');
1198 unset($post_type_names['attachment']);
1199
1200 // var_dump($post_type_names);
1201
1202 // Modify which post types to optimize
1203 $post_type_names = apply_filters('berqwp_post_types', $post_type_names);
1204
1205 // Save the names in a WordPress option
1206 update_option('berqwp_post_type_names', $post_type_names);
1207 }
1208
1209 function plugin_settings_links($links)
1210 {
1211 $mylinks = array(
1212 '<a target="_blank" href="https://berqwp.com/help-center/">' . __('Help Center', 'searchpro') . '</a>',
1213 '<a href="' . admin_url('admin.php?page=berqwp') . '">' . __('Settings', 'searchpro') . '</a>',
1214 );
1215
1216 return array_merge($links, $mylinks);
1217 }
1218
1219 function authenticate_license($license_key, $check_only = false) {
1220
1221 $berqconfigs = berqConfigs::getInstance();
1222 $berqwp_configs = $berqconfigs->get_configs();
1223 $site_id = $berqwp_configs['site_id'];
1224
1225 if (empty($site_id)) {
1226 $blog_id = get_current_blog_id();
1227 $network_id = function_exists('get_current_network_id') ? get_current_network_id() : 1;
1228 $siteurl = get_option('siteurl');
1229 $site_id = md5("berqwp|$network_id|$blog_id|$siteurl");
1230
1231 $berqconfigs->update_configs(['site_id' => $site_id]);
1232 }
1233
1234 global $berq_log;
1235 $transient_key = 'berqwp_lic_response_cache'; // Set a unique key for the transient
1236 $expire_transient_key = 'berqwp_lic_cache_expire'; // Set a unique key for the transient
1237
1238 if (!$check_only) {
1239 berqwp_delete_network_option($transient_key);
1240 }
1241
1242 // Check if the response is already cached
1243 $lic_json = berqwp_get_network_option($transient_key);
1244 $cache_expire_time = (int) berqwp_get_network_option($expire_transient_key);
1245
1246 if (false === $lic_json || $cache_expire_time < time()) {
1247
1248 $berq_log->info("Authenticating license key");
1249
1250 $body = [
1251 'license_key' => $license_key,
1252 'site_id' => $site_id,
1253 'site_url' => home_url(),
1254 ];
1255
1256 if ($check_only) {
1257 $body['check'] = true;
1258 }
1259
1260 $lic_response = wp_remote_post(BerqWPCloud::$endpoint."authenticate", [
1261 'timeout' => 30,
1262 'body' => $body
1263 ]);
1264
1265 if (is_wp_error($lic_response)) {
1266 $berq_log->info("Couldn't reach BerqWP server.".print_r($lic_response, true));
1267 return;
1268 }
1269
1270 $lic_body = wp_remote_retrieve_body($lic_response);
1271 $lic_json = json_decode($lic_body);
1272
1273 if (!empty($lic_json)) {
1274 berqwp_update_network_option($transient_key, $lic_json);
1275 berqwp_update_network_option($expire_transient_key, time() + DAY_IN_SECONDS);
1276 }
1277
1278 }
1279
1280
1281 return $lic_json;
1282
1283 }
1284
1285 function verify_license_key($license_key, $action = 'slm_check')
1286 {
1287 if ($action !== 'slm_deactivate') {
1288 return;
1289 }
1290
1291 // Action
1292 // slm_activate
1293 // slm_deactivate
1294 // slm_check
1295
1296 if (empty($license_key)) {
1297 return;
1298 }
1299
1300 if (defined('BERQWP_DOING_LICENSE_CHECK')) {
1301 // sleep(1);
1302 return;
1303 }
1304
1305 /**
1306 * Replaced transients with options
1307 */
1308
1309 global $berq_log;
1310 $transient_key = 'berq_lic_response_cache'; // Set a unique key for the transient
1311 $expire_transient_key = 'berq_lic_cache_expire'; // Set a unique key for the transient
1312
1313 if ($action !== 'slm_check') {
1314 // delete_transient( $transient_key );
1315 berqwp_delete_network_option($transient_key);
1316 }
1317
1318 // Check if the response is already cached
1319 // $cached_response = get_transient($transient_key);
1320 $cached_response = berqwp_get_network_option($transient_key);
1321 $cache_expire_time = (int) berqwp_get_network_option($expire_transient_key);
1322
1323
1324 if (false === $cached_response || $cache_expire_time < time()) {
1325 // If not cached, perform the API request
1326
1327 $rateLimiter = new RateLimiter(5, 60, optifer_cache . 'ratelimit/');
1328 $clientIdentifier = gethostname();
1329 if (function_exists('is_multisite') && is_multisite()) {
1330 $clientIdentifier .= '-site-' . get_current_blog_id();
1331 }
1332
1333 if ($rateLimiter->isRateLimited($clientIdentifier)) {
1334 return false;
1335 }
1336
1337 define('BERQWP_DOING_LICENSE_CHECK', true);
1338
1339 $berq_log->info('Checking the license key.');
1340
1341 $parsed_url = parse_url(home_url());
1342 $domain = $parsed_url['host'];
1343
1344 $api_params = array(
1345 'registered_domain' => $domain,
1346 'slm_action' => $action,
1347 'secret_key' => BERQ_SECRET,
1348 'license_key' => $license_key,
1349 'version' => BERQWP_VERSION,
1350 't' => '',
1351 );
1352
1353 $endpoint_url = esc_url(add_query_arg($api_params, BERQ_SERVER));
1354
1355 $args = array(
1356 'method' => 'POST', // Only POST works for unknown reason
1357 'timeout' => 20,
1358 'redirection' => 5,
1359 'blocking' => true,
1360 'headers' => array(
1361 'User-Agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
1362 'Accept' => 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
1363 'Accept-Encoding' => 'gzip, deflate, br',
1364 'Accept-Language' => 'en-US,en;q=0.9',
1365 'Connection' => 'keep-alive',
1366 'Referer' => 'https://berqwp.com/', // Adjust based on actual referer
1367 ),
1368 'cookies' => array(),
1369 'sslverify' => false, // Disable SSL verification (for debugging purposes)
1370 );
1371
1372 // $request = wp_remote_request( $endpoint_url, $args );
1373
1374
1375 $berq_log->info('Making request 1');
1376
1377 $query_string = http_build_query($api_params);
1378 // $client = new HttpClient(BERQ_SERVER);
1379 // $client->setUserAgent('BerqWP');
1380 // $client->post('?' . $query_string, $api_params);
1381 // $client->setTimeout(30);
1382
1383 $client = new \BerqWP_Deps\GuzzleHttp\Client([
1384 'timeout' => 60,
1385 'http_errors' => false,
1386 'verify' => false,
1387 ]);
1388
1389 $response = $client->post(BERQ_SERVER . '?' . $query_string, [
1390 'form_params' => $api_params,
1391 ]);
1392 $statusCode = $response->getStatusCode();
1393 $response = $response->getBody()->getContents();
1394
1395 // var_dump($client->getContent(), $client->getError(), $api_params);
1396
1397 // $berq_log->info(print_r($client->getContent(), true).'---'.$client->ok());
1398
1399 if ($statusCode >= 200 && $statusCode < 300) {
1400 // $response = $client->getContent();
1401 $JSON = json_decode($response);
1402
1403 if ($action == 'slm_activate' && isset($JSON->error_code) && $JSON->message !== 'Invalid license key') {
1404 sleep(1);
1405 $api_params = array(
1406 'registered_domain' => $domain,
1407 'slm_action' => 'slm_check',
1408 'secret_key' => BERQ_SECRET,
1409 'license_key' => $license_key,
1410 't' => '',
1411 );
1412
1413 $berq_log->info('Making request 2');
1414
1415 $query_string = http_build_query($api_params);
1416 // $client = new HttpClient(BERQ_SERVER);
1417 // $client->setUserAgent('BerqWP');
1418 // $client->post('?' . $query_string, $api_params);
1419
1420
1421 // if ($client->ok()) {
1422 // $response = $client->getContent();
1423 // }
1424
1425 $response = $client->post(BERQ_SERVER . '?' . $query_string, [
1426 'form_params' => $api_params,
1427 ]);
1428
1429 $response = $response->getBody()->getContents();
1430 }
1431 }
1432
1433 if ($action !== 'slm_check') {
1434 $api_params = array(
1435 'registered_domain' => $domain,
1436 'slm_action' => 'slm_check',
1437 'secret_key' => BERQ_SECRET,
1438 'license_key' => $license_key,
1439 'version' => BERQWP_VERSION,
1440 't' => '',
1441 );
1442
1443 $berq_log->info('Making request 3');
1444
1445 $query_string = http_build_query($api_params);
1446 // $client = new HttpClient(BERQ_SERVER);
1447 // $client->setUserAgent('BerqWP');
1448 // $client->post('?' . $query_string, $api_params);
1449
1450
1451 // if ($client->ok()) {
1452 // $response = $client->getContent();
1453 // }
1454
1455 $response = $client->post(BERQ_SERVER . '?' . $query_string, [
1456 'form_params' => $api_params,
1457 ]);
1458
1459 $response = $response->getBody()->getContents();
1460 }
1461
1462 if (empty($response)) {
1463 return;
1464 }
1465
1466 $cached_response = json_decode($response);
1467
1468
1469 if ($action == 'slm_check' && !empty($cached_response) && !empty($cached_response->result)) {
1470 $domain_found = false;
1471
1472 foreach ($cached_response->registered_domains as $reg_domain) {
1473 $domain_name = str_replace('www.', '', $domain);
1474 $domain_name_www = 'www.' . $domain;
1475
1476 if ($reg_domain->registered_domain == $domain_name || $reg_domain->registered_domain == $domain_name_www || $reg_domain->registered_domain == $domain) {
1477 $domain_found = true;
1478 break;
1479 }
1480 }
1481
1482 if ($domain_found) {
1483 // Cache the response for 24 hours
1484 // set_transient($transient_key, $cached_response, 24 * HOUR_IN_SECONDS);
1485 berqwp_update_network_option($transient_key, $cached_response);
1486 berqwp_update_network_option($expire_transient_key, time() + MONTH_IN_SECONDS);
1487 } else {
1488
1489 berqwp_delete_network_option($transient_key);
1490 berqwp_delete_network_option($expire_transient_key);
1491 berqwp_delete_license_key();
1492
1493 return false;
1494 }
1495 }
1496
1497 // if ($action == 'slm_check' && !empty($cached_response) && $cached_response->result == 'success' && $cached_response->status == 'active') {
1498 // // Cache the response for 24 hours
1499 // set_transient($transient_key, $cached_response, 24 * HOUR_IN_SECONDS);
1500 // }
1501
1502 // if ($action == 'slm_check' && !empty($cached_response) && $cached_response->result == 'error') {
1503 // // Key verification failed, cache the response for 14 hours
1504 // // preventing unnecessary verification requests
1505 // set_transient($transient_key, $cached_response, 14 * HOUR_IN_SECONDS);
1506 // }
1507
1508 } else {
1509 // $berq_log->info('Delivering license key object from the transient cache.');
1510 }
1511
1512 // Return the cached response
1513 return $cached_response;
1514 }
1515
1516 // Disable emoji functionality
1517 function disable_emoji()
1518 {
1519 // Remove emoji-related actions and filters
1520 remove_action('wp_head', 'print_emoji_detection_script', 7);
1521 remove_action('admin_print_scripts', 'print_emoji_detection_script');
1522 remove_action('wp_print_styles', 'print_emoji_styles');
1523 remove_action('admin_print_styles', 'print_emoji_styles');
1524 remove_filter('the_content_feed', 'wp_staticize_emoji');
1525 remove_filter('comment_text_rss', 'wp_staticize_emoji');
1526 remove_filter('wp_mail', 'wp_staticize_emoji_for_email');
1527
1528 // Remove emoji-related TinyMCE plugins
1529 add_filter('tiny_mce_plugins', [$this, 'disable_emoji_tinymce']);
1530 }
1531
1532 // Filter function to disable emoji-related TinyMCE plugins
1533 function disable_emoji_tinymce($plugins)
1534 {
1535 if (is_array($plugins)) {
1536 return array_diff($plugins, array('wpemoji'));
1537 } else {
1538 return array();
1539 }
1540 }
1541
1542 // Apply the selected Heartbeat API mode (default / restrict / throttle / disable)
1543 function apply_heartbeat_settings()
1544 {
1545 $mode = get_option('berqwp_heartbeat_mode', 'default');
1546
1547 if ($mode === 'default') {
1548 return;
1549 }
1550
1551 if ($mode === 'disable') {
1552 wp_deregister_script('heartbeat');
1553 return;
1554 }
1555
1556 if ($mode === 'restrict') {
1557 global $pagenow;
1558 if (!in_array($pagenow, ['post.php', 'post-new.php'], true)) {
1559 wp_deregister_script('heartbeat');
1560 }
1561 return;
1562 }
1563
1564 if ($mode === 'throttle') {
1565 add_filter('heartbeat_settings', [$this, 'throttle_heartbeat_interval']);
1566 }
1567 }
1568
1569 // Filter callback: clamp Heartbeat interval to the configured value
1570 function throttle_heartbeat_interval($settings)
1571 {
1572 $settings['interval'] = max(15, min(300, (int) get_option('berqwp_heartbeat_interval', 60)));
1573 return $settings;
1574 }
1575
1576 // Apply the remaining Debloat toggles (embeds, XML-RPC, REST head links, head meta, self pingbacks)
1577 function apply_debloat_settings()
1578 {
1579 if (get_option('berqwp_disable_embeds')) {
1580 add_action('wp_footer', [$this, 'deregister_embed_script']);
1581 remove_action('wp_head', 'wp_oembed_add_discovery_links');
1582 remove_action('wp_head', 'wp_oembed_add_host_js');
1583 add_filter('embed_oembed_discover', '__return_false');
1584 }
1585
1586 if (get_option('berqwp_disable_xmlrpc')) {
1587 add_filter('xmlrpc_enabled', '__return_false');
1588 }
1589
1590 if (get_option('berqwp_remove_rest_head_links')) {
1591 remove_action('wp_head', 'rest_output_link_wp_head');
1592 remove_action('template_redirect', 'rest_output_link_header', 11);
1593 }
1594
1595 if (get_option('berqwp_remove_head_meta')) {
1596 remove_action('wp_head', 'rsd_link');
1597 remove_action('wp_head', 'wlwmanifest_link');
1598 remove_action('wp_head', 'wp_shortlink_wp_head');
1599 remove_action('wp_head', 'wp_generator');
1600 }
1601
1602 if (get_option('berqwp_disable_self_pingbacks')) {
1603 add_action('pre_ping', [$this, 'remove_self_pingbacks']);
1604 }
1605 }
1606
1607 // Callback: deregister the embed.min.js frontend script
1608 function deregister_embed_script()
1609 {
1610 wp_deregister_script('wp-embed');
1611 }
1612
1613 // Callback: strip links pointing back to this site from the pingback target list
1614 function remove_self_pingbacks(&$links)
1615 {
1616 $home = get_option('home');
1617 foreach ($links as $key => $link) {
1618 if (strpos($link, (string) $home) === 0) {
1619 unset($links[$key]);
1620 }
1621 }
1622 }
1623
1624 // AJAX: run a single Database tab cleanup action on demand
1625 function ajax_db_run_action()
1626 {
1627 check_ajax_referer('wp_rest', 'nonce');
1628
1629 if (!current_user_can('manage_options')) {
1630 wp_send_json_error(['message' => __('Permission denied.', 'searchpro')]);
1631 }
1632
1633 $task = isset($_POST['task']) ? sanitize_text_field($_POST['task']) : '';
1634 $limit = isset($_POST['limit']) ? max(0, (int) $_POST['limit']) : (int) get_option('berqwp_db_revision_limit', 5);
1635
1636 $result = berqDatabase::run_task($task, ['limit' => $limit]);
1637
1638 if ($result === false) {
1639 wp_send_json_error(['message' => __('Unknown cleanup task.', 'searchpro')]);
1640 }
1641
1642 wp_send_json_success($result);
1643 }
1644
1645 // Register a "once monthly" WP-Cron interval (core only ships hourly/twicedaily/daily)
1646 function add_monthly_cron_schedule($schedules)
1647 {
1648 $schedules['berqwp_monthly'] = [
1649 'interval' => 30 * DAY_IN_SECONDS,
1650 'display' => __('Once Monthly', 'searchpro'),
1651 ];
1652 return $schedules;
1653 }
1654
1655 // (Re)schedule the Database tab's recurring optimization cron event based on current settings
1656 function reschedule_db_cron()
1657 {
1658 wp_clear_scheduled_hook('berqwp_db_scheduled_optimize');
1659
1660 if (!get_option('berqwp_db_schedule_enabled')) {
1661 return;
1662 }
1663
1664 $frequency = get_option('berqwp_db_schedule_frequency', 'weekly');
1665 $recurrence = 'weekly';
1666 if ($frequency === 'daily') {
1667 $recurrence = 'daily';
1668 } elseif ($frequency === 'monthly') {
1669 $recurrence = 'berqwp_monthly';
1670 }
1671
1672 if (!wp_next_scheduled('berqwp_db_scheduled_optimize')) {
1673 wp_schedule_event(time(), $recurrence, 'berqwp_db_scheduled_optimize');
1674 }
1675 }
1676
1677 // Clear the Database tab's cron event (called on plugin deactivation)
1678 function clear_db_cron()
1679 {
1680 wp_clear_scheduled_hook('berqwp_db_scheduled_optimize');
1681 }
1682
1683 // WP-Cron callback: run every scheduled task and record the run timestamp
1684 function run_scheduled_db_optimize()
1685 {
1686 $tasks = get_option('berqwp_db_scheduled_tasks', []);
1687 $limit = (int) get_option('berqwp_db_revision_limit', 5);
1688
1689 foreach ((array) $tasks as $task) {
1690 berqDatabase::run_task($task, ['limit' => $limit]);
1691 }
1692
1693 update_option('berqwp_db_optimize_last_run', time());
1694 }
1695
1696 // function clear_cache(WP_REST_Request $request)
1697 // {
1698 // require_once optifer_PATH . 'api/clear_cache.php';
1699 // }
1700
1701 // function warmup_cache(WP_REST_Request $request)
1702 // {
1703 // require_once optifer_PATH . 'api/warmup_cache.php';
1704 // }
1705
1706 // function store_cache(WP_REST_Request $request)
1707 // {
1708 // require_once optifer_PATH . 'api/store_cache.php';
1709 // }
1710
1711 // function store_javascript_cache(WP_REST_Request $request)
1712 // {
1713 // require_once optifer_PATH . 'api/store_javascript_cache.php';
1714 // }
1715
1716 // ==========================================
1717 // Multisite Network Support
1718 // ==========================================
1719
1720 function register_network_menu()
1721 {
1722 $svg = '<svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
1723 <path fill-rule="evenodd" clip-rule="evenodd" d="M6.43896 0H17.561C21.1172 0 24 2.88287 24 6.43903V17.561C24 21.1171 21.1172 24 17.561 24H6.43896C2.88281 24 0 21.1171 0 17.561V6.43903C0 2.88287 2.88281 0 6.43896 0ZM15.7888 4.09753L8.59961 12.7534H12.3517L7.02441 20.4878L16.3903 11.0222L12.7814 10.3799L15.7888 4.09753Z" fill="#a7aaad"/>
1724 </svg>';
1725
1726 $plugin_name = defined('BERQWP_PLUGIN_NAME') ? BERQWP_PLUGIN_NAME : 'BerqWP';
1727
1728 add_menu_page(
1729 $plugin_name . ' Network',
1730 $plugin_name,
1731 'manage_network_options',
1732 'berqwp-network',
1733 [$this, 'network_admin_page'],
1734 'data:image/svg+xml;base64,' . base64_encode($svg),
1735 80
1736 );
1737 }
1738
1739 function network_admin_page()
1740 {
1741 require_once optifer_PATH . 'admin/network-admin-page.php';
1742 }
1743
1744 function save_network_settings()
1745 {
1746 check_admin_referer('berqwp_network_save');
1747
1748 if (!current_user_can('manage_network_options')) {
1749 wp_die('Unauthorized');
1750 }
1751
1752 // License activation
1753 if (!empty($_POST['berqwp_license_key'])) {
1754 $key = sanitize_text_field($_POST['berqwp_license_key']);
1755 // $key_response = $this->verify_license_key($key, 'slm_activate');
1756 $key_response = $this->authenticate_license($key);
1757 if (!empty($key_response->lic) && isset($key_response->lic->result) && $key_response->lic->result == 'success') {
1758 berqwp_update_license_key($key);
1759 }
1760 }
1761
1762 // License deactivation
1763 if (!empty($_POST['berq_deactivate_key'])) {
1764 $existing_key = berqwp_get_license_key();
1765 if (!empty($existing_key)) {
1766 $this->verify_license_key($existing_key, 'slm_deactivate');
1767 }
1768 berqwp_delete_license_key();
1769 berqwp_delete_network_option('berq_lic_response_cache');
1770 berqwp_delete_network_option('berq_lic_cache_expire');
1771 berqwp_delete_network_option('berqwp_lic_response_cache');
1772 berqwp_delete_network_option('berqwp_lic_cache_expire');
1773 }
1774
1775 // Flush all sites cache
1776 if (!empty($_POST['berqwp_flush_all_cache'])) {
1777 $cache_directory = WP_CONTENT_DIR . '/cache/berqwp/html/';
1778 if (is_dir($cache_directory)) {
1779 berqwp_unlink_recursive($cache_directory);
1780 }
1781 }
1782
1783 wp_safe_redirect(network_admin_url('admin.php?page=berqwp-network&updated=true'));
1784 exit;
1785 }
1786
1787 function regenerate_blog_map()
1788 {
1789 if (!function_exists('is_multisite') || !is_multisite()) {
1790 return;
1791 }
1792
1793 $sites = get_sites(['fields' => 'ids', 'number' => 0]);
1794 $map = [
1795 'subdomains' => [],
1796 'subdirs' => [],
1797 ];
1798
1799 $is_subdomain = defined('SUBDOMAIN_INSTALL') && SUBDOMAIN_INSTALL;
1800
1801 foreach ($sites as $site_id) {
1802 $site = get_site($site_id);
1803 if (!$site) continue;
1804
1805 if ($is_subdomain) {
1806 $map['subdomains'][strtolower($site->domain)] = (int) $site_id;
1807 } else {
1808 $path = rtrim($site->path, '/') . '/';
1809 if ($path !== '/') {
1810 $map['subdirs'][$path] = (int) $site_id;
1811 }
1812 }
1813 }
1814
1815 $map_file = WP_CONTENT_DIR . '/cache/berqwp/blog-map.json';
1816 $map_dir = dirname($map_file);
1817 if (!is_dir($map_dir)) {
1818 wp_mkdir_p($map_dir);
1819 }
1820 file_put_contents($map_file, json_encode($map, JSON_PRETTY_PRINT));
1821 }
1822
1823 function on_new_site_created($new_site)
1824 {
1825 if (function_exists('is_plugin_active_for_network') && is_plugin_active_for_network('searchpro/berqwp.php')) {
1826 switch_to_blog($new_site->blog_id);
1827 berqwp_activate_single_site();
1828 restore_current_blog();
1829 }
1830 $this->regenerate_blog_map();
1831 }
1832
1833 function register_menu()
1834 {
1835 $svg = '<svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
1836 <path fill-rule="evenodd" clip-rule="evenodd" d="M6.43896 0H17.561C21.1172 0 24 2.88287 24 6.43903V17.561C24 21.1171 21.1172 24 17.561 24H6.43896C2.88281 24 0 21.1171 0 17.561V6.43903C0 2.88287 2.88281 0 6.43896 0ZM15.7888 4.09753L8.59961 12.7534H12.3517L7.02441 20.4878L16.3903 11.0222L12.7814 10.3799L15.7888 4.09753Z" fill="#a7aaad"/>
1837 </svg>';
1838
1839 $plugin_name = defined('BERQWP_PLUGIN_NAME') ? BERQWP_PLUGIN_NAME : 'BerqWP';
1840
1841 add_menu_page($plugin_name, $plugin_name, 'manage_options', 'berqwp', [$this, 'admin_page'], 'data:image/svg+xml;base64,' . base64_encode($svg), 80);
1842 }
1843
1844 function admin_page()
1845 {
1846
1847 $berqconfigs = berqConfigs::getInstance();
1848 $configs = $berqconfigs->get_configs();
1849
1850 if (!empty($configs['optimization_method'])) {
1851 require_once optifer_PATH . 'admin/admin-page.php';
1852 } else {
1853 require_once optifer_PATH . 'admin/intro-page.php';
1854 }
1855 }
1856 }
1857
1858 berqWP::getInstance();
1859 // global $berqWP;
1860 // $berqWP = new berqWP();
1861 }
1862