PluginProbe
BerqWP – All-In-One Optimization for Core Web Vitals, Cache, CDN, Images, CSS & JavaScript / 4.1.13
BerqWP – All-In-One Optimization for Core Web Vitals, Cache, CDN, Images, CSS & JavaScript v4.1.13
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.13, at inc/class-berqwp.php

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