("/wp-json/wp/v2/posts", {
430 | _embed: true,
431 | per_page: perPage,
432 | page,
433 | author: authorId,
434 | });
435 | }
436 |
437 | export { WordPressAPIError };
438 |
--------------------------------------------------------------------------------
/wordpress/next-revalidate/next-revalidate.php:
--------------------------------------------------------------------------------
1 | option_name, $this->option_name, [
48 | 'sanitize_callback' => [$this, 'sanitize_settings']
49 | ]);
50 |
51 | add_settings_section(
52 | 'next_revalidate_main',
53 | 'Configuration',
54 | null,
55 | 'next-revalidate'
56 | );
57 |
58 | add_settings_field('nextjs_url', 'Next.js Site URL', [$this, 'field_nextjs_url'], 'next-revalidate', 'next_revalidate_main');
59 | add_settings_field('webhook_secret', 'Webhook Secret', [$this, 'field_webhook_secret'], 'next-revalidate', 'next_revalidate_main');
60 | add_settings_field('cooldown', 'Cooldown (seconds)', [$this, 'field_cooldown'], 'next-revalidate', 'next_revalidate_main');
61 | add_settings_field('max_retries', 'Max Retries', [$this, 'field_max_retries'], 'next-revalidate', 'next_revalidate_main');
62 | add_settings_field('debug_mode', 'Debug Mode', [$this, 'field_debug_mode'], 'next-revalidate', 'next_revalidate_main');
63 | }
64 |
65 | public function sanitize_settings($input) {
66 | $sanitized = [];
67 | $sanitized['nextjs_url'] = esc_url_raw(rtrim($input['nextjs_url'] ?? '', '/'));
68 | $sanitized['webhook_secret'] = sanitize_text_field($input['webhook_secret'] ?? '');
69 | $sanitized['cooldown'] = absint($input['cooldown'] ?? 2);
70 | $sanitized['max_retries'] = min(10, max(0, absint($input['max_retries'] ?? 3)));
71 | $sanitized['debug_mode'] = !empty($input['debug_mode']);
72 | return $sanitized;
73 | }
74 |
75 | public function field_nextjs_url() {
76 | $options = get_option($this->option_name);
77 | $value = $options['nextjs_url'] ?? '';
78 | echo ' ';
79 | echo 'The URL of your Next.js application (without trailing slash)
';
80 | }
81 |
82 | public function field_webhook_secret() {
83 | $options = get_option($this->option_name);
84 | $value = $options['webhook_secret'] ?? '';
85 | echo ' ';
86 | echo 'Must match WORDPRESS_WEBHOOK_SECRET in your Next.js environment
';
87 | }
88 |
89 | public function field_cooldown() {
90 | $options = get_option($this->option_name);
91 | $value = $options['cooldown'] ?? 2;
92 | echo ' seconds';
93 | echo 'Minimum time between revalidation requests (prevents spam)
';
94 | }
95 |
96 | public function field_max_retries() {
97 | $options = get_option($this->option_name);
98 | $value = $options['max_retries'] ?? 3;
99 | echo ' ';
100 | echo 'Number of retry attempts for failed requests (with exponential backoff)
';
101 | }
102 |
103 | public function field_debug_mode() {
104 | $options = get_option($this->option_name);
105 | $checked = !empty($options['debug_mode']) ? 'checked' : '';
106 | echo ' Enable debug logging ';
107 | echo 'Logs detailed request/response info to PHP error log
';
108 | }
109 |
110 | public function settings_page() {
111 | if (!current_user_can('manage_options')) {
112 | return;
113 | }
114 |
115 | $last = get_option($this->last_option);
116 | $log = get_option($this->log_option, []);
117 | ?>
118 |
119 |
Next.js Revalidation Settings
120 |
121 |
122 |
123 |
124 | Last revalidation:
125 | —
126 | Type: —
127 | Status:
128 |
129 | — Error:
130 |
131 |
132 | — HTTP
133 |
134 |
135 |
136 |
137 |
138 |
145 |
146 |
147 |
Test Connection
148 |
149 | Send Test Request
150 |
151 |
152 |
153 |
154 |
Recent Activity
155 |
156 |
157 | Clear Log
158 |
159 |
160 |
161 |
162 |
163 | Time
164 | Type
165 | Action
166 | Status
167 | HTTP
168 | Retries
169 | Details
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 | ✓' : '✗ '; ?>
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 | -
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
No revalidation requests yet.
196 |
197 |
198 |
246 |
247 | post_status !== 'publish') {
256 | return;
257 | }
258 |
259 | $this->trigger_revalidation('post', [
260 | 'id' => $post_id,
261 | 'slug' => $post->post_name,
262 | 'type' => $post->post_type,
263 | 'action' => $update ? 'update' : 'create'
264 | ]);
265 | }
266 |
267 | public function on_post_delete($post_id) {
268 | $post = get_post($post_id);
269 | if (!$post || $post->post_status !== 'publish') {
270 | return;
271 | }
272 |
273 | $this->trigger_revalidation('post', [
274 | 'id' => $post_id,
275 | 'slug' => $post->post_name,
276 | 'type' => $post->post_type,
277 | 'action' => 'delete'
278 | ]);
279 | }
280 |
281 | public function on_status_change($new_status, $old_status, $post) {
282 | if ($new_status === $old_status) {
283 | return;
284 | }
285 |
286 | if ($old_status === 'publish' || $new_status === 'publish') {
287 | $this->trigger_revalidation('post', [
288 | 'id' => $post->ID,
289 | 'slug' => $post->post_name,
290 | 'type' => $post->post_type,
291 | 'action' => 'status_change',
292 | 'old_status' => $old_status,
293 | 'new_status' => $new_status
294 | ]);
295 | }
296 | }
297 |
298 | public function on_term_change($term_id, $tt_id, $taxonomy) {
299 | $term = get_term($term_id, $taxonomy);
300 | $this->trigger_revalidation('term', [
301 | 'id' => $term_id,
302 | 'slug' => $term ? $term->slug : '',
303 | 'taxonomy' => $taxonomy,
304 | 'action' => current_action()
305 | ]);
306 | }
307 |
308 | private function trigger_revalidation($type, $data) {
309 | $options = get_option($this->option_name);
310 |
311 | if (empty($options['nextjs_url'])) {
312 | return;
313 | }
314 |
315 | // Check cooldown
316 | $cooldown = $options['cooldown'] ?? 2;
317 | $last = get_option($this->last_option);
318 | if ($last && (time() - $last['time']) < $cooldown) {
319 | $this->debug_log('Skipped: cooldown active');
320 | return;
321 | }
322 |
323 | $max_retries = $options['max_retries'] ?? 3;
324 | $result = $this->send_with_retry($type, $data, $max_retries);
325 |
326 | // Update last status
327 | update_option($this->last_option, [
328 | 'time' => time(),
329 | 'type' => $type,
330 | 'success' => $result['success'],
331 | 'http_code' => $result['http_code'],
332 | 'error' => $result['error']
333 | ]);
334 |
335 | // Add to log
336 | $this->add_log_entry([
337 | 'time' => time(),
338 | 'type' => $type,
339 | 'data' => $data,
340 | 'success' => $result['success'],
341 | 'http_code' => $result['http_code'],
342 | 'error' => $result['error'],
343 | 'retries' => $result['retries']
344 | ]);
345 | }
346 |
347 | private function send_with_retry($type, $data, $max_retries, $attempt = 0) {
348 | $result = $this->send_request($type, $data);
349 |
350 | if (!$result['success'] && $attempt < $max_retries) {
351 | $delay = pow(2, $attempt); // Exponential backoff: 1, 2, 4, 8...
352 | $this->debug_log("Retry {$attempt}/{$max_retries} after {$delay}s delay");
353 | sleep($delay);
354 | return $this->send_with_retry($type, $data, $max_retries, $attempt + 1);
355 | }
356 |
357 | $result['retries'] = $attempt;
358 | return $result;
359 | }
360 |
361 | private function send_request($type, $data) {
362 | $options = get_option($this->option_name);
363 | $url = $options['nextjs_url'] . '/api/revalidate';
364 | $secret = $options['webhook_secret'] ?? '';
365 |
366 | $payload = [
367 | 'type' => $type,
368 | 'data' => $data,
369 | 'timestamp' => time()
370 | ];
371 |
372 | $this->debug_log("Sending request to {$url}", $payload);
373 |
374 | $response = wp_remote_post($url, [
375 | 'timeout' => 15,
376 | 'headers' => [
377 | 'Content-Type' => 'application/json',
378 | 'x-webhook-secret' => $secret
379 | ],
380 | 'body' => json_encode($payload)
381 | ]);
382 |
383 | if (is_wp_error($response)) {
384 | $error = $response->get_error_message();
385 | $this->debug_log("Request failed: {$error}");
386 | return [
387 | 'success' => false,
388 | 'http_code' => null,
389 | 'error' => $error
390 | ];
391 | }
392 |
393 | $http_code = wp_remote_retrieve_response_code($response);
394 | $body = wp_remote_retrieve_body($response);
395 | $success = $http_code === 200;
396 |
397 | $this->debug_log("Response: HTTP {$http_code}", $body);
398 |
399 | return [
400 | 'success' => $success,
401 | 'http_code' => $http_code,
402 | 'error' => $success ? null : "HTTP {$http_code}"
403 | ];
404 | }
405 |
406 | private function add_log_entry($entry) {
407 | $log = get_option($this->log_option, []);
408 | array_unshift($log, $entry);
409 | $log = array_slice($log, 0, 50); // Keep last 50
410 | update_option($this->log_option, $log);
411 | }
412 |
413 | private function debug_log($message, $data = null) {
414 | $options = get_option($this->option_name);
415 | if (empty($options['debug_mode'])) {
416 | return;
417 | }
418 |
419 | $log_message = '[Next.js Revalidation] ' . $message;
420 | if ($data !== null) {
421 | $log_message .= ' - ' . (is_string($data) ? $data : json_encode($data));
422 | }
423 | error_log($log_message);
424 | }
425 | }
426 |
427 | // Initialize plugin
428 | add_action('init', function() {
429 | new NextRevalidate();
430 | });
431 |
432 | // AJAX handler for test button
433 | add_action('wp_ajax_next_revalidate_test', function() {
434 | if (!current_user_can('manage_options')) {
435 | wp_send_json_error('Unauthorized');
436 | }
437 |
438 | $options = get_option('next_revalidate_settings');
439 |
440 | if (empty($options['nextjs_url'])) {
441 | wp_send_json_error('Next.js URL not configured');
442 | }
443 |
444 | $url = $options['nextjs_url'] . '/api/revalidate';
445 | $secret = $options['webhook_secret'] ?? '';
446 |
447 | $response = wp_remote_post($url, [
448 | 'timeout' => 15,
449 | 'headers' => [
450 | 'Content-Type' => 'application/json',
451 | 'x-webhook-secret' => $secret
452 | ],
453 | 'body' => json_encode([
454 | 'type' => 'test',
455 | 'data' => ['message' => 'Test from WordPress'],
456 | 'timestamp' => time()
457 | ])
458 | ]);
459 |
460 | if (is_wp_error($response)) {
461 | wp_send_json_error($response->get_error_message());
462 | }
463 |
464 | $code = wp_remote_retrieve_response_code($response);
465 | if ($code !== 200) {
466 | wp_send_json_error('HTTP ' . $code . ' - ' . wp_remote_retrieve_body($response));
467 | }
468 |
469 | wp_send_json_success('Connection successful!');
470 | });
471 |
472 | // AJAX handler for clearing log
473 | add_action('wp_ajax_next_revalidate_clear_log', function() {
474 | if (!current_user_can('manage_options')) {
475 | wp_send_json_error('Unauthorized');
476 | }
477 |
478 | delete_option('next_revalidate_log');
479 | wp_send_json_success('Log cleared');
480 | });
481 |
--------------------------------------------------------------------------------
/plugin/next-revalidate/next-revalidate.php:
--------------------------------------------------------------------------------
1 | options = get_option($this->option_name);
68 | }
69 |
70 | public function register_settings() {
71 | register_setting(
72 | 'next_revalidation_group',
73 | $this->option_name,
74 | array($this, 'sanitize_settings')
75 | );
76 |
77 | add_settings_section(
78 | 'next_revalidation_section',
79 | 'Next.js Revalidation Settings',
80 | array($this, 'settings_section_callback'),
81 | 'next-revalidation-settings'
82 | );
83 |
84 | add_settings_field(
85 | 'next_url',
86 | 'Next.js Site URL',
87 | array($this, 'next_url_callback'),
88 | 'next-revalidation-settings',
89 | 'next_revalidation_section'
90 | );
91 |
92 | add_settings_field(
93 | 'webhook_secret',
94 | 'Webhook Secret',
95 | array($this, 'webhook_secret_callback'),
96 | 'next-revalidation-settings',
97 | 'next_revalidation_section'
98 | );
99 |
100 | add_settings_field(
101 | 'enable_notifications',
102 | 'Enable Notifications',
103 | array($this, 'enable_notifications_callback'),
104 | 'next-revalidation-settings',
105 | 'next_revalidation_section'
106 | );
107 |
108 | add_settings_field(
109 | 'revalidation_cooldown',
110 | 'Revalidation Cooldown',
111 | array($this, 'revalidation_cooldown_callback'),
112 | 'next-revalidation-settings',
113 | 'next_revalidation_section'
114 | );
115 | }
116 |
117 | public function sanitize_settings($input) {
118 | $new_input = array();
119 |
120 | if (isset($input['next_url'])) {
121 | // Normalize and sanitize Next.js site URL (remove trailing slash)
122 | $url = rtrim(trim($input['next_url']), '/');
123 | $new_input['next_url'] = esc_url_raw($url);
124 | }
125 |
126 | if(isset($input['webhook_secret'])) {
127 | $new_input['webhook_secret'] = sanitize_text_field($input['webhook_secret']);
128 | }
129 |
130 | if(isset($input['enable_notifications'])) {
131 | $new_input['enable_notifications'] = (bool)$input['enable_notifications'];
132 | }
133 |
134 | if(isset($input['revalidation_cooldown'])) {
135 | $cooldown = intval($input['revalidation_cooldown']);
136 | $new_input['revalidation_cooldown'] = max(0, min(60, $cooldown)); // Between 0 and 60 seconds
137 | }
138 |
139 | return $new_input;
140 | }
141 |
142 | public function settings_section_callback() {
143 | echo 'Configure the connection to your Next.js site for revalidation.
';
144 | }
145 |
146 | public function next_url_callback() {
147 | $value = isset($this->options['next_url']) ? esc_attr($this->options['next_url']) : '';
148 | echo ' ';
149 | echo 'Your Next.js site URL without trailing slash.
';
150 | }
151 |
152 | public function webhook_secret_callback() {
153 | $value = isset($this->options['webhook_secret']) ? esc_attr($this->options['webhook_secret']) : '';
154 | echo ' ';
155 | echo 'This should match the WORDPRESS_WEBHOOK_SECRET in your Next.js environment.
';
156 | }
157 |
158 | public function enable_notifications_callback() {
159 | $value = isset($this->options['enable_notifications']) ? (bool)$this->options['enable_notifications'] : false;
160 | echo ' ';
161 | echo 'Show admin notifications for revalidation events ';
162 | }
163 |
164 | public function revalidation_cooldown_callback() {
165 | $value = isset($this->options['revalidation_cooldown']) ? intval($this->options['revalidation_cooldown']) : 2;
166 | echo ' ';
167 | echo 'Minimum seconds between revalidation requests (0-60). Use higher values for busy sites.
';
168 | }
169 |
170 | public function add_admin_menu() {
171 | add_menu_page(
172 | 'Next.js Revalidation',
173 | 'Next.js',
174 | 'manage_options',
175 | 'next-revalidation-settings',
176 | array($this, 'admin_page_content'),
177 | 'dashicons-update', // WordPress dashicon
178 | 100 // Position in menu
179 | );
180 |
181 | // Add submenu pages
182 | add_submenu_page(
183 | 'next-revalidation-settings',
184 | 'Settings',
185 | 'Settings',
186 | 'manage_options',
187 | 'next-revalidation-settings'
188 | );
189 |
190 | add_submenu_page(
191 | 'next-revalidation-settings',
192 | 'Revalidation History',
193 | 'History',
194 | 'manage_options',
195 | 'next-revalidation-history',
196 | array($this, 'history_page_content')
197 | );
198 | }
199 |
200 | public function admin_page_content() {
201 | ?>
202 |
203 |
204 |
211 |
212 |
Manual Revalidation
213 |
Click the button below to manually trigger a full revalidation of your Next.js site.
214 |
Revalidate All Content
215 |
216 |
217 |
244 |
245 | history_option_name, array());
251 |
252 | ?>
253 |
254 |
255 |
256 |
257 |
258 |
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 | Time
271 | Content Type
272 | Content ID
273 | Status
274 | Response
275 |
276 |
277 |
278 |
279 |
280 | No revalidation history found.
281 |
282 |
283 |
284 |
285 |
286 |
287 |
288 |
289 |
290 | Success
291 |
292 | Failed
293 |
294 |
295 |
296 | 50) {
300 | echo '...';
301 | }
302 | } else {
303 | echo 'N/A';
304 | }
305 | ?>
306 |
307 |
308 |
309 |
310 |
311 |
312 |
313 | history_option_name);
319 | echo '';
320 | }
321 | }
322 | }
323 |
324 | // AJAX action for manual revalidation
325 | public function register_ajax_actions() {
326 | add_action('wp_ajax_manual_revalidation', array($this, 'handle_manual_revalidation'));
327 | }
328 |
329 | public function handle_manual_revalidation() {
330 | check_ajax_referer('next_revalidation_nonce', 'nonce');
331 |
332 | if (!current_user_can('manage_options')) {
333 | wp_send_json_error('Permission denied');
334 | return;
335 | }
336 |
337 | $result = $this->send_revalidation_request('all');
338 |
339 | if ($result) {
340 | wp_send_json_success('Successfully revalidated all content on your Next.js site.');
341 | } else {
342 | wp_send_json_error('Failed to revalidate content. Please check your settings.');
343 | }
344 | }
345 |
346 | // Triggered when a post changes status (draft, publish, trash, etc.)
347 | public function on_post_status_change($new_status, $old_status, $post) {
348 | // Skip if it's a revision or autosave
349 | if (wp_is_post_revision($post->ID) || wp_is_post_autosave($post->ID)) {
350 | return;
351 | }
352 |
353 | // If the status is changing, we should revalidate
354 | if ($new_status !== $old_status) {
355 | error_log("Next.js Revalidation: Post status changed from {$old_status} to {$new_status} for post {$post->ID}");
356 | $this->send_revalidation_request($post->post_type, $post->ID);
357 | }
358 | }
359 |
360 | public function on_content_change($post_id, $post = null, $update = null) {
361 | // Don't revalidate on autosave or revision
362 | if (wp_is_post_revision($post_id) || wp_is_post_autosave($post_id)) {
363 | return;
364 | }
365 |
366 | // Get the post if not provided
367 | if (null === $post) {
368 | $post = get_post($post_id);
369 | }
370 |
371 | error_log("Next.js Revalidation: Content changed for post {$post_id}");
372 | $this->send_revalidation_request($post->post_type, $post_id);
373 | }
374 |
375 | public function on_post_trash($post_id) {
376 | $post_type = get_post_type($post_id);
377 | error_log("Next.js Revalidation: Post trashed {$post_id} of type {$post_type}");
378 | $this->send_revalidation_request($post_type, $post_id);
379 | }
380 |
381 | public function on_post_untrash($post_id) {
382 | $post_type = get_post_type($post_id);
383 | error_log("Next.js Revalidation: Post untrashed {$post_id} of type {$post_type}");
384 | $this->send_revalidation_request($post_type, $post_id);
385 | }
386 |
387 | public function on_post_delete($post_id) {
388 | $post_type = get_post_type($post_id);
389 | if (!$post_type) {
390 | $post_type = 'unknown'; // Fallback if post type can't be determined
391 | }
392 | error_log("Next.js Revalidation: Post deleted {$post_id} of type {$post_type}");
393 | $this->send_revalidation_request($post_type, $post_id);
394 | }
395 |
396 | public function on_term_change($term_id, $tt_id, $taxonomy) {
397 | // Map taxonomy to content type
398 | $content_type = $taxonomy;
399 | if ($taxonomy === 'category') {
400 | $content_type = 'category';
401 | } elseif ($taxonomy === 'post_tag') {
402 | $content_type = 'tag';
403 | }
404 |
405 | error_log("Next.js Revalidation: Term changed {$term_id} of type {$content_type}");
406 | $this->send_revalidation_request($content_type, $term_id);
407 | }
408 |
409 | public function on_user_change($user_id) {
410 | error_log("Next.js Revalidation: User changed {$user_id}");
411 | $this->send_revalidation_request('author', $user_id);
412 | }
413 |
414 | public function on_media_change($attachment_id) {
415 | error_log("Next.js Revalidation: Media changed {$attachment_id}");
416 | $this->send_revalidation_request('media', $attachment_id);
417 | }
418 |
419 | public function on_menu_change($menu_id) {
420 | error_log("Next.js Revalidation: Menu changed {$menu_id}");
421 | $this->send_revalidation_request('menu', $menu_id);
422 | }
423 |
424 | private function log_revalidation($content_type, $content_id, $success, $response = '') {
425 | $history = get_option($this->history_option_name, array());
426 |
427 | // Add new entry at the beginning
428 | array_unshift($history, array(
429 | 'time' => time(),
430 | 'content_type' => $content_type,
431 | 'content_id' => $content_id,
432 | 'success' => $success,
433 | 'response' => $response
434 | ));
435 |
436 | // Keep only the last X entries
437 | if (count($history) > $this->max_history_items) {
438 | $history = array_slice($history, 0, $this->max_history_items);
439 | }
440 |
441 | update_option($this->history_option_name, $history);
442 | }
443 |
444 | private function send_revalidation_request($content_type, $content_id = null) {
445 | // Get cooldown from settings if available
446 | $cooldown = isset($this->options['revalidation_cooldown']) ? intval($this->options['revalidation_cooldown']) : $this->revalidation_cooldown;
447 |
448 | // Implement throttling to prevent too many requests
449 | $current_time = time();
450 | if ($current_time - $this->last_revalidation < $cooldown) {
451 | error_log("Next.js Revalidation: Throttled request for {$content_type} {$content_id}");
452 | $this->log_revalidation($content_type, $content_id, false, 'Throttled: Too many requests');
453 | return false;
454 | }
455 | $this->last_revalidation = $current_time;
456 |
457 | // Check if we have the required settings
458 | if (empty($this->options['next_url']) || empty($this->options['webhook_secret'])) {
459 | if (!empty($this->options['enable_notifications'])) {
460 | add_action('admin_notices', function() {
461 | echo 'Next.js revalidation failed: Missing URL or webhook secret. Please configure the plugin settings.
';
462 | });
463 | }
464 | error_log("Next.js Revalidation: Missing URL or webhook secret");
465 | $this->log_revalidation($content_type, $content_id, false, 'Missing URL or webhook secret');
466 | return false;
467 | }
468 |
469 | // Prepare API endpoint
470 | $endpoint = trailingslashit($this->options['next_url']) . 'api/revalidate';
471 |
472 | // Prepare request payload
473 | $payload = array(
474 | 'contentType' => $content_type,
475 | );
476 |
477 | if ($content_id !== null) {
478 | $payload['contentId'] = $content_id;
479 | }
480 |
481 | error_log("Next.js Revalidation: Sending request to {$endpoint} for {$content_type} {$content_id}");
482 |
483 | // Send revalidation request
484 | $response = wp_remote_post($endpoint, array(
485 | 'method' => 'POST',
486 | 'timeout' => 5,
487 | 'redirection' => 5,
488 | 'httpversion' => '1.1',
489 | 'headers' => array(
490 | 'Content-Type' => 'application/json',
491 | 'x-webhook-secret' => $this->options['webhook_secret']
492 | ),
493 | 'body' => json_encode($payload),
494 | 'sslverify' => true
495 | ));
496 |
497 | // Check for success
498 | if (is_wp_error($response)) {
499 | $error_message = $response->get_error_message();
500 | error_log('Next.js revalidation error: ' . $error_message);
501 | if (!empty($this->options['enable_notifications'])) {
502 | add_action('admin_notices', function() use ($response) {
503 | echo 'Next.js revalidation failed: ' . esc_html($response->get_error_message()) . '
';
504 | });
505 | }
506 | $this->log_revalidation($content_type, $content_id, false, $error_message);
507 | return false;
508 | }
509 |
510 | $status_code = wp_remote_retrieve_response_code($response);
511 | $body = wp_remote_retrieve_body($response);
512 | $success = $status_code >= 200 && $status_code < 300;
513 |
514 | if ($success) {
515 | error_log("Next.js Revalidation: Success for {$content_type} {$content_id}");
516 | if (!empty($this->options['enable_notifications'])) {
517 | add_action('admin_notices', function() use ($content_type, $content_id) {
518 | echo 'Next.js site revalidated successfully due to ' . esc_html($content_type) . ' update' . ($content_id ? ' (ID: ' . esc_html($content_id) . ')' : '') . '
';
519 | });
520 | }
521 | $this->log_revalidation($content_type, $content_id, true, $body);
522 | return true;
523 | } else {
524 | error_log("Next.js revalidation failed with status {$status_code}: {$body}");
525 | if (!empty($this->options['enable_notifications'])) {
526 | add_action('admin_notices', function() use ($status_code, $body) {
527 | echo 'Next.js revalidation failed with status ' . esc_html($status_code) . ': ' . esc_html($body) . '
';
528 | });
529 | }
530 | $this->log_revalidation($content_type, $content_id, false, "Status {$status_code}: {$body}");
531 | return false;
532 | }
533 | }
534 | }
535 |
536 | // Initialize the plugin
537 | $next_revalidation = new Next_Revalidation();
--------------------------------------------------------------------------------