Skip to content

Commit 4b1c127

Browse files
committed
chore: update next-revalidate plugin
1 parent a7de241 commit 4b1c127

File tree

2 files changed

+194
-11
lines changed

2 files changed

+194
-11
lines changed

plugin/next-revalidate.zip

1.56 KB
Binary file not shown.

plugin/next-revalidate/next-revalidate.php

Lines changed: 194 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
/**
33
* Plugin Name: Next.js Revalidation
44
* Description: Revalidates Next.js site when WordPress content changes
5-
* Version: 1.0.3
5+
* Version: 1.0.5
66
* Author: 9d8
77
* Author URI: https://9d8.dev
88
*/
@@ -15,6 +15,10 @@
1515
class Next_Revalidation {
1616
private $options;
1717
private $option_name = 'next_revalidation_settings';
18+
private $last_revalidation = 0;
19+
private $revalidation_cooldown = 2; // seconds between revalidations
20+
private $history_option_name = 'next_revalidation_history';
21+
private $max_history_items = 50;
1822

1923
public function __construct() {
2024
// Initialize plugin
@@ -31,9 +35,12 @@ public function __construct() {
3135
// Post save/update hooks - any post status change
3236
add_action('transition_post_status', array($this, 'on_post_status_change'), 10, 3);
3337
add_action('save_post', array($this, 'on_content_change'), 10, 3);
34-
add_action('wp_trash_post', array($this, 'on_post_trash'), 10);
35-
add_action('untrash_post', array($this, 'on_post_untrash'), 10);
36-
add_action('before_delete_post', array($this, 'on_post_delete'), 10);
38+
39+
// Trash and delete hooks with higher priority to ensure they run
40+
add_action('trashed_post', array($this, 'on_post_trash'), 5);
41+
add_action('untrashed_post', array($this, 'on_post_untrash'), 5);
42+
add_action('delete_post', array($this, 'on_post_delete'), 5);
43+
add_action('after_delete_post', array($this, 'on_post_delete'), 5);
3744

3845
// Term changes
3946
add_action('created_term', array($this, 'on_term_change'), 10, 3);
@@ -49,6 +56,11 @@ public function __construct() {
4956
add_action('add_attachment', array($this, 'on_media_change'), 10);
5057
add_action('edit_attachment', array($this, 'on_media_change'), 10);
5158
add_action('delete_attachment', array($this, 'on_media_change'), 10);
59+
60+
// Menu changes
61+
add_action('wp_update_nav_menu', array($this, 'on_menu_change'), 10);
62+
add_action('wp_create_nav_menu', array($this, 'on_menu_change'), 10);
63+
add_action('wp_delete_nav_menu', array($this, 'on_menu_change'), 10);
5264
}
5365

5466
public function init() {
@@ -92,6 +104,14 @@ public function register_settings() {
92104
'next-revalidation-settings',
93105
'next_revalidation_section'
94106
);
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+
);
95115
}
96116

97117
public function sanitize_settings($input) {
@@ -111,6 +131,11 @@ public function sanitize_settings($input) {
111131
$new_input['enable_notifications'] = (bool)$input['enable_notifications'];
112132
}
113133

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+
114139
return $new_input;
115140
}
116141

@@ -135,14 +160,40 @@ public function enable_notifications_callback() {
135160
echo '<input type="checkbox" id="enable_notifications" name="' . $this->option_name . '[enable_notifications]" ' . checked($value, true, false) . ' />';
136161
echo '<label for="enable_notifications">Show admin notifications for revalidation events</label>';
137162
}
163+
164+
public function revalidation_cooldown_callback() {
165+
$value = isset($this->options['revalidation_cooldown']) ? intval($this->options['revalidation_cooldown']) : 2;
166+
echo '<input type="number" min="0" max="60" id="revalidation_cooldown" name="' . $this->option_name . '[revalidation_cooldown]" value="' . $value . '" class="small-text" />';
167+
echo '<p class="description">Minimum seconds between revalidation requests (0-60). Use higher values for busy sites.</p>';
168+
}
138169

139170
public function add_admin_menu() {
140-
add_options_page(
141-
'Next.js Revalidation',
171+
add_menu_page(
142172
'Next.js Revalidation',
173+
'Next.js',
143174
'manage_options',
144175
'next-revalidation-settings',
145-
array($this, 'admin_page_content')
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')
146197
);
147198
}
148199

@@ -194,6 +245,82 @@ public function admin_page_content() {
194245
<?php
195246
}
196247

248+
public function history_page_content() {
249+
// Get revalidation history
250+
$history = get_option($this->history_option_name, array());
251+
252+
?>
253+
<div class="wrap">
254+
<h1><?php echo esc_html(get_admin_page_title()); ?></h1>
255+
256+
<div class="tablenav top">
257+
<div class="alignleft actions">
258+
<form method="post" action="">
259+
<?php wp_nonce_field('clear_revalidation_history', 'clear_history_nonce'); ?>
260+
<input type="hidden" name="action" value="clear_history">
261+
<input type="submit" class="button" value="Clear History" onclick="return confirm('Are you sure you want to clear the revalidation history?');">
262+
</form>
263+
</div>
264+
<br class="clear">
265+
</div>
266+
267+
<table class="wp-list-table widefat fixed striped">
268+
<thead>
269+
<tr>
270+
<th>Time</th>
271+
<th>Content Type</th>
272+
<th>Content ID</th>
273+
<th>Status</th>
274+
<th>Response</th>
275+
</tr>
276+
</thead>
277+
<tbody>
278+
<?php if (empty($history)): ?>
279+
<tr>
280+
<td colspan="5">No revalidation history found.</td>
281+
</tr>
282+
<?php else: ?>
283+
<?php foreach ($history as $entry): ?>
284+
<tr>
285+
<td><?php echo esc_html(date('Y-m-d H:i:s', $entry['time'])); ?></td>
286+
<td><?php echo esc_html($entry['content_type']); ?></td>
287+
<td><?php echo isset($entry['content_id']) ? esc_html($entry['content_id']) : 'N/A'; ?></td>
288+
<td>
289+
<?php if ($entry['success']): ?>
290+
<span style="color: green;">Success</span>
291+
<?php else: ?>
292+
<span style="color: red;">Failed</span>
293+
<?php endif; ?>
294+
</td>
295+
<td>
296+
<?php
297+
if (isset($entry['response'])) {
298+
echo esc_html(substr($entry['response'], 0, 50));
299+
if (strlen($entry['response']) > 50) {
300+
echo '...';
301+
}
302+
} else {
303+
echo 'N/A';
304+
}
305+
?>
306+
</td>
307+
</tr>
308+
<?php endforeach; ?>
309+
<?php endif; ?>
310+
</tbody>
311+
</table>
312+
</div>
313+
<?php
314+
315+
// Handle clear history action
316+
if (isset($_POST['action']) && $_POST['action'] === 'clear_history') {
317+
if (check_admin_referer('clear_revalidation_history', 'clear_history_nonce')) {
318+
delete_option($this->history_option_name);
319+
echo '<script>window.location.reload();</script>';
320+
}
321+
}
322+
}
323+
197324
// AJAX action for manual revalidation
198325
public function register_ajax_actions() {
199326
add_action('wp_ajax_manual_revalidation', array($this, 'handle_manual_revalidation'));
@@ -225,6 +352,7 @@ public function on_post_status_change($new_status, $old_status, $post) {
225352

226353
// If the status is changing, we should revalidate
227354
if ($new_status !== $old_status) {
355+
error_log("Next.js Revalidation: Post status changed from {$old_status} to {$new_status} for post {$post->ID}");
228356
$this->send_revalidation_request($post->post_type, $post->ID);
229357
}
230358
}
@@ -240,22 +368,28 @@ public function on_content_change($post_id, $post = null, $update = null) {
240368
$post = get_post($post_id);
241369
}
242370

243-
// Revalidate regardless of post status
371+
error_log("Next.js Revalidation: Content changed for post {$post_id}");
244372
$this->send_revalidation_request($post->post_type, $post_id);
245373
}
246374

247375
public function on_post_trash($post_id) {
248376
$post_type = get_post_type($post_id);
377+
error_log("Next.js Revalidation: Post trashed {$post_id} of type {$post_type}");
249378
$this->send_revalidation_request($post_type, $post_id);
250379
}
251380

252381
public function on_post_untrash($post_id) {
253382
$post_type = get_post_type($post_id);
383+
error_log("Next.js Revalidation: Post untrashed {$post_id} of type {$post_type}");
254384
$this->send_revalidation_request($post_type, $post_id);
255385
}
256386

257387
public function on_post_delete($post_id) {
258388
$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}");
259393
$this->send_revalidation_request($post_type, $post_id);
260394
}
261395

@@ -268,25 +402,67 @@ public function on_term_change($term_id, $tt_id, $taxonomy) {
268402
$content_type = 'tag';
269403
}
270404

405+
error_log("Next.js Revalidation: Term changed {$term_id} of type {$content_type}");
271406
$this->send_revalidation_request($content_type, $term_id);
272407
}
273408

274409
public function on_user_change($user_id) {
410+
error_log("Next.js Revalidation: User changed {$user_id}");
275411
$this->send_revalidation_request('author', $user_id);
276412
}
277413

278414
public function on_media_change($attachment_id) {
415+
error_log("Next.js Revalidation: Media changed {$attachment_id}");
279416
$this->send_revalidation_request('media', $attachment_id);
280417
}
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+
}
281443

282444
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+
283457
// Check if we have the required settings
284458
if (empty($this->options['next_url']) || empty($this->options['webhook_secret'])) {
285459
if (!empty($this->options['enable_notifications'])) {
286460
add_action('admin_notices', function() {
287461
echo '<div class="notice notice-error is-dismissible"><p>Next.js revalidation failed: Missing URL or webhook secret. Please configure the plugin settings.</p></div>';
288462
});
289463
}
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');
290466
return false;
291467
}
292468

@@ -302,6 +478,8 @@ private function send_revalidation_request($content_type, $content_id = null) {
302478
$payload['contentId'] = $content_id;
303479
}
304480

481+
error_log("Next.js Revalidation: Sending request to {$endpoint} for {$content_type} {$content_id}");
482+
305483
// Send revalidation request
306484
$response = wp_remote_post($endpoint, array(
307485
'method' => 'POST',
@@ -318,33 +496,38 @@ private function send_revalidation_request($content_type, $content_id = null) {
318496

319497
// Check for success
320498
if (is_wp_error($response)) {
321-
error_log('Next.js revalidation error: ' . $response->get_error_message());
499+
$error_message = $response->get_error_message();
500+
error_log('Next.js revalidation error: ' . $error_message);
322501
if (!empty($this->options['enable_notifications'])) {
323502
add_action('admin_notices', function() use ($response) {
324503
echo '<div class="notice notice-error is-dismissible"><p>Next.js revalidation failed: ' . esc_html($response->get_error_message()) . '</p></div>';
325504
});
326505
}
506+
$this->log_revalidation($content_type, $content_id, false, $error_message);
327507
return false;
328508
}
329509

330510
$status_code = wp_remote_retrieve_response_code($response);
511+
$body = wp_remote_retrieve_body($response);
331512
$success = $status_code >= 200 && $status_code < 300;
332513

333514
if ($success) {
515+
error_log("Next.js Revalidation: Success for {$content_type} {$content_id}");
334516
if (!empty($this->options['enable_notifications'])) {
335517
add_action('admin_notices', function() use ($content_type, $content_id) {
336518
echo '<div class="notice notice-success is-dismissible"><p>Next.js site revalidated successfully due to ' . esc_html($content_type) . ' update' . ($content_id ? ' (ID: ' . esc_html($content_id) . ')' : '') . '</p></div>';
337519
});
338520
}
521+
$this->log_revalidation($content_type, $content_id, true, $body);
339522
return true;
340523
} else {
341-
$body = wp_remote_retrieve_body($response);
342-
error_log('Next.js revalidation failed: ' . $body);
524+
error_log("Next.js revalidation failed with status {$status_code}: {$body}");
343525
if (!empty($this->options['enable_notifications'])) {
344526
add_action('admin_notices', function() use ($status_code, $body) {
345527
echo '<div class="notice notice-error is-dismissible"><p>Next.js revalidation failed with status ' . esc_html($status_code) . ': ' . esc_html($body) . '</p></div>';
346528
});
347529
}
530+
$this->log_revalidation($content_type, $content_id, false, "Status {$status_code}: {$body}");
348531
return false;
349532
}
350533
}

0 commit comments

Comments
 (0)