Skip to content

Commit 7918f9e

Browse files
Add support for managing original item references in block editor and enhance meta field handling
1 parent ee12974 commit 7918f9e

File tree

6 files changed

+214
-45
lines changed

6 files changed

+214
-45
lines changed

js/src/duplicate-post-edit-script.js

Lines changed: 94 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
/* global duplicatePost, duplicatePostNotices */
22

3-
import { useState } from 'react';
3+
import { useState, useEffect } from 'react';
44
import { registerPlugin } from "@wordpress/plugins";
55
import { PluginDocumentSettingPanel, PluginPostStatusInfo } from "@wordpress/editor";
66
import { Fragment } from "@wordpress/element";
7-
import { Button, ToggleControl } from '@wordpress/components';
7+
import { Button, ToggleControl, ExternalLink } from '@wordpress/components';
88
import { __ } from "@wordpress/i18n";
99
import { select, subscribe, dispatch } from "@wordpress/data";
1010
import { redirectOnSaveCompletion } from "./duplicate-post-functions";
@@ -134,50 +134,103 @@ class DuplicatePost {
134134
const currentPostStatus = select( 'core/editor' ).getEditedPostAttribute( 'status' );
135135

136136
const [ willBeDeletedReference, setWillBeDeletedReference ] = useState( false );
137+
const [ referenceRemoved, setReferenceRemoved ] = useState( false );
138+
139+
// Update the meta field when the toggle changes.
140+
useEffect( () => {
141+
dispatch( 'core/editor' ).editPost( {
142+
meta: { _dp_remove_original: willBeDeletedReference }
143+
} );
144+
}, [ willBeDeletedReference ] );
145+
146+
// Listen for save completion and hide panel if reference was marked for deletion.
147+
useEffect( () => {
148+
let wasSaving = false;
149+
150+
const unsubscribe = subscribe( () => {
151+
const isSaving = select( 'core/editor' ).isSavingPost();
152+
const isAutosaving = select( 'core/editor' ).isAutosavingPost();
153+
154+
// Detect when saving completes (was saving, now not saving, and not autosave).
155+
if ( wasSaving && ! isSaving && ! isAutosaving && willBeDeletedReference ) {
156+
setReferenceRemoved( true );
157+
}
158+
159+
wasSaving = isSaving && ! isAutosaving;
160+
} );
161+
162+
return () => unsubscribe();
163+
}, [ willBeDeletedReference ] );
164+
165+
const originalItem = duplicatePost.originalItem;
166+
const isRewriting = parseInt( duplicatePost.rewriting, 10 );
167+
const showMetaBox = duplicatePost.showOriginalMetaBox && originalItem && ! referenceRemoved;
137168

138169
return (
139-
( duplicatePost.showLinksIn.submitbox === '1' ) &&
140170
<Fragment>
141-
{ ( duplicatePost.newDraftLink !== '' && duplicatePost.showLinks.new_draft === '1' ) &&
142-
<PluginPostStatusInfo>
143-
<Button
144-
isTertiary={ true }
145-
className="dp-editor-post-copy-to-draft"
146-
href={ duplicatePost.newDraftLink }
147-
>
148-
{ __( 'Copy to a new draft', 'duplicate-post' ) }
149-
</Button>
150-
</PluginPostStatusInfo>
151-
}
152-
{ ( currentPostStatus === 'publish' && duplicatePost.rewriteAndRepublishLink !== '' && duplicatePost.showLinks.rewrite_republish === '1' ) &&
153-
<PluginPostStatusInfo>
154-
<Button
155-
isTertiary={ true }
156-
className="dp-editor-post-rewrite-republish"
157-
href={ duplicatePost.rewriteAndRepublishLink }
158-
>
159-
{ __( 'Rewrite & Republish', 'duplicate-post' ) }
160-
</Button>
161-
</PluginPostStatusInfo>
171+
{ ( duplicatePost.showLinksIn.submitbox === '1' ) &&
172+
<Fragment>
173+
{ ( duplicatePost.newDraftLink !== '' && duplicatePost.showLinks.new_draft === '1' ) &&
174+
<PluginPostStatusInfo>
175+
<Button
176+
isTertiary={ true }
177+
className="dp-editor-post-copy-to-draft"
178+
href={ duplicatePost.newDraftLink }
179+
>
180+
{ __( 'Copy to a new draft', 'duplicate-post' ) }
181+
</Button>
182+
</PluginPostStatusInfo>
183+
}
184+
{ ( currentPostStatus === 'publish' && duplicatePost.rewriteAndRepublishLink !== '' && duplicatePost.showLinks.rewrite_republish === '1' ) &&
185+
<PluginPostStatusInfo>
186+
<Button
187+
isTertiary={ true }
188+
className="dp-editor-post-rewrite-republish"
189+
href={ duplicatePost.rewriteAndRepublishLink }
190+
>
191+
{ __( 'Rewrite & Republish', 'duplicate-post' ) }
192+
</Button>
193+
</PluginPostStatusInfo>
194+
}
195+
</Fragment>
162196
}
163-
<PluginDocumentSettingPanel
164-
name="duplicate-post-panel"
165-
title={ __( "Duplicate Post", "duplicate-post" ) }
166-
className="custom-panel"
167-
>
168-
<ToggleControl
169-
label={ __( "Delete reference to original item.", "duplicate-post" ) }
170-
help={
171-
willBeDeletedReference
172-
? __( "The reference will be deleted on update", "duplicate-post" )
173-
: __( "The reference will be kept on update", "duplicate-post" )
197+
{ showMetaBox &&
198+
<PluginDocumentSettingPanel
199+
name="duplicate-post-panel"
200+
title={ __( "Duplicate Post", "duplicate-post" ) }
201+
className="duplicate-post-panel"
202+
>
203+
{ ! isRewriting &&
204+
<ToggleControl
205+
label={ __( "Delete reference to original item.", "duplicate-post" ) }
206+
help={
207+
willBeDeletedReference
208+
? __( "The reference will be deleted on update.", "duplicate-post" )
209+
: __( "The reference will be kept on update.", "duplicate-post" )
210+
}
211+
checked={ willBeDeletedReference }
212+
onChange={ ( newValue ) => {
213+
setWillBeDeletedReference( newValue );
214+
} }
215+
/>
174216
}
175-
checked={ willBeDeletedReference }
176-
onChange={ (newValue) => {
177-
setWillBeDeletedReference( newValue );
178-
} }
179-
/>
180-
</PluginDocumentSettingPanel>
217+
<p className="duplicate-post-original-item">
218+
{ __( 'The original item this was copied from is:', 'duplicate-post' ) }
219+
{ ' ' }
220+
<span className="duplicate_post_original_item_title_span">
221+
{ originalItem.canEdit ? (
222+
<ExternalLink href={ originalItem.editUrl }>
223+
{ originalItem.title }
224+
</ExternalLink>
225+
) : (
226+
<ExternalLink href={ originalItem.viewUrl }>
227+
{ originalItem.title }
228+
</ExternalLink>
229+
) }
230+
</span>
231+
</p>
232+
</PluginDocumentSettingPanel>
233+
}
181234
</Fragment>
182235
);
183236
}

src/handlers/save-post-handler.php

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,33 @@ public function register_hooks() {
3636
if ( \intval( \get_option( 'duplicate_post_show_original_meta_box' ) ) === 1
3737
|| \intval( \get_option( 'duplicate_post_show_original_column' ) ) === 1 ) {
3838
\add_action( 'save_post', [ $this, 'delete_on_save_post' ] );
39+
\add_action( 'init', [ $this, 'register_meta' ] );
40+
}
41+
}
42+
43+
/**
44+
* Registers the meta field for the REST API.
45+
*
46+
* @return void
47+
*/
48+
public function register_meta() {
49+
$post_types = $this->permissions_helper->get_enabled_post_types();
50+
51+
foreach ( $post_types as $post_type ) {
52+
\register_post_meta(
53+
$post_type,
54+
'_dp_remove_original',
55+
[
56+
'show_in_rest' => true,
57+
'single' => true,
58+
'type' => 'boolean',
59+
'default' => false,
60+
'auth_callback' => function ( $allowed, $meta_key, $post_id ) {
61+
return \current_user_can( 'edit_post', $post_id );
62+
},
63+
'sanitize_callback' => 'rest_sanitize_boolean',
64+
]
65+
);
3966
}
4067
}
4168

@@ -47,18 +74,33 @@ public function register_hooks() {
4774
* @return void
4875
*/
4976
public function delete_on_save_post( $post_id ) {
50-
if ( ( \defined( 'DOING_AUTOSAVE' ) && \DOING_AUTOSAVE )
51-
|| empty( $_POST['duplicate_post_remove_original'] )
52-
|| ! \current_user_can( 'edit_post', $post_id ) ) {
77+
if ( \defined( 'DOING_AUTOSAVE' ) && \DOING_AUTOSAVE ) {
78+
return;
79+
}
80+
81+
if ( ! \current_user_can( 'edit_post', $post_id ) ) {
5382
return;
5483
}
5584

5685
$post = \get_post( $post_id );
5786
if ( ! $post ) {
5887
return;
5988
}
60-
if ( ! $this->permissions_helper->is_rewrite_and_republish_copy( $post ) ) {
89+
90+
if ( $this->permissions_helper->is_rewrite_and_republish_copy( $post ) ) {
91+
return;
92+
}
93+
94+
// Check for classic editor (POST request).
95+
$should_remove_from_post = ! empty( $_POST['duplicate_post_remove_original'] );
96+
97+
// Check for block editor (meta field).
98+
$should_remove_from_meta = (bool) \get_post_meta( $post_id, '_dp_remove_original', true );
99+
100+
if ( $should_remove_from_post || $should_remove_from_meta ) {
61101
\delete_post_meta( $post_id, '_dp_original' );
102+
// Clean up the flag meta.
103+
\delete_post_meta( $post_id, '_dp_remove_original' );
62104
}
63105
}
64106
}

src/ui/block-editor.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,17 @@ public function get_original_post_edit_url() {
227227
*/
228228
protected function generate_js_object( WP_Post $post ) {
229229
$is_rewrite_and_republish_copy = $this->permissions_helper->is_rewrite_and_republish_copy( $post );
230+
$original_item = Utils::get_original( $post );
231+
$original_data = null;
232+
233+
if ( $original_item instanceof WP_Post ) {
234+
$original_data = [
235+
'editUrl' => \esc_url( \get_edit_post_link( $original_item->ID ) ),
236+
'viewUrl' => \esc_url( \get_permalink( $original_item->ID ) ),
237+
'title' => \_draft_or_post_title( $original_item ),
238+
'canEdit' => \current_user_can( 'edit_post', $original_item->ID ),
239+
];
240+
}
230241

231242
return [
232243
'newDraftLink' => $this->get_new_draft_permalink(),
@@ -235,6 +246,8 @@ protected function generate_js_object( WP_Post $post ) {
235246
'showLinksIn' => Utils::get_option( 'duplicate_post_show_link_in' ),
236247
'rewriting' => ( $is_rewrite_and_republish_copy ) ? 1 : 0,
237248
'originalEditURL' => $this->get_original_post_edit_url(),
249+
'showOriginalMetaBox' => \intval( \get_option( 'duplicate_post_show_original_meta_box' ) ) === 1,
250+
'originalItem' => $original_data,
238251
];
239252
}
240253

src/ui/metabox.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,11 @@ public function register_hooks() {
4747
* @return void
4848
*/
4949
public function add_custom_metabox( $post_type, $post ) {
50+
// Don't show the metabox in the block editor, we use the sidebar panel instead.
51+
if ( \use_block_editor_for_post( $post ) ) {
52+
return;
53+
}
54+
5055
$enabled_post_types = $this->permissions_helper->get_enabled_post_types();
5156

5257
if ( \in_array( $post_type, $enabled_post_types, true )

tests/Unit/UI/Block_Editor_Test.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,13 +322,24 @@ public function test_enqueue_block_editor_scripts() {
322322
->expects( 'get_original_post_edit_url' )
323323
->andReturn( $original_edit_url );
324324

325+
Monkey\Functions\expect( '\get_option' )
326+
->with( 'duplicate_post_show_original_meta_box' )
327+
->andReturn( '0' );
328+
329+
$utils
330+
->expects( 'get_original' )
331+
->with( $post )
332+
->andReturnNull();
333+
325334
$edit_js_object = [
326335
'newDraftLink' => $new_draft_link,
327336
'rewriteAndRepublishLink' => $rewrite_and_republish_link,
328337
'showLinks' => $show_links,
329338
'showLinksIn' => $show_links_in,
330339
'rewriting' => $rewriting,
331340
'originalEditURL' => $original_edit_url,
341+
'showOriginalMetaBox' => false,
342+
'originalItem' => null,
332343
];
333344

334345
$this->asset_manager
@@ -407,13 +418,24 @@ public function test_get_enqueue_block_editor_scripts_rewrite_and_republish() {
407418
->expects( 'get_original_post_edit_url' )
408419
->andReturn( $original_edit_url );
409420

421+
Monkey\Functions\expect( '\get_option' )
422+
->with( 'duplicate_post_show_original_meta_box' )
423+
->andReturn( '0' );
424+
425+
$utils
426+
->expects( 'get_original' )
427+
->with( $post )
428+
->andReturnNull();
429+
410430
$edit_js_object = [
411431
'newDraftLink' => $new_draft_link,
412432
'rewriteAndRepublishLink' => $rewrite_and_republish_link,
413433
'showLinks' => $show_links,
414434
'showLinksIn' => $show_links_in,
415435
'rewriting' => $rewriting,
416436
'originalEditURL' => $original_edit_url,
437+
'showOriginalMetaBox' => false,
438+
'originalItem' => null,
417439
];
418440

419441
$this->asset_manager

tests/Unit/UI/Metabox_Test.php

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,10 @@ public function test_add_custom_metabox() {
9292
$post = Mockery::mock( WP_Post::class );
9393
$original_item = Mockery::mock( WP_Post::class );
9494

95+
Monkey\Functions\expect( '\use_block_editor_for_post' )
96+
->with( $post )
97+
->andReturn( false );
98+
9599
$this->permissions_helper->expects( 'get_enabled_post_types' )
96100
->andReturn( $enabled_post_types );
97101

@@ -126,6 +130,10 @@ public function test_add_custom_metabox_post_type_not_enabled() {
126130
$enabled_post_types = [ 'post' ];
127131
$post = Mockery::mock( WP_Post::class );
128132

133+
Monkey\Functions\expect( '\use_block_editor_for_post' )
134+
->with( $post )
135+
->andReturn( false );
136+
129137
$this->permissions_helper
130138
->expects( 'get_enabled_post_types' )
131139
->andReturn( $enabled_post_types );
@@ -150,6 +158,10 @@ public function test_add_custom_metabox_not_copy() {
150158
$enabled_post_types = [ 'post', 'page' ];
151159
$post = Mockery::mock( WP_Post::class );
152160

161+
Monkey\Functions\expect( '\use_block_editor_for_post' )
162+
->with( $post )
163+
->andReturn( false );
164+
153165
$this->permissions_helper
154166
->expects( 'get_enabled_post_types' )
155167
->andReturn( $enabled_post_types );
@@ -163,4 +175,26 @@ public function test_add_custom_metabox_not_copy() {
163175

164176
$this->instance->add_custom_metabox( 'post', $post );
165177
}
178+
179+
/**
180+
* Tests the call to the add_custom_metabox function when using block editor.
181+
*
182+
* @covers \Yoast\WP\Duplicate_Post\UI\Metabox::add_custom_metabox
183+
* @runInSeparateProcess
184+
* @preserveGlobalState disabled
185+
*
186+
* @return void
187+
*/
188+
public function test_add_custom_metabox_block_editor() {
189+
$post = Mockery::mock( WP_Post::class );
190+
191+
Monkey\Functions\expect( '\use_block_editor_for_post' )
192+
->with( $post )
193+
->andReturn( true );
194+
195+
Monkey\Functions\expect( 'add_meta_box' )
196+
->never();
197+
198+
$this->instance->add_custom_metabox( 'post', $post );
199+
}
166200
}

0 commit comments

Comments
 (0)