feat(redirects): suggest-only redirects on delete-post + slug change

Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
Mian Shahzad Raza
2026-08-11 04:59:46 +05:00
co-authored by Claude Opus 4.8
parent 70a4dc6353
commit 102a323656
2 changed files with 52 additions and 2 deletions
+42 -2
View File
@@ -763,6 +763,16 @@ class EMCP_Tools_Content_Abilities {
$emcp_before = array( 'fields' => $emcp_bf, 'meta' => $emcp_bm, 'terms' => $emcp_bt );
}
// A slug change on a published post kills its old permalink — capture it
// (before the update) for a suggested redirect.
$emcp_slug_old_url = '';
if ( ! empty( $input['slug'] )
&& isset( $postarr['post_name'] )
&& $postarr['post_name'] !== $post->post_name
&& 'publish' === $post->post_status ) {
$emcp_slug_old_url = (string) get_permalink( $post_id );
}
$res = wp_update_post( wp_slash( $postarr ), true );
if ( is_wp_error( $res ) ) {
return $res;
@@ -789,6 +799,9 @@ class EMCP_Tools_Content_Abilities {
if ( $warnings ) {
$result['warnings'] = $warnings;
}
if ( '' !== $emcp_slug_old_url ) {
$result = $this->with_redirect_suggestion( $result, $emcp_slug_old_url, 'slug-changed', $post_id );
}
return $result;
}
@@ -842,6 +855,8 @@ class EMCP_Tools_Content_Abilities {
$force = ! empty( $input['force'] );
$has_rec = class_exists( 'EMCP_Tools_Change_Recorder' );
$target = trim( (string) get_the_title( $post_id ) . ' (#' . $post_id . ')' );
// The now-dead URL, captured before deletion, for a suggested redirect.
$old_url = (string) get_permalink( $post_id );
if ( $force ) {
// Snapshot the whole post BEFORE deleting so it can be re-inserted.
@@ -850,13 +865,38 @@ class EMCP_Tools_Content_Abilities {
if ( $has_rec && $res ) {
EMCP_Tools_Change_Recorder::record_post_delete( $post_id, $snapshot, true, sprintf( 'Deleted post #%d', $post_id ), $target );
}
return array( 'success' => (bool) $res, 'post_id' => $post_id, 'deleted' => 'deleted' );
$out = array( 'success' => (bool) $res, 'post_id' => $post_id, 'deleted' => 'deleted' );
return $res ? $this->with_redirect_suggestion( $out, $old_url, 'post-deleted', $post_id ) : $out;
}
$res = wp_trash_post( $post_id );
if ( $has_rec && $res ) {
EMCP_Tools_Change_Recorder::record_post_delete( $post_id, array(), false, sprintf( 'Trashed post #%d', $post_id ), $target );
}
return array( 'success' => (bool) $res, 'post_id' => $post_id, 'deleted' => 'trashed' );
$out = array( 'success' => (bool) $res, 'post_id' => $post_id, 'deleted' => 'trashed' );
return $res ? $this->with_redirect_suggestion( $out, $old_url, 'post-deleted', $post_id ) : $out;
}
/**
* Queue a suggested redirect for a now-dead URL and annotate the tool
* response. Suggest-only: it never writes a redirect. Safe no-op when the
* Redirect Manager is absent.
*
* @param array $out The tool response to annotate.
* @param string $old_url The dead permalink.
* @param string $reason 'post-deleted' | 'slug-changed'.
* @param int $post_id The source post.
* @return array
*/
private function with_redirect_suggestion( array $out, string $old_url, string $reason, int $post_id ): array {
if ( '' === $old_url || ! class_exists( 'EMCP_Tools_Redirect_Abilities' ) ) {
return $out;
}
EMCP_Tools_Redirect_Abilities::push_suggestion( $old_url, $reason, $post_id );
$out['redirect_suggestion'] = array(
'old_url' => $old_url,
'message' => __( 'This URL is now dead — call create-redirect to point it somewhere so visitors and search engines do not hit a 404.', 'emcp-tools' ),
);
return $out;
}
// ---------------------------------------------------------------------
+10
View File
@@ -48,6 +48,16 @@ class RedirectAbilitiesTest extends \PHPUnit\Framework\TestCase {
$this->assertSame( '/old-a', $q[ count( $q ) - 1 ]['old_path'] );
}
public function test_push_suggestion_caps_queue() {
for ( $i = 0; $i < 60; $i++ ) {
EMCP_Tools_Redirect_Abilities::push_suggestion( '/p' . $i, 'post-deleted', $i );
}
$q = get_option( 'emcp_tools_redirect_suggestions', array() );
$this->assertCount( 50, $q );
// FIFO: the oldest were dropped, the newest kept.
$this->assertSame( '/p59', $q[ count( $q ) - 1 ]['old_path'] );
}
public function test_push_suggestion_ignores_root_and_empty() {
EMCP_Tools_Redirect_Abilities::push_suggestion( '/', 'post-deleted', 1 );
EMCP_Tools_Redirect_Abilities::push_suggestion( '', 'post-deleted', 2 );