PluginProbe
Search Atlas SEO – OTTO AI SEO Automation for WordPress / 2.6.16
Search Atlas SEO – OTTO AI SEO Automation for WordPress v2.6.16
2.6.26 2.6.25 2.6.24 2.6.23 2.6.22 2.6.21 2.6.20 2.6.19 2.6.18 2.6.17 2.6.16 2.6.15 2.6.14 2.6.13 2.6.12 2.6.11 2.6.10 2.6.9 2.6.8 2.6.7 2.6.6 2.6.5 2.6.4 2.6.3 2.5.23 All 138 releases
metasync / admin / class-metasync-edge-cache-settings.php

class-metasync-edge-cache-settings.php in Search Atlas SEO – OTTO AI SEO Automation for WordPress 2.6.16, at admin/class-metasync-edge-cache-settings.php

307 lines 15.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 // If this file is called directly, abort.
3 if (!defined('ABSPATH')) {
4 exit;
5 }
6
7 /**
8 * Edge Cache / CDN Settings for MetaSync Plugin
9 *
10 * Manages CDN credentials and cache-tag header configuration.
11 * Extracted from the main admin class to reduce file size.
12 *
13 * @package Metasync
14 * @subpackage Metasync/admin
15 */
16 class Metasync_Edge_Cache_Settings {
17
18 /**
19 * Option key for storing edge cache settings.
20 */
21 const OPTION_KEY = 'metasync_edge_cache_options';
22
23 /**
24 * Nonce action for edge cache settings.
25 */
26 const NONCE_ACTION = 'metasync_edge_cache_nonce';
27
28 /**
29 * Nonce field name.
30 */
31 const NONCE_FIELD = 'edge_cache_nonce';
32
33 /**
34 * Default settings.
35 *
36 * @return array
37 */
38 public static function get_defaults() {
39 return array(
40 'cache_tags_enabled' => true,
41 'cloudflare_enabled' => false,
42 'cloudflare_zone_id' => '',
43 'cloudflare_api_token' => '',
44 'sucuri_enabled' => false,
45 'sucuri_api_key' => '',
46 'sucuri_api_secret' => '',
47 'fastly_enabled' => false,
48 'fastly_service_id' => '',
49 'fastly_api_token' => '',
50 'akamai_enabled' => false,
51 'akamai_client_token' => '',
52 'akamai_access_token' => '',
53 'akamai_client_secret' => '',
54 'akamai_host' => '',
55 'sevalla_enabled' => false,
56 'sevalla_api_key' => '',
57 'sevalla_application_id' => '',
58 'cloudways_enabled' => false,
59 'flywheel_enabled' => false,
60 );
61 }
62
63 /**
64 * Get all settings merged with defaults.
65 *
66 * @return array
67 */
68 public static function get_settings() {
69 return wp_parse_args(
70 get_option(self::OPTION_KEY, array()),
71 self::get_defaults()
72 );
73 }
74
75 /**
76 * All setting keys that hold boolean toggles.
77 *
78 * @return array
79 */
80 private static function get_bool_keys() {
81 return array(
82 'cache_tags_enabled',
83 'cloudflare_enabled',
84 'sucuri_enabled',
85 'fastly_enabled',
86 'akamai_enabled',
87 'sevalla_enabled',
88 'cloudways_enabled',
89 'flywheel_enabled',
90 );
91 }
92
93 /**
94 * Sanitize and save settings from $_POST.
95 *
96 * @param array $post POST data (defaults to $_POST).
97 * @return bool True if saved.
98 */
99 public static function save_from_post($post = null) {
100 if ($post === null) {
101 $post = $_POST;
102 }
103
104 // Only save if at least one edge-cache field is present in the payload.
105 if (!isset($post['cache_tags_enabled'])) {
106 return false;
107 }
108
109 $bool_keys = self::get_bool_keys();
110 $defaults = self::get_defaults();
111 $settings = array();
112
113 foreach ($defaults as $key => $default) {
114 if (in_array($key, $bool_keys, true)) {
115 $settings[$key] = !empty($post[$key]) && $post[$key] === '1';
116 } else {
117 $settings[$key] = sanitize_text_field($post[$key] ?? '');
118 }
119 }
120
121 update_option(self::OPTION_KEY, $settings);
122 return true;
123 }
124
125 /**
126 * AJAX handler: save edge cache settings (standalone endpoint).
127 */
128 public static function ajax_save() {
129 if (!isset($_POST[self::NONCE_FIELD]) || !wp_verify_nonce($_POST[self::NONCE_FIELD], self::NONCE_ACTION)) {
130 wp_send_json_error(array('message' => 'Security check failed'));
131 return;
132 }
133
134 if (!Metasync::current_user_has_plugin_access()) {
135 wp_send_json_error(array('message' => 'Insufficient permissions'));
136 return;
137 }
138
139 self::save_from_post();
140 wp_send_json_success(array('message' => 'Edge cache settings saved'));
141 }
142
143 /**
144 * Render the Edge Cache / CDN settings section.
145 */
146 public static function render() {
147 $settings = self::get_settings();
148 $show_sevalla = !class_exists('KinstaCache');
149 $show_cloudways = !empty($_SERVER['HTTP_X_VARNISH']) || !empty(get_option('metasync_cloudways_detected'));
150 $show_flywheel = defined('FLYWHEEL_CONFIG_DIR');
151 ?>
152 <div style="background: var(--dashboard-card-bg); padding: 20px; border-radius: 8px;">
153 <p style="color: var(--dashboard-text-secondary); margin: 0 0 20px 0;">
154 Configure CDN credentials for cache-tag purging. When enabled, cache-tag headers are added to <?php echo esc_html(Metasync::get_whitelabel_otto_name()); ?>-processed pages so CDNs can purge by post ID.
155 </p>
156
157 <?php wp_nonce_field(self::NONCE_ACTION, self::NONCE_FIELD); ?>
158
159 <!-- Cache-Tag Headers Master Toggle -->
160 <div style="background: var(--dashboard-card-bg-alt, rgba(255,255,255,0.05)); border: 1px solid var(--dashboard-border); border-radius: 8px; padding: 20px; margin-bottom: 20px;">
161 <h3 style="color: var(--dashboard-text-primary); margin: 0 0 14px 0; font-size: 16px; font-weight: 600;">Cache-Tag Headers</h3>
162 <label style="display: flex; align-items: center; gap: 10px; margin-bottom: 12px; cursor: pointer;">
163 <input type="hidden" name="cache_tags_enabled" value="0" />
164 <input type="checkbox" name="cache_tags_enabled" value="1" <?php checked($settings['cache_tags_enabled']); ?> style="width: 16px; height: 16px; cursor: pointer;" />
165 <span style="color: var(--dashboard-text-primary); font-weight: 500;">Enable Cache-Tag Response Headers</span>
166 <span style="color: var(--dashboard-text-secondary); font-size: 12px;"> adds purge-by-tag support for CDNs</span>
167 </label>
168 <div style="background: rgba(59, 130, 246, 0.1); border: 1px solid rgba(59, 130, 246, 0.3); border-radius: 8px; padding: 16px;">
169 <p style="color: var(--dashboard-text-secondary); margin: 0; font-size: 13px;">
170 <strong style="color: var(--dashboard-info, #3b82f6);">How it works:</strong><br>
171 Adds <code style="background: rgba(255,255,255,0.1); padding: 2px 6px; border-radius: 3px;">Cache-Tag</code>,
172 <code style="background: rgba(255,255,255,0.1); padding: 2px 6px; border-radius: 3px;">Surrogate-Key</code>, and
173 <code style="background: rgba(255,255,255,0.1); padding: 2px 6px; border-radius: 3px;">Edge-Cache-Tag</code>
174 headers to singular pages. These allow Cloudflare, Fastly, Akamai, and other CDNs to purge individual posts by tag instead of flushing the entire cache.
175 </p>
176 </div>
177 </div>
178
179 <!-- CDN Providers -->
180 <div style="background: rgba(255,255,255,0.02); border: 1px solid var(--dashboard-border); border-radius: 8px; padding: 20px;">
181 <h5 style="margin: 0 0 14px 0; color: var(--dashboard-text-primary);">CDN Provider Credentials</h5>
182
183 <?php
184 self::render_provider('cloudflare', 'Cloudflare', 'purges via Cache-Tag API', $settings, array(
185 array('name' => 'cloudflare_zone_id', 'label' => 'Zone ID', 'type' => 'text', 'placeholder' => 'e.g. 023e105f4ecef8ad9ca31a8372d0c353', 'hint' => 'Found in Cloudflare dashboard &rarr; Overview &rarr; Zone ID'),
186 array('name' => 'cloudflare_api_token', 'label' => 'API Token', 'type' => 'password', 'hint' => 'Requires <strong>Cache Purge</strong> permission on the zone'),
187 ));
188
189 self::render_provider('sucuri', 'Sucuri WAF', 'purges Sucuri firewall cache', $settings, array(
190 array('name' => 'sucuri_api_key', 'label' => 'API Key', 'type' => 'password', 'hint' => 'Found in Sucuri dashboard &rarr; Settings &rarr; API'),
191 array('name' => 'sucuri_api_secret', 'label' => 'API Secret', 'type' => 'password'),
192 ));
193
194 self::render_provider('fastly', 'Fastly', 'purges via Surrogate-Key', $settings, array(
195 array('name' => 'fastly_service_id', 'label' => 'Service ID', 'type' => 'text', 'hint' => 'Found in Fastly dashboard &rarr; Service details'),
196 array('name' => 'fastly_api_token', 'label' => 'API Token', 'type' => 'password', 'hint' => 'Requires <strong>Purge all</strong> or <strong>Purge by key</strong> scope'),
197 ));
198
199 $has_more_after_akamai = $show_sevalla || $show_cloudways || $show_flywheel;
200 self::render_provider('akamai', 'Akamai', 'purges via Edge-Cache-Tag', $settings, array(
201 array('name' => 'akamai_client_token', 'label' => 'Client Token', 'type' => 'password'),
202 array('name' => 'akamai_access_token', 'label' => 'Access Token', 'type' => 'password'),
203 array('name' => 'akamai_client_secret', 'label' => 'Client Secret', 'type' => 'password'),
204 array('name' => 'akamai_host', 'label' => 'Host', 'type' => 'text', 'placeholder' => 'e.g. akab-xxxxx.purge.akamaiapis.net', 'hint' => 'Found in Akamai Control Center &rarr; Identity &amp; Access &rarr; API credentials'),
205 ), !$has_more_after_akamai);
206
207 if ($show_sevalla) {
208 $has_more_after_sevalla = $show_cloudways || $show_flywheel;
209 self::render_provider('sevalla', 'Kinsta / Sevalla', 'purges edge cache via v3 API (v2 fallback)', $settings, array(
210 array('name' => 'sevalla_api_key', 'label' => 'Sevalla API Key', 'type' => 'password', 'hint' => 'Found in Sevalla dashboard &rarr; Company &rarr; API Keys'),
211 array('name' => 'sevalla_application_id', 'label' => 'Application ID', 'type' => 'text', 'placeholder' => 'e.g. fb5e5168-4281-4bec-94c5-0d1584e9e657', 'hint' => 'UUID format — found in the site URL: <code style="background: rgba(255,255,255,0.1); padding: 2px 6px; border-radius: 3px;">my.sevalla.com/applications/<strong>{UUID}</strong></code>'),
212 ), !$has_more_after_sevalla, 'Uses v3 edge cache purge API with v2 fallback. If KinstaCache mu-plugin is available, native purge is used instead.');
213 }
214
215 if ($show_cloudways) {
216 self::render_provider('cloudways', 'Cloudways', 'purges Varnish cache per URL', $settings, array(), !$show_flywheel, sprintf('Detected Cloudways Varnish. Enable to purge Varnish on each %s update.', Metasync::get_whitelabel_otto_name()));
217 }
218
219 if ($show_flywheel) {
220 self::render_provider('flywheel', 'Flywheel', sprintf('full cache flush on %s updates', Metasync::get_whitelabel_otto_name()), $settings, array(), true, sprintf('Detected Flywheel hosting. Enable to clear cache on each %s update.', Metasync::get_whitelabel_otto_name()));
221 }
222 ?>
223 </div>
224 </div>
225
226 <script>
227 jQuery(document).ready(function($) {
228 // Toggle credential fields visibility when provider checkbox changes
229 var providers = ['cloudflare', 'sucuri', 'fastly', 'akamai', 'sevalla', 'cloudways', 'flywheel'];
230 $.each(providers, function(_, name) {
231 var $checkbox = $('#metasync-ec-' + name);
232 var $fields = $('.metasync-ec-' + name + '-fields');
233 if ($checkbox.length && $fields.length) {
234 $checkbox.on('change', function() {
235 if ($(this).is(':checked')) {
236 $fields.slideDown(200);
237 } else {
238 $fields.slideUp(200);
239 }
240 });
241 }
242 });
243 });
244 </script>
245 <?php
246 }
247
248 /**
249 * Render a single CDN provider block.
250 *
251 * @param string $key Provider key (e.g. 'cloudflare').
252 * @param string $title Display title.
253 * @param string $description Short dash-prefixed description.
254 * @param array $settings Current settings.
255 * @param array $fields Field definitions.
256 * @param bool $is_last Whether this is the last provider (no bottom border).
257 * @param string $notice Optional warning notice text.
258 */
259 private static function render_provider($key, $title, $description, $settings, $fields, $is_last = false, $notice = '') {
260 $enabled_key = $key . '_enabled';
261 $is_enabled = !empty($settings[$enabled_key]);
262 $border = $is_last ? '' : 'border-bottom: 1px solid var(--dashboard-border); padding-bottom: 20px; margin-bottom: 20px;';
263 ?>
264 <div class="metasync-edge-provider" style="<?php echo $border; ?>">
265 <label style="display: flex; align-items: center; gap: 10px; margin-bottom: 12px; cursor: pointer;">
266 <input type="hidden" name="<?php echo esc_attr($enabled_key); ?>" value="0" />
267 <input type="checkbox"
268 id="metasync-ec-<?php echo esc_attr($key); ?>"
269 name="<?php echo esc_attr($enabled_key); ?>"
270 value="1"
271 <?php checked($is_enabled); ?>
272 style="width: 16px; height: 16px; cursor: pointer;" />
273 <span style="color: var(--dashboard-text-primary); font-weight: 500;"><?php echo esc_html($title); ?></span>
274 <span style="color: var(--dashboard-text-secondary); font-size: 12px;"> <?php echo esc_html($description); ?></span>
275 </label>
276 <div class="metasync-ec-<?php echo esc_attr($key); ?>-fields" style="margin-left: 26px; <?php echo $is_enabled ? '' : 'display: none;'; ?>">
277 <?php if (!empty($notice)) : ?>
278 <div style="background: rgba(255, 193, 7, 0.1); border-left: 4px solid #ffc107; padding: 12px; border-radius: 4px; margin-bottom: 16px;">
279 <span style="color: var(--dashboard-text-secondary); font-size: 12px;"><?php echo esc_html($notice); ?></span>
280 </div>
281 <?php endif; ?>
282
283 <div style="display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 16px;">
284 <?php foreach ($fields as $field) : ?>
285 <div>
286 <label style="display: block; color: var(--dashboard-text-primary); margin-bottom: 8px; font-weight: 500;">
287 <?php echo esc_html($field['label']); ?>
288 </label>
289 <input type="<?php echo esc_attr($field['type']); ?>"
290 name="<?php echo esc_attr($field['name']); ?>"
291 value="<?php echo esc_attr($settings[$field['name']]); ?>"
292 <?php if (!empty($field['placeholder'])) : ?>placeholder="<?php echo esc_attr($field['placeholder']); ?>"<?php endif; ?>
293 style="width: 100%; padding: 8px; border: 1px solid var(--dashboard-border); border-radius: 6px; background: var(--dashboard-card-bg); color: var(--dashboard-text-primary); box-sizing: border-box;" />
294 <?php if (!empty($field['hint'])) : ?>
295 <p style="color: var(--dashboard-text-secondary); font-size: 12px; margin: 4px 0 0 0;">
296 <?php echo wp_kses($field['hint'], array('strong' => array(), 'code' => array('style' => array()))); ?>
297 </p>
298 <?php endif; ?>
299 </div>
300 <?php endforeach; ?>
301 </div>
302 </div>
303 </div>
304 <?php
305 }
306 }
307