PluginProbe
BerqWP – All-In-One Optimization for Core Web Vitals, Cache, CDN, Images, CSS & JavaScript / trunk
BerqWP – All-In-One Optimization for Core Web Vitals, Cache, CDN, Images, CSS & JavaScript vtrunk
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 1.9.7 All 169 releases
searchpro / inc / class-berqwp.php

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

1,852 lines 60.1 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 (!empty($_GET['page']) && $_GET['page'] == 'berqwp' && isset($_GET['activate-free'])) {
968 $berqconfigs = berqConfigs::getInstance();
969 $berqconfigs->update_configs(['optimization_method' => 'local']);
970
971 wp_remote_post(BerqWPCloud::$endpoint."free-user", [
972 'timeout' => 30,
973 'body' => [
974 'site_id' => $berqconfigs->get_configs()['site_id'],
975 'site_url' => get_option('home'),
976 ]
977 ]);
978
979 $location = get_admin_url() . 'admin.php?page=berqwp';
980 wp_safe_redirect($location);
981 exit;
982 }
983
984 require_once optifer_PATH . '/admin/save-settings.php';
985 }
986
987 function notices()
988 {
989
990 $plugin_name = defined('BERQWP_PLUGIN_NAME') ? BERQWP_PLUGIN_NAME : 'BerqWP';
991
992 $this->upgrade_notice();
993
994 if (isset($_GET['berqwp_page_compression_enabled'])) {
995 bwp_notice('success', 'Page Compression Enabled', "<p>Page compression has been successfully enabled on your website.</p>");
996 }
997
998 if (isset($_GET['dismiss_feedback'])) {
999 set_transient('bwp_hide_feedback_notice', true, DAY_IN_SECONDS * 14);
1000 }
1001
1002 if (isset($_GET['bwp_quit_feedback'])) {
1003 update_option('bwp_quit_feedback', true);
1004 }
1005
1006
1007 if (!empty($this->key_response) && $this->key_response->result == 'success' && $this->key_response->status == 'expired') {
1008 ?>
1009 <div class="notice notice-error">
1010 <p><strong>Error:</strong> <?php echo $plugin_name; ?> license key has expired. Please renew your subscription.</p>
1011 </div>
1012 <?php
1013 }
1014
1015 // Check connection
1016 // $check_rest = bwp_check_connection(false, !empty($_GET['bwp_connection_test']) === true);
1017 // if ($check_rest['status'] == 'error') {
1018 // 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>", [
1019 // [
1020 // 'href' => esc_attr(add_query_arg(['bwp_connection_test' => true], get_admin_url())),
1021 // 'text' => 'Check again',
1022 // 'classes' => '',
1023 // ]
1024 // ]);
1025 // }
1026
1027 // Check Permissions
1028 $cache_directory = WP_CONTENT_DIR . '/cache/berqwp/';
1029 $wp_config_file = defined('BERQWP_WP_CONFIG') ? BERQWP_WP_CONFIG : ABSPATH . 'wp-config.php';
1030
1031 if (!is_writable($cache_directory)) {
1032 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>", []);
1033 }
1034
1035 if (!defined('WP_CACHE') && !is_writable($wp_config_file)) {
1036 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>", []);
1037 }
1038
1039 if (defined('BERQWP_ADVANCED_CACHE_PATH')) {
1040 $adv_cache_path = BERQWP_ADVANCED_CACHE_PATH;
1041 } else {
1042 $adv_cache_path = WP_CONTENT_DIR . '/advanced-cache.php';
1043 }
1044
1045 // if (file_exists($adv_cache_path) && !is_writable($adv_cache_path)) {
1046 // 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>", []);
1047 // }
1048
1049 $berqconfigs = berqConfigs::getInstance();
1050 $configs = $berqconfigs->get_configs();
1051 $readyForCache = !empty($configs['optimization_method']);
1052
1053 if ($readyForCache && bwp_is_home_cached() && !get_transient('bwp_hide_feedback_notice') && !get_option('bwp_quit_feedback') && bwp_show_account()) {
1054 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>', [
1055 [
1056 'href' => 'https://wordpress.org/support/plugin/searchpro/reviews/#new-post',
1057 'text' => '❤️ You deserve it',
1058 'classes' => '',
1059 'target' => '_blank',
1060 ],
1061 [
1062 'href' => get_admin_url() . 'admin.php?page=berqwp&bwp_quit_feedback',
1063 'text' => '👍 Already done',
1064 'classes' => '',
1065 'target' => '',
1066 ],
1067 [
1068 'href' => get_admin_url() . 'admin.php?page=berqwp&dismiss_feedback',
1069 'text' => 'Not Now',
1070 'classes' => '',
1071 'target' => '',
1072 ]
1073 ]);
1074
1075 // $notice = '<div class="bwp-notice bwp_feedback">';
1076 // $notice .= '<p>';
1077 // $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');
1078 // $notice .= '<a href="'.get_admin_url().'admin.php?page=berqwp&dismiss_feedback" style="display: table;margin-left: 50px;color: #969595;display: table;">Dismiss</a>';
1079 // $notice .= '</p>';
1080 // $notice .= '</div>';
1081 // echo wp_kses_post($notice);
1082 }
1083
1084 // purge single page notice
1085 if (get_transient('berq_purge_page_notice')) {
1086 $flushed_page = esc_url(get_transient('berq_purge_page_notice'));
1087 delete_transient('berq_purge_page_notice');
1088 bwp_notice('success', '', "<p>Page cache has been flushed for $flushed_page.</p>", []);
1089 }
1090
1091 // purge all cache notice
1092 if (get_transient('berq_cache_cleared_notice')) {
1093 delete_transient('berq_cache_cleared_notice');
1094 bwp_notice('success', '', '<p>The cache has been cleared. Our automatic cache warm-up system will generate the cache. Alternatively, you can
1095 visit any page to create its cache immediately.</p>', []);
1096 }
1097
1098 // purge cdn cache notice
1099 if (get_transient('berq_purge_cdn_notice')) {
1100 delete_transient('berq_purge_cdn_notice');
1101 bwp_notice('success', '', '<p>The CDN and page cache have been flushed.</p>', []);
1102 }
1103
1104 // purge critical css cache notice
1105 if (get_transient('berq_purge_criticalcss_notice')) {
1106 delete_transient('berq_purge_criticalcss_notice');
1107 bwp_notice('success', '', '<p>Critical CSS cache has been flushed from the cloud.</p>', []);
1108 }
1109
1110 // cache warmup notice
1111 if (get_transient('berq_cache_warmup_notice')) {
1112 delete_transient('berq_cache_warmup_notice');
1113 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>', []);
1114 }
1115
1116 // force page cache notice
1117 if (get_transient('berq_force_cache_notice')) {
1118 $forced_page = esc_url(get_transient('berq_force_cache_notice'));
1119 delete_transient('berq_force_cache_notice');
1120 bwp_notice('success', '', "<p>The cache for page ($forced_page) has been queued with high priority and will be generated soon.</p>", []);
1121 }
1122
1123 if ($readyForCache && get_option('bwp_require_flush_cache', false)) {
1124 $cache_rebuild_btns = [
1125 [
1126 'href' => esc_attr(wp_nonce_url(admin_url('admin-post.php?action=clear_cache'), 'clear_cache_action')),
1127 'text' => 'Flush cache',
1128 'classes' => '',
1129 ]
1130 ];
1131
1132 if (berqwp_can_use_cloud()) {
1133 $cache_rebuild_btns[] = [
1134 'href' => esc_attr(wp_nonce_url(admin_url('admin-post.php?action=warmup_cache'), 'warmup_cache_action')),
1135 'text' => 'Warmup cache',
1136 'classes' => '',
1137 ];
1138 }
1139
1140 bwp_notice('warning', 'Cache Flush Required', '<p>To apply the changes, please flush the cache.</p>', $cache_rebuild_btns);
1141 }
1142
1143 if (berq_is_localhost() && berqwp_can_use_cloud()) {
1144 bwp_notice('warning', 'Localhost environment detected:', "<p>$plugin_name requires a live, publicly accessible website to function.</p>", []);
1145 }
1146
1147 $plugins_to_deactivate = '';
1148
1149 foreach ($this->conflicting_plugins as $plugin) {
1150 if (is_plugin_active($plugin)) {
1151 $plugins_to_deactivate .= '<li><b>' . basename(dirname($plugin)) . '</b></li>';
1152 }
1153 }
1154
1155 if (!empty($plugins_to_deactivate)) {
1156 echo "<style>.berqwp-plugin-conflict ul {
1157 list-style: disc;
1158 margin-left: 20px;
1159 }.berqwp-plugin-conflict form {
1160 padding: 10px;
1161 }
1162 .berqwp-plugin-conflict {
1163 display: grid;
1164 grid-template-columns: auto min-content;
1165 }</style>";
1166 echo '<div class="bwp-notice notice notice-error berqwp-plugin-conflict">';
1167 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'));
1168 ?>
1169 <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">
1170
1171 <?php
1172 $my_nonce = wp_create_nonce('berqwp_plugins_deactivate');
1173 echo '<input type="hidden" name="berqwp_plugins_deactivate" value="' . esc_attr($my_nonce) . '" />';
1174 ?>
1175
1176 <input type="submit" class="button-secondary alignright" value="Deactivate Conflicting Plugins">
1177 </form>
1178 <?php
1179 echo wp_kses_post("<ul>$plugins_to_deactivate</ul>");
1180 echo '</div>';
1181 }
1182 }
1183
1184 function berq_post_types()
1185 {
1186 // Get post type names
1187 $post_type_names = get_post_types(['public' => true, 'exclude_from_search' => false], 'names');
1188 unset($post_type_names['attachment']);
1189
1190 // var_dump($post_type_names);
1191
1192 // Modify which post types to optimize
1193 $post_type_names = apply_filters('berqwp_post_types', $post_type_names);
1194
1195 // Save the names in a WordPress option
1196 update_option('berqwp_post_type_names', $post_type_names);
1197 }
1198
1199 function plugin_settings_links($links)
1200 {
1201 $mylinks = array(
1202 '<a target="_blank" href="https://berqwp.com/help-center/">' . __('Help Center', 'searchpro') . '</a>',
1203 '<a href="' . admin_url('admin.php?page=berqwp') . '">' . __('Settings', 'searchpro') . '</a>',
1204 );
1205
1206 return array_merge($links, $mylinks);
1207 }
1208
1209 function authenticate_license($license_key, $check_only = false) {
1210
1211 $berqconfigs = berqConfigs::getInstance();
1212 $berqwp_configs = $berqconfigs->get_configs();
1213 $site_id = $berqwp_configs['site_id'];
1214
1215 if (empty($site_id)) {
1216 $blog_id = get_current_blog_id();
1217 $network_id = function_exists('get_current_network_id') ? get_current_network_id() : 1;
1218 $siteurl = get_option('siteurl');
1219 $site_id = md5("berqwp|$network_id|$blog_id|$siteurl");
1220
1221 $berqconfigs->update_configs(['site_id' => $site_id]);
1222 }
1223
1224 global $berq_log;
1225 $transient_key = 'berqwp_lic_response_cache'; // Set a unique key for the transient
1226 $expire_transient_key = 'berqwp_lic_cache_expire'; // Set a unique key for the transient
1227
1228 if (!$check_only) {
1229 berqwp_delete_network_option($transient_key);
1230 }
1231
1232 // Check if the response is already cached
1233 $lic_json = berqwp_get_network_option($transient_key);
1234 $cache_expire_time = (int) berqwp_get_network_option($expire_transient_key);
1235
1236 if (false === $lic_json || $cache_expire_time < time()) {
1237
1238 $berq_log->info("Authenticating license key");
1239
1240 $body = [
1241 'license_key' => $license_key,
1242 'site_id' => $site_id,
1243 'site_url' => home_url(),
1244 ];
1245
1246 if ($check_only) {
1247 $body['check'] = true;
1248 }
1249
1250 $lic_response = wp_remote_post(BerqWPCloud::$endpoint."authenticate", [
1251 'timeout' => 30,
1252 'body' => $body
1253 ]);
1254
1255 if (is_wp_error($lic_response)) {
1256 $berq_log->info("Couldn't reach BerqWP server.".print_r($lic_response, true));
1257 return;
1258 }
1259
1260 $lic_body = wp_remote_retrieve_body($lic_response);
1261 $lic_json = json_decode($lic_body);
1262
1263 if (!empty($lic_json)) {
1264 berqwp_update_network_option($transient_key, $lic_json);
1265 berqwp_update_network_option($expire_transient_key, time() + DAY_IN_SECONDS);
1266 }
1267
1268 }
1269
1270
1271 return $lic_json;
1272
1273 }
1274
1275 function verify_license_key($license_key, $action = 'slm_check')
1276 {
1277 if ($action !== 'slm_deactivate') {
1278 return;
1279 }
1280
1281 // Action
1282 // slm_activate
1283 // slm_deactivate
1284 // slm_check
1285
1286 if (empty($license_key)) {
1287 return;
1288 }
1289
1290 if (defined('BERQWP_DOING_LICENSE_CHECK')) {
1291 // sleep(1);
1292 return;
1293 }
1294
1295 /**
1296 * Replaced transients with options
1297 */
1298
1299 global $berq_log;
1300 $transient_key = 'berq_lic_response_cache'; // Set a unique key for the transient
1301 $expire_transient_key = 'berq_lic_cache_expire'; // Set a unique key for the transient
1302
1303 if ($action !== 'slm_check') {
1304 // delete_transient( $transient_key );
1305 berqwp_delete_network_option($transient_key);
1306 }
1307
1308 // Check if the response is already cached
1309 // $cached_response = get_transient($transient_key);
1310 $cached_response = berqwp_get_network_option($transient_key);
1311 $cache_expire_time = (int) berqwp_get_network_option($expire_transient_key);
1312
1313
1314 if (false === $cached_response || $cache_expire_time < time()) {
1315 // If not cached, perform the API request
1316
1317 $rateLimiter = new RateLimiter(5, 60, optifer_cache . 'ratelimit/');
1318 $clientIdentifier = gethostname();
1319 if (function_exists('is_multisite') && is_multisite()) {
1320 $clientIdentifier .= '-site-' . get_current_blog_id();
1321 }
1322
1323 if ($rateLimiter->isRateLimited($clientIdentifier)) {
1324 return false;
1325 }
1326
1327 define('BERQWP_DOING_LICENSE_CHECK', true);
1328
1329 $berq_log->info('Checking the license key.');
1330
1331 $parsed_url = parse_url(home_url());
1332 $domain = $parsed_url['host'];
1333
1334 $api_params = array(
1335 'registered_domain' => $domain,
1336 'slm_action' => $action,
1337 'secret_key' => BERQ_SECRET,
1338 'license_key' => $license_key,
1339 'version' => BERQWP_VERSION,
1340 't' => '',
1341 );
1342
1343 $endpoint_url = esc_url(add_query_arg($api_params, BERQ_SERVER));
1344
1345 $args = array(
1346 'method' => 'POST', // Only POST works for unknown reason
1347 'timeout' => 20,
1348 'redirection' => 5,
1349 'blocking' => true,
1350 'headers' => array(
1351 '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',
1352 'Accept' => 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
1353 'Accept-Encoding' => 'gzip, deflate, br',
1354 'Accept-Language' => 'en-US,en;q=0.9',
1355 'Connection' => 'keep-alive',
1356 'Referer' => 'https://berqwp.com/', // Adjust based on actual referer
1357 ),
1358 'cookies' => array(),
1359 'sslverify' => false, // Disable SSL verification (for debugging purposes)
1360 );
1361
1362 // $request = wp_remote_request( $endpoint_url, $args );
1363
1364
1365 $berq_log->info('Making request 1');
1366
1367 $query_string = http_build_query($api_params);
1368 // $client = new HttpClient(BERQ_SERVER);
1369 // $client->setUserAgent('BerqWP');
1370 // $client->post('?' . $query_string, $api_params);
1371 // $client->setTimeout(30);
1372
1373 $client = new \BerqWP_Deps\GuzzleHttp\Client([
1374 'timeout' => 60,
1375 'http_errors' => false,
1376 'verify' => false,
1377 ]);
1378
1379 $response = $client->post(BERQ_SERVER . '?' . $query_string, [
1380 'form_params' => $api_params,
1381 ]);
1382 $statusCode = $response->getStatusCode();
1383 $response = $response->getBody()->getContents();
1384
1385 // var_dump($client->getContent(), $client->getError(), $api_params);
1386
1387 // $berq_log->info(print_r($client->getContent(), true).'---'.$client->ok());
1388
1389 if ($statusCode >= 200 && $statusCode < 300) {
1390 // $response = $client->getContent();
1391 $JSON = json_decode($response);
1392
1393 if ($action == 'slm_activate' && isset($JSON->error_code) && $JSON->message !== 'Invalid license key') {
1394 sleep(1);
1395 $api_params = array(
1396 'registered_domain' => $domain,
1397 'slm_action' => 'slm_check',
1398 'secret_key' => BERQ_SECRET,
1399 'license_key' => $license_key,
1400 't' => '',
1401 );
1402
1403 $berq_log->info('Making request 2');
1404
1405 $query_string = http_build_query($api_params);
1406 // $client = new HttpClient(BERQ_SERVER);
1407 // $client->setUserAgent('BerqWP');
1408 // $client->post('?' . $query_string, $api_params);
1409
1410
1411 // if ($client->ok()) {
1412 // $response = $client->getContent();
1413 // }
1414
1415 $response = $client->post(BERQ_SERVER . '?' . $query_string, [
1416 'form_params' => $api_params,
1417 ]);
1418
1419 $response = $response->getBody()->getContents();
1420 }
1421 }
1422
1423 if ($action !== 'slm_check') {
1424 $api_params = array(
1425 'registered_domain' => $domain,
1426 'slm_action' => 'slm_check',
1427 'secret_key' => BERQ_SECRET,
1428 'license_key' => $license_key,
1429 'version' => BERQWP_VERSION,
1430 't' => '',
1431 );
1432
1433 $berq_log->info('Making request 3');
1434
1435 $query_string = http_build_query($api_params);
1436 // $client = new HttpClient(BERQ_SERVER);
1437 // $client->setUserAgent('BerqWP');
1438 // $client->post('?' . $query_string, $api_params);
1439
1440
1441 // if ($client->ok()) {
1442 // $response = $client->getContent();
1443 // }
1444
1445 $response = $client->post(BERQ_SERVER . '?' . $query_string, [
1446 'form_params' => $api_params,
1447 ]);
1448
1449 $response = $response->getBody()->getContents();
1450 }
1451
1452 if (empty($response)) {
1453 return;
1454 }
1455
1456 $cached_response = json_decode($response);
1457
1458
1459 if ($action == 'slm_check' && !empty($cached_response) && !empty($cached_response->result)) {
1460 $domain_found = false;
1461
1462 foreach ($cached_response->registered_domains as $reg_domain) {
1463 $domain_name = str_replace('www.', '', $domain);
1464 $domain_name_www = 'www.' . $domain;
1465
1466 if ($reg_domain->registered_domain == $domain_name || $reg_domain->registered_domain == $domain_name_www || $reg_domain->registered_domain == $domain) {
1467 $domain_found = true;
1468 break;
1469 }
1470 }
1471
1472 if ($domain_found) {
1473 // Cache the response for 24 hours
1474 // set_transient($transient_key, $cached_response, 24 * HOUR_IN_SECONDS);
1475 berqwp_update_network_option($transient_key, $cached_response);
1476 berqwp_update_network_option($expire_transient_key, time() + MONTH_IN_SECONDS);
1477 } else {
1478
1479 berqwp_delete_network_option($transient_key);
1480 berqwp_delete_network_option($expire_transient_key);
1481 berqwp_delete_license_key();
1482
1483 return false;
1484 }
1485 }
1486
1487 // if ($action == 'slm_check' && !empty($cached_response) && $cached_response->result == 'success' && $cached_response->status == 'active') {
1488 // // Cache the response for 24 hours
1489 // set_transient($transient_key, $cached_response, 24 * HOUR_IN_SECONDS);
1490 // }
1491
1492 // if ($action == 'slm_check' && !empty($cached_response) && $cached_response->result == 'error') {
1493 // // Key verification failed, cache the response for 14 hours
1494 // // preventing unnecessary verification requests
1495 // set_transient($transient_key, $cached_response, 14 * HOUR_IN_SECONDS);
1496 // }
1497
1498 } else {
1499 // $berq_log->info('Delivering license key object from the transient cache.');
1500 }
1501
1502 // Return the cached response
1503 return $cached_response;
1504 }
1505
1506 // Disable emoji functionality
1507 function disable_emoji()
1508 {
1509 // Remove emoji-related actions and filters
1510 remove_action('wp_head', 'print_emoji_detection_script', 7);
1511 remove_action('admin_print_scripts', 'print_emoji_detection_script');
1512 remove_action('wp_print_styles', 'print_emoji_styles');
1513 remove_action('admin_print_styles', 'print_emoji_styles');
1514 remove_filter('the_content_feed', 'wp_staticize_emoji');
1515 remove_filter('comment_text_rss', 'wp_staticize_emoji');
1516 remove_filter('wp_mail', 'wp_staticize_emoji_for_email');
1517
1518 // Remove emoji-related TinyMCE plugins
1519 add_filter('tiny_mce_plugins', [$this, 'disable_emoji_tinymce']);
1520 }
1521
1522 // Filter function to disable emoji-related TinyMCE plugins
1523 function disable_emoji_tinymce($plugins)
1524 {
1525 if (is_array($plugins)) {
1526 return array_diff($plugins, array('wpemoji'));
1527 } else {
1528 return array();
1529 }
1530 }
1531
1532 // Apply the selected Heartbeat API mode (default / restrict / throttle / disable)
1533 function apply_heartbeat_settings()
1534 {
1535 $mode = get_option('berqwp_heartbeat_mode', 'default');
1536
1537 if ($mode === 'default') {
1538 return;
1539 }
1540
1541 if ($mode === 'disable') {
1542 wp_deregister_script('heartbeat');
1543 return;
1544 }
1545
1546 if ($mode === 'restrict') {
1547 global $pagenow;
1548 if (!in_array($pagenow, ['post.php', 'post-new.php'], true)) {
1549 wp_deregister_script('heartbeat');
1550 }
1551 return;
1552 }
1553
1554 if ($mode === 'throttle') {
1555 add_filter('heartbeat_settings', [$this, 'throttle_heartbeat_interval']);
1556 }
1557 }
1558
1559 // Filter callback: clamp Heartbeat interval to the configured value
1560 function throttle_heartbeat_interval($settings)
1561 {
1562 $settings['interval'] = max(15, min(300, (int) get_option('berqwp_heartbeat_interval', 60)));
1563 return $settings;
1564 }
1565
1566 // Apply the remaining Debloat toggles (embeds, XML-RPC, REST head links, head meta, self pingbacks)
1567 function apply_debloat_settings()
1568 {
1569 if (get_option('berqwp_disable_embeds')) {
1570 add_action('wp_footer', [$this, 'deregister_embed_script']);
1571 remove_action('wp_head', 'wp_oembed_add_discovery_links');
1572 remove_action('wp_head', 'wp_oembed_add_host_js');
1573 add_filter('embed_oembed_discover', '__return_false');
1574 }
1575
1576 if (get_option('berqwp_disable_xmlrpc')) {
1577 add_filter('xmlrpc_enabled', '__return_false');
1578 }
1579
1580 if (get_option('berqwp_remove_rest_head_links')) {
1581 remove_action('wp_head', 'rest_output_link_wp_head');
1582 remove_action('template_redirect', 'rest_output_link_header', 11);
1583 }
1584
1585 if (get_option('berqwp_remove_head_meta')) {
1586 remove_action('wp_head', 'rsd_link');
1587 remove_action('wp_head', 'wlwmanifest_link');
1588 remove_action('wp_head', 'wp_shortlink_wp_head');
1589 remove_action('wp_head', 'wp_generator');
1590 }
1591
1592 if (get_option('berqwp_disable_self_pingbacks')) {
1593 add_action('pre_ping', [$this, 'remove_self_pingbacks']);
1594 }
1595 }
1596
1597 // Callback: deregister the embed.min.js frontend script
1598 function deregister_embed_script()
1599 {
1600 wp_deregister_script('wp-embed');
1601 }
1602
1603 // Callback: strip links pointing back to this site from the pingback target list
1604 function remove_self_pingbacks(&$links)
1605 {
1606 $home = get_option('home');
1607 foreach ($links as $key => $link) {
1608 if (strpos($link, (string) $home) === 0) {
1609 unset($links[$key]);
1610 }
1611 }
1612 }
1613
1614 // AJAX: run a single Database tab cleanup action on demand
1615 function ajax_db_run_action()
1616 {
1617 check_ajax_referer('wp_rest', 'nonce');
1618
1619 if (!current_user_can('manage_options')) {
1620 wp_send_json_error(['message' => __('Permission denied.', 'searchpro')]);
1621 }
1622
1623 $task = isset($_POST['task']) ? sanitize_text_field($_POST['task']) : '';
1624 $limit = isset($_POST['limit']) ? max(0, (int) $_POST['limit']) : (int) get_option('berqwp_db_revision_limit', 5);
1625
1626 $result = berqDatabase::run_task($task, ['limit' => $limit]);
1627
1628 if ($result === false) {
1629 wp_send_json_error(['message' => __('Unknown cleanup task.', 'searchpro')]);
1630 }
1631
1632 wp_send_json_success($result);
1633 }
1634
1635 // Register a "once monthly" WP-Cron interval (core only ships hourly/twicedaily/daily)
1636 function add_monthly_cron_schedule($schedules)
1637 {
1638 $schedules['berqwp_monthly'] = [
1639 'interval' => 30 * DAY_IN_SECONDS,
1640 'display' => __('Once Monthly', 'searchpro'),
1641 ];
1642 return $schedules;
1643 }
1644
1645 // (Re)schedule the Database tab's recurring optimization cron event based on current settings
1646 function reschedule_db_cron()
1647 {
1648 wp_clear_scheduled_hook('berqwp_db_scheduled_optimize');
1649
1650 if (!get_option('berqwp_db_schedule_enabled')) {
1651 return;
1652 }
1653
1654 $frequency = get_option('berqwp_db_schedule_frequency', 'weekly');
1655 $recurrence = 'weekly';
1656 if ($frequency === 'daily') {
1657 $recurrence = 'daily';
1658 } elseif ($frequency === 'monthly') {
1659 $recurrence = 'berqwp_monthly';
1660 }
1661
1662 if (!wp_next_scheduled('berqwp_db_scheduled_optimize')) {
1663 wp_schedule_event(time(), $recurrence, 'berqwp_db_scheduled_optimize');
1664 }
1665 }
1666
1667 // Clear the Database tab's cron event (called on plugin deactivation)
1668 function clear_db_cron()
1669 {
1670 wp_clear_scheduled_hook('berqwp_db_scheduled_optimize');
1671 }
1672
1673 // WP-Cron callback: run every scheduled task and record the run timestamp
1674 function run_scheduled_db_optimize()
1675 {
1676 $tasks = get_option('berqwp_db_scheduled_tasks', []);
1677 $limit = (int) get_option('berqwp_db_revision_limit', 5);
1678
1679 foreach ((array) $tasks as $task) {
1680 berqDatabase::run_task($task, ['limit' => $limit]);
1681 }
1682
1683 update_option('berqwp_db_optimize_last_run', time());
1684 }
1685
1686 // function clear_cache(WP_REST_Request $request)
1687 // {
1688 // require_once optifer_PATH . 'api/clear_cache.php';
1689 // }
1690
1691 // function warmup_cache(WP_REST_Request $request)
1692 // {
1693 // require_once optifer_PATH . 'api/warmup_cache.php';
1694 // }
1695
1696 // function store_cache(WP_REST_Request $request)
1697 // {
1698 // require_once optifer_PATH . 'api/store_cache.php';
1699 // }
1700
1701 // function store_javascript_cache(WP_REST_Request $request)
1702 // {
1703 // require_once optifer_PATH . 'api/store_javascript_cache.php';
1704 // }
1705
1706 // ==========================================
1707 // Multisite Network Support
1708 // ==========================================
1709
1710 function register_network_menu()
1711 {
1712 $svg = '<svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
1713 <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"/>
1714 </svg>';
1715
1716 $plugin_name = defined('BERQWP_PLUGIN_NAME') ? BERQWP_PLUGIN_NAME : 'BerqWP';
1717
1718 add_menu_page(
1719 $plugin_name . ' Network',
1720 $plugin_name,
1721 'manage_network_options',
1722 'berqwp-network',
1723 [$this, 'network_admin_page'],
1724 'data:image/svg+xml;base64,' . base64_encode($svg),
1725 80
1726 );
1727 }
1728
1729 function network_admin_page()
1730 {
1731 require_once optifer_PATH . 'admin/network-admin-page.php';
1732 }
1733
1734 function save_network_settings()
1735 {
1736 check_admin_referer('berqwp_network_save');
1737
1738 if (!current_user_can('manage_network_options')) {
1739 wp_die('Unauthorized');
1740 }
1741
1742 // License activation
1743 if (!empty($_POST['berqwp_license_key'])) {
1744 $key = sanitize_text_field($_POST['berqwp_license_key']);
1745 // $key_response = $this->verify_license_key($key, 'slm_activate');
1746 $key_response = $this->authenticate_license($key);
1747 if (!empty($key_response->lic) && isset($key_response->lic->result) && $key_response->lic->result == 'success') {
1748 berqwp_update_license_key($key);
1749 }
1750 }
1751
1752 // License deactivation
1753 if (!empty($_POST['berq_deactivate_key'])) {
1754 $existing_key = berqwp_get_license_key();
1755 if (!empty($existing_key)) {
1756 $this->verify_license_key($existing_key, 'slm_deactivate');
1757 }
1758 berqwp_delete_license_key();
1759 berqwp_delete_network_option('berq_lic_response_cache');
1760 berqwp_delete_network_option('berq_lic_cache_expire');
1761 berqwp_delete_network_option('berqwp_lic_response_cache');
1762 berqwp_delete_network_option('berqwp_lic_cache_expire');
1763 }
1764
1765 // Flush all sites cache
1766 if (!empty($_POST['berqwp_flush_all_cache'])) {
1767 $cache_directory = WP_CONTENT_DIR . '/cache/berqwp/html/';
1768 if (is_dir($cache_directory)) {
1769 berqwp_unlink_recursive($cache_directory);
1770 }
1771 }
1772
1773 wp_safe_redirect(network_admin_url('admin.php?page=berqwp-network&updated=true'));
1774 exit;
1775 }
1776
1777 function regenerate_blog_map()
1778 {
1779 if (!function_exists('is_multisite') || !is_multisite()) {
1780 return;
1781 }
1782
1783 $sites = get_sites(['fields' => 'ids', 'number' => 0]);
1784 $map = [
1785 'subdomains' => [],
1786 'subdirs' => [],
1787 ];
1788
1789 $is_subdomain = defined('SUBDOMAIN_INSTALL') && SUBDOMAIN_INSTALL;
1790
1791 foreach ($sites as $site_id) {
1792 $site = get_site($site_id);
1793 if (!$site) continue;
1794
1795 if ($is_subdomain) {
1796 $map['subdomains'][strtolower($site->domain)] = (int) $site_id;
1797 } else {
1798 $path = rtrim($site->path, '/') . '/';
1799 if ($path !== '/') {
1800 $map['subdirs'][$path] = (int) $site_id;
1801 }
1802 }
1803 }
1804
1805 $map_file = WP_CONTENT_DIR . '/cache/berqwp/blog-map.json';
1806 $map_dir = dirname($map_file);
1807 if (!is_dir($map_dir)) {
1808 wp_mkdir_p($map_dir);
1809 }
1810 file_put_contents($map_file, json_encode($map, JSON_PRETTY_PRINT));
1811 }
1812
1813 function on_new_site_created($new_site)
1814 {
1815 if (function_exists('is_plugin_active_for_network') && is_plugin_active_for_network('searchpro/berqwp.php')) {
1816 switch_to_blog($new_site->blog_id);
1817 berqwp_activate_single_site();
1818 restore_current_blog();
1819 }
1820 $this->regenerate_blog_map();
1821 }
1822
1823 function register_menu()
1824 {
1825 $svg = '<svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
1826 <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"/>
1827 </svg>';
1828
1829 $plugin_name = defined('BERQWP_PLUGIN_NAME') ? BERQWP_PLUGIN_NAME : 'BerqWP';
1830
1831 add_menu_page($plugin_name, $plugin_name, 'manage_options', 'berqwp', [$this, 'admin_page'], 'data:image/svg+xml;base64,' . base64_encode($svg), 80);
1832 }
1833
1834 function admin_page()
1835 {
1836
1837 $berqconfigs = berqConfigs::getInstance();
1838 $configs = $berqconfigs->get_configs();
1839
1840 if (!empty($configs['optimization_method'])) {
1841 require_once optifer_PATH . 'admin/admin-page.php';
1842 } else {
1843 require_once optifer_PATH . 'admin/intro-page.php';
1844 }
1845 }
1846 }
1847
1848 berqWP::getInstance();
1849 // global $berqWP;
1850 // $berqWP = new berqWP();
1851 }
1852