Duplicate Post - Version 4.2

Version Description

(2021-11-18) =

Bugfixes:

  • Fixes a bug where some strings where not translated.
  • Fixes a bug where meta data went missing for duplicate posts created by the Rewrite & Republish feature. Props to ocean90.
  • Fixes a bug where the plugin would not initialize if installed via composer.

Other:

  • Replaces Mailchimp with MailBlue newsletter integration.
  • Improved compatibility for PHP 8.0.
  • Improved sort order of the taxonomies list on the options page.
  • Most plugin files have been renamed. If you extend this plugin of include any of the files from the plugin from within your own plugin, you may need to adjust the file name used in the include/require statement.
Download this release

Release Info

Developer lopo
Plugin Icon 128x128 Duplicate Post
Version 4.2
Comparing to
See all releases

Code changes from version 4.1.2 to 4.2

Files changed (56) hide show
  1. duplicate-post-admin.php → admin-functions.php +99 -143
  2. duplicate-post-common.php → common-functions.php +11 -9
  3. compat/duplicate-post-gutenberg.php +0 -39
  4. compat/{duplicate-post-jetpack.php → jetpack-functions.php} +2 -2
  5. compat/{duplicate-post-wpml.php → wpml-functions.php} +18 -10
  6. duplicate-post.php +26 -18
  7. js/dist/{duplicate-post-edit-412.js → duplicate-post-edit-420.js} +0 -0
  8. js/dist/{duplicate-post-elementor-412.js → duplicate-post-elementor-420.js} +0 -0
  9. js/dist/{duplicate-post-options-412.js → duplicate-post-options-420.js} +0 -0
  10. js/dist/{duplicate-post-quick-edit-412.js → duplicate-post-quick-edit-420.js} +0 -0
  11. js/dist/{duplicate-post-strings-412.js → duplicate-post-strings-420.js} +0 -0
  12. duplicate-post-options.php → options.php +1 -7
  13. readme.txt +16 -1
  14. src/admin/{class-options-form-generator.php → options-form-generator.php} +23 -19
  15. src/admin/{class-options-inputs.php → options-inputs.php} +14 -19
  16. src/admin/{class-options-page.php → options-page.php} +10 -11
  17. src/admin/{class-options.php → options.php} +27 -30
  18. src/admin/views/options.php +7 -9
  19. src/{class-duplicate-post.php → duplicate-post.php} +3 -7
  20. src/handlers/{class-bulk-handler.php → bulk-handler.php} +5 -9
  21. src/handlers/{class-check-changes-handler.php → check-changes-handler.php} +28 -28
  22. src/handlers/{class-handler.php → handler.php} +3 -7
  23. src/handlers/{class-link-handler.php → link-handler.php} +21 -23
  24. src/handlers/{class-save-post-handler.php → save-post-handler.php} +5 -9
  25. src/{class-permissions-helper.php → permissions-helper.php} +30 -36
  26. src/{class-post-duplicator.php → post-duplicator.php} +58 -51
  27. src/{class-post-republisher.php → post-republisher.php} +20 -23
  28. src/{class-revisions-migrator.php → revisions-migrator.php} +5 -9
  29. src/ui/{class-admin-bar.php → admin-bar.php} +5 -8
  30. src/ui/{class-asset-manager.php → asset-manager.php} +24 -29
  31. src/ui/{class-block-editor.php → block-editor.php} +3 -8
  32. src/ui/{class-bulk-actions.php → bulk-actions.php} +2 -8
  33. src/ui/class-metabox.php +0 -121
  34. src/ui/{class-classic-editor.php → classic-editor.php} +27 -32
  35. src/ui/{class-column.php → column.php} +13 -13
  36. src/ui/{class-link-builder.php → link-builder.php} +28 -32
  37. src/ui/metabox.php +116 -0
  38. src/ui/newsletter.php +127 -0
  39. src/ui/{class-post-states.php → post-states.php} +3 -8
  40. src/ui/{class-row-actions.php → row-actions.php} +23 -28
  41. src/ui/{class-user-interface.php → user-interface.php} +9 -6
  42. src/{class-utils.php → utils.php} +24 -22
  43. src/watchers/{class-bulk-actions-watcher.php → bulk-actions-watcher.php} +11 -16
  44. src/watchers/{class-copied-post-watcher.php → copied-post-watcher.php} +8 -12
  45. src/watchers/{class-link-actions-watcher.php → link-actions-watcher.php} +14 -19
  46. src/watchers/{class-original-post-watcher.php → original-post-watcher.php} +9 -12
  47. src/watchers/{class-republished-post-watcher.php → republished-post-watcher.php} +8 -12
  48. src/watchers/{class-watchers.php → watchers.php} +1 -6
  49. vendor/composer/ClassLoader.php +105 -12
  50. vendor/composer/InstalledVersions.php +339 -272
  51. vendor/composer/autoload_classmap.php +135 -32
  52. vendor/composer/autoload_psr4.php +1 -0
  53. vendor/composer/autoload_real.php +2 -0
  54. vendor/composer/autoload_static.php +151 -32
  55. vendor/composer/installed.php +41 -21
  56. vendor/composer/platform_check.php +26 -0
duplicate-post-admin.php → admin-functions.php RENAMED
@@ -2,7 +2,7 @@
2
  /**
3
  * Backend functions.
4
  *
5
- * @package Duplicate Post
6
  * @since 2.0
7
  */
8
 
@@ -10,10 +10,12 @@ if ( ! is_admin() ) {
10
  return;
11
  }
12
 
13
- require_once dirname( __FILE__ ) . '/duplicate-post-options.php';
14
 
15
- require_once dirname( __FILE__ ) . '/compat/duplicate-post-wpml.php';
16
- require_once dirname( __FILE__ ) . '/compat/duplicate-post-jetpack.php';
 
 
17
 
18
  /**
19
  * Wrapper for the option 'duplicate_post_version'.
@@ -40,7 +42,8 @@ function duplicate_post_admin_init() {
40
  if ( intval( get_site_option( 'duplicate_post_show_notice' ) ) === 1 ) {
41
  if ( is_multisite() ) {
42
  add_action( 'network_admin_notices', 'duplicate_post_show_update_notice' );
43
- } else {
 
44
  add_action( 'admin_notices', 'duplicate_post_show_update_notice' );
45
  }
46
  add_action( 'wp_ajax_duplicate_post_dismiss_notice', 'duplicate_post_dismiss_notice' );
@@ -82,12 +85,12 @@ function duplicate_post_plugin_upgrade() {
82
 
83
  if ( empty( $installed_version ) ) {
84
  // Get default roles.
85
- $default_roles = array(
86
  'editor',
87
  'administrator',
88
  'wpseo_manager',
89
  'wpseo_editor',
90
- );
91
 
92
  foreach ( $default_roles as $name ) {
93
  $role = get_role( $name );
@@ -119,9 +122,9 @@ function duplicate_post_plugin_upgrade() {
119
  add_option( 'duplicate_post_copychildren', '0' );
120
  add_option( 'duplicate_post_copycomments', '0' );
121
  add_option( 'duplicate_post_copymenuorder', '1' );
122
- add_option( 'duplicate_post_taxonomies_blacklist', array() );
123
  add_option( 'duplicate_post_blacklist', '' );
124
- add_option( 'duplicate_post_types_enabled', array( 'post', 'page' ) );
125
  add_option( 'duplicate_post_show_original_column', '0' );
126
  add_option( 'duplicate_post_show_original_in_post_states', '0' );
127
  add_option( 'duplicate_post_show_original_meta_box', '0' );
@@ -136,32 +139,32 @@ function duplicate_post_plugin_upgrade() {
136
  add_option( 'duplicate_post_show_link_in', $show_links_in_defaults );
137
 
138
  $taxonomies_blacklist = get_option( 'duplicate_post_taxonomies_blacklist' );
139
- if ( '' === $taxonomies_blacklist ) {
140
- $taxonomies_blacklist = array();
141
  }
142
  if ( in_array( 'post_format', $taxonomies_blacklist, true ) ) {
143
  update_option( 'duplicate_post_copyformat', 0 );
144
- $taxonomies_blacklist = array_diff( $taxonomies_blacklist, array( 'post_format' ) );
145
  update_option( 'duplicate_post_taxonomies_blacklist', $taxonomies_blacklist );
146
  }
147
 
148
  $meta_blacklist = explode( ',', get_option( 'duplicate_post_blacklist' ) );
149
- if ( '' === $meta_blacklist ) {
150
- $meta_blacklist = array();
151
  }
152
  $meta_blacklist = array_map( 'trim', $meta_blacklist );
153
  if ( in_array( '_wp_page_template', $meta_blacklist, true ) ) {
154
  update_option( 'duplicate_post_copytemplate', 0 );
155
- $meta_blacklist = array_diff( $meta_blacklist, array( '_wp_page_template' ) );
156
  }
157
  if ( in_array( '_thumbnail_id', $meta_blacklist, true ) ) {
158
  update_option( 'duplicate_post_copythumbnail', 0 );
159
- $meta_blacklist = array_diff( $meta_blacklist, array( '_thumbnail_id' ) );
160
  }
161
  update_option( 'duplicate_post_blacklist', implode( ',', $meta_blacklist ) );
162
 
163
  delete_option( 'duplicate_post_show_notice' );
164
- if ( version_compare( $installed_version, '4.1.0' ) < 0 ) {
165
  update_site_option( 'duplicate_post_show_notice', 1 );
166
  }
167
 
@@ -191,12 +194,12 @@ function duplicate_post_migrate_show_links_in_options( $defaults ) {
191
 
192
  $new_options = [];
193
  foreach ( $options_to_migrate as $old => $new ) {
194
- $new_options[ $new ] = \get_option( $old, $defaults[ $new ] );
195
 
196
- \delete_option( $old );
197
  }
198
 
199
- \update_option( 'duplicate_post_show_link_in', $new_options );
200
  }
201
 
202
  /**
@@ -210,10 +213,9 @@ function duplicate_post_show_update_notice() {
210
  }
211
 
212
  $current_screen = get_current_screen();
213
- if (
214
- empty( $current_screen ) ||
215
- empty( $current_screen->base ) ||
216
- ( $current_screen->base !== 'dashboard' && $current_screen->base !== 'plugins' )
217
  ) {
218
  return;
219
  }
@@ -224,37 +226,37 @@ function duplicate_post_show_update_notice() {
224
  __( "What's new in Yoast Duplicate Post version %s:", 'duplicate-post' ),
225
  DUPLICATE_POST_CURRENT_VERSION
226
  ) . '</strong> ';
227
- $message .= __( 'Now also available in Elementor: the powerful Rewrite & Republish feature. Updating your content has never been easier!', 'duplicate-post' )
228
  . ' ';
229
 
230
- $message .= '<a href="https://yoa.st/duplicate-post-4-1">'
231
  . sprintf(
232
  /* translators: %s: Yoast Duplicate Post version. */
233
- __( 'Read more about what’s new in Yoast Duplicate Post %s!', 'duplicate-post' ),
234
  DUPLICATE_POST_CURRENT_VERSION
235
  )
236
  . '</a></p>';
237
 
238
  $message .= '<p>%%SIGNUP_FORM%%</p>';
239
 
240
- $allowed_tags = array(
241
- 'a' => array(
242
- 'href' => array(),
243
- ),
244
- 'br' => array(),
245
- 'p' => array(),
246
- 'strong' => array(),
247
- );
248
 
249
  $sanitized_message = wp_kses( $message, $allowed_tags );
250
- $sanitized_message = str_replace( '%%SIGNUP_FORM%%', duplicate_post_newsletter_signup_form(), $sanitized_message );
251
 
252
  $img_path = plugins_url( '/duplicate_post_yoast_icon-125x125.png', __FILE__ );
253
 
254
- echo '<div id="duplicate-post-notice" class="' . esc_attr( $class ) . '" style="display: flex; align-items: center;">
255
- <img src="' . esc_url( $img_path ) . '" alt=""/>
256
- <div style="margin: 0.5em">' . $sanitized_message . // phpcs:ignore WordPress.Security.EscapeOutput
257
- '</div></div>';
258
 
259
  echo "<script>
260
  function duplicate_post_dismiss_notice(){
@@ -281,8 +283,7 @@ function duplicate_post_show_update_notice() {
281
  * @return bool
282
  */
283
  function duplicate_post_dismiss_notice() {
284
- $result = update_site_option( 'duplicate_post_show_notice', 0 );
285
- return $result;
286
  }
287
 
288
  /**
@@ -306,8 +307,8 @@ function duplicate_post_copy_post_taxonomies( $new_id, $post ) {
306
  }
307
 
308
  $taxonomies_blacklist = get_option( 'duplicate_post_taxonomies_blacklist' );
309
- if ( '' === $taxonomies_blacklist ) {
310
- $taxonomies_blacklist = array();
311
  }
312
  if ( intval( get_option( 'duplicate_post_copyformat' ) ) === 0 ) {
313
  $taxonomies_blacklist[] = 'post_format';
@@ -324,8 +325,8 @@ function duplicate_post_copy_post_taxonomies( $new_id, $post ) {
324
 
325
  $taxonomies = array_diff( $post_taxonomies, $taxonomies_blacklist );
326
  foreach ( $taxonomies as $taxonomy ) {
327
- $post_terms = wp_get_object_terms( $post->ID, $taxonomy, array( 'orderby' => 'term_order' ) );
328
- $terms = array();
329
  $num_terms = count( $post_terms );
330
  for ( $i = 0; $i < $num_terms; $i++ ) {
331
  $terms[] = $post_terms[ $i ]->slug;
@@ -347,9 +348,10 @@ function duplicate_post_copy_post_meta_info( $new_id, $post ) {
347
  return;
348
  }
349
  $meta_blacklist = get_option( 'duplicate_post_blacklist' );
350
- if ( '' === $meta_blacklist ) {
351
- $meta_blacklist = array();
352
- } else {
 
353
  $meta_blacklist = explode( ',', $meta_blacklist );
354
  $meta_blacklist = array_filter( $meta_blacklist );
355
  $meta_blacklist = array_map( 'trim', $meta_blacklist );
@@ -365,7 +367,8 @@ function duplicate_post_copy_post_meta_info( $new_id, $post ) {
365
  $meta_blacklist[] = '_thumbnail_id';
366
  }
367
 
368
- $meta_blacklist = apply_filters_deprecated( 'duplicate_post_blacklist_filter', array( $meta_blacklist ), '3.2.5', 'duplicate_post_excludelist_filter' );
 
369
  /**
370
  * Filters the meta fields excludelist when copying a post.
371
  *
@@ -377,15 +380,16 @@ function duplicate_post_copy_post_meta_info( $new_id, $post ) {
377
 
378
  $meta_blacklist_string = '(' . implode( ')|(', $meta_blacklist ) . ')';
379
  if ( strpos( $meta_blacklist_string, '*' ) !== false ) {
380
- $meta_blacklist_string = str_replace( array( '*' ), array( '[a-zA-Z0-9_]*' ), $meta_blacklist_string );
381
 
382
- $meta_keys = array();
383
  foreach ( $post_meta_keys as $meta_key ) {
384
  if ( ! preg_match( '#^' . $meta_blacklist_string . '$#', $meta_key ) ) {
385
  $meta_keys[] = $meta_key;
386
  }
387
  }
388
- } else {
 
389
  $meta_keys = array_diff( $post_meta_keys, $meta_blacklist );
390
  }
391
 
@@ -411,15 +415,14 @@ function duplicate_post_copy_post_meta_info( $new_id, $post ) {
411
  * Workaround for inconsistent wp_slash.
412
  * Works only with WP 4.4+ (map_deep)
413
  *
414
- * @ignore
415
- *
416
  * @param mixed $value Array or object to be recursively slashed.
417
  * @return string|mixed
418
  */
419
  function duplicate_post_addslashes_deep( $value ) {
420
  if ( function_exists( 'map_deep' ) ) {
421
  return map_deep( $value, 'duplicate_post_addslashes_to_strings_only' );
422
- } else {
 
423
  return wp_slash( $value );
424
  }
425
  }
@@ -427,20 +430,16 @@ function duplicate_post_addslashes_deep( $value ) {
427
  /**
428
  * Adds slashes only to strings.
429
  *
430
- * @ignore
431
- *
432
  * @param mixed $value Value to slash only if string.
433
  * @return string|mixed
434
  */
435
  function duplicate_post_addslashes_to_strings_only( $value ) {
436
- return is_string( $value ) ? addslashes( $value ) : $value;
437
  }
438
 
439
  /**
440
  * Replacement function for faulty core wp_slash().
441
  *
442
- * @ignore
443
- *
444
  * @param mixed $value What to add slash to.
445
  * @return mixed
446
  */
@@ -459,16 +458,16 @@ function duplicate_post_copy_attachments( $new_id, $post ) {
459
  $old_thumbnail_id = get_post_thumbnail_id( $post->ID );
460
  // Get children.
461
  $children = get_posts(
462
- array(
463
  'post_type' => 'any',
464
  'numberposts' => -1,
465
  'post_status' => 'any',
466
  'post_parent' => $post->ID,
467
- )
468
  );
469
  // Clone old attachments.
470
  foreach ( $children as $child ) {
471
- if ( 'attachment' !== $child->post_type ) {
472
  continue;
473
  }
474
  $url = wp_get_attachment_url( $child->ID );
@@ -480,7 +479,7 @@ function duplicate_post_copy_attachments( $new_id, $post ) {
480
 
481
  $desc = wp_slash( $child->post_content );
482
 
483
- $file_array = array();
484
  $file_array['name'] = basename( $url );
485
  $file_array['tmp_name'] = $tmp;
486
  // "Upload" to the media collection
@@ -491,12 +490,12 @@ function duplicate_post_copy_attachments( $new_id, $post ) {
491
  continue;
492
  }
493
  $new_post_author = wp_get_current_user();
494
- $cloned_child = array(
495
  'ID' => $new_attachment_id,
496
  'post_title' => $child->post_title,
497
  'post_exceprt' => $child->post_title,
498
  'post_author' => $new_post_author->ID,
499
- );
500
  wp_update_post( wp_slash( $cloned_child ) );
501
 
502
  $alt_title = get_post_meta( $child->ID, '_wp_attachment_image_alt', true );
@@ -521,16 +520,16 @@ function duplicate_post_copy_attachments( $new_id, $post ) {
521
  function duplicate_post_copy_children( $new_id, $post, $status = '' ) {
522
  // Get children.
523
  $children = get_posts(
524
- array(
525
  'post_type' => 'any',
526
  'numberposts' => -1,
527
  'post_status' => 'any',
528
  'post_parent' => $post->ID,
529
- )
530
  );
531
 
532
  foreach ( $children as $child ) {
533
- if ( 'attachment' === $child->post_type ) {
534
  continue;
535
  }
536
  duplicate_post_create_duplicate( $child, $status, $new_id );
@@ -545,21 +544,21 @@ function duplicate_post_copy_children( $new_id, $post, $status = '' ) {
545
  */
546
  function duplicate_post_copy_comments( $new_id, $post ) {
547
  $comments = get_comments(
548
- array(
549
  'post_id' => $post->ID,
550
  'order' => 'ASC',
551
  'orderby' => 'comment_date_gmt',
552
- )
553
  );
554
 
555
- $old_id_to_new = array();
556
  foreach ( $comments as $comment ) {
557
  // Do not copy pingbacks or trackbacks.
558
  if ( $comment->comment_type === 'pingback' || $comment->comment_type === 'trackback' ) {
559
  continue;
560
  }
561
  $parent = ( $comment->comment_parent && $old_id_to_new[ $comment->comment_parent ] ) ? $old_id_to_new[ $comment->comment_parent ] : 0;
562
- $commentdata = array(
563
  'comment_post_ID' => $new_id,
564
  'comment_author' => $comment->comment_author,
565
  'comment_author_email' => $comment->comment_author_email,
@@ -572,7 +571,7 @@ function duplicate_post_copy_comments( $new_id, $post ) {
572
  'comment_agent' => $comment->comment_agent,
573
  'comment_karma' => $comment->comment_karma,
574
  'comment_approved' => $comment->comment_approved,
575
- );
576
  if ( intval( get_option( 'duplicate_post_copydate' ) ) === 1 ) {
577
  $commentdata['comment_date'] = $comment->comment_date;
578
  $commentdata['comment_date_gmt'] = get_gmt_from_date( $comment->comment_date );
@@ -621,11 +620,11 @@ function duplicate_post_create_duplicate( $post, $status = '', $parent_id = '' )
621
  wp_die( esc_html( __( 'You aren\'t allowed to duplicate this post', 'duplicate-post' ) ) );
622
  }
623
 
624
- if ( ! duplicate_post_is_post_type_enabled( $post->post_type ) && 'attachment' !== $post->post_type ) {
625
  wp_die(
626
  esc_html(
627
- __( 'Copy features for this post type are not enabled in options page', 'duplicate-post' ) . ': ' .
628
- $post->post_type
629
  )
630
  );
631
  }
@@ -633,7 +632,7 @@ function duplicate_post_create_duplicate( $post, $status = '', $parent_id = '' )
633
  $new_post_status = ( empty( $status ) ) ? $post->post_status : $status;
634
  $title = ' ';
635
 
636
- if ( 'attachment' !== $post->post_type ) {
637
  $prefix = sanitize_text_field( get_option( 'duplicate_post_title_prefix' ) );
638
  $suffix = sanitize_text_field( get_option( 'duplicate_post_title_suffix' ) );
639
  if ( intval( get_option( 'duplicate_post_copytitle' ) ) === 1 ) {
@@ -644,7 +643,8 @@ function duplicate_post_create_duplicate( $post, $status = '', $parent_id = '' )
644
  if ( ! empty( $suffix ) ) {
645
  $suffix = ' ' . $suffix;
646
  }
647
- } else {
 
648
  $title = ' ';
649
  }
650
  $title = trim( $prefix . $title . $suffix );
@@ -659,19 +659,17 @@ function duplicate_post_create_duplicate( $post, $status = '', $parent_id = '' )
659
 
660
  if ( intval( get_option( 'duplicate_post_copystatus' ) ) === 0 ) {
661
  $new_post_status = 'draft';
662
- } else {
663
- if ( 'publish' === $new_post_status || 'future' === $new_post_status ) {
664
- // Check if the user has the right capability.
665
- if ( is_post_type_hierarchical( $post->post_type ) ) {
666
- if ( ! current_user_can( 'publish_pages' ) ) {
667
- $new_post_status = 'pending';
668
- }
669
- } else {
670
- if ( ! current_user_can( 'publish_posts' ) ) {
671
- $new_post_status = 'pending';
672
- }
673
  }
674
  }
 
 
 
675
  }
676
  }
677
 
@@ -683,10 +681,9 @@ function duplicate_post_create_duplicate( $post, $status = '', $parent_id = '' )
683
  if ( current_user_can( 'edit_others_pages' ) ) {
684
  $new_post_author_id = $post->post_author;
685
  }
686
- } else {
687
- if ( current_user_can( 'edit_others_posts' ) ) {
688
- $new_post_author_id = $post->post_author;
689
- }
690
  }
691
  }
692
 
@@ -702,7 +699,7 @@ function duplicate_post_create_duplicate( $post, $status = '', $parent_id = '' )
702
  }
703
  $new_post_parent = empty( $parent_id ) ? $post->post_parent : $parent_id;
704
 
705
- $new_post = array(
706
  'menu_order' => $menu_order,
707
  'comment_status' => $post->comment_status,
708
  'ping_status' => $post->ping_status,
@@ -717,7 +714,7 @@ function duplicate_post_create_duplicate( $post, $status = '', $parent_id = '' )
717
  'post_title' => $title,
718
  'post_type' => $post->post_type,
719
  'post_name' => $post_name,
720
- );
721
 
722
  if ( intval( get_option( 'duplicate_post_copydate' ) ) === 1 ) {
723
  $new_post_date = $post->post_date;
@@ -738,11 +735,12 @@ function duplicate_post_create_duplicate( $post, $status = '', $parent_id = '' )
738
 
739
  // If you have written a plugin which uses non-WP database tables to save
740
  // information about a post you can hook this action to dupe that data.
741
- if ( 0 !== $new_post_id && ! is_wp_error( $new_post_id ) ) {
742
 
743
- if ( 'page' === $post->post_type || is_post_type_hierarchical( $post->post_type ) ) {
744
  do_action( 'dp_duplicate_page', $new_post_id, $post, $status );
745
- } else {
 
746
  do_action( 'dp_duplicate_post', $new_post_id, $post, $status );
747
  }
748
 
@@ -771,50 +769,8 @@ function duplicate_post_create_duplicate( $post, $status = '', $parent_id = '' )
771
  * @return array
772
  */
773
  function duplicate_post_add_plugin_links( $links, $file ) {
774
- if ( plugin_basename( dirname( __FILE__ ) . '/duplicate-post.php' ) === $file ) {
775
  $links[] = '<a href="https://yoast.com/wordpress/plugins/duplicate-post">' . esc_html__( 'Documentation', 'duplicate-post' ) . '</a>';
776
  }
777
  return $links;
778
  }
779
-
780
- /**
781
- * Renders the newsletter signup form.
782
- *
783
- * @return string The HTML of the newsletter signup form (escaped).
784
- */
785
- function duplicate_post_newsletter_signup_form() {
786
- $copy = sprintf(
787
- /* translators: 1: Yoast */
788
- __(
789
- 'If you want to stay up to date about all the exciting developments around Duplicate Post, subscribe to the %1$s newsletter!',
790
- 'duplicate-post'
791
- ),
792
- 'Yoast'
793
- );
794
-
795
- $email_label = __( 'Email Address', 'duplicate-post' );
796
-
797
- $html = '
798
- <!-- Begin Mailchimp Signup Form -->
799
- <div id="mc_embed_signup">
800
- <form action="https://yoast.us1.list-manage.com/subscribe/post?u=ffa93edfe21752c921f860358&amp;id=972f1c9122" method="post" id="mc-embedded-subscribe-form" name="mc-embedded-subscribe-form" class="validate" target="_blank" novalidate>
801
- <div id="mc_embed_signup_scroll">
802
- ' . $copy . '
803
- <div class="mc-field-group" style="margin-top: 8px;">
804
- <label for="mce-EMAIL">' . $email_label . '</label>
805
- <input type="email" value="" name="EMAIL" class="required email" id="mce-EMAIL">
806
- <input type="submit" value="' . esc_attr__( 'Subscribe', 'duplicate-post' ) . '" name="subscribe" id="mc-embedded-subscribe" class="button">
807
- </div>
808
- <div id="mce-responses" class="clear">
809
- <div class="response" id="mce-error-response" style="display:none"></div>
810
- <div class="response" id="mce-success-response" style="display:none"></div>
811
- </div> <!-- real people should not fill this in and expect good things - do not remove this or risk form bot signups-->
812
- <div class="screen-reader-text" aria-hidden="true"><input type="text" name="b_ffa93edfe21752c921f860358_972f1c9122" tabindex="-1" value=""></div>
813
- </div>
814
- </form>
815
- </div>
816
- <!--End mc_embed_signup-->
817
- ';
818
-
819
- return $html;
820
- }
2
  /**
3
  * Backend functions.
4
  *
5
+ * @package Yoast\WP\Duplicate_Post
6
  * @since 2.0
7
  */
8
 
10
  return;
11
  }
12
 
13
+ use Yoast\WP\Duplicate_Post\UI\Newsletter;
14
 
15
+ require_once DUPLICATE_POST_PATH . 'options.php';
16
+
17
+ require_once DUPLICATE_POST_PATH . 'compat/wpml-functions.php';
18
+ require_once DUPLICATE_POST_PATH . 'compat/jetpack-functions.php';
19
 
20
  /**
21
  * Wrapper for the option 'duplicate_post_version'.
42
  if ( intval( get_site_option( 'duplicate_post_show_notice' ) ) === 1 ) {
43
  if ( is_multisite() ) {
44
  add_action( 'network_admin_notices', 'duplicate_post_show_update_notice' );
45
+ }
46
+ else {
47
  add_action( 'admin_notices', 'duplicate_post_show_update_notice' );
48
  }
49
  add_action( 'wp_ajax_duplicate_post_dismiss_notice', 'duplicate_post_dismiss_notice' );
85
 
86
  if ( empty( $installed_version ) ) {
87
  // Get default roles.
88
+ $default_roles = [
89
  'editor',
90
  'administrator',
91
  'wpseo_manager',
92
  'wpseo_editor',
93
+ ];
94
 
95
  foreach ( $default_roles as $name ) {
96
  $role = get_role( $name );
122
  add_option( 'duplicate_post_copychildren', '0' );
123
  add_option( 'duplicate_post_copycomments', '0' );
124
  add_option( 'duplicate_post_copymenuorder', '1' );
125
+ add_option( 'duplicate_post_taxonomies_blacklist', [] );
126
  add_option( 'duplicate_post_blacklist', '' );
127
+ add_option( 'duplicate_post_types_enabled', [ 'post', 'page' ] );
128
  add_option( 'duplicate_post_show_original_column', '0' );
129
  add_option( 'duplicate_post_show_original_in_post_states', '0' );
130
  add_option( 'duplicate_post_show_original_meta_box', '0' );
139
  add_option( 'duplicate_post_show_link_in', $show_links_in_defaults );
140
 
141
  $taxonomies_blacklist = get_option( 'duplicate_post_taxonomies_blacklist' );
142
+ if ( $taxonomies_blacklist === '' ) {
143
+ $taxonomies_blacklist = [];
144
  }
145
  if ( in_array( 'post_format', $taxonomies_blacklist, true ) ) {
146
  update_option( 'duplicate_post_copyformat', 0 );
147
+ $taxonomies_blacklist = array_diff( $taxonomies_blacklist, [ 'post_format' ] );
148
  update_option( 'duplicate_post_taxonomies_blacklist', $taxonomies_blacklist );
149
  }
150
 
151
  $meta_blacklist = explode( ',', get_option( 'duplicate_post_blacklist' ) );
152
+ if ( $meta_blacklist === '' ) {
153
+ $meta_blacklist = [];
154
  }
155
  $meta_blacklist = array_map( 'trim', $meta_blacklist );
156
  if ( in_array( '_wp_page_template', $meta_blacklist, true ) ) {
157
  update_option( 'duplicate_post_copytemplate', 0 );
158
+ $meta_blacklist = array_diff( $meta_blacklist, [ '_wp_page_template' ] );
159
  }
160
  if ( in_array( '_thumbnail_id', $meta_blacklist, true ) ) {
161
  update_option( 'duplicate_post_copythumbnail', 0 );
162
+ $meta_blacklist = array_diff( $meta_blacklist, [ '_thumbnail_id' ] );
163
  }
164
  update_option( 'duplicate_post_blacklist', implode( ',', $meta_blacklist ) );
165
 
166
  delete_option( 'duplicate_post_show_notice' );
167
+ if ( version_compare( $installed_version, '4.2.0' ) < 0 ) {
168
  update_site_option( 'duplicate_post_show_notice', 1 );
169
  }
170
 
194
 
195
  $new_options = [];
196
  foreach ( $options_to_migrate as $old => $new ) {
197
+ $new_options[ $new ] = get_option( $old, $defaults[ $new ] );
198
 
199
+ delete_option( $old );
200
  }
201
 
202
+ update_option( 'duplicate_post_show_link_in', $new_options );
203
  }
204
 
205
  /**
213
  }
214
 
215
  $current_screen = get_current_screen();
216
+ if ( empty( $current_screen )
217
+ || empty( $current_screen->base )
218
+ || ( $current_screen->base !== 'dashboard' && $current_screen->base !== 'plugins' )
 
219
  ) {
220
  return;
221
  }
226
  __( "What's new in Yoast Duplicate Post version %s:", 'duplicate-post' ),
227
  DUPLICATE_POST_CURRENT_VERSION
228
  ) . '</strong> ';
229
+ $message .= __( 'improved compatibility with PHP 8.0 and fixes for some bugs regarding translations and the Rewrite & Republish feature.', 'duplicate-post' )
230
  . ' ';
231
 
232
+ $message .= '<a href="https://wordpress.org/plugins/duplicate-post/#developers">'
233
  . sprintf(
234
  /* translators: %s: Yoast Duplicate Post version. */
235
+ __( 'Read the changelog', 'duplicate-post' ),
236
  DUPLICATE_POST_CURRENT_VERSION
237
  )
238
  . '</a></p>';
239
 
240
  $message .= '<p>%%SIGNUP_FORM%%</p>';
241
 
242
+ $allowed_tags = [
243
+ 'a' => [
244
+ 'href' => [],
245
+ ],
246
+ 'br' => [],
247
+ 'p' => [],
248
+ 'strong' => [],
249
+ ];
250
 
251
  $sanitized_message = wp_kses( $message, $allowed_tags );
252
+ $sanitized_message = str_replace( '%%SIGNUP_FORM%%', Newsletter::newsletter_signup_form(), $sanitized_message );
253
 
254
  $img_path = plugins_url( '/duplicate_post_yoast_icon-125x125.png', __FILE__ );
255
 
256
+ echo '<div id="duplicate-post-notice" class="' . esc_attr( $class ) . '" style="display: flex; align-items: flex-start;">
257
+ <img src="' . esc_url( $img_path ) . '" alt="" style="margin: 1.5em 0.5em 1.5em 0;"/>
258
+ <div style="margin: 0.5em">' . $sanitized_message // phpcs:ignore WordPress.Security.EscapeOutput -- Reason: escaped properly above.
259
+ . '</div></div>';
260
 
261
  echo "<script>
262
  function duplicate_post_dismiss_notice(){
283
  * @return bool
284
  */
285
  function duplicate_post_dismiss_notice() {
286
+ return update_site_option( 'duplicate_post_show_notice', 0 );
 
287
  }
288
 
289
  /**
307
  }
308
 
309
  $taxonomies_blacklist = get_option( 'duplicate_post_taxonomies_blacklist' );
310
+ if ( $taxonomies_blacklist === '' ) {
311
+ $taxonomies_blacklist = [];
312
  }
313
  if ( intval( get_option( 'duplicate_post_copyformat' ) ) === 0 ) {
314
  $taxonomies_blacklist[] = 'post_format';
325
 
326
  $taxonomies = array_diff( $post_taxonomies, $taxonomies_blacklist );
327
  foreach ( $taxonomies as $taxonomy ) {
328
+ $post_terms = wp_get_object_terms( $post->ID, $taxonomy, [ 'orderby' => 'term_order' ] );
329
+ $terms = [];
330
  $num_terms = count( $post_terms );
331
  for ( $i = 0; $i < $num_terms; $i++ ) {
332
  $terms[] = $post_terms[ $i ]->slug;
348
  return;
349
  }
350
  $meta_blacklist = get_option( 'duplicate_post_blacklist' );
351
+ if ( $meta_blacklist === '' ) {
352
+ $meta_blacklist = [];
353
+ }
354
+ else {
355
  $meta_blacklist = explode( ',', $meta_blacklist );
356
  $meta_blacklist = array_filter( $meta_blacklist );
357
  $meta_blacklist = array_map( 'trim', $meta_blacklist );
367
  $meta_blacklist[] = '_thumbnail_id';
368
  }
369
 
370
+ $meta_blacklist = apply_filters_deprecated( 'duplicate_post_blacklist_filter', [ $meta_blacklist ], '3.2.5', 'duplicate_post_excludelist_filter' );
371
+
372
  /**
373
  * Filters the meta fields excludelist when copying a post.
374
  *
380
 
381
  $meta_blacklist_string = '(' . implode( ')|(', $meta_blacklist ) . ')';
382
  if ( strpos( $meta_blacklist_string, '*' ) !== false ) {
383
+ $meta_blacklist_string = str_replace( [ '*' ], [ '[a-zA-Z0-9_]*' ], $meta_blacklist_string );
384
 
385
+ $meta_keys = [];
386
  foreach ( $post_meta_keys as $meta_key ) {
387
  if ( ! preg_match( '#^' . $meta_blacklist_string . '$#', $meta_key ) ) {
388
  $meta_keys[] = $meta_key;
389
  }
390
  }
391
+ }
392
+ else {
393
  $meta_keys = array_diff( $post_meta_keys, $meta_blacklist );
394
  }
395
 
415
  * Workaround for inconsistent wp_slash.
416
  * Works only with WP 4.4+ (map_deep)
417
  *
 
 
418
  * @param mixed $value Array or object to be recursively slashed.
419
  * @return string|mixed
420
  */
421
  function duplicate_post_addslashes_deep( $value ) {
422
  if ( function_exists( 'map_deep' ) ) {
423
  return map_deep( $value, 'duplicate_post_addslashes_to_strings_only' );
424
+ }
425
+ else {
426
  return wp_slash( $value );
427
  }
428
  }
430
  /**
431
  * Adds slashes only to strings.
432
  *
 
 
433
  * @param mixed $value Value to slash only if string.
434
  * @return string|mixed
435
  */
436
  function duplicate_post_addslashes_to_strings_only( $value ) {
437
+ return Yoast\WP\Duplicate_Post\Utils::addslashes_to_strings_only( $value );
438
  }
439
 
440
  /**
441
  * Replacement function for faulty core wp_slash().
442
  *
 
 
443
  * @param mixed $value What to add slash to.
444
  * @return mixed
445
  */
458
  $old_thumbnail_id = get_post_thumbnail_id( $post->ID );
459
  // Get children.
460
  $children = get_posts(
461
+ [
462
  'post_type' => 'any',
463
  'numberposts' => -1,
464
  'post_status' => 'any',
465
  'post_parent' => $post->ID,
466
+ ]
467
  );
468
  // Clone old attachments.
469
  foreach ( $children as $child ) {
470
+ if ( $child->post_type !== 'attachment' ) {
471
  continue;
472
  }
473
  $url = wp_get_attachment_url( $child->ID );
479
 
480
  $desc = wp_slash( $child->post_content );
481
 
482
+ $file_array = [];
483
  $file_array['name'] = basename( $url );
484
  $file_array['tmp_name'] = $tmp;
485
  // "Upload" to the media collection
490
  continue;
491
  }
492
  $new_post_author = wp_get_current_user();
493
+ $cloned_child = [
494
  'ID' => $new_attachment_id,
495
  'post_title' => $child->post_title,
496
  'post_exceprt' => $child->post_title,
497
  'post_author' => $new_post_author->ID,
498
+ ];
499
  wp_update_post( wp_slash( $cloned_child ) );
500
 
501
  $alt_title = get_post_meta( $child->ID, '_wp_attachment_image_alt', true );
520
  function duplicate_post_copy_children( $new_id, $post, $status = '' ) {
521
  // Get children.
522
  $children = get_posts(
523
+ [
524
  'post_type' => 'any',
525
  'numberposts' => -1,
526
  'post_status' => 'any',
527
  'post_parent' => $post->ID,
528
+ ]
529
  );
530
 
531
  foreach ( $children as $child ) {
532
+ if ( $child->post_type === 'attachment' ) {
533
  continue;
534
  }
535
  duplicate_post_create_duplicate( $child, $status, $new_id );
544
  */
545
  function duplicate_post_copy_comments( $new_id, $post ) {
546
  $comments = get_comments(
547
+ [
548
  'post_id' => $post->ID,
549
  'order' => 'ASC',
550
  'orderby' => 'comment_date_gmt',
551
+ ]
552
  );
553
 
554
+ $old_id_to_new = [];
555
  foreach ( $comments as $comment ) {
556
  // Do not copy pingbacks or trackbacks.
557
  if ( $comment->comment_type === 'pingback' || $comment->comment_type === 'trackback' ) {
558
  continue;
559
  }
560
  $parent = ( $comment->comment_parent && $old_id_to_new[ $comment->comment_parent ] ) ? $old_id_to_new[ $comment->comment_parent ] : 0;
561
+ $commentdata = [
562
  'comment_post_ID' => $new_id,
563
  'comment_author' => $comment->comment_author,
564
  'comment_author_email' => $comment->comment_author_email,
571
  'comment_agent' => $comment->comment_agent,
572
  'comment_karma' => $comment->comment_karma,
573
  'comment_approved' => $comment->comment_approved,
574
+ ];
575
  if ( intval( get_option( 'duplicate_post_copydate' ) ) === 1 ) {
576
  $commentdata['comment_date'] = $comment->comment_date;
577
  $commentdata['comment_date_gmt'] = get_gmt_from_date( $comment->comment_date );
620
  wp_die( esc_html( __( 'You aren\'t allowed to duplicate this post', 'duplicate-post' ) ) );
621
  }
622
 
623
+ if ( ! duplicate_post_is_post_type_enabled( $post->post_type ) && $post->post_type !== 'attachment' ) {
624
  wp_die(
625
  esc_html(
626
+ __( 'Copy features for this post type are not enabled in options page', 'duplicate-post' ) . ': '
627
+ . $post->post_type
628
  )
629
  );
630
  }
632
  $new_post_status = ( empty( $status ) ) ? $post->post_status : $status;
633
  $title = ' ';
634
 
635
+ if ( $post->post_type !== 'attachment' ) {
636
  $prefix = sanitize_text_field( get_option( 'duplicate_post_title_prefix' ) );
637
  $suffix = sanitize_text_field( get_option( 'duplicate_post_title_suffix' ) );
638
  if ( intval( get_option( 'duplicate_post_copytitle' ) ) === 1 ) {
643
  if ( ! empty( $suffix ) ) {
644
  $suffix = ' ' . $suffix;
645
  }
646
+ }
647
+ else {
648
  $title = ' ';
649
  }
650
  $title = trim( $prefix . $title . $suffix );
659
 
660
  if ( intval( get_option( 'duplicate_post_copystatus' ) ) === 0 ) {
661
  $new_post_status = 'draft';
662
+ }
663
+ elseif ( $new_post_status === 'publish' || $new_post_status === 'future' ) {
664
+ // Check if the user has the right capability.
665
+ if ( is_post_type_hierarchical( $post->post_type ) ) {
666
+ if ( ! current_user_can( 'publish_pages' ) ) {
667
+ $new_post_status = 'pending';
 
 
 
 
 
668
  }
669
  }
670
+ elseif ( ! current_user_can( 'publish_posts' ) ) {
671
+ $new_post_status = 'pending';
672
+ }
673
  }
674
  }
675
 
681
  if ( current_user_can( 'edit_others_pages' ) ) {
682
  $new_post_author_id = $post->post_author;
683
  }
684
+ }
685
+ elseif ( current_user_can( 'edit_others_posts' ) ) {
686
+ $new_post_author_id = $post->post_author;
 
687
  }
688
  }
689
 
699
  }
700
  $new_post_parent = empty( $parent_id ) ? $post->post_parent : $parent_id;
701
 
702
+ $new_post = [
703
  'menu_order' => $menu_order,
704
  'comment_status' => $post->comment_status,
705
  'ping_status' => $post->ping_status,
714
  'post_title' => $title,
715
  'post_type' => $post->post_type,
716
  'post_name' => $post_name,
717
+ ];
718
 
719
  if ( intval( get_option( 'duplicate_post_copydate' ) ) === 1 ) {
720
  $new_post_date = $post->post_date;
735
 
736
  // If you have written a plugin which uses non-WP database tables to save
737
  // information about a post you can hook this action to dupe that data.
738
+ if ( $new_post_id !== 0 && ! is_wp_error( $new_post_id ) ) {
739
 
740
+ if ( $post->post_type === 'page' || is_post_type_hierarchical( $post->post_type ) ) {
741
  do_action( 'dp_duplicate_page', $new_post_id, $post, $status );
742
+ }
743
+ else {
744
  do_action( 'dp_duplicate_post', $new_post_id, $post, $status );
745
  }
746
 
769
  * @return array
770
  */
771
  function duplicate_post_add_plugin_links( $links, $file ) {
772
+ if ( plugin_basename( __DIR__ . '/duplicate-post.php' ) === $file ) {
773
  $links[] = '<a href="https://yoast.com/wordpress/plugins/duplicate-post">' . esc_html__( 'Documentation', 'duplicate-post' ) . '</a>';
774
  }
775
  return $links;
776
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
duplicate-post-common.php → common-functions.php RENAMED
@@ -2,8 +2,8 @@
2
  /**
3
  * Common functions.
4
  *
5
- * @package Duplicate Post
6
- * @since 2.0
7
  */
8
 
9
  use Yoast\WP\Duplicate_Post\Permissions_Helper;
@@ -17,11 +17,12 @@ use Yoast\WP\Duplicate_Post\Utils;
17
  * @return bool
18
  */
19
  function duplicate_post_is_post_type_enabled( $post_type ) {
20
- $duplicate_post_types_enabled = get_option( 'duplicate_post_types_enabled', array( 'post', 'page' ) );
21
  if ( ! is_array( $duplicate_post_types_enabled ) ) {
22
- $duplicate_post_types_enabled = array( $duplicate_post_types_enabled );
23
  }
24
- /** This filter is documented in src/class-permissions-helper.php */
 
25
  $duplicate_post_types_enabled = apply_filters( 'duplicate_post_enabled_post_types', $duplicate_post_types_enabled );
26
  return in_array( $post_type, $duplicate_post_types_enabled, true );
27
  }
@@ -49,7 +50,8 @@ function duplicate_post_get_clone_post_link( $id = 0, $context = 'display', $dra
49
 
50
  if ( $draft ) {
51
  return $link_builder->build_new_draft_link( $post, $context );
52
- } else {
 
53
  return $link_builder->build_clone_link( $post, $context );
54
  }
55
  }
@@ -73,11 +75,11 @@ function duplicate_post_clone_post_link( $link = null, $before = '', $after = ''
73
  return;
74
  }
75
 
76
- if ( null === $link ) {
77
- $link = esc_html__( 'Copy to a new draft', 'duplicate-post' );
78
  }
79
 
80
- $link = '<a class="post-clone-link" href="' . esc_url( $url ) . '">' . $link . '</a>';
81
 
82
  /**
83
  * Filter on the clone link HTML.
2
  /**
3
  * Common functions.
4
  *
5
+ * @package Yoast\WP\Duplicate_Post
6
+ * @since 2.0
7
  */
8
 
9
  use Yoast\WP\Duplicate_Post\Permissions_Helper;
17
  * @return bool
18
  */
19
  function duplicate_post_is_post_type_enabled( $post_type ) {
20
+ $duplicate_post_types_enabled = get_option( 'duplicate_post_types_enabled', [ 'post', 'page' ] );
21
  if ( ! is_array( $duplicate_post_types_enabled ) ) {
22
+ $duplicate_post_types_enabled = [ $duplicate_post_types_enabled ];
23
  }
24
+
25
+ /** This filter is documented in src/permissions-helper.php */
26
  $duplicate_post_types_enabled = apply_filters( 'duplicate_post_enabled_post_types', $duplicate_post_types_enabled );
27
  return in_array( $post_type, $duplicate_post_types_enabled, true );
28
  }
50
 
51
  if ( $draft ) {
52
  return $link_builder->build_new_draft_link( $post, $context );
53
+ }
54
+ else {
55
  return $link_builder->build_clone_link( $post, $context );
56
  }
57
  }
75
  return;
76
  }
77
 
78
+ if ( $link === null ) {
79
+ $link = __( 'Copy to a new draft', 'duplicate-post' );
80
  }
81
 
82
+ $link = '<a class="post-clone-link" href="' . esc_url( $url ) . '">' . esc_html( $link ) . '</a>';
83
 
84
  /**
85
  * Filter on the clone link HTML.
compat/duplicate-post-gutenberg.php DELETED
@@ -1,39 +0,0 @@
1
- <?php
2
- /**
3
- * Gutenberg (Block editor)/Classic Editor compatibility functions
4
- *
5
- * @package Duplicate Post
6
- * @since 4.0
7
- */
8
-
9
- add_filter( 'duplicate_post_get_clone_post_link', 'duplicate_post_classic_editor_clone_link', 10, 4 );
10
-
11
- /**
12
- * Edits the clone link URL to enforce Classic Editor legacy support.
13
- *
14
- * @see duplicate_post_get_clone_post_link()
15
- *
16
- * @param string $url The duplicate post link URL.
17
- * @param int $post_id The original post ID.
18
- * @param string $context The context in which the URL is used.
19
- * @param bool $draft Whether the link is "New Draft" or "Clone".
20
- *
21
- * @return string
22
- */
23
- function duplicate_post_classic_editor_clone_link( $url, $post_id, $context, $draft ) {
24
- $post = get_post( $post_id );
25
- if ( ! $post ) {
26
- return $url;
27
- }
28
-
29
- if ( isset( $_GET['classic-editor'] ) // phpcs:ignore WordPress.Security.NonceVerification
30
- || ( $draft && function_exists( 'gutenberg_post_has_blocks' ) && ! gutenberg_post_has_blocks( $post ) )
31
- || ( $draft && function_exists( 'has_blocks' ) && ! has_blocks( $post ) ) ) {
32
- if ( 'display' === $context ) {
33
- $url .= '&amp;classic-editor';
34
- } else {
35
- $url .= '&classic-editor';
36
- }
37
- }
38
- return $url;
39
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
compat/{duplicate-post-jetpack.php → jetpack-functions.php} RENAMED
@@ -2,8 +2,8 @@
2
  /**
3
  * JetPack compatibility functions.
4
  *
5
- * @package Duplicate Post
6
- * @since 3.2
7
  */
8
 
9
  add_action( 'admin_init', 'duplicate_post_jetpack_init' );
2
  /**
3
  * JetPack compatibility functions.
4
  *
5
+ * @package Yoast\WP\Duplicate_Post
6
+ * @since 3.2
7
  */
8
 
9
  add_action( 'admin_init', 'duplicate_post_jetpack_init' );
compat/{duplicate-post-wpml.php → wpml-functions.php} RENAMED
@@ -4,8 +4,8 @@
4
  *
5
  * @global array $duplicated_posts Array to store the posts being duplicated.
6
  *
7
- * @package Duplicate Post
8
- * @since 3.2
9
  */
10
 
11
  add_action( 'admin_init', 'duplicate_post_wpml_init' );
@@ -21,8 +21,10 @@ function duplicate_post_wpml_init() {
21
  }
22
  }
23
 
24
- global $duplicated_posts; // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals
25
- $duplicated_posts = array(); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals
 
 
26
 
27
  /**
28
  * Copy post translations.
@@ -66,7 +68,9 @@ function duplicate_post_wpml_copy_translations( $post_id, $post, $status = '' )
66
  }
67
  }
68
  }
69
- $duplicated_posts[ $post->ID ] = $post_id; // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals
 
 
70
  }
71
  }
72
 
@@ -75,16 +79,19 @@ function duplicate_post_wpml_copy_translations( $post_id, $post, $status = '' )
75
  *
76
  * @global array() $duplicated_posts Array of duplicated posts.
77
  */
78
- function duplicate_wpml_string_packages() { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals
79
  global $duplicated_posts;
80
 
81
  foreach ( $duplicated_posts as $original_post_id => $duplicate_post_id ) {
 
 
 
 
 
82
 
83
- $original_string_packages = apply_filters( 'wpml_st_get_post_string_packages', false, $original_post_id ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals
84
- $new_string_packages = apply_filters( 'wpml_st_get_post_string_packages', false, $duplicate_post_id ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals
85
  if ( is_array( $original_string_packages ) ) {
86
  foreach ( $original_string_packages as $original_string_package ) {
87
- $translated_original_strings = $original_string_package->get_translated_strings( array() );
88
 
89
  foreach ( $new_string_packages as $new_string_package ) {
90
  $cache = new WPML_WP_Cache( 'WPML_Package' );
@@ -96,7 +103,8 @@ function duplicate_wpml_string_packages() { // phpcs:ignore WordPress.NamingConv
96
  foreach ( $translated_original_strings[ $new_string->name ] as $language => $translated_string ) {
97
 
98
  do_action(
99
- 'wpml_add_string_translation', // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals
 
100
  $new_string->id,
101
  $language,
102
  $translated_string['value'],
4
  *
5
  * @global array $duplicated_posts Array to store the posts being duplicated.
6
  *
7
+ * @package Yoast\WP\Duplicate_Post
8
+ * @since 3.2
9
  */
10
 
11
  add_action( 'admin_init', 'duplicate_post_wpml_init' );
21
  }
22
  }
23
 
24
+ global $duplicated_posts;
25
+
26
+ // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals -- Reason: Renaming a global variable is a BC break.
27
+ $duplicated_posts = [];
28
 
29
  /**
30
  * Copy post translations.
68
  }
69
  }
70
  }
71
+
72
+ // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals -- Reason: see above.
73
+ $duplicated_posts[ $post->ID ] = $post_id;
74
  }
75
  }
76
 
79
  *
80
  * @global array() $duplicated_posts Array of duplicated posts.
81
  */
82
+ function duplicate_wpml_string_packages() { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals -- Reason: renaming the function would be a BC-break.
83
  global $duplicated_posts;
84
 
85
  foreach ( $duplicated_posts as $original_post_id => $duplicate_post_id ) {
86
+ // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals -- Reason: using WPML native filter.
87
+ $original_string_packages = apply_filters( 'wpml_st_get_post_string_packages', false, $original_post_id );
88
+
89
+ // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals -- Reason: using WPML native filter.
90
+ $new_string_packages = apply_filters( 'wpml_st_get_post_string_packages', false, $duplicate_post_id );
91
 
 
 
92
  if ( is_array( $original_string_packages ) ) {
93
  foreach ( $original_string_packages as $original_string_package ) {
94
+ $translated_original_strings = $original_string_package->get_translated_strings( [] );
95
 
96
  foreach ( $new_string_packages as $new_string_package ) {
97
  $cache = new WPML_WP_Cache( 'WPML_Package' );
103
  foreach ( $translated_original_strings[ $new_string->name ] as $language => $translated_string ) {
104
 
105
  do_action(
106
+ // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals -- Reason: using WPML native filter.
107
+ 'wpml_add_string_translation',
108
  $new_string->id,
109
  $language,
110
  $translated_string['value'],
duplicate-post.php CHANGED
@@ -1,16 +1,19 @@
1
  <?php
2
  /**
 
 
 
 
 
 
3
  * Plugin Name: Yoast Duplicate Post
4
- * Plugin URI: https://yoast.com/wordpress/plugins/duplicate-post/
5
  * Description: The go-to tool for cloning posts and pages, including the powerful Rewrite & Republish feature.
6
- * Version: 4.1.2
7
- * Author: Enrico Battocchi & Team Yoast
8
- * Author URI: https://yoast.com
9
  * Text Domain: duplicate-post
10
  *
11
- * @package Duplicate Post
12
- * @since 0.1
13
- *
14
  * Copyright 2020 Yoast BV (email : info@yoast.com)
15
  *
16
  * This program is free software; you can redistribute it and/or modify
@@ -42,13 +45,15 @@ if ( ! defined( 'DUPLICATE_POST_PATH' ) ) {
42
  define( 'DUPLICATE_POST_PATH', plugin_dir_path( __FILE__ ) );
43
  }
44
 
45
- define( 'DUPLICATE_POST_CURRENT_VERSION', '4.1.2' );
46
 
47
- $duplicate_post_autoload_file = __DIR__ . '/vendor/autoload.php';
48
 
49
  if ( is_readable( $duplicate_post_autoload_file ) ) {
50
  require $duplicate_post_autoload_file;
 
51
 
 
52
  // Initialize the main autoloaded class.
53
  add_action( 'plugins_loaded', '__duplicate_post_main' );
54
  }
@@ -56,7 +61,11 @@ if ( is_readable( $duplicate_post_autoload_file ) ) {
56
  /**
57
  * Loads the Duplicate Post main class.
58
  *
59
- * @phpcs:disable PHPCompatibility.FunctionNameRestrictions.ReservedFunctionNames.FunctionDoubleUnderscore,WordPress.NamingConventions.ValidFunctionName.FunctionDoubleUnderscore,WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound -- Function name change would be BC-break.
 
 
 
 
60
  */
61
  function __duplicate_post_main() {
62
  new Duplicate_Post();
@@ -67,7 +76,7 @@ function __duplicate_post_main() {
67
  * Initialises the internationalisation domain.
68
  */
69
  function duplicate_post_load_plugin_textdomain() {
70
- load_plugin_textdomain( 'duplicate-post', false, basename( dirname( __FILE__ ) ) . '/languages/' );
71
  }
72
  add_action( 'plugins_loaded', 'duplicate_post_load_plugin_textdomain' );
73
 
@@ -76,28 +85,27 @@ add_filter( 'plugin_action_links_' . plugin_basename( __FILE__ ), 'duplicate_pos
76
  /**
77
  * Adds 'Settings' link to plugin entry in the Plugins list.
78
  *
79
- * @ignore
80
  * @see 'plugin_action_links_$plugin_file'
81
  *
82
  * @param array $actions An array of plugin action links.
83
  * @return array
84
  */
85
  function duplicate_post_plugin_actions( $actions ) {
86
- $settings_action = array(
87
  'settings' => sprintf(
88
  '<a href="%1$s" %2$s>%3$s</a>',
89
  menu_page_url( 'duplicatepost', false ),
90
  'aria-label="' . __( 'Settings for Duplicate Post', 'duplicate-post' ) . '"',
91
- esc_html__( 'Settings', 'default' )
92
  ),
93
- );
94
 
95
- $actions = $settings_action + $actions;
96
  return $actions;
97
  }
98
 
99
- require_once dirname( __FILE__ ) . '/duplicate-post-common.php';
100
 
101
  if ( is_admin() ) {
102
- include_once dirname( __FILE__ ) . '/duplicate-post-admin.php';
103
  }
1
  <?php
2
  /**
3
+ * Duplicate Post plugin.
4
+ *
5
+ * @package Yoast\WP\Duplicate_Post
6
+ * @since 0.1
7
+ *
8
+ * @wordpress-plugin
9
  * Plugin Name: Yoast Duplicate Post
10
+ * Plugin URI: https://yoast.com/wordpress/plugins/duplicate-post/
11
  * Description: The go-to tool for cloning posts and pages, including the powerful Rewrite & Republish feature.
12
+ * Version: 4.2
13
+ * Author: Enrico Battocchi & Team Yoast
14
+ * Author URI: https://yoast.com
15
  * Text Domain: duplicate-post
16
  *
 
 
 
17
  * Copyright 2020 Yoast BV (email : info@yoast.com)
18
  *
19
  * This program is free software; you can redistribute it and/or modify
45
  define( 'DUPLICATE_POST_PATH', plugin_dir_path( __FILE__ ) );
46
  }
47
 
48
+ define( 'DUPLICATE_POST_CURRENT_VERSION', '4.2' );
49
 
50
+ $duplicate_post_autoload_file = DUPLICATE_POST_PATH . 'vendor/autoload.php';
51
 
52
  if ( is_readable( $duplicate_post_autoload_file ) ) {
53
  require $duplicate_post_autoload_file;
54
+ }
55
 
56
+ if ( class_exists( Duplicate_Post::class ) ) {
57
  // Initialize the main autoloaded class.
58
  add_action( 'plugins_loaded', '__duplicate_post_main' );
59
  }
61
  /**
62
  * Loads the Duplicate Post main class.
63
  *
64
+ * {@internal Function name change would be BC-break.}
65
+ *
66
+ * @phpcs:disable PHPCompatibility.FunctionNameRestrictions.ReservedFunctionNames.FunctionDoubleUnderscore
67
+ * @phpcs:disable WordPress.NamingConventions.ValidFunctionName.FunctionDoubleUnderscore
68
+ * @phpcs:disable WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound
69
  */
70
  function __duplicate_post_main() {
71
  new Duplicate_Post();
76
  * Initialises the internationalisation domain.
77
  */
78
  function duplicate_post_load_plugin_textdomain() {
79
+ load_plugin_textdomain( 'duplicate-post', false, basename( __DIR__ ) . '/languages/' );
80
  }
81
  add_action( 'plugins_loaded', 'duplicate_post_load_plugin_textdomain' );
82
 
85
  /**
86
  * Adds 'Settings' link to plugin entry in the Plugins list.
87
  *
 
88
  * @see 'plugin_action_links_$plugin_file'
89
  *
90
  * @param array $actions An array of plugin action links.
91
  * @return array
92
  */
93
  function duplicate_post_plugin_actions( $actions ) {
94
+ $settings_action = [
95
  'settings' => sprintf(
96
  '<a href="%1$s" %2$s>%3$s</a>',
97
  menu_page_url( 'duplicatepost', false ),
98
  'aria-label="' . __( 'Settings for Duplicate Post', 'duplicate-post' ) . '"',
99
+ esc_html__( 'Settings', 'duplicate-post' )
100
  ),
101
+ ];
102
 
103
+ $actions = ( $settings_action + $actions );
104
  return $actions;
105
  }
106
 
107
+ require_once DUPLICATE_POST_PATH . 'common-functions.php';
108
 
109
  if ( is_admin() ) {
110
+ include_once DUPLICATE_POST_PATH . 'admin-functions.php';
111
  }
js/dist/{duplicate-post-edit-412.js → duplicate-post-edit-420.js} RENAMED
File without changes
js/dist/{duplicate-post-elementor-412.js → duplicate-post-elementor-420.js} RENAMED
File without changes
js/dist/{duplicate-post-options-412.js → duplicate-post-options-420.js} RENAMED
File without changes
js/dist/{duplicate-post-quick-edit-412.js → duplicate-post-quick-edit-420.js} RENAMED
File without changes
js/dist/{duplicate-post-strings-412.js → duplicate-post-strings-420.js} RENAMED
File without changes
duplicate-post-options.php → options.php RENAMED
@@ -1,10 +1,4 @@
1
  <?php
2
- /**
3
- * Options page
4
- *
5
- * @package Duplicate Post
6
- * @since 2.0
7
- */
8
 
9
  namespace Yoast\WP\Duplicate_Post;
10
 
@@ -14,7 +8,7 @@ use Yoast\WP\Duplicate_Post\Admin\Options_Inputs;
14
  use Yoast\WP\Duplicate_Post\Admin\Options_Page;
15
  use Yoast\WP\Duplicate_Post\UI\Asset_Manager;
16
 
17
- if ( ! defined( 'ABSPATH' ) ) {
18
  exit();
19
  }
20
 
1
  <?php
 
 
 
 
 
 
2
 
3
  namespace Yoast\WP\Duplicate_Post;
4
 
8
  use Yoast\WP\Duplicate_Post\Admin\Options_Page;
9
  use Yoast\WP\Duplicate_Post\UI\Asset_Manager;
10
 
11
+ if ( ! \defined( 'ABSPATH' ) ) {
12
  exit();
13
  }
14
 
readme.txt CHANGED
@@ -4,7 +4,7 @@ Donate link: https://yoast.com/wordpress/plugins/duplicate-post/
4
  Tags: duplicate post, copy, clone
5
  Requires at least: 5.6
6
  Tested up to: 5.8
7
- Stable tag: 4.1.2
8
  Requires PHP: 5.6.20
9
  License: GPLv2 or later
10
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
@@ -151,6 +151,21 @@ New features and customization, WP 3.0 compatibility: you should upgrade if you
151
 
152
  == Changelog ==
153
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
154
  = 4.1.2 (2021-03-23) =
155
 
156
  Enhancements:
4
  Tags: duplicate post, copy, clone
5
  Requires at least: 5.6
6
  Tested up to: 5.8
7
+ Stable tag: 4.2
8
  Requires PHP: 5.6.20
9
  License: GPLv2 or later
10
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
151
 
152
  == Changelog ==
153
 
154
+ = 4.2 (2021-11-18) =
155
+
156
+ Bugfixes:
157
+
158
+ * Fixes a bug where some strings where not translated.
159
+ * Fixes a bug where meta data went missing for duplicate posts created by the Rewrite & Republish feature. Props to [ocean90](https://github.com/ocean90).
160
+ * Fixes a bug where the plugin would not initialize if installed via composer.
161
+
162
+ Other:
163
+
164
+ * Replaces Mailchimp with MailBlue newsletter integration.
165
+ * Improved compatibility for PHP 8.0.
166
+ * Improved sort order of the taxonomies list on the options page.
167
+ * Most plugin files have been renamed. If you extend this plugin of include any of the files from the plugin from within your own plugin, you may need to adjust the file name used in the `include`/`require` statement.
168
+
169
  = 4.1.2 (2021-03-23) =
170
 
171
  Enhancements:
src/admin/{class-options-form-generator.php → options-form-generator.php} RENAMED
@@ -1,18 +1,12 @@
1
  <?php
2
- /**
3
- * Duplicate Post plugin file.
4
- *
5
- * @package Yoast\WP\Duplicate_Post\Admin
6
- */
7
 
8
  namespace Yoast\WP\Duplicate_Post\Admin;
9
 
 
10
  use Yoast\WP\Duplicate_Post\Utils;
11
 
12
  /**
13
- * Class Options_Form_Generator
14
- *
15
- * @package Yoast\WP\Duplicate_Post\Admin
16
  */
17
  class Options_Form_Generator {
18
 
@@ -109,16 +103,25 @@ class Options_Form_Generator {
109
  }
110
 
111
  /**
112
- * Sorts taxonomy objects based on being public, followed by being private.
 
113
  *
114
- * @param \WP_Taxonomy $taxonomy1 First taxonomy object.
115
- * @param \WP_Taxonomy $taxonomy2 Second taxonomy object.
116
  *
117
- * @return bool True when the first taxonomy is public.
118
- * @ignore
119
  */
120
  public function sort_taxonomy_objects( $taxonomy1, $taxonomy2 ) {
121
- return ( $taxonomy1->public < $taxonomy2->public );
 
 
 
 
 
 
 
 
122
  }
123
 
124
  /**
@@ -171,7 +174,7 @@ class Options_Form_Generator {
171
  'id' => 'duplicate-post-' . $this->prepare_input_id( $name ),
172
  'value' => $name,
173
  'checked' => \in_array( $taxonomy->name, $taxonomies_blacklist, true ),
174
- 'label' => \esc_html( $taxonomy->labels->name . ' [' . $taxonomy->name . ']' ),
175
  ],
176
  ]
177
  );
@@ -199,7 +202,7 @@ class Options_Form_Generator {
199
  foreach ( Utils::get_roles() as $name => $display_name ) {
200
  $role = \get_role( $name );
201
 
202
- if ( count( \array_intersect_key( $role->capabilities, $edit_capabilities ) ) > 0 ) {
203
  $output .= $this->generate_options_input(
204
  [
205
  'duplicate_post_roles[]' => [
@@ -207,7 +210,7 @@ class Options_Form_Generator {
207
  'id' => 'duplicate-post-' . $this->prepare_input_id( $name ),
208
  'value' => $name,
209
  'checked' => $role->has_cap( 'copy_posts' ),
210
- 'label' => \esc_html( \translate_user_role( $display_name ) ),
211
  ],
212
  ]
213
  );
@@ -241,7 +244,7 @@ class Options_Form_Generator {
241
  'id' => 'duplicate-post-' . $this->prepare_input_id( $name ),
242
  'value' => $name,
243
  'checked' => $this->is_post_type_enabled( $post_type_object->name ),
244
- 'label' => \esc_html( $post_type_object->labels->name ),
245
  ],
246
  ]
247
  );
@@ -291,10 +294,11 @@ class Options_Form_Generator {
291
  /**
292
  * Checks whether or not a post type is enabled.
293
  *
 
 
294
  * @param string $post_type The post type.
295
  *
296
  * @return bool Whether or not the post type is enabled.
297
- * @codeCoverageIgnore As this is a simple wrapper for a function that is also being used elsewhere, we can skip testing for now.
298
  */
299
  public function is_post_type_enabled( $post_type ) {
300
  $duplicate_post_types_enabled = \get_option( 'duplicate_post_types_enabled', [ 'post', 'page' ] );
1
  <?php
 
 
 
 
 
2
 
3
  namespace Yoast\WP\Duplicate_Post\Admin;
4
 
5
+ use WP_Taxonomy;
6
  use Yoast\WP\Duplicate_Post\Utils;
7
 
8
  /**
9
+ * Class Options_Form_Generator.
 
 
10
  */
11
  class Options_Form_Generator {
12
 
103
  }
104
 
105
  /**
106
+ * Sorts taxonomy objects based on being public, followed by being private
107
+ * and when the visibility is equal, on the taxonomy public name (case-sensitive).
108
  *
109
+ * @param WP_Taxonomy $taxonomy1 First taxonomy object.
110
+ * @param WP_Taxonomy $taxonomy2 Second taxonomy object.
111
  *
112
+ * @return int An integer less than, equal to, or greater than zero indicating respectively
113
+ * the first taxonomy should be sorted before, at the same level or after the second taxonomy.
114
  */
115
  public function sort_taxonomy_objects( $taxonomy1, $taxonomy2 ) {
116
+ if ( $taxonomy1->public === true && $taxonomy2->public === false ) {
117
+ return -1;
118
+ }
119
+ elseif ( $taxonomy1->public === false && $taxonomy2->public === true ) {
120
+ return 1;
121
+ }
122
+
123
+ // Same visibility, sort by name.
124
+ return \strnatcmp( $taxonomy1->labels->name, $taxonomy2->labels->name );
125
  }
126
 
127
  /**
174
  'id' => 'duplicate-post-' . $this->prepare_input_id( $name ),
175
  'value' => $name,
176
  'checked' => \in_array( $taxonomy->name, $taxonomies_blacklist, true ),
177
+ 'label' => $taxonomy->labels->name . ' [' . $taxonomy->name . ']',
178
  ],
179
  ]
180
  );
202
  foreach ( Utils::get_roles() as $name => $display_name ) {
203
  $role = \get_role( $name );
204
 
205
+ if ( \count( \array_intersect_key( $role->capabilities, $edit_capabilities ) ) > 0 ) {
206
  $output .= $this->generate_options_input(
207
  [
208
  'duplicate_post_roles[]' => [
210
  'id' => 'duplicate-post-' . $this->prepare_input_id( $name ),
211
  'value' => $name,
212
  'checked' => $role->has_cap( 'copy_posts' ),
213
+ 'label' => \translate_user_role( $display_name ),
214
  ],
215
  ]
216
  );
244
  'id' => 'duplicate-post-' . $this->prepare_input_id( $name ),
245
  'value' => $name,
246
  'checked' => $this->is_post_type_enabled( $post_type_object->name ),
247
+ 'label' => $post_type_object->labels->name,
248
  ],
249
  ]
250
  );
294
  /**
295
  * Checks whether or not a post type is enabled.
296
  *
297
+ * @codeCoverageIgnore As this is a simple wrapper for a function that is also being used elsewhere, we can skip testing for now.
298
+ *
299
  * @param string $post_type The post type.
300
  *
301
  * @return bool Whether or not the post type is enabled.
 
302
  */
303
  public function is_post_type_enabled( $post_type ) {
304
  $duplicate_post_types_enabled = \get_option( 'duplicate_post_types_enabled', [ 'post', 'page' ] );
src/admin/{class-options-inputs.php → options-inputs.php} RENAMED
@@ -1,25 +1,20 @@
1
  <?php
2
- /**
3
- * Duplicate Post plugin file.
4
- *
5
- * @package Yoast\WP\Duplicate_Post\Admin
6
- */
7
 
8
  namespace Yoast\WP\Duplicate_Post\Admin;
9
 
10
  /**
11
- * Class Options_Inputs
12
  */
13
  class Options_Inputs {
14
 
15
  /**
16
  * Creates a basic input based on the passed parameters.
17
  *
18
- * @param string $type The type of input.
19
- * @param string $name The name of the input.
20
- * @param string $value The value of the input.
21
- * @param string $id The ID of the input.
22
- * @param string $attributes The additional attributes to use. Optional.
23
  *
24
  * @return string The input's HTML output.
25
  */
@@ -37,15 +32,15 @@ class Options_Inputs {
37
  /**
38
  * Creates a checkbox input.
39
  *
40
- * @param string $name The name of the checkbox.
41
- * @param string $value The value of the checkbox.
42
- * @param string $id The ID of the checkbox.
43
  * @param bool $checked Whether or not the checkbox should be checked.
44
  *
45
  * @return string The checkbox' HTML output.
46
  */
47
  public function checkbox( $name, $value, $id, $checked = false ) {
48
- $checked = $checked ? 'checked="checked"' : '';
49
 
50
  return $this->input( 'checkbox', $name, $value, $id, $checked );
51
  }
@@ -53,9 +48,9 @@ class Options_Inputs {
53
  /**
54
  * Creates a text field input.
55
  *
56
- * @param string $name The name of the text field.
57
  * @param string $value The value of the text field.
58
- * @param string $id The ID of the text field.
59
  *
60
  * @return string The text field's HTML output.
61
  */
@@ -66,9 +61,9 @@ class Options_Inputs {
66
  /**
67
  * Creates a number input.
68
  *
69
- * @param string $name The name of the number input.
70
  * @param string $value The value of the number input.
71
- * @param string $id The ID of the number input.
72
  *
73
  * @return string The number input's HTML output.
74
  */
1
  <?php
 
 
 
 
 
2
 
3
  namespace Yoast\WP\Duplicate_Post\Admin;
4
 
5
  /**
6
+ * Class Options_Inputs.
7
  */
8
  class Options_Inputs {
9
 
10
  /**
11
  * Creates a basic input based on the passed parameters.
12
  *
13
+ * @param string $type The type of input.
14
+ * @param string $name The name of the input.
15
+ * @param string $value The value of the input.
16
+ * @param string $id The ID of the input.
17
+ * @param string $attributes The additional attributes to use. Optional.
18
  *
19
  * @return string The input's HTML output.
20
  */
32
  /**
33
  * Creates a checkbox input.
34
  *
35
+ * @param string $name The name of the checkbox.
36
+ * @param string $value The value of the checkbox.
37
+ * @param string $id The ID of the checkbox.
38
  * @param bool $checked Whether or not the checkbox should be checked.
39
  *
40
  * @return string The checkbox' HTML output.
41
  */
42
  public function checkbox( $name, $value, $id, $checked = false ) {
43
+ $checked = \checked( $checked, true, false );
44
 
45
  return $this->input( 'checkbox', $name, $value, $id, $checked );
46
  }
48
  /**
49
  * Creates a text field input.
50
  *
51
+ * @param string $name The name of the text field.
52
  * @param string $value The value of the text field.
53
+ * @param string $id The ID of the text field.
54
  *
55
  * @return string The text field's HTML output.
56
  */
61
  /**
62
  * Creates a number input.
63
  *
64
+ * @param string $name The name of the number input.
65
  * @param string $value The value of the number input.
66
+ * @param string $id The ID of the number input.
67
  *
68
  * @return string The number input's HTML output.
69
  */
src/admin/{class-options-page.php → options-page.php} RENAMED
@@ -1,9 +1,4 @@
1
  <?php
2
- /**
3
- * Duplicate Post plugin file.
4
- *
5
- * @package Yoast\WP\Duplicate_Post\Admin
6
- */
7
 
8
  namespace Yoast\WP\Duplicate_Post\Admin;
9
 
@@ -11,9 +6,10 @@ use Yoast\WP\Duplicate_Post\UI\Asset_Manager;
11
  use Yoast\WP\Duplicate_Post\Utils;
12
 
13
  /**
14
- * Class Options_Page
15
  */
16
  class Options_Page {
 
17
  /**
18
  * The Options instance.
19
  *
@@ -90,11 +86,12 @@ class Options_Page {
90
  /**
91
  * Generates the inputs for the specified tab / fieldset.
92
  *
 
 
93
  * @param string $tab The tab to get the configuration for.
94
  * @param string $fieldset The fieldset to get the configuration for. Optional.
95
  *
96
  * @return string The HTML output for the controls present on the tab / fieldset.
97
- * @codeCoverageIgnore As this is a simple wrapper for two functions that are already tested elsewhere, we can skip testing.
98
  */
99
  public function generate_tab_inputs( $tab, $fieldset = '' ) {
100
  $options = $this->options->get_options_for_tab( $tab, $fieldset );
@@ -105,10 +102,11 @@ class Options_Page {
105
  /**
106
  * Generates an input for a single option.
107
  *
 
 
108
  * @param string $option The option configuration to base the input on.
109
  *
110
  * @return string The input HTML.
111
- * @codeCoverageIgnore As this is a simple wrapper for two functions that are already tested elsewhere, we can skip testing.
112
  */
113
  public function generate_input( $option ) {
114
  return $this->generator->generate_options_input( $this->options->get_option( $option ) );
@@ -144,13 +142,14 @@ class Options_Page {
144
  /**
145
  * Generates the options page.
146
  *
147
- * @return void
148
  * @codeCoverageIgnore
 
 
149
  */
150
  public function generate_page() {
151
  $this->register_capabilities();
152
 
153
- require_once DUPLICATE_POST_PATH . 'src/admin/views/options.php';
154
  }
155
 
156
  /**
@@ -159,7 +158,7 @@ class Options_Page {
159
  * @return bool Whether or not the settings have been updated.
160
  */
161
  protected function settings_updated() {
162
- return isset( $_GET['settings-updated'] ) && $_GET['settings-updated'] === 'true'; // phpcs:ignore WordPress.Security.NonceVerification
163
  }
164
 
165
  /**
1
  <?php
 
 
 
 
 
2
 
3
  namespace Yoast\WP\Duplicate_Post\Admin;
4
 
6
  use Yoast\WP\Duplicate_Post\Utils;
7
 
8
  /**
9
+ * Class Options_Page.
10
  */
11
  class Options_Page {
12
+
13
  /**
14
  * The Options instance.
15
  *
86
  /**
87
  * Generates the inputs for the specified tab / fieldset.
88
  *
89
+ * @codeCoverageIgnore As this is a simple wrapper for two functions that are already tested elsewhere, we can skip testing.
90
+ *
91
  * @param string $tab The tab to get the configuration for.
92
  * @param string $fieldset The fieldset to get the configuration for. Optional.
93
  *
94
  * @return string The HTML output for the controls present on the tab / fieldset.
 
95
  */
96
  public function generate_tab_inputs( $tab, $fieldset = '' ) {
97
  $options = $this->options->get_options_for_tab( $tab, $fieldset );
102
  /**
103
  * Generates an input for a single option.
104
  *
105
+ * @codeCoverageIgnore As this is a simple wrapper for two functions that are already tested elsewhere, we can skip testing.
106
+ *
107
  * @param string $option The option configuration to base the input on.
108
  *
109
  * @return string The input HTML.
 
110
  */
111
  public function generate_input( $option ) {
112
  return $this->generator->generate_options_input( $this->options->get_option( $option ) );
142
  /**
143
  * Generates the options page.
144
  *
 
145
  * @codeCoverageIgnore
146
+ *
147
+ * @return void
148
  */
149
  public function generate_page() {
150
  $this->register_capabilities();
151
 
152
+ require_once \DUPLICATE_POST_PATH . 'src/admin/views/options.php';
153
  }
154
 
155
  /**
158
  * @return bool Whether or not the settings have been updated.
159
  */
160
  protected function settings_updated() {
161
+ return isset( $_GET['settings-updated'] ) && $_GET['settings-updated'] === 'true';
162
  }
163
 
164
  /**
src/admin/{class-options.php → options.php} RENAMED
@@ -1,15 +1,11 @@
1
  <?php
2
- /**
3
- * Options class
4
- *
5
- * @package Duplicate Post
6
- * @since 4.0
7
- */
8
 
9
  namespace Yoast\WP\Duplicate_Post\Admin;
10
 
11
  /**
12
- * Class Options
 
 
13
  */
14
  class Options {
15
 
@@ -39,7 +35,7 @@ class Options {
39
 
40
  $options = \array_filter(
41
  $options,
42
- function ( $option ) use ( $tab ) {
43
  return \array_key_exists( 'tab', $option ) && $option['tab'] === $tab;
44
  }
45
  );
@@ -52,7 +48,7 @@ class Options {
52
  if ( ! empty( $fieldset ) ) {
53
  $options = \array_filter(
54
  $options,
55
- function ( $option ) use ( $fieldset ) {
56
  return \array_key_exists( 'fieldset', $option ) && $option['fieldset'] === $fieldset;
57
  }
58
  );
@@ -77,8 +73,9 @@ class Options {
77
  /**
78
  * Gets the list of registered options.
79
  *
80
- * @return array The options.
81
  * @codeCoverageIgnore
 
 
82
  */
83
  public function get_options() {
84
  return [
@@ -86,84 +83,84 @@ class Options {
86
  'tab' => 'what-to-copy',
87
  'fieldset' => 'elements-to-copy',
88
  'type' => 'checkbox',
89
- 'label' => \__( 'Title', 'default' ),
90
  'value' => 1,
91
  ],
92
  'duplicate_post_copydate' => [
93
  'tab' => 'what-to-copy',
94
  'fieldset' => 'elements-to-copy',
95
  'type' => 'checkbox',
96
- 'label' => \__( 'Date', 'default' ),
97
  'value' => 1,
98
  ],
99
  'duplicate_post_copystatus' => [
100
  'tab' => 'what-to-copy',
101
  'fieldset' => 'elements-to-copy',
102
  'type' => 'checkbox',
103
- 'label' => \__( 'Status', 'default' ),
104
  'value' => 1,
105
  ],
106
  'duplicate_post_copyslug' => [
107
  'tab' => 'what-to-copy',
108
  'fieldset' => 'elements-to-copy',
109
  'type' => 'checkbox',
110
- 'label' => \__( 'Slug', 'default' ),
111
  'value' => 1,
112
  ],
113
  'duplicate_post_copyexcerpt' => [
114
  'tab' => 'what-to-copy',
115
  'fieldset' => 'elements-to-copy',
116
  'type' => 'checkbox',
117
- 'label' => \__( 'Excerpt', 'default' ),
118
  'value' => 1,
119
  ],
120
  'duplicate_post_copycontent' => [
121
  'tab' => 'what-to-copy',
122
  'fieldset' => 'elements-to-copy',
123
  'type' => 'checkbox',
124
- 'label' => \__( 'Content', 'default' ),
125
  'value' => 1,
126
  ],
127
  'duplicate_post_copythumbnail' => [
128
  'tab' => 'what-to-copy',
129
  'fieldset' => 'elements-to-copy',
130
  'type' => 'checkbox',
131
- 'label' => \__( 'Featured Image', 'default' ),
132
  'value' => 1,
133
  ],
134
  'duplicate_post_copytemplate' => [
135
  'tab' => 'what-to-copy',
136
  'fieldset' => 'elements-to-copy',
137
  'type' => 'checkbox',
138
- 'label' => \__( 'Template', 'default' ),
139
  'value' => 1,
140
  ],
141
  'duplicate_post_copyformat' => [
142
  'tab' => 'what-to-copy',
143
  'fieldset' => 'elements-to-copy',
144
  'type' => 'checkbox',
145
- 'label' => \__( 'Post format', 'default' ),
146
  'value' => 1,
147
  ],
148
  'duplicate_post_copyauthor' => [
149
  'tab' => 'what-to-copy',
150
  'fieldset' => 'elements-to-copy',
151
  'type' => 'checkbox',
152
- 'label' => \__( 'Author', 'default' ),
153
  'value' => 1,
154
  ],
155
  'duplicate_post_copypassword' => [
156
  'tab' => 'what-to-copy',
157
  'fieldset' => 'elements-to-copy',
158
  'type' => 'checkbox',
159
- 'label' => \__( 'Password', 'default' ),
160
  'value' => 1,
161
  ],
162
  'duplicate_post_copyattachments' => [
163
  'tab' => 'what-to-copy',
164
  'fieldset' => 'elements-to-copy',
165
  'type' => 'checkbox',
166
- 'label' => \__( 'Attachments', 'default' ),
167
  'value' => 1,
168
  'description' => \__( 'you probably want this unchecked, unless you have very special requirements', 'duplicate-post' ),
169
  ],
@@ -171,14 +168,14 @@ class Options {
171
  'tab' => 'what-to-copy',
172
  'fieldset' => 'elements-to-copy',
173
  'type' => 'checkbox',
174
- 'label' => \__( 'Children', 'default' ),
175
  'value' => 1,
176
  ],
177
  'duplicate_post_copycomments' => [
178
  'tab' => 'what-to-copy',
179
  'fieldset' => 'elements-to-copy',
180
  'type' => 'checkbox',
181
- 'label' => \__( 'Comments', 'default' ),
182
  'value' => 1,
183
  'description' => \__( 'except pingbacks and trackbacks', 'duplicate-post' ),
184
  ],
@@ -186,7 +183,7 @@ class Options {
186
  'tab' => 'what-to-copy',
187
  'fieldset' => 'elements-to-copy',
188
  'type' => 'checkbox',
189
- 'label' => \__( 'Menu order', 'default' ),
190
  'value' => 1,
191
  ],
192
  'duplicate_post_title_prefix' => [
@@ -216,8 +213,8 @@ class Options {
216
  'label' => \__( 'Do not copy these fields', 'duplicate-post' ),
217
  'value' => \get_option( 'duplicate_post_blacklist' ),
218
  'description' => [
219
- __( 'Comma-separated list of meta fields that must not be copied.', 'duplicate-post' ),
220
- __( 'You can use * to match zero or more alphanumeric characters or underscores: e.g. field*', 'duplicate-post' ),
221
  ],
222
  ],
223
  'duplicate_post_taxonomies_blacklist' => [
@@ -239,7 +236,7 @@ class Options {
239
  'label' => \__( 'In a metabox in the Edit screen', 'duplicate-post' ),
240
  'value' => 1,
241
  'description' => [
242
- __( "You'll also be able to delete the reference to the original item with a checkbox", 'duplicate-post' ),
243
  ],
244
  ],
245
  'duplicate_post_show_original_column' => [
@@ -249,7 +246,7 @@ class Options {
249
  'label' => \__( 'In a column in the Post list', 'duplicate-post' ),
250
  'value' => 1,
251
  'description' => [
252
- __( "You'll also be able to delete the reference to the original item with a checkbox in Quick Edit", 'duplicate-post' ),
253
  ],
254
  ],
255
  'duplicate_post_show_original_in_post_states' => [
@@ -307,7 +304,7 @@ class Options {
307
  ],
308
  'bulkactions' => [
309
  'type' => 'checkbox',
310
- 'label' => \__( 'Bulk Actions', 'default' ),
311
  'value' => 1,
312
  ],
313
  ],
1
  <?php
 
 
 
 
 
 
2
 
3
  namespace Yoast\WP\Duplicate_Post\Admin;
4
 
5
  /**
6
+ * Options class.
7
+ *
8
+ * @since 4.0
9
  */
10
  class Options {
11
 
35
 
36
  $options = \array_filter(
37
  $options,
38
+ static function ( $option ) use ( $tab ) {
39
  return \array_key_exists( 'tab', $option ) && $option['tab'] === $tab;
40
  }
41
  );
48
  if ( ! empty( $fieldset ) ) {
49
  $options = \array_filter(
50
  $options,
51
+ static function ( $option ) use ( $fieldset ) {
52
  return \array_key_exists( 'fieldset', $option ) && $option['fieldset'] === $fieldset;
53
  }
54
  );
73
  /**
74
  * Gets the list of registered options.
75
  *
 
76
  * @codeCoverageIgnore
77
+ *
78
+ * @return array The options.
79
  */
80
  public function get_options() {
81
  return [
83
  'tab' => 'what-to-copy',
84
  'fieldset' => 'elements-to-copy',
85
  'type' => 'checkbox',
86
+ 'label' => \__( 'Title', 'duplicate-post' ),
87
  'value' => 1,
88
  ],
89
  'duplicate_post_copydate' => [
90
  'tab' => 'what-to-copy',
91
  'fieldset' => 'elements-to-copy',
92
  'type' => 'checkbox',
93
+ 'label' => \__( 'Date', 'duplicate-post' ),
94
  'value' => 1,
95
  ],
96
  'duplicate_post_copystatus' => [
97
  'tab' => 'what-to-copy',
98
  'fieldset' => 'elements-to-copy',
99
  'type' => 'checkbox',
100
+ 'label' => \__( 'Status', 'duplicate-post' ),
101
  'value' => 1,
102
  ],
103
  'duplicate_post_copyslug' => [
104
  'tab' => 'what-to-copy',
105
  'fieldset' => 'elements-to-copy',
106
  'type' => 'checkbox',
107
+ 'label' => \__( 'Slug', 'duplicate-post' ),
108
  'value' => 1,
109
  ],
110
  'duplicate_post_copyexcerpt' => [
111
  'tab' => 'what-to-copy',
112
  'fieldset' => 'elements-to-copy',
113
  'type' => 'checkbox',
114
+ 'label' => \__( 'Excerpt', 'duplicate-post' ),
115
  'value' => 1,
116
  ],
117
  'duplicate_post_copycontent' => [
118
  'tab' => 'what-to-copy',
119
  'fieldset' => 'elements-to-copy',
120
  'type' => 'checkbox',
121
+ 'label' => \__( 'Content', 'duplicate-post' ),
122
  'value' => 1,
123
  ],
124
  'duplicate_post_copythumbnail' => [
125
  'tab' => 'what-to-copy',
126
  'fieldset' => 'elements-to-copy',
127
  'type' => 'checkbox',
128
+ 'label' => \__( 'Featured Image', 'duplicate-post' ),
129
  'value' => 1,
130
  ],
131
  'duplicate_post_copytemplate' => [
132
  'tab' => 'what-to-copy',
133
  'fieldset' => 'elements-to-copy',
134
  'type' => 'checkbox',
135
+ 'label' => \__( 'Template', 'duplicate-post' ),
136
  'value' => 1,
137
  ],
138
  'duplicate_post_copyformat' => [
139
  'tab' => 'what-to-copy',
140
  'fieldset' => 'elements-to-copy',
141
  'type' => 'checkbox',
142
+ 'label' => \__( 'Post format', 'duplicate-post' ),
143
  'value' => 1,
144
  ],
145
  'duplicate_post_copyauthor' => [
146
  'tab' => 'what-to-copy',
147
  'fieldset' => 'elements-to-copy',
148
  'type' => 'checkbox',
149
+ 'label' => \__( 'Author', 'duplicate-post' ),
150
  'value' => 1,
151
  ],
152
  'duplicate_post_copypassword' => [
153
  'tab' => 'what-to-copy',
154
  'fieldset' => 'elements-to-copy',
155
  'type' => 'checkbox',
156
+ 'label' => \__( 'Password', 'duplicate-post' ),
157
  'value' => 1,
158
  ],
159
  'duplicate_post_copyattachments' => [
160
  'tab' => 'what-to-copy',
161
  'fieldset' => 'elements-to-copy',
162
  'type' => 'checkbox',
163
+ 'label' => \__( 'Attachments', 'duplicate-post' ),
164
  'value' => 1,
165
  'description' => \__( 'you probably want this unchecked, unless you have very special requirements', 'duplicate-post' ),
166
  ],
168
  'tab' => 'what-to-copy',
169
  'fieldset' => 'elements-to-copy',
170
  'type' => 'checkbox',
171
+ 'label' => \__( 'Children', 'duplicate-post' ),
172
  'value' => 1,
173
  ],
174
  'duplicate_post_copycomments' => [
175
  'tab' => 'what-to-copy',
176
  'fieldset' => 'elements-to-copy',
177
  'type' => 'checkbox',
178
+ 'label' => \__( 'Comments', 'duplicate-post' ),
179
  'value' => 1,
180
  'description' => \__( 'except pingbacks and trackbacks', 'duplicate-post' ),
181
  ],
183
  'tab' => 'what-to-copy',
184
  'fieldset' => 'elements-to-copy',
185
  'type' => 'checkbox',
186
+ 'label' => \__( 'Menu order', 'duplicate-post' ),
187
  'value' => 1,
188
  ],
189
  'duplicate_post_title_prefix' => [
213
  'label' => \__( 'Do not copy these fields', 'duplicate-post' ),
214
  'value' => \get_option( 'duplicate_post_blacklist' ),
215
  'description' => [
216
+ \__( 'Comma-separated list of meta fields that must not be copied.', 'duplicate-post' ),
217
+ \__( 'You can use * to match zero or more alphanumeric characters or underscores: e.g. field*', 'duplicate-post' ),
218
  ],
219
  ],
220
  'duplicate_post_taxonomies_blacklist' => [
236
  'label' => \__( 'In a metabox in the Edit screen', 'duplicate-post' ),
237
  'value' => 1,
238
  'description' => [
239
+ \__( "You'll also be able to delete the reference to the original item with a checkbox", 'duplicate-post' ),
240
  ],
241
  ],
242
  'duplicate_post_show_original_column' => [
246
  'label' => \__( 'In a column in the Post list', 'duplicate-post' ),
247
  'value' => 1,
248
  'description' => [
249
+ \__( "You'll also be able to delete the reference to the original item with a checkbox in Quick Edit", 'duplicate-post' ),
250
  ],
251
  ],
252
  'duplicate_post_show_original_in_post_states' => [
304
  ],
305
  'bulkactions' => [
306
  'type' => 'checkbox',
307
+ 'label' => \__( 'Bulk Actions', 'duplicate-post' ),
308
  'value' => 1,
309
  ],
310
  ],
src/admin/views/options.php CHANGED
@@ -1,15 +1,13 @@
1
  <?php
2
- /**
3
- * Duplicate Post plugin file.
4
- *
5
- * @package Yoast\WP\Duplicate_Post\Admin\Views
6
- */
7
 
8
- if ( ! defined( 'DUPLICATE_POST_CURRENT_VERSION' ) ) {
9
- header( 'Status: 403 Forbidden' );
10
- header( 'HTTP/1.1 403 Forbidden' );
 
 
11
  exit();
12
  }
 
13
  ?>
14
  <div class="wrap">
15
  <h1>
@@ -214,7 +212,7 @@ if ( ! defined( 'DUPLICATE_POST_CURRENT_VERSION' ) ) {
214
  <br/>
215
  <?php
216
  \printf(
217
- /* translators: 1: Code start tag, 2: Code closing tag, 3: Link start tag to the template tag documentation, 4: Link closing tag. */
218
  \esc_html__( 'You can also use the template tag %1$sduplicate_post_clone_post_link( $link, $before, $after, $id )%2$s. %3$sMore info on the template tag%4$s.', 'duplicate-post' ),
219
  '<code>',
220
  '</code>',
1
  <?php
 
 
 
 
 
2
 
3
+ namespace Yoast\WP\Duplicate_Post\Admin\Views;
4
+
5
+ if ( ! \defined( 'DUPLICATE_POST_CURRENT_VERSION' ) ) {
6
+ \header( 'Status: 403 Forbidden' );
7
+ \header( 'HTTP/1.1 403 Forbidden' );
8
  exit();
9
  }
10
+
11
  ?>
12
  <div class="wrap">
13
  <h1>
212
  <br/>
213
  <?php
214
  \printf(
215
+ /* translators: 1: Code start tag, 2: Code closing tag, 3: Link start tag to the template tag documentation, 4: Link closing tag. */
216
  \esc_html__( 'You can also use the template tag %1$sduplicate_post_clone_post_link( $link, $before, $after, $id )%2$s. %3$sMore info on the template tag%4$s.', 'duplicate-post' ),
217
  '<code>',
218
  '</code>',
src/{class-duplicate-post.php → duplicate-post.php} RENAMED
@@ -1,10 +1,4 @@
1
  <?php
2
- /**
3
- * Duplicate Post main class.
4
- *
5
- * @package Duplicate_Post
6
- * @since 4.0
7
- */
8
 
9
  namespace Yoast\WP\Duplicate_Post;
10
 
@@ -13,7 +7,9 @@ use Yoast\WP\Duplicate_Post\UI\User_Interface;
13
  use Yoast\WP\Duplicate_Post\Watchers\Watchers;
14
 
15
  /**
16
- * Represents the Duplicate Post main class.
 
 
17
  */
18
  class Duplicate_Post {
19
 
1
  <?php
 
 
 
 
 
 
2
 
3
  namespace Yoast\WP\Duplicate_Post;
4
 
7
  use Yoast\WP\Duplicate_Post\Watchers\Watchers;
8
 
9
  /**
10
+ * Duplicate Post main class.
11
+ *
12
+ * @since 4.0
13
  */
14
  class Duplicate_Post {
15
 
src/handlers/{class-bulk-handler.php → bulk-handler.php} RENAMED
@@ -1,10 +1,4 @@
1
  <?php
2
- /**
3
- * Duplicate Post handler class for duplication bulk actions.
4
- *
5
- * @package Duplicate_Post
6
- * @since 4.0
7
- */
8
 
9
  namespace Yoast\WP\Duplicate_Post\Handlers;
10
 
@@ -13,7 +7,9 @@ use Yoast\WP\Duplicate_Post\Post_Duplicator;
13
  use Yoast\WP\Duplicate_Post\Utils;
14
 
15
  /**
16
- * Represents the handler for duplication bulk actions.
 
 
17
  */
18
  class Bulk_Handler {
19
 
@@ -99,7 +95,7 @@ class Bulk_Handler {
99
  if ( ! empty( $post ) && $this->permissions_helper->should_rewrite_and_republish_be_allowed( $post ) ) {
100
  $new_post_id = $this->post_duplicator->create_duplicate_for_rewrite_and_republish( $post );
101
  if ( ! \is_wp_error( $new_post_id ) ) {
102
- $counter ++;
103
  }
104
  }
105
  }
@@ -131,7 +127,7 @@ class Bulk_Handler {
131
  || ( \is_post_type_hierarchical( $post->post_type ) && ! Utils::has_ancestors_marked( $post, $post_ids ) )
132
  ) {
133
  if ( ! \is_wp_error( \duplicate_post_create_duplicate( $post ) ) ) {
134
- $counter ++;
135
  }
136
  }
137
  }
1
  <?php
 
 
 
 
 
 
2
 
3
  namespace Yoast\WP\Duplicate_Post\Handlers;
4
 
7
  use Yoast\WP\Duplicate_Post\Utils;
8
 
9
  /**
10
+ * Duplicate Post handler class for duplication bulk actions.
11
+ *
12
+ * @since 4.0
13
  */
14
  class Bulk_Handler {
15
 
95
  if ( ! empty( $post ) && $this->permissions_helper->should_rewrite_and_republish_be_allowed( $post ) ) {
96
  $new_post_id = $this->post_duplicator->create_duplicate_for_rewrite_and_republish( $post );
97
  if ( ! \is_wp_error( $new_post_id ) ) {
98
+ ++$counter;
99
  }
100
  }
101
  }
127
  || ( \is_post_type_hierarchical( $post->post_type ) && ! Utils::has_ancestors_marked( $post, $post_ids ) )
128
  ) {
129
  if ( ! \is_wp_error( \duplicate_post_create_duplicate( $post ) ) ) {
130
+ ++$counter;
131
  }
132
  }
133
  }
src/handlers/{class-check-changes-handler.php → check-changes-handler.php} RENAMED
@@ -1,18 +1,17 @@
1
  <?php
2
- /**
3
- * Duplicate Post handler class for changes overview.
4
- *
5
- * @package Duplicate_Post
6
- * @since 4.0
7
- */
8
 
9
  namespace Yoast\WP\Duplicate_Post\Handlers;
10
 
 
11
  use Yoast\WP\Duplicate_Post\Permissions_Helper;
12
  use Yoast\WP\Duplicate_Post\Utils;
13
 
14
  /**
 
 
15
  * Represents the handler for checking the changes between a copy and the original post.
 
 
16
  */
17
  class Check_Changes_Handler {
18
 
@@ -26,14 +25,14 @@ class Check_Changes_Handler {
26
  /**
27
  * Holds the current post object.
28
  *
29
- * @var \WP_Post
30
  */
31
  private $post;
32
 
33
  /**
34
  * Holds the original post object.
35
  *
36
- * @var \WP_Post
37
  */
38
  private $original;
39
 
@@ -63,17 +62,17 @@ class Check_Changes_Handler {
63
  public function check_changes_action_handler() {
64
  global $wp_version;
65
 
66
- if ( ! ( isset( $_GET['post'] ) || isset( $_POST['post'] ) || // Input var okay.
67
- ( isset( $_REQUEST['action'] ) && 'duplicate_post_check_changes' === $_REQUEST['action'] ) ) ) { // Input var okay.
68
  \wp_die(
69
  \esc_html__( 'No post has been supplied!', 'duplicate-post' )
70
  );
71
  return;
72
  }
73
 
74
- $id = ( isset( $_GET['post'] ) ? \intval( \wp_unslash( $_GET['post'] ) ) : \intval( \wp_unslash( $_POST['post'] ) ) ); // Input var okay.
75
 
76
- \check_admin_referer( 'duplicate_post_check_changes_' . $id ); // Input var okay.
77
 
78
  $this->post = \get_post( $id );
79
 
@@ -108,13 +107,13 @@ class Check_Changes_Handler {
108
  <h1 class="long-header">
109
  <?php
110
  echo \sprintf(
111
- /* translators: %s: original item link (to view or edit) or title. */
112
  \esc_html__( 'Compare changes of duplicated post with the original (&#8220;%s&#8221;)', 'duplicate-post' ),
113
  Utils::get_edit_or_view_link( $this->original ) // phpcs:ignore WordPress.Security.EscapeOutput
114
  );
115
  ?>
116
  </h1>
117
- <a href="<?php echo \esc_url( $post_edit_link ); ?>"><?php \esc_html_e( '&larr; Return to editor', 'default' ); ?></a>
118
  <div class="revisions">
119
  <div class="revisions-control-frame">
120
  <div class="revisions-controls"></div>
@@ -124,16 +123,16 @@ class Check_Changes_Handler {
124
  <div class="diff">
125
  <?php
126
  $fields = [
127
- 'post_title' => \__( 'Title', 'default' ),
128
- 'post_content' => \__( 'Content', 'default' ),
129
- 'post_excerpt' => \__( 'Excerpt', 'default' ),
130
  ];
131
 
132
- $args = array(
133
  'show_split_view' => true,
134
- 'title_left' => __( 'Removed', 'default' ),
135
- 'title_right' => __( 'Added', 'default' ),
136
- );
137
 
138
  if ( \version_compare( $wp_version, '5.7' ) < 0 ) {
139
  unset( $args['title_left'] );
@@ -141,22 +140,23 @@ class Check_Changes_Handler {
141
  }
142
 
143
  $post_array = \get_post( $this->post, \ARRAY_A );
 
144
  /** This filter is documented in wp-admin/includes/revision.php */
145
- // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Reason: we want to use a WP filter from the revision feature.
146
  $fields = \apply_filters( '_wp_post_revision_fields', $fields, $post_array );
147
 
148
  foreach ( $fields as $field => $name ) {
149
  /** This filter is documented in wp-admin/includes/revision.php */
150
- // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Reason: we want to use a WP filter from the revision feature.
151
- $content_from = apply_filters( "_wp_post_revision_field_{$field}", $this->original->$field, $field, $this->original, 'from' );
152
 
153
  /** This filter is documented in wp-admin/includes/revision.php */
154
- // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Reason: we want to use a WP filter from the revision feature.
155
  $content_to = \apply_filters( "_wp_post_revision_field_{$field}", $this->post->$field, $field, $this->post, 'to' );
156
 
157
  $diff = \wp_text_diff( $content_from, $content_to, $args );
158
 
159
- if ( ! $diff && 'post_title' === $field ) {
160
  // It's a better user experience to still show the Title, even if it didn't change.
161
  $diff = '<table class="diff"><colgroup><col class="content diffsplit left"><col class="content diffsplit middle"><col class="content diffsplit right"></colgroup><tbody><tr>';
162
  $diff .= '<td>' . \esc_html( $this->original->post_title ) . '</td><td></td><td>' . \esc_html( $this->post->post_title ) . '</td>';
@@ -194,7 +194,7 @@ class Check_Changes_Handler {
194
  \set_current_screen( 'revision' );
195
  // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited -- The revision screen expects $post to be set.
196
  $post = $this->post;
197
- require_once ABSPATH . 'wp-admin/admin-header.php';
198
  }
199
 
200
  /**
@@ -205,6 +205,6 @@ class Check_Changes_Handler {
205
  * @return void
206
  */
207
  public function require_wordpress_footer() {
208
- require_once ABSPATH . 'wp-admin/admin-footer.php';
209
  }
210
  }
1
  <?php
 
 
 
 
 
 
2
 
3
  namespace Yoast\WP\Duplicate_Post\Handlers;
4
 
5
+ use WP_Post;
6
  use Yoast\WP\Duplicate_Post\Permissions_Helper;
7
  use Yoast\WP\Duplicate_Post\Utils;
8
 
9
  /**
10
+ * Duplicate Post handler class for changes overview.
11
+ *
12
  * Represents the handler for checking the changes between a copy and the original post.
13
+ *
14
+ * @since 4.0
15
  */
16
  class Check_Changes_Handler {
17
 
25
  /**
26
  * Holds the current post object.
27
  *
28
+ * @var WP_Post
29
  */
30
  private $post;
31
 
32
  /**
33
  * Holds the original post object.
34
  *
35
+ * @var WP_Post
36
  */
37
  private $original;
38
 
62
  public function check_changes_action_handler() {
63
  global $wp_version;
64
 
65
+ if ( ! ( isset( $_GET['post'] ) || isset( $_POST['post'] )
66
+ || ( isset( $_REQUEST['action'] ) && $_REQUEST['action'] === 'duplicate_post_check_changes' ) ) ) {
67
  \wp_die(
68
  \esc_html__( 'No post has been supplied!', 'duplicate-post' )
69
  );
70
  return;
71
  }
72
 
73
+ $id = ( isset( $_GET['post'] ) ? \intval( \wp_unslash( $_GET['post'] ) ) : \intval( \wp_unslash( $_POST['post'] ) ) );
74
 
75
+ \check_admin_referer( 'duplicate_post_check_changes_' . $id );
76
 
77
  $this->post = \get_post( $id );
78
 
107
  <h1 class="long-header">
108
  <?php
109
  echo \sprintf(
110
+ /* translators: %s: original item link (to view or edit) or title. */
111
  \esc_html__( 'Compare changes of duplicated post with the original (&#8220;%s&#8221;)', 'duplicate-post' ),
112
  Utils::get_edit_or_view_link( $this->original ) // phpcs:ignore WordPress.Security.EscapeOutput
113
  );
114
  ?>
115
  </h1>
116
+ <a href="<?php echo \esc_url( $post_edit_link ); ?>"><?php \esc_html_e( '&larr; Return to editor', 'duplicate-post' ); ?></a>
117
  <div class="revisions">
118
  <div class="revisions-control-frame">
119
  <div class="revisions-controls"></div>
123
  <div class="diff">
124
  <?php
125
  $fields = [
126
+ 'post_title' => \__( 'Title', 'duplicate-post' ),
127
+ 'post_content' => \__( 'Content', 'duplicate-post' ),
128
+ 'post_excerpt' => \__( 'Excerpt', 'duplicate-post' ),
129
  ];
130
 
131
+ $args = [
132
  'show_split_view' => true,
133
+ 'title_left' => \__( 'Removed', 'duplicate-post' ),
134
+ 'title_right' => \__( 'Added', 'duplicate-post' ),
135
+ ];
136
 
137
  if ( \version_compare( $wp_version, '5.7' ) < 0 ) {
138
  unset( $args['title_left'] );
140
  }
141
 
142
  $post_array = \get_post( $this->post, \ARRAY_A );
143
+
144
  /** This filter is documented in wp-admin/includes/revision.php */
145
+ // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Reason: using WP core hook.
146
  $fields = \apply_filters( '_wp_post_revision_fields', $fields, $post_array );
147
 
148
  foreach ( $fields as $field => $name ) {
149
  /** This filter is documented in wp-admin/includes/revision.php */
150
+ // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Reason: using WP core hook.
151
+ $content_from = \apply_filters( "_wp_post_revision_field_{$field}", $this->original->$field, $field, $this->original, 'from' );
152
 
153
  /** This filter is documented in wp-admin/includes/revision.php */
154
+ // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Reason: using WP core hook.
155
  $content_to = \apply_filters( "_wp_post_revision_field_{$field}", $this->post->$field, $field, $this->post, 'to' );
156
 
157
  $diff = \wp_text_diff( $content_from, $content_to, $args );
158
 
159
+ if ( ! $diff && $field === 'post_title' ) {
160
  // It's a better user experience to still show the Title, even if it didn't change.
161
  $diff = '<table class="diff"><colgroup><col class="content diffsplit left"><col class="content diffsplit middle"><col class="content diffsplit right"></colgroup><tbody><tr>';
162
  $diff .= '<td>' . \esc_html( $this->original->post_title ) . '</td><td></td><td>' . \esc_html( $this->post->post_title ) . '</td>';
194
  \set_current_screen( 'revision' );
195
  // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited -- The revision screen expects $post to be set.
196
  $post = $this->post;
197
+ require_once \ABSPATH . 'wp-admin/admin-header.php';
198
  }
199
 
200
  /**
205
  * @return void
206
  */
207
  public function require_wordpress_footer() {
208
+ require_once \ABSPATH . 'wp-admin/admin-footer.php';
209
  }
210
  }
src/handlers/{class-handler.php → handler.php} RENAMED
@@ -1,10 +1,4 @@
1
  <?php
2
- /**
3
- * Duplicate Post handler class for duplication actions.
4
- *
5
- * @package Duplicate_Post
6
- * @since 4.0
7
- */
8
 
9
  namespace Yoast\WP\Duplicate_Post\Handlers;
10
 
@@ -12,7 +6,9 @@ use Yoast\WP\Duplicate_Post\Permissions_Helper;
12
  use Yoast\WP\Duplicate_Post\Post_Duplicator;
13
 
14
  /**
15
- * Represents the handler for duplication actions.
 
 
16
  */
17
  class Handler {
18
 
1
  <?php
 
 
 
 
 
 
2
 
3
  namespace Yoast\WP\Duplicate_Post\Handlers;
4
 
6
  use Yoast\WP\Duplicate_Post\Post_Duplicator;
7
 
8
  /**
9
+ * Duplicate Post handler class for duplication actions.
10
+ *
11
+ * @since 4.0
12
  */
13
  class Handler {
14
 
src/handlers/{class-link-handler.php → link-handler.php} RENAMED
@@ -1,10 +1,4 @@
1
  <?php
2
- /**
3
- * Duplicate Post handler class for duplication actions from links.
4
- *
5
- * @package Duplicate_Post
6
- * @since 4.0
7
- */
8
 
9
  namespace Yoast\WP\Duplicate_Post\Handlers;
10
 
@@ -12,7 +6,9 @@ use Yoast\WP\Duplicate_Post\Permissions_Helper;
12
  use Yoast\WP\Duplicate_Post\Post_Duplicator;
13
 
14
  /**
15
- * Represents the handler for duplication actions from links.
 
 
16
  */
17
  class Link_Handler {
18
 
@@ -62,14 +58,14 @@ class Link_Handler {
62
  \wp_die( \esc_html__( 'Current user is not allowed to copy posts.', 'duplicate-post' ) );
63
  }
64
 
65
- if ( ! ( isset( $_GET['post'] ) || isset( $_POST['post'] ) || // Input var okay.
66
- ( isset( $_REQUEST['action'] ) && 'duplicate_post_new_draft' === $_REQUEST['action'] ) ) ) { // Input var okay.
67
  \wp_die( \esc_html__( 'No post to duplicate has been supplied!', 'duplicate-post' ) );
68
  }
69
 
70
- $id = ( isset( $_GET['post'] ) ? \intval( \wp_unslash( $_GET['post'] ) ) : \intval( \wp_unslash( $_POST['post'] ) ) ); // Input var okay.
71
 
72
- \check_admin_referer( 'duplicate_post_new_draft_' . $id ); // Input var okay.
73
 
74
  $post = \get_post( $id );
75
 
@@ -118,14 +114,14 @@ class Link_Handler {
118
  \wp_die( \esc_html__( 'Current user is not allowed to copy posts.', 'duplicate-post' ) );
119
  }
120
 
121
- if ( ! ( isset( $_GET['post'] ) || isset( $_POST['post'] ) || // Input var okay.
122
- ( isset( $_REQUEST['action'] ) && 'duplicate_post_clone' === $_REQUEST['action'] ) ) ) { // Input var okay.
123
  \wp_die( \esc_html__( 'No post to duplicate has been supplied!', 'duplicate-post' ) );
124
  }
125
 
126
- $id = ( isset( $_GET['post'] ) ? \intval( \wp_unslash( $_GET['post'] ) ) : \intval( \wp_unslash( $_POST['post'] ) ) ); // Input var okay.
127
 
128
- \check_admin_referer( 'duplicate_post_clone_' . $id ); // Input var okay.
129
 
130
  $post = \get_post( $id );
131
 
@@ -154,16 +150,18 @@ class Link_Handler {
154
 
155
  $post_type = $post->post_type;
156
  $sendback = \wp_get_referer();
157
- if ( ! $sendback || strpos( $sendback, 'post.php' ) !== false || strpos( $sendback, 'post-new.php' ) !== false ) {
158
- if ( 'attachment' === $post_type ) {
159
  $sendback = \admin_url( 'upload.php' );
160
- } else {
 
161
  $sendback = \admin_url( 'edit.php' );
162
  if ( ! empty( $post_type ) ) {
163
  $sendback = \add_query_arg( 'post_type', $post_type, $sendback );
164
  }
165
  }
166
- } else {
 
167
  $sendback = \remove_query_arg( [ 'trashed', 'untrashed', 'deleted', 'cloned', 'ids' ], $sendback );
168
  }
169
 
@@ -190,14 +188,14 @@ class Link_Handler {
190
  \wp_die( \esc_html__( 'Current user is not allowed to copy posts.', 'duplicate-post' ) );
191
  }
192
 
193
- if ( ! ( isset( $_GET['post'] ) || isset( $_POST['post'] ) || // Input var okay.
194
- ( isset( $_REQUEST['action'] ) && 'duplicate_post_rewrite' === $_REQUEST['action'] ) ) ) { // Input var okay.
195
  \wp_die( \esc_html__( 'No post to duplicate has been supplied!', 'duplicate-post' ) );
196
  }
197
 
198
- $id = ( isset( $_GET['post'] ) ? \intval( \wp_unslash( $_GET['post'] ) ) : \intval( \wp_unslash( $_POST['post'] ) ) ); // Input var okay.
199
 
200
- \check_admin_referer( 'duplicate_post_rewrite_' . $id ); // Input var okay.
201
 
202
  $post = \get_post( $id );
203
 
1
  <?php
 
 
 
 
 
 
2
 
3
  namespace Yoast\WP\Duplicate_Post\Handlers;
4
 
6
  use Yoast\WP\Duplicate_Post\Post_Duplicator;
7
 
8
  /**
9
+ * Duplicate Post handler class for duplication actions from links.
10
+ *
11
+ * @since 4.0
12
  */
13
  class Link_Handler {
14
 
58
  \wp_die( \esc_html__( 'Current user is not allowed to copy posts.', 'duplicate-post' ) );
59
  }
60
 
61
+ if ( ! ( isset( $_GET['post'] ) || isset( $_POST['post'] )
62
+ || ( isset( $_REQUEST['action'] ) && $_REQUEST['action'] === 'duplicate_post_new_draft' ) ) ) {
63
  \wp_die( \esc_html__( 'No post to duplicate has been supplied!', 'duplicate-post' ) );
64
  }
65
 
66
+ $id = ( isset( $_GET['post'] ) ? \intval( \wp_unslash( $_GET['post'] ) ) : \intval( \wp_unslash( $_POST['post'] ) ) );
67
 
68
+ \check_admin_referer( 'duplicate_post_new_draft_' . $id );
69
 
70
  $post = \get_post( $id );
71
 
114
  \wp_die( \esc_html__( 'Current user is not allowed to copy posts.', 'duplicate-post' ) );
115
  }
116
 
117
+ if ( ! ( isset( $_GET['post'] ) || isset( $_POST['post'] )
118
+ || ( isset( $_REQUEST['action'] ) && $_REQUEST['action'] === 'duplicate_post_clone' ) ) ) {
119
  \wp_die( \esc_html__( 'No post to duplicate has been supplied!', 'duplicate-post' ) );
120
  }
121
 
122
+ $id = ( isset( $_GET['post'] ) ? \intval( \wp_unslash( $_GET['post'] ) ) : \intval( \wp_unslash( $_POST['post'] ) ) );
123
 
124
+ \check_admin_referer( 'duplicate_post_clone_' . $id );
125
 
126
  $post = \get_post( $id );
127
 
150
 
151
  $post_type = $post->post_type;
152
  $sendback = \wp_get_referer();
153
+ if ( ! $sendback || \strpos( $sendback, 'post.php' ) !== false || \strpos( $sendback, 'post-new.php' ) !== false ) {
154
+ if ( $post_type === 'attachment' ) {
155
  $sendback = \admin_url( 'upload.php' );
156
+ }
157
+ else {
158
  $sendback = \admin_url( 'edit.php' );
159
  if ( ! empty( $post_type ) ) {
160
  $sendback = \add_query_arg( 'post_type', $post_type, $sendback );
161
  }
162
  }
163
+ }
164
+ else {
165
  $sendback = \remove_query_arg( [ 'trashed', 'untrashed', 'deleted', 'cloned', 'ids' ], $sendback );
166
  }
167
 
188
  \wp_die( \esc_html__( 'Current user is not allowed to copy posts.', 'duplicate-post' ) );
189
  }
190
 
191
+ if ( ! ( isset( $_GET['post'] ) || isset( $_POST['post'] )
192
+ || ( isset( $_REQUEST['action'] ) && $_REQUEST['action'] === 'duplicate_post_rewrite' ) ) ) {
193
  \wp_die( \esc_html__( 'No post to duplicate has been supplied!', 'duplicate-post' ) );
194
  }
195
 
196
+ $id = ( isset( $_GET['post'] ) ? \intval( \wp_unslash( $_GET['post'] ) ) : \intval( \wp_unslash( $_POST['post'] ) ) );
197
 
198
+ \check_admin_referer( 'duplicate_post_rewrite_' . $id );
199
 
200
  $post = \get_post( $id );
201
 
src/handlers/{class-save-post-handler.php → save-post-handler.php} RENAMED
@@ -1,17 +1,13 @@
1
  <?php
2
- /**
3
- * Duplicate Post handler class for save_post action.
4
- *
5
- * @package Duplicate_Post
6
- * @since 4.0
7
- */
8
 
9
  namespace Yoast\WP\Duplicate_Post\Handlers;
10
 
11
  use Yoast\WP\Duplicate_Post\Permissions_Helper;
12
 
13
  /**
14
- * Represents the handler for save_post action.
 
 
15
  */
16
  class Save_Post_Handler {
17
 
@@ -51,8 +47,8 @@ class Save_Post_Handler {
51
  * @return void
52
  */
53
  public function delete_on_save_post( $post_id ) {
54
- if ( ( \defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE )
55
- || empty( $_POST['duplicate_post_remove_original'] ) // phpcs:ignore WordPress.Security.NonceVerification
56
  || ! \current_user_can( 'edit_post', $post_id ) ) {
57
  return;
58
  }
1
  <?php
 
 
 
 
 
 
2
 
3
  namespace Yoast\WP\Duplicate_Post\Handlers;
4
 
5
  use Yoast\WP\Duplicate_Post\Permissions_Helper;
6
 
7
  /**
8
+ * Duplicate Post handler class for save_post action.
9
+ *
10
+ * @since 4.0
11
  */
12
  class Save_Post_Handler {
13
 
47
  * @return void
48
  */
49
  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 ) ) {
53
  return;
54
  }
src/{class-permissions-helper.php → permissions-helper.php} RENAMED
@@ -1,15 +1,13 @@
1
  <?php
2
- /**
3
- * Permissions helper for Duplicate Post.
4
- *
5
- * @package Duplicate_Post
6
- * @since 4.0
7
- */
8
 
9
  namespace Yoast\WP\Duplicate_Post;
10
 
 
 
11
  /**
12
- * Represents the Permissions_Helper class.
 
 
13
  */
14
  class Permissions_Helper {
15
 
@@ -61,33 +59,33 @@ class Permissions_Helper {
61
  /**
62
  * Determines if the post is a copy intended for Rewrite & Republish.
63
  *
64
- * @param \WP_Post $post The post object.
65
  *
66
  * @return bool Whether the post is a copy intended for Rewrite & Republish.
67
  */
68
- public function is_rewrite_and_republish_copy( \WP_Post $post ) {
69
  return ( \intval( \get_post_meta( $post->ID, '_dp_is_rewrite_republish_copy', true ) ) === 1 );
70
  }
71
 
72
  /**
73
  * Gets the Rewrite & Republish copy ID for the passed post.
74
  *
75
- * @param \WP_Post $post The post object.
76
  *
77
  * @return int The Rewrite & Republish copy ID.
78
  */
79
- public function get_rewrite_and_republish_copy_id( \WP_Post $post ) {
80
  return \get_post_meta( $post->ID, '_dp_has_rewrite_republish_copy', true );
81
  }
82
 
83
  /**
84
  * Gets the copy post object for the passed post.
85
  *
86
- * @param \WP_Post $post The post to get the copy for.
87
  *
88
- * @return \WP_Post|null The copy's post object or null if it doesn't exist.
89
  */
90
- public function get_rewrite_and_republish_copy( \WP_Post $post ) {
91
  $copy_id = $this->get_rewrite_and_republish_copy_id( $post );
92
 
93
  if ( empty( $copy_id ) ) {
@@ -100,22 +98,22 @@ class Permissions_Helper {
100
  /**
101
  * Determines if the post has a copy intended for Rewrite & Republish.
102
  *
103
- * @param \WP_Post $post The post object.
104
  *
105
  * @return bool Whether the post has a copy intended for Rewrite & Republish.
106
  */
107
- public function has_rewrite_and_republish_copy( \WP_Post $post ) {
108
  return ( ! empty( $this->get_rewrite_and_republish_copy_id( $post ) ) );
109
  }
110
 
111
  /**
112
  * Determines if the post has a copy intended for Rewrite & Republish which is scheduled to be published.
113
  *
114
- * @param \WP_Post $post The post object.
115
  *
116
- * @return bool|\WP_Post The scheduled copy if present, false if the post has no scheduled copy.
117
  */
118
- public function has_scheduled_rewrite_and_republish_copy( \WP_Post $post ) {
119
  $copy = $this->get_rewrite_and_republish_copy( $post );
120
 
121
  if ( ! empty( $copy ) && $copy->post_status === 'future' ) {
@@ -176,11 +174,11 @@ class Permissions_Helper {
176
  /**
177
  * Determines if the original post has changed since the creation of the copy.
178
  *
179
- * @param \WP_Post $post The post object.
180
  *
181
  * @return bool Whether the original post has changed since the creation of the copy.
182
  */
183
- public function has_original_changed( \WP_Post $post ) {
184
  if ( ! $this->is_rewrite_and_republish_copy( $post ) ) {
185
  return false;
186
  }
@@ -200,16 +198,16 @@ class Permissions_Helper {
200
  /**
201
  * Determines if duplicate links for the post can be displayed.
202
  *
203
- * @param \WP_Post $post The post object.
204
  *
205
  * @return bool Whether the links can be displayed.
206
  */
207
- public function should_links_be_displayed( \WP_Post $post ) {
208
  /**
209
  * Filter allowing displaying duplicate post links for current post.
210
  *
211
- * @param bool $display_links Whether the duplicate links will be displayed.
212
- * @param \WP_Post $post The post object.
213
  *
214
  * @return bool Whether or not to display the duplicate post links.
215
  */
@@ -221,11 +219,11 @@ class Permissions_Helper {
221
  /**
222
  * Determines if the Rewrite & Republish link for the post should be displayed.
223
  *
224
- * @param \WP_Post $post The post object.
225
  *
226
  * @return bool Whether the links should be displayed.
227
  */
228
- public function should_rewrite_and_republish_be_allowed( \WP_Post $post ) {
229
  return $post->post_status === 'publish'
230
  && ! $this->is_rewrite_and_republish_copy( $post )
231
  && ! $this->has_rewrite_and_republish_copy( $post );
@@ -251,22 +249,22 @@ class Permissions_Helper {
251
  /**
252
  * Determines whether a Rewrite & Republish copy can be republished.
253
  *
254
- * @param \WP_Post $post The post object.
255
  *
256
  * @return bool Whether the Rewrite & Republish copy can be republished.
257
  */
258
- public function is_copy_allowed_to_be_republished( \WP_Post $post ) {
259
  return \in_array( $post->post_status, [ 'dp-rewrite-republish', 'private' ], true );
260
  }
261
 
262
  /**
263
  * Determines if the post has a trashed copy intended for Rewrite & Republish.
264
  *
265
- * @param \WP_Post $post The post object.
266
  *
267
  * @return bool Whether the post has a trashed copy intended for Rewrite & Republish.
268
  */
269
- public function has_trashed_rewrite_and_republish_copy( \WP_Post $post ) {
270
  $copy_id = \get_post_meta( $post->ID, '_dp_has_rewrite_republish_copy', true );
271
 
272
  if ( ! $copy_id ) {
@@ -275,10 +273,6 @@ class Permissions_Helper {
275
 
276
  $copy = \get_post( $copy_id );
277
 
278
- if ( $copy && $copy->post_status === 'trash' ) {
279
- return true;
280
- }
281
-
282
- return false;
283
  }
284
  }
1
  <?php
 
 
 
 
 
 
2
 
3
  namespace Yoast\WP\Duplicate_Post;
4
 
5
+ use WP_Post;
6
+
7
  /**
8
+ * Permissions helper for Duplicate Post.
9
+ *
10
+ * @since 4.0
11
  */
12
  class Permissions_Helper {
13
 
59
  /**
60
  * Determines if the post is a copy intended for Rewrite & Republish.
61
  *
62
+ * @param WP_Post $post The post object.
63
  *
64
  * @return bool Whether the post is a copy intended for Rewrite & Republish.
65
  */
66
+ public function is_rewrite_and_republish_copy( WP_Post $post ) {
67
  return ( \intval( \get_post_meta( $post->ID, '_dp_is_rewrite_republish_copy', true ) ) === 1 );
68
  }
69
 
70
  /**
71
  * Gets the Rewrite & Republish copy ID for the passed post.
72
  *
73
+ * @param WP_Post $post The post object.
74
  *
75
  * @return int The Rewrite & Republish copy ID.
76
  */
77
+ public function get_rewrite_and_republish_copy_id( WP_Post $post ) {
78
  return \get_post_meta( $post->ID, '_dp_has_rewrite_republish_copy', true );
79
  }
80
 
81
  /**
82
  * Gets the copy post object for the passed post.
83
  *
84
+ * @param WP_Post $post The post to get the copy for.
85
  *
86
+ * @return WP_Post|null The copy's post object or null if it doesn't exist.
87
  */
88
+ public function get_rewrite_and_republish_copy( WP_Post $post ) {
89
  $copy_id = $this->get_rewrite_and_republish_copy_id( $post );
90
 
91
  if ( empty( $copy_id ) ) {
98
  /**
99
  * Determines if the post has a copy intended for Rewrite & Republish.
100
  *
101
+ * @param WP_Post $post The post object.
102
  *
103
  * @return bool Whether the post has a copy intended for Rewrite & Republish.
104
  */
105
+ public function has_rewrite_and_republish_copy( WP_Post $post ) {
106
  return ( ! empty( $this->get_rewrite_and_republish_copy_id( $post ) ) );
107
  }
108
 
109
  /**
110
  * Determines if the post has a copy intended for Rewrite & Republish which is scheduled to be published.
111
  *
112
+ * @param WP_Post $post The post object.
113
  *
114
+ * @return bool|WP_Post The scheduled copy if present, false if the post has no scheduled copy.
115
  */
116
+ public function has_scheduled_rewrite_and_republish_copy( WP_Post $post ) {
117
  $copy = $this->get_rewrite_and_republish_copy( $post );
118
 
119
  if ( ! empty( $copy ) && $copy->post_status === 'future' ) {
174
  /**
175
  * Determines if the original post has changed since the creation of the copy.
176
  *
177
+ * @param WP_Post $post The post object.
178
  *
179
  * @return bool Whether the original post has changed since the creation of the copy.
180
  */
181
+ public function has_original_changed( WP_Post $post ) {
182
  if ( ! $this->is_rewrite_and_republish_copy( $post ) ) {
183
  return false;
184
  }
198
  /**
199
  * Determines if duplicate links for the post can be displayed.
200
  *
201
+ * @param WP_Post $post The post object.
202
  *
203
  * @return bool Whether the links can be displayed.
204
  */
205
+ public function should_links_be_displayed( WP_Post $post ) {
206
  /**
207
  * Filter allowing displaying duplicate post links for current post.
208
  *
209
+ * @param bool $display_links Whether the duplicate links will be displayed.
210
+ * @param WP_Post $post The post object.
211
  *
212
  * @return bool Whether or not to display the duplicate post links.
213
  */
219
  /**
220
  * Determines if the Rewrite & Republish link for the post should be displayed.
221
  *
222
+ * @param WP_Post $post The post object.
223
  *
224
  * @return bool Whether the links should be displayed.
225
  */
226
+ public function should_rewrite_and_republish_be_allowed( WP_Post $post ) {
227
  return $post->post_status === 'publish'
228
  && ! $this->is_rewrite_and_republish_copy( $post )
229
  && ! $this->has_rewrite_and_republish_copy( $post );
249
  /**
250
  * Determines whether a Rewrite & Republish copy can be republished.
251
  *
252
+ * @param WP_Post $post The post object.
253
  *
254
  * @return bool Whether the Rewrite & Republish copy can be republished.
255
  */
256
+ public function is_copy_allowed_to_be_republished( WP_Post $post ) {
257
  return \in_array( $post->post_status, [ 'dp-rewrite-republish', 'private' ], true );
258
  }
259
 
260
  /**
261
  * Determines if the post has a trashed copy intended for Rewrite & Republish.
262
  *
263
+ * @param WP_Post $post The post object.
264
  *
265
  * @return bool Whether the post has a trashed copy intended for Rewrite & Republish.
266
  */
267
+ public function has_trashed_rewrite_and_republish_copy( WP_Post $post ) {
268
  $copy_id = \get_post_meta( $post->ID, '_dp_has_rewrite_republish_copy', true );
269
 
270
  if ( ! $copy_id ) {
273
 
274
  $copy = \get_post( $copy_id );
275
 
276
+ return ( $copy && $copy->post_status === 'trash' );
 
 
 
 
277
  }
278
  }
src/{class-post-duplicator.php → post-duplicator.php} RENAMED
@@ -1,15 +1,14 @@
1
  <?php
2
- /**
3
- * Duplicate Post class to create copies.
4
- *
5
- * @package Duplicate_Post
6
- * @since 4.0
7
- */
8
 
9
  namespace Yoast\WP\Duplicate_Post;
10
 
 
 
 
11
  /**
12
- * Represents the Post Duplicator class.
 
 
13
  */
14
  class Post_Duplicator {
15
 
@@ -48,41 +47,45 @@ class Post_Duplicator {
48
  /**
49
  * Creates a copy of a post object, accordingly to an options array.
50
  *
51
- * @param \WP_Post $post The original post object.
52
- * @param array $options The options overriding the default ones.
53
  *
54
- * @return int|\WP_Error The copy ID, or a WP_Error object on failure.
55
  */
56
- public function create_duplicate( \WP_Post $post, array $options = [] ) {
57
  $defaults = $this->get_default_options();
58
  $options = \wp_parse_args( $options, $defaults );
59
 
60
  $title = '';
61
  $new_post_status = $post->post_status;
62
- if ( 'attachment' !== $post->post_type ) {
63
  $title = $this->generate_copy_title( $post, $options );
64
  $new_post_status = $this->generate_copy_status( $post, $options );
65
  }
66
 
67
  $new_post_author_id = $this->generate_copy_author( $post, $options );
68
 
69
- $menu_order = $options['copy_menu_order'] ? $post->menu_order : 0;
 
 
 
 
70
  if ( ! empty( $options['increase_menu_order_by'] ) && \is_numeric( $options['increase_menu_order_by'] ) ) {
71
  $menu_order += \intval( $options['increase_menu_order_by'] );
72
  }
73
 
74
  $new_post = [
75
  'post_author' => $new_post_author_id,
76
- 'post_content' => $options['copy_content'] ? $post->post_content : '',
77
- 'post_content_filtered' => $options['copy_content'] ? $post->post_content_filtered : '',
78
  'post_title' => $title,
79
- 'post_excerpt' => $options['copy_excerpt'] ? $post->post_excerpt : '',
80
  'post_status' => $new_post_status,
81
  'post_type' => $post->post_type,
82
  'comment_status' => $post->comment_status,
83
  'ping_status' => $post->ping_status,
84
- 'post_password' => $options['copy_password'] ? $post->post_password : '',
85
- 'post_name' => $options['copy_name'] ? $post->post_name : '',
86
  'post_parent' => empty( $options['parent_id'] ) ? $post->post_parent : $options['parent_id'],
87
  'menu_order' => $menu_order,
88
  'post_mime_type' => $post->post_mime_type,
@@ -99,8 +102,8 @@ class Post_Duplicator {
99
  /**
100
  * Filter new post values.
101
  *
102
- * @param array $new_post New post values.
103
- * @param \WP_Post $post Original post object.
104
  *
105
  * @return array
106
  */
@@ -141,11 +144,11 @@ class Post_Duplicator {
141
  /**
142
  * Wraps the function to create a copy for the Rewrite & Republish feature.
143
  *
144
- * @param \WP_Post $post The original post object.
145
  *
146
- * @return int|\WP_Error The copy ID, or a WP_Error object on failure.
147
  */
148
- public function create_duplicate_for_rewrite_and_republish( \WP_Post $post ) {
149
  $options = [
150
  'copy_title' => true,
151
  'copy_date' => true,
@@ -176,9 +179,9 @@ class Post_Duplicator {
176
  /**
177
  * Copies the taxonomies of a post to another post.
178
  *
179
- * @param int $new_id New post ID.
180
- * @param \WP_Post $post The original post object.
181
- * @param array $options The options array.
182
  *
183
  * @return void
184
  */
@@ -228,9 +231,9 @@ class Post_Duplicator {
228
  /**
229
  * Copies the meta information of a post to another post.
230
  *
231
- * @param int $new_id The new post ID.
232
- * @param \WP_Post $post The original post object.
233
- * @param array $options The options array.
234
  *
235
  * @return void
236
  */
@@ -272,7 +275,8 @@ class Post_Duplicator {
272
  $meta_keys[] = $meta_key;
273
  }
274
  }
275
- } else {
 
276
  $meta_keys = \array_diff( $post_meta_keys, $meta_excludelist );
277
  }
278
 
@@ -289,9 +293,13 @@ class Post_Duplicator {
289
 
290
  foreach ( $meta_keys as $meta_key ) {
291
  $meta_values = \get_post_custom_values( $meta_key, $post->ID );
 
 
 
 
292
  foreach ( $meta_values as $meta_value ) {
293
  $meta_value = \maybe_unserialize( $meta_value );
294
- \update_post_meta( $new_id, $meta_key, Utils::recursively_slash_strings( $meta_value ) );
295
  }
296
  }
297
  }
@@ -299,12 +307,12 @@ class Post_Duplicator {
299
  /**
300
  * Generates and returns the title for the copy.
301
  *
302
- * @param \WP_Post $post The original post object.
303
- * @param array $options The options array.
304
  *
305
  * @return string The calculated title for the copy.
306
  */
307
- public function generate_copy_title( \WP_Post $post, array $options ) {
308
  $prefix = \sanitize_text_field( $options['title_prefix'] );
309
  $suffix = \sanitize_text_field( $options['title_suffix'] );
310
  if ( $options['copy_title'] ) {
@@ -315,7 +323,8 @@ class Post_Duplicator {
315
  if ( ! empty( $suffix ) ) {
316
  $suffix = ' ' . $suffix;
317
  }
318
- } else {
 
319
  $title = '';
320
  }
321
  return \trim( $prefix . $title . $suffix );
@@ -324,26 +333,25 @@ class Post_Duplicator {
324
  /**
325
  * Generates and returns the status for the copy.
326
  *
327
- * @param \WP_Post $post The original post object.
328
- * @param array $options The options array.
329
  *
330
  * @return string The calculated status for the copy.
331
  */
332
- public function generate_copy_status( \WP_Post $post, array $options ) {
333
  $new_post_status = 'draft';
334
 
335
  if ( $options['copy_status'] ) {
336
  $new_post_status = $post->post_status;
337
  if ( $new_post_status === 'publish' || $new_post_status === 'future' ) {
338
- // check if the user has the right capability.
339
  if ( \is_post_type_hierarchical( $post->post_type ) ) {
340
  if ( ! \current_user_can( 'publish_pages' ) ) {
341
  $new_post_status = 'pending';
342
  }
343
- } else {
344
- if ( ! \current_user_can( 'publish_posts' ) ) {
345
- $new_post_status = 'pending';
346
- }
347
  }
348
  }
349
  }
@@ -354,24 +362,23 @@ class Post_Duplicator {
354
  /**
355
  * Generates and returns the author ID for the copy.
356
  *
357
- * @param \WP_Post $post The original post object.
358
- * @param array $options The options array.
359
  *
360
  * @return int|string The calculated author ID for the copy.
361
  */
362
- public function generate_copy_author( \WP_Post $post, array $options ) {
363
  $new_post_author = \wp_get_current_user();
364
  $new_post_author_id = $new_post_author->ID;
365
  if ( $options['copy_author'] ) {
366
- // check if the user has the right capability.
367
  if ( \is_post_type_hierarchical( $post->post_type ) ) {
368
  if ( \current_user_can( 'edit_others_pages' ) ) {
369
  $new_post_author_id = $post->post_author;
370
  }
371
- } else {
372
- if ( \current_user_can( 'edit_others_posts' ) ) {
373
- $new_post_author_id = $post->post_author;
374
- }
375
  }
376
  }
377
 
1
  <?php
 
 
 
 
 
 
2
 
3
  namespace Yoast\WP\Duplicate_Post;
4
 
5
+ use WP_Error;
6
+ use WP_Post;
7
+
8
  /**
9
+ * Duplicate Post class to create copies.
10
+ *
11
+ * @since 4.0
12
  */
13
  class Post_Duplicator {
14
 
47
  /**
48
  * Creates a copy of a post object, accordingly to an options array.
49
  *
50
+ * @param WP_Post $post The original post object.
51
+ * @param array $options The options overriding the default ones.
52
  *
53
+ * @return int|WP_Error The copy ID, or a WP_Error object on failure.
54
  */
55
+ public function create_duplicate( WP_Post $post, array $options = [] ) {
56
  $defaults = $this->get_default_options();
57
  $options = \wp_parse_args( $options, $defaults );
58
 
59
  $title = '';
60
  $new_post_status = $post->post_status;
61
+ if ( $post->post_type !== 'attachment' ) {
62
  $title = $this->generate_copy_title( $post, $options );
63
  $new_post_status = $this->generate_copy_status( $post, $options );
64
  }
65
 
66
  $new_post_author_id = $this->generate_copy_author( $post, $options );
67
 
68
+ $menu_order = 0;
69
+ if ( $options['copy_menu_order'] ) {
70
+ $menu_order = $post->menu_order;
71
+ }
72
+
73
  if ( ! empty( $options['increase_menu_order_by'] ) && \is_numeric( $options['increase_menu_order_by'] ) ) {
74
  $menu_order += \intval( $options['increase_menu_order_by'] );
75
  }
76
 
77
  $new_post = [
78
  'post_author' => $new_post_author_id,
79
+ 'post_content' => ( $options['copy_content'] ) ? $post->post_content : '',
80
+ 'post_content_filtered' => ( $options['copy_content'] ) ? $post->post_content_filtered : '',
81
  'post_title' => $title,
82
+ 'post_excerpt' => ( $options['copy_excerpt'] ) ? $post->post_excerpt : '',
83
  'post_status' => $new_post_status,
84
  'post_type' => $post->post_type,
85
  'comment_status' => $post->comment_status,
86
  'ping_status' => $post->ping_status,
87
+ 'post_password' => ( $options['copy_password'] ) ? $post->post_password : '',
88
+ 'post_name' => ( $options['copy_name'] ) ? $post->post_name : '',
89
  'post_parent' => empty( $options['parent_id'] ) ? $post->post_parent : $options['parent_id'],
90
  'menu_order' => $menu_order,
91
  'post_mime_type' => $post->post_mime_type,
102
  /**
103
  * Filter new post values.
104
  *
105
+ * @param array $new_post New post values.
106
+ * @param WP_Post $post Original post object.
107
  *
108
  * @return array
109
  */
144
  /**
145
  * Wraps the function to create a copy for the Rewrite & Republish feature.
146
  *
147
+ * @param WP_Post $post The original post object.
148
  *
149
+ * @return int|WP_Error The copy ID, or a WP_Error object on failure.
150
  */
151
+ public function create_duplicate_for_rewrite_and_republish( WP_Post $post ) {
152
  $options = [
153
  'copy_title' => true,
154
  'copy_date' => true,
179
  /**
180
  * Copies the taxonomies of a post to another post.
181
  *
182
+ * @param int $new_id New post ID.
183
+ * @param WP_Post $post The original post object.
184
+ * @param array $options The options array.
185
  *
186
  * @return void
187
  */
231
  /**
232
  * Copies the meta information of a post to another post.
233
  *
234
+ * @param int $new_id The new post ID.
235
+ * @param WP_Post $post The original post object.
236
+ * @param array $options The options array.
237
  *
238
  * @return void
239
  */
275
  $meta_keys[] = $meta_key;
276
  }
277
  }
278
+ }
279
+ else {
280
  $meta_keys = \array_diff( $post_meta_keys, $meta_excludelist );
281
  }
282
 
293
 
294
  foreach ( $meta_keys as $meta_key ) {
295
  $meta_values = \get_post_custom_values( $meta_key, $post->ID );
296
+
297
+ // Clear existing meta data so that add_post_meta() works properly with non-unique keys.
298
+ \delete_post_meta( $new_id, $meta_key );
299
+
300
  foreach ( $meta_values as $meta_value ) {
301
  $meta_value = \maybe_unserialize( $meta_value );
302
+ \add_post_meta( $new_id, $meta_key, Utils::recursively_slash_strings( $meta_value ) );
303
  }
304
  }
305
  }
307
  /**
308
  * Generates and returns the title for the copy.
309
  *
310
+ * @param WP_Post $post The original post object.
311
+ * @param array $options The options array.
312
  *
313
  * @return string The calculated title for the copy.
314
  */
315
+ public function generate_copy_title( WP_Post $post, array $options ) {
316
  $prefix = \sanitize_text_field( $options['title_prefix'] );
317
  $suffix = \sanitize_text_field( $options['title_suffix'] );
318
  if ( $options['copy_title'] ) {
323
  if ( ! empty( $suffix ) ) {
324
  $suffix = ' ' . $suffix;
325
  }
326
+ }
327
+ else {
328
  $title = '';
329
  }
330
  return \trim( $prefix . $title . $suffix );
333
  /**
334
  * Generates and returns the status for the copy.
335
  *
336
+ * @param WP_Post $post The original post object.
337
+ * @param array $options The options array.
338
  *
339
  * @return string The calculated status for the copy.
340
  */
341
+ public function generate_copy_status( WP_Post $post, array $options ) {
342
  $new_post_status = 'draft';
343
 
344
  if ( $options['copy_status'] ) {
345
  $new_post_status = $post->post_status;
346
  if ( $new_post_status === 'publish' || $new_post_status === 'future' ) {
347
+ // Check if the user has the right capability.
348
  if ( \is_post_type_hierarchical( $post->post_type ) ) {
349
  if ( ! \current_user_can( 'publish_pages' ) ) {
350
  $new_post_status = 'pending';
351
  }
352
+ }
353
+ elseif ( ! \current_user_can( 'publish_posts' ) ) {
354
+ $new_post_status = 'pending';
 
355
  }
356
  }
357
  }
362
  /**
363
  * Generates and returns the author ID for the copy.
364
  *
365
+ * @param WP_Post $post The original post object.
366
+ * @param array $options The options array.
367
  *
368
  * @return int|string The calculated author ID for the copy.
369
  */
370
+ public function generate_copy_author( WP_Post $post, array $options ) {
371
  $new_post_author = \wp_get_current_user();
372
  $new_post_author_id = $new_post_author->ID;
373
  if ( $options['copy_author'] ) {
374
+ // Check if the user has the right capability.
375
  if ( \is_post_type_hierarchical( $post->post_type ) ) {
376
  if ( \current_user_can( 'edit_others_pages' ) ) {
377
  $new_post_author_id = $post->post_author;
378
  }
379
+ }
380
+ elseif ( \current_user_can( 'edit_others_posts' ) ) {
381
+ $new_post_author_id = $post->post_author;
 
382
  }
383
  }
384
 
src/{class-post-republisher.php → post-republisher.php} RENAMED
@@ -1,17 +1,13 @@
1
  <?php
2
- /**
3
- * Duplicate Post class to republish a rewritten post.
4
- *
5
- * @package Duplicate_Post
6
- * @since 4.0
7
- */
8
 
9
  namespace Yoast\WP\Duplicate_Post;
10
 
11
  use WP_Post;
12
 
13
  /**
14
- * Represents the Post Republisher class.
 
 
15
  */
16
  class Post_Republisher {
17
 
@@ -126,7 +122,7 @@ class Post_Republisher {
126
  /**
127
  * Executes the republish request.
128
  *
129
- * @param \WP_Post $post The copy's post object.
130
  *
131
  * @return void
132
  */
@@ -156,7 +152,7 @@ class Post_Republisher {
156
  /**
157
  * Republishes the original post with the passed post, when using the Block Editor.
158
  *
159
- * @param \WP_Post $post The copy's post object.
160
  *
161
  * @return void
162
  */
@@ -170,8 +166,8 @@ class Post_Republisher {
170
  * Runs also in the Block Editor to save the custom meta data only when there
171
  * are custom meta boxes.
172
  *
173
- * @param int $post_id The copy's post ID.
174
- * @param \WP_Post $post The copy's post object.
175
  *
176
  * @return void
177
  */
@@ -186,7 +182,7 @@ class Post_Republisher {
186
  /**
187
  * Republishes the scheduled Rewrited and Republish post.
188
  *
189
- * @param \WP_Post $copy The scheduled copy.
190
  *
191
  * @return void
192
  */
@@ -222,7 +218,8 @@ class Post_Republisher {
222
 
223
  if ( \intval( \get_post_meta( $copy_id, '_dp_has_been_republished', true ) ) === 1 ) {
224
  $this->delete_copy( $copy_id, $post_id );
225
- } else {
 
226
  \wp_die( \esc_html__( 'An error occurred while deleting the Rewrite & Republish copy.', 'duplicate-post' ) );
227
  }
228
  }
@@ -238,7 +235,7 @@ class Post_Republisher {
238
  return false;
239
  }
240
 
241
- return isset( $_GET['meta-box-loader'] ) === false; // phpcs:ignore WordPress.Security.NonceVerification
242
  }
243
 
244
  /**
@@ -247,7 +244,7 @@ class Post_Republisher {
247
  * @return bool Whether or not the request is a REST request.
248
  */
249
  public function is_rest_request() {
250
- return defined( 'REST_REQUEST' ) && REST_REQUEST;
251
  }
252
 
253
  /**
@@ -279,8 +276,8 @@ class Post_Republisher {
279
  /**
280
  * Deletes the copy and associated post meta, if applicable.
281
  *
282
- * @param int $copy_id The copy's ID.
283
- * @param int|null $post_id The original post's ID. Optional.
284
  * @param bool $permanently_delete Whether to permanently delete the copy. Defaults to true.
285
  *
286
  * @return void
@@ -306,8 +303,8 @@ class Post_Republisher {
306
  /**
307
  * Republishes the post elements overwriting the original post.
308
  *
309
- * @param \WP_Post $post The post object.
310
- * @param \WP_Post $original_post The original post.
311
  *
312
  * @return void
313
  */
@@ -338,7 +335,7 @@ class Post_Republisher {
338
  /**
339
  * Republishes the post taxonomies overwriting the ones of the original post.
340
  *
341
- * @param \WP_Post $post The copy's post object.
342
  *
343
  * @return void
344
  */
@@ -356,7 +353,7 @@ class Post_Republisher {
356
  /**
357
  * Republishes the post meta overwriting the ones of the original post.
358
  *
359
- * @param \WP_Post $post The copy's post object.
360
  *
361
  * @return void
362
  */
@@ -397,8 +394,8 @@ class Post_Republisher {
397
  /**
398
  * Determines the post status to use when publishing the Rewrite & Republish copy.
399
  *
400
- * @param \WP_Post $post The post object.
401
- * @param \WP_Post $original_post The original post object.
402
  *
403
  * @return string The post status to use.
404
  */
1
  <?php
 
 
 
 
 
 
2
 
3
  namespace Yoast\WP\Duplicate_Post;
4
 
5
  use WP_Post;
6
 
7
  /**
8
+ * Duplicate Post class to republish a rewritten post.
9
+ *
10
+ * @since 4.0
11
  */
12
  class Post_Republisher {
13
 
122
  /**
123
  * Executes the republish request.
124
  *
125
+ * @param WP_Post $post The copy's post object.
126
  *
127
  * @return void
128
  */
152
  /**
153
  * Republishes the original post with the passed post, when using the Block Editor.
154
  *
155
+ * @param WP_Post $post The copy's post object.
156
  *
157
  * @return void
158
  */
166
  * Runs also in the Block Editor to save the custom meta data only when there
167
  * are custom meta boxes.
168
  *
169
+ * @param int $post_id The copy's post ID.
170
+ * @param WP_Post $post The copy's post object.
171
  *
172
  * @return void
173
  */
182
  /**
183
  * Republishes the scheduled Rewrited and Republish post.
184
  *
185
+ * @param WP_Post $copy The scheduled copy.
186
  *
187
  * @return void
188
  */
218
 
219
  if ( \intval( \get_post_meta( $copy_id, '_dp_has_been_republished', true ) ) === 1 ) {
220
  $this->delete_copy( $copy_id, $post_id );
221
+ }
222
+ else {
223
  \wp_die( \esc_html__( 'An error occurred while deleting the Rewrite & Republish copy.', 'duplicate-post' ) );
224
  }
225
  }
235
  return false;
236
  }
237
 
238
+ return isset( $_GET['meta-box-loader'] ) === false;
239
  }
240
 
241
  /**
244
  * @return bool Whether or not the request is a REST request.
245
  */
246
  public function is_rest_request() {
247
+ return \defined( 'REST_REQUEST' ) && \REST_REQUEST;
248
  }
249
 
250
  /**
276
  /**
277
  * Deletes the copy and associated post meta, if applicable.
278
  *
279
+ * @param int $copy_id The copy's ID.
280
+ * @param int|null $post_id The original post's ID. Optional.
281
  * @param bool $permanently_delete Whether to permanently delete the copy. Defaults to true.
282
  *
283
  * @return void
303
  /**
304
  * Republishes the post elements overwriting the original post.
305
  *
306
+ * @param WP_Post $post The post object.
307
+ * @param WP_Post $original_post The original post.
308
  *
309
  * @return void
310
  */
335
  /**
336
  * Republishes the post taxonomies overwriting the ones of the original post.
337
  *
338
+ * @param WP_Post $post The copy's post object.
339
  *
340
  * @return void
341
  */
353
  /**
354
  * Republishes the post meta overwriting the ones of the original post.
355
  *
356
+ * @param WP_Post $post The copy's post object.
357
  *
358
  * @return void
359
  */
394
  /**
395
  * Determines the post status to use when publishing the Rewrite & Republish copy.
396
  *
397
+ * @param WP_Post $post The post object.
398
+ * @param WP_Post $original_post The original post object.
399
  *
400
  * @return string The post status to use.
401
  */
src/{class-revisions-migrator.php → revisions-migrator.php} RENAMED
@@ -1,15 +1,11 @@
1
  <?php
2
- /**
3
- * Duplicate Post class to migrate revisions from the Rewrite & Republish copy to the original post.
4
- *
5
- * @package Duplicate_Post
6
- * @since 4.0
7
- */
8
 
9
  namespace Yoast\WP\Duplicate_Post;
10
 
11
  /**
12
- * Represents the Revisions Migrator class.
 
 
13
  */
14
  class Revisions_Migrator {
15
 
@@ -54,14 +50,14 @@ class Revisions_Migrator {
54
  }
55
 
56
  $revisions = \wp_get_post_revisions( $original_post, [ 'order' => 'ASC' ] );
57
- $delete = \count( $revisions ) - $revisions_to_keep;
58
  if ( $delete < 1 ) {
59
  return;
60
  }
61
 
62
  $revisions = \array_slice( $revisions, 0, $delete );
63
 
64
- for ( $i = 0; isset( $revisions[ $i ] ); $i ++ ) {
65
  if ( \strpos( $revisions[ $i ]->post_name, 'autosave' ) !== false ) {
66
  continue;
67
  }
1
  <?php
 
 
 
 
 
 
2
 
3
  namespace Yoast\WP\Duplicate_Post;
4
 
5
  /**
6
+ * Duplicate Post class to migrate revisions from the Rewrite & Republish copy to the original post.
7
+ *
8
+ * @since 4.0
9
  */
10
  class Revisions_Migrator {
11
 
50
  }
51
 
52
  $revisions = \wp_get_post_revisions( $original_post, [ 'order' => 'ASC' ] );
53
+ $delete = ( \count( $revisions ) - $revisions_to_keep );
54
  if ( $delete < 1 ) {
55
  return;
56
  }
57
 
58
  $revisions = \array_slice( $revisions, 0, $delete );
59
 
60
+ for ( $i = 0; isset( $revisions[ $i ] ); $i++ ) {
61
  if ( \strpos( $revisions[ $i ]->post_name, 'autosave' ) !== false ) {
62
  continue;
63
  }
src/ui/{class-admin-bar.php → admin-bar.php} RENAMED
@@ -1,9 +1,4 @@
1
  <?php
2
- /**
3
- * Duplicate Post class to manage the admin bar.
4
- *
5
- * @package Duplicate_Post
6
- */
7
 
8
  namespace Yoast\WP\Duplicate_Post\UI;
9
 
@@ -12,7 +7,7 @@ use Yoast\WP\Duplicate_Post\Permissions_Helper;
12
  use Yoast\WP\Duplicate_Post\Utils;
13
 
14
  /**
15
- * Represents the Admin_Bar class.
16
  */
17
  class Admin_Bar {
18
 
@@ -111,7 +106,8 @@ class Admin_Bar {
111
  'href' => $this->link_builder->build_rewrite_and_republish_link( $post ),
112
  ]
113
  );
114
- } else {
 
115
  if ( $show_new_draft ) {
116
  $wp_admin_bar->add_menu(
117
  [
@@ -167,7 +163,8 @@ class Admin_Bar {
167
 
168
  if ( \is_admin() ) {
169
  $post = \get_post();
170
- } else {
 
171
  $post = $wp_the_query->get_queried_object();
172
  }
173
 
1
  <?php
 
 
 
 
 
2
 
3
  namespace Yoast\WP\Duplicate_Post\UI;
4
 
7
  use Yoast\WP\Duplicate_Post\Utils;
8
 
9
  /**
10
+ * Duplicate Post class to manage the admin bar.
11
  */
12
  class Admin_Bar {
13
 
106
  'href' => $this->link_builder->build_rewrite_and_republish_link( $post ),
107
  ]
108
  );
109
+ }
110
+ else {
111
  if ( $show_new_draft ) {
112
  $wp_admin_bar->add_menu(
113
  [
163
 
164
  if ( \is_admin() ) {
165
  $post = \get_post();
166
+ }
167
+ else {
168
  $post = $wp_the_query->get_queried_object();
169
  }
170
 
src/ui/{class-asset-manager.php → asset-manager.php} RENAMED
@@ -1,16 +1,11 @@
1
  <?php
2
- /**
3
- * Duplicate Post class to manage assets.
4
- *
5
- * @package Duplicate_Post
6
- */
7
 
8
  namespace Yoast\WP\Duplicate_Post\UI;
9
 
10
  use Yoast\WP\Duplicate_Post\Utils;
11
 
12
  /**
13
- * Represents the Duplicate Post Asset Manager class.
14
  */
15
  class Asset_Manager {
16
 
@@ -30,8 +25,8 @@ class Asset_Manager {
30
  * @return void
31
  */
32
  public function register_styles() {
33
- \wp_register_style( 'duplicate-post', \plugins_url( '/css/duplicate-post.css', DUPLICATE_POST_FILE ), [], DUPLICATE_POST_CURRENT_VERSION );
34
- \wp_register_style( 'duplicate-post-options', \plugins_url( '/css/duplicate-post-options.css', DUPLICATE_POST_FILE ), [], DUPLICATE_POST_CURRENT_VERSION );
35
  }
36
 
37
  /**
@@ -40,45 +35,45 @@ class Asset_Manager {
40
  * @return void
41
  */
42
  public function register_scripts() {
43
- $flattened_version = Utils::flatten_version( DUPLICATE_POST_CURRENT_VERSION );
44
 
45
  \wp_register_script(
46
  'duplicate_post_edit_script',
47
- \plugins_url( \sprintf( 'js/dist/duplicate-post-edit-%s.js', $flattened_version ), DUPLICATE_POST_FILE ),
48
  [
49
  'wp-components',
50
  'wp-element',
51
  'wp-i18n',
52
  ],
53
- DUPLICATE_POST_CURRENT_VERSION,
54
  true
55
  );
56
 
57
  \wp_register_script(
58
  'duplicate_post_strings',
59
- \plugins_url( \sprintf( 'js/dist/duplicate-post-strings-%s.js', $flattened_version ), DUPLICATE_POST_FILE ),
60
  [
61
  'wp-components',
62
  'wp-element',
63
  'wp-i18n',
64
  ],
65
- DUPLICATE_POST_CURRENT_VERSION,
66
  true
67
  );
68
 
69
  \wp_register_script(
70
  'duplicate_post_quick_edit_script',
71
- \plugins_url( \sprintf( 'js/dist/duplicate-post-quick-edit-%s.js', $flattened_version ), DUPLICATE_POST_FILE ),
72
  [ 'jquery' ],
73
- DUPLICATE_POST_CURRENT_VERSION,
74
  true
75
  );
76
 
77
  \wp_register_script(
78
  'duplicate_post_options_script',
79
- \plugins_url( \sprintf( 'js/dist/duplicate-post-options-%s.js', $flattened_version ), DUPLICATE_POST_FILE ),
80
  [ 'jquery' ],
81
- DUPLICATE_POST_CURRENT_VERSION,
82
  true
83
  );
84
  }
@@ -104,11 +99,11 @@ class Asset_Manager {
104
  /**
105
  * Enqueues the script for the Block editor and passes object via localization.
106
  *
107
- * @param array $object The object to pass to the script.
108
  *
109
  * @return void
110
  */
111
- public function enqueue_edit_script( $object = [] ) {
112
  $handle = 'duplicate_post_edit_script';
113
  \wp_enqueue_script( $handle );
114
  \wp_add_inline_script(
@@ -119,24 +114,24 @@ class Asset_Manager {
119
  \wp_localize_script(
120
  $handle,
121
  'duplicatePost',
122
- $object
123
  );
124
  }
125
 
126
  /**
127
  * Enqueues the script for the Javascript strings and passes object via localization.
128
  *
129
- * @param array $object The object to pass to the script.
130
  *
131
  * @return void
132
  */
133
- public function enqueue_strings_script( $object = [] ) {
134
  $handle = 'duplicate_post_strings';
135
  \wp_enqueue_script( $handle );
136
  \wp_localize_script(
137
  $handle,
138
  'duplicatePostStrings',
139
- $object
140
  );
141
  }
142
 
@@ -161,26 +156,26 @@ class Asset_Manager {
161
  /**
162
  * Enqueues the script for the Elementor plugin.
163
  *
164
- * @param array $object The object to pass to the script.
165
  *
166
  * @return void
167
  */
168
- public function enqueue_elementor_script( $object = [] ) {
169
- $flattened_version = Utils::flatten_version( DUPLICATE_POST_CURRENT_VERSION );
170
  $handle = 'duplicate_post_elementor_script';
171
 
172
  \wp_register_script(
173
  $handle,
174
- \plugins_url( \sprintf( 'js/dist/duplicate-post-elementor-%s.js', $flattened_version ), DUPLICATE_POST_FILE ),
175
  [ 'jquery' ],
176
- DUPLICATE_POST_CURRENT_VERSION,
177
  true
178
  );
179
  \wp_enqueue_script( $handle );
180
  \wp_localize_script(
181
  $handle,
182
  'duplicatePost',
183
- $object
184
  );
185
  }
186
  }
1
  <?php
 
 
 
 
 
2
 
3
  namespace Yoast\WP\Duplicate_Post\UI;
4
 
5
  use Yoast\WP\Duplicate_Post\Utils;
6
 
7
  /**
8
+ * Duplicate Post class to manage assets.
9
  */
10
  class Asset_Manager {
11
 
25
  * @return void
26
  */
27
  public function register_styles() {
28
+ \wp_register_style( 'duplicate-post', \plugins_url( '/css/duplicate-post.css', \DUPLICATE_POST_FILE ), [], \DUPLICATE_POST_CURRENT_VERSION );
29
+ \wp_register_style( 'duplicate-post-options', \plugins_url( '/css/duplicate-post-options.css', \DUPLICATE_POST_FILE ), [], \DUPLICATE_POST_CURRENT_VERSION );
30
  }
31
 
32
  /**
35
  * @return void
36
  */
37
  public function register_scripts() {
38
+ $flattened_version = Utils::flatten_version( \DUPLICATE_POST_CURRENT_VERSION );
39
 
40
  \wp_register_script(
41
  'duplicate_post_edit_script',
42
+ \plugins_url( \sprintf( 'js/dist/duplicate-post-edit-%s.js', $flattened_version ), \DUPLICATE_POST_FILE ),
43
  [
44
  'wp-components',
45
  'wp-element',
46
  'wp-i18n',
47
  ],
48
+ \DUPLICATE_POST_CURRENT_VERSION,
49
  true
50
  );
51
 
52
  \wp_register_script(
53
  'duplicate_post_strings',
54
+ \plugins_url( \sprintf( 'js/dist/duplicate-post-strings-%s.js', $flattened_version ), \DUPLICATE_POST_FILE ),
55
  [
56
  'wp-components',
57
  'wp-element',
58
  'wp-i18n',
59
  ],
60
+ \DUPLICATE_POST_CURRENT_VERSION,
61
  true
62
  );
63
 
64
  \wp_register_script(
65
  'duplicate_post_quick_edit_script',
66
+ \plugins_url( \sprintf( 'js/dist/duplicate-post-quick-edit-%s.js', $flattened_version ), \DUPLICATE_POST_FILE ),
67
  [ 'jquery' ],
68
+ \DUPLICATE_POST_CURRENT_VERSION,
69
  true
70
  );
71
 
72
  \wp_register_script(
73
  'duplicate_post_options_script',
74
+ \plugins_url( \sprintf( 'js/dist/duplicate-post-options-%s.js', $flattened_version ), \DUPLICATE_POST_FILE ),
75
  [ 'jquery' ],
76
+ \DUPLICATE_POST_CURRENT_VERSION,
77
  true
78
  );
79
  }
99
  /**
100
  * Enqueues the script for the Block editor and passes object via localization.
101
  *
102
+ * @param array $data_object The object to pass to the script.
103
  *
104
  * @return void
105
  */
106
+ public function enqueue_edit_script( $data_object = [] ) {
107
  $handle = 'duplicate_post_edit_script';
108
  \wp_enqueue_script( $handle );
109
  \wp_add_inline_script(
114
  \wp_localize_script(
115
  $handle,
116
  'duplicatePost',
117
+ $data_object
118
  );
119
  }
120
 
121
  /**
122
  * Enqueues the script for the Javascript strings and passes object via localization.
123
  *
124
+ * @param array $data_object The object to pass to the script.
125
  *
126
  * @return void
127
  */
128
+ public function enqueue_strings_script( $data_object = [] ) {
129
  $handle = 'duplicate_post_strings';
130
  \wp_enqueue_script( $handle );
131
  \wp_localize_script(
132
  $handle,
133
  'duplicatePostStrings',
134
+ $data_object
135
  );
136
  }
137
 
156
  /**
157
  * Enqueues the script for the Elementor plugin.
158
  *
159
+ * @param array $data_object The object to pass to the script.
160
  *
161
  * @return void
162
  */
163
+ public function enqueue_elementor_script( $data_object = [] ) {
164
+ $flattened_version = Utils::flatten_version( \DUPLICATE_POST_CURRENT_VERSION );
165
  $handle = 'duplicate_post_elementor_script';
166
 
167
  \wp_register_script(
168
  $handle,
169
+ \plugins_url( \sprintf( 'js/dist/duplicate-post-elementor-%s.js', $flattened_version ), \DUPLICATE_POST_FILE ),
170
  [ 'jquery' ],
171
+ \DUPLICATE_POST_CURRENT_VERSION,
172
  true
173
  );
174
  \wp_enqueue_script( $handle );
175
  \wp_localize_script(
176
  $handle,
177
  'duplicatePost',
178
+ $data_object
179
  );
180
  }
181
  }
src/ui/{class-block-editor.php → block-editor.php} RENAMED
@@ -1,9 +1,4 @@
1
  <?php
2
- /**
3
- * Duplicate Post class to manage the block editor UI.
4
- *
5
- * @package Duplicate_Post
6
- */
7
 
8
  namespace Yoast\WP\Duplicate_Post\UI;
9
 
@@ -12,7 +7,7 @@ use Yoast\WP\Duplicate_Post\Permissions_Helper;
12
  use Yoast\WP\Duplicate_Post\Utils;
13
 
14
  /**
15
- * Represents the Block_Editor class.
16
  */
17
  class Block_Editor {
18
 
@@ -234,7 +229,7 @@ class Block_Editor {
234
  'rewriteAndRepublishLink' => $this->get_rewrite_republish_permalink(),
235
  'showLinks' => Utils::get_option( 'duplicate_post_show_link' ),
236
  'showLinksIn' => Utils::get_option( 'duplicate_post_show_link_in' ),
237
- 'rewriting' => $is_rewrite_and_republish_copy ? 1 : 0,
238
  'originalEditURL' => $this->get_original_post_edit_url(),
239
  ];
240
  }
@@ -267,7 +262,7 @@ class Block_Editor {
267
 
268
  return \array_filter(
269
  $suggestions,
270
- function( $suggestion ) use ( $original_post_id ) {
271
  return $suggestion->object_id !== $original_post_id;
272
  }
273
  );
1
  <?php
 
 
 
 
 
2
 
3
  namespace Yoast\WP\Duplicate_Post\UI;
4
 
7
  use Yoast\WP\Duplicate_Post\Utils;
8
 
9
  /**
10
+ * Duplicate Post class to manage the block editor UI.
11
  */
12
  class Block_Editor {
13
 
229
  'rewriteAndRepublishLink' => $this->get_rewrite_republish_permalink(),
230
  'showLinks' => Utils::get_option( 'duplicate_post_show_link' ),
231
  'showLinksIn' => Utils::get_option( 'duplicate_post_show_link_in' ),
232
+ 'rewriting' => ( $is_rewrite_and_republish_copy ) ? 1 : 0,
233
  'originalEditURL' => $this->get_original_post_edit_url(),
234
  ];
235
  }
262
 
263
  return \array_filter(
264
  $suggestions,
265
+ static function ( $suggestion ) use ( $original_post_id ) {
266
  return $suggestion->object_id !== $original_post_id;
267
  }
268
  );
src/ui/{class-bulk-actions.php → bulk-actions.php} RENAMED
@@ -1,9 +1,4 @@
1
  <?php
2
- /**
3
- * Duplicate Post class to manage the bulk actions menu.
4
- *
5
- * @package Duplicate_Post
6
- */
7
 
8
  namespace Yoast\WP\Duplicate_Post\UI;
9
 
@@ -11,7 +6,7 @@ use Yoast\WP\Duplicate_Post\Permissions_Helper;
11
  use Yoast\WP\Duplicate_Post\Utils;
12
 
13
  /**
14
- * Represents the Bulk_Actions class.
15
  */
16
  class Bulk_Actions {
17
 
@@ -68,8 +63,7 @@ class Bulk_Actions {
68
  * @return array The bulk actions array.
69
  */
70
  public function register_bulk_action( $bulk_actions ) {
71
- // phpcs:ignore WordPress.Security.NonceVerification
72
- $is_draft_or_trash = isset( $_REQUEST['post_status'] ) && in_array( $_REQUEST['post_status'], array( 'draft', 'trash' ), true );
73
 
74
  if ( \intval( Utils::get_option( 'duplicate_post_show_link', 'clone' ) ) === 1 ) {
75
  $bulk_actions['duplicate_post_bulk_clone'] = \esc_html__( 'Clone', 'duplicate-post' );
1
  <?php
 
 
 
 
 
2
 
3
  namespace Yoast\WP\Duplicate_Post\UI;
4
 
6
  use Yoast\WP\Duplicate_Post\Utils;
7
 
8
  /**
9
+ * Duplicate Post class to manage the bulk actions menu.
10
  */
11
  class Bulk_Actions {
12
 
63
  * @return array The bulk actions array.
64
  */
65
  public function register_bulk_action( $bulk_actions ) {
66
+ $is_draft_or_trash = isset( $_REQUEST['post_status'] ) && \in_array( $_REQUEST['post_status'], [ 'draft', 'trash' ], true );
 
67
 
68
  if ( \intval( Utils::get_option( 'duplicate_post_show_link', 'clone' ) ) === 1 ) {
69
  $bulk_actions['duplicate_post_bulk_clone'] = \esc_html__( 'Clone', 'duplicate-post' );
src/ui/class-metabox.php DELETED
@@ -1,121 +0,0 @@
1
- <?php
2
- /**
3
- * Duplicate Post class to manage the metabox.
4
- *
5
- * @package Duplicate_Post
6
- */
7
-
8
- namespace Yoast\WP\Duplicate_Post\UI;
9
-
10
- use Yoast\WP\Duplicate_Post\Permissions_Helper;
11
- use Yoast\WP\Duplicate_Post\Utils;
12
-
13
- /**
14
- * Represents the Metabox class.
15
- */
16
- class Metabox {
17
-
18
- /**
19
- * Holds the permissions helper.
20
- *
21
- * @var Permissions_Helper
22
- */
23
- protected $permissions_helper;
24
-
25
- /**
26
- * Initializes the class.
27
- *
28
- * @param Permissions_Helper $permissions_helper The permissions helper.
29
- */
30
- public function __construct( Permissions_Helper $permissions_helper ) {
31
- $this->permissions_helper = $permissions_helper;
32
- }
33
-
34
- /**
35
- * Adds hooks to integrate with WordPress.
36
- *
37
- * @return void
38
- */
39
- public function register_hooks() {
40
- if ( \intval( \get_option( 'duplicate_post_show_original_meta_box' ) ) === 1 ) {
41
- \add_action( 'add_meta_boxes', [ $this, 'add_custom_metabox' ] );
42
- }
43
- }
44
-
45
- /**
46
- * Adds a metabox to Edit screen.
47
- *
48
- * @return void
49
- */
50
- public function add_custom_metabox() {
51
- $screens = $this->permissions_helper->get_enabled_post_types();
52
- if ( ! \is_array( $screens ) ) {
53
- $screens = [ $screens ];
54
- }
55
- foreach ( $screens as $screen ) {
56
- \add_meta_box(
57
- 'duplicate_post_show_original',
58
- \__( 'Duplicate Post', 'duplicate-post' ),
59
- [ $this, 'custom_metabox_html' ],
60
- $screen,
61
- 'side'
62
- );
63
- }
64
- }
65
-
66
- /**
67
- * Outputs the HTML for the metabox.
68
- *
69
- * @param \WP_Post $post The current post.
70
- *
71
- * @return void
72
- */
73
- public function custom_metabox_html( $post ) {
74
- $original_item = Utils::get_original( $post );
75
- if ( $post instanceof \WP_Post && $original_item instanceof \WP_Post ) {
76
- if ( ! $this->permissions_helper->is_rewrite_and_republish_copy( $post ) ) {
77
- ?>
78
- <p>
79
- <input type="checkbox"
80
- name="duplicate_post_remove_original"
81
- id="duplicate-post-remove-original"
82
- value="duplicate_post_remove_original"
83
- aria-describedby="duplicate-post-remove-original-description">
84
- <label for="duplicate-post-remove-original">
85
- <?php \esc_html_e( 'Delete reference to original item.', 'duplicate-post' ); ?>
86
- </label>
87
- </p>
88
- <?php
89
- }
90
- ?>
91
- <p id="duplicate-post-remove-original-description">
92
- <?php
93
- \printf(
94
- \wp_kses(
95
- /* translators: %s: post title */
96
- \__(
97
- 'The original item this was copied from is: <span class="duplicate_post_original_item_title_span">%s</span>',
98
- 'duplicate-post'
99
- ),
100
- [
101
- 'span' => [
102
- 'class' => [],
103
- ],
104
- ]
105
- ),
106
- Utils::get_edit_or_view_link( $original_item ) // phpcs:ignore WordPress.Security.EscapeOutput
107
- );
108
- ?>
109
- </p>
110
- <?php
111
- } else {
112
- ?>
113
- <script>
114
- (function(jQuery){
115
- jQuery('#duplicate_post_show_original').hide();
116
- })(jQuery);
117
- </script>
118
- <?php
119
- }
120
- }
121
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/ui/{class-classic-editor.php → classic-editor.php} RENAMED
@@ -1,9 +1,4 @@
1
  <?php
2
- /**
3
- * Duplicate Post class to manage the classic editor UI.
4
- *
5
- * @package Duplicate_Post
6
- */
7
 
8
  namespace Yoast\WP\Duplicate_Post\UI;
9
 
@@ -12,7 +7,7 @@ use Yoast\WP\Duplicate_Post\Permissions_Helper;
12
  use Yoast\WP\Duplicate_Post\Utils;
13
 
14
  /**
15
- * Represents the Classic_Editor class.
16
  */
17
  class Classic_Editor {
18
 
@@ -86,8 +81,8 @@ class Classic_Editor {
86
  * @return void
87
  */
88
  public function enqueue_classic_editor_scripts() {
89
- if ( $this->permissions_helper->is_classic_editor() && isset( $_GET['post'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
90
- $id = \intval( \wp_unslash( $_GET['post'] ) ); // phpcs:ignore WordPress.Security.NonceVerification
91
  $post = \get_post( $id );
92
 
93
  if ( ! \is_null( $post ) && $this->permissions_helper->is_rewrite_and_republish_copy( $post ) ) {
@@ -103,8 +98,8 @@ class Classic_Editor {
103
  */
104
  public function enqueue_classic_editor_styles() {
105
  if ( $this->permissions_helper->is_classic_editor()
106
- && isset( $_GET['post'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
107
- $id = \intval( \wp_unslash( $_GET['post'] ) ); // phpcs:ignore WordPress.Security.NonceVerification
108
  $post = \get_post( $id );
109
 
110
  if ( ! \is_null( $post ) && $this->permissions_helper->is_rewrite_and_republish_copy( $post ) ) {
@@ -116,14 +111,14 @@ class Classic_Editor {
116
  /**
117
  * Adds a button in the post/page edit screen to create a clone
118
  *
119
- * @param \WP_Post|null $post The post object that's being edited.
120
  *
121
  * @return void
122
  */
123
  public function add_new_draft_post_button( $post = null ) {
124
  if ( \is_null( $post ) ) {
125
- if ( isset( $_GET['post'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
126
- $id = \intval( \wp_unslash( $_GET['post'] ) ); // phpcs:ignore WordPress.Security.NonceVerification
127
  $post = \get_post( $id );
128
  }
129
  }
@@ -142,14 +137,14 @@ class Classic_Editor {
142
  /**
143
  * Adds a button in the post/page edit screen to create a clone for Rewrite & Republish.
144
  *
145
- * @param \WP_Post|null $post The post object that's being edited.
146
  *
147
  * @return void
148
  */
149
  public function add_rewrite_and_republish_post_button( $post = null ) {
150
  if ( \is_null( $post ) ) {
151
- if ( isset( $_GET['post'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
152
- $id = \intval( \wp_unslash( $_GET['post'] ) ); // phpcs:ignore WordPress.Security.NonceVerification
153
  $post = \get_post( $id );
154
  }
155
  }
@@ -171,14 +166,14 @@ class Classic_Editor {
171
  /**
172
  * Adds a message in the post/page edit screen to create a clone for Rewrite & Republish.
173
  *
174
- * @param \WP_Post|null $post The post object that's being edited.
175
  *
176
  * @return void
177
  */
178
  public function add_check_changes_link( $post = null ) {
179
  if ( \is_null( $post ) ) {
180
- if ( isset( $_GET['post'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
181
- $id = \intval( \wp_unslash( $_GET['post'] ) ); // phpcs:ignore WordPress.Security.NonceVerification
182
  $post = \get_post( $id );
183
  }
184
  }
@@ -254,7 +249,7 @@ class Classic_Editor {
254
 
255
  if ( $post->post_type === 'post' ) {
256
  $messages['post'][9] = \sprintf(
257
- /* translators: 1: The post title with a link to the frontend page, 2: The scheduled date and time. */
258
  \esc_html__(
259
  'This rewritten post %1$s is now scheduled to replace the original post. It will be published on %2$s.',
260
  'duplicate-post'
@@ -267,7 +262,7 @@ class Classic_Editor {
267
 
268
  if ( $post->post_type === 'page' ) {
269
  $messages['page'][9] = \sprintf(
270
- /* translators: 1: The page title with a link to the frontend page, 2: The scheduled date and time. */
271
  \esc_html__(
272
  'This rewritten page %1$s is now scheduled to replace the original page. It will be published on %2$s.',
273
  'duplicate-post'
@@ -283,7 +278,7 @@ class Classic_Editor {
283
  /**
284
  * Determines if the Rewrite & Republish copies for the post should be used.
285
  *
286
- * @param \WP_Post $post The current post object.
287
  *
288
  * @return bool True if the Rewrite & Republish copies should be used.
289
  */
@@ -303,8 +298,8 @@ class Classic_Editor {
303
  /**
304
  * Removes the slug meta box in the Classic Editor when the post is a Rewrite & Republish copy.
305
  *
306
- * @param string $post_type Post type.
307
- * @param \WP_Post $post Post object.
308
  *
309
  * @return void
310
  */
@@ -317,23 +312,23 @@ class Classic_Editor {
317
  /**
318
  * Removes the sample permalink slug editor in the Classic Editor when the post is a Rewrite & Republish copy.
319
  *
320
- * @param string $return Sample permalink HTML markup.
321
- * @param int $post_id Post ID.
322
- * @param string $new_title New sample permalink title.
323
- * @param string $new_slug New sample permalink slug.
324
- * @param \WP_Post $post Post object.
325
  *
326
  * @return string The filtered HTML of the sample permalink slug editor.
327
  */
328
- public function remove_sample_permalink_slug_editor( $return, $post_id, $new_title, $new_slug, $post ) {
329
  if ( ! $post instanceof WP_Post ) {
330
- return $return;
331
  }
332
 
333
  if ( $this->permissions_helper->is_rewrite_and_republish_copy( $post ) ) {
334
  return '';
335
  }
336
 
337
- return $return;
338
  }
339
  }
1
  <?php
 
 
 
 
 
2
 
3
  namespace Yoast\WP\Duplicate_Post\UI;
4
 
7
  use Yoast\WP\Duplicate_Post\Utils;
8
 
9
  /**
10
+ * Duplicate Post class to manage the classic editor UI.
11
  */
12
  class Classic_Editor {
13
 
81
  * @return void
82
  */
83
  public function enqueue_classic_editor_scripts() {
84
+ if ( $this->permissions_helper->is_classic_editor() && isset( $_GET['post'] ) ) {
85
+ $id = \intval( \wp_unslash( $_GET['post'] ) );
86
  $post = \get_post( $id );
87
 
88
  if ( ! \is_null( $post ) && $this->permissions_helper->is_rewrite_and_republish_copy( $post ) ) {
98
  */
99
  public function enqueue_classic_editor_styles() {
100
  if ( $this->permissions_helper->is_classic_editor()
101
+ && isset( $_GET['post'] ) ) {
102
+ $id = \intval( \wp_unslash( $_GET['post'] ) );
103
  $post = \get_post( $id );
104
 
105
  if ( ! \is_null( $post ) && $this->permissions_helper->is_rewrite_and_republish_copy( $post ) ) {
111
  /**
112
  * Adds a button in the post/page edit screen to create a clone
113
  *
114
+ * @param WP_Post|null $post The post object that's being edited.
115
  *
116
  * @return void
117
  */
118
  public function add_new_draft_post_button( $post = null ) {
119
  if ( \is_null( $post ) ) {
120
+ if ( isset( $_GET['post'] ) ) {
121
+ $id = \intval( \wp_unslash( $_GET['post'] ) );
122
  $post = \get_post( $id );
123
  }
124
  }
137
  /**
138
  * Adds a button in the post/page edit screen to create a clone for Rewrite & Republish.
139
  *
140
+ * @param WP_Post|null $post The post object that's being edited.
141
  *
142
  * @return void
143
  */
144
  public function add_rewrite_and_republish_post_button( $post = null ) {
145
  if ( \is_null( $post ) ) {
146
+ if ( isset( $_GET['post'] ) ) {
147
+ $id = \intval( \wp_unslash( $_GET['post'] ) );
148
  $post = \get_post( $id );
149
  }
150
  }
166
  /**
167
  * Adds a message in the post/page edit screen to create a clone for Rewrite & Republish.
168
  *
169
+ * @param WP_Post|null $post The post object that's being edited.
170
  *
171
  * @return void
172
  */
173
  public function add_check_changes_link( $post = null ) {
174
  if ( \is_null( $post ) ) {
175
+ if ( isset( $_GET['post'] ) ) {
176
+ $id = \intval( \wp_unslash( $_GET['post'] ) );
177
  $post = \get_post( $id );
178
  }
179
  }
249
 
250
  if ( $post->post_type === 'post' ) {
251
  $messages['post'][9] = \sprintf(
252
+ /* translators: 1: The post title with a link to the frontend page, 2: The scheduled date and time. */
253
  \esc_html__(
254
  'This rewritten post %1$s is now scheduled to replace the original post. It will be published on %2$s.',
255
  'duplicate-post'
262
 
263
  if ( $post->post_type === 'page' ) {
264
  $messages['page'][9] = \sprintf(
265
+ /* translators: 1: The page title with a link to the frontend page, 2: The scheduled date and time. */
266
  \esc_html__(
267
  'This rewritten page %1$s is now scheduled to replace the original page. It will be published on %2$s.',
268
  'duplicate-post'
278
  /**
279
  * Determines if the Rewrite & Republish copies for the post should be used.
280
  *
281
+ * @param WP_Post $post The current post object.
282
  *
283
  * @return bool True if the Rewrite & Republish copies should be used.
284
  */
298
  /**
299
  * Removes the slug meta box in the Classic Editor when the post is a Rewrite & Republish copy.
300
  *
301
+ * @param string $post_type Post type.
302
+ * @param WP_Post $post Post object.
303
  *
304
  * @return void
305
  */
312
  /**
313
  * Removes the sample permalink slug editor in the Classic Editor when the post is a Rewrite & Republish copy.
314
  *
315
+ * @param string $html Sample permalink HTML markup.
316
+ * @param int $post_id Post ID.
317
+ * @param string $new_title New sample permalink title.
318
+ * @param string $new_slug New sample permalink slug.
319
+ * @param WP_Post $post Post object.
320
  *
321
  * @return string The filtered HTML of the sample permalink slug editor.
322
  */
323
+ public function remove_sample_permalink_slug_editor( $html, $post_id, $new_title, $new_slug, $post ) {
324
  if ( ! $post instanceof WP_Post ) {
325
+ return $html;
326
  }
327
 
328
  if ( $this->permissions_helper->is_rewrite_and_republish_copy( $post ) ) {
329
  return '';
330
  }
331
 
332
+ return $html;
333
  }
334
  }
src/ui/{class-column.php → column.php} RENAMED
@@ -1,17 +1,13 @@
1
  <?php
2
- /**
3
- * Duplicate Post class to manage the custom column + quick edit.
4
- *
5
- * @package Duplicate_Post
6
- */
7
 
8
  namespace Yoast\WP\Duplicate_Post\UI;
9
 
 
10
  use Yoast\WP\Duplicate_Post\Permissions_Helper;
11
  use Yoast\WP\Duplicate_Post\Utils;
12
 
13
  /**
14
- * Represents the Column class.
15
  */
16
  class Column {
17
 
@@ -83,15 +79,19 @@ class Column {
83
  * @return void
84
  */
85
  public function show_original_item( $column_name, $post_id ) {
86
- if ( 'duplicate_post_original_item' === $column_name ) {
87
  $column_content = '-';
88
  $data_attr = ' data-no-original="1"';
89
  $original_item = Utils::get_original( $post_id );
90
  if ( $original_item ) {
91
- $post = \get_post( $post_id );
92
- $is_rewrite_and_republish_copy = \is_null( $post ) ? false : $this->permissions_helper->is_rewrite_and_republish_copy( $post );
 
 
 
 
 
93
 
94
- $data_attr = $is_rewrite_and_republish_copy ? ' data-copy-is-for-rewrite-and-republish="1"' : '';
95
  $column_content = Utils::get_edit_or_view_link( $original_item );
96
  }
97
  echo \sprintf(
@@ -110,7 +110,7 @@ class Column {
110
  * @return void
111
  */
112
  public function quick_edit_remove_original( $column_name ) {
113
- if ( 'duplicate_post_original_item' !== $column_name ) {
114
  return;
115
  }
116
  \printf(
@@ -153,7 +153,7 @@ class Column {
153
  * @return void
154
  */
155
  public function admin_enqueue_scripts( $hook ) {
156
- if ( 'edit.php' === $hook ) {
157
  $this->asset_manager->enqueue_quick_edit_script();
158
  }
159
  }
@@ -166,7 +166,7 @@ class Column {
166
  * @return void
167
  */
168
  public function admin_enqueue_styles( $hook ) {
169
- if ( 'edit.php' === $hook ) {
170
  $this->asset_manager->enqueue_styles();
171
  }
172
  }
1
  <?php
 
 
 
 
 
2
 
3
  namespace Yoast\WP\Duplicate_Post\UI;
4
 
5
+ use WP_Post;
6
  use Yoast\WP\Duplicate_Post\Permissions_Helper;
7
  use Yoast\WP\Duplicate_Post\Utils;
8
 
9
  /**
10
+ * Duplicate Post class to manage the custom column + quick edit.
11
  */
12
  class Column {
13
 
79
  * @return void
80
  */
81
  public function show_original_item( $column_name, $post_id ) {
82
+ if ( $column_name === 'duplicate_post_original_item' ) {
83
  $column_content = '-';
84
  $data_attr = ' data-no-original="1"';
85
  $original_item = Utils::get_original( $post_id );
86
  if ( $original_item ) {
87
+ $post = \get_post( $post_id );
88
+ $data_attr = '';
89
+
90
+ if ( $post instanceof WP_Post
91
+ && $this->permissions_helper->is_rewrite_and_republish_copy( $post ) ) {
92
+ $data_attr = ' data-copy-is-for-rewrite-and-republish="1"';
93
+ }
94
 
 
95
  $column_content = Utils::get_edit_or_view_link( $original_item );
96
  }
97
  echo \sprintf(
110
  * @return void
111
  */
112
  public function quick_edit_remove_original( $column_name ) {
113
+ if ( $column_name !== 'duplicate_post_original_item' ) {
114
  return;
115
  }
116
  \printf(
153
  * @return void
154
  */
155
  public function admin_enqueue_scripts( $hook ) {
156
+ if ( $hook === 'edit.php' ) {
157
  $this->asset_manager->enqueue_quick_edit_script();
158
  }
159
  }
166
  * @return void
167
  */
168
  public function admin_enqueue_styles( $hook ) {
169
+ if ( $hook === 'edit.php' ) {
170
  $this->asset_manager->enqueue_styles();
171
  }
172
  }
src/ui/{class-link-builder.php → link-builder.php} RENAMED
@@ -1,24 +1,19 @@
1
  <?php
2
- /**
3
- * Duplicate Post link builder.
4
- *
5
- * @package Duplicate_Post
6
- */
7
 
8
  namespace Yoast\WP\Duplicate_Post\UI;
9
 
 
 
10
  /**
11
- * Class Link_Builder
12
- *
13
- * @package Yoast\WP\Duplicate_Post
14
  */
15
  class Link_Builder {
16
 
17
  /**
18
  * Builds URL for duplication action for the Rewrite & Republish feature.
19
  *
20
- * @param int|\WP_Post $post The post object or ID.
21
- * @param string $context The context in which the URL will be used.
22
  *
23
  * @return string The URL for the link.
24
  */
@@ -29,8 +24,8 @@ class Link_Builder {
29
  /**
30
  * Builds URL for the "Clone" action.
31
  *
32
- * @param int|\WP_Post $post The post object or ID.
33
- * @param string $context The context in which the URL will be used.
34
  *
35
  * @return string The URL for the link.
36
  */
@@ -41,8 +36,8 @@ class Link_Builder {
41
  /**
42
  * Builds URL for the "Copy to a new draft" action.
43
  *
44
- * @param int|\WP_Post $post The post object or ID.
45
- * @param string $context The context in which the URL will be used.
46
  *
47
  * @return string The URL for the link.
48
  */
@@ -53,8 +48,8 @@ class Link_Builder {
53
  /**
54
  * Builds URL for the "Check Changes" action.
55
  *
56
- * @param int|\WP_Post $post The post object or ID.
57
- * @param string $context The context in which the URL will be used.
58
  *
59
  * @return string The URL for the link.
60
  */
@@ -65,35 +60,36 @@ class Link_Builder {
65
  /**
66
  * Builds URL for duplication action.
67
  *
68
- * @param int|\WP_Post $post The post object or ID.
69
- * @param string $context The context in which the URL will be used.
70
- * @param string $action_name The action for the URL.
71
  *
72
  * @return string The URL for the link.
73
  */
74
  public function build_link( $post, $context, $action_name ) {
75
  $post = \get_post( $post );
76
- if ( ! $post instanceof \WP_Post ) {
77
  return '';
78
  }
79
 
80
- if ( 'display' === $context ) {
81
  $action = '?action=' . $action_name . '&amp;post=' . $post->ID;
82
- } else {
 
83
  $action = '?action=' . $action_name . '&post=' . $post->ID;
84
  }
85
 
86
  return \wp_nonce_url(
87
- /**
88
- * Filter on the URL of the clone link
89
- *
90
- * @param string $url The URL of the clone link.
91
- * @param int $ID The ID of the post
92
- * @param string $context The context in which the URL is used.
93
- * @param string $action_name The action name.
94
- *
95
- * @return string
96
- */
97
  \apply_filters( 'duplicate_post_get_clone_post_link', \admin_url( 'admin.php' . $action ), $post->ID, $context, $action_name ),
98
  $action_name . '_' . $post->ID
99
  );
1
  <?php
 
 
 
 
 
2
 
3
  namespace Yoast\WP\Duplicate_Post\UI;
4
 
5
+ use WP_Post;
6
+
7
  /**
8
+ * Duplicate Post link builder.
 
 
9
  */
10
  class Link_Builder {
11
 
12
  /**
13
  * Builds URL for duplication action for the Rewrite & Republish feature.
14
  *
15
+ * @param int|WP_Post $post The post object or ID.
16
+ * @param string $context The context in which the URL will be used.
17
  *
18
  * @return string The URL for the link.
19
  */
24
  /**
25
  * Builds URL for the "Clone" action.
26
  *
27
+ * @param int|WP_Post $post The post object or ID.
28
+ * @param string $context The context in which the URL will be used.
29
  *
30
  * @return string The URL for the link.
31
  */
36
  /**
37
  * Builds URL for the "Copy to a new draft" action.
38
  *
39
+ * @param int|WP_Post $post The post object or ID.
40
+ * @param string $context The context in which the URL will be used.
41
  *
42
  * @return string The URL for the link.
43
  */
48
  /**
49
  * Builds URL for the "Check Changes" action.
50
  *
51
+ * @param int|WP_Post $post The post object or ID.
52
+ * @param string $context The context in which the URL will be used.
53
  *
54
  * @return string The URL for the link.
55
  */
60
  /**
61
  * Builds URL for duplication action.
62
  *
63
+ * @param int|WP_Post $post The post object or ID.
64
+ * @param string $context The context in which the URL will be used.
65
+ * @param string $action_name The action for the URL.
66
  *
67
  * @return string The URL for the link.
68
  */
69
  public function build_link( $post, $context, $action_name ) {
70
  $post = \get_post( $post );
71
+ if ( ! $post instanceof WP_Post ) {
72
  return '';
73
  }
74
 
75
+ if ( $context === 'display' ) {
76
  $action = '?action=' . $action_name . '&amp;post=' . $post->ID;
77
+ }
78
+ else {
79
  $action = '?action=' . $action_name . '&post=' . $post->ID;
80
  }
81
 
82
  return \wp_nonce_url(
83
+ /**
84
+ * Filter on the URL of the clone link
85
+ *
86
+ * @param string $url The URL of the clone link.
87
+ * @param int $ID The ID of the post
88
+ * @param string $context The context in which the URL is used.
89
+ * @param string $action_name The action name.
90
+ *
91
+ * @return string
92
+ */
93
  \apply_filters( 'duplicate_post_get_clone_post_link', \admin_url( 'admin.php' . $action ), $post->ID, $context, $action_name ),
94
  $action_name . '_' . $post->ID
95
  );
src/ui/metabox.php ADDED
@@ -0,0 +1,116 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Yoast\WP\Duplicate_Post\UI;
4
+
5
+ use WP_Post;
6
+ use Yoast\WP\Duplicate_Post\Permissions_Helper;
7
+ use Yoast\WP\Duplicate_Post\Utils;
8
+
9
+ /**
10
+ * Duplicate Post class to manage the metabox.
11
+ */
12
+ class Metabox {
13
+
14
+ /**
15
+ * Holds the permissions helper.
16
+ *
17
+ * @var Permissions_Helper
18
+ */
19
+ protected $permissions_helper;
20
+
21
+ /**
22
+ * Initializes the class.
23
+ *
24
+ * @param Permissions_Helper $permissions_helper The permissions helper.
25
+ */
26
+ public function __construct( Permissions_Helper $permissions_helper ) {
27
+ $this->permissions_helper = $permissions_helper;
28
+ }
29
+
30
+ /**
31
+ * Adds hooks to integrate with WordPress.
32
+ *
33
+ * @return void
34
+ */
35
+ public function register_hooks() {
36
+ if ( \intval( \get_option( 'duplicate_post_show_original_meta_box' ) ) === 1 ) {
37
+ \add_action( 'add_meta_boxes', [ $this, 'add_custom_metabox' ], 10, 2 );
38
+ }
39
+ }
40
+
41
+ /**
42
+ * Adds a metabox to Edit screen.
43
+ *
44
+ * @param string $post_type The post type.
45
+ * @param WP_Post $post The current post object.
46
+ *
47
+ * @return void
48
+ */
49
+ public function add_custom_metabox( $post_type, $post ) {
50
+ $enabled_post_types = $this->permissions_helper->get_enabled_post_types();
51
+
52
+ if ( \in_array( $post_type, $enabled_post_types, true )
53
+ && $post instanceof WP_Post ) {
54
+ $original_item = Utils::get_original( $post );
55
+
56
+ if ( $original_item instanceof WP_Post ) {
57
+ \add_meta_box(
58
+ 'duplicate_post_show_original',
59
+ \__( 'Duplicate Post', 'duplicate-post' ),
60
+ [ $this, 'custom_metabox_html' ],
61
+ $post_type,
62
+ 'side',
63
+ 'default',
64
+ [ 'original' => $original_item ]
65
+ );
66
+ }
67
+ }
68
+ }
69
+
70
+ /**
71
+ * Outputs the HTML for the metabox.
72
+ *
73
+ * @param WP_Post $post The current post.
74
+ * @param array $metabox The array containing the metabox data.
75
+ *
76
+ * @return void
77
+ */
78
+ public function custom_metabox_html( $post, $metabox ) {
79
+ $original_item = $metabox['args']['original'];
80
+ if ( ! $this->permissions_helper->is_rewrite_and_republish_copy( $post ) ) {
81
+ ?>
82
+ <p>
83
+ <input type="checkbox"
84
+ name="duplicate_post_remove_original"
85
+ id="duplicate-post-remove-original"
86
+ value="duplicate_post_remove_original"
87
+ aria-describedby="duplicate-post-remove-original-description">
88
+ <label for="duplicate-post-remove-original">
89
+ <?php \esc_html_e( 'Delete reference to original item.', 'duplicate-post' ); ?>
90
+ </label>
91
+ </p>
92
+ <?php
93
+ }
94
+ ?>
95
+ <p id="duplicate-post-remove-original-description">
96
+ <?php
97
+ \printf(
98
+ \wp_kses(
99
+ /* translators: %s: post title */
100
+ \__(
101
+ 'The original item this was copied from is: <span class="duplicate_post_original_item_title_span">%s</span>',
102
+ 'duplicate-post'
103
+ ),
104
+ [
105
+ 'span' => [
106
+ 'class' => [],
107
+ ],
108
+ ]
109
+ ),
110
+ Utils::get_edit_or_view_link( $original_item ) // phpcs:ignore WordPress.Security.EscapeOutput
111
+ );
112
+ ?>
113
+ </p>
114
+ <?php
115
+ }
116
+ }
src/ui/newsletter.php ADDED
@@ -0,0 +1,127 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Yoast\WP\Duplicate_Post\UI;
4
+
5
+ /**
6
+ * Newsletter class.
7
+ */
8
+ class Newsletter {
9
+
10
+ /**
11
+ * Renders the newsletter signup form.
12
+ *
13
+ * @return string The HTML of the newsletter signup form (escaped).
14
+ */
15
+ public static function newsletter_signup_form() {
16
+
17
+ $newsletter_form_response = self::newsletter_handle_form();
18
+
19
+
20
+ $copy = sprintf(
21
+ /* translators: 1: Yoast */
22
+ esc_html__(
23
+ 'If you want to stay up to date about all the exciting developments around Duplicate Post, subscribe to the %1$s newsletter!',
24
+ 'duplicate-post'
25
+ ),
26
+ 'Yoast'
27
+ );
28
+
29
+ $email_label = esc_html__( 'Email Address', 'duplicate-post' );
30
+
31
+ $response_html = '';
32
+ if ( is_array( $newsletter_form_response ) ) {
33
+ $response_status = $newsletter_form_response['status'];
34
+ $response_message = $newsletter_form_response['message'];
35
+
36
+ $response_html = '<div class="newsletter-response-' . $response_status . ' clear" id="newsletter-response" style="margin-top: 6px;">' . $response_message . '</div>';
37
+ }
38
+
39
+ $html = '
40
+ <!-- Begin Newsletter Signup Form -->
41
+ <form method="post" id="newsletter-subscribe-form" name="newsletter-subscribe-form" novalidate>
42
+ ' . wp_nonce_field( 'newsletter', 'newsletter_nonce' ) . '
43
+ <p>' . $copy . '</p>
44
+ <div class="newsletter-field-group" style="display: flex; align-items: center;">
45
+ <label for="newsletter-email" style="margin-right: 4px;"><strong>' . $email_label . '</strong></label>
46
+ <input type="email" value="" name="EMAIL" class="required email" id="newsletter-email" style="margin-right: 4px;">
47
+ <input type="submit" value="' . esc_attr__( 'Subscribe', 'duplicate-post' ) . '" name="subscribe" id="newsletter-subscribe" class="button">
48
+ </div>
49
+ ' . $response_html . '
50
+ </form>
51
+ <!--End Newsletter Signup Form-->
52
+ ';
53
+
54
+ return $html;
55
+ }
56
+
57
+ /**
58
+ * Handles and validates Newsletter form.
59
+ *
60
+ * @return null|array.
61
+ */
62
+ private static function newsletter_handle_form() {
63
+
64
+ //phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Already sanitized.
65
+ if ( isset( $_POST['newsletter_nonce'] ) && ! wp_verify_nonce( wp_unslash( $_POST['newsletter_nonce'] ), 'newsletter' ) ) {
66
+ return [
67
+ 'status' => 'error',
68
+ 'message' => esc_html__( 'Something went wrong. Please try again later.', 'duplicate-post' ),
69
+ ];
70
+ }
71
+
72
+ $email = null;
73
+ if ( isset( $_POST['EMAIL'] ) ) {
74
+ $email = sanitize_email( wp_unslash( $_POST['EMAIL'] ) );
75
+ }
76
+
77
+ if ( $email === null ) {
78
+ return null;
79
+ }
80
+
81
+ if ( ! is_email( $email ) ) {
82
+ return [
83
+ 'status' => 'error',
84
+ 'message' => esc_html__( 'Please enter valid e-mail address.', 'duplicate-post' ),
85
+ ];
86
+ }
87
+
88
+ return self::newsletter_subscribe_to_mailblue( $email );
89
+ }
90
+
91
+ /**
92
+ * Handles subscription request and provides feedback response.
93
+ *
94
+ * @param string $email Subscriber email.
95
+ *
96
+ * @return array Feedback response.
97
+ */
98
+ private static function newsletter_subscribe_to_mailblue( $email ) {
99
+ $response = wp_remote_post(
100
+ 'https://my.yoast.com/api/Mailing-list/subscribe',
101
+ [
102
+ 'method' => 'POST',
103
+ 'body' => [
104
+ 'customerDetails' => [
105
+ 'email' => $email,
106
+ 'firstName' => '',
107
+ ],
108
+ 'list' => 'Yoast newsletter',
109
+ ],
110
+ ]
111
+ );
112
+
113
+ $wp_remote_retrieve_response_code = wp_remote_retrieve_response_code( $response );
114
+
115
+ if ( $wp_remote_retrieve_response_code <= 200 || $wp_remote_retrieve_response_code >= 300 ) {
116
+ return [
117
+ 'status' => 'error',
118
+ 'message' => esc_html__( 'Something went wrong. Please try again later.', 'duplicate-post' ),
119
+ ];
120
+ }
121
+
122
+ return [
123
+ 'status' => 'success',
124
+ 'message' => esc_html__( 'You have successfully subscribed to the newsletter. Please check your inbox.', 'duplicate-post' ),
125
+ ];
126
+ }
127
+ }
src/ui/{class-post-states.php → post-states.php} RENAMED
@@ -1,9 +1,4 @@
1
  <?php
2
- /**
3
- * Duplicate Post class to manage the post states display.
4
- *
5
- * @package Duplicate_Post
6
- */
7
 
8
  namespace Yoast\WP\Duplicate_Post\UI;
9
 
@@ -12,7 +7,7 @@ use Yoast\WP\Duplicate_Post\Permissions_Helper;
12
  use Yoast\WP\Duplicate_Post\Utils;
13
 
14
  /**
15
- * Represents the Post_States class.
16
  */
17
  class Post_States {
18
 
@@ -44,8 +39,8 @@ class Post_States {
44
  /**
45
  * Shows link to original post in the post states.
46
  *
47
- * @param array $post_states The array of post states.
48
- * @param \WP_Post $post The current post.
49
  *
50
  * @return array The updated post states array.
51
  */
1
  <?php
 
 
 
 
 
2
 
3
  namespace Yoast\WP\Duplicate_Post\UI;
4
 
7
  use Yoast\WP\Duplicate_Post\Utils;
8
 
9
  /**
10
+ * Duplicate Post class to manage the post states display.
11
  */
12
  class Post_States {
13
 
39
  /**
40
  * Shows link to original post in the post states.
41
  *
42
+ * @param array $post_states The array of post states.
43
+ * @param WP_Post $post The current post.
44
  *
45
  * @return array The updated post states array.
46
  */
src/ui/{class-row-actions.php → row-actions.php} RENAMED
@@ -1,9 +1,4 @@
1
  <?php
2
- /**
3
- * Duplicate Post class to manage the row actions.
4
- *
5
- * @package Duplicate_Post
6
- */
7
 
8
  namespace Yoast\WP\Duplicate_Post\UI;
9
 
@@ -12,7 +7,7 @@ use Yoast\WP\Duplicate_Post\Permissions_Helper;
12
  use Yoast\WP\Duplicate_Post\Utils;
13
 
14
  /**
15
- * Represents the Row_Action class.
16
  */
17
  class Row_Actions {
18
 
@@ -70,8 +65,8 @@ class Row_Actions {
70
  /**
71
  * Hooks in the `post_row_actions` and `page_row_actions` filters to add a 'Clone' link.
72
  *
73
- * @param array $actions The array of actions from the filter.
74
- * @param \WP_Post $post The post object.
75
  *
76
  * @return array The updated array of actions.
77
  */
@@ -84,12 +79,12 @@ class Row_Actions {
84
 
85
  $title = \_draft_or_post_title( $post );
86
 
87
- $actions['clone'] = '<a href="' . $this->link_builder->build_clone_link( $post->ID ) .
88
- '" aria-label="' . \esc_attr(
89
- /* translators: %s: Post title. */
90
  \sprintf( \__( 'Clone &#8220;%s&#8221;', 'duplicate-post' ), $title )
91
- ) . '">' .
92
- \esc_html_x( 'Clone', 'verb', 'duplicate-post' ) . '</a>';
93
 
94
  return $actions;
95
  }
@@ -97,8 +92,8 @@ class Row_Actions {
97
  /**
98
  * Hooks in the `post_row_actions` and `page_row_actions` filters to add a 'New Draft' link.
99
  *
100
- * @param array $actions The array of actions from the filter.
101
- * @param \WP_Post $post The post object.
102
  *
103
  * @return array The updated array of actions.
104
  */
@@ -111,13 +106,13 @@ class Row_Actions {
111
 
112
  $title = \_draft_or_post_title( $post );
113
 
114
- $actions['edit_as_new_draft'] = '<a href="' . $this->link_builder->build_new_draft_link( $post->ID ) .
115
- '" aria-label="' . \esc_attr(
116
- /* translators: %s: Post title. */
117
  \sprintf( \__( 'New draft of &#8220;%s&#8221;', 'duplicate-post' ), $title )
118
- ) . '">' .
119
- \esc_html__( 'New Draft', 'duplicate-post' ) .
120
- '</a>';
121
 
122
  return $actions;
123
  }
@@ -125,8 +120,8 @@ class Row_Actions {
125
  /**
126
  * Hooks in the `post_row_actions` and `page_row_actions` filters to add a 'Rewrite & Republish' link.
127
  *
128
- * @param array $actions The array of actions from the filter.
129
- * @param \WP_Post $post The post object.
130
  *
131
  * @return array The updated array of actions.
132
  */
@@ -142,12 +137,12 @@ class Row_Actions {
142
 
143
  $title = \_draft_or_post_title( $post );
144
 
145
- $actions['rewrite'] = '<a href="' . $this->link_builder->build_rewrite_and_republish_link( $post->ID ) .
146
- '" aria-label="' . \esc_attr(
147
- /* translators: %s: Post title. */
148
  \sprintf( \__( 'Rewrite & Republish &#8220;%s&#8221;', 'duplicate-post' ), $title )
149
- ) . '">' .
150
- \esc_html_x( 'Rewrite & Republish', 'verb', 'duplicate-post' ) . '</a>';
151
 
152
  return $actions;
153
  }
1
  <?php
 
 
 
 
 
2
 
3
  namespace Yoast\WP\Duplicate_Post\UI;
4
 
7
  use Yoast\WP\Duplicate_Post\Utils;
8
 
9
  /**
10
+ * Duplicate Post class to manage the row actions.
11
  */
12
  class Row_Actions {
13
 
65
  /**
66
  * Hooks in the `post_row_actions` and `page_row_actions` filters to add a 'Clone' link.
67
  *
68
+ * @param array $actions The array of actions from the filter.
69
+ * @param WP_Post $post The post object.
70
  *
71
  * @return array The updated array of actions.
72
  */
79
 
80
  $title = \_draft_or_post_title( $post );
81
 
82
+ $actions['clone'] = '<a href="' . $this->link_builder->build_clone_link( $post->ID )
83
+ . '" aria-label="' . \esc_attr(
84
+ /* translators: %s: Post title. */
85
  \sprintf( \__( 'Clone &#8220;%s&#8221;', 'duplicate-post' ), $title )
86
+ ) . '">'
87
+ . \esc_html_x( 'Clone', 'verb', 'duplicate-post' ) . '</a>';
88
 
89
  return $actions;
90
  }
92
  /**
93
  * Hooks in the `post_row_actions` and `page_row_actions` filters to add a 'New Draft' link.
94
  *
95
+ * @param array $actions The array of actions from the filter.
96
+ * @param WP_Post $post The post object.
97
  *
98
  * @return array The updated array of actions.
99
  */
106
 
107
  $title = \_draft_or_post_title( $post );
108
 
109
+ $actions['edit_as_new_draft'] = '<a href="' . $this->link_builder->build_new_draft_link( $post->ID )
110
+ . '" aria-label="' . \esc_attr(
111
+ /* translators: %s: Post title. */
112
  \sprintf( \__( 'New draft of &#8220;%s&#8221;', 'duplicate-post' ), $title )
113
+ ) . '">'
114
+ . \esc_html__( 'New Draft', 'duplicate-post' )
115
+ . '</a>';
116
 
117
  return $actions;
118
  }
120
  /**
121
  * Hooks in the `post_row_actions` and `page_row_actions` filters to add a 'Rewrite & Republish' link.
122
  *
123
+ * @param array $actions The array of actions from the filter.
124
+ * @param WP_Post $post The post object.
125
  *
126
  * @return array The updated array of actions.
127
  */
137
 
138
  $title = \_draft_or_post_title( $post );
139
 
140
+ $actions['rewrite'] = '<a href="' . $this->link_builder->build_rewrite_and_republish_link( $post->ID )
141
+ . '" aria-label="' . \esc_attr(
142
+ /* translators: %s: Post title. */
143
  \sprintf( \__( 'Rewrite & Republish &#8220;%s&#8221;', 'duplicate-post' ), $title )
144
+ ) . '">'
145
+ . \esc_html_x( 'Rewrite & Republish', 'verb', 'duplicate-post' ) . '</a>';
146
 
147
  return $actions;
148
  }
src/ui/{class-user-interface.php → user-interface.php} RENAMED
@@ -1,16 +1,11 @@
1
  <?php
2
- /**
3
- * Duplicate Post user interface.
4
- *
5
- * @package Duplicate_Post
6
- */
7
 
8
  namespace Yoast\WP\Duplicate_Post\UI;
9
 
10
  use Yoast\WP\Duplicate_Post\Permissions_Helper;
11
 
12
  /**
13
- * Represents the Duplicate Post User Interface class.
14
  */
15
  class User_Interface {
16
 
@@ -70,6 +65,13 @@ class User_Interface {
70
  */
71
  protected $metabox;
72
 
 
 
 
 
 
 
 
73
  /**
74
  * Column object.
75
  *
@@ -107,6 +109,7 @@ class User_Interface {
107
  $this->bulk_actions = new Bulk_Actions( $this->permissions_helper );
108
  $this->column = new Column( $this->permissions_helper, $this->asset_manager );
109
  $this->metabox = new Metabox( $this->permissions_helper );
 
110
  $this->post_states = new Post_States( $this->permissions_helper );
111
  $this->classic_editor = new Classic_Editor( $this->link_builder, $this->permissions_helper, $this->asset_manager );
112
  $this->row_actions = new Row_Actions( $this->link_builder, $this->permissions_helper );
1
  <?php
 
 
 
 
 
2
 
3
  namespace Yoast\WP\Duplicate_Post\UI;
4
 
5
  use Yoast\WP\Duplicate_Post\Permissions_Helper;
6
 
7
  /**
8
+ * Duplicate Post user interface.
9
  */
10
  class User_Interface {
11
 
65
  */
66
  protected $metabox;
67
 
68
+ /**
69
+ * Newsletter object.
70
+ *
71
+ * @var Newsletter
72
+ */
73
+ protected $newsletter;
74
+
75
  /**
76
  * Column object.
77
  *
109
  $this->bulk_actions = new Bulk_Actions( $this->permissions_helper );
110
  $this->column = new Column( $this->permissions_helper, $this->asset_manager );
111
  $this->metabox = new Metabox( $this->permissions_helper );
112
+ $this->newsletter = new Newsletter();
113
  $this->post_states = new Post_States( $this->permissions_helper );
114
  $this->classic_editor = new Classic_Editor( $this->link_builder, $this->permissions_helper, $this->asset_manager );
115
  $this->row_actions = new Row_Actions( $this->link_builder, $this->permissions_helper );
src/{class-utils.php → utils.php} RENAMED
@@ -1,15 +1,13 @@
1
  <?php
2
- /**
3
- * Utility methods for Duplicate Post.
4
- *
5
- * @package Duplicate_Post
6
- * @since 4.0
7
- */
8
 
9
  namespace Yoast\WP\Duplicate_Post;
10
 
 
 
11
  /**
12
- * Represents the Utils class.
 
 
13
  */
14
  class Utils {
15
 
@@ -57,10 +55,10 @@ class Utils {
57
  /**
58
  * Gets the original post.
59
  *
60
- * @param int|\WP_Post|null $post Optional. Post ID or Post object.
61
- * @param string $output Optional, default is Object. Either OBJECT, ARRAY_A, or ARRAY_N.
62
  *
63
- * @return \WP_Post|null Post data if successful, null otherwise.
64
  */
65
  public static function get_original( $post = null, $output = \OBJECT ) {
66
  $post = \get_post( $post );
@@ -82,8 +80,8 @@ class Utils {
82
  *
83
  * If we are copying children, and the post has already an ancestor marked for copy, we have to filter it out.
84
  *
85
- * @param \WP_Post $post The post object.
86
- * @param array $post_ids The array of marked post IDs.
87
  *
88
  * @return bool Whether the post has ancestors marked for copy.
89
  */
@@ -92,7 +90,7 @@ class Utils {
92
  $parent = \wp_get_post_parent_id( $post->ID );
93
  while ( $parent ) {
94
  if ( \in_array( $parent, $post_ids, true ) ) {
95
- $ancestors_in_array++;
96
  }
97
  $parent = \wp_get_post_parent_id( $parent );
98
  }
@@ -102,7 +100,7 @@ class Utils {
102
  /**
103
  * Returns a link to edit, preview or view a post, in accordance to user capabilities.
104
  *
105
- * @param \WP_Post $post Post ID or Post object.
106
  *
107
  * @return string|null The link to edit, preview or view a post.
108
  */
@@ -119,12 +117,13 @@ class Utils {
119
  if ( $can_edit_post && $post->post_status !== 'trash' ) {
120
  return \sprintf(
121
  '<a href="%s" aria-label="%s">%s</a>',
122
- \get_edit_post_link( $post->ID ),
123
  /* translators: %s: post title */
124
- \esc_attr( \sprintf( \__( 'Edit &#8220;%s&#8221;', 'default' ), $title ) ),
125
  $title
126
  );
127
- } elseif ( \is_post_type_viewable( $post_type_object ) ) {
 
128
  if ( \in_array( $post->post_status, [ 'pending', 'draft', 'future' ], true ) ) {
129
  if ( $can_edit_post ) {
130
  $preview_link = \get_preview_post_link( $post );
@@ -132,20 +131,22 @@ class Utils {
132
  '<a href="%s" rel="bookmark" aria-label="%s">%s</a>',
133
  \esc_url( $preview_link ),
134
  /* translators: %s: post title */
135
- \esc_attr( \sprintf( \__( 'Preview &#8220;%s&#8221;', 'default' ), $title ) ),
136
  $title
137
  );
138
  }
139
- } elseif ( $post->post_status !== 'trash' ) {
 
140
  return \sprintf(
141
  '<a href="%s" rel="bookmark" aria-label="%s">%s</a>',
142
- \get_permalink( $post->ID ),
143
  /* translators: %s: post title */
144
- \esc_attr( \sprintf( \__( 'View &#8220;%s&#8221;', 'default' ), $title ) ),
145
  $title
146
  );
147
  }
148
  }
 
149
  return $title;
150
  }
151
 
@@ -163,8 +164,9 @@ class Utils {
163
  /**
164
  * Gets the registered WordPress roles.
165
  *
166
- * @return array The roles.
167
  * @codeCoverageIgnore As this is a simple wrapper method for a built-in WordPress method, we don't have to test it.
 
 
168
  */
169
  public static function get_roles() {
170
  global $wp_roles;
1
  <?php
 
 
 
 
 
 
2
 
3
  namespace Yoast\WP\Duplicate_Post;
4
 
5
+ use WP_Post;
6
+
7
  /**
8
+ * Utility methods for Duplicate Post.
9
+ *
10
+ * @since 4.0
11
  */
12
  class Utils {
13
 
55
  /**
56
  * Gets the original post.
57
  *
58
+ * @param int|WP_Post|null $post Optional. Post ID or Post object.
59
+ * @param string $output Optional, default is Object. Either OBJECT, ARRAY_A, or ARRAY_N.
60
  *
61
+ * @return WP_Post|null Post data if successful, null otherwise.
62
  */
63
  public static function get_original( $post = null, $output = \OBJECT ) {
64
  $post = \get_post( $post );
80
  *
81
  * If we are copying children, and the post has already an ancestor marked for copy, we have to filter it out.
82
  *
83
+ * @param WP_Post $post The post object.
84
+ * @param array $post_ids The array of marked post IDs.
85
  *
86
  * @return bool Whether the post has ancestors marked for copy.
87
  */
90
  $parent = \wp_get_post_parent_id( $post->ID );
91
  while ( $parent ) {
92
  if ( \in_array( $parent, $post_ids, true ) ) {
93
+ ++$ancestors_in_array;
94
  }
95
  $parent = \wp_get_post_parent_id( $parent );
96
  }
100
  /**
101
  * Returns a link to edit, preview or view a post, in accordance to user capabilities.
102
  *
103
+ * @param WP_Post $post Post ID or Post object.
104
  *
105
  * @return string|null The link to edit, preview or view a post.
106
  */
117
  if ( $can_edit_post && $post->post_status !== 'trash' ) {
118
  return \sprintf(
119
  '<a href="%s" aria-label="%s">%s</a>',
120
+ \esc_url( \get_edit_post_link( $post->ID ) ),
121
  /* translators: %s: post title */
122
+ \esc_attr( \sprintf( \__( 'Edit &#8220;%s&#8221;', 'duplicate-post' ), $title ) ),
123
  $title
124
  );
125
+ }
126
+ elseif ( \is_post_type_viewable( $post_type_object ) ) {
127
  if ( \in_array( $post->post_status, [ 'pending', 'draft', 'future' ], true ) ) {
128
  if ( $can_edit_post ) {
129
  $preview_link = \get_preview_post_link( $post );
131
  '<a href="%s" rel="bookmark" aria-label="%s">%s</a>',
132
  \esc_url( $preview_link ),
133
  /* translators: %s: post title */
134
+ \esc_attr( \sprintf( \__( 'Preview &#8220;%s&#8221;', 'duplicate-post' ), $title ) ),
135
  $title
136
  );
137
  }
138
+ }
139
+ elseif ( $post->post_status !== 'trash' ) {
140
  return \sprintf(
141
  '<a href="%s" rel="bookmark" aria-label="%s">%s</a>',
142
+ \esc_url( \get_permalink( $post->ID ) ),
143
  /* translators: %s: post title */
144
+ \esc_attr( \sprintf( \__( 'View &#8220;%s&#8221;', 'duplicate-post' ), $title ) ),
145
  $title
146
  );
147
  }
148
  }
149
+
150
  return $title;
151
  }
152
 
164
  /**
165
  * Gets the registered WordPress roles.
166
  *
 
167
  * @codeCoverageIgnore As this is a simple wrapper method for a built-in WordPress method, we don't have to test it.
168
+ *
169
+ * @return array The roles.
170
  */
171
  public static function get_roles() {
172
  global $wp_roles;
src/watchers/{class-bulk-actions-watcher.php → bulk-actions-watcher.php} RENAMED
@@ -1,14 +1,9 @@
1
  <?php
2
- /**
3
- * Duplicate Post class to watch for the bulk actions and show notices.
4
- *
5
- * @package Duplicate_Post
6
- */
7
 
8
  namespace Yoast\WP\Duplicate_Post\Watchers;
9
 
10
  /**
11
- * Represents the Bulk_Actions_Watcher class.
12
  */
13
  class Bulk_Actions_Watcher {
14
 
@@ -51,12 +46,12 @@ class Bulk_Actions_Watcher {
51
  * @return void
52
  */
53
  public function add_bulk_clone_admin_notice() {
54
- if ( ! empty( $_REQUEST['bulk_cloned'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
55
- $copied_posts = \intval( $_REQUEST['bulk_cloned'] ); // phpcs:ignore WordPress.Security.NonceVerification
56
  \printf(
57
- '<div id="message" class="notice notice-success fade"><p>' .
58
- \esc_html(
59
- /* translators: %s: Number of posts copied. */
60
  \_n(
61
  '%s item copied.',
62
  '%s items copied.',
@@ -75,12 +70,12 @@ class Bulk_Actions_Watcher {
75
  * @return void
76
  */
77
  public function add_bulk_rewrite_and_republish_admin_notice() {
78
- if ( ! empty( $_REQUEST['bulk_rewriting'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
79
- $copied_posts = \intval( $_REQUEST['bulk_rewriting'] ); // phpcs:ignore WordPress.Security.NonceVerification
80
  \printf(
81
- '<div id="message" class="notice notice-success fade"><p>' .
82
- \esc_html(
83
- /* translators: %s: Number of posts copied. */
84
  \_n(
85
  '%s post duplicated. You can now start rewriting your post in the duplicate of the original post. Once you choose to republish it your changes will be merged back into the original post.',
86
  '%s posts duplicated. You can now start rewriting your posts in the duplicates of the original posts. Once you choose to republish them your changes will be merged back into the original post.',
1
  <?php
 
 
 
 
 
2
 
3
  namespace Yoast\WP\Duplicate_Post\Watchers;
4
 
5
  /**
6
+ * Duplicate Post class to watch for the bulk actions and show notices.
7
  */
8
  class Bulk_Actions_Watcher {
9
 
46
  * @return void
47
  */
48
  public function add_bulk_clone_admin_notice() {
49
+ if ( ! empty( $_REQUEST['bulk_cloned'] ) ) {
50
+ $copied_posts = \intval( $_REQUEST['bulk_cloned'] );
51
  \printf(
52
+ '<div id="message" class="notice notice-success fade"><p>'
53
+ . \esc_html(
54
+ /* translators: %s: Number of posts copied. */
55
  \_n(
56
  '%s item copied.',
57
  '%s items copied.',
70
  * @return void
71
  */
72
  public function add_bulk_rewrite_and_republish_admin_notice() {
73
+ if ( ! empty( $_REQUEST['bulk_rewriting'] ) ) {
74
+ $copied_posts = \intval( $_REQUEST['bulk_rewriting'] );
75
  \printf(
76
+ '<div id="message" class="notice notice-success fade"><p>'
77
+ . \esc_html(
78
+ /* translators: %s: Number of posts copied. */
79
  \_n(
80
  '%s post duplicated. You can now start rewriting your post in the duplicate of the original post. Once you choose to republish it your changes will be merged back into the original post.',
81
  '%s posts duplicated. You can now start rewriting your posts in the duplicates of the original posts. Once you choose to republish them your changes will be merged back into the original post.',
src/watchers/{class-copied-post-watcher.php → copied-post-watcher.php} RENAMED
@@ -1,16 +1,12 @@
1
  <?php
2
- /**
3
- * Duplicate Post class to watch if the current post has a Rewrite & Republish copy.
4
- *
5
- * @package Duplicate_Post
6
- */
7
 
8
  namespace Yoast\WP\Duplicate_Post\Watchers;
9
 
 
10
  use Yoast\WP\Duplicate_Post\Permissions_Helper;
11
 
12
  /**
13
- * Represents the Copied_Post_Watcher class.
14
  */
15
  class Copied_Post_Watcher {
16
 
@@ -45,7 +41,7 @@ class Copied_Post_Watcher {
45
  /**
46
  * Generates the translated text for the notice.
47
  *
48
- * @param \WP_Post $post The current post object.
49
  *
50
  * @return string The translated text for the notice.
51
  */
@@ -88,14 +84,14 @@ class Copied_Post_Watcher {
88
 
89
  $post = \get_post();
90
 
91
- if ( ! $post instanceof \WP_Post ) {
92
  return;
93
  }
94
 
95
  if ( $this->permissions_helper->has_rewrite_and_republish_copy( $post ) ) {
96
- print '<div id="message" class="notice notice-warning is-dismissible fade"><p>' .
97
- \esc_html( $this->get_notice_text( $post ) ) .
98
- '</p></div>';
99
  }
100
  }
101
 
@@ -107,7 +103,7 @@ class Copied_Post_Watcher {
107
  public function add_block_editor_notice() {
108
  $post = \get_post();
109
 
110
- if ( ! $post instanceof \WP_Post ) {
111
  return;
112
  }
113
 
1
  <?php
 
 
 
 
 
2
 
3
  namespace Yoast\WP\Duplicate_Post\Watchers;
4
 
5
+ use WP_Post;
6
  use Yoast\WP\Duplicate_Post\Permissions_Helper;
7
 
8
  /**
9
+ * Duplicate Post class to watch if the current post has a Rewrite & Republish copy.
10
  */
11
  class Copied_Post_Watcher {
12
 
41
  /**
42
  * Generates the translated text for the notice.
43
  *
44
+ * @param WP_Post $post The current post object.
45
  *
46
  * @return string The translated text for the notice.
47
  */
84
 
85
  $post = \get_post();
86
 
87
+ if ( ! $post instanceof WP_Post ) {
88
  return;
89
  }
90
 
91
  if ( $this->permissions_helper->has_rewrite_and_republish_copy( $post ) ) {
92
+ print '<div id="message" class="notice notice-warning is-dismissible fade"><p>'
93
+ . \esc_html( $this->get_notice_text( $post ) )
94
+ . '</p></div>';
95
  }
96
  }
97
 
103
  public function add_block_editor_notice() {
104
  $post = \get_post();
105
 
106
+ if ( ! $post instanceof WP_Post ) {
107
  return;
108
  }
109
 
src/watchers/{class-link-actions-watcher.php → link-actions-watcher.php} RENAMED
@@ -1,16 +1,11 @@
1
  <?php
2
- /**
3
- * Duplicate Post class to watch for the link actions and show notices.
4
- *
5
- * @package Duplicate_Post
6
- */
7
 
8
  namespace Yoast\WP\Duplicate_Post\Watchers;
9
 
10
  use Yoast\WP\Duplicate_Post\Permissions_Helper;
11
 
12
  /**
13
- * Represents the Link_Actions_Watcher class.
14
  */
15
  class Link_Actions_Watcher {
16
 
@@ -65,16 +60,16 @@ class Link_Actions_Watcher {
65
  * @return void
66
  */
67
  public function add_clone_admin_notice() {
68
- if ( ! empty( $_REQUEST['cloned'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
69
  if ( ! $this->permissions_helper->is_classic_editor() ) {
70
  return;
71
  }
72
 
73
- $copied_posts = \intval( $_REQUEST['cloned'] ); // phpcs:ignore WordPress.Security.NonceVerification
74
  \printf(
75
- '<div id="message" class="notice notice-success fade"><p>' .
76
- \esc_html(
77
- /* translators: %s: Number of posts copied. */
78
  \_n(
79
  '%s item copied.',
80
  '%s items copied.',
@@ -93,16 +88,16 @@ class Link_Actions_Watcher {
93
  * @return void
94
  */
95
  public function add_rewrite_and_republish_admin_notice() {
96
- if ( ! empty( $_REQUEST['rewriting'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
97
  if ( ! $this->permissions_helper->is_classic_editor() ) {
98
  return;
99
  }
100
 
101
- print '<div id="message" class="notice notice-warning is-dismissible fade"><p>' .
102
- \esc_html__(
103
- 'You can now start rewriting your post in this duplicate of the original post. If you click "Republish", your changes will be merged into the original post and you’ll be redirected there.',
104
- 'duplicate-post'
105
- ) . '</p></div>';
106
  }
107
  }
108
 
@@ -112,10 +107,10 @@ class Link_Actions_Watcher {
112
  * @return void
113
  */
114
  public function add_rewrite_and_republish_block_editor_notice() {
115
- if ( ! empty( $_REQUEST['rewriting'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
116
  $notice = [
117
  'text' => \wp_slash(
118
- __(
119
  'You can now start rewriting your post in this duplicate of the original post. If you click "Republish", this rewritten post will replace the original post.',
120
  'duplicate-post'
121
  )
1
  <?php
 
 
 
 
 
2
 
3
  namespace Yoast\WP\Duplicate_Post\Watchers;
4
 
5
  use Yoast\WP\Duplicate_Post\Permissions_Helper;
6
 
7
  /**
8
+ * Duplicate Post class to watch for the link actions and show notices.
9
  */
10
  class Link_Actions_Watcher {
11
 
60
  * @return void
61
  */
62
  public function add_clone_admin_notice() {
63
+ if ( ! empty( $_REQUEST['cloned'] ) ) {
64
  if ( ! $this->permissions_helper->is_classic_editor() ) {
65
  return;
66
  }
67
 
68
+ $copied_posts = \intval( $_REQUEST['cloned'] );
69
  \printf(
70
+ '<div id="message" class="notice notice-success fade"><p>'
71
+ . \esc_html(
72
+ /* translators: %s: Number of posts copied. */
73
  \_n(
74
  '%s item copied.',
75
  '%s items copied.',
88
  * @return void
89
  */
90
  public function add_rewrite_and_republish_admin_notice() {
91
+ if ( ! empty( $_REQUEST['rewriting'] ) ) {
92
  if ( ! $this->permissions_helper->is_classic_editor() ) {
93
  return;
94
  }
95
 
96
+ print '<div id="message" class="notice notice-warning is-dismissible fade"><p>'
97
+ . \esc_html__(
98
+ 'You can now start rewriting your post in this duplicate of the original post. If you click "Republish", your changes will be merged into the original post and you’ll be redirected there.',
99
+ 'duplicate-post'
100
+ ) . '</p></div>';
101
  }
102
  }
103
 
107
  * @return void
108
  */
109
  public function add_rewrite_and_republish_block_editor_notice() {
110
+ if ( ! empty( $_REQUEST['rewriting'] ) ) {
111
  $notice = [
112
  'text' => \wp_slash(
113
+ \__(
114
  'You can now start rewriting your post in this duplicate of the original post. If you click "Republish", this rewritten post will replace the original post.',
115
  'duplicate-post'
116
  )
src/watchers/{class-original-post-watcher.php → original-post-watcher.php} RENAMED
@@ -1,19 +1,16 @@
1
  <?php
2
- /**
3
- * Duplicate Post Original post watcher class.
4
- *
5
- * @package Duplicate_Post
6
- * @since 4.0
7
- */
8
 
9
  namespace Yoast\WP\Duplicate_Post\Watchers;
10
 
 
11
  use Yoast\WP\Duplicate_Post\Permissions_Helper;
12
 
13
  /**
14
- * Original post watcher.
15
  *
16
  * Watches the original post for changes.
 
 
17
  */
18
  class Original_Post_Watcher {
19
 
@@ -69,14 +66,14 @@ class Original_Post_Watcher {
69
 
70
  $post = \get_post();
71
 
72
- if ( ! $post instanceof \WP_Post ) {
73
  return;
74
  }
75
 
76
  if ( $this->permissions_helper->has_original_changed( $post ) ) {
77
- print '<div id="message" class="notice notice-warning is-dismissible fade"><p>' .
78
- \esc_html( $this->get_notice_text() ) .
79
- '</p></div>';
80
  }
81
  }
82
 
@@ -88,7 +85,7 @@ class Original_Post_Watcher {
88
  public function add_block_editor_notice() {
89
  $post = \get_post();
90
 
91
- if ( ! $post instanceof \WP_Post ) {
92
  return;
93
  }
94
 
1
  <?php
 
 
 
 
 
 
2
 
3
  namespace Yoast\WP\Duplicate_Post\Watchers;
4
 
5
+ use WP_Post;
6
  use Yoast\WP\Duplicate_Post\Permissions_Helper;
7
 
8
  /**
9
+ * Duplicate Post Original post watcher class.
10
  *
11
  * Watches the original post for changes.
12
+ *
13
+ * @since 4.0
14
  */
15
  class Original_Post_Watcher {
16
 
66
 
67
  $post = \get_post();
68
 
69
+ if ( ! $post instanceof WP_Post ) {
70
  return;
71
  }
72
 
73
  if ( $this->permissions_helper->has_original_changed( $post ) ) {
74
+ print '<div id="message" class="notice notice-warning is-dismissible fade"><p>'
75
+ . \esc_html( $this->get_notice_text() )
76
+ . '</p></div>';
77
  }
78
  }
79
 
85
  public function add_block_editor_notice() {
86
  $post = \get_post();
87
 
88
+ if ( ! $post instanceof WP_Post ) {
89
  return;
90
  }
91
 
src/watchers/{class-republished-post-watcher.php → republished-post-watcher.php} RENAMED
@@ -1,17 +1,13 @@
1
  <?php
2
- /**
3
- * Duplicate Post class to watch if the post has been republished for Rewrite & Republish.
4
- *
5
- * @package Duplicate_Post
6
- * @since 4.0
7
- */
8
 
9
  namespace Yoast\WP\Duplicate_Post\Watchers;
10
 
11
  use Yoast\WP\Duplicate_Post\Permissions_Helper;
12
 
13
  /**
14
- * Represents the Republished_Post_Watcher class.
 
 
15
  */
16
  class Republished_Post_Watcher {
17
 
@@ -82,10 +78,10 @@ class Republished_Post_Watcher {
82
  return;
83
  }
84
 
85
- if ( ! empty( $_REQUEST['dprepublished'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
86
- echo '<div id="message" class="notice notice-success is-dismissible"><p>' .
87
- \esc_html( $this->get_notice_text() ) .
88
- '</p></div>';
89
  }
90
  }
91
 
@@ -95,7 +91,7 @@ class Republished_Post_Watcher {
95
  * @return void
96
  */
97
  public function add_block_editor_notice() {
98
- if ( ! empty( $_REQUEST['dprepublished'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
99
  $notice = [
100
  'text' => \wp_slash( $this->get_notice_text() ),
101
  'status' => 'success',
1
  <?php
 
 
 
 
 
 
2
 
3
  namespace Yoast\WP\Duplicate_Post\Watchers;
4
 
5
  use Yoast\WP\Duplicate_Post\Permissions_Helper;
6
 
7
  /**
8
+ * Duplicate Post class to watch if the post has been republished for Rewrite & Republish.
9
+ *
10
+ * @since 4.0
11
  */
12
  class Republished_Post_Watcher {
13
 
78
  return;
79
  }
80
 
81
+ if ( ! empty( $_REQUEST['dprepublished'] ) ) {
82
+ echo '<div id="message" class="notice notice-success is-dismissible"><p>'
83
+ . \esc_html( $this->get_notice_text() )
84
+ . '</p></div>';
85
  }
86
  }
87
 
91
  * @return void
92
  */
93
  public function add_block_editor_notice() {
94
+ if ( ! empty( $_REQUEST['dprepublished'] ) ) {
95
  $notice = [
96
  'text' => \wp_slash( $this->get_notice_text() ),
97
  'status' => 'success',
src/watchers/{class-watchers.php → watchers.php} RENAMED
@@ -1,16 +1,11 @@
1
  <?php
2
- /**
3
- * Duplicate Post user interface.
4
- *
5
- * @package Duplicate_Post
6
- */
7
 
8
  namespace Yoast\WP\Duplicate_Post\Watchers;
9
 
10
  use Yoast\WP\Duplicate_Post\Permissions_Helper;
11
 
12
  /**
13
- * Represents the Duplicate Post User Interface class.
14
  */
15
  class Watchers {
16
 
1
  <?php
 
 
 
 
 
2
 
3
  namespace Yoast\WP\Duplicate_Post\Watchers;
4
 
5
  use Yoast\WP\Duplicate_Post\Permissions_Helper;
6
 
7
  /**
8
+ * Duplicate Post user interface.
9
  */
10
  class Watchers {
11
 
vendor/composer/ClassLoader.php CHANGED
@@ -42,30 +42,75 @@ namespace Composer\Autoload;
42
  */
43
  class ClassLoader
44
  {
 
45
  private $vendorDir;
46
 
47
  // PSR-4
 
 
 
 
48
  private $prefixLengthsPsr4 = array();
 
 
 
 
49
  private $prefixDirsPsr4 = array();
 
 
 
 
50
  private $fallbackDirsPsr4 = array();
51
 
52
  // PSR-0
 
 
 
 
53
  private $prefixesPsr0 = array();
 
 
 
 
54
  private $fallbackDirsPsr0 = array();
55
 
 
56
  private $useIncludePath = false;
 
 
 
 
 
57
  private $classMap = array();
 
 
58
  private $classMapAuthoritative = false;
 
 
 
 
 
59
  private $missingClasses = array();
 
 
60
  private $apcuPrefix;
61
 
 
 
 
62
  private static $registeredLoaders = array();
63
 
 
 
 
64
  public function __construct($vendorDir = null)
65
  {
66
  $this->vendorDir = $vendorDir;
67
  }
68
 
 
 
 
69
  public function getPrefixes()
70
  {
71
  if (!empty($this->prefixesPsr0)) {
@@ -75,28 +120,47 @@ class ClassLoader
75
  return array();
76
  }
77
 
 
 
 
 
78
  public function getPrefixesPsr4()
79
  {
80
  return $this->prefixDirsPsr4;
81
  }
82
 
 
 
 
 
83
  public function getFallbackDirs()
84
  {
85
  return $this->fallbackDirsPsr0;
86
  }
87
 
 
 
 
 
88
  public function getFallbackDirsPsr4()
89
  {
90
  return $this->fallbackDirsPsr4;
91
  }
92
 
 
 
 
 
93
  public function getClassMap()
94
  {
95
  return $this->classMap;
96
  }
97
 
98
  /**
99
- * @param array $classMap Class to filename map
 
 
 
100
  */
101
  public function addClassMap(array $classMap)
102
  {
@@ -111,9 +175,11 @@ class ClassLoader
111
  * Registers a set of PSR-0 directories for a given prefix, either
112
  * appending or prepending to the ones previously set for this prefix.
113
  *
114
- * @param string $prefix The prefix
115
- * @param array|string $paths The PSR-0 root directories
116
- * @param bool $prepend Whether to prepend the directories
 
 
117
  */
118
  public function add($prefix, $paths, $prepend = false)
119
  {
@@ -156,11 +222,13 @@ class ClassLoader
156
  * Registers a set of PSR-4 directories for a given namespace, either
157
  * appending or prepending to the ones previously set for this namespace.
158
  *
159
- * @param string $prefix The prefix/namespace, with trailing '\\'
160
- * @param array|string $paths The PSR-4 base directories
161
- * @param bool $prepend Whether to prepend the directories
162
  *
163
  * @throws \InvalidArgumentException
 
 
164
  */
165
  public function addPsr4($prefix, $paths, $prepend = false)
166
  {
@@ -204,8 +272,10 @@ class ClassLoader
204
  * Registers a set of PSR-0 directories for a given prefix,
205
  * replacing any others previously set for this prefix.
206
  *
207
- * @param string $prefix The prefix
208
- * @param array|string $paths The PSR-0 base directories
 
 
209
  */
210
  public function set($prefix, $paths)
211
  {
@@ -220,10 +290,12 @@ class ClassLoader
220
  * Registers a set of PSR-4 directories for a given namespace,
221
  * replacing any others previously set for this namespace.
222
  *
223
- * @param string $prefix The prefix/namespace, with trailing '\\'
224
- * @param array|string $paths The PSR-4 base directories
225
  *
226
  * @throws \InvalidArgumentException
 
 
227
  */
228
  public function setPsr4($prefix, $paths)
229
  {
@@ -243,6 +315,8 @@ class ClassLoader
243
  * Turns on searching the include path for class files.
244
  *
245
  * @param bool $useIncludePath
 
 
246
  */
247
  public function setUseIncludePath($useIncludePath)
248
  {
@@ -265,6 +339,8 @@ class ClassLoader
265
  * that have not been registered with the class map.
266
  *
267
  * @param bool $classMapAuthoritative
 
 
268
  */
269
  public function setClassMapAuthoritative($classMapAuthoritative)
270
  {
@@ -285,6 +361,8 @@ class ClassLoader
285
  * APCu prefix to use to cache found/not-found classes, if the extension is enabled.
286
  *
287
  * @param string|null $apcuPrefix
 
 
288
  */
289
  public function setApcuPrefix($apcuPrefix)
290
  {
@@ -305,6 +383,8 @@ class ClassLoader
305
  * Registers this instance as an autoloader.
306
  *
307
  * @param bool $prepend Whether to prepend the autoloader or not
 
 
308
  */
309
  public function register($prepend = false)
310
  {
@@ -324,6 +404,8 @@ class ClassLoader
324
 
325
  /**
326
  * Unregisters this instance as an autoloader.
 
 
327
  */
328
  public function unregister()
329
  {
@@ -338,7 +420,7 @@ class ClassLoader
338
  * Loads the given class or interface.
339
  *
340
  * @param string $class The name of the class
341
- * @return bool|null True if loaded, null otherwise
342
  */
343
  public function loadClass($class)
344
  {
@@ -347,6 +429,8 @@ class ClassLoader
347
 
348
  return true;
349
  }
 
 
350
  }
351
 
352
  /**
@@ -401,6 +485,11 @@ class ClassLoader
401
  return self::$registeredLoaders;
402
  }
403
 
 
 
 
 
 
404
  private function findFileWithExtension($class, $ext)
405
  {
406
  // PSR-4 lookup
@@ -472,6 +561,10 @@ class ClassLoader
472
  * Scope isolated include.
473
  *
474
  * Prevents access to $this/self from included files.
 
 
 
 
475
  */
476
  function includeFile($file)
477
  {
42
  */
43
  class ClassLoader
44
  {
45
+ /** @var ?string */
46
  private $vendorDir;
47
 
48
  // PSR-4
49
+ /**
50
+ * @var array[]
51
+ * @psalm-var array<string, array<string, int>>
52
+ */
53
  private $prefixLengthsPsr4 = array();
54
+ /**
55
+ * @var array[]
56
+ * @psalm-var array<string, array<int, string>>
57
+ */
58
  private $prefixDirsPsr4 = array();
59
+ /**
60
+ * @var array[]
61
+ * @psalm-var array<string, string>
62
+ */
63
  private $fallbackDirsPsr4 = array();
64
 
65
  // PSR-0
66
+ /**
67
+ * @var array[]
68
+ * @psalm-var array<string, array<string, string[]>>
69
+ */
70
  private $prefixesPsr0 = array();
71
+ /**
72
+ * @var array[]
73
+ * @psalm-var array<string, string>
74
+ */
75
  private $fallbackDirsPsr0 = array();
76
 
77
+ /** @var bool */
78
  private $useIncludePath = false;
79
+
80
+ /**
81
+ * @var string[]
82
+ * @psalm-var array<string, string>
83
+ */
84
  private $classMap = array();
85
+
86
+ /** @var bool */
87
  private $classMapAuthoritative = false;
88
+
89
+ /**
90
+ * @var bool[]
91
+ * @psalm-var array<string, bool>
92
+ */
93
  private $missingClasses = array();
94
+
95
+ /** @var ?string */
96
  private $apcuPrefix;
97
 
98
+ /**
99
+ * @var self[]
100
+ */
101
  private static $registeredLoaders = array();
102
 
103
+ /**
104
+ * @param ?string $vendorDir
105
+ */
106
  public function __construct($vendorDir = null)
107
  {
108
  $this->vendorDir = $vendorDir;
109
  }
110
 
111
+ /**
112
+ * @return string[]
113
+ */
114
  public function getPrefixes()
115
  {
116
  if (!empty($this->prefixesPsr0)) {
120
  return array();
121
  }
122
 
123
+ /**
124
+ * @return array[]
125
+ * @psalm-return array<string, array<int, string>>
126
+ */
127
  public function getPrefixesPsr4()
128
  {
129
  return $this->prefixDirsPsr4;
130
  }
131
 
132
+ /**
133
+ * @return array[]
134
+ * @psalm-return array<string, string>
135
+ */
136
  public function getFallbackDirs()
137
  {
138
  return $this->fallbackDirsPsr0;
139
  }
140
 
141
+ /**
142
+ * @return array[]
143
+ * @psalm-return array<string, string>
144
+ */
145
  public function getFallbackDirsPsr4()
146
  {
147
  return $this->fallbackDirsPsr4;
148
  }
149
 
150
+ /**
151
+ * @return string[] Array of classname => path
152
+ * @psalm-var array<string, string>
153
+ */
154
  public function getClassMap()
155
  {
156
  return $this->classMap;
157
  }
158
 
159
  /**
160
+ * @param string[] $classMap Class to filename map
161
+ * @psalm-param array<string, string> $classMap
162
+ *
163
+ * @return void
164
  */
165
  public function addClassMap(array $classMap)
166
  {
175
  * Registers a set of PSR-0 directories for a given prefix, either
176
  * appending or prepending to the ones previously set for this prefix.
177
  *
178
+ * @param string $prefix The prefix
179
+ * @param string[]|string $paths The PSR-0 root directories
180
+ * @param bool $prepend Whether to prepend the directories
181
+ *
182
+ * @return void
183
  */
184
  public function add($prefix, $paths, $prepend = false)
185
  {
222
  * Registers a set of PSR-4 directories for a given namespace, either
223
  * appending or prepending to the ones previously set for this namespace.
224
  *
225
+ * @param string $prefix The prefix/namespace, with trailing '\\'
226
+ * @param string[]|string $paths The PSR-4 base directories
227
+ * @param bool $prepend Whether to prepend the directories
228
  *
229
  * @throws \InvalidArgumentException
230
+ *
231
+ * @return void
232
  */
233
  public function addPsr4($prefix, $paths, $prepend = false)
234
  {
272
  * Registers a set of PSR-0 directories for a given prefix,
273
  * replacing any others previously set for this prefix.
274
  *
275
+ * @param string $prefix The prefix
276
+ * @param string[]|string $paths The PSR-0 base directories
277
+ *
278
+ * @return void
279
  */
280
  public function set($prefix, $paths)
281
  {
290
  * Registers a set of PSR-4 directories for a given namespace,
291
  * replacing any others previously set for this namespace.
292
  *
293
+ * @param string $prefix The prefix/namespace, with trailing '\\'
294
+ * @param string[]|string $paths The PSR-4 base directories
295
  *
296
  * @throws \InvalidArgumentException
297
+ *
298
+ * @return void
299
  */
300
  public function setPsr4($prefix, $paths)
301
  {
315
  * Turns on searching the include path for class files.
316
  *
317
  * @param bool $useIncludePath
318
+ *
319
+ * @return void
320
  */
321
  public function setUseIncludePath($useIncludePath)
322
  {
339
  * that have not been registered with the class map.
340
  *
341
  * @param bool $classMapAuthoritative
342
+ *
343
+ * @return void
344
  */
345
  public function setClassMapAuthoritative($classMapAuthoritative)
346
  {
361
  * APCu prefix to use to cache found/not-found classes, if the extension is enabled.
362
  *
363
  * @param string|null $apcuPrefix
364
+ *
365
+ * @return void
366
  */
367
  public function setApcuPrefix($apcuPrefix)
368
  {
383
  * Registers this instance as an autoloader.
384
  *
385
  * @param bool $prepend Whether to prepend the autoloader or not
386
+ *
387
+ * @return void
388
  */
389
  public function register($prepend = false)
390
  {
404
 
405
  /**
406
  * Unregisters this instance as an autoloader.
407
+ *
408
+ * @return void
409
  */
410
  public function unregister()
411
  {
420
  * Loads the given class or interface.
421
  *
422
  * @param string $class The name of the class
423
+ * @return true|null True if loaded, null otherwise
424
  */
425
  public function loadClass($class)
426
  {
429
 
430
  return true;
431
  }
432
+
433
+ return null;
434
  }
435
 
436
  /**
485
  return self::$registeredLoaders;
486
  }
487
 
488
+ /**
489
+ * @param string $class
490
+ * @param string $ext
491
+ * @return string|false
492
+ */
493
  private function findFileWithExtension($class, $ext)
494
  {
495
  // PSR-4 lookup
561
  * Scope isolated include.
562
  *
563
  * Prevents access to $this/self from included files.
564
+ *
565
+ * @param string $file
566
+ * @return void
567
+ * @private
568
  */
569
  function includeFile($file)
570
  {
vendor/composer/InstalledVersions.php CHANGED
@@ -1,283 +1,350 @@
1
  <?php
2
 
3
-
4
-
5
-
6
-
7
-
8
-
9
-
10
-
11
-
12
 
13
  namespace Composer;
14
 
15
  use Composer\Autoload\ClassLoader;
16
  use Composer\Semver\VersionParser;
17
 
18
-
19
-
20
-
21
-
22
-
 
 
23
  class InstalledVersions
24
  {
25
- private static $installed = array (
26
- 'root' =>
27
- array (
28
- 'pretty_version' => 'dev-master',
29
- 'version' => 'dev-master',
30
- 'aliases' =>
31
- array (
32
- ),
33
- 'reference' => '4b6a2ad764ef5fdaa8cb40081a3f00f437163d9e',
34
- 'name' => '__root__',
35
- ),
36
- 'versions' =>
37
- array (
38
- '__root__' =>
39
- array (
40
- 'pretty_version' => 'dev-master',
41
- 'version' => 'dev-master',
42
- 'aliases' =>
43
- array (
44
- ),
45
- 'reference' => '4b6a2ad764ef5fdaa8cb40081a3f00f437163d9e',
46
- ),
47
- ),
48
- );
49
- private static $canGetVendors;
50
- private static $installedByVendor = array();
51
-
52
-
53
-
54
-
55
-
56
-
57
-
58
- public static function getInstalledPackages()
59
- {
60
- $packages = array();
61
- foreach (self::getInstalled() as $installed) {
62
- $packages[] = array_keys($installed['versions']);
63
- }
64
-
65
-
66
- if (1 === \count($packages)) {
67
- return $packages[0];
68
- }
69
-
70
- return array_keys(array_flip(\call_user_func_array('array_merge', $packages)));
71
- }
72
-
73
-
74
-
75
-
76
-
77
-
78
-
79
-
80
-
81
- public static function isInstalled($packageName)
82
- {
83
- foreach (self::getInstalled() as $installed) {
84
- if (isset($installed['versions'][$packageName])) {
85
- return true;
86
- }
87
- }
88
-
89
- return false;
90
- }
91
-
92
-
93
-
94
-
95
-
96
-
97
-
98
-
99
-
100
-
101
-
102
-
103
-
104
-
105
- public static function satisfies(VersionParser $parser, $packageName, $constraint)
106
- {
107
- $constraint = $parser->parseConstraints($constraint);
108
- $provided = $parser->parseConstraints(self::getVersionRanges($packageName));
109
-
110
- return $provided->matches($constraint);
111
- }
112
-
113
-
114
-
115
-
116
-
117
-
118
-
119
-
120
-
121
-
122
- public static function getVersionRanges($packageName)
123
- {
124
- foreach (self::getInstalled() as $installed) {
125
- if (!isset($installed['versions'][$packageName])) {
126
- continue;
127
- }
128
-
129
- $ranges = array();
130
- if (isset($installed['versions'][$packageName]['pretty_version'])) {
131
- $ranges[] = $installed['versions'][$packageName]['pretty_version'];
132
- }
133
- if (array_key_exists('aliases', $installed['versions'][$packageName])) {
134
- $ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']);
135
- }
136
- if (array_key_exists('replaced', $installed['versions'][$packageName])) {
137
- $ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']);
138
- }
139
- if (array_key_exists('provided', $installed['versions'][$packageName])) {
140
- $ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']);
141
- }
142
-
143
- return implode(' || ', $ranges);
144
- }
145
-
146
- throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
147
- }
148
-
149
-
150
-
151
-
152
-
153
- public static function getVersion($packageName)
154
- {
155
- foreach (self::getInstalled() as $installed) {
156
- if (!isset($installed['versions'][$packageName])) {
157
- continue;
158
- }
159
-
160
- if (!isset($installed['versions'][$packageName]['version'])) {
161
- return null;
162
- }
163
-
164
- return $installed['versions'][$packageName]['version'];
165
- }
166
-
167
- throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
168
- }
169
-
170
-
171
-
172
-
173
-
174
- public static function getPrettyVersion($packageName)
175
- {
176
- foreach (self::getInstalled() as $installed) {
177
- if (!isset($installed['versions'][$packageName])) {
178
- continue;
179
- }
180
-
181
- if (!isset($installed['versions'][$packageName]['pretty_version'])) {
182
- return null;
183
- }
184
-
185
- return $installed['versions'][$packageName]['pretty_version'];
186
- }
187
-
188
- throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
189
- }
190
-
191
-
192
-
193
-
194
-
195
- public static function getReference($packageName)
196
- {
197
- foreach (self::getInstalled() as $installed) {
198
- if (!isset($installed['versions'][$packageName])) {
199
- continue;
200
- }
201
-
202
- if (!isset($installed['versions'][$packageName]['reference'])) {
203
- return null;
204
- }
205
-
206
- return $installed['versions'][$packageName]['reference'];
207
- }
208
-
209
- throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
210
- }
211
-
212
-
213
-
214
-
215
-
216
- public static function getRootPackage()
217
- {
218
- $installed = self::getInstalled();
219
-
220
- return $installed[0]['root'];
221
- }
222
-
223
-
224
-
225
-
226
-
227
-
228
-
229
- public static function getRawData()
230
- {
231
- return self::$installed;
232
- }
233
-
234
-
235
-
236
-
237
-
238
-
239
-
240
-
241
-
242
-
243
-
244
-
245
-
246
-
247
-
248
-
249
-
250
-
251
-
252
- public static function reload($data)
253
- {
254
- self::$installed = $data;
255
- self::$installedByVendor = array();
256
- }
257
-
258
-
259
-
260
-
261
- private static function getInstalled()
262
- {
263
- if (null === self::$canGetVendors) {
264
- self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders');
265
- }
266
-
267
- $installed = array();
268
-
269
- if (self::$canGetVendors) {
270
- foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
271
- if (isset(self::$installedByVendor[$vendorDir])) {
272
- $installed[] = self::$installedByVendor[$vendorDir];
273
- } elseif (is_file($vendorDir.'/composer/installed.php')) {
274
- $installed[] = self::$installedByVendor[$vendorDir] = require $vendorDir.'/composer/installed.php';
275
- }
276
- }
277
- }
278
-
279
- $installed[] = self::$installed;
280
-
281
- return $installed;
282
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
283
  }
1
  <?php
2
 
3
+ /*
4
+ * This file is part of Composer.
5
+ *
6
+ * (c) Nils Adermann <naderman@naderman.de>
7
+ * Jordi Boggiano <j.boggiano@seld.be>
8
+ *
9
+ * For the full copyright and license information, please view the LICENSE
10
+ * file that was distributed with this source code.
11
+ */
12
 
13
  namespace Composer;
14
 
15
  use Composer\Autoload\ClassLoader;
16
  use Composer\Semver\VersionParser;
17
 
18
+ /**
19
+ * This class is copied in every Composer installed project and available to all
20
+ *
21
+ * See also https://getcomposer.org/doc/07-runtime.md#installed-versions
22
+ *
23
+ * To require its presence, you can require `composer-runtime-api ^2.0`
24
+ */
25
  class InstalledVersions
26
  {
27
+ /**
28
+ * @var mixed[]|null
29
+ * @psalm-var array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}|array{}|null
30
+ */
31
+ private static $installed;
32
+
33
+ /**
34
+ * @var bool|null
35
+ */
36
+ private static $canGetVendors;
37
+
38
+ /**
39
+ * @var array[]
40
+ * @psalm-var array<string, array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}>
41
+ */
42
+ private static $installedByVendor = array();
43
+
44
+ /**
45
+ * Returns a list of all package names which are present, either by being installed, replaced or provided
46
+ *
47
+ * @return string[]
48
+ * @psalm-return list<string>
49
+ */
50
+ public static function getInstalledPackages()
51
+ {
52
+ $packages = array();
53
+ foreach (self::getInstalled() as $installed) {
54
+ $packages[] = array_keys($installed['versions']);
55
+ }
56
+
57
+ if (1 === \count($packages)) {
58
+ return $packages[0];
59
+ }
60
+
61
+ return array_keys(array_flip(\call_user_func_array('array_merge', $packages)));
62
+ }
63
+
64
+ /**
65
+ * Returns a list of all package names with a specific type e.g. 'library'
66
+ *
67
+ * @param string $type
68
+ * @return string[]
69
+ * @psalm-return list<string>
70
+ */
71
+ public static function getInstalledPackagesByType($type)
72
+ {
73
+ $packagesByType = array();
74
+
75
+ foreach (self::getInstalled() as $installed) {
76
+ foreach ($installed['versions'] as $name => $package) {
77
+ if (isset($package['type']) && $package['type'] === $type) {
78
+ $packagesByType[] = $name;
79
+ }
80
+ }
81
+ }
82
+
83
+ return $packagesByType;
84
+ }
85
+
86
+ /**
87
+ * Checks whether the given package is installed
88
+ *
89
+ * This also returns true if the package name is provided or replaced by another package
90
+ *
91
+ * @param string $packageName
92
+ * @param bool $includeDevRequirements
93
+ * @return bool
94
+ */
95
+ public static function isInstalled($packageName, $includeDevRequirements = true)
96
+ {
97
+ foreach (self::getInstalled() as $installed) {
98
+ if (isset($installed['versions'][$packageName])) {
99
+ return $includeDevRequirements || empty($installed['versions'][$packageName]['dev_requirement']);
100
+ }
101
+ }
102
+
103
+ return false;
104
+ }
105
+
106
+ /**
107
+ * Checks whether the given package satisfies a version constraint
108
+ *
109
+ * e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call:
110
+ *
111
+ * Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3')
112
+ *
113
+ * @param VersionParser $parser Install composer/semver to have access to this class and functionality
114
+ * @param string $packageName
115
+ * @param string|null $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package
116
+ * @return bool
117
+ */
118
+ public static function satisfies(VersionParser $parser, $packageName, $constraint)
119
+ {
120
+ $constraint = $parser->parseConstraints($constraint);
121
+ $provided = $parser->parseConstraints(self::getVersionRanges($packageName));
122
+
123
+ return $provided->matches($constraint);
124
+ }
125
+
126
+ /**
127
+ * Returns a version constraint representing all the range(s) which are installed for a given package
128
+ *
129
+ * It is easier to use this via isInstalled() with the $constraint argument if you need to check
130
+ * whether a given version of a package is installed, and not just whether it exists
131
+ *
132
+ * @param string $packageName
133
+ * @return string Version constraint usable with composer/semver
134
+ */
135
+ public static function getVersionRanges($packageName)
136
+ {
137
+ foreach (self::getInstalled() as $installed) {
138
+ if (!isset($installed['versions'][$packageName])) {
139
+ continue;
140
+ }
141
+
142
+ $ranges = array();
143
+ if (isset($installed['versions'][$packageName]['pretty_version'])) {
144
+ $ranges[] = $installed['versions'][$packageName]['pretty_version'];
145
+ }
146
+ if (array_key_exists('aliases', $installed['versions'][$packageName])) {
147
+ $ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']);
148
+ }
149
+ if (array_key_exists('replaced', $installed['versions'][$packageName])) {
150
+ $ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']);
151
+ }
152
+ if (array_key_exists('provided', $installed['versions'][$packageName])) {
153
+ $ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']);
154
+ }
155
+
156
+ return implode(' || ', $ranges);
157
+ }
158
+
159
+ throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
160
+ }
161
+
162
+ /**
163
+ * @param string $packageName
164
+ * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
165
+ */
166
+ public static function getVersion($packageName)
167
+ {
168
+ foreach (self::getInstalled() as $installed) {
169
+ if (!isset($installed['versions'][$packageName])) {
170
+ continue;
171
+ }
172
+
173
+ if (!isset($installed['versions'][$packageName]['version'])) {
174
+ return null;
175
+ }
176
+
177
+ return $installed['versions'][$packageName]['version'];
178
+ }
179
+
180
+ throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
181
+ }
182
+
183
+ /**
184
+ * @param string $packageName
185
+ * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
186
+ */
187
+ public static function getPrettyVersion($packageName)
188
+ {
189
+ foreach (self::getInstalled() as $installed) {
190
+ if (!isset($installed['versions'][$packageName])) {
191
+ continue;
192
+ }
193
+
194
+ if (!isset($installed['versions'][$packageName]['pretty_version'])) {
195
+ return null;
196
+ }
197
+
198
+ return $installed['versions'][$packageName]['pretty_version'];
199
+ }
200
+
201
+ throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
202
+ }
203
+
204
+ /**
205
+ * @param string $packageName
206
+ * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference
207
+ */
208
+ public static function getReference($packageName)
209
+ {
210
+ foreach (self::getInstalled() as $installed) {
211
+ if (!isset($installed['versions'][$packageName])) {
212
+ continue;
213
+ }
214
+
215
+ if (!isset($installed['versions'][$packageName]['reference'])) {
216
+ return null;
217
+ }
218
+
219
+ return $installed['versions'][$packageName]['reference'];
220
+ }
221
+
222
+ throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
223
+ }
224
+
225
+ /**
226
+ * @param string $packageName
227
+ * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path.
228
+ */
229
+ public static function getInstallPath($packageName)
230
+ {
231
+ foreach (self::getInstalled() as $installed) {
232
+ if (!isset($installed['versions'][$packageName])) {
233
+ continue;
234
+ }
235
+
236
+ return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null;
237
+ }
238
+
239
+ throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
240
+ }
241
+
242
+ /**
243
+ * @return array
244
+ * @psalm-return array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}
245
+ */
246
+ public static function getRootPackage()
247
+ {
248
+ $installed = self::getInstalled();
249
+
250
+ return $installed[0]['root'];
251
+ }
252
+
253
+ /**
254
+ * Returns the raw installed.php data for custom implementations
255
+ *
256
+ * @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect.
257
+ * @return array[]
258
+ * @psalm-return array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}
259
+ */
260
+ public static function getRawData()
261
+ {
262
+ @trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED);
263
+
264
+ if (null === self::$installed) {
265
+ // only require the installed.php file if this file is loaded from its dumped location,
266
+ // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
267
+ if (substr(__DIR__, -8, 1) !== 'C') {
268
+ self::$installed = include __DIR__ . '/installed.php';
269
+ } else {
270
+ self::$installed = array();
271
+ }
272
+ }
273
+
274
+ return self::$installed;
275
+ }
276
+
277
+ /**
278
+ * Returns the raw data of all installed.php which are currently loaded for custom implementations
279
+ *
280
+ * @return array[]
281
+ * @psalm-return list<array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}>
282
+ */
283
+ public static function getAllRawData()
284
+ {
285
+ return self::getInstalled();
286
+ }
287
+
288
+ /**
289
+ * Lets you reload the static array from another file
290
+ *
291
+ * This is only useful for complex integrations in which a project needs to use
292
+ * this class but then also needs to execute another project's autoloader in process,
293
+ * and wants to ensure both projects have access to their version of installed.php.
294
+ *
295
+ * A typical case would be PHPUnit, where it would need to make sure it reads all
296
+ * the data it needs from this class, then call reload() with
297
+ * `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure
298
+ * the project in which it runs can then also use this class safely, without
299
+ * interference between PHPUnit's dependencies and the project's dependencies.
300
+ *
301
+ * @param array[] $data A vendor/composer/installed.php data set
302
+ * @return void
303
+ *
304
+ * @psalm-param array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>} $data
305
+ */
306
+ public static function reload($data)
307
+ {
308
+ self::$installed = $data;
309
+ self::$installedByVendor = array();
310
+ }
311
+
312
+ /**
313
+ * @return array[]
314
+ * @psalm-return list<array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}>
315
+ */
316
+ private static function getInstalled()
317
+ {
318
+ if (null === self::$canGetVendors) {
319
+ self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders');
320
+ }
321
+
322
+ $installed = array();
323
+
324
+ if (self::$canGetVendors) {
325
+ foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
326
+ if (isset(self::$installedByVendor[$vendorDir])) {
327
+ $installed[] = self::$installedByVendor[$vendorDir];
328
+ } elseif (is_file($vendorDir.'/composer/installed.php')) {
329
+ $installed[] = self::$installedByVendor[$vendorDir] = require $vendorDir.'/composer/installed.php';
330
+ if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) {
331
+ self::$installed = $installed[count($installed) - 1];
332
+ }
333
+ }
334
+ }
335
+ }
336
+
337
+ if (null === self::$installed) {
338
+ // only require the installed.php file if this file is loaded from its dumped location,
339
+ // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
340
+ if (substr(__DIR__, -8, 1) !== 'C') {
341
+ self::$installed = require __DIR__ . '/installed.php';
342
+ } else {
343
+ self::$installed = array();
344
+ }
345
+ }
346
+ $installed[] = self::$installed;
347
+
348
+ return $installed;
349
+ }
350
  }
vendor/composer/autoload_classmap.php CHANGED
@@ -7,36 +7,139 @@ $baseDir = dirname($vendorDir);
7
 
8
  return array(
9
  'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
10
- 'Yoast\\WP\\Duplicate_Post\\Admin\\Options' => $baseDir . '/src/admin/class-options.php',
11
- 'Yoast\\WP\\Duplicate_Post\\Admin\\Options_Form_Generator' => $baseDir . '/src/admin/class-options-form-generator.php',
12
- 'Yoast\\WP\\Duplicate_Post\\Admin\\Options_Inputs' => $baseDir . '/src/admin/class-options-inputs.php',
13
- 'Yoast\\WP\\Duplicate_Post\\Admin\\Options_Page' => $baseDir . '/src/admin/class-options-page.php',
14
- 'Yoast\\WP\\Duplicate_Post\\Duplicate_Post' => $baseDir . '/src/class-duplicate-post.php',
15
- 'Yoast\\WP\\Duplicate_Post\\Handlers\\Bulk_Handler' => $baseDir . '/src/handlers/class-bulk-handler.php',
16
- 'Yoast\\WP\\Duplicate_Post\\Handlers\\Check_Changes_Handler' => $baseDir . '/src/handlers/class-check-changes-handler.php',
17
- 'Yoast\\WP\\Duplicate_Post\\Handlers\\Handler' => $baseDir . '/src/handlers/class-handler.php',
18
- 'Yoast\\WP\\Duplicate_Post\\Handlers\\Link_Handler' => $baseDir . '/src/handlers/class-link-handler.php',
19
- 'Yoast\\WP\\Duplicate_Post\\Handlers\\Save_Post_Handler' => $baseDir . '/src/handlers/class-save-post-handler.php',
20
- 'Yoast\\WP\\Duplicate_Post\\Permissions_Helper' => $baseDir . '/src/class-permissions-helper.php',
21
- 'Yoast\\WP\\Duplicate_Post\\Post_Duplicator' => $baseDir . '/src/class-post-duplicator.php',
22
- 'Yoast\\WP\\Duplicate_Post\\Post_Republisher' => $baseDir . '/src/class-post-republisher.php',
23
- 'Yoast\\WP\\Duplicate_Post\\Revisions_Migrator' => $baseDir . '/src/class-revisions-migrator.php',
24
- 'Yoast\\WP\\Duplicate_Post\\UI\\Admin_Bar' => $baseDir . '/src/ui/class-admin-bar.php',
25
- 'Yoast\\WP\\Duplicate_Post\\UI\\Asset_Manager' => $baseDir . '/src/ui/class-asset-manager.php',
26
- 'Yoast\\WP\\Duplicate_Post\\UI\\Block_Editor' => $baseDir . '/src/ui/class-block-editor.php',
27
- 'Yoast\\WP\\Duplicate_Post\\UI\\Bulk_Actions' => $baseDir . '/src/ui/class-bulk-actions.php',
28
- 'Yoast\\WP\\Duplicate_Post\\UI\\Classic_Editor' => $baseDir . '/src/ui/class-classic-editor.php',
29
- 'Yoast\\WP\\Duplicate_Post\\UI\\Column' => $baseDir . '/src/ui/class-column.php',
30
- 'Yoast\\WP\\Duplicate_Post\\UI\\Link_Builder' => $baseDir . '/src/ui/class-link-builder.php',
31
- 'Yoast\\WP\\Duplicate_Post\\UI\\Metabox' => $baseDir . '/src/ui/class-metabox.php',
32
- 'Yoast\\WP\\Duplicate_Post\\UI\\Post_States' => $baseDir . '/src/ui/class-post-states.php',
33
- 'Yoast\\WP\\Duplicate_Post\\UI\\Row_Actions' => $baseDir . '/src/ui/class-row-actions.php',
34
- 'Yoast\\WP\\Duplicate_Post\\UI\\User_Interface' => $baseDir . '/src/ui/class-user-interface.php',
35
- 'Yoast\\WP\\Duplicate_Post\\Utils' => $baseDir . '/src/class-utils.php',
36
- 'Yoast\\WP\\Duplicate_Post\\Watchers\\Bulk_Actions_Watcher' => $baseDir . '/src/watchers/class-bulk-actions-watcher.php',
37
- 'Yoast\\WP\\Duplicate_Post\\Watchers\\Copied_Post_Watcher' => $baseDir . '/src/watchers/class-copied-post-watcher.php',
38
- 'Yoast\\WP\\Duplicate_Post\\Watchers\\Link_Actions_Watcher' => $baseDir . '/src/watchers/class-link-actions-watcher.php',
39
- 'Yoast\\WP\\Duplicate_Post\\Watchers\\Original_Post_Watcher' => $baseDir . '/src/watchers/class-original-post-watcher.php',
40
- 'Yoast\\WP\\Duplicate_Post\\Watchers\\Republished_Post_Watcher' => $baseDir . '/src/watchers/class-republished-post-watcher.php',
41
- 'Yoast\\WP\\Duplicate_Post\\Watchers\\Watchers' => $baseDir . '/src/watchers/class-watchers.php',
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
  );
7
 
8
  return array(
9
  'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
10
+ 'Composer\\Installers\\AglInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/AglInstaller.php',
11
+ 'Composer\\Installers\\AimeosInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/AimeosInstaller.php',
12
+ 'Composer\\Installers\\AnnotateCmsInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/AnnotateCmsInstaller.php',
13
+ 'Composer\\Installers\\AsgardInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/AsgardInstaller.php',
14
+ 'Composer\\Installers\\AttogramInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/AttogramInstaller.php',
15
+ 'Composer\\Installers\\BaseInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/BaseInstaller.php',
16
+ 'Composer\\Installers\\BitrixInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/BitrixInstaller.php',
17
+ 'Composer\\Installers\\BonefishInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/BonefishInstaller.php',
18
+ 'Composer\\Installers\\CakePHPInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/CakePHPInstaller.php',
19
+ 'Composer\\Installers\\ChefInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/ChefInstaller.php',
20
+ 'Composer\\Installers\\CiviCrmInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/CiviCrmInstaller.php',
21
+ 'Composer\\Installers\\ClanCatsFrameworkInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/ClanCatsFrameworkInstaller.php',
22
+ 'Composer\\Installers\\CockpitInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/CockpitInstaller.php',
23
+ 'Composer\\Installers\\CodeIgniterInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/CodeIgniterInstaller.php',
24
+ 'Composer\\Installers\\Concrete5Installer' => $vendorDir . '/composer/installers/src/Composer/Installers/Concrete5Installer.php',
25
+ 'Composer\\Installers\\CraftInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/CraftInstaller.php',
26
+ 'Composer\\Installers\\CroogoInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/CroogoInstaller.php',
27
+ 'Composer\\Installers\\DecibelInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/DecibelInstaller.php',
28
+ 'Composer\\Installers\\DframeInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/DframeInstaller.php',
29
+ 'Composer\\Installers\\DokuWikiInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/DokuWikiInstaller.php',
30
+ 'Composer\\Installers\\DolibarrInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/DolibarrInstaller.php',
31
+ 'Composer\\Installers\\DrupalInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/DrupalInstaller.php',
32
+ 'Composer\\Installers\\ElggInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/ElggInstaller.php',
33
+ 'Composer\\Installers\\EliasisInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/EliasisInstaller.php',
34
+ 'Composer\\Installers\\ExpressionEngineInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/ExpressionEngineInstaller.php',
35
+ 'Composer\\Installers\\EzPlatformInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/EzPlatformInstaller.php',
36
+ 'Composer\\Installers\\FuelInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/FuelInstaller.php',
37
+ 'Composer\\Installers\\FuelphpInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/FuelphpInstaller.php',
38
+ 'Composer\\Installers\\GravInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/GravInstaller.php',
39
+ 'Composer\\Installers\\HuradInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/HuradInstaller.php',
40
+ 'Composer\\Installers\\ImageCMSInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/ImageCMSInstaller.php',
41
+ 'Composer\\Installers\\Installer' => $vendorDir . '/composer/installers/src/Composer/Installers/Installer.php',
42
+ 'Composer\\Installers\\ItopInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/ItopInstaller.php',
43
+ 'Composer\\Installers\\JoomlaInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/JoomlaInstaller.php',
44
+ 'Composer\\Installers\\KanboardInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/KanboardInstaller.php',
45
+ 'Composer\\Installers\\KirbyInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/KirbyInstaller.php',
46
+ 'Composer\\Installers\\KnownInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/KnownInstaller.php',
47
+ 'Composer\\Installers\\KodiCMSInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/KodiCMSInstaller.php',
48
+ 'Composer\\Installers\\KohanaInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/KohanaInstaller.php',
49
+ 'Composer\\Installers\\LanManagementSystemInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/LanManagementSystemInstaller.php',
50
+ 'Composer\\Installers\\LaravelInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/LaravelInstaller.php',
51
+ 'Composer\\Installers\\LavaLiteInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/LavaLiteInstaller.php',
52
+ 'Composer\\Installers\\LithiumInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/LithiumInstaller.php',
53
+ 'Composer\\Installers\\MODULEWorkInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/MODULEWorkInstaller.php',
54
+ 'Composer\\Installers\\MODXEvoInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/MODXEvoInstaller.php',
55
+ 'Composer\\Installers\\MagentoInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/MagentoInstaller.php',
56
+ 'Composer\\Installers\\MajimaInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/MajimaInstaller.php',
57
+ 'Composer\\Installers\\MakoInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/MakoInstaller.php',
58
+ 'Composer\\Installers\\MantisBTInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/MantisBTInstaller.php',
59
+ 'Composer\\Installers\\MauticInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/MauticInstaller.php',
60
+ 'Composer\\Installers\\MayaInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/MayaInstaller.php',
61
+ 'Composer\\Installers\\MediaWikiInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/MediaWikiInstaller.php',
62
+ 'Composer\\Installers\\MiaoxingInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/MiaoxingInstaller.php',
63
+ 'Composer\\Installers\\MicroweberInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/MicroweberInstaller.php',
64
+ 'Composer\\Installers\\ModxInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/ModxInstaller.php',
65
+ 'Composer\\Installers\\MoodleInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/MoodleInstaller.php',
66
+ 'Composer\\Installers\\OctoberInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/OctoberInstaller.php',
67
+ 'Composer\\Installers\\OntoWikiInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/OntoWikiInstaller.php',
68
+ 'Composer\\Installers\\OsclassInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/OsclassInstaller.php',
69
+ 'Composer\\Installers\\OxidInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/OxidInstaller.php',
70
+ 'Composer\\Installers\\PPIInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/PPIInstaller.php',
71
+ 'Composer\\Installers\\PantheonInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/PantheonInstaller.php',
72
+ 'Composer\\Installers\\PhiftyInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/PhiftyInstaller.php',
73
+ 'Composer\\Installers\\PhpBBInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/PhpBBInstaller.php',
74
+ 'Composer\\Installers\\PimcoreInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/PimcoreInstaller.php',
75
+ 'Composer\\Installers\\PiwikInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/PiwikInstaller.php',
76
+ 'Composer\\Installers\\PlentymarketsInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/PlentymarketsInstaller.php',
77
+ 'Composer\\Installers\\Plugin' => $vendorDir . '/composer/installers/src/Composer/Installers/Plugin.php',
78
+ 'Composer\\Installers\\PortoInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/PortoInstaller.php',
79
+ 'Composer\\Installers\\PrestashopInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/PrestashopInstaller.php',
80
+ 'Composer\\Installers\\ProcessWireInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/ProcessWireInstaller.php',
81
+ 'Composer\\Installers\\PuppetInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/PuppetInstaller.php',
82
+ 'Composer\\Installers\\PxcmsInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/PxcmsInstaller.php',
83
+ 'Composer\\Installers\\RadPHPInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/RadPHPInstaller.php',
84
+ 'Composer\\Installers\\ReIndexInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/ReIndexInstaller.php',
85
+ 'Composer\\Installers\\Redaxo5Installer' => $vendorDir . '/composer/installers/src/Composer/Installers/Redaxo5Installer.php',
86
+ 'Composer\\Installers\\RedaxoInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/RedaxoInstaller.php',
87
+ 'Composer\\Installers\\RoundcubeInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/RoundcubeInstaller.php',
88
+ 'Composer\\Installers\\SMFInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/SMFInstaller.php',
89
+ 'Composer\\Installers\\ShopwareInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/ShopwareInstaller.php',
90
+ 'Composer\\Installers\\SilverStripeInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/SilverStripeInstaller.php',
91
+ 'Composer\\Installers\\SiteDirectInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/SiteDirectInstaller.php',
92
+ 'Composer\\Installers\\StarbugInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/StarbugInstaller.php',
93
+ 'Composer\\Installers\\SyDESInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/SyDESInstaller.php',
94
+ 'Composer\\Installers\\SyliusInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/SyliusInstaller.php',
95
+ 'Composer\\Installers\\Symfony1Installer' => $vendorDir . '/composer/installers/src/Composer/Installers/Symfony1Installer.php',
96
+ 'Composer\\Installers\\TYPO3CmsInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/TYPO3CmsInstaller.php',
97
+ 'Composer\\Installers\\TYPO3FlowInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/TYPO3FlowInstaller.php',
98
+ 'Composer\\Installers\\TaoInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/TaoInstaller.php',
99
+ 'Composer\\Installers\\TastyIgniterInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/TastyIgniterInstaller.php',
100
+ 'Composer\\Installers\\TheliaInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/TheliaInstaller.php',
101
+ 'Composer\\Installers\\TuskInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/TuskInstaller.php',
102
+ 'Composer\\Installers\\UserFrostingInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/UserFrostingInstaller.php',
103
+ 'Composer\\Installers\\VanillaInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/VanillaInstaller.php',
104
+ 'Composer\\Installers\\VgmcpInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/VgmcpInstaller.php',
105
+ 'Composer\\Installers\\WHMCSInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/WHMCSInstaller.php',
106
+ 'Composer\\Installers\\WinterInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/WinterInstaller.php',
107
+ 'Composer\\Installers\\WolfCMSInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/WolfCMSInstaller.php',
108
+ 'Composer\\Installers\\WordPressInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/WordPressInstaller.php',
109
+ 'Composer\\Installers\\YawikInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/YawikInstaller.php',
110
+ 'Composer\\Installers\\ZendInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/ZendInstaller.php',
111
+ 'Composer\\Installers\\ZikulaInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/ZikulaInstaller.php',
112
+ 'Yoast\\WP\\Duplicate_Post\\Admin\\Options' => $baseDir . '/src/admin/options.php',
113
+ 'Yoast\\WP\\Duplicate_Post\\Admin\\Options_Form_Generator' => $baseDir . '/src/admin/options-form-generator.php',
114
+ 'Yoast\\WP\\Duplicate_Post\\Admin\\Options_Inputs' => $baseDir . '/src/admin/options-inputs.php',
115
+ 'Yoast\\WP\\Duplicate_Post\\Admin\\Options_Page' => $baseDir . '/src/admin/options-page.php',
116
+ 'Yoast\\WP\\Duplicate_Post\\Duplicate_Post' => $baseDir . '/src/duplicate-post.php',
117
+ 'Yoast\\WP\\Duplicate_Post\\Handlers\\Bulk_Handler' => $baseDir . '/src/handlers/bulk-handler.php',
118
+ 'Yoast\\WP\\Duplicate_Post\\Handlers\\Check_Changes_Handler' => $baseDir . '/src/handlers/check-changes-handler.php',
119
+ 'Yoast\\WP\\Duplicate_Post\\Handlers\\Handler' => $baseDir . '/src/handlers/handler.php',
120
+ 'Yoast\\WP\\Duplicate_Post\\Handlers\\Link_Handler' => $baseDir . '/src/handlers/link-handler.php',
121
+ 'Yoast\\WP\\Duplicate_Post\\Handlers\\Save_Post_Handler' => $baseDir . '/src/handlers/save-post-handler.php',
122
+ 'Yoast\\WP\\Duplicate_Post\\Permissions_Helper' => $baseDir . '/src/permissions-helper.php',
123
+ 'Yoast\\WP\\Duplicate_Post\\Post_Duplicator' => $baseDir . '/src/post-duplicator.php',
124
+ 'Yoast\\WP\\Duplicate_Post\\Post_Republisher' => $baseDir . '/src/post-republisher.php',
125
+ 'Yoast\\WP\\Duplicate_Post\\Revisions_Migrator' => $baseDir . '/src/revisions-migrator.php',
126
+ 'Yoast\\WP\\Duplicate_Post\\UI\\Admin_Bar' => $baseDir . '/src/ui/admin-bar.php',
127
+ 'Yoast\\WP\\Duplicate_Post\\UI\\Asset_Manager' => $baseDir . '/src/ui/asset-manager.php',
128
+ 'Yoast\\WP\\Duplicate_Post\\UI\\Block_Editor' => $baseDir . '/src/ui/block-editor.php',
129
+ 'Yoast\\WP\\Duplicate_Post\\UI\\Bulk_Actions' => $baseDir . '/src/ui/bulk-actions.php',
130
+ 'Yoast\\WP\\Duplicate_Post\\UI\\Classic_Editor' => $baseDir . '/src/ui/classic-editor.php',
131
+ 'Yoast\\WP\\Duplicate_Post\\UI\\Column' => $baseDir . '/src/ui/column.php',
132
+ 'Yoast\\WP\\Duplicate_Post\\UI\\Link_Builder' => $baseDir . '/src/ui/link-builder.php',
133
+ 'Yoast\\WP\\Duplicate_Post\\UI\\Metabox' => $baseDir . '/src/ui/metabox.php',
134
+ 'Yoast\\WP\\Duplicate_Post\\UI\\Newsletter' => $baseDir . '/src/ui/newsletter.php',
135
+ 'Yoast\\WP\\Duplicate_Post\\UI\\Post_States' => $baseDir . '/src/ui/post-states.php',
136
+ 'Yoast\\WP\\Duplicate_Post\\UI\\Row_Actions' => $baseDir . '/src/ui/row-actions.php',
137
+ 'Yoast\\WP\\Duplicate_Post\\UI\\User_Interface' => $baseDir . '/src/ui/user-interface.php',
138
+ 'Yoast\\WP\\Duplicate_Post\\Utils' => $baseDir . '/src/utils.php',
139
+ 'Yoast\\WP\\Duplicate_Post\\Watchers\\Bulk_Actions_Watcher' => $baseDir . '/src/watchers/bulk-actions-watcher.php',
140
+ 'Yoast\\WP\\Duplicate_Post\\Watchers\\Copied_Post_Watcher' => $baseDir . '/src/watchers/copied-post-watcher.php',
141
+ 'Yoast\\WP\\Duplicate_Post\\Watchers\\Link_Actions_Watcher' => $baseDir . '/src/watchers/link-actions-watcher.php',
142
+ 'Yoast\\WP\\Duplicate_Post\\Watchers\\Original_Post_Watcher' => $baseDir . '/src/watchers/original-post-watcher.php',
143
+ 'Yoast\\WP\\Duplicate_Post\\Watchers\\Republished_Post_Watcher' => $baseDir . '/src/watchers/republished-post-watcher.php',
144
+ 'Yoast\\WP\\Duplicate_Post\\Watchers\\Watchers' => $baseDir . '/src/watchers/watchers.php',
145
  );
vendor/composer/autoload_psr4.php CHANGED
@@ -6,4 +6,5 @@ $vendorDir = dirname(dirname(__FILE__));
6
  $baseDir = dirname($vendorDir);
7
 
8
  return array(
 
9
  );
6
  $baseDir = dirname($vendorDir);
7
 
8
  return array(
9
+ 'Composer\\Installers\\' => array($vendorDir . '/composer/installers/src/Composer/Installers'),
10
  );
vendor/composer/autoload_real.php CHANGED
@@ -22,6 +22,8 @@ class ComposerAutoloaderInitb771dd36b1819b8209dfac03b31cc9ed
22
  return self::$loader;
23
  }
24
 
 
 
25
  spl_autoload_register(array('ComposerAutoloaderInitb771dd36b1819b8209dfac03b31cc9ed', 'loadClassLoader'), true, true);
26
  self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(\dirname(__FILE__)));
27
  spl_autoload_unregister(array('ComposerAutoloaderInitb771dd36b1819b8209dfac03b31cc9ed', 'loadClassLoader'));
22
  return self::$loader;
23
  }
24
 
25
+ require __DIR__ . '/platform_check.php';
26
+
27
  spl_autoload_register(array('ComposerAutoloaderInitb771dd36b1819b8209dfac03b31cc9ed', 'loadClassLoader'), true, true);
28
  self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(\dirname(__FILE__)));
29
  spl_autoload_unregister(array('ComposerAutoloaderInitb771dd36b1819b8209dfac03b31cc9ed', 'loadClassLoader'));
vendor/composer/autoload_static.php CHANGED
@@ -6,45 +6,164 @@ namespace Composer\Autoload;
6
 
7
  class ComposerStaticInitb771dd36b1819b8209dfac03b31cc9ed
8
  {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
  public static $classMap = array (
10
  'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
11
- 'Yoast\\WP\\Duplicate_Post\\Admin\\Options' => __DIR__ . '/../..' . '/src/admin/class-options.php',
12
- 'Yoast\\WP\\Duplicate_Post\\Admin\\Options_Form_Generator' => __DIR__ . '/../..' . '/src/admin/class-options-form-generator.php',
13
- 'Yoast\\WP\\Duplicate_Post\\Admin\\Options_Inputs' => __DIR__ . '/../..' . '/src/admin/class-options-inputs.php',
14
- 'Yoast\\WP\\Duplicate_Post\\Admin\\Options_Page' => __DIR__ . '/../..' . '/src/admin/class-options-page.php',
15
- 'Yoast\\WP\\Duplicate_Post\\Duplicate_Post' => __DIR__ . '/../..' . '/src/class-duplicate-post.php',
16
- 'Yoast\\WP\\Duplicate_Post\\Handlers\\Bulk_Handler' => __DIR__ . '/../..' . '/src/handlers/class-bulk-handler.php',
17
- 'Yoast\\WP\\Duplicate_Post\\Handlers\\Check_Changes_Handler' => __DIR__ . '/../..' . '/src/handlers/class-check-changes-handler.php',
18
- 'Yoast\\WP\\Duplicate_Post\\Handlers\\Handler' => __DIR__ . '/../..' . '/src/handlers/class-handler.php',
19
- 'Yoast\\WP\\Duplicate_Post\\Handlers\\Link_Handler' => __DIR__ . '/../..' . '/src/handlers/class-link-handler.php',
20
- 'Yoast\\WP\\Duplicate_Post\\Handlers\\Save_Post_Handler' => __DIR__ . '/../..' . '/src/handlers/class-save-post-handler.php',
21
- 'Yoast\\WP\\Duplicate_Post\\Permissions_Helper' => __DIR__ . '/../..' . '/src/class-permissions-helper.php',
22
- 'Yoast\\WP\\Duplicate_Post\\Post_Duplicator' => __DIR__ . '/../..' . '/src/class-post-duplicator.php',
23
- 'Yoast\\WP\\Duplicate_Post\\Post_Republisher' => __DIR__ . '/../..' . '/src/class-post-republisher.php',
24
- 'Yoast\\WP\\Duplicate_Post\\Revisions_Migrator' => __DIR__ . '/../..' . '/src/class-revisions-migrator.php',
25
- 'Yoast\\WP\\Duplicate_Post\\UI\\Admin_Bar' => __DIR__ . '/../..' . '/src/ui/class-admin-bar.php',
26
- 'Yoast\\WP\\Duplicate_Post\\UI\\Asset_Manager' => __DIR__ . '/../..' . '/src/ui/class-asset-manager.php',
27
- 'Yoast\\WP\\Duplicate_Post\\UI\\Block_Editor' => __DIR__ . '/../..' . '/src/ui/class-block-editor.php',
28
- 'Yoast\\WP\\Duplicate_Post\\UI\\Bulk_Actions' => __DIR__ . '/../..' . '/src/ui/class-bulk-actions.php',
29
- 'Yoast\\WP\\Duplicate_Post\\UI\\Classic_Editor' => __DIR__ . '/../..' . '/src/ui/class-classic-editor.php',
30
- 'Yoast\\WP\\Duplicate_Post\\UI\\Column' => __DIR__ . '/../..' . '/src/ui/class-column.php',
31
- 'Yoast\\WP\\Duplicate_Post\\UI\\Link_Builder' => __DIR__ . '/../..' . '/src/ui/class-link-builder.php',
32
- 'Yoast\\WP\\Duplicate_Post\\UI\\Metabox' => __DIR__ . '/../..' . '/src/ui/class-metabox.php',
33
- 'Yoast\\WP\\Duplicate_Post\\UI\\Post_States' => __DIR__ . '/../..' . '/src/ui/class-post-states.php',
34
- 'Yoast\\WP\\Duplicate_Post\\UI\\Row_Actions' => __DIR__ . '/../..' . '/src/ui/class-row-actions.php',
35
- 'Yoast\\WP\\Duplicate_Post\\UI\\User_Interface' => __DIR__ . '/../..' . '/src/ui/class-user-interface.php',
36
- 'Yoast\\WP\\Duplicate_Post\\Utils' => __DIR__ . '/../..' . '/src/class-utils.php',
37
- 'Yoast\\WP\\Duplicate_Post\\Watchers\\Bulk_Actions_Watcher' => __DIR__ . '/../..' . '/src/watchers/class-bulk-actions-watcher.php',
38
- 'Yoast\\WP\\Duplicate_Post\\Watchers\\Copied_Post_Watcher' => __DIR__ . '/../..' . '/src/watchers/class-copied-post-watcher.php',
39
- 'Yoast\\WP\\Duplicate_Post\\Watchers\\Link_Actions_Watcher' => __DIR__ . '/../..' . '/src/watchers/class-link-actions-watcher.php',
40
- 'Yoast\\WP\\Duplicate_Post\\Watchers\\Original_Post_Watcher' => __DIR__ . '/../..' . '/src/watchers/class-original-post-watcher.php',
41
- 'Yoast\\WP\\Duplicate_Post\\Watchers\\Republished_Post_Watcher' => __DIR__ . '/../..' . '/src/watchers/class-republished-post-watcher.php',
42
- 'Yoast\\WP\\Duplicate_Post\\Watchers\\Watchers' => __DIR__ . '/../..' . '/src/watchers/class-watchers.php',
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
  );
44
 
45
  public static function getInitializer(ClassLoader $loader)
46
  {
47
  return \Closure::bind(function () use ($loader) {
 
 
48
  $loader->classMap = ComposerStaticInitb771dd36b1819b8209dfac03b31cc9ed::$classMap;
49
 
50
  }, null, ClassLoader::class);
6
 
7
  class ComposerStaticInitb771dd36b1819b8209dfac03b31cc9ed
8
  {
9
+ public static $prefixLengthsPsr4 = array (
10
+ 'C' =>
11
+ array (
12
+ 'Composer\\Installers\\' => 20,
13
+ ),
14
+ );
15
+
16
+ public static $prefixDirsPsr4 = array (
17
+ 'Composer\\Installers\\' =>
18
+ array (
19
+ 0 => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers',
20
+ ),
21
+ );
22
+
23
  public static $classMap = array (
24
  'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
25
+ 'Composer\\Installers\\AglInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/AglInstaller.php',
26
+ 'Composer\\Installers\\AimeosInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/AimeosInstaller.php',
27
+ 'Composer\\Installers\\AnnotateCmsInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/AnnotateCmsInstaller.php',
28
+ 'Composer\\Installers\\AsgardInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/AsgardInstaller.php',
29
+ 'Composer\\Installers\\AttogramInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/AttogramInstaller.php',
30
+ 'Composer\\Installers\\BaseInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/BaseInstaller.php',
31
+ 'Composer\\Installers\\BitrixInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/BitrixInstaller.php',
32
+ 'Composer\\Installers\\BonefishInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/BonefishInstaller.php',
33
+ 'Composer\\Installers\\CakePHPInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/CakePHPInstaller.php',
34
+ 'Composer\\Installers\\ChefInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/ChefInstaller.php',
35
+ 'Composer\\Installers\\CiviCrmInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/CiviCrmInstaller.php',
36
+ 'Composer\\Installers\\ClanCatsFrameworkInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/ClanCatsFrameworkInstaller.php',
37
+ 'Composer\\Installers\\CockpitInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/CockpitInstaller.php',
38
+ 'Composer\\Installers\\CodeIgniterInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/CodeIgniterInstaller.php',
39
+ 'Composer\\Installers\\Concrete5Installer' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/Concrete5Installer.php',
40
+ 'Composer\\Installers\\CraftInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/CraftInstaller.php',
41
+ 'Composer\\Installers\\CroogoInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/CroogoInstaller.php',
42
+ 'Composer\\Installers\\DecibelInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/DecibelInstaller.php',
43
+ 'Composer\\Installers\\DframeInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/DframeInstaller.php',
44
+ 'Composer\\Installers\\DokuWikiInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/DokuWikiInstaller.php',
45
+ 'Composer\\Installers\\DolibarrInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/DolibarrInstaller.php',
46
+ 'Composer\\Installers\\DrupalInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/DrupalInstaller.php',
47
+ 'Composer\\Installers\\ElggInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/ElggInstaller.php',
48
+ 'Composer\\Installers\\EliasisInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/EliasisInstaller.php',
49
+ 'Composer\\Installers\\ExpressionEngineInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/ExpressionEngineInstaller.php',
50
+ 'Composer\\Installers\\EzPlatformInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/EzPlatformInstaller.php',
51
+ 'Composer\\Installers\\FuelInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/FuelInstaller.php',
52
+ 'Composer\\Installers\\FuelphpInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/FuelphpInstaller.php',
53
+ 'Composer\\Installers\\GravInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/GravInstaller.php',
54
+ 'Composer\\Installers\\HuradInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/HuradInstaller.php',
55
+ 'Composer\\Installers\\ImageCMSInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/ImageCMSInstaller.php',
56
+ 'Composer\\Installers\\Installer' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/Installer.php',
57
+ 'Composer\\Installers\\ItopInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/ItopInstaller.php',
58
+ 'Composer\\Installers\\JoomlaInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/JoomlaInstaller.php',
59
+ 'Composer\\Installers\\KanboardInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/KanboardInstaller.php',
60
+ 'Composer\\Installers\\KirbyInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/KirbyInstaller.php',
61
+ 'Composer\\Installers\\KnownInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/KnownInstaller.php',
62
+ 'Composer\\Installers\\KodiCMSInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/KodiCMSInstaller.php',
63
+ 'Composer\\Installers\\KohanaInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/KohanaInstaller.php',
64
+ 'Composer\\Installers\\LanManagementSystemInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/LanManagementSystemInstaller.php',
65
+ 'Composer\\Installers\\LaravelInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/LaravelInstaller.php',
66
+ 'Composer\\Installers\\LavaLiteInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/LavaLiteInstaller.php',
67
+ 'Composer\\Installers\\LithiumInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/LithiumInstaller.php',
68
+ 'Composer\\Installers\\MODULEWorkInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/MODULEWorkInstaller.php',
69
+ 'Composer\\Installers\\MODXEvoInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/MODXEvoInstaller.php',
70
+ 'Composer\\Installers\\MagentoInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/MagentoInstaller.php',
71
+ 'Composer\\Installers\\MajimaInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/MajimaInstaller.php',
72
+ 'Composer\\Installers\\MakoInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/MakoInstaller.php',
73
+ 'Composer\\Installers\\MantisBTInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/MantisBTInstaller.php',
74
+ 'Composer\\Installers\\MauticInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/MauticInstaller.php',
75
+ 'Composer\\Installers\\MayaInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/MayaInstaller.php',
76
+ 'Composer\\Installers\\MediaWikiInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/MediaWikiInstaller.php',
77
+ 'Composer\\Installers\\MiaoxingInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/MiaoxingInstaller.php',
78
+ 'Composer\\Installers\\MicroweberInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/MicroweberInstaller.php',
79
+ 'Composer\\Installers\\ModxInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/ModxInstaller.php',
80
+ 'Composer\\Installers\\MoodleInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/MoodleInstaller.php',
81
+ 'Composer\\Installers\\OctoberInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/OctoberInstaller.php',
82
+ 'Composer\\Installers\\OntoWikiInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/OntoWikiInstaller.php',
83
+ 'Composer\\Installers\\OsclassInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/OsclassInstaller.php',
84
+ 'Composer\\Installers\\OxidInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/OxidInstaller.php',
85
+ 'Composer\\Installers\\PPIInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/PPIInstaller.php',
86
+ 'Composer\\Installers\\PantheonInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/PantheonInstaller.php',
87
+ 'Composer\\Installers\\PhiftyInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/PhiftyInstaller.php',
88
+ 'Composer\\Installers\\PhpBBInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/PhpBBInstaller.php',
89
+ 'Composer\\Installers\\PimcoreInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/PimcoreInstaller.php',
90
+ 'Composer\\Installers\\PiwikInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/PiwikInstaller.php',
91
+ 'Composer\\Installers\\PlentymarketsInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/PlentymarketsInstaller.php',
92
+ 'Composer\\Installers\\Plugin' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/Plugin.php',
93
+ 'Composer\\Installers\\PortoInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/PortoInstaller.php',
94
+ 'Composer\\Installers\\PrestashopInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/PrestashopInstaller.php',
95
+ 'Composer\\Installers\\ProcessWireInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/ProcessWireInstaller.php',
96
+ 'Composer\\Installers\\PuppetInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/PuppetInstaller.php',
97
+ 'Composer\\Installers\\PxcmsInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/PxcmsInstaller.php',
98
+ 'Composer\\Installers\\RadPHPInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/RadPHPInstaller.php',
99
+ 'Composer\\Installers\\ReIndexInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/ReIndexInstaller.php',
100
+ 'Composer\\Installers\\Redaxo5Installer' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/Redaxo5Installer.php',
101
+ 'Composer\\Installers\\RedaxoInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/RedaxoInstaller.php',
102
+ 'Composer\\Installers\\RoundcubeInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/RoundcubeInstaller.php',
103
+ 'Composer\\Installers\\SMFInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/SMFInstaller.php',
104
+ 'Composer\\Installers\\ShopwareInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/ShopwareInstaller.php',
105
+ 'Composer\\Installers\\SilverStripeInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/SilverStripeInstaller.php',
106
+ 'Composer\\Installers\\SiteDirectInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/SiteDirectInstaller.php',
107
+ 'Composer\\Installers\\StarbugInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/StarbugInstaller.php',
108
+ 'Composer\\Installers\\SyDESInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/SyDESInstaller.php',
109
+ 'Composer\\Installers\\SyliusInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/SyliusInstaller.php',
110
+ 'Composer\\Installers\\Symfony1Installer' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/Symfony1Installer.php',
111
+ 'Composer\\Installers\\TYPO3CmsInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/TYPO3CmsInstaller.php',
112
+ 'Composer\\Installers\\TYPO3FlowInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/TYPO3FlowInstaller.php',
113
+ 'Composer\\Installers\\TaoInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/TaoInstaller.php',
114
+ 'Composer\\Installers\\TastyIgniterInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/TastyIgniterInstaller.php',
115
+ 'Composer\\Installers\\TheliaInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/TheliaInstaller.php',
116
+ 'Composer\\Installers\\TuskInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/TuskInstaller.php',
117
+ 'Composer\\Installers\\UserFrostingInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/UserFrostingInstaller.php',
118
+ 'Composer\\Installers\\VanillaInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/VanillaInstaller.php',
119
+ 'Composer\\Installers\\VgmcpInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/VgmcpInstaller.php',
120
+ 'Composer\\Installers\\WHMCSInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/WHMCSInstaller.php',
121
+ 'Composer\\Installers\\WinterInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/WinterInstaller.php',
122
+ 'Composer\\Installers\\WolfCMSInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/WolfCMSInstaller.php',
123
+ 'Composer\\Installers\\WordPressInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/WordPressInstaller.php',
124
+ 'Composer\\Installers\\YawikInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/YawikInstaller.php',
125
+ 'Composer\\Installers\\ZendInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/ZendInstaller.php',
126
+ 'Composer\\Installers\\ZikulaInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/ZikulaInstaller.php',
127
+ 'Yoast\\WP\\Duplicate_Post\\Admin\\Options' => __DIR__ . '/../..' . '/src/admin/options.php',
128
+ 'Yoast\\WP\\Duplicate_Post\\Admin\\Options_Form_Generator' => __DIR__ . '/../..' . '/src/admin/options-form-generator.php',
129
+ 'Yoast\\WP\\Duplicate_Post\\Admin\\Options_Inputs' => __DIR__ . '/../..' . '/src/admin/options-inputs.php',
130
+ 'Yoast\\WP\\Duplicate_Post\\Admin\\Options_Page' => __DIR__ . '/../..' . '/src/admin/options-page.php',
131
+ 'Yoast\\WP\\Duplicate_Post\\Duplicate_Post' => __DIR__ . '/../..' . '/src/duplicate-post.php',
132
+ 'Yoast\\WP\\Duplicate_Post\\Handlers\\Bulk_Handler' => __DIR__ . '/../..' . '/src/handlers/bulk-handler.php',
133
+ 'Yoast\\WP\\Duplicate_Post\\Handlers\\Check_Changes_Handler' => __DIR__ . '/../..' . '/src/handlers/check-changes-handler.php',
134
+ 'Yoast\\WP\\Duplicate_Post\\Handlers\\Handler' => __DIR__ . '/../..' . '/src/handlers/handler.php',
135
+ 'Yoast\\WP\\Duplicate_Post\\Handlers\\Link_Handler' => __DIR__ . '/../..' . '/src/handlers/link-handler.php',
136
+ 'Yoast\\WP\\Duplicate_Post\\Handlers\\Save_Post_Handler' => __DIR__ . '/../..' . '/src/handlers/save-post-handler.php',
137
+ 'Yoast\\WP\\Duplicate_Post\\Permissions_Helper' => __DIR__ . '/../..' . '/src/permissions-helper.php',
138
+ 'Yoast\\WP\\Duplicate_Post\\Post_Duplicator' => __DIR__ . '/../..' . '/src/post-duplicator.php',
139
+ 'Yoast\\WP\\Duplicate_Post\\Post_Republisher' => __DIR__ . '/../..' . '/src/post-republisher.php',
140
+ 'Yoast\\WP\\Duplicate_Post\\Revisions_Migrator' => __DIR__ . '/../..' . '/src/revisions-migrator.php',
141
+ 'Yoast\\WP\\Duplicate_Post\\UI\\Admin_Bar' => __DIR__ . '/../..' . '/src/ui/admin-bar.php',
142
+ 'Yoast\\WP\\Duplicate_Post\\UI\\Asset_Manager' => __DIR__ . '/../..' . '/src/ui/asset-manager.php',
143
+ 'Yoast\\WP\\Duplicate_Post\\UI\\Block_Editor' => __DIR__ . '/../..' . '/src/ui/block-editor.php',
144
+ 'Yoast\\WP\\Duplicate_Post\\UI\\Bulk_Actions' => __DIR__ . '/../..' . '/src/ui/bulk-actions.php',
145
+ 'Yoast\\WP\\Duplicate_Post\\UI\\Classic_Editor' => __DIR__ . '/../..' . '/src/ui/classic-editor.php',
146
+ 'Yoast\\WP\\Duplicate_Post\\UI\\Column' => __DIR__ . '/../..' . '/src/ui/column.php',
147
+ 'Yoast\\WP\\Duplicate_Post\\UI\\Link_Builder' => __DIR__ . '/../..' . '/src/ui/link-builder.php',
148
+ 'Yoast\\WP\\Duplicate_Post\\UI\\Metabox' => __DIR__ . '/../..' . '/src/ui/metabox.php',
149
+ 'Yoast\\WP\\Duplicate_Post\\UI\\Newsletter' => __DIR__ . '/../..' . '/src/ui/newsletter.php',
150
+ 'Yoast\\WP\\Duplicate_Post\\UI\\Post_States' => __DIR__ . '/../..' . '/src/ui/post-states.php',
151
+ 'Yoast\\WP\\Duplicate_Post\\UI\\Row_Actions' => __DIR__ . '/../..' . '/src/ui/row-actions.php',
152
+ 'Yoast\\WP\\Duplicate_Post\\UI\\User_Interface' => __DIR__ . '/../..' . '/src/ui/user-interface.php',
153
+ 'Yoast\\WP\\Duplicate_Post\\Utils' => __DIR__ . '/../..' . '/src/utils.php',
154
+ 'Yoast\\WP\\Duplicate_Post\\Watchers\\Bulk_Actions_Watcher' => __DIR__ . '/../..' . '/src/watchers/bulk-actions-watcher.php',
155
+ 'Yoast\\WP\\Duplicate_Post\\Watchers\\Copied_Post_Watcher' => __DIR__ . '/../..' . '/src/watchers/copied-post-watcher.php',
156
+ 'Yoast\\WP\\Duplicate_Post\\Watchers\\Link_Actions_Watcher' => __DIR__ . '/../..' . '/src/watchers/link-actions-watcher.php',
157
+ 'Yoast\\WP\\Duplicate_Post\\Watchers\\Original_Post_Watcher' => __DIR__ . '/../..' . '/src/watchers/original-post-watcher.php',
158
+ 'Yoast\\WP\\Duplicate_Post\\Watchers\\Republished_Post_Watcher' => __DIR__ . '/../..' . '/src/watchers/republished-post-watcher.php',
159
+ 'Yoast\\WP\\Duplicate_Post\\Watchers\\Watchers' => __DIR__ . '/../..' . '/src/watchers/watchers.php',
160
  );
161
 
162
  public static function getInitializer(ClassLoader $loader)
163
  {
164
  return \Closure::bind(function () use ($loader) {
165
+ $loader->prefixLengthsPsr4 = ComposerStaticInitb771dd36b1819b8209dfac03b31cc9ed::$prefixLengthsPsr4;
166
+ $loader->prefixDirsPsr4 = ComposerStaticInitb771dd36b1819b8209dfac03b31cc9ed::$prefixDirsPsr4;
167
  $loader->classMap = ComposerStaticInitb771dd36b1819b8209dfac03b31cc9ed::$classMap;
168
 
169
  }, null, ClassLoader::class);
vendor/composer/installed.php CHANGED
@@ -1,24 +1,44 @@
1
- <?php return array (
2
- 'root' =>
3
- array (
4
- 'pretty_version' => 'dev-master',
5
- 'version' => 'dev-master',
6
- 'aliases' =>
7
- array (
 
 
 
8
  ),
9
- 'reference' => '4b6a2ad764ef5fdaa8cb40081a3f00f437163d9e',
10
- 'name' => '__root__',
11
- ),
12
- 'versions' =>
13
- array (
14
- '__root__' =>
15
- array (
16
- 'pretty_version' => 'dev-master',
17
- 'version' => 'dev-master',
18
- 'aliases' =>
19
- array (
20
- ),
21
- 'reference' => '4b6a2ad764ef5fdaa8cb40081a3f00f437163d9e',
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
  ),
23
- ),
24
  );
1
+ <?php return array(
2
+ 'root' => array(
3
+ 'pretty_version' => 'dev-master',
4
+ 'version' => 'dev-master',
5
+ 'type' => 'wordpress-plugin',
6
+ 'install_path' => __DIR__ . '/../../',
7
+ 'aliases' => array(),
8
+ 'reference' => '95ce770dd62b0a430e53adcd2cbdd36d33b89fa4',
9
+ 'name' => 'yoast/duplicate-post',
10
+ 'dev' => false,
11
  ),
12
+ 'versions' => array(
13
+ 'composer/installers' => array(
14
+ 'pretty_version' => 'v1.12.0',
15
+ 'version' => '1.12.0.0',
16
+ 'type' => 'composer-plugin',
17
+ 'install_path' => __DIR__ . '/./installers',
18
+ 'aliases' => array(),
19
+ 'reference' => 'd20a64ed3c94748397ff5973488761b22f6d3f19',
20
+ 'dev_requirement' => false,
21
+ ),
22
+ 'roundcube/plugin-installer' => array(
23
+ 'dev_requirement' => false,
24
+ 'replaced' => array(
25
+ 0 => '*',
26
+ ),
27
+ ),
28
+ 'shama/baton' => array(
29
+ 'dev_requirement' => false,
30
+ 'replaced' => array(
31
+ 0 => '*',
32
+ ),
33
+ ),
34
+ 'yoast/duplicate-post' => array(
35
+ 'pretty_version' => 'dev-master',
36
+ 'version' => 'dev-master',
37
+ 'type' => 'wordpress-plugin',
38
+ 'install_path' => __DIR__ . '/../../',
39
+ 'aliases' => array(),
40
+ 'reference' => '95ce770dd62b0a430e53adcd2cbdd36d33b89fa4',
41
+ 'dev_requirement' => false,
42
+ ),
43
  ),
 
44
  );
vendor/composer/platform_check.php ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // platform_check.php @generated by Composer
4
+
5
+ $issues = array();
6
+
7
+ if (!(PHP_VERSION_ID >= 50600)) {
8
+ $issues[] = 'Your Composer dependencies require a PHP version ">= 5.6.0". You are running ' . PHP_VERSION . '.';
9
+ }
10
+
11
+ if ($issues) {
12
+ if (!headers_sent()) {
13
+ header('HTTP/1.1 500 Internal Server Error');
14
+ }
15
+ if (!ini_get('display_errors')) {
16
+ if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
17
+ fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL);
18
+ } elseif (!headers_sent()) {
19
+ echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL;
20
+ }
21
+ }
22
+ trigger_error(
23
+ 'Composer detected issues in your platform: ' . implode(' ', $issues),
24
+ E_USER_ERROR
25
+ );
26
+ }