• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Yoast / duplicate-post / 21647398675

03 Feb 2026 08:52PM UTC coverage: 59.671% (+1.8%) from 57.839%
21647398675

Pull #452

github

web-flow
Merge 343f002ca into 9b356055d
Pull Request #452: Improves permissions checks for the Bulk Clone action and the republishing of a copy.

56 of 57 new or added lines in 3 files covered. (98.25%)

1 existing line in 1 file now uncovered.

1598 of 2678 relevant lines covered (59.67%)

7.61 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

80.77
/src/handlers/bulk-handler.php
1
<?php
2

3
namespace Yoast\WP\Duplicate_Post\Handlers;
4

5
use Yoast\WP\Duplicate_Post\Permissions_Helper;
6
use Yoast\WP\Duplicate_Post\Post_Duplicator;
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

16
        /**
17
         * Post_Duplicator object.
18
         *
19
         * @var Post_Duplicator
20
         */
21
        protected $post_duplicator;
22

23
        /**
24
         * Holds the permissions helper.
25
         *
26
         * @var Permissions_Helper
27
         */
28
        protected $permissions_helper;
29

30
        /**
31
         * Initializes the class.
32
         *
33
         * @param Post_Duplicator    $post_duplicator    The Post_Duplicator object.
34
         * @param Permissions_Helper $permissions_helper The Permissions Helper object.
35
         */
36
        public function __construct( Post_Duplicator $post_duplicator, Permissions_Helper $permissions_helper ) {
2✔
37
                $this->post_duplicator    = $post_duplicator;
2✔
38
                $this->permissions_helper = $permissions_helper;
2✔
39
        }
40

41
        /**
42
         * Adds hooks to integrate with WordPress.
43
         *
44
         * @return void
45
         */
46
        public function register_hooks() {
×
47
                \add_action( 'admin_init', [ $this, 'add_bulk_handlers' ] );
×
48
        }
49

50
        /**
51
         * Hooks the handler for the Rewrite & Republish action for all the selected post types.
52
         *
53
         * @return void
54
         */
55
        public function add_bulk_handlers() {
×
56
                $duplicate_post_types_enabled = $this->permissions_helper->get_enabled_post_types();
×
57

58
                foreach ( $duplicate_post_types_enabled as $duplicate_post_type_enabled ) {
×
59
                        \add_filter( "handle_bulk_actions-edit-{$duplicate_post_type_enabled}", [ $this, 'bulk_action_handler' ], 10, 3 );
×
60
                }
61
        }
62

63
        /**
64
         * Handles the bulk actions.
65
         *
66
         * @param string $redirect_to The URL to redirect to.
67
         * @param string $doaction    The action that has been called.
68
         * @param array  $post_ids    The array of marked post IDs.
69
         *
70
         * @return string The URL to redirect to.
71
         */
72
        public function bulk_action_handler( $redirect_to, $doaction, $post_ids ) {
×
73
                $redirect_to = $this->clone_bulk_action_handler( $redirect_to, $doaction, $post_ids );
×
74
                return $this->rewrite_bulk_action_handler( $redirect_to, $doaction, $post_ids );
×
75
        }
76

77
        /**
78
         * Handles the bulk action for the Rewrite & Republish feature.
79
         *
80
         * @param string $redirect_to The URL to redirect to.
81
         * @param string $doaction    The action that has been called.
82
         * @param array  $post_ids    The array of marked post IDs.
83
         *
84
         * @return string The URL to redirect to.
85
         */
86
        public function rewrite_bulk_action_handler( $redirect_to, $doaction, $post_ids ) {
6✔
87
                if ( $doaction !== 'duplicate_post_bulk_rewrite_republish' ) {
6✔
88
                        return $redirect_to;
2✔
89
                }
90

91
                $counter = 0;
4✔
92
                $skipped = 0;
4✔
93
                if ( \is_array( $post_ids ) ) {
4✔
94
                        foreach ( $post_ids as $post_id ) {
4✔
95
                                if ( ! \current_user_can( 'edit_post', $post_id ) ) {
4✔
96
                                        ++$skipped;
2✔
97
                                        continue;
2✔
98
                                }
99
                                $post = \get_post( $post_id );
2✔
100
                                if ( ! empty( $post ) && $this->permissions_helper->should_rewrite_and_republish_be_allowed( $post ) ) {
2✔
101
                                        $new_post_id = $this->post_duplicator->create_duplicate_for_rewrite_and_republish( $post );
2✔
102
                                        if ( ! \is_wp_error( $new_post_id ) ) {
2✔
103
                                                ++$counter;
2✔
104
                                        }
105
                                }
106
                        }
107
                }
108
                $redirect_to = \add_query_arg( 'bulk_rewriting', $counter, $redirect_to );
4✔
109
                if ( $skipped > 0 ) {
4✔
110
                        $redirect_to = \add_query_arg( 'bulk_rewriting_skipped', $skipped, $redirect_to );
2✔
111
                }
112
                return $redirect_to;
4✔
113
        }
114

115
        /**
116
         * Handles the bulk action for the Clone feature.
117
         *
118
         * @param string $redirect_to The URL to redirect to.
119
         * @param string $doaction    The action that has been called.
120
         * @param array  $post_ids    The array of marked post IDs.
121
         *
122
         * @return string The URL to redirect to.
123
         */
124
        public function clone_bulk_action_handler( $redirect_to, $doaction, $post_ids ) {
6✔
125
                if ( $doaction !== 'duplicate_post_bulk_clone' ) {
6✔
126
                        return $redirect_to;
2✔
127
                }
128

129
                $counter = 0;
4✔
130
                $skipped = 0;
4✔
131
                if ( \is_array( $post_ids ) ) {
4✔
132
                        foreach ( $post_ids as $post_id ) {
4✔
133
                                if ( ! \current_user_can( 'edit_post', $post_id ) ) {
4✔
134
                                        ++$skipped;
2✔
135
                                        continue;
2✔
136
                                }
137
                                $post = \get_post( $post_id );
2✔
138
                                if ( ! empty( $post ) && ! $this->permissions_helper->is_rewrite_and_republish_copy( $post ) ) {
2✔
139
                                        if ( \intval( \get_option( 'duplicate_post_copychildren' ) !== 1 )
2✔
NEW
140
                                                || ! \is_post_type_hierarchical( $post->post_type )
×
141
                                                || ( \is_post_type_hierarchical( $post->post_type ) && ! Utils::has_ancestors_marked( $post, $post_ids ) )
2✔
142
                                        ) {
143
                                                if ( ! \is_wp_error( \duplicate_post_create_duplicate( $post ) ) ) {
2✔
144
                                                        ++$counter;
2✔
145
                                                }
146
                                        }
147
                                }
148
                        }
149
                }
150
                $redirect_to = \add_query_arg( 'bulk_cloned', $counter, $redirect_to );
4✔
151
                if ( $skipped > 0 ) {
4✔
152
                        $redirect_to = \add_query_arg( 'bulk_cloned_skipped', $skipped, $redirect_to );
2✔
153
                }
154
                return $redirect_to;
4✔
155
        }
156
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc