PluginProbe
ezCache / trunk
ezCache vtrunk
2.6.5 2.6.4 2.6.2 2.6.3 2.6.1 2.6.0 2.5.6 2.5.5 2.5.4 2.5.3 2.5.2 2.5.1 2.5 2.2.1 2.2.2 trunk 1.2 1.2.1 1.2.2 1.2.3 1.2.4 1.3 1.3.1 1.3.10 1.3.11 All 49 releases
ezcache / includes / Admin.php

Admin.php in ezCache trunk, at includes/Admin.php

1,020 lines 50.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Upress\EzCache;
4
5 use WP_Admin_Bar;
6 use wpdb;
7
8 class Admin {
9 /** @var Plugin */
10 private $plugin;
11 protected static $svg = '<svg viewBox="0 0 480 480" version="1.1" xmlns="http://www.w3.org/2000/svg" xml:space="preserve" xmlns:serif="http://www.serif.com/" fill-rule="evenodd" clip-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2">
12 <g id="Camada-1" serif:id="Camada 1" fill-rule="nonzero">
13 <path d="M206.088 138.266l-90.584 127.353h115.3l-67.078 160.647 201.096-213.077H254.006l53.292-74.923h-101.21z" fill="#fc0"/>
14 <path d="M205.088 138.266l-90.585 127.352h115.3l-67.078 160.647L285.68 239.266H180.327l75.364-101h-50.603z" fill="url(#_Linear1)"/>
15 </g>
16 <defs>
17 <linearGradient id="_Linear1" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(171.176 0 0 -171.176 114.504 282.266)">
18 <stop offset="0" stop-color="#deb203" stop-opacity="1"/>
19 <stop offset="1" stop-color="#fc0" stop-opacity="1"/>
20 </linearGradient>
21 </defs>
22 </svg> ';
23
24 function __construct( $plugin ) {
25 $this->plugin = $plugin;
26
27 add_filter( 'show_admin_bar', [ $this, 'maybe_show_admin_bar' ], 100 );
28 add_action( 'admin_init', [ $this, 'register_settings' ] );
29 add_action( 'admin_menu', [ $this, 'register_menu' ] );
30 add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_scripts' ] );
31 // Premium UI gates removed — Pro is unlocked for everyone.
32 // inject_branding_css removed — branding is now built into Vue 2.0
33 add_action( 'plugin_action_links_' . plugin_basename( $this->plugin->plugin_file ), [
34 $this,
35 'plugin_action_links',
36 ] );
37 add_action( 'admin_notices', [ $this, 'maybe_show_advanced_cache_notice' ] );
38 // Trial banner removed — licensing system disabled.
39
40 add_action( 'admin_bar_menu', [ $this, 'add_admin_bar_button' ], 999 );
41 add_action( 'admin_post_wpb_clear_cache', [ $this, 'admin_clear_cache' ] );
42 add_action( 'delete_attachment', [ $this, 'delete_webp_image' ] );
43
44 add_action( 'init', [ $this, 'register_post_meta' ] );
45 add_action( 'add_meta_boxes', [ $this, 'post_cache_metabox' ] );
46 add_action( 'save_post', [ $this, 'save_post_cache_metabox' ] );
47 add_action( 'pre_post_update', [ $this, 'maybe_clear_cache_on_post_update' ], 10, 2 );
48 }
49
50 /**
51 * Delete the webp versions when the image is deleted
52 *
53 * @param int $attachment_id
54 */
55 function delete_webp_image( $attachment_id ) {
56 global $wpdb;
57
58 $upload_dir = wp_upload_dir()['basedir'];
59
60 $meta = wp_get_attachment_metadata( $attachment_id );
61 if ( ! $meta || ! isset( $meta['file'] ) ) {
62 return;
63 }
64
65 if ( ! isset( $meta['sizes'] ) ) {
66 $meta['sizes'] = [];
67 }
68
69 $meta['sizes'][] = [ 'file' => $meta['file'] ];
70
71 $hashes = [];
72 foreach ( $meta['sizes'] as $size ) {
73 $image_path = trailingslashit( $upload_dir ) . $meta['file'];
74 $ext = pathinfo( $image_path, PATHINFO_EXTENSION );
75 $image_webp_path = preg_replace( '/^(.+)\.' . preg_quote( $ext, '/' ) . '$/u', '$1.webp', $image_path );
76
77 $hashes[] = sha1( $image_path );
78
79 if ( file_exists( $image_webp_path ) ) {
80 unlink( $image_webp_path );
81 }
82 }
83
84 $wpdb->query(
85 $wpdb->prepare(
86 "DELETE FROM `{$wpdb->prefix}ezcache_webp_images` WHERE `uid` IN (" . substr( str_repeat( "%d, ", count( $hashes ) ), 0, - 2 ) . ")",
87 $hashes
88 )
89 );
90
91 $wpdb->query( "OPTIMIZE TABLE `{$wpdb->prefix}ezcache_webp_images`" );
92 }
93
94 /**
95 * Register the post meta fields
96 */
97 function register_post_meta() {
98 if ( function_exists( 'register_post_meta' ) ) {
99 register_post_meta( 'post', '_ezcache_do_not_cache_post', [
100 'show_in_rest' => true,
101 'single' => true,
102 'type' => 'bool',
103 'auth_callback' => function () {
104 return current_user_can( 'edit_posts' );
105 },
106 ] );
107 }
108 }
109
110 /**
111 * Register the meta box
112 */
113 function post_cache_metabox() {
114 global $post_type;
115
116 if ( 'attachement' == $post_type ) {
117 return;
118 }
119
120 add_meta_box(
121 'ezcache_metabox',
122 __( 'Caching', 'ezcache' ),
123 [ $this, 'post_cache_metabox_output' ],
124 null,
125 'side'
126 );
127 }
128
129 /**
130 * Render the post meta box
131 *
132 * @param $post
133 */
134 function post_cache_metabox_output( $post ) {
135 global $post_type_object;
136
137 $value = get_post_meta( $post->ID, '_ezcache_do_not_cache_post', true );
138 wp_nonce_field( 'ezcache_metabox', '_eznonce' );
139 ?>
140 <div style="padding:8px 0">
141 <div style="display:flex;align-items:center">
142 <input type="checkbox"
143 id="ezcache_no_cache"
144 name="ezcache_no_cache"
145 class="checkbox" style="width:16px;height:16px;margin-right:8px;vertical-align:middle"
146 <?php checked( $value ); ?>
147 >
148 <label style="vertical-align:middle;cursor:pointer" for="ezcache_no_cache">
149 <?php echo 'page' == $post_type_object->capability_type ? esc_html__( 'Do not cache this page', 'ezcache' ) : esc_html__( 'Do not cache this post', 'ezcache' ); ?>
150 </label>
151 </div>
152 </div>
153 <?php
154 }
155
156 /**
157 * Save the meta box settings
158 *
159 * @param $post_id
160 */
161 function save_post_cache_metabox( $post_id ) {
162 if ( ! isset( $_POST['_eznonce'] ) || ! wp_verify_nonce( $_POST['_eznonce'], 'ezcache_metabox' ) ) {
163 return;
164 }
165
166 if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
167 return;
168 }
169
170 if ( ! current_user_can( 'edit_post', $post_id ) ) {
171 return;
172 }
173
174 if ( isset( $_POST['ezcache_no_cache'] ) ) {
175 update_post_meta( $post_id, '_ezcache_do_not_cache_post', true );
176 Cache::instance()->clear_cache_single( $post_id );
177 } else {
178 delete_post_meta( $post_id, '_ezcache_do_not_cache_post' );
179 }
180 }
181
182 /**
183 * Runs when a post is created, updated, or a comment is left on it
184 *
185 * @param int $post_id Post ID
186 * @param array $data Array of unslashed post data
187 */
188 function maybe_clear_cache_on_post_update( $post_id, $data ) {
189 $settings = Settings::get_settings();
190
191 if ( $settings->cache_clear_on_post_edit ) {
192 $this->plugin->ezcache->clear_cache_single( $post_id );
193
194 return;
195 }
196
197 if ( $settings->cache_clear_home_on_post_edit ) {
198 $this->plugin->ezcache->clear_cache_url( '/' );
199
200 $blog_page_url = $this->plugin->ezcache->get_relative_url( get_post_type_archive_link( 'post' ) );
201 $this->plugin->ezcache->clear_cache_url( $blog_page_url );
202 }
203 }
204
205 /**
206 * Add settings link to the plugin action links
207 *
208 * @param array $links
209 *
210 * @return array
211 */
212 function plugin_action_links( $links ) {
213 $links[] = sprintf(
214 '<a href="%s">%s</a>',
215 admin_url( 'admin.php?page=ezcache' ),
216 __( 'Settings' )
217 );
218
219 return $links;
220 }
221
222 /**
223 * Add the menu to the admin bar
224 *
225 * @param WP_Admin_Bar $wp_admin_bar
226 */
227 function add_admin_bar_button( $wp_admin_bar ) {
228 if ( ! current_user_can( 'manage_options' ) ) {
229 return;
230 }
231
232 $referer = rawurlencode( wp_unslash( remove_query_arg( 'fl_builder', $_SERVER['REQUEST_URI'] ) ) );
233 $svg = preg_replace( '/<svg(\s+?)(?:(?:style=[\'"]([\s\S]+?);?[\'"]([\s\S]*?))|([\s\S]*?))>/i', '<svg class="ab-icon" style="height:1em;width:auto;color:#a0a5aa;color:rgba(240,245,250,.6);$2" $3$4>', self::$svg );
234
235 $wp_admin_bar->add_menu( [
236 'id' => 'ezcache',
237 'title' => $svg . __( 'ezCache', 'ezcache' ),
238 'href' => admin_url( 'admin.php?page=ezcache' ),
239 ] );
240
241 $wp_admin_bar->add_menu( [
242 'id' => 'ezcache-settings',
243 'parent' => 'ezcache',
244 'title' => __( 'Settings', 'ezcache' ),
245 'href' => admin_url( 'admin.php?page=ezcache' ),
246 ] );
247
248 $wp_admin_bar->add_menu( [
249 'id' => 'ezcache-clear-cache',
250 'parent' => 'ezcache',
251 'title' => __( 'Clear Cache', 'ezcache' ),
252 'href' => wp_nonce_url( admin_url( 'admin-post.php?action=wpb_clear_cache&_wp_http_referer=' . $referer ), 'wpb-clear-cache' ),
253 ] );
254 }
255
256 function register_settings() {
257 register_setting( $this->plugin->plugin_settings_key . '_group', $this->plugin->plugin_settings_key );
258 }
259
260 /**
261 * Add the menu under the 'Settings' sidebar item
262 */
263 function register_menu() {
264 global $submenu;
265
266 add_menu_page(
267 __( 'ezCache' ),
268 __( 'ezCache' ),
269 'manage_options',
270 'ezcache',
271 [ $this, 'settings_page' ],
272 'data:image/svg+xml;base64,' . base64_encode( self::$svg )
273 );
274
275 $urls = [
276 __( 'Statistics', 'ezcache' ) => '/',
277 __( 'Settings', 'ezcache' ) => '/settings',
278 __( 'Advanced Settings', 'ezcache' ) => '/advanced',
279 __( 'License', 'ezcache' ) => '/license',
280 ];
281
282 if ( ! isset( $submenu['ezcache'] ) ) {
283 $submenu['ezcache'] = [];
284 }
285
286 foreach ( $urls as $title => $url ) {
287 $submenu['ezcache'][] = [ $title, 'manage_options', admin_url( 'admin.php?page=ezcache' ) . '#' . $url ];
288 }
289 }
290
291 /**
292 * Render the settings page
293 */
294 function settings_page() {
295 $trans = $this->getJsTraslations();
296 ?>
297 <div class="wrap"><h1 aria-hidden="true"><!-- WordPress notices trap --></h1></div>
298 <div id="ezcache-options" class="wrap ezcache-options">
299 <!--suppress HtmlUnknownTag -->
300 <ezc-options>
301 <div class="ezcache-options--preload" aria-hidden="true" style="display:none">
302 <header class="ezcache-header">
303 <h1>
304 <svg viewBox="0 0 360 68" version="1.1" xmlns="http://www.w3.org/2000/svg"
305 xml:space="preserve" fill-rule="evenodd"
306 clip-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" class="logo">
307 <g id="Camada-1" fill-rule="nonzero">
308 <path d="M51.154 53.752H13.691V37.239h22.056l9.144-12.855h-31.2V13.378H52.72L62.235 0H0v67.338h45.91l5.244-13.586z"
309 fill="#1e1e1e"></path>
310 <path d="M72.596 24.521h21.219L53.38 67.338h58.228V54.797H80.976l39.09-41.42H80.389l-7.794 11.144z"
311 fill="#1e1e1e"></path>
312 <path d="M57.073 13.378L40.101 37.239h21.603l-12.568 30.1 37.678-39.923H66.051l9.985-14.038H57.073z"
313 fill="#fc0"></path>
314 <path d="M57.074 13.378L40.102 37.239h21.602L49.136 67.338l23.038-35.036H52.435l14.12-18.924h-9.481z"
315 fill="url(#_Linear1)"></path>
316 <path d="M200.202 47.707h-19.221l9.611-22.993 9.61 22.993zm8.279 19.631h10.645l-23.212-53.965h-10.645l-23.213 53.965h10.572l4.435-10.163h27.056l4.362 10.163z"
317 fill="gray"></path>
318 <path d="M313.541 67.338V13.373h-9.684V36.29h-25.43V13.373h-9.758v53.965h9.758v-22.03h25.43v22.03h9.684z"
319 fill="gray"></path>
320 <path d="M360 57.728h-29.053V44.865h16.255l3.911-9.092h-20.166V22.835h24.983l4.07-9.461h-38.736v53.964H360v-9.61z"
321 fill="gray"></path>
322 <path d="M143.812 13.373h15.746v9.314h-15.746c-12.493 0-17.89 9.315-17.815 17.964.073 8.575 5.026 17.52 17.815 17.52h15.659l-3.943 9.167H143.812c-19.22 0-27.352-13.233-27.426-26.687-.073-13.528 8.723-27.278 27.426-27.278z"
323 fill="gray"></path>
324 <path d="M245.201 13.373h15.746v9.314H245.201c-12.493 0-17.89 9.315-17.816 17.964.074 8.575 5.027 17.52 17.816 17.52h15.746v9.167H245.201c-19.221 0-27.352-13.233-27.426-26.687-.074-13.528 8.723-27.278 27.426-27.278"
325 fill="gray"></path>
326 </g>
327 <defs>
328 <linearGradient id="_Linear1" x1="0" y1="0" x2="1" y2="0"
329 gradientUnits="userSpaceOnUse"
330 gradientTransform="matrix(32.072 0 0 -32.072 40.101 40.358)">
331 <stop offset="0" stop-color="#deb203" stop-opacity="1"></stop>
332 <stop offset="1" stop-color="#fc0" stop-opacity="1"></stop>
333 </linearGradient>
334 </defs>
335 </svg>
336 </h1>
337 </header>
338 <div class="row">
339 <div class="col-12 order-first col-sm-4 col-md-3 col-xl-2">
340 <nav class="ezcache-nav">
341 <a href="#" class="nav-item">
342 <?php echo str_repeat( '', mb_strlen( $trans['stats'] ) ); ?>
343 </a>
344 <a href="#" class="nav-item">
345 <?php echo str_repeat( '', mb_strlen( $trans['settings'] ) ); ?>
346 </a>
347 <a href="#" class="nav-item">
348 <?php echo str_repeat( '', mb_strlen( $trans['advanced_settings'] ) ); ?>
349 </a>
350 <a href="#" class="nav-item">
351 <?php echo str_repeat( '', mb_strlen( $trans['license'] ) ); ?>
352 </a>
353 </nav>
354 </div>
355 <div class="col-12 col-sm-4 order-sm-last col-md-3 col-xl-2 mb-4">
356 <div class="ezcache-nav mt-1">
357 <button type="button" class="wpb-button" disabled>
358 <svg viewBox="0 0 24 24">
359 <path d="M19,8L15,12H18A6,6 0 0,1 12,18C11,18 10.03,17.75 9.2,17.3L7.74,18.76C8.97,19.54 10.43,20 12,20A8,8 0 0,0 20,12H23M6,12A6,6 0 0,1 12,6C13,6 13.97,6.25 14.8,6.7L16.26,5.24C15.03,4.46 13.57,4 12,4A8,8 0 0,0 4,12H1L5,16L9,12"></path>
360 </svg>
361 <?php echo str_repeat( '', mb_strlen( $trans['clear_cache'] ) ); ?>
362 </button>
363 <a href="#" target="_blank" class="mt-2 wpb-button wpb-button-outlined disabled">
364 <svg viewBox="0 0 24 24">
365 <path d="M12,16A3,3 0 0,1 9,13C9,11.88 9.61,10.9 10.5,10.39L20.21,4.77L14.68,14.35C14.18,15.33 13.17,16 12,16M12,3C13.81,3 15.5,3.5 16.97,4.32L14.87,5.53C14,5.19 13,5 12,5A8,8 0 0,0 4,13C4,15.21 4.89,17.21 6.34,18.65H6.35C6.74,19.04 6.74,19.67 6.35,20.06C5.96,20.45 5.32,20.45 4.93,20.07V20.07C3.12,18.26 2,15.76 2,13A10,10 0 0,1 12,3M22,13C22,15.76 20.88,18.26 19.07,20.07V20.07C18.68,20.45 18.05,20.45 17.66,20.06C17.27,19.67 17.27,19.04 17.66,18.65V18.65C19.11,17.2 20,15.21 20,13C20,12 19.81,11 19.46,10.1L20.67,8C21.5,9.5 22,11.18 22,13Z"></path>
366 </svg>
367 <?php echo str_repeat( '', mb_strlen( $trans['run_speed_test'] ) ); ?>
368 </a>
369 </div>
370 <div class="side-links">
371 <a href="#" target="_blank" class="side-links-item disabled">
372 <?php echo str_repeat( '', mb_strlen( $trans['documentation'] ) ); ?>
373 </a>
374 <a href="#" target="_blank" class="side-links-item disabled">
375 <?php echo str_repeat( '', mb_strlen( $trans['knowledgebase'] ) ); ?>
376 </a>
377 </div>
378 </div>
379 <div class="col-12 col-sm-4 order-sm-first col-md-6 col-xl-8">
380 <section class="ezcache-main">
381 <div>
382 <header class="ezcache-screen-header">
383 <svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
384 </svg>
385 <?php echo str_repeat( '', mb_strlen( $trans['stats'] ) ); ?>
386 </header>
387 <div class="row">
388 </div>
389 </div>
390 </section>
391 </div>
392 </div>
393 </div>
394 <noscript>
395 <p class="text-center">
396 <strong><?php esc_html_e( 'This application requires JavaScript to run', 'ezcache' ); ?></strong>
397 </p>
398 </noscript>
399 </ezc-options>
400 </div>
401 <?php
402 }
403
404 /**
405 * Queue up the options page scripts
406 */
407 public function enqueue_scripts() {
408 $screen = get_current_screen();
409
410 if ( 'toplevel_page_ezcache' != $screen->id ) {
411 return;
412 }
413
414 $ver = defined( 'WP_DEBUG' ) && WP_DEBUG ? time() : $this->plugin->plugin_version;
415
416 wp_enqueue_script( 'ezcache-options', $this->plugin->plugin_url . '/assets/dist/js/options.js', [], $ver, true );
417 wp_enqueue_style( 'ezcache-options', $this->plugin->plugin_url . '/assets/dist/css/options.css', [], $ver );
418 // branding.css removed — branding is embedded in Vue 2.0 options.css
419
420 // Freemius removed — Pro is unlocked for everyone.
421 wp_localize_script( 'ezcache-options', 'ezcache', [
422 'version' => EZCACHE_VERSION,
423 'assets_url' => esc_url_raw( EZCACHE_URL . '/assets' ),
424 'ajax_url' => esc_url_raw( admin_url( 'admin-ajax.php' ) ),
425 'rest_url' => esc_url_raw( rest_url() ),
426 'ajax_nonce' => wp_create_nonce( 'ezcache-options' ),
427 'rest_nonce' => wp_create_nonce( 'wp_rest' ),
428 'is_premium' => true,
429 'is_trial' => false,
430 'trial_days_remaining' => 0,
431 'premium_features' => \Upress\EzCache\PremiumFeatures::get_premium_features(),
432 'upgrade_url' => '',
433 'is_freemius_registered' => false,
434 'is_freemius_trial' => false,
435 'is_freemius_paying' => true,
436 'trial_start_url' => '',
437 'opt_in_url' => '',
438 'is_rtl' => is_rtl(),
439 'site_url' => esc_url_raw( untrailingslashit( site_url() ) ),
440 'performance_url' => admin_url( 'admin.php?page=ezcache-performance' ),
441 'trans' => $this->getJsTraslations(),
442 'is_https_2' => $this->check_https_2_support(),
443 'is_elementor_installed' => is_plugin_active( 'elementor/elementor.php' ),
444 ] );
445 }
446
447 /**
448 * Check if current website supports HTTPS/2
449 *
450 * @return bool
451 */
452 protected function check_https_2_support() {
453 if ( ! is_ssl() ) {
454 return false;
455 }
456
457 $supports = get_transient( 'ezcache_https_2_support' );
458
459 if ( ! $supports ) {
460 $response = wp_safe_remote_head( home_url(), [
461 'httpversion' => '2.0',
462 ] );
463
464 $supports = false;
465 if ( ! is_wp_error( $response ) ) {
466 $response = $response['http_response']->get_response_object();
467 $supports = $response->protocol_version == 2;
468 }
469
470 set_transient( 'ezcache_https_2_support', [ 'has_support' => $supports ], DAY_IN_SECONDS );
471 }
472
473 return isset( $supports['has_support'] ) ? $supports['has_support'] : false;
474 }
475
476 /**
477 * Get the trnaslations required for the javascript frontend
478 *
479 * @return array
480 */
481 protected function getJsTraslations() {
482 return [
483 'is_rtl' => is_rtl(),
484
485 'select_all' => __( 'Select All', 'ezcache' ),
486 'select_none' => __( 'Select None', 'ezcache' ),
487 'ezcache' => __( 'ezCache', 'ezcache' ),
488 'enabled' => __( 'Enabled', 'ezcache' ),
489 'disabled' => __( 'Disabled', 'ezcache' ),
490 'run_speed_test' => __( 'Run a Speed Test', 'ezcache' ),
491 'save_settings' => __( 'Save Settings', 'ezcache' ),
492 'reset' => __( 'Reset', 'ezcache' ),
493 'reset_settings' => __( 'Reset Settings', 'ezcache' ),
494 'confirm_reset_settings' => __( 'Are you sure you want to reset the settings to their default values?', 'ezcache' ),
495 'settings_saved' => __( 'Settings updated.', 'ezcache' ),
496 'error_saving_settings' => __( 'Could not save settings.', 'ezcache' ),
497 'clear_cache' => __( 'Clear Cache', 'ezcache' ),
498 'cache_cleared' => __( 'Cache has been cleared.', 'ezcache' ),
499 'documentation' => __( 'Documentation', 'ezcache' ),
500 'knowledgebase' => __( 'Knowledgebase', 'ezcache' ),
501 'settings' => __( 'Settings', 'ezcache' ),
502 'advanced_settings' => __( 'Advanced Settings', 'ezcache' ),
503 'stats' => __( 'Statistics', 'ezcache' ),
504 'license' => __( 'License', 'ezcache' ),
505 'performance' => __( 'Performance', 'ezcache' ),
506
507 // Performance page translations
508 'cache_preload' => __( 'Cache Preload', 'ezcache' ),
509 'status' => __( 'Status', 'ezcache' ),
510 'processed' => __( 'Processed', 'ezcache' ),
511 'remaining' => __( 'remaining', 'ezcache' ),
512 'run_preload_now' => __( 'Run Preload Now', 'ezcache' ),
513 'stop_preload' => __( 'Stop Preload', 'ezcache' ),
514 'preload_completed' => __( 'Completed', 'ezcache' ),
515 'preload_last_run' => __( 'Last preload', 'ezcache' ),
516 'pages' => __( 'pages', 'ezcache' ),
517 'time_ago' => __( 'ago', 'ezcache' ),
518 'time_just_now' => __( 'just now', 'ezcache' ),
519 'preload_settings' => __( 'Preload Settings', 'ezcache' ),
520 'enable_preload' => __( 'Enable Preload', 'ezcache' ),
521 'enable_preload_desc' => __( 'Activate the cache preloader. Pages will be crawled in the background to keep the cache warm.', 'ezcache' ),
522 'preload_after_clear' => __( 'Preload after cache clear', 'ezcache' ),
523 'crawl_homepage' => __( 'Crawl homepage links', 'ezcache' ),
524 'sitemap_url' => __( 'Sitemap URL (optional)', 'ezcache' ),
525 'sitemap_url_desc' => __( 'Leave blank to auto-detect /wp-sitemap.xml, /sitemap_index.xml or /sitemap.xml.', 'ezcache' ),
526 'urls_per_batch' => __( 'URLs per batch', 'ezcache' ),
527 'frontend_optimizations' => __( 'Front-end Optimizations', 'ezcache' ),
528 'lazy_load_images' => __( 'Lazy load images', 'ezcache' ),
529 'lazy_load_iframes' => __( 'Lazy load iframes', 'ezcache' ),
530 'defer_js' => __( 'Defer JavaScript', 'ezcache' ),
531 'defer_js_desc' => __( 'Adds defer to local script tags to reduce render-blocking JS.', 'ezcache' ),
532 'defer_js_exclusions' => __( 'Defer JS exclusions', 'ezcache' ),
533 'defer_js_exclusions_desc' => __( 'One match per line. URLs containing any of these strings will not be deferred.', 'ezcache' ),
534 'remove_query_strings' => __( 'Remove query strings from static assets', 'ezcache' ),
535 'dns_prefetch' => __( 'DNS prefetch hosts', 'ezcache' ),
536 'preconnect' => __( 'Preconnect hosts', 'ezcache' ),
537 'heartbeat_control' => __( 'Heartbeat Control', 'ezcache' ),
538 'enable_heartbeat' => __( 'Enable heartbeat control', 'ezcache' ),
539 'mode' => __( 'Mode', 'ezcache' ),
540 'reduce_activity' => __( 'Reduce activity (60s ticks)', 'ezcache' ),
541 'disable_completely' => __( 'Disable completely', 'ezcache' ),
542 'cdn' => __( 'CDN', 'ezcache' ),
543 'enable_cdn' => __( 'Enable CDN rewriting', 'ezcache' ),
544 'cdn_url' => __( 'CDN URL', 'ezcache' ),
545 'cdn_url_desc' => __( 'Static assets under /wp-content/uploads, /themes and /plugins will be rewritten to this host.', 'ezcache' ),
546 'database_cleanup' => __( 'Database Cleanup', 'ezcache' ),
547 'delete_revisions' => __( 'Delete post revisions', 'ezcache' ),
548 'delete_auto_drafts' => __( 'Delete auto drafts', 'ezcache' ),
549 'delete_trashed_posts' => __( 'Delete trashed posts', 'ezcache' ),
550 'delete_spam_comments' => __( 'Delete spam comments', 'ezcache' ),
551 'delete_trashed_comments' => __( 'Delete trashed comments', 'ezcache' ),
552 'delete_transients' => __( 'Delete expired transients', 'ezcache' ),
553 'delete_orphan_meta' => __( 'Delete orphan post meta', 'ezcache' ),
554 'optimize_tables' => __( 'Run OPTIMIZE TABLE on all tables', 'ezcache' ),
555 'cleanup_schedule' => __( 'Automatic cleanup schedule', 'ezcache' ),
556 'never' => __( 'Never (manual only)', 'ezcache' ),
557 'run_cleanup_now' => __( 'Run Cleanup Now', 'ezcache' ),
558 'preload_started' => __( 'Preload started in the background.', 'ezcache' ),
559 'preload_stopped' => __( 'Preload cancelled.', 'ezcache' ),
560 'db_cleaned' => __( 'Database cleanup completed.', 'ezcache' ),
561 'saving' => __( 'Saving', 'ezcache' ),
562 'error' => __( 'An error occurred', 'ezcache' ),
563
564 'recommended' => __( 'Recommended', 'ezcache' ),
565 'basic_settings' => __( 'Basic Settings', 'ezcache' ),
566 'confirm' => __( 'OK', 'ezcache' ),
567 'cancel' => __( 'Cancel', 'ezcache' ),
568 'delete' => __( 'Delete', 'ezcache' ),
569
570 'no_cache_known_users' => __( 'Don\'t cache pages for known users', 'ezcache' ),
571 'no_cache_known_users_description' => __( 'This disables cache for logged in users.', 'ezcache' ),
572 'no_cache_known_users_admin_bar_notice' => __( 'The admin bar will be hidden for all users to prevent it being saved into the cache.', 'ezcache' ),
573 'no_cache_comment_authors' => __( 'Don\'t cache pages for comment authors', 'ezcache' ),
574 'no_cache_comment_authors_description' => __( 'Do not save cache for users who saved their name and email for the next time they comment.', 'ezcache' ),
575 'cache_expiry' => __( 'Cache Expiry', 'ezcache' ),
576 'cache_lifetime' => __( 'Cache Timeout', 'ezcache' ),
577 'cache_lifetime_description' => __( 'How long to keep the cached data, the recommended and effective starting point is one day.', 'ezcache' ),
578 'cache_expiry_interval' => __( 'Check for stale cache every', 'ezcache' ),
579 'cache_expiry_interval_description' => __( 'Automatically check for stale cache files and delete them at a set interval.', 'ezcache' ),
580 'interval' => __( 'Interval', 'ezcache' ),
581 'every' => __( 'Every', 'ezcache' ),
582 'seconds' => __( 'Seconds', 'ezcache' ),
583 'cache_schedule_type_time' => __( 'At Time', 'ezcache' ),
584 'cache_schedule_interval' => __( 'Interval', 'ezcache' ),
585 'weekly' => __( 'Weekly', 'ezcache' ),
586 'daily' => __( 'Daily', 'ezcache' ),
587 'twicedaily' => __( 'Twice Daily', 'ezcache' ),
588 'hourly' => __( 'Hourly', 'ezcache' ),
589 'at' => __( 'At', 'ezcache' ),
590 'cache_bypass' => __( 'Cache Bypass', 'ezcache' ),
591 'no_cache_query_params' => __( 'Don\'t cache pages where the URLs contain parameters in the query string', 'ezcache' ),
592 'no_cache_query_params_description' => __( 'Don\'t save cache when the URL contains query string parameters, such as ?page_id=15 at the end of the URL.', 'ezcache' ),
593 'ignore_query_params' => __( 'Ignore tracking parameters when caching', 'ezcache' ),
594 'ignore_query_params_description' => __( 'Advanced: serve the same cached page regardless of the value of tracking parameters (such as gclid or utm_source). Useful for ad-campaign traffic so every click is served from cache instead of creating a new cache entry per click.', 'ezcache' ),
595 'ignored_query_params_list_label' => __( 'Parameters to ignore (one per line; add a trailing * to match by prefix, e.g. utm_*)', 'ezcache' ),
596 'separate_mobile_cache' => __( 'Separate cache for mobile devices', 'ezcache' ),
597 'separate_mobile_cache_description' => __( 'Save separate cache files for mobile browsers and a separate file for desktop browsers, you should only enable this option if your theme is not responsive and you use a theme or a plugin that produces a separate mobile version of the website.', 'ezcache' ),
598 'cache_clear_on_post_edit' => __( 'Clear cache when a post or page is published or updated', 'ezcache' ),
599 'cache_clear_on_post_edit_description' => __( 'Keep your posts up to date even when they are updated by clearing their cache.', 'ezcache' ),
600 'cache_clear_home_on_post_edit' => __( 'Clear homepage cache when a post or page is published or updated', 'ezcache' ),
601 'cache_clear_home_on_post_edit_description' => __( 'Make sure your visitors read the latest posts by clearing the cache when you update or publish a post.', 'ezcache' ),
602 'enable_varnish_purge' => __( 'Send PURGE requests to Varnish on cache clear', 'ezcache' ),
603 'enable_varnish_purge_description' => __( 'When enabled, ezCache sends a PURGE request to the local Varnish instance (127.0.0.1) whenever the cache is cleared. Disable this if Varnish is not in your site\'s request path to avoid unnecessary 403 errors in your logs.', 'ezcache' ),
604 'bypass_cache_title' => __( 'Disable caching for the following pages', 'ezcache' ),
605 'bypass_cache_single' => __( 'Single Posts', 'ezcache' ),
606 'bypass_cache_pages' => __( 'Pages', 'ezcache' ),
607 'bypass_cache_frontpage' => __( 'Front Page', 'ezcache' ),
608 'bypass_cache_home' => __( 'Home', 'ezcache' ),
609 'bypass_cache_archives' => __( 'Archives', 'ezcache' ),
610 'bypass_cache_tag' => __( 'Tags', 'ezcache' ),
611 'bypass_cache_category' => __( 'Categories', 'ezcache' ),
612 'bypass_cache_feed' => __( 'Feeds', 'ezcache' ),
613 'bypass_cache_search' => __( 'Search Results', 'ezcache' ),
614 'bypass_cache_author' => __( 'Author Pages', 'ezcache' ),
615 'rejected_uri' => __( 'Links (URLs) which will never get cached', 'ezcache' ),
616 'rejected_uri_description' => __( 'Do not serve cached content if the URL matches any link in the following list', 'ezcache' ),
617 'rejected_uri_wildcard' => __( 'The domain part of the URL will be stripped automatically. Use * wildcard character to match multiple characters at this position (eg. /product/*).', 'ezcache' ),
618 'rejected_user_agent' => __( 'User Agents which will never receive cached data', 'ezcache' ),
619 'rejected_user_agent_description' => __( 'Do not serve cached content to the following useragents', 'ezcache' ),
620 'rejected_cookies' => __( 'Cookies which will prevent pages from getting cached', 'ezcache' ),
621 'rejected_cookies_description' => __( 'Specify the cookies that when set in the visitor\'s browser, should prevent a page from getting cached (one per line)', 'ezcache' ),
622 'rejected_cookies_placeholder' => _x( 'wordpress_logged_in_', 'rejected cookies example', 'ezcache' ),
623 'n_minutes' => __( '%s Minutes', 'ezcache' ),
624 'n_hours' => __( '%s Hours', 'ezcache' ),
625 'n_days' => __( '%s Days', 'ezcache' ),
626 'never_expire' => __( 'Never Expire', 'ezcache' ),
627 'cache_disabled' => __( 'WP_CACHE is disabled in your wp-config.php, caching will not work.', 'ezcache' ),
628 'adv_cache_not_exists' => __( 'advanced-cache.php is missing from your wp-content folder, caching will not work.', 'ezcache' ),
629 'plugin_badly_installed' => __( 'Plugin is not installed properly, please reinstall, caching will not work.', 'ezcache' ),
630 'desktop' => _x( 'Desktop', 'statistics block title', 'ezcache' ),
631 'mobile' => _x( 'Mobile', 'statistics block title', 'ezcache' ),
632 'expired' => _x( 'Expire Cache', 'statistics block title', 'ezcache' ),
633 'javascript' => _x( 'JavaScript Files', 'statistics block title', 'ezcache' ),
634 'css' => _x( 'CSS Files', 'statistics block title', 'ezcache' ),
635 'webp_images' => _x( 'WebP Images', 'statistics block title', 'ezcache' ),
636 'cache_usage' => __( 'ezCache Cache Usage', 'ezcache' ),
637 'webp_stats_description' => __( 'Images optimized with WebP.', 'ezcache' ),
638 'cache_bypass_settings' => _x( 'Cache Bypass', 'settings block title', 'ezcache' ),
639 'cache_settings' => _x( 'Caching', 'settings block title', 'ezcache' ),
640 'performance_settings' => _x( 'Performance', 'settings block title', 'ezcache' ),
641 'cache_expiry_settings' => _x( 'Cache Expiration', 'settings block title', 'ezcache' ),
642 'optimize_google_fonts' => __( 'Optimize Google fonts', 'ezcache' ),
643 'optimize_google_fonts_description' => __( 'Combine multiple Google Fonts declerations into one.', 'ezcache' ),
644 'minify_html' => __( 'Minify HTML', 'ezcache' ),
645 'minify_html_description' => __( 'Minify the cached HTML to make cached files smaller.', 'ezcache' ),
646 'minify_inline_js' => __( 'Minify Inline JavaScript', 'ezcache' ),
647 'minify_inline_js_description' => __( 'Include JavaScript embedded in the HTML in the minification process.', 'ezcache' ),
648 'minify_inline_css' => __( 'Minify Inline CSS', 'ezcache' ),
649 'minify_inline_css_description' => __( 'Include CSS embedded in the HTML in the minification process.', 'ezcache' ),
650 'minify_js' => __( 'Minify JavaScript', 'ezcache' ),
651 'minify_js_description' => __( 'Minify JavaScript files to reduce their size.', 'ezcache' ),
652 'minify_html_comments' => __( 'Remove HTML comments', 'ezcache' ),
653 'minify_html_comments_description' => __( 'Remove embeded comments from minified HTML.', 'ezcache' ),
654 'combine_head_js' => __( 'Combine JavaScript in Head', 'ezcache' ),
655 'combine_head_js_description' => __( 'Combine multiple JavaScript files found in the head section of the HTML into one file.', 'ezcache' ),
656 'combine_body_js' => __( 'Combine JavaScript in Body', 'ezcache' ),
657 'combine_body_js_description' => __( 'Combine multiple JavaScript files found in the body section of the HTML into one file.', 'ezcache' ),
658 'combine_head_inline_js' => __( 'Combine Inline JavaScript in Head', 'ezcache' ),
659 'combine_head_inline_js_description' => __( 'Combine JavaScript scripts found in the head section of the HTML into the combined JS file.', 'ezcache' ),
660 'combine_body_inline_js' => __( 'Combine Inline JavaScript in Body', 'ezcache' ),
661 'combine_body_inline_js_description' => __( 'Combine JavaScript scripts found in the body section of the HTML into the combined JS file.', 'ezcache' ),
662 'combine_js_elementor_notice' => __( 'Elementor generates a unique JS file for each post/page which can cause the cache to use large amounts of disk space, to fix this make sure to exclude Elementor JS files (`elementor/js/post-*`) from optimizations.', 'ezcache' ),
663 'minify_css' => __( 'Minify CSS', 'ezcache' ),
664 'minify_css_description' => __( 'Minify CSS files to reduce their size.', 'ezcache' ),
665 'combine_css' => __( 'Combine CSS', 'ezcache' ),
666 'combine_css_description' => __( 'Combine multiple CSS files into one to reduce HTTP requests.', 'ezcache' ),
667 'combine_css_elementor_notice' => __( 'Elementor generates a unique CSS file for each post/page which can cause the cache to use large amounts of disk space, to fix this make sure to exclude Elementor CSS files (`elementor/css/post-*`) from optimizations.', 'ezcache' ),
668 'disable_wp_emoji' => __( 'Disable WordPress Emoji', 'ezcache' ),
669 'disable_wp_emoji_description' => __( 'Remove extra code related to Emoji from WordPress which was added recently to support Emoji in an older browsers.', 'ezcache' ),
670 'not_recommended_https2' => __( 'Your server supports HTTP/2 which benefits from having this option kept disabled', 'ezcache' ),
671 'enable_webp_support' => __( 'Optimize Images With WebP', 'ezcache' ),
672 /* xgettext:no-php-format */
673 'enable_webp_support_description' => __( 'WebP is a modern image format that provides superior lossless and lossy compression for images on the web. WebP lossless images are 26% smaller in size compared to PNGs, and 25-34% smaller than comparable JPEG images.', 'ezcache' ),
674 'requries_premium_license' => __( 'Requires a premium license', 'ezcache' ),
675 'rejected_user_agent_placeholder' => _x( 'Googlebot', 'user agent example', 'ezcache' ),
676 'rejected_uri_placeholder' => _x( '/products/*', 'rejected uri example', 'ezcache' ),
677 'css_stats_description' => __( 'Optimized and cached CSS files.', 'ezcache' ),
678 'javascript_stats_description' => __( 'Optimized and cached JavaScript files.', 'ezcache' ),
679 'expired_stats_description' => __( 'Cache data that has expired and waiting to be cleaned up.', 'ezcache' ),
680 'mobile_stats_description' => __( 'Cache data for pages viewed from mobile devices.', 'ezcache' ),
681 'desktop_stats_description' => __( 'Cache data for pages viewed from desktop computers.', 'ezcache' ),
682 'general_advanced_settings' => __( 'General', 'ezcache' ),
683
684 'combine_css_footer' => __( 'Move combined CSS to footer', 'ezcache' ),
685 'combine_css_footer_description' => __( 'Eliminate render blocking CSS by moving the combined CSS file to the footer section.', 'ezcache' ),
686 'critical_css' => __( 'Critical CSS', 'ezcache' ),
687 'critical_css_description' => __( "Critical CSS is the minimum set of blocking CSS required to render the first screen's worth of content to the user in order to render content to the user as fast as possible.\nThe Critical CSS can (and should) be manually created to fit the website perfectly, however there are tools that can automatically generate the Critical CSS for you.", 'ezcache' ),
688 'critical_css_more_information' => __( 'More information about Critical CSS', 'ezcache' ),
689 'critical_css_online_tool' => __( 'Online Critical CSS Generator', 'ezcache' ),
690 'combine_css_footer_requires_combine_css_notice' => __( 'Moving combined CSS to footer requires the Combine CSS option enabled.', 'ezcache' ),
691 'excluded_minify_files' => __( 'Exclude JS/CSS files from optimization', 'ezcache' ),
692 'excluded_minify_files_description' => __( 'List filenames or paths to files which should be excluded from minification or combining.', 'ezcache' ),
693 'excluded_minify_files_placeholder' => __( "jquery.js\nrecaptcha/api.js\ngoogleadservices.com", 'ezcache' ),
694
695 'advanced_tools' => __( 'Advanced Tools', 'ezcache' ),
696 'delete_webp_images' => __( 'Clear WebP Images Cache', 'ezcache' ),
697 'confirm_delete_webp_images' => __( "Are you sure that you want to delete all of the cached WebP images?\nTheses images will have to be converted again which may take some time.", 'ezcache' ),
698 'error_delete_webp_images' => __( "Error clearing WebP image cache", 'ezcache' ),
699 'schedule_webp_images_process_started' => __( "Scheduled task queued", 'ezcache' ),
700 'error_schedule_webp_images_process' => __( "Error scheduling WebP process", 'ezcache' ),
701 'schedule_webp_images_process' => __( "Re-Schedule WebP Process Scheduled Task", 'ezcache' ),
702
703 'license_key' => __( 'License Key', 'ezcache' ),
704 'license_key_description' => __( 'Pro features are unlocked.', 'ezcache' ),
705 'deactivate_license' => __( 'Account Settings', 'ezcache' ),
706 'activate_license' => __( 'Manage License', 'ezcache' ),
707 'license_valid' => __( 'License is valid', 'ezcache' ),
708 'license_invalid' => __( 'License is invalid', 'ezcache' ),
709 'license_expires_at' => __( 'License expires at %s', 'ezcache' ),
710 'unknown' => __( 'Unknown', 'ezcache' ),
711 'license_status' => __( 'Status', 'ezcache' ),
712 'license_details' => __( 'License Details', 'ezcache' ),
713 'license_type' => __( 'License Type', 'ezcache' ),
714 'trial_license' => _x( 'Trial', 'license type', 'ezcache' ),
715 'regular_license' => _x( 'Pro', 'license type', 'ezcache' ),
716 'expires_in' => __( 'Expires In', 'ezcache' ),
717 'expires_at' => __( 'Expires At', 'ezcache' ),
718 'uses_left' => __( 'Convertions Left', 'ezcache' ),
719 'trial_not_started' => __( '%s days after first usage', 'ezcache' ),
720 'purchase_license' => __( 'Get a Pro License', 'ezcache' ),
721 'license_expired' => __( 'Expired', 'ezcache' ),
722 'no_expiry_while_upress_client' => __( 'Will not expire while hosted on uPress', 'ezcache' ),
723
724 'upress_ad' => __( 'uPress Premium WordPress Hosting', 'ezcache' ),
725 'upress_domain' => __( 'www.upress.io', 'ezcache' ),
726 'upress_ad_link' => __( 'https://www.upress.io/?utm_source=wordpress&utm_medium=cpc&utm_campaign=ezcache', 'ezcache' ),
727 'speed_test_url' => __( 'https://speedom.net/?url=%s&location=US-NY&utm_source=wordpress&utm_medium=cpc&utm_campaign=ezcache', 'ezcache' ),
728 'ezcache_docs_link' => __( 'https://ezcache-wp.com/documentation?utm_source=wordpress&utm_medium=cpc&utm_campaign=ezcache', 'ezcache' ),
729 'ezcache_knowledgebase_link' => __( 'https://ezcache-wp.com/knowledgebase?utm_source=wordpress&utm_medium=cpc&utm_campaign=ezcache', 'ezcache' ),
730 'ezcache_pricing_link' => __( 'https://ezcache-wp.com/pricing?utm_source=wordpress&utm_medium=cpc&utm_campaign=ezcache', 'ezcache' ),
731 ];
732 }
733
734 /**
735 * Show cache status admin notices when needed
736 */
737
738 /**
739 * Show trial banner in admin
740 */
741 function show_trial_banner() {
742 // No-op — licensing/trial system removed. Pro is unlocked for everyone.
743 return;
744 }
745
746 function maybe_show_advanced_cache_notice() {
747 $webp_queue = get_site_option( 'ezcache_convert_images_to_webp_reprocess_queue' );
748 if ( ! $webp_queue ) {
749 $webp_queue = [];
750 }
751 $count_pending = array_reduce( $webp_queue, function ( $count, $images ) {
752 return $count + count( $images );
753 }, 0 );
754
755 if ( $count_pending > 0 ) {
756 echo '<div class="notice notice-info"><p><strong>' . esc_html__( 'ezCache', 'ezcache' ) . ':</strong> ' . sprintf( esc_html__( 'WebP Processing is currently running, %d images remaining.', 'ezcache' ), $count_pending ) . '</p></div>';
757 }
758
759 if ( ! get_site_option( 'ezcache_first_run', false ) ) {
760 update_site_option( 'ezcache_first_run', 1 );
761 }
762 }
763
764 function admin_clear_cache() {
765 if ( ! isset( $_GET['_wpnonce'] ) || ! wp_verify_nonce( $_GET['_wpnonce'], 'wpb-clear-cache' ) ) {
766 wp_nonce_ays( 'wpb-clear-cache' );
767 }
768
769 $this->plugin->ezcache->clear_cache();
770
771 wp_redirect( wp_get_referer() );
772 exit;
773 }
774
775 function maybe_show_admin_bar( $show_admin_bar ) {
776 $settings = Settings::get_settings();
777
778 if ( ! $settings->no_cache_known_users ) {
779 return false;
780 }
781
782 return $show_admin_bar;
783 }
784
785 /**
786 * Inject premium feature gates into the UI
787 */
788
789 /**
790 * @deprecated Branding is now handled by the Vue 2.0 frontend (options.css/options.js).
791 * This method is no longer hooked. Kept for reference only.
792 */
793 function inject_branding_css() {
794 $screen = get_current_screen();
795 if ( ! $screen || strpos( $screen->id, 'ezcache' ) === false ) {
796 return;
797 }
798 ?>
799 <style>
800 /* ezCache 2026 Branding */
801 .ezcache-screen-header {
802 background: linear-gradient(135deg, #0a0a0a 0%, #1a1a2e 50%, #16213e 100%) !important;
803 color: #fff !important;
804 padding: 24px 28px !important;
805 border-radius: 12px !important;
806 margin: 20px 0 24px !important;
807 display: flex !important;
808 align-items: center !important;
809 gap: 12px !important;
810 font-size: 22px !important;
811 font-weight: 700 !important;
812 position: relative;
813 overflow: hidden;
814 border: 1px solid rgba(255,204,0,0.2);
815 box-shadow: 0 4px 20px rgba(0,0,0,0.15);
816 }
817 .ezcache-screen-header::before {
818 content: none;
819 font-size: 0;
820 margin-right: 4px;
821 }
822 .ezcache-screen-header::after {
823 content: none;
824 position: absolute;
825 right: 20px;
826 top: 50%;
827 transform: translateY(-50%);
828 background: #ffcc00;
829 color: #0a0a0a;
830 font-size: 11px;
831 font-weight: 800;
832 padding: 4px 12px;
833 border-radius: 20px;
834 letter-spacing: 0.5px;
835 }
836 .ezcache-screen-header svg {
837 fill: #ffcc00 !important;
838 width: 28px !important;
839 height: 28px !important;
840 }
841
842 /* Modernize toggle panels */
843 .ezcache-toggle-panel,
844 .ezcache-options .postbox,
845 .ezcache-options .card {
846 border-radius: 10px !important;
847 border: 1px solid #e0e0e0 !important;
848 box-shadow: 0 2px 8px rgba(0,0,0,0.04) !important;
849 overflow: hidden;
850 }
851
852 /* Save button styling */
853 .wpb-button-primary {
854 background: linear-gradient(135deg, #ffcc00, #e6b800) !important;
855 color: #0a0a0a !important;
856 border: none !important;
857 border-radius: 8px !important;
858 font-weight: 700 !important;
859 text-transform: none !important;
860 box-shadow: 0 2px 10px rgba(255,204,0,0.25) !important;
861 transition: all 0.2s !important;
862 }
863 .wpb-button-primary:hover {
864 transform: translateY(-1px) !important;
865 box-shadow: 0 4px 16px rgba(255,204,0,0.35) !important;
866 }
867
868 /* Sidebar menu icon */
869 #toplevel_page_ezcache .wp-menu-image::before {
870 content: '' !important;
871 font-size: 18px !important;
872 }
873 </style>
874 <?php
875 }
876 function inject_premium_ui_gates() {
877 $screen = get_current_screen();
878 if ( 'toplevel_page_ezcache' != $screen->id ) {
879 return;
880 }
881
882 if ( \Upress\EzCache\PremiumFeatures::is_premium() ) {
883 return; // Premium user, no gates needed
884 }
885
886 $premium_features = \Upress\EzCache\PremiumFeatures::get_premium_features();
887 $features_json = json_encode( $premium_features );
888 $upgrade_url = '#pricing';
889
890 ?>
891 <style>
892 .ezcache-premium-overlay {
893 position: relative;
894 }
895 .ezcache-premium-overlay::after {
896 content: '🔒 PRO';
897 position: absolute;
898 top: 50%;
899 right: 12px;
900 transform: translateY(-50%);
901 background: #ffcc00;
902 color: #1a1a1a;
903 font-size: 11px;
904 font-weight: 800;
905 padding: 2px 10px;
906 border-radius: 12px;
907 pointer-events: none;
908 }
909 .ezcache-premium-disabled {
910 opacity: 0.45;
911 pointer-events: none;
912 user-select: none;
913 }
914 .ezcache-upgrade-banner {
915 background: linear-gradient(135deg, #1a1a1a 0%, #2a2a2a 100%);
916 border: 1px solid #ffcc00;
917 border-radius: 12px;
918 padding: 20px 24px;
919 margin: 16px 0;
920 display: flex;
921 align-items: center;
922 justify-content: space-between;
923 flex-wrap: wrap;
924 gap: 16px;
925 }
926 .ezcache-upgrade-banner .upgrade-text {
927 color: #e0e0e0;
928 font-size: 14px;
929 line-height: 1.5;
930 }
931 .ezcache-upgrade-banner .upgrade-text strong {
932 color: #ffcc00;
933 }
934 .ezcache-upgrade-btn {
935 background: #ffcc00;
936 color: #1a1a1a;
937 border: none;
938 padding: 10px 24px;
939 border-radius: 50px;
940 font-size: 14px;
941 font-weight: 700;
942 text-decoration: none;
943 cursor: pointer;
944 white-space: nowrap;
945 transition: all 0.2s;
946 }
947 .ezcache-upgrade-btn:hover {
948 background: #e6b800;
949 transform: translateY(-1px);
950 box-shadow: 0 4px 15px rgba(255,204,0,0.3);
951 color: #1a1a1a;
952 }
953 </style>
954 <script>
955 document.addEventListener('DOMContentLoaded', function() {
956 var premiumFeatures = <?php echo $features_json; ?>;
957 var upgradeUrl = '<?php echo esc_url( $upgrade_url ); ?>';
958
959 function applyPremiumGates() {
960 // Find all checkboxes and select elements
961 var inputs = document.querySelectorAll('input[type="checkbox"], select, textarea');
962
963 inputs.forEach(function(input) {
964 var id = input.id || '';
965 var name = input.name || '';
966
967 // Check if this input's ID matches a premium feature
968 premiumFeatures.forEach(function(feature) {
969 if (id.indexOf(feature) !== -1 || name.indexOf(feature) !== -1) {
970 // Disable the input
971 input.disabled = true;
972 input.checked = false;
973
974 // Find parent container and add overlay
975 var parent = input.closest('.form-group, div, tr');
976 if (parent && !parent.classList.contains('ezcache-premium-disabled')) {
977 parent.classList.add('ezcache-premium-disabled');
978 parent.classList.add('ezcache-premium-overlay');
979 }
980 }
981 });
982 });
983
984 // Add upgrade banner at the top of the main content
985 var mainContent = document.querySelector('.ezcache-main, .ezcache-options, .wrap');
986 if (mainContent && !document.querySelector('.ezcache-upgrade-banner')) {
987 var banner = document.createElement('div');
988 banner.className = 'ezcache-upgrade-banner';
989 banner.innerHTML = '<div class="upgrade-text">⚡ <strong>Upgrade to Pro</strong> to unlock CSS/JS optimization, WebP images, cache preloading, CDN, database cleanup and more — just <strong>$29/year</strong></div><a href="' + upgradeUrl + '" class="ezcache-upgrade-btn">🔓 Upgrade to Pro</a>';
990
991 var firstChild = mainContent.firstChild;
992 if (firstChild) {
993 mainContent.insertBefore(banner, firstChild);
994 } else {
995 mainContent.appendChild(banner);
996 }
997 }
998 }
999
1000 // Apply immediately and also watch for Vue rendering
1001 setTimeout(applyPremiumGates, 500);
1002 setTimeout(applyPremiumGates, 1500);
1003 setTimeout(applyPremiumGates, 3000);
1004
1005 // Also watch for DOM changes (Vue renders async)
1006 var observer = new MutationObserver(function() {
1007 setTimeout(applyPremiumGates, 100);
1008 });
1009 var target = document.querySelector('#ezcache-options, .ezcache-options, .wrap');
1010 if (target) {
1011 observer.observe(target, { childList: true, subtree: true });
1012 // Stop observing after 10 seconds to avoid performance issues
1013 setTimeout(function() { observer.disconnect(); }, 10000);
1014 }
1015 });
1016 </script>
1017 <?php
1018 }
1019 }
1020