| @@ -75,10 +75,11 @@ | ||
| 75 | 75 | public function ui_metadata(): array { |
| 76 | 76 | return array( |
| 77 | 77 | 'label' => __( 'Cloudflare', 'xspeed' ), |
| 78 | 78 | 'icon' => 'Cloud', |
| 79 | - 'description' => __( 'Connect a Cloudflare zone for automatic edge purging when xSpeed clears its cache, plus a dev-mode toggle.', 'xspeed' ), | |
| 79 | + 'description' => __( 'Clear the Cloudflare cache whenever xSpeed clears its own cache.', 'xspeed' ), | |
| 80 | 80 | 'custom_panel' => 'CloudflarePanel', |
| 81 | + 'group' => 'network', | |
| 81 | 82 | ); |
| 82 | 83 | } |
| 83 | 84 | |
| 84 | 85 | /** |
| @@ -98,10 +99,10 @@ | ||
| 98 | 99 | return array( |
| 99 | 100 | 'enabled' => array( |
| 100 | 101 | 'type' => 'bool', |
| 101 | 102 | 'default' => false, |
| 102 | - 'label' => __( 'Enable Cloudflare integration', 'xspeed' ), | |
| 103 | - 'description' => __( 'Use the credentials below to verify your zone and run purges.', 'xspeed' ), | |
| 103 | + 'label' => __( 'Connect Cloudflare', 'xspeed' ), | |
| 104 | + 'description' => __( 'Lets xSpeed clear the Cloudflare cache for your domain, using the details below.', 'xspeed' ), | |
| 104 | 105 | ), |
| 105 | 106 | 'auth_method' => array( |
| 106 | 107 | 'type' => 'enum', |
| 107 | 108 | 'default' => 'token', |
| @@ -110,9 +111,9 @@ | ||
| 110 | 111 | 'token' => 'API Token', |
| 111 | 112 | 'key' => 'Global API Key', |
| 112 | 113 | ), |
| 113 | 114 | 'label' => __( 'Authentication', 'xspeed' ), |
| 114 | - 'description' => __( 'API Tokens (scoped, recommended) or the legacy Global API Key with your account email.', 'xspeed' ), | |
| 115 | + 'description' => __( 'An API token is safer and recommended. The older Global API Key also needs your account email.', 'xspeed' ), | |
| 115 | 116 | 'dependsOn' => array( 'field' => 'enabled' ), |
| 116 | 117 | ), |
| 117 | 118 | 'api_token' => array( |
| 118 | 119 | 'type' => 'secret', |
| @@ -117,9 +118,9 @@ | ||
| 117 | 118 | 'api_token' => array( |
| 118 | 119 | 'type' => 'secret', |
| 119 | 120 | 'default' => '', |
| 120 | 121 | 'label' => __( 'API Token', 'xspeed' ), |
| 121 | - 'description' => __( 'Create a token at dash.cloudflare.com → My Profile → API Tokens. Needs "Zone → Cache Purge" + "Zone Settings" permissions.', 'xspeed' ), | |
| 122 | + 'description' => __( 'Create one at dash.cloudflare.com → My Profile → API Tokens. Give it the "Zone → Cache Purge" and "Zone Settings" permissions.', 'xspeed' ), | |
| 122 | 123 | // Only the token auth branch (and only while CF is enabled, via |
| 123 | 124 | // the transitive gate on auth_method → enabled). |
| 124 | 125 | 'dependsOn' => array( 'field' => 'auth_method', 'value' => 'token' ), |
| 125 | 126 | ), |
| @@ -125,10 +126,10 @@ | ||
| 125 | 126 | ), |
| 126 | 127 | 'email' => array( |
| 127 | 128 | 'type' => 'string', |
| 128 | 129 | 'default' => '', |
| 129 | - 'label' => __( 'Account Email', 'xspeed' ), | |
| 130 | - 'description' => __( 'Only used when Authentication is set to Global API Key.', 'xspeed' ), | |
| 130 | + 'label' => __( 'Account email', 'xspeed' ), | |
| 131 | + 'description' => __( 'The email address of your Cloudflare account.', 'xspeed' ), | |
| 131 | 132 | 'dependsOn' => array( 'field' => 'auth_method', 'value' => 'key' ), |
| 132 | 133 | ), |
| 133 | 134 | 'api_key' => array( |
| 134 | 135 | 'type' => 'secret', |
| @@ -133,9 +134,9 @@ | ||
| 133 | 134 | 'api_key' => array( |
| 134 | 135 | 'type' => 'secret', |
| 135 | 136 | 'default' => '', |
| 136 | 137 | 'label' => __( 'Global API Key', 'xspeed' ), |
| 137 | - 'description' => __( 'Found at dash.cloudflare.com → My Profile → API Tokens → Global API Key.', 'xspeed' ), | |
| 138 | + 'description' => __( 'Find it at dash.cloudflare.com → My Profile → API Tokens → Global API Key.', 'xspeed' ), | |
| 138 | 139 | 'dependsOn' => array( 'field' => 'auth_method', 'value' => 'key' ), |
| 139 | 140 | ), |
| 140 | 141 | 'zone_id' => array( |
| 141 | 142 | 'type' => 'string', |
| @@ -140,16 +141,16 @@ | ||
| 140 | 141 | 'zone_id' => array( |
| 141 | 142 | 'type' => 'string', |
| 142 | 143 | 'default' => '', |
| 143 | 144 | 'label' => __( 'Zone ID', 'xspeed' ), |
| 144 | - 'description' => __( 'The 32-character hex Zone ID from your domain overview page.', 'xspeed' ), | |
| 145 | + 'description' => __( 'The 32-character Zone ID shown on your domain overview page in Cloudflare.', 'xspeed' ), | |
| 145 | 146 | 'dependsOn' => array( 'field' => 'enabled' ), |
| 146 | 147 | ), |
| 147 | 148 | 'auto_purge_on_update' => array( |
| 148 | 149 | 'type' => 'bool', |
| 149 | 150 | 'default' => true, |
| 150 | - 'label' => __( 'Auto-purge Cloudflare on xSpeed purge', 'xspeed' ), | |
| 151 | - 'description' => __( 'When xSpeed clears its own cache (post save, settings change, manual purge), trigger a Cloudflare purge too.', 'xspeed' ), | |
| 151 | + 'label' => __( 'Clear Cloudflare with xSpeed', 'xspeed' ), | |
| 152 | + 'description' => __( 'Clear the Cloudflare cache each time xSpeed clears its own, for example after you save a post.', 'xspeed' ), | |
| 152 | 153 | 'dependsOn' => array( 'field' => 'enabled' ), |
| 153 | 154 | ), |
| 154 | 155 | ); |
| 155 | 156 | } |
| @@ -248,8 +249,14 @@ | ||
| 248 | 249 | $opts = $this->get_settings(); |
| 249 | 250 | if ( empty( $opts['enabled'] ) ) { |
| 250 | 251 | return; |
| 251 | 252 | } |
| 253 | + // Something else on the site is already the Cloudflare layer in front | |
| 254 | + // of it. The switch stays as the owner left it, and this zone is not | |
| 255 | + // purged while the block lasts. See Module::blocked_by(). | |
| 256 | + if ( null !== $this->blocked_by() ) { | |
| 257 | + return; | |
| 258 | + } | |
| 252 | 259 | if ( ! empty( $opts['auto_purge_on_update'] ) ) { |
| 253 | 260 | // xSpeed fires this action whenever it purges its own |
| 254 | 261 | // cache (see Cache::purge_all). Listening here keeps |
| 255 | 262 | // CF in sync without any new wiring elsewhere. |
| @@ -284,11 +291,12 @@ | ||
| 284 | 291 | |
| 285 | 292 | /** |
| 286 | 293 | * Mirror a single-URL purge at the edge. |
| 287 | 294 | * |
| 288 | - * NOT about post edits — `on_save_post()` calls `purge_all()`, so those | |
| 289 | - * have always reached Cloudflare through the full-purge listener above. | |
| 290 | - * What reaches `purge_url()` is the narrower set: the two admin purge | |
| 295 | + * Post edits arrive here too when they clear only their affected pages | |
| 296 | + * (`Cache::purge_urls()` publishes one event with every URL); an edit | |
| 297 | + * that clears the whole site comes through the full-purge listener | |
| 298 | + * above. What else reaches `purge_url()` is the narrower set: the two admin purge | |
| 291 | 299 | * buttons, an approved comment, a user change, a WooCommerce product or |
| 292 | 300 | * stock change, `--url` on the CLI and REST, and MCP. Every one of those |
| 293 | 301 | * cleared xSpeed's copy and left Cloudflare's, so the page stayed stale |
| 294 | 302 | * at the edge until its lifetime ran out or somebody pressed Purge All — |
| @@ -320,11 +328,11 @@ | ||
| 320 | 328 | if ( true !== $this->can_purge_edge() ) { |
| 321 | 329 | return; |
| 322 | 330 | } |
| 323 | 331 | |
| 324 | - // Collected and sent once, not one API call per URL. `Purge_Ui`'s | |
| 325 | - // post purge and the WooCommerce product path both fire a handful of | |
| 326 | - // these in a loop, and a round trip each would be a wait each. | |
| 332 | + // Collected and sent once, not one API call per URL. A request can | |
| 333 | + // raise several of these (a bulk edit, a stock change on each item | |
| 334 | + // of an order), and a round trip each would be a wait each. | |
| 327 | 335 | if ( array() === $this->pending_edge_urls ) { |
| 328 | 336 | add_action( 'shutdown', array( $this, 'flush_edge_url_purges' ), 20 ); |
| 329 | 337 | } |
| 330 | 338 | $blog = function_exists( 'get_current_blog_id' ) ? (int) get_current_blog_id() : 0; |
| @@ -330,8 +338,9 @@ | ||
| 330 | 338 | $blog = function_exists( 'get_current_blog_id' ) ? (int) get_current_blog_id() : 0; |
| 331 | 339 | foreach ( $urls as $url ) { |
| 332 | 340 | $this->pending_edge_urls[ $blog ][ $url ] = true; |
| 333 | 341 | } |
| 342 | + \XSpeed\Cache::note_purge_forwarded( 'Cloudflare' ); | |
| 334 | 343 | } |
| 335 | 344 | |
| 336 | 345 | /** |
| 337 | 346 | * Hand whatever `on_xspeed_purge_url()` collected to cron. |
| @@ -604,8 +613,12 @@ | ||
| 604 | 613 | $opts = $this->get_settings(); |
| 605 | 614 | if ( empty( $opts['enabled'] ) ) { |
| 606 | 615 | return __( 'the Cloudflare integration is switched off', 'xspeed' ); |
| 607 | 616 | } |
| 617 | + $blocked = $this->blocked_by(); | |
| 618 | + if ( null !== $blocked ) { | |
| 619 | + return $blocked; | |
| 620 | + } | |
| 608 | 621 | if ( ! $this->has_credentials( $opts ) ) { |
| 609 | 622 | return __( 'no zone ID or API credentials are configured', 'xspeed' ); |
| 610 | 623 | } |
| 611 | 624 | |
| @@ -673,17 +686,19 @@ | ||
| 673 | 686 | * Persist any settings sent with the save, then verify the credentials |
| 674 | 687 | * immediately so an invalid or newly-changed token surfaces on the panel |
| 675 | 688 | * instead of failing silently the next time xSpeed purges. Response shape |
| 676 | 689 | * is unchanged (flat settings) so the autosave client is unaffected. (#119) |
| 690 | + * | |
| 691 | + * The parent writes the settings, so its licence, blocked-by and | |
| 692 | + * wp-config refusals apply here too; a refused write is not verified. | |
| 677 | 693 | */ |
| 678 | 694 | public function rest_update_settings( \WP_REST_Request $request ) { |
| 679 | - $params = $request->get_json_params(); | |
| 680 | - if ( ! is_array( $params ) ) { | |
| 681 | - $params = $request->get_params(); | |
| 695 | + $response = parent::rest_update_settings( $request ); | |
| 696 | + if ( is_wp_error( $response ) ) { | |
| 697 | + return $response; | |
| 682 | 698 | } |
| 683 | - $settings = $this->update_settings( is_array( $params ) ? $params : array() ); | |
| 684 | 699 | $this->verify_and_record(); |
| 685 | - return rest_ensure_response( $settings ); | |
| 700 | + return $response; | |
| 686 | 701 | } |
| 687 | 702 | |
| 688 | 703 | public function rest_verify( \WP_REST_Request $request ) { |
| 689 | 704 | $res = Cloudflare::verify( $this->get_settings() ); |