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.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
← All changes | inc/class-berqwp.php +1491 -197 1.9.5trunk View file →
@@ -1,8 +1,11 @@
1 1 <?php
2 2 if (!defined('ABSPATH'))
3 3 exit;
4 4
5 +use BerqWP\BerqWP as BerqWPCloud;
6 +use BerqWP\RateLimiter;
7 +
5 8 if (!class_exists('berqWP')) {
6 9 class berqWP
7 10 {
8 11
@@ -7,8 +10,9 @@
7 10 {
8 11
9 12 public $is_key_verified = false;
10 13 public $key_response = false;
14 + public static $instance = null;
11 15
12 16 public $conflicting_plugins = [
13 17 'autoptimize/autoptimize.php', // Autoptimize
14 18 'wp-super-cache/wp-cache.php', // WP Super Cache
@@ -25,8 +29,9 @@
25 29 'simple-cache/simple-cache.php', // Simple Cache
26 30 'wp-optimize/wp-optimize.php', // WP-Optimize
27 31 'swift-performance-lite/performance.php', // Swift Performance Lite
28 32 'nitropack/nitropack.php', // NitroPack
33 + 'nitropack/main.php', // NitroPack
29 34 'jetpack-boost/jetpack-boost.php', // Jetpack Boost
30 35 'tenweb-speed-optimizer/tenweb_speed_optimizer.php', // 10Web Booster
31 36 'speed-booster-pack/speed-booster-pack.php', // Speed booster pack
32 37 'wp-speed-of-light/wp-speed-of-light.php', // WP speed of light
@@ -43,9 +48,9 @@
43 48 'wpspeed/wpspeed.php',
44 49 'debloat/debloat.php',
45 50 'perfmatters/perfmatters.php',
46 51 'phastpress/phastpress.php',
47 - // 'hide-my-wp/index.php',
52 + 'wp-meteor/wp-meteor.php',
48 53 ];
49 54
50 55 function __construct()
51 56 {
@@ -54,34 +59,666 @@
54 59
55 60 // Save settings
56 61 add_action('admin_init', [$this, 'save_settings']);
57 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 +
58 70 require_once optifer_PATH . '/api/register_apis.php';
59 71
60 72 add_action('admin_menu', [$this, 'register_menu']);
61 73 // add_action('init', [$this, 'berq_post_types'], 20);
62 - add_action('admin_notices', [$this, 'notices']);
74 + add_action('berqwp_notices', [$this, 'notices']);
63 75
64 76 add_filter('plugin_action_links_searchpro/berqwp.php', [$this, 'plugin_settings_links']);
65 77
66 - add_action('wp_ajax_berqwp_fetch_remote_html', [$this, 'fetch_remote_html']);
78 + // add_action('wp_ajax_berqwp_fetch_remote_html', [$this, 'fetch_remote_html']);
67 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 +
68 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 + }
69 161 }
70 162
71 - function ashp_increase_concurrent_batches( $concurrent_batches ) {
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 + {
72 667 return $concurrent_batches * 2;
73 668 }
74 669
75 - function fetch_remote_html() {
670 + function refresh_cache_stats()
671 + {
76 672
77 673 check_ajax_referer('wp_rest', 'nonce');
78 -
79 - $url = get_option('berqwp_enable_sandbox') ? home_url().'/?berqwp' : home_url();
80 -
81 - // $response = wp_remote_get($url);
82 - $response = bwp_wp_remote_get($url);
83 -
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 +
84 721 if (is_array($response) && !is_wp_error($response)) {
85 722 $html = wp_remote_retrieve_body($response);
86 723 echo $html;
87 724 } else {
@@ -86,9 +723,9 @@
86 723 echo $html;
87 724 } else {
88 725 echo 'Error fetching HTML.';
89 726 }
90 -
727 +
91 728 die(); // Always exit in AJAX functions
92 729 }
93 730
94 731 function activate_license_from_multi_site()
@@ -98,33 +735,32 @@
98 735 }
99 736
100 737 $berqwp_license_key_from_parent = constant('BERQWP_LICENSE_KEY');
101 738
102 - if (!empty($berqwp_license_key_from_parent) && empty(get_option('berqwp_license_key'))) {
739 + if (!empty($berqwp_license_key_from_parent) && empty(berqwp_get_license_key())) {
103 740 $key = sanitize_text_field($berqwp_license_key_from_parent);
104 741 $key_response = $this->verify_license_key($key, 'slm_activate');
105 742
106 743
107 744 if (!empty($key_response) && $key_response->result == 'success') {
108 - update_option('berqwp_license_key', $key);
745 + berqwp_update_license_key($key);
109 746
110 747 if (is_admin()) {
111 - ?>
748 +?>
112 749 <div class="notice notice-success is-dismissible">
113 750 <?php esc_html_e('The BerqWP license has been activated for your parent multisite.', 'searchpro'); ?>
114 751 </div>
115 - <?php
752 + <?php
116 753 }
117 -
118 754 } elseif ($key_response->result == 'error') {
119 755 $error = $key_response->message;
120 756
121 757 if (is_admin()) {
122 - ?>
758 + ?>
123 759 <div class="notice notice-error is-dismissible">
124 760 <?php echo esc_html($error); ?>
125 761 </div>
126 - <?php
762 + <?php
127 763
128 764 }
129 765 }
130 766 }
@@ -129,40 +765,177 @@
129 765 }
130 766 }
131 767 }
132 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 +
133 866 function initialize()
134 867 {
135 868
869 + if (defined('DOING_CRON') && DOING_CRON) {
870 + return;
871 + }
872 +
873 + if (defined('DOING_AJAX') && DOING_AJAX) {
874 + return;
875 + }
876 +
136 877 // Set default settings
137 878 require_once optifer_PATH . '/inc/initialize.php';
138 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 +
139 891 // Activate the license from parent site
140 - if (defined('BERQWP_LICENSE_KEY')) {
141 - $this->activate_license_from_multi_site();
142 - }
892 + // if (defined('BERQWP_LICENSE_KEY')) {
893 + // $this->activate_license_from_multi_site();
894 + // }
143 895
144 896 if (is_admin()) {
145 897 $this->berq_post_types();
146 898 }
147 899
148 - if (get_option('berqwp_disable_emojis') == 1) {
149 - $this->disable_emoji();
150 - }
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 + // }
151 905
152 - if (is_admin() && !empty(get_option('berqwp_license_key'))) {
153 - $license_key = get_option('berqwp_license_key');
154 - $key_response = $this->verify_license_key($license_key);
906 + // if (is_admin() && !empty(berqwp_get_license_key())) {
907 + // $license_key = berqwp_get_license_key();
155 908
156 - if (!empty($key_response) && $key_response->result == 'success' && $key_response->status == 'active') {
157 - $this->is_key_verified = true;
158 - $this->key_response = $key_response;
909 + // global $berq_log;
910 + // // $berq_log->info("License key check from initialize function.");
159 911
160 - } else {
161 - $this->is_key_verified = false;
162 - }
163 - }
912 + // $key_response = $this->verify_license_key($license_key);
164 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 +
165 938 // redirect to berqwp admin page
166 939 if (get_transient('berqwp_redirect')) {
167 940 delete_transient('berqwp_redirect');
168 941 // Set the URL to redirect to after activation
@@ -182,121 +955,206 @@
182 955 if (is_plugin_active($plugin)) {
183 956 deactivate_plugins($plugin);
184 957 }
185 958 }
186 - header('location: ' . esc_url(get_site_url() . add_query_arg($_GET)));
959 + header('location: ' . esc_url(admin_url('admin.php') . add_query_arg(['page' => 'berqwp', 'tab_id' => sanitize_text_field(wp_unslash($_GET['tab_id'] ?? ''))])));
187 960 exit;
188 961 }
189 -
190 962 }
191 963
192 964 function save_settings()
193 965 {
966 +
967 + if (current_user_can('manage_options') && !empty($_GET['page']) && $_GET['page'] == 'berqwp' && isset($_GET['activate-free'])) {
968 + $berqconfigs = berqConfigs::getInstance();
969 + $configs = $berqconfigs->get_configs();
970 + $site_id = $configs['site_id'];
971 +
972 + if (empty($site_id)) {
973 + $site_id = berqwp_generate_site_id();
974 + }
975 +
976 + $berqconfigs->update_configs([
977 + 'site_id' => $site_id,
978 + 'optimization_method' => 'local'
979 + ]);
980 +
981 + wp_remote_post(BerqWPCloud::$endpoint."free-user", [
982 + 'timeout' => 30,
983 + 'body' => [
984 + 'site_id' => $site_id,
985 + 'site_url' => get_option('home'),
986 + ]
987 + ]);
988 +
989 + $location = get_admin_url() . 'admin.php?page=berqwp';
990 + wp_safe_redirect($location);
991 + exit;
992 + }
993 +
194 994 require_once optifer_PATH . '/admin/save-settings.php';
195 995 }
196 996
197 - function berqwp_cleanup_completed_and_failed_tasks()
997 + function notices()
198 998 {
199 999
200 - if (!get_transient('berqwp_action_cleanup')) {
201 - set_transient('berqwp_action_cleanup', true, 60 * 60 * 10);
1000 + $plugin_name = defined('BERQWP_PLUGIN_NAME') ? BERQWP_PLUGIN_NAME : 'BerqWP';
202 1001
203 - global $berq_log;
204 - $berq_log->info("* * * * * * * * *");
205 - $berq_log->info("* Task Cleanup");
206 - $berq_log->info("* * * * * * * * *");
1002 + $this->upgrade_notice();
207 1003
208 - global $wpdb;
1004 + if (isset($_GET['berqwp_page_compression_enabled'])) {
1005 + bwp_notice('success', 'Page Compression Enabled', "<p>Page compression has been successfully enabled on your website.</p>");
1006 + }
209 1007
210 - // Define the hook name
211 - $hook_name = 'warmup_cache_by_slug';
1008 + if (isset($_GET['dismiss_feedback'])) {
1009 + set_transient('bwp_hide_feedback_notice', true, DAY_IN_SECONDS * 14);
1010 + }
212 1011
213 - // Count the number of completed and failed actions for the given hook
214 - $count = $wpdb->get_var(
215 - $wpdb->prepare(
216 - "SELECT COUNT(*) FROM {$wpdb->prefix}actionscheduler_actions WHERE hook = %s AND (status = 'complete' OR status = 'failed')",
217 - $hook_name
218 - )
219 - );
1012 + if (isset($_GET['bwp_quit_feedback'])) {
1013 + update_option('bwp_quit_feedback', true);
1014 + }
220 1015
221 - $berq_log->info("Completed + failed task count: $count");
222 -
223 - // Check if the count exceeds 50
224 - if ($count > 50) {
225 - $delete_count = $count - 50; // Calculate how many rows to delete
226 1016
227 - $berq_log->info("Deleting last $delete_count");
1017 + if (!empty($this->key_response) && $this->key_response->result == 'success' && $this->key_response->status == 'expired') {
1018 + ?>
1019 + <div class="notice notice-error">
1020 + <p><strong>Error:</strong> <?php echo $plugin_name; ?> license key has expired. Please renew your subscription.</p>
1021 + </div>
1022 + <?php
1023 + }
228 1024
229 - $wpdb->query(
230 - $wpdb->prepare(
231 - "DELETE FROM {$wpdb->prefix}actionscheduler_actions WHERE hook = %s AND (status = 'complete' OR status = 'failed') ORDER BY scheduled_date_gmt ASC LIMIT %d",
232 - $hook_name,
233 - $delete_count
234 - )
235 - );
236 - }
1025 + // Check connection
1026 + // $check_rest = bwp_check_connection(false, !empty($_GET['bwp_connection_test']) === true);
1027 + // if ($check_rest['status'] == 'error') {
1028 + // bwp_notice('error', 'Website Unreachable: Connection Blocked', "<p>$plugin_name server is unable to access this website, please whitelist our server IP address. <a href='https://berqwp.com/help-center/get-started-with-berqwp/' target='_blank'>Find our server IP address here.</a></p>", [
1029 + // [
1030 + // 'href' => esc_attr(add_query_arg(['bwp_connection_test' => true], get_admin_url())),
1031 + // 'text' => 'Check again',
1032 + // 'classes' => '',
1033 + // ]
1034 + // ]);
1035 + // }
237 1036
238 - // clear logs, keep the last 1000
239 - if ( class_exists( 'ActionScheduler_QueueRunner' ) ) {
240 - global $wpdb;
241 - $table_name = $wpdb->prefix . 'actionscheduler_logs';
242 -
243 - // Count the total number of logs
244 - $total_logs = $wpdb->get_var( "SELECT COUNT(*) FROM $table_name" );
245 -
246 - // Set the limit to keep the latest 1000 logs
247 - $limit = max( 0, $total_logs - 1000 );
248 -
249 - // Delete logs beyond the limit
250 - $wpdb->query( "DELETE FROM $table_name WHERE log_date_gmt < (SELECT log_date_gmt FROM $table_name ORDER BY log_date_gmt DESC LIMIT 1 OFFSET $limit)" );
251 - }
1037 + // Check Permissions
1038 + $cache_directory = WP_CONTENT_DIR . '/cache/berqwp/';
1039 + $wp_config_file = defined('BERQWP_WP_CONFIG') ? BERQWP_WP_CONFIG : ABSPATH . 'wp-config.php';
252 1040
253 - $berq_log->info("* * * * * * * * * *");
254 - $berq_log->info("* Task Cleanup End");
255 - $berq_log->info("* * * * * * * * * *");
1041 + if (!is_writable($cache_directory)) {
1042 + bwp_notice('error', 'Cache directory is not writable', "<p>The $plugin_name cache directory at /wp-content/cache/berqwp/ is not writable. Please update the directory permissions to allow the plugin to store cached files.</p>", []);
1043 + }
256 1044
1045 + if (!defined('WP_CACHE') && !is_writable($wp_config_file)) {
1046 + bwp_notice('warning', 'wp-config.php is not writable', "<p>The wp-config.php file is not writable. $plugin_name needs to write configuration settings to this file. Please adjust the file permissions or manually add the WP_CACHE constant and set it to true.</p>", []);
257 1047 }
258 - }
259 1048
260 - function notices()
261 - {
1049 + if (defined('BERQWP_ADVANCED_CACHE_PATH')) {
1050 + $adv_cache_path = BERQWP_ADVANCED_CACHE_PATH;
1051 + } else {
1052 + $adv_cache_path = WP_CONTENT_DIR . '/advanced-cache.php';
1053 + }
262 1054
263 - if (isset($_GET['dismiss_feedback'])) {
264 - set_transient( 'bqwp_hide_feedback_notice', true, DAY_IN_SECONDS * 2 );
1055 + // if (file_exists($adv_cache_path) && !is_writable($adv_cache_path)) {
1056 + // bwp_notice('warning', 'advanced-cache.php is not writable', "<p>$plugin_name can't write to wp-content/advanced-cache.php — please check file permissions or re-save settings to regenerate it.</p>", []);
1057 + // }
1058 +
1059 + $berqconfigs = berqConfigs::getInstance();
1060 + $configs = $berqconfigs->get_configs();
1061 + $readyForCache = !empty($configs['optimization_method']);
1062 +
1063 + if ($readyForCache && bwp_is_home_cached() && !get_transient('bwp_hide_feedback_notice') && !get_option('bwp_quit_feedback') && bwp_show_account()) {
1064 + bwp_notice('info bwp_feedback', 'Loving BerqWP\'s performance?', '<p>Show some love and help us grow 👉 - <a href="https://wordpress.org/support/plugin/searchpro/reviews/#new-post" target="_blank">Rate BerqWP Plugin</a>. Your insights shape our journey.</p>', [
1065 + [
1066 + 'href' => 'https://wordpress.org/support/plugin/searchpro/reviews/#new-post',
1067 + 'text' => '❤️ You deserve it',
1068 + 'classes' => '',
1069 + 'target' => '_blank',
1070 + ],
1071 + [
1072 + 'href' => get_admin_url() . 'admin.php?page=berqwp&bwp_quit_feedback',
1073 + 'text' => '👍 Already done',
1074 + 'classes' => '',
1075 + 'target' => '',
1076 + ],
1077 + [
1078 + 'href' => get_admin_url() . 'admin.php?page=berqwp&dismiss_feedback',
1079 + 'text' => 'Not Now',
1080 + 'classes' => '',
1081 + 'target' => '',
1082 + ]
1083 + ]);
1084 +
1085 + // $notice = '<div class="bwp-notice bwp_feedback">';
1086 + // $notice .= '<p>';
1087 + // $notice .= __('🎉 <b>Loving BerqWP\'s performance? 🚀</b> Show some love and help us grow 👉 - <a href="https://wordpress.org/support/plugin/searchpro/reviews/#new-post" target="_blank">Rate BerqWP Plugin</a>. Your insights shape our journey.', 'searchpro');
1088 + // $notice .= '<a href="'.get_admin_url().'admin.php?page=berqwp&dismiss_feedback" style="display: table;margin-left: 50px;color: #969595;display: table;">Dismiss</a>';
1089 + // $notice .= '</p>';
1090 + // $notice .= '</div>';
1091 + // echo wp_kses_post($notice);
265 1092 }
266 1093
267 - // Check rest api
268 - $check_rest = bwp_check_rest_api();
269 - if ( $check_rest['status'] == 'error' ) {
270 - ?>
271 - <div class="notice notice-error">
272 - <p><strong>Error:</strong> The WordPress REST API is disabled. BerqWP plugin will not function correctly without the
273 - REST API. Please enable the REST API for full functionality.</p>
274 - </div>
275 - <?php
1094 + // purge single page notice
1095 + if (get_transient('berq_purge_page_notice')) {
1096 + $flushed_page = esc_url(get_transient('berq_purge_page_notice'));
1097 + delete_transient('berq_purge_page_notice');
1098 + bwp_notice('success', '', "<p>Page cache has been flushed for $flushed_page.</p>", []);
276 1099 }
277 1100
278 - if (isset($_GET['page']) && $_GET['page'] == 'berqwp' && !get_transient( 'bqwp_hide_feedback_notice' ) && $this->is_key_verified) {
279 - $notice = '<div class="notice notice-info bwp_feedback">';
280 - $notice .= '<p>';
281 - $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');
282 - $notice .= '<a href="'.get_admin_url().'admin.php?page=berqwp&dismiss_feedback" style="display: table;margin-left: 50px;color: #969595;display: table;">Dismiss</a>';
283 - $notice .= '</p>';
284 - $notice .= '</div>';
285 - echo wp_kses_post($notice);
1101 + // purge all cache notice
1102 + if (get_transient('berq_cache_cleared_notice')) {
1103 + delete_transient('berq_cache_cleared_notice');
1104 + bwp_notice('success', '', '<p>The cache has been cleared. Our automatic cache warm-up system will generate the cache. Alternatively, you can
1105 + visit any page to create its cache immediately.</p>', []);
286 1106 }
287 1107
288 - if (berq_is_localhost()) {
289 - ?>
290 - <div class="notice notice-warning">
291 - <?php
292 - echo wp_kses_post(__("<p><b>Localhost Detected:</b> BerqWP doesn't operate in a localhost environment.</p>", 'searchpro'));
293 - ?>
294 - </div>
295 - <?php
1108 + // purge cdn cache notice
1109 + if (get_transient('berq_purge_cdn_notice')) {
1110 + delete_transient('berq_purge_cdn_notice');
1111 + bwp_notice('success', '', '<p>The CDN and page cache have been flushed.</p>', []);
296 1112 }
297 1113
1114 + // purge critical css cache notice
1115 + if (get_transient('berq_purge_criticalcss_notice')) {
1116 + delete_transient('berq_purge_criticalcss_notice');
1117 + bwp_notice('success', '', '<p>Critical CSS cache has been flushed from the cloud.</p>', []);
1118 + }
298 1119
1120 + // cache warmup notice
1121 + if (get_transient('berq_cache_warmup_notice')) {
1122 + delete_transient('berq_cache_warmup_notice');
1123 + bwp_notice('success', 'Cache Warmup Queued', '<p>The cache warmup has been added to the queue. Your website pages will be optimized in the background and ready soon.</p>', []);
1124 + }
1125 +
1126 + // force page cache notice
1127 + if (get_transient('berq_force_cache_notice')) {
1128 + $forced_page = esc_url(get_transient('berq_force_cache_notice'));
1129 + delete_transient('berq_force_cache_notice');
1130 + bwp_notice('success', '', "<p>The cache for page ($forced_page) has been queued with high priority and will be generated soon.</p>", []);
1131 + }
1132 +
1133 + if ($readyForCache && get_option('bwp_require_flush_cache', false)) {
1134 + $cache_rebuild_btns = [
1135 + [
1136 + 'href' => esc_attr(wp_nonce_url(admin_url('admin-post.php?action=clear_cache'), 'clear_cache_action')),
1137 + 'text' => 'Flush cache',
1138 + 'classes' => '',
1139 + ]
1140 + ];
1141 +
1142 + if (berqwp_can_use_cloud()) {
1143 + $cache_rebuild_btns[] = [
1144 + 'href' => esc_attr(wp_nonce_url(admin_url('admin-post.php?action=warmup_cache'), 'warmup_cache_action')),
1145 + 'text' => 'Warmup cache',
1146 + 'classes' => '',
1147 + ];
1148 + }
1149 +
1150 + bwp_notice('warning', 'Cache Flush Required', '<p>To apply the changes, please flush the cache.</p>', $cache_rebuild_btns);
1151 + }
1152 +
1153 + if (berq_is_localhost() && berqwp_can_use_cloud()) {
1154 + bwp_notice('warning', 'Localhost environment detected:', "<p>$plugin_name requires a live, publicly accessible website to function.</p>", []);
1155 + }
1156 +
299 1157 $plugins_to_deactivate = '';
300 1158
301 1159 foreach ($this->conflicting_plugins as $plugin) {
302 1160 if (is_plugin_active($plugin)) {
@@ -314,12 +1172,12 @@
314 1172 .berqwp-plugin-conflict {
315 1173 display: grid;
316 1174 grid-template-columns: auto min-content;
317 1175 }</style>";
318 - echo '<div class="notice notice-error berqwp-plugin-conflict">';
1176 + echo '<div class="bwp-notice notice notice-error berqwp-plugin-conflict">';
319 1177 echo wp_kses_post(__('<p><strong>BerqWP Plugin Conflict:</strong> The following plugins have a same nature as BerqWP plugin. Having multiple plugins of the same type can cause unexpected results.</p>', 'searchpro'));
320 - ?>
321 - <form action="<?php echo esc_url(get_site_url() . add_query_arg($_GET)); ?>" method="post">
1178 + ?>
1179 + <form action="<?php echo esc_url(admin_url('admin.php') . add_query_arg(['page' => 'berqwp', 'tab_id' => sanitize_text_field(wp_unslash($_GET['tab_id'] ?? ''))])); ?>" method="post">
322 1180
323 1181 <?php
324 1182 $my_nonce = wp_create_nonce('berqwp_plugins_deactivate');
325 1183 echo '<input type="hidden" name="berqwp_plugins_deactivate" value="' . esc_attr($my_nonce) . '" />';
@@ -326,9 +1184,9 @@
326 1184 ?>
327 1185
328 1186 <input type="submit" class="button-secondary alignright" value="Deactivate Conflicting Plugins">
329 1187 </form>
330 - <?php
1188 +<?php
331 1189 echo wp_kses_post("<ul>$plugins_to_deactivate</ul>");
332 1190 echo '</div>';
333 1191 }
334 1192 }
@@ -335,24 +1193,18 @@
335 1193
336 1194 function berq_post_types()
337 1195 {
338 1196 // Get post type names
339 - $post_type_names = get_post_types(array(
340 - 'public' => true,
341 - 'exclude_from_search' => false,
342 - ), 'names');
1197 + $post_type_names = get_post_types(['public' => true, 'exclude_from_search' => false], 'names');
343 1198 unset($post_type_names['attachment']);
344 1199
345 1200 // var_dump($post_type_names);
346 1201
347 1202 // Modify which post types to optimize
348 - $post_type_names = apply_filters( 'berqwp_post_types', $post_type_names );
1203 + $post_type_names = apply_filters('berqwp_post_types', $post_type_names);
349 1204
350 1205 // Save the names in a WordPress option
351 1206 update_option('berqwp_post_type_names', $post_type_names);
352 -
353 - // Cleanup actions
354 - $this->berqwp_cleanup_completed_and_failed_tasks();
355 1207 }
356 1208
357 1209 function plugin_settings_links($links)
358 1210 {
@@ -363,10 +1215,80 @@
363 1215
364 1216 return array_merge($links, $mylinks);
365 1217 }
366 1218
1219 + function authenticate_license($license_key, $check_only = false) {
1220 +
1221 + $berqconfigs = berqConfigs::getInstance();
1222 + $berqwp_configs = $berqconfigs->get_configs();
1223 + $site_id = $berqwp_configs['site_id'];
1224 +
1225 + if (empty($site_id)) {
1226 + $blog_id = get_current_blog_id();
1227 + $network_id = function_exists('get_current_network_id') ? get_current_network_id() : 1;
1228 + $siteurl = get_option('siteurl');
1229 + $site_id = md5("berqwp|$network_id|$blog_id|$siteurl");
1230 +
1231 + $berqconfigs->update_configs(['site_id' => $site_id]);
1232 + }
1233 +
1234 + global $berq_log;
1235 + $transient_key = 'berqwp_lic_response_cache'; // Set a unique key for the transient
1236 + $expire_transient_key = 'berqwp_lic_cache_expire'; // Set a unique key for the transient
1237 +
1238 + if (!$check_only) {
1239 + berqwp_delete_network_option($transient_key);
1240 + }
1241 +
1242 + // Check if the response is already cached
1243 + $lic_json = berqwp_get_network_option($transient_key);
1244 + $cache_expire_time = (int) berqwp_get_network_option($expire_transient_key);
1245 +
1246 + if (false === $lic_json || $cache_expire_time < time()) {
1247 +
1248 + $berq_log->info("Authenticating license key");
1249 +
1250 + $body = [
1251 + 'license_key' => $license_key,
1252 + 'site_id' => $site_id,
1253 + 'site_url' => home_url(),
1254 + ];
1255 +
1256 + if ($check_only) {
1257 + $body['check'] = true;
1258 + }
1259 +
1260 + $lic_response = wp_remote_post(BerqWPCloud::$endpoint."authenticate", [
1261 + 'timeout' => 30,
1262 + 'body' => $body
1263 + ]);
1264 +
1265 + if (is_wp_error($lic_response)) {
1266 + $berq_log->info("Couldn't reach BerqWP server.".print_r($lic_response, true));
1267 + return;
1268 + }
1269 +
1270 + $lic_body = wp_remote_retrieve_body($lic_response);
1271 + $lic_json = json_decode($lic_body);
1272 +
1273 + if (!empty($lic_json)) {
1274 + berqwp_update_network_option($transient_key, $lic_json);
1275 + berqwp_update_network_option($expire_transient_key, time() + DAY_IN_SECONDS);
1276 + }
1277 +
1278 + }
1279 +
1280 +
1281 + return $lic_json;
1282 +
1283 + }
1284 +
367 1285 function verify_license_key($license_key, $action = 'slm_check')
368 1286 {
1287 + if ($action !== 'slm_deactivate') {
1288 + return;
1289 + }
1290 +
369 1291 // Action
370 1292 // slm_activate
371 1293 // slm_deactivate
372 1294 // slm_check
@@ -373,30 +1295,60 @@
373 1295
374 1296 if (empty($license_key)) {
375 1297 return;
376 1298 }
377 -
1299 +
1300 + if (defined('BERQWP_DOING_LICENSE_CHECK')) {
1301 + // sleep(1);
1302 + return;
1303 + }
1304 +
1305 + /**
1306 + * Replaced transients with options
1307 + */
1308 +
378 1309 global $berq_log;
379 1310 $transient_key = 'berq_lic_response_cache'; // Set a unique key for the transient
380 -
1311 + $expire_transient_key = 'berq_lic_cache_expire'; // Set a unique key for the transient
1312 +
381 1313 if ($action !== 'slm_check') {
382 - delete_transient( $transient_key );
1314 + // delete_transient( $transient_key );
1315 + berqwp_delete_network_option($transient_key);
383 1316 }
384 1317
385 1318 // Check if the response is already cached
386 - $cached_response = get_transient($transient_key);
1319 + // $cached_response = get_transient($transient_key);
1320 + $cached_response = berqwp_get_network_option($transient_key);
1321 + $cache_expire_time = (int) berqwp_get_network_option($expire_transient_key);
387 1322
388 1323
389 - if (false === $cached_response) {
1324 + if (false === $cached_response || $cache_expire_time < time()) {
390 1325 // If not cached, perform the API request
391 1326
1327 + $rateLimiter = new RateLimiter(5, 60, optifer_cache . 'ratelimit/');
1328 + $clientIdentifier = gethostname();
1329 + if (function_exists('is_multisite') && is_multisite()) {
1330 + $clientIdentifier .= '-site-' . get_current_blog_id();
1331 + }
1332 +
1333 + if ($rateLimiter->isRateLimited($clientIdentifier)) {
1334 + return false;
1335 + }
1336 +
1337 + define('BERQWP_DOING_LICENSE_CHECK', true);
1338 +
392 1339 $berq_log->info('Checking the license key.');
393 1340
1341 + $parsed_url = parse_url(home_url());
1342 + $domain = $parsed_url['host'];
1343 +
394 1344 $api_params = array(
395 - 'registered_domain' => $_SERVER['SERVER_NAME'],
1345 + 'registered_domain' => $domain,
396 1346 'slm_action' => $action,
397 1347 'secret_key' => BERQ_SECRET,
398 1348 'license_key' => $license_key,
1349 + 'version' => BERQWP_VERSION,
1350 + 't' => '',
399 1351 );
400 1352
401 1353 $endpoint_url = esc_url(add_query_arg($api_params, BERQ_SERVER));
402 1354
@@ -401,9 +1353,9 @@
401 1353 $endpoint_url = esc_url(add_query_arg($api_params, BERQ_SERVER));
402 1354
403 1355 $args = array(
404 1356 'method' => 'POST', // Only POST works for unknown reason
405 - 'timeout' => 10,
1357 + 'timeout' => 20,
406 1358 'redirection' => 5,
407 1359 'blocking' => true,
408 1360 'headers' => array(
409 1361 'User-Agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
@@ -415,62 +1367,151 @@
415 1367 ),
416 1368 'cookies' => array(),
417 1369 'sslverify' => false, // Disable SSL verification (for debugging purposes)
418 1370 );
419 -
420 - $response = wp_remote_request(add_query_arg($api_params, BERQ_SERVER), $args);
421 1371
422 - // Send query to the license manager server
423 - // $response = (array) wp_remote_post($endpoint_url, array('timeout' => 20, 'sslverify' => false));
1372 + // $request = wp_remote_request( $endpoint_url, $args );
424 1373
425 - // if wp_remote_get didn't work
426 - if (is_array($response) && empty($response['body'])) {
427 - // Create context options with headers
428 - $context_options = [
429 - "ssl" => array(
430 - "verify_peer" => false,
431 - "verify_peer_name" => false,
432 - ),
433 - 'http' => [
434 - 'method' => 'GET',
435 - 'header' => [
436 - 'Content-Type: application/json',
437 - ],
438 - ],
439 - ];
440 1374
441 - // Create a stream context
442 - $context = stream_context_create($context_options);
1375 + $berq_log->info('Making request 1');
443 1376
444 - // Make the GET request
445 - $response = json_decode(file_get_contents($endpoint_url, false, $context));
1377 + $query_string = http_build_query($api_params);
1378 + // $client = new HttpClient(BERQ_SERVER);
1379 + // $client->setUserAgent('BerqWP');
1380 + // $client->post('?' . $query_string, $api_params);
1381 + // $client->setTimeout(30);
1382 +
1383 + $client = new \BerqWP_Deps\GuzzleHttp\Client([
1384 + 'timeout' => 60,
1385 + 'http_errors' => false,
1386 + 'verify' => false,
1387 + ]);
1388 +
1389 + $response = $client->post(BERQ_SERVER . '?' . $query_string, [
1390 + 'form_params' => $api_params,
1391 + ]);
1392 + $statusCode = $response->getStatusCode();
1393 + $response = $response->getBody()->getContents();
1394 +
1395 + // var_dump($client->getContent(), $client->getError(), $api_params);
1396 +
1397 + // $berq_log->info(print_r($client->getContent(), true).'---'.$client->ok());
1398 +
1399 + if ($statusCode >= 200 && $statusCode < 300) {
1400 + // $response = $client->getContent();
1401 + $JSON = json_decode($response);
1402 +
1403 + if ($action == 'slm_activate' && isset($JSON->error_code) && $JSON->message !== 'Invalid license key') {
1404 + sleep(1);
1405 + $api_params = array(
1406 + 'registered_domain' => $domain,
1407 + 'slm_action' => 'slm_check',
1408 + 'secret_key' => BERQ_SECRET,
1409 + 'license_key' => $license_key,
1410 + 't' => '',
1411 + );
1412 +
1413 + $berq_log->info('Making request 2');
1414 +
1415 + $query_string = http_build_query($api_params);
1416 + // $client = new HttpClient(BERQ_SERVER);
1417 + // $client->setUserAgent('BerqWP');
1418 + // $client->post('?' . $query_string, $api_params);
1419 +
1420 +
1421 + // if ($client->ok()) {
1422 + // $response = $client->getContent();
1423 + // }
1424 +
1425 + $response = $client->post(BERQ_SERVER . '?' . $query_string, [
1426 + 'form_params' => $api_params,
1427 + ]);
1428 +
1429 + $response = $response->getBody()->getContents();
1430 + }
446 1431 }
447 1432
448 - if (is_array($response)) {
449 - $cached_response = json_decode($response['body']);
1433 + if ($action !== 'slm_check') {
1434 + $api_params = array(
1435 + 'registered_domain' => $domain,
1436 + 'slm_action' => 'slm_check',
1437 + 'secret_key' => BERQ_SECRET,
1438 + 'license_key' => $license_key,
1439 + 'version' => BERQWP_VERSION,
1440 + 't' => '',
1441 + );
450 1442
451 - if ($action == 'slm_check' && !empty($cached_response)) {
1443 + $berq_log->info('Making request 3');
1444 +
1445 + $query_string = http_build_query($api_params);
1446 + // $client = new HttpClient(BERQ_SERVER);
1447 + // $client->setUserAgent('BerqWP');
1448 + // $client->post('?' . $query_string, $api_params);
1449 +
1450 +
1451 + // if ($client->ok()) {
1452 + // $response = $client->getContent();
1453 + // }
1454 +
1455 + $response = $client->post(BERQ_SERVER . '?' . $query_string, [
1456 + 'form_params' => $api_params,
1457 + ]);
1458 +
1459 + $response = $response->getBody()->getContents();
1460 + }
1461 +
1462 + if (empty($response)) {
1463 + return;
1464 + }
1465 +
1466 + $cached_response = json_decode($response);
1467 +
1468 +
1469 + if ($action == 'slm_check' && !empty($cached_response) && !empty($cached_response->result)) {
1470 + $domain_found = false;
1471 +
1472 + foreach ($cached_response->registered_domains as $reg_domain) {
1473 + $domain_name = str_replace('www.', '', $domain);
1474 + $domain_name_www = 'www.' . $domain;
1475 +
1476 + if ($reg_domain->registered_domain == $domain_name || $reg_domain->registered_domain == $domain_name_www || $reg_domain->registered_domain == $domain) {
1477 + $domain_found = true;
1478 + break;
1479 + }
1480 + }
1481 +
1482 + if ($domain_found) {
452 1483 // Cache the response for 24 hours
453 - set_transient($transient_key, $cached_response, 24 * HOUR_IN_SECONDS);
1484 + // set_transient($transient_key, $cached_response, 24 * HOUR_IN_SECONDS);
1485 + berqwp_update_network_option($transient_key, $cached_response);
1486 + berqwp_update_network_option($expire_transient_key, time() + MONTH_IN_SECONDS);
1487 + } else {
1488 +
1489 + berqwp_delete_network_option($transient_key);
1490 + berqwp_delete_network_option($expire_transient_key);
1491 + berqwp_delete_license_key();
1492 +
1493 + return false;
454 1494 }
1495 + }
455 1496
456 - } else {
457 - $cached_response = $response;
1497 + // if ($action == 'slm_check' && !empty($cached_response) && $cached_response->result == 'success' && $cached_response->status == 'active') {
1498 + // // Cache the response for 24 hours
1499 + // set_transient($transient_key, $cached_response, 24 * HOUR_IN_SECONDS);
1500 + // }
458 1501
459 - if ($action == 'slm_check' && !empty($cached_response) && $cached_response->result == 'success' && $cached_response->status == 'active') {
460 - // Cache the response for 24 hours
461 - set_transient($transient_key, $cached_response, 24 * HOUR_IN_SECONDS);
462 - }
1502 + // if ($action == 'slm_check' && !empty($cached_response) && $cached_response->result == 'error') {
1503 + // // Key verification failed, cache the response for 14 hours
1504 + // // preventing unnecessary verification requests
1505 + // set_transient($transient_key, $cached_response, 14 * HOUR_IN_SECONDS);
1506 + // }
463 1507
464 - }
465 1508 } else {
466 - $berq_log->info('Delivering license key object from the transient cache.');
1509 + // $berq_log->info('Delivering license key object from the transient cache.');
467 1510 }
468 1511
469 -
470 1512 // Return the cached response
471 1513 return $cached_response;
472 -
473 1514 }
474 1515
475 1516 // Disable emoji functionality
476 1517 function disable_emoji()
@@ -497,71 +1538,324 @@
497 1538 return array();
498 1539 }
499 1540 }
500 1541
501 - function get_media_ids(WP_REST_Request $request)
1542 + // Apply the selected Heartbeat API mode (default / restrict / throttle / disable)
1543 + function apply_heartbeat_settings()
502 1544 {
503 - require_once optifer_PATH . 'api/get_media_ids.php';
1545 + $mode = get_option('berqwp_heartbeat_mode', 'default');
1546 +
1547 + if ($mode === 'default') {
1548 + return;
1549 + }
1550 +
1551 + if ($mode === 'disable') {
1552 + wp_deregister_script('heartbeat');
1553 + return;
1554 + }
1555 +
1556 + if ($mode === 'restrict') {
1557 + global $pagenow;
1558 + if (!in_array($pagenow, ['post.php', 'post-new.php'], true)) {
1559 + wp_deregister_script('heartbeat');
1560 + }
1561 + return;
1562 + }
1563 +
1564 + if ($mode === 'throttle') {
1565 + add_filter('heartbeat_settings', [$this, 'throttle_heartbeat_interval']);
1566 + }
504 1567 }
505 1568
506 - function optimize_images(WP_REST_Request $request)
1569 + // Filter callback: clamp Heartbeat interval to the configured value
1570 + function throttle_heartbeat_interval($settings)
507 1571 {
508 - require_once optifer_PATH . 'api/optimize_images.php';
1572 + $settings['interval'] = max(15, min(300, (int) get_option('berqwp_heartbeat_interval', 60)));
1573 + return $settings;
509 1574 }
510 1575
1576 + // Apply the remaining Debloat toggles (embeds, XML-RPC, REST head links, head meta, self pingbacks)
1577 + function apply_debloat_settings()
1578 + {
1579 + if (get_option('berqwp_disable_embeds')) {
1580 + add_action('wp_footer', [$this, 'deregister_embed_script']);
1581 + remove_action('wp_head', 'wp_oembed_add_discovery_links');
1582 + remove_action('wp_head', 'wp_oembed_add_host_js');
1583 + add_filter('embed_oembed_discover', '__return_false');
1584 + }
511 1585
512 - function delete_images(WP_REST_Request $request)
1586 + if (get_option('berqwp_disable_xmlrpc')) {
1587 + add_filter('xmlrpc_enabled', '__return_false');
1588 + }
1589 +
1590 + if (get_option('berqwp_remove_rest_head_links')) {
1591 + remove_action('wp_head', 'rest_output_link_wp_head');
1592 + remove_action('template_redirect', 'rest_output_link_header', 11);
1593 + }
1594 +
1595 + if (get_option('berqwp_remove_head_meta')) {
1596 + remove_action('wp_head', 'rsd_link');
1597 + remove_action('wp_head', 'wlwmanifest_link');
1598 + remove_action('wp_head', 'wp_shortlink_wp_head');
1599 + remove_action('wp_head', 'wp_generator');
1600 + }
1601 +
1602 + if (get_option('berqwp_disable_self_pingbacks')) {
1603 + add_action('pre_ping', [$this, 'remove_self_pingbacks']);
1604 + }
1605 + }
1606 +
1607 + // Callback: deregister the embed.min.js frontend script
1608 + function deregister_embed_script()
513 1609 {
514 - require_once optifer_PATH . 'api/delete_images.php';
1610 + wp_deregister_script('wp-embed');
515 1611 }
516 1612
517 - function clear_cache(WP_REST_Request $request)
1613 + // Callback: strip links pointing back to this site from the pingback target list
1614 + function remove_self_pingbacks(&$links)
518 1615 {
519 - require_once optifer_PATH . 'api/clear_cache.php';
1616 + $home = get_option('home');
1617 + foreach ($links as $key => $link) {
1618 + if (strpos($link, (string) $home) === 0) {
1619 + unset($links[$key]);
1620 + }
1621 + }
520 1622 }
521 1623
522 - function warmup_cache(WP_REST_Request $request)
1624 + // AJAX: run a single Database tab cleanup action on demand
1625 + function ajax_db_run_action()
523 1626 {
524 - require_once optifer_PATH . 'api/warmup_cache.php';
1627 + check_ajax_referer('wp_rest', 'nonce');
1628 +
1629 + if (!current_user_can('manage_options')) {
1630 + wp_send_json_error(['message' => __('Permission denied.', 'searchpro')]);
1631 + }
1632 +
1633 + $task = isset($_POST['task']) ? sanitize_text_field($_POST['task']) : '';
1634 + $limit = isset($_POST['limit']) ? max(0, (int) $_POST['limit']) : (int) get_option('berqwp_db_revision_limit', 5);
1635 +
1636 + $result = berqDatabase::run_task($task, ['limit' => $limit]);
1637 +
1638 + if ($result === false) {
1639 + wp_send_json_error(['message' => __('Unknown cleanup task.', 'searchpro')]);
1640 + }
1641 +
1642 + wp_send_json_success($result);
525 1643 }
526 1644
527 - function store_webp(WP_REST_Request $request)
1645 + // Register a "once monthly" WP-Cron interval (core only ships hourly/twicedaily/daily)
1646 + function add_monthly_cron_schedule($schedules)
528 1647 {
529 - require_once optifer_PATH . 'api/store_webp.php';
1648 + $schedules['berqwp_monthly'] = [
1649 + 'interval' => 30 * DAY_IN_SECONDS,
1650 + 'display' => __('Once Monthly', 'searchpro'),
1651 + ];
1652 + return $schedules;
530 1653 }
531 1654
532 - function store_cache(WP_REST_Request $request)
1655 + // (Re)schedule the Database tab's recurring optimization cron event based on current settings
1656 + function reschedule_db_cron()
533 1657 {
534 - require_once optifer_PATH . 'api/store_cache.php';
1658 + wp_clear_scheduled_hook('berqwp_db_scheduled_optimize');
1659 +
1660 + if (!get_option('berqwp_db_schedule_enabled')) {
1661 + return;
1662 + }
1663 +
1664 + $frequency = get_option('berqwp_db_schedule_frequency', 'weekly');
1665 + $recurrence = 'weekly';
1666 + if ($frequency === 'daily') {
1667 + $recurrence = 'daily';
1668 + } elseif ($frequency === 'monthly') {
1669 + $recurrence = 'berqwp_monthly';
1670 + }
1671 +
1672 + if (!wp_next_scheduled('berqwp_db_scheduled_optimize')) {
1673 + wp_schedule_event(time(), $recurrence, 'berqwp_db_scheduled_optimize');
1674 + }
535 1675 }
536 1676
537 - function store_javascript_cache(WP_REST_Request $request)
1677 + // Clear the Database tab's cron event (called on plugin deactivation)
1678 + function clear_db_cron()
538 1679 {
539 - require_once optifer_PATH . 'api/store_javascript_cache.php';
1680 + wp_clear_scheduled_hook('berqwp_db_scheduled_optimize');
540 1681 }
541 1682
1683 + // WP-Cron callback: run every scheduled task and record the run timestamp
1684 + function run_scheduled_db_optimize()
1685 + {
1686 + $tasks = get_option('berqwp_db_scheduled_tasks', []);
1687 + $limit = (int) get_option('berqwp_db_revision_limit', 5);
1688 +
1689 + foreach ((array) $tasks as $task) {
1690 + berqDatabase::run_task($task, ['limit' => $limit]);
1691 + }
1692 +
1693 + update_option('berqwp_db_optimize_last_run', time());
1694 + }
1695 +
1696 + // function clear_cache(WP_REST_Request $request)
1697 + // {
1698 + // require_once optifer_PATH . 'api/clear_cache.php';
1699 + // }
1700 +
1701 + // function warmup_cache(WP_REST_Request $request)
1702 + // {
1703 + // require_once optifer_PATH . 'api/warmup_cache.php';
1704 + // }
1705 +
1706 + // function store_cache(WP_REST_Request $request)
1707 + // {
1708 + // require_once optifer_PATH . 'api/store_cache.php';
1709 + // }
1710 +
1711 + // function store_javascript_cache(WP_REST_Request $request)
1712 + // {
1713 + // require_once optifer_PATH . 'api/store_javascript_cache.php';
1714 + // }
1715 +
1716 + // ==========================================
1717 + // Multisite Network Support
1718 + // ==========================================
1719 +
1720 + function register_network_menu()
1721 + {
1722 + $svg = '<svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
1723 + <path fill-rule="evenodd" clip-rule="evenodd" d="M6.43896 0H17.561C21.1172 0 24 2.88287 24 6.43903V17.561C24 21.1171 21.1172 24 17.561 24H6.43896C2.88281 24 0 21.1171 0 17.561V6.43903C0 2.88287 2.88281 0 6.43896 0ZM15.7888 4.09753L8.59961 12.7534H12.3517L7.02441 20.4878L16.3903 11.0222L12.7814 10.3799L15.7888 4.09753Z" fill="#a7aaad"/>
1724 + </svg>';
1725 +
1726 + $plugin_name = defined('BERQWP_PLUGIN_NAME') ? BERQWP_PLUGIN_NAME : 'BerqWP';
1727 +
1728 + add_menu_page(
1729 + $plugin_name . ' Network',
1730 + $plugin_name,
1731 + 'manage_network_options',
1732 + 'berqwp-network',
1733 + [$this, 'network_admin_page'],
1734 + 'data:image/svg+xml;base64,' . base64_encode($svg),
1735 + 80
1736 + );
1737 + }
1738 +
1739 + function network_admin_page()
1740 + {
1741 + require_once optifer_PATH . 'admin/network-admin-page.php';
1742 + }
1743 +
1744 + function save_network_settings()
1745 + {
1746 + check_admin_referer('berqwp_network_save');
1747 +
1748 + if (!current_user_can('manage_network_options')) {
1749 + wp_die('Unauthorized');
1750 + }
1751 +
1752 + // License activation
1753 + if (!empty($_POST['berqwp_license_key'])) {
1754 + $key = sanitize_text_field($_POST['berqwp_license_key']);
1755 + // $key_response = $this->verify_license_key($key, 'slm_activate');
1756 + $key_response = $this->authenticate_license($key);
1757 + if (!empty($key_response->lic) && isset($key_response->lic->result) && $key_response->lic->result == 'success') {
1758 + berqwp_update_license_key($key);
1759 + }
1760 + }
1761 +
1762 + // License deactivation
1763 + if (!empty($_POST['berq_deactivate_key'])) {
1764 + $existing_key = berqwp_get_license_key();
1765 + if (!empty($existing_key)) {
1766 + $this->verify_license_key($existing_key, 'slm_deactivate');
1767 + }
1768 + berqwp_delete_license_key();
1769 + berqwp_delete_network_option('berq_lic_response_cache');
1770 + berqwp_delete_network_option('berq_lic_cache_expire');
1771 + berqwp_delete_network_option('berqwp_lic_response_cache');
1772 + berqwp_delete_network_option('berqwp_lic_cache_expire');
1773 + }
1774 +
1775 + // Flush all sites cache
1776 + if (!empty($_POST['berqwp_flush_all_cache'])) {
1777 + $cache_directory = WP_CONTENT_DIR . '/cache/berqwp/html/';
1778 + if (is_dir($cache_directory)) {
1779 + berqwp_unlink_recursive($cache_directory);
1780 + }
1781 + }
1782 +
1783 + wp_safe_redirect(network_admin_url('admin.php?page=berqwp-network&updated=true'));
1784 + exit;
1785 + }
1786 +
1787 + function regenerate_blog_map()
1788 + {
1789 + if (!function_exists('is_multisite') || !is_multisite()) {
1790 + return;
1791 + }
1792 +
1793 + $sites = get_sites(['fields' => 'ids', 'number' => 0]);
1794 + $map = [
1795 + 'subdomains' => [],
1796 + 'subdirs' => [],
1797 + ];
1798 +
1799 + $is_subdomain = defined('SUBDOMAIN_INSTALL') && SUBDOMAIN_INSTALL;
1800 +
1801 + foreach ($sites as $site_id) {
1802 + $site = get_site($site_id);
1803 + if (!$site) continue;
1804 +
1805 + if ($is_subdomain) {
1806 + $map['subdomains'][strtolower($site->domain)] = (int) $site_id;
1807 + } else {
1808 + $path = rtrim($site->path, '/') . '/';
1809 + if ($path !== '/') {
1810 + $map['subdirs'][$path] = (int) $site_id;
1811 + }
1812 + }
1813 + }
1814 +
1815 + $map_file = WP_CONTENT_DIR . '/cache/berqwp/blog-map.json';
1816 + $map_dir = dirname($map_file);
1817 + if (!is_dir($map_dir)) {
1818 + wp_mkdir_p($map_dir);
1819 + }
1820 + file_put_contents($map_file, json_encode($map, JSON_PRETTY_PRINT));
1821 + }
1822 +
1823 + function on_new_site_created($new_site)
1824 + {
1825 + if (function_exists('is_plugin_active_for_network') && is_plugin_active_for_network('searchpro/berqwp.php')) {
1826 + switch_to_blog($new_site->blog_id);
1827 + berqwp_activate_single_site();
1828 + restore_current_blog();
1829 + }
1830 + $this->regenerate_blog_map();
1831 + }
1832 +
542 1833 function register_menu()
543 1834 {
544 1835 $svg = '<svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
545 - <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="#1F71FF"/>
1836 + <path fill-rule="evenodd" clip-rule="evenodd" d="M6.43896 0H17.561C21.1172 0 24 2.88287 24 6.43903V17.561C24 21.1171 21.1172 24 17.561 24H6.43896C2.88281 24 0 21.1171 0 17.561V6.43903C0 2.88287 2.88281 0 6.43896 0ZM15.7888 4.09753L8.59961 12.7534H12.3517L7.02441 20.4878L16.3903 11.0222L12.7814 10.3799L15.7888 4.09753Z" fill="#a7aaad"/>
546 1837 </svg>';
547 1838
548 - add_menu_page('BerqWP', 'BerqWP', 'manage_options', 'berqwp', [$this, 'admin_page'], 'data:image/svg+xml;base64,' . base64_encode($svg), 10);
1839 + $plugin_name = defined('BERQWP_PLUGIN_NAME') ? BERQWP_PLUGIN_NAME : 'BerqWP';
1840 +
1841 + add_menu_page($plugin_name, $plugin_name, 'manage_options', 'berqwp', [$this, 'admin_page'], 'data:image/svg+xml;base64,' . base64_encode($svg), 80);
549 1842 }
550 1843
551 1844 function admin_page()
552 1845 {
553 - if ($this->is_key_verified) {
1846 +
1847 + $berqconfigs = berqConfigs::getInstance();
1848 + $configs = $berqconfigs->get_configs();
1849 +
1850 + if (!empty($configs['optimization_method'])) {
554 1851 require_once optifer_PATH . 'admin/admin-page.php';
555 1852 } else {
556 1853 require_once optifer_PATH . 'admin/intro-page.php';
557 -
558 1854 }
559 -
560 1855 }
561 -
562 -
563 1856 }
564 1857
565 - global $berqWP;
566 - $berqWP = new berqWP();
567 -}
1858 + berqWP::getInstance();
1859 + // global $berqWP;
1860 + // $berqWP = new berqWP();
1861 +}