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

Yoast / duplicate-post / 21648102573

03 Feb 2026 09:14PM UTC coverage: 59.642% (+1.8%) from 57.839%
21648102573

Pull #452

github

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

62 of 65 new or added lines in 3 files covered. (95.38%)

1 existing line in 1 file now uncovered.

1599 of 2681 relevant lines covered (59.64%)

7.62 hits per line

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

78.18
/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
                                $post = \get_post( $post_id );
4✔
96
                                if ( empty( $post ) || ! $this->permissions_helper->should_rewrite_and_republish_be_allowed( $post ) ) {
4✔
NEW
97
                                        continue;
×
98
                                }
99
                                if ( ! \current_user_can( 'edit_post', $post_id ) ) {
4✔
100
                                        ++$skipped;
2✔
101
                                        continue;
2✔
102
                                }
103
                                $new_post_id = $this->post_duplicator->create_duplicate_for_rewrite_and_republish( $post );
2✔
104
                                if ( ! \is_wp_error( $new_post_id ) ) {
2✔
105
                                        ++$counter;
2✔
106
                                }
107
                        }
108
                }
109
                $redirect_to = \add_query_arg( 'bulk_rewriting', $counter, $redirect_to );
4✔
110
                if ( $skipped > 0 ) {
4✔
111
                        $redirect_to = \add_query_arg( 'bulk_rewriting_skipped', $skipped, $redirect_to );
2✔
112
                }
113
                return $redirect_to;
4✔
114
        }
115

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

130
                $counter = 0;
6✔
131
                $skipped = 0;
6✔
132
                if ( \is_array( $post_ids ) ) {
6✔
133
                        foreach ( $post_ids as $post_id ) {
6✔
134
                                $post = \get_post( $post_id );
6✔
135
                                if ( empty( $post ) || $this->permissions_helper->is_rewrite_and_republish_copy( $post ) ) {
6✔
NEW
136
                                        continue;
×
137
                                }
138
                                if ( \intval( \get_option( 'duplicate_post_copychildren' ) ) === 1
6✔
139
                                        && \is_post_type_hierarchical( $post->post_type )
6✔
140
                                        && Utils::has_ancestors_marked( $post, $post_ids )
6✔
141
                                ) {
NEW
142
                                        continue;
×
143
                                }
144
                                if ( ! \current_user_can( 'edit_post', $post_id ) ) {
6✔
145
                                        ++$skipped;
2✔
146
                                        continue;
2✔
147
                                }
148
                                if ( ! \is_wp_error( \duplicate_post_create_duplicate( $post ) ) ) {
4✔
149
                                        ++$counter;
2✔
150
                                }
151
                        }
152
                }
153
                $redirect_to = \add_query_arg( 'bulk_cloned', $counter, $redirect_to );
6✔
154
                if ( $skipped > 0 ) {
6✔
155
                        $redirect_to = \add_query_arg( 'bulk_cloned_skipped', $skipped, $redirect_to );
2✔
156
                }
157
                return $redirect_to;
6✔
158
        }
159
}
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