PluginProbe
xSpeed Cache: AI-Powered Performance Hub with MCP, Caching & CDN / 1.3.2
xSpeed Cache: AI-Powered Performance Hub with MCP, Caching & CDN v1.3.2
1.3.2 1.3.1 1.3.0 1.2.4 trunk 1.0.0 1.0.1 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.0.7 1.0.8 1.0.9 1.1.0 1.1.1 1.1.2 1.1.3 1.1.4 1.1.5 1.1.6 1.1.7 1.1.8 1.2.0 All 28 releases
xspeed / includes / class-cache-meta-box.php

class-cache-meta-box.php in xSpeed Cache: AI-Powered Performance Hub with MCP, Caching & CDN 1.3.2, at includes/class-cache-meta-box.php

167 lines 5.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Per-post cache rules meta box (Phase 3.4).
4 *
5 * Renders in the classic editor sidebar and (with WP 5.0+'s legacy
6 * meta-box compatibility) in the block editor's bottom panel. The
7 * Gutenberg-native sidebar UI is a separate enhancement; this file
8 * ships the read/write surface so power users get the feature today
9 * regardless of editor choice.
10 *
11 * The postmetas themselves are registered (with show_in_rest) by
12 * Cache_Rules::register_post_meta — letting any REST consumer
13 * (Gutenberg, automation, an external CMS) read + write the rules
14 * without going through this meta box.
15 *
16 * @package XSpeed
17 */
18
19 declare(strict_types=1);
20
21 namespace XSpeed;
22
23 defined( 'ABSPATH' ) || exit;
24
25 final class Cache_Meta_Box {
26
27 public const NONCE_ACTION = 'xspeed_per_post_rules';
28 public const NONCE_FIELD = 'xspeed_per_post_rules_nonce';
29
30 public static function boot(): void {
31 add_action( 'init', array( Cache_Rules::class, 'register_post_meta' ) );
32 add_action( 'add_meta_boxes', array( __CLASS__, 'register_meta_box' ) );
33 add_action( 'save_post', array( __CLASS__, 'handle_save' ), 10, 2 );
34 }
35
36 public static function register_meta_box(): void {
37 foreach ( Cache_Rules::supported_post_types() as $type ) {
38 add_meta_box(
39 'xspeed-cache-rules',
40 __( 'xSpeed · Cache Rules', 'xspeed' ),
41 array( __CLASS__, 'render' ),
42 $type,
43 'side',
44 'default'
45 );
46 }
47 }
48
49 public static function render( \WP_Post $post ): void {
50 wp_nonce_field( self::NONCE_ACTION, self::NONCE_FIELD );
51
52 $no_cache = (bool) get_post_meta( $post->ID, Cache_Rules::META_NO_CACHE, true );
53 $expiry = (int) get_post_meta( $post->ID, Cache_Rules::META_EXPIRY_HOURS, true );
54 ?>
55 <p>
56 <label>
57 <input
58 type="checkbox"
59 name="xspeed_no_cache"
60 value="1"
61 <?php checked( $no_cache ); ?>
62 />
63 <?php esc_html_e( 'Never cache this page', 'xspeed' ); ?>
64 </label>
65 </p>
66 <p style="font-size:12px;color:#666;margin:-8px 0 12px;">
67 <?php esc_html_e( 'Skip the page cache entirely for this post. Useful for highly dynamic pages.', 'xspeed' ); ?>
68 </p>
69
70 <p>
71 <label for="xspeed_expiry_hours">
72 <?php esc_html_e( 'Custom cache lifetime', 'xspeed' ); ?>
73 </label>
74 <br />
75 <input
76 type="number"
77 id="xspeed_expiry_hours"
78 name="xspeed_expiry_hours"
79 min="0"
80 max="720"
81 value="<?php echo esc_attr( (string) max( 0, $expiry ) ); ?>"
82 style="width:80px;"
83 />
84 <span style="font-size:12px;color:#666;">
85 <?php esc_html_e( 'hours · 0 = use global', 'xspeed' ); ?>
86 </span>
87 </p>
88 <p style="font-size:12px;color:#666;margin:-4px 0 0;">
89 <?php esc_html_e( 'Override the global cache expiry for this post (1–720 hours). Leave at 0 to inherit the site default.', 'xspeed' ); ?>
90 </p>
91 <?php
92 self::render_purge_action( $post );
93 }
94
95 /**
96 * "Purge this post's cache" under the rules.
97 *
98 * Shown only for a published, publicly viewable post — a draft has no
99 * cached page to clear, so the button could only ever report that it
100 * removed nothing. It leaves the editor rather than submitting the post
101 * form, so an unsaved draft is never silently published by a purge.
102 */
103 private static function render_purge_action( \WP_Post $post ): void {
104 if ( ! Purge_Ui::user_can_purge() ) {
105 return;
106 }
107 if ( '' === Purge_Ui::permalink_of( (int) $post->ID ) ) {
108 return;
109 }
110 ?>
111 <p style="margin:14px 0 0;padding-top:12px;border-top:1px solid #dcdcde;">
112 <a href="<?php echo esc_url( Purge_Ui::post_purge_link( (int) $post->ID ) ); ?>" class="button button-secondary">
113 <?php esc_html_e( 'Purge this post’s cache', 'xspeed' ); ?>
114 </a>
115 </p>
116 <p style="font-size:12px;color:#666;margin:6px 0 0;">
117 <?php esc_html_e( 'Clears this post and the pages that list it — the homepage, its archive and the neighbouring posts. The rest of the site keeps its cache.', 'xspeed' ); ?>
118 </p>
119 <?php
120 }
121
122 /**
123 * @param int $post_id
124 * @param \WP_Post $post
125 */
126 public static function handle_save( $post_id, $post ): void {
127 // Skip the noise paths first — autosaves, revisions, ajax,
128 // REST writes (REST has its own auth via register_post_meta).
129 if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
130 return;
131 }
132 if ( wp_is_post_revision( $post_id ) ) {
133 return;
134 }
135 if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) {
136 return; // block editor goes through register_post_meta + the REST auth_callback.
137 }
138 if ( ! isset( $_POST[ self::NONCE_FIELD ] ) ) {
139 return; // meta box wasn't rendered on this save (quick edit, bulk edit, etc.).
140 }
141 if ( ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST[ self::NONCE_FIELD ] ) ), self::NONCE_ACTION ) ) {
142 return;
143 }
144 if ( ! current_user_can( 'edit_post', $post_id ) ) {
145 return;
146 }
147 if ( ! in_array( $post->post_type, Cache_Rules::supported_post_types(), true ) ) {
148 return;
149 }
150
151 $no_cache = ! empty( $_POST['xspeed_no_cache'] );
152 if ( $no_cache ) {
153 update_post_meta( $post_id, Cache_Rules::META_NO_CACHE, true );
154 } else {
155 delete_post_meta( $post_id, Cache_Rules::META_NO_CACHE );
156 }
157
158 $expiry = isset( $_POST['xspeed_expiry_hours'] ) ? (int) $_POST['xspeed_expiry_hours'] : 0;
159 $expiry = max( 0, min( 720, $expiry ) );
160 if ( $expiry > 0 ) {
161 update_post_meta( $post_id, Cache_Rules::META_EXPIRY_HOURS, $expiry );
162 } else {
163 delete_post_meta( $post_id, Cache_Rules::META_EXPIRY_HOURS );
164 }
165 }
166 }
167