PluginProbe
Ultimate Post Kit – Elementor Post Grid, Post Carousel, Post Slider & Blog Layout Widgets / 4.5.6
Ultimate Post Kit – Elementor Post Grid, Post Carousel, Post Slider & Blog Layout Widgets v4.5.6
4.5.6 4.5.5 4.5.4 4.2.1 4.2.2 4.2.3 4.5.0 4.5.2 4.5.3 4.2.0 4.1.18 4.1.17 4.1.16 4.1.15 4.1.14 4.1.13 4.1.12 4.1.11 4.1.10 4.1.9 4.1.8 4.0.9 4.1.0 4.1.1 4.1.2 All 148 releases
← All changes | includes/class-duplicator.php +66 -29 4.1.16 → 4.5.6 View file →
@@ -12,13 +12,13 @@
12 12 /**
13 13 * Duplicator Class
14 14 */
15 15
16 -if (!class_exists('BdThemes_Duplicator')) :
16 +if (!class_exists(__NAMESPACE__ . '\\BdThemes_Duplicator')) :
17 17 class BdThemes_Duplicator {
18 18
19 19 public function __construct() {
20 - add_action('admin_action_bdt_duplicate_as_draft', [$this, 'bdt_duplicate_as_draft']);
20 + add_action('admin_action_ultimate_post_kit_duplicate_as_draft', [$this, 'bdt_duplicate_as_draft']);
21 21 add_filter('post_row_actions', [$this, 'bdt_duplicate_post_link'], 10, 2);
22 22 add_filter('page_row_actions', [$this, 'bdt_duplicate_post_link'], 10, 2);
23 23 }
24 24
@@ -27,40 +27,61 @@
27 27 if (!current_user_can('edit_posts')) {
28 28 wp_die('You don\'t have permission to duplicate it; please go back!');
29 29 }
30 30
31 - if (!(isset($_GET['post']) || isset($_POST['post']) || (isset($_REQUEST['action']) && 'bdt_duplicate_as_draft' == $_REQUEST['action']))) {
31 + if (!(isset($_GET['post']) || isset($_POST['post']) || (isset($_REQUEST['action']) && 'ultimate_post_kit_duplicate_as_draft' == $_REQUEST['action']))) {
32 32 wp_die('No post to duplicate has been supplied!');
33 33 }
34 34
35 35 /**
36 - * Nonce verification
36 + * get the original post id
37 + *
38 + * This has to be read before the nonce check because the nonce action is bound
39 + * to the post being duplicated (see bdt_duplicate_post_link()). The value is
40 + * cast to an integer and used only to build that action string; nothing is read
41 + * or written with it until the nonce and capability checks below have passed.
37 42 */
38 - if (!isset($_GET['duplicate_nonce']) || !wp_verify_nonce($_GET['duplicate_nonce'], basename(__FILE__))) {
43 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.NonceVerification.Missing -- only used to construct the nonce action, which is verified on the next statement.
44 + $post_id = isset($_GET['post']) ? absint($_GET['post']) : absint($_POST['post'] ?? 0);
45 +
46 + /**
47 + * Nonce verification.
48 + *
49 + * The nonce is bound to the post being duplicated, so one nonce cannot be
50 + * replayed against every other post (and post type) on the site.
51 + */
52 + if (!isset($_GET['duplicate_nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_GET['duplicate_nonce'])), 'upk_duplicate_post_' . $post_id)) {
39 53 return;
40 54 }
41 55
42 56 /**
43 - * get the original post id
44 - */
45 - $post_id = (isset($_GET['post']) ? absint($_GET['post']) : absint($_POST['post']));
46 - /**
47 57 * and all the original post data then
48 58 */
49 59 $post = get_post($post_id);
50 60
61 + if (!$post) {
62 + wp_die(esc_html('Failed. Not Found Post: ' . $post_id));
63 + }
64 +
51 65 /**
52 - * if you don't want current user to be the new post author,
66 + * Authorise against THIS post, not against a generic role capability.
67 + *
68 + * edit_others_posts is only the capability for the built-in 'post' type; it
69 + * grants nothing over a post type registered with its own capability set. Use
70 + * the meta capability so WordPress maps it through the target post type, and
71 + * check create_posts separately because duplicating creates a new object.
53 72 */
54 - $current_user_id = get_current_user_id();
73 + if (!current_user_can('edit_post', $post_id)) {
74 + wp_die('You don\'t have permission to duplicate it; please go back!');
75 + }
55 76
56 - if (current_user_can('manage_options') || current_user_can('edit_others_posts')) {
57 - $this->duplicate_edit_post($post_id);
58 - } else if (current_user_can('edit_posts') && $post->post_author == $current_user_id) {
59 - $this->duplicate_edit_post($post_id);
60 - } else {
77 + $post_type_object = get_post_type_object($post->post_type);
78 +
79 + if (!$post_type_object || !current_user_can($post_type_object->cap->create_posts)) {
61 80 wp_die('You don\'t have permission to duplicate it; please go back!');
62 81 }
82 +
83 + $this->duplicate_edit_post($post_id);
63 84 }
64 85
65 86 /**
66 87 * duplicate edit post
@@ -123,21 +144,27 @@
123 144
124 145 /**
125 146 * duplicate all post meta just in two SQL queries
126 147 */
127 - $bdt_post_meta_infos = $wpdb->get_results("SELECT meta_key, meta_value FROM $wpdb->postmeta WHERE post_id=$bdt_post_id");
148 + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- one-off admin duplicate action, caching not applicable.
149 + $bdt_post_meta_infos = $wpdb->get_results($wpdb->prepare("SELECT meta_key, meta_value FROM {$wpdb->postmeta} WHERE post_id = %d", $post_id));
128 150
129 151 if (is_array($bdt_post_meta_infos)) {
130 - $bdt_sql_query = "INSERT INTO {$wpdb->postmeta} ( post_id, meta_key, meta_value ) VALUES ";
131 152 $bdt_sql_query_sel = [];
153 + $bdt_sql_values = [];
132 154
133 155 foreach ($bdt_post_meta_infos as $bdt_meta_info) {
134 - $bdt_meta_value = wp_slash($bdt_meta_info->meta_value);
135 - $bdt_sql_query_sel[] = "( $bdt_new_post_id, '{$bdt_meta_info->meta_key}', '{$bdt_meta_value}' )";
156 + $bdt_sql_query_sel[] = '( %d, %s, %s )';
157 + $bdt_sql_values[] = $bdt_new_post_id;
158 + $bdt_sql_values[] = $bdt_meta_info->meta_key;
159 + $bdt_sql_values[] = wp_slash($bdt_meta_info->meta_value);
136 160 }
137 161
138 - $bdt_sql_query .= implode(', ', $bdt_sql_query_sel) . ';';
139 - $wpdb->query($bdt_sql_query);
162 + if (!empty($bdt_sql_query_sel)) {
163 + $bdt_sql_query = "INSERT INTO {$wpdb->postmeta} ( post_id, meta_key, meta_value ) VALUES " . implode(', ', $bdt_sql_query_sel);
164 + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Query is built only from static "%d, %s, %s" placeholders and the trusted {$wpdb->postmeta} table name; all values are bound via $wpdb->prepare().
165 + $wpdb->query($wpdb->prepare($bdt_sql_query, $bdt_sql_values));
166 + }
140 167
141 168 /**
142 169 * fix template type issues
143 170 */
@@ -161,14 +188,15 @@
161 188
162 189 $current_post_type = get_post_type($post_id);
163 190
164 191 if (is_array($bdt_names) && in_array($current_post_type, $bdt_names)) {
165 - wp_redirect(admin_url('edit.php?post_type=' . $current_post_type));
192 + wp_safe_redirect(admin_url('edit.php?post_type=' . $current_post_type));
193 + exit;
166 194 }
167 195
168 196 exit;
169 197 } else {
170 - wp_die('Failed. Not Found Post: ' . $post_id);
198 + wp_die(esc_html('Failed. Not Found Post: ' . $post_id));
171 199 }
172 200 }
173 201
174 202
@@ -173,15 +201,15 @@
173 201
174 202
175 203 public function bdt_duplicate_post_link($actions, $post) {
176 204
177 - if (current_user_can('manage_options') || current_user_can('edit_others_posts')) {
205 + if (current_user_can('edit_post', $post->ID)) {
178 206 if ($post->post_type == 'post') {
179 - $actions['duplicate'] = '<a href="' . wp_nonce_url('admin.php?action=bdt_duplicate_as_draft&post=' . $post->ID, basename(__FILE__), 'duplicate_nonce') . '" title="Duplicate this post" rel="permalink">' . esc_html_x("Duplicate Post", "Admin String", "ultimate-post-kit") . '</a>';
207 + $actions['duplicate'] = '<a href="' . wp_nonce_url('admin.php?action=ultimate_post_kit_duplicate_as_draft&post=' . $post->ID, 'upk_duplicate_post_' . $post->ID, 'duplicate_nonce') . '" title="Duplicate this post" rel="permalink">' . esc_html_x("Duplicate Post", "Admin String", "ultimate-post-kit") . '</a>';
180 208 } elseif ($post->post_type == 'page') {
181 - $actions['duplicate'] = '<a href="' . wp_nonce_url('admin.php?action=bdt_duplicate_as_draft&post=' . $post->ID, basename(__FILE__), 'duplicate_nonce') . '" title="Duplicate this page" rel="permalink">' . esc_html_x("Duplicate Page", "Admin String", "ultimate-post-kit") . '</a>';
209 + $actions['duplicate'] = '<a href="' . wp_nonce_url('admin.php?action=ultimate_post_kit_duplicate_as_draft&post=' . $post->ID, 'upk_duplicate_post_' . $post->ID, 'duplicate_nonce') . '" title="Duplicate this page" rel="permalink">' . esc_html_x("Duplicate Page", "Admin String", "ultimate-post-kit") . '</a>';
182 210 } elseif ($post->post_type == 'elementor_library') {
183 - $actions['duplicate'] = '<a href="' . wp_nonce_url('admin.php?action=bdt_duplicate_as_draft&post=' . $post->ID, basename(__FILE__), 'duplicate_nonce') . '" title="Duplicate this template" rel="permalink">' . esc_html_x("Duplicate Template", "Admin String", "ultimate-post-kit") . '</a>';
211 + $actions['duplicate'] = '<a href="' . wp_nonce_url('admin.php?action=ultimate_post_kit_duplicate_as_draft&post=' . $post->ID, 'upk_duplicate_post_' . $post->ID, 'duplicate_nonce') . '" title="Duplicate this template" rel="permalink">' . esc_html_x("Duplicate Template", "Admin String", "ultimate-post-kit") . '</a>';
184 212 }
185 213 }
186 214 return $actions;
187 215 }
@@ -187,6 +215,15 @@
187 215 }
188 216 }
189 217 endif;
190 218
191 -
219 +/**
220 + * Instantiate the namespaced class.
221 + *
222 + * The guard above and this statement must resolve to the same class. An
223 + * unqualified class_exists() string is always resolved against the global
224 + * namespace, so a sibling plugin declaring a global \BdThemes_Duplicator
225 + * (Live Copy Paste does) used to satisfy the old guard and skip the
226 + * declaration, while this line still asked for
227 + * UltimatePostKit\Includes\BdThemes_Duplicator -- a fatal error.
228 + */
192 229 new BdThemes_Duplicator();