PluginProbe
Tag, Category, and Taxonomy Manager – Autotagger Automatically Add Terms / trunk
Tag, Category, and Taxonomy Manager – Autotagger Automatically Add Terms vtrunk
3.53.0 3.52.0 3.51.0 3.50.0 3.45.0 3.38.0 3.4.0 3.4.1 3.4.2 3.4.3 3.4.4 3.4.5 3.40.0 3.40.1 3.41.0 3.42.0 3.43.0 3.44.0 3.5.0 3.5.1 3.5.2 3.5.3 3.6.0 3.6.1 3.6.2 All 177 releases
simple-tags / inc / terms-functions.php

terms-functions.php in Tag, Category, and Taxonomy Manager – Autotagger Automatically Add Terms trunk, at inc/terms-functions.php

353 lines 12.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * Generate a unique term slug should user decide to copy same term more than once.
5 *
6 * @param string $slug The initial slug to check
7 * @param string $taxonomy The taxonomy to check against
8 * @return string Unique slug
9 */
10 function taxopress_get_unique_term_slug($slug, $taxonomy)
11 {
12 $original_slug = $slug;
13 $count = 1;
14
15 while (term_exists($slug, $taxonomy)) {
16 $slug = $original_slug . '-' . $count;
17 $count++;
18 }
19
20 return $slug;
21 }
22
23 /**
24 * Check the current user's native capabilities for a taxonomy operation.
25 *
26 * @param string $taxonomy Taxonomy name.
27 * @param string|array $capabilities Taxonomy capability property or properties.
28 * @return bool
29 */
30 function taxopress_current_user_can_for_taxonomy($taxonomy, $capabilities)
31 {
32 $taxonomy_object = get_taxonomy(sanitize_key($taxonomy));
33 if (!$taxonomy_object) {
34 return false;
35 }
36
37 foreach ((array) $capabilities as $capability) {
38 if (
39 empty($taxonomy_object->cap->{$capability})
40 || !current_user_can($taxonomy_object->cap->{$capability})
41 ) {
42 return false;
43 }
44 }
45
46 return true;
47 }
48
49 /**
50 * Fetch post IDs for Terms screen bulk/row actions without hydrating WP_Post objects.
51 *
52 * @param array $args Optional WP_Query arguments.
53 * @return int[]
54 */
55 function taxopress_get_post_ids_for_terms_action($args = [])
56 {
57 $batch_size = 500;
58 $defaults = [
59 'post_type' => 'any',
60 'fields' => 'ids',
61 'no_found_rows' => true,
62 'update_post_meta_cache' => false,
63 'update_post_term_cache' => false,
64 ];
65
66 $post_ids = [];
67 $page = 1;
68
69 do {
70 $query_args = array_merge(
71 $defaults,
72 $args,
73 [
74 'posts_per_page' => $batch_size,
75 'paged' => $page,
76 ]
77 );
78 $batch_post_ids = get_posts($query_args);
79
80 foreach ($batch_post_ids as $post_id) {
81 $post_ids[] = (int) $post_id;
82 }
83
84 $page++;
85 } while (count($batch_post_ids) === $batch_size);
86
87 return $post_ids;
88 }
89
90 /**
91 * Preserve the current Terms screen filters when row actions redirect/reload.
92 *
93 * @param array $extra Extra query arguments.
94 * @return array
95 */
96 function taxopress_get_terms_screen_query_args($extra = [])
97 {
98 $preserve_keys = [
99 'terms_filter_post_type',
100 'terms_filter_taxonomy',
101 'taxonomy_type',
102 'taxopress_terms_taxonomy',
103 'taxopress_show_all',
104 'orderby',
105 'order',
106 'paged',
107 's',
108 ];
109
110 $query_args = ['page' => 'st_terms'];
111
112 foreach ($preserve_keys as $key) {
113 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Preserving current non-state-changing table filters.
114 if (isset($_REQUEST[$key]) && $_REQUEST[$key] !== '') {
115 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Preserving current non-state-changing table filters.
116 $query_args[$key] = sanitize_text_field(wp_unslash($_REQUEST[$key]));
117 }
118 }
119
120 return array_merge($query_args, $extra);
121 }
122
123 /**
124 * Keep a copied term near the original in saved manual order when that order exists.
125 *
126 * @param string $taxonomy Taxonomy slug.
127 * @param int $original_term_id Original term ID.
128 * @param int $copied_term_id Copied term ID.
129 */
130 function taxopress_place_copied_term_near_original($taxonomy, $original_term_id, $copied_term_id)
131 {
132 $taxonomy = sanitize_key($taxonomy);
133 $original_term_id = (int) $original_term_id;
134 $copied_term_id = (int) $copied_term_id;
135
136 if (empty($taxonomy) || $original_term_id <= 0 || $copied_term_id <= 0) {
137 return;
138 }
139
140 $option_name = 'taxopress_term_order_' . $taxonomy;
141 $custom_order = array_values(array_filter(array_map('intval', (array) get_option($option_name, []))));
142
143 if (empty($custom_order)) {
144 return;
145 }
146
147 $custom_order = array_values(array_diff($custom_order, [$copied_term_id]));
148 $original_position = array_search($original_term_id, $custom_order, true);
149
150 if ($original_position === false) {
151 $custom_order[] = $copied_term_id;
152 } else {
153 $taxonomy_settings = taxopress_get_all_edited_taxonomy_data();
154 $order_setting = isset($taxonomy_settings[$taxonomy]['order']) ? strtolower($taxonomy_settings[$taxonomy]['order']) : 'asc';
155 $insert_position = ($order_setting === 'desc') ? $original_position + 1 : $original_position;
156 array_splice($custom_order, $insert_position, 0, [$copied_term_id]);
157 }
158
159 update_option($option_name, array_values(array_unique($custom_order)));
160 }
161
162 /**
163 * Handle the save and deletion of terms data.
164 */
165 function taxopress_process_terms()
166 {
167
168 if (wp_doing_ajax()) {
169 return;
170 }
171
172 if (!is_admin()) {
173 return;
174 }
175
176 if (!current_user_can('simple_tags')) {
177 return;
178 }
179
180 if (empty($_GET)) {
181 return;
182 }
183
184 if (!isset($_GET['page'])) {
185 return;
186 }
187 if ('st_terms' !== $_GET['page']) {
188 return;
189 }
190
191 if (isset($_REQUEST['action']) && $_REQUEST['action'] === 'taxopress-delete-terms') {
192 $nonce = !empty($_REQUEST['_wpnonce']) ? sanitize_text_field(wp_unslash($_REQUEST['_wpnonce'])) : '';
193 if (wp_verify_nonce($nonce, 'terms-action-request-nonce') && isset($_REQUEST['taxopress_terms'])) {
194 $term = get_term(sanitize_text_field(wp_unslash($_REQUEST['taxopress_terms'])));
195 if (
196 !$term
197 || is_wp_error($term)
198 || !taxopress_current_user_can_for_taxonomy($term->taxonomy, 'delete_terms')
199 ) {
200 wp_die(esc_html__('Permission denied.', 'simple-tags'), '', ['response' => 403]);
201 }
202 wp_delete_term($term->term_id, $term->taxonomy);
203 }
204 add_action('admin_notices', "taxopress_term_delete_success_admin_notice");
205 add_filter('removable_query_args', 'taxopress_delete_terms_filter_removable_query_args');
206 }
207
208 if (isset($_REQUEST['action']) && $_REQUEST['action'] === 'taxopress-remove-from-posts') {
209 $nonce = !empty($_REQUEST['_wpnonce']) ? sanitize_text_field(wp_unslash($_REQUEST['_wpnonce'])) : '';
210 if (wp_verify_nonce($nonce, 'terms-action-request-nonce') && isset($_REQUEST['taxopress_terms'])) {
211 $term = get_term(sanitize_text_field(wp_unslash($_REQUEST['taxopress_terms'])));
212 if (
213 !$term
214 || is_wp_error($term)
215 || !taxopress_current_user_can_for_taxonomy($term->taxonomy, 'assign_terms')
216 ) {
217 wp_die(esc_html__('Permission denied.', 'simple-tags'), '', ['response' => 403]);
218 }
219 $args = array(
220 'post_type' => 'any',
221 // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query -- Necessary to filter posts by specific term for accurate removal
222 'tax_query' => array(
223 array(
224 'taxonomy' => $term->taxonomy,
225 'field' => 'id',
226 'terms' => $term->term_id
227 )
228 )
229 );
230 $post_ids = taxopress_get_post_ids_for_terms_action($args);
231 $counter = 0;
232 foreach ($post_ids as $post_id) {
233 if (!current_user_can('edit_post', $post_id)) {
234 continue;
235 }
236 $remove = wp_remove_object_terms($post_id, $term->term_id, $term->taxonomy);
237 if ($remove) {
238 clean_object_term_cache($post_id, $term->taxonomy);
239 clean_term_cache($term->term_id, $term->taxonomy);
240 $counter++;
241 }
242 }
243 }
244 add_action('admin_notices', "taxopress_term_posts_remov_success_admin_notice");
245 add_filter('removable_query_args', 'taxopress_delete_terms_filter_removable_query_args');
246 }
247
248 if (isset($_REQUEST['action']) && $_REQUEST['action'] === 'taxopress-copy-term') {
249 $nonce = !empty($_REQUEST['_wpnonce']) ? sanitize_text_field(wp_unslash($_REQUEST['_wpnonce'])) : '';
250 if (wp_verify_nonce($nonce, 'terms-action-request-nonce') && isset($_REQUEST['taxopress_terms'])) {
251 $term = get_term(sanitize_text_field(wp_unslash($_REQUEST['taxopress_terms'])));
252
253 if (
254 !$term
255 || is_wp_error($term)
256 || !taxopress_current_user_can_for_taxonomy(
257 $term->taxonomy,
258 ['edit_terms', 'assign_terms']
259 )
260 ) {
261 wp_die(esc_html__('Permission denied.', 'simple-tags'), '', ['response' => 403]);
262 }
263
264 $taxopress_term_name = $term->name . ' Copy';
265 $base_slug = $term->slug . '-copy';
266 $taxopress_term_slug = taxopress_get_unique_term_slug($base_slug, $term->taxonomy);
267 $taxopress_term_data = wp_insert_term($taxopress_term_name, $term->taxonomy, [
268 'slug' => $taxopress_term_slug,
269 'description' => $term->description,
270 'parent' => $term->parent,
271 ]);
272
273 if (!is_wp_error($taxopress_term_data)) {
274 $taxopress_term_id = $taxopress_term_data['term_id'];
275 taxopress_place_copied_term_near_original($term->taxonomy, $term->term_id, $taxopress_term_id);
276 $_REQUEST['taxopress_copied_term_id'] = $taxopress_term_id;
277 $_REQUEST['taxopress_original_term_id'] = $term->term_id;
278 $_REQUEST['taxopress_copied_taxonomy'] = $term->taxonomy;
279
280 $args = array(
281 'post_type' => 'any',
282 // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query -- Necessary to filter posts by specific term for accurate term copying
283 'tax_query' => array(
284 array(
285 'taxonomy' => $term->taxonomy,
286 'field' => 'id',
287 'terms' => $term->term_id
288 )
289 )
290 );
291 $post_ids = taxopress_get_post_ids_for_terms_action($args);
292
293 foreach ($post_ids as $post_id) {
294 if (!current_user_can('edit_post', $post_id)) {
295 continue;
296 }
297 wp_set_object_terms($post_id, $taxopress_term_id, $term->taxonomy, true);
298 }
299 }
300 }
301 add_action('admin_notices', "taxopress_term_copy_success_admin_notice");
302 add_filter('removable_query_args', 'taxopress_delete_terms_filter_removable_query_args');
303 }
304 }
305 add_action('admin_init', 'taxopress_process_terms', 8);
306
307 /**
308 * Successful term copy callback.
309 */
310 function taxopress_term_copy_success_admin_notice()
311 {
312 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
313 echo taxopress_admin_notices_helper(esc_html__('Term copied successfully.', 'simple-tags'), true);
314 }
315
316 /**
317 * Successful deleted callback.
318 */
319 function taxopress_term_delete_success_admin_notice()
320 {
321 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
322 echo taxopress_admin_notices_helper(esc_html__('Term deleted successfully.', 'simple-tags'), false);
323 }
324
325 /**
326 * Successful remove term from posts callback.
327 */
328 function taxopress_term_posts_remov_success_admin_notice()
329 {
330 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
331 echo taxopress_admin_notices_helper(esc_html__('Term removed from all posts successfully.', 'simple-tags'), true);
332 }
333
334 /**
335 * Filters the list of query arguments which get removed from admin area URLs in WordPress.
336 *
337 * @link https://core.trac.wordpress.org/ticket/23367
338 *
339 * @param string[] $args Array of removable query arguments.
340 * @return string[] Updated array of removable query arguments.
341 */
342 function taxopress_delete_terms_filter_removable_query_args(array $args)
343 {
344 return array_merge($args, [
345 'action',
346 'taxopress_terms',
347 '_wpnonce',
348 'taxopress_copied_term_id',
349 'taxopress_original_term_id',
350 'taxopress_copied_taxonomy',
351 ]);
352 }
353