PluginProbe
Tag, Category, and Taxonomy Manager – Autotagger Automatically Add Terms / 3.52.0
Tag, Category, and Taxonomy Manager – Autotagger Automatically Add Terms v3.52.0
3.54.0 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 All 178 releases
simple-tags / inc / class.admin.manage.php

class.admin.manage.php in Tag, Category, and Taxonomy Manager – Autotagger Automatically Add Terms 3.52.0, at inc/class.admin.manage.php

1,749 lines 80.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 // phpcs:disable Squiz.PHP.CommentedOutCode.Found,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.SlowDBQuery.slow_db_query_tax_query,WordPress.Security.NonceVerification.Missing,WordPress.Security.NonceVerification.Recommended,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized,WordPress.Security.ValidatedSanitizedInput.InputNotValidated -- Legacy TaxoPress file: keep behavior unchanged while documenting existing PHPCS exceptions.
4
5 class SimpleTags_Admin_Manage
6 {
7 public const MENU_SLUG = 'st_options';
8
9 // class instance
10 public static $instance;
11
12 /**
13 * Constructor
14 *
15 * @return void
16 * @author WebFactory Ltd
17 */
18 public function __construct()
19 {
20 add_filter('set-screen-option', [ __CLASS__, 'set_screen' ], 10, 3);
21 // Admin menu
22 add_action('admin_menu', array( $this, 'admin_menu' ));
23
24 // Register taxo, parent method...
25 SimpleTags_Admin::register_taxonomy();
26
27 // Javascript
28 add_action('admin_enqueue_scripts', array( __CLASS__, 'admin_enqueue_scripts' ), 11);
29
30 //load ajax
31 add_action('wp_ajax_taxopress_check_delete_terms', array( $this, 'handle_taxopress_check_delete_terms_ajax'));
32 add_action('wp_ajax_taxopress_autocomplete_terms', [ $this, 'handle_taxopress_autocomplete_terms']);
33 add_action('wp_ajax_taxopress_merge_terms_preflight', [$this, 'taxopress_merge_terms_preflight']);
34 add_action('wp_ajax_taxopress_merge_terms_batch', [$this, 'taxopress_merge_terms_batch']);
35 add_action('wp_ajax_taxopress_merge_suggestions', [$this, 'handle_taxopress_merge_suggestions']);
36 }
37
38 /**
39 * Init somes JS and CSS need for this feature
40 *
41 * @return void
42 * @author WebFactory Ltd
43 */
44 public static function admin_enqueue_scripts()
45 {
46 wp_register_script('st-helper-manage', STAGS_URL . '/assets/js/helper-manage.js', array( 'jquery' ), STAGS_VERSION);
47
48 // add JS for manage click tags
49 if (isset($_GET['page']) && $_GET['page'] == 'st_manage') {
50 wp_enqueue_script('st-helper-manage');
51 }
52 }
53
54 public static function set_screen($status, $option, $value)
55 {
56 return $value;
57 }
58
59 /**
60 * Add WP admin menu for Tags
61 *
62 * @return void
63 * @author WebFactory Ltd
64 */
65 public function admin_menu()
66 {
67 $hook = add_submenu_page(
68 self::MENU_SLUG,
69 __('TaxoPress: Manage Terms', 'simple-tags'),
70 __('Manage Terms', 'simple-tags'),
71 'simple_tags',
72 'st_manage',
73 array(
74 $this,
75 'page_manage_tags',
76 )
77 );
78 }
79
80 /**
81 * Method for build the page HTML manage tags
82 *
83 * @return void
84 * @author WebFactory Ltd
85 */
86 public function page_manage_tags()
87 {
88 $default_tab = '';
89 // Control Post data
90 if (isset($_POST['term_action'])) {
91 if (!current_user_can('simple_tags')) {
92 add_settings_error(__CLASS__, __CLASS__, esc_html__('Permission denied!', 'simple-tags'), 'error taxopress-notice');
93 } elseif (! wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['term_nonce'])), 'simpletags_admin')) { // Origination and intention
94 add_settings_error(__CLASS__, __CLASS__, esc_html__('Security problem. Try again.', 'simple-tags'), 'error taxopress-notice');
95 } elseif (! isset(SimpleTags_Admin::$taxonomy) || ! taxonomy_exists(SimpleTags_Admin::$taxonomy)) { // Valid taxo ?
96 add_settings_error(__CLASS__, __CLASS__, esc_html__('Missing valid taxonomy for work. Try again.', 'simple-tags'), 'error taxopress-notice');
97 } elseif ($_POST['term_action'] == 'renameterm') {
98 $taxonomy = isset($_POST['current_taxo']) ? sanitize_text_field(wp_unslash($_POST['current_taxo'])) : 'post_tag';
99 $post_type = isset($_POST['current_cpt']) ? sanitize_text_field(wp_unslash($_POST['current_cpt'])) : 'post';
100
101 SimpleTags_Admin::$taxonomy = $taxonomy;
102 SimpleTags_Admin::$post_type = $post_type;
103
104 $oldtag = isset($_POST['renameterm_old']) ? sanitize_text_field(wp_unslash($_POST['renameterm_old'])) : '';
105 $newtag = isset($_POST['renameterm_new']) ? sanitize_text_field(wp_unslash($_POST['renameterm_new'])) : '';
106 self::renameTerms($taxonomy, $oldtag, $newtag);
107 $default_tab = '.st-rename-terms';
108 } elseif ($_POST['term_action'] == 'mergeterm') {
109 $taxonomy = isset($_POST['current_taxo']) ? sanitize_text_field(wp_unslash($_POST['current_taxo'])) : 'post_tag';
110 $post_type = isset($_POST['current_cpt']) ? sanitize_text_field(wp_unslash($_POST['current_cpt'])) : 'post';
111
112 SimpleTags_Admin::$taxonomy = $taxonomy;
113 SimpleTags_Admin::$post_type = $post_type;
114
115 $oldtag = isset($_POST['renameterm_old']) ? sanitize_text_field(wp_unslash($_POST['renameterm_old'])) : '';
116 $newtag = isset($_POST['renameterm_new']) ? sanitize_text_field(wp_unslash($_POST['renameterm_new'])) : '';
117 $merge_type = isset($_POST['mergeterm_type']) ? sanitize_text_field(wp_unslash($_POST['mergeterm_type'])) : '';
118 self::mergeTerms($taxonomy, $oldtag, $newtag, $merge_type);
119 $default_tab = '.st-merge-terms';
120 } elseif ($_POST['term_action'] == 'addterm') {
121 $taxonomy = isset($_POST['current_taxo']) ? sanitize_text_field(wp_unslash($_POST['current_taxo'])) : 'post_tag';
122 $post_type = isset($_POST['current_cpt']) ? sanitize_text_field(wp_unslash($_POST['current_cpt'])) : 'post';
123
124 SimpleTags_Admin::$taxonomy = $taxonomy;
125 SimpleTags_Admin::$post_type = $post_type;
126
127 $oldtag = isset($_POST['addterm_match']) ? sanitize_text_field(wp_unslash($_POST['addterm_match'])) : '';
128 $newtag = isset($_POST['addterm_new']) ? sanitize_text_field(wp_unslash($_POST['addterm_new'])) : '';
129 self::addMatchTerms($taxonomy, $oldtag, $newtag);
130 $default_tab = '.st-add-terms';
131 } elseif ($_POST['term_action'] == 'removeterm') {
132 $taxonomy = isset($_POST['current_taxo']) ? sanitize_text_field(wp_unslash($_POST['current_taxo'])) : 'post_tag';
133 $post_type = isset($_POST['current_cpt']) ? sanitize_text_field(wp_unslash($_POST['current_cpt'])) : 'post';
134
135 SimpleTags_Admin::$taxonomy = $taxonomy;
136 SimpleTags_Admin::$post_type = $post_type;
137
138 $matchtag = isset($_POST['removeterm_match']) ? sanitize_text_field(wp_unslash($_POST['removeterm_match'])) : '';
139 $removetag = isset($_POST['remove_term']) ? sanitize_text_field(wp_unslash($_POST['remove_term'])) : '';
140 self::removeMatchTerms($taxonomy, $matchtag, $removetag);
141 $default_tab = '.st-remove-terms';
142 } elseif ($_POST['term_action'] == 'remove-rarelyterms') {
143 $taxonomy = isset($_POST['current_taxo']) ? sanitize_text_field(wp_unslash($_POST['current_taxo'])) : 'post_tag';
144 $post_type = isset($_POST['current_cpt']) ? sanitize_text_field(wp_unslash($_POST['current_cpt'])) : 'post';
145
146 SimpleTags_Admin::$taxonomy = $taxonomy;
147 SimpleTags_Admin::$post_type = $post_type;
148
149 self::removeRarelyUsed($taxonomy, (int) $_POST['number-rarely']);
150 $default_tab = '.st-delete-unuused-terms';
151 } /* elseif ( $_POST['term_action'] == 'editslug' ) {
152
153 $matchtag = (isset($_POST['tagname_match'])) ? $_POST['tagname_match'] : '';
154 $newslug = (isset($_POST['tagslug_new'])) ? $_POST['tagslug_new'] : '';
155 self::editTermSlug( SimpleTags_Admin::$taxonomy, $matchtag, $newslug );
156
157 }*/
158 }
159
160 if ($default_tab && !empty($default_tab)) {
161 //trigger default tab click on load
162 echo '<div class="load-st-default-tab" data-page="' . esc_attr($default_tab) . '"></div>';
163 }
164 $active_tab_slug = isset($_GET['tab']) ? sanitize_text_field(wp_unslash($_GET['tab'])) : 'add-terms';
165
166 // Default order
167 if (! isset($_GET['order'])) {
168 $_GET['order'] = 'name-asc';
169 }
170
171 settings_errors(__CLASS__); ?>
172 <div class="clear"></div>
173 <div class="taxopress-block-wrap">
174 <div class="wrap st_wrap tagcloudui st-manage-terms-page admin-settings wrap st_wrap">
175
176 <h1><?php _e('Manage Terms', 'simple-tags'); ?>
177 </h1>
178 <div class="taxopress-description"><?php esc_html_e('This feature allows you to manage your content terms by adding, renaming, merging and deleting unused terms.', 'simple-tags'); ?></div>
179
180 <div class="clear"></div>
181
182 <hr class="wp-header-end">
183 <div class="clear"></div>
184
185 <div id="col-container" class="wp-clearfix">
186 <div class="col-wrap">
187 <div class="form-wrap">
188 <ul class="simple-tags-nav-tab-wrapper">
189 <li class="nav-tab <?php echo $active_tab_slug === 'add-terms' ? 'nav-tab-active' : ''; ?>" data-page=".st-add-terms">
190 <?php echo esc_html__('Add terms', 'simple-tags'); ?>
191 </li>
192 <li class="nav-tab <?php echo $active_tab_slug === 'remove-terms' ? 'nav-tab-active' : ''; ?>" data-page=".st-remove-terms">
193 <?php echo esc_html__('Remove terms', 'simple-tags'); ?>
194 </li>
195 <li class="nav-tab <?php echo $active_tab_slug === 'rename-terms' ? 'nav-tab-active' : ''; ?>" data-page=".st-rename-terms">
196 <?php echo esc_html__('Rename terms', 'simple-tags'); ?>
197 </li>
198 <li class="nav-tab <?php echo $active_tab_slug === 'merge-terms' ? 'nav-tab-active' : ''; ?>" data-page=".st-merge-terms">
199 <?php echo esc_html__('Merge terms', 'simple-tags'); ?>
200 </li>
201 <li class="nav-tab <?php echo $active_tab_slug === 'delete-unuused-terms' ? 'nav-tab-active' : ''; ?>" data-page=".st-delete-unuused-terms">
202 <?php echo esc_html__('Delete unused terms', 'simple-tags'); ?>
203 </li>
204 </ul>
205 <div class="clear"></div>
206
207
208
209 <table class="form-table">
210
211 <tr valign="top" class="auto-terms-content st-add-terms" style="<?php echo $active_tab_slug === 'add-terms' ? '' : 'display:none;'; ?>">
212 <td>
213 <?php SimpleTags_Admin::tabSelectorTaxonomy('add-terms', 'st_manage'); ?>
214 <h2><?php _e('Add Terms', 'simple-tags'); ?></h2>
215 <p><?php printf(esc_html__('This feature lets you add one or more new terms to all %s which match any of the terms given.', 'simple-tags'), esc_html(SimpleTags_Admin::$post_type_name)); ?></p>
216 <p><?php printf(esc_html__('Terms will be added to all %s If no "Term(s) to match" is specified.', 'simple-tags'), esc_html(SimpleTags_Admin::$post_type_name)); ?></p>
217
218 <fieldset>
219 <form action="" method="post">
220 <input type="hidden" name="term_action" value="addterm" />
221 <input type="hidden" name="term_nonce" value="<?php echo esc_attr(wp_create_nonce('simpletags_admin')); ?>" />
222 <input type="hidden" name="current_tab" value="add-terms" />
223 <input type="hidden" name="current_taxo" value="<?php echo esc_attr(get_option('add-terms_taxo')); ?>" />
224 <input type="hidden" name="current_cpt" value="<?php echo esc_attr(get_option('add-terms_cpt')); ?>" />
225
226 <p class="terms-type-options">
227 <label>
228 <input type="radio" id="addterm_type" class="addterm_type_all_posts" name="addterm_type" value="all_posts" checked="checked">
229 <?php printf(esc_html__('Add terms to all %s.', 'simple-tags'), esc_html(SimpleTags_Admin::$post_type_name)); ?>
230 </label><br>
231 <label>
232 <input type="radio" id="addterm_type" class="addterm_type_matched_only" name="addterm_type" value="matched_only">
233 <?php _e('Add terms only to posts with specific terms attached.', 'simple-tags'); ?>
234 </label>
235 </p>
236
237 <p class="terms-to-maatch-input" style="display: none;">
238 <label for="addterm_match"><?php _e('Term(s) to match:', 'simple-tags'); ?></label><br />
239 <textarea type="text" class="autocomplete-input tag-cloud-input taxopress-expandable-textarea add-terms-autocomplete" id="addterm_match" name="addterm_match" size="80" data-tab="add-terms"
240 data-taxo="<?php echo esc_attr(get_option('add-terms_taxo')); ?>"></textarea>
241 </p>
242
243 <p>
244 <label for="addterm_new"><?php _e('Term(s) to add:', 'simple-tags'); ?></label><br />
245 <textarea type="text" class="autocomplete-input taxopress-expandable-textarea add-terms-autocomplete" id="addterm_new" name="addterm_new" size="80" data-tab="add-terms"
246 data-taxo="<?php echo esc_attr(get_option('add-terms_taxo')); ?>"></textarea>
247 </p>
248
249 <input class="button-primary" type="submit" name="Add" value="<?php _e('Add', 'simple-tags'); ?>" />
250 </form>
251 </fieldset>
252 </td>
253 </tr>
254
255 <tr valign="top" class="auto-terms-content st-remove-terms" style="<?php echo $active_tab_slug === 'remove-terms' ? '' : 'display:none;'; ?>">
256 <td>
257 <?php SimpleTags_Admin::tabSelectorTaxonomy('remove-terms', 'st_manage'); ?>
258 <h2><?php _e('Remove Terms', 'simple-tags'); ?></h2>
259 <p><?php printf(esc_html__('This feature lets you remove one or more terms from all %s which match any of the terms given.', 'simple-tags'), esc_html(SimpleTags_Admin::$post_type_name)); ?></p>
260 <p><?php printf(esc_html__('Terms will be removed from all %s If no "Term(s) to match" is specified.', 'simple-tags'), esc_html(SimpleTags_Admin::$post_type_name)); ?></p>
261
262 <fieldset>
263 <form action="" method="post">
264 <input type="hidden" name="term_action" value="removeterm" />
265 <input type="hidden" name="term_nonce" value="<?php echo esc_attr(wp_create_nonce('simpletags_admin')); ?>" />
266 <input type="hidden" name="current_tab" value="remove-terms" />
267 <input type="hidden" name="current_taxo" value="<?php echo esc_attr(get_option('remove-terms_taxo')); ?>" />
268 <input type="hidden" name="current_cpt" value="<?php echo esc_attr(get_option('remove-terms_cpt')); ?>" />
269
270 <p class="terms-type-options">
271 <label>
272 <input type="radio" id="removeterm_type" class="removeterm_type_all_posts" name="removeterm_type" value="all_posts" checked="checked">
273 <?php printf(esc_html__('Remove terms from all %s.', 'simple-tags'), esc_html(SimpleTags_Admin::$post_type_name)); ?>
274 </label><br>
275 <label>
276 <input type="radio" id="removeterm_type" class="removeterm_type_matched_only" name="removeterm_type" value="matched_only">
277 <?php _e('Remove terms only from posts with specific terms attached.', 'simple-tags'); ?>
278 </label>
279 </p>
280
281 <p class="removeterms-to-match-input" style="display: none;">
282 <label for="removeterm_match"><?php _e('Term(s) to match:', 'simple-tags'); ?></label><br />
283 <textarea type="text" class="autocomplete-input tag-cloud-input taxopress-expandable-textarea remove-terms-autocomplete" id="removeterm_match" name="removeterm_match" size="80" data-tab="remove-terms"
284 data-taxo="<?php echo esc_attr(get_option('remove-terms_taxo')); ?>"></textarea>
285 </p>
286
287 <p>
288 <label for="remove_term"><?php _e('Term(s) to remove:', 'simple-tags'); ?></label><br />
289 <textarea type="text" class="autocomplete-input taxopress-expandable-textarea remove-terms-autocomplete" id="remove_term" name="remove_term" size="80" data-tab="remove-terms"
290 data-taxo="<?php echo esc_attr(get_option('remove-terms_taxo')); ?>"></textarea>
291 </p>
292
293 <input class="button-primary" type="submit" name="Remove" value="<?php _e('Remove', 'simple-tags'); ?>" />
294 </form>
295 </fieldset>
296 </td>
297 </tr>
298
299 <tr valign="top" class="auto-terms-content st-rename-terms" style="<?php echo $active_tab_slug === 'rename-terms' ? '' : 'display:none;'; ?>">
300 <td>
301 <?php SimpleTags_Admin::tabSelectorTaxonomy('rename-terms', 'st_manage'); ?>
302 <h2><?php _e('Rename Terms', 'simple-tags'); ?></h2>
303 <p><?php _e('Enter the terms to rename and their new names.', 'simple-tags'); ?></p>
304
305 <fieldset>
306 <form action="" method="post">
307 <input type="hidden" name="term_action" value="renameterm" />
308 <input type="hidden" name="term_nonce" value="<?php echo esc_attr(wp_create_nonce('simpletags_admin')); ?>" />
309 <input type="hidden" name="current_tab" value="rename-terms" />
310 <input type="hidden" name="current_taxo" value="<?php echo esc_attr(get_option('rename-terms_taxo')); ?>" />
311 <input type="hidden" name="current_cpt" value="<?php echo esc_attr(get_option('rename-terms_cpt')); ?>" />
312 <p>
313 <label for="renameterm_old"><?php _e('Term(s) to rename:', 'simple-tags'); ?></label><br />
314 <textarea type="text" class="autocomplete-input tag-cloud-input taxopress-expandable-textarea rename-terms-autocomplete" id="renameterm_old" name="renameterm_old" size="80" data-taxo="<?php echo esc_attr(get_option('rename-terms_taxo')); ?>"></textarea>
315 </p>
316
317 <p>
318 <label for="renameterm_new"><?php _e('New term name(s):', 'simple-tags'); ?></label><br />
319 <textarea type="text" class="autocomplete-input taxopress-expandable-textarea rename-terms-autocomplete" id="renameterm_new" name="renameterm_new" size="80" data-taxo="<?php echo esc_attr(get_option('rename-terms_taxo')); ?>"></textarea>
320 </p>
321
322 <input class="button-primary" type="submit" name="rename" value="<?php _e('Rename', 'simple-tags'); ?>" />
323 </form>
324 </fieldset>
325 </td>
326 </tr>
327
328 <tr valign="top" class="auto-terms-content st-merge-terms" style="<?php echo $active_tab_slug === 'merge-terms' ? '' : 'display:none;'; ?>">
329 <td>
330 <?php SimpleTags_Admin::tabSelectorTaxonomy('merge-terms', 'st_manage'); ?>
331 <h2><?php _e('Merge Terms', 'simple-tags'); ?></h2>
332 <p><?php esc_html_e('This feature will delete existing terms and replace them with another term.', 'simple-tags'); ?></p>
333
334 <fieldset>
335 <form action="" method="post" class="merge-terms-form">
336 <input type="hidden" name="term_action" value="mergeterm" />
337 <input type="hidden" name="term_nonce" value="<?php echo esc_attr(wp_create_nonce('simpletags_admin')); ?>" />
338 <input type="hidden" name="current_tab" value="merge-terms" />
339 <input type="hidden" name="current_taxo" value="<?php echo esc_attr(get_option('merge-terms_taxo')); ?>" />
340 <input type="hidden" name="current_cpt" value="<?php echo esc_attr(get_option('merge-terms_cpt')); ?>" />
341
342 <p class="terms-type-options">
343 <label><input type="radio" id="mergeterm_type" class="mergeterm_type_different_name" name="mergeterm_type" value="different_name" checked="checked"><?php _e('Merge terms with different name.', 'simple-tags'); ?></label><br>
344 <label><input type="radio" id="mergeterm_type" class="mergeterm_type_same_name" name="mergeterm_type" value="same_name"><?php esc_html_e('Merge terms with same name.', 'simple-tags'); ?></label>
345 </p>
346
347 <p>
348 <label for="renameterm_old"><?php _e('Term(s) to merge.', 'simple-tags'); ?></label><br />
349 <textarea type="text" class="autocomplete-input tag-cloud-input taxopress-expandable-textarea merge-feature-autocomplete" id="mergeterm_old" name="renameterm_old" size="80" data-taxo="<?php echo esc_attr(get_option('merge-terms_taxo')); ?>"></textarea>
350 </p>
351
352 <p class="new_name_input">
353 <label for="renameterm_new"><?php _e('New term. The Old terms will be deleted and any posts assigned to the old terms will be re-assigned to this term.', 'simple-tags'); ?></label><br />
354 <textarea type="text" class="autocomplete-input taxopress-expandable-textarea merge-feature-autocomplete" id="mergeterm_new" name="renameterm_new" size="80" data-taxo="<?php echo esc_attr(get_option('merge-terms_taxo')); ?>"></textarea>
355 </p>
356
357 <input class="suggest-merge-terms" type="button" id="suggest-merge-terms" value="<?php _e('Suggest Terms to Merge', 'simple-tags'); ?>" />
358 <input class="button-primary" type="submit" name="merge" id="merge-terms" value="<?php _e('Merge Terms', 'simple-tags'); ?>" />
359 <div id="merge-progress" style="margin-top: 10px;"></div>
360 </form>
361 </fieldset>
362 </td>
363 </tr>
364
365 <tr valign="top" class="auto-terms-content st-delete-unuused-terms" style="<?php echo $active_tab_slug === 'delete-unuused-terms' ? '' : 'display:none;'; ?>">
366 <td>
367 <?php SimpleTags_Admin::tabSelectorTaxonomy('delete-unuused-terms', 'st_manage'); ?>
368 <h2><?php esc_html_e('Remove rarely used terms', 'simple-tags'); ?></h2>
369 <p><?php esc_html_e('This feature allows you to remove rarely used terms.', 'simple-tags'); ?></p>
370 <p><?php printf(esc_html__('If you choose 5, Taxopress will delete all terms attached to less than 5 %s.', 'simple-tags'), esc_html(SimpleTags_Admin::$post_type_name)); ?></p>
371
372 <fieldset>
373 <form action="" method="post">
374 <input type="hidden" name="term_action" value="remove-rarelyterms" />
375 <input type="hidden" name="term_nonce" value="<?php echo esc_attr(wp_create_nonce('simpletags_admin')); ?>" />
376 <input type="hidden" name="current_tab" value="delete-unuused-terms" />
377 <input type="hidden" name="current_taxo" value="<?php echo esc_attr(get_option('delete-unuused-terms_taxo')); ?>" />
378 <input type="hidden" name="current_cpt" value="<?php echo esc_attr(get_option('delete-unuused-terms_cpt')); ?>" />
379
380 <p>
381 <label for="number-delete"><?php _e('Minimum number of uses for each term:', 'simple-tags'); ?></label><br />
382 <select name="number-rarely" id="number-delete">
383 <?php for ($i = 1; $i <= 100; $i++) : ?>
384 <option value="<?php echo esc_attr($i); ?>"><?php echo esc_html($i); ?></option>
385 <?php endfor; ?>
386 </select>
387 </p>
388
389 <label for="check-terms"><?php _e('Check how many terms will be deleted:', 'simple-tags'); ?></label><br />
390 <input style="margin-top: 2px;" id="check-terms-btn" class="button-primary" type="submit" name="Check" value="<?php esc_attr_e('Check Terms', 'simple-tags'); ?>" />
391 <div id="terms-feedback"></div>
392
393 <label for="terms-delete"><?php _e('Delete rarely used terms:', 'simple-tags'); ?></label><br />
394 <input style="margin-top: 2px;" class="button-secondary delete-unused-term" type="submit" name="Delete" value="<?php esc_attr_e('Delete Terms', 'simple-tags'); ?>" />
395 </form>
396 </fieldset>
397 </td>
398 </tr>
399
400 <?php /*
401 <tr valign="top">
402 <th scope="row"><strong><?php _e('Edit Term Slug', 'simple-tags'); ?></strong></th>
403 <td>
404 <p><?php _e('Enter the term name to edit and its new slug. <a href="http://codex.wordpress.org/Glossary#Slug">Slug definition</a>', 'simple-tags'); ?></p>
405
406 <fieldset>
407 <form action="" method="post">
408 <input type="hidden" name="taxo" value="<?php echo esc_attr(SimpleTags_Admin::$taxonomy); ?>" />
409 <input type="hidden" name="cpt" value="<?php echo esc_attr(SimpleTags_Admin::$post_type); ?>" />
410
411 <input type="hidden" name="term_action" value="editslug" />
412 <input type="hidden" name="term_nonce" value="<?php echo wp_create_nonce('simpletags_admin'); ?>" />
413
414 <p>
415 <label for="tagname_match"><?php _e('Term(s) to match:', 'simple-tags'); ?></label>
416 <br />
417 <input type="text" class="autocomplete-input" id="tagname_match" name="tagname_match" size="80" />
418 </p>
419
420 <p>
421 <label for="tagslug_new"><?php _e('Slug(s) to set:', 'simple-tags'); ?></label>
422 <br />
423 <input type="text" class="autocomplete-input" id="tagslug_new" name="tagslug_new" size="80" />
424 </p>
425
426 <input class="button-primary" type="submit" name="edit" value="<?php _e('Edit', 'simple-tags'); ?>" />
427 </form>
428 </fieldset>
429 </td>
430 </tr>
431 */
432 ?>
433
434 </table>
435
436 <div class="remodal" data-remodal-id="taxopress-modal-merge-warning"
437 data-remodal-options="hashTracking: false, closeOnOutsideClick: false">
438 <div class="taxopress-merge-warning-title">
439 <?php echo esc_html__('Multiple merge suggestions selected', 'simple-tags'); ?>
440 </div>
441 <div id="taxopress-modal-merge-warning-content">
442 <?php echo esc_html__(
443 'You have selected multiple merge suggestions. All selected terms will be merged into a single new term.',
444 'simple-tags'
445 ); ?>
446 </div>
447 <br>
448 <button id="taxopress-merge-warning-cancel" data-remodal-action="cancel" class="button button-secondary remodal-cancel">
449 <?php echo esc_html__('Cancel', 'simple-tags'); ?>
450 </button>
451 <button id="taxopress-merge-warning-confirm" class="button button-primary">
452 <?php echo esc_html__('Continue', 'simple-tags'); ?>
453 </button>
454 </div>
455
456
457
458 </div>
459 </div>
460
461
462 <div class="clear"></div>
463
464 </div>
465 </div>
466 <div class="taxopress-right-sidebar admin-settings-sidebar">
467 <?php do_action('taxopress_admin_after_sidebar'); ?>
468 </div>
469
470 </div>
471
472
473 <?php SimpleTags_Admin::printAdminFooter(); ?>
474
475
476
477 <?php
478 do_action('simpletags-manage_terms', SimpleTags_Admin::$taxonomy);
479 }
480
481 /**
482 * Method to merge tags
483 *
484 * @param string $taxonomy
485 * @param string $old
486 * @param string $new
487 *
488 * @return boolean
489 * @author olatechpro
490 */
491 public static function mergeTerms($taxonomy = 'post_tag', $old = '', $new = '', $merge_type = '')
492 {
493 // Helper function to extract term name (ignoring slug in brackets)
494 $extractTermName = function ($term) {
495 return trim(preg_replace('/\s*\(.*?\)$/', '', $term));
496 };
497
498 if ($merge_type === 'same_name') {
499 $old_terms = explode(',', $old);
500 $old_terms = array_map($extractTermName, $old_terms);
501 $old_terms = array_filter($old_terms, '_delete_empty_element');
502 $old_terms = array_values(array_unique(array_map('sanitize_text_field', $old_terms)));
503 $retained_slug_param = isset($_POST['retained_slug']) ? sanitize_title(wp_unslash($_POST['retained_slug'])) : '';
504
505 if (empty($old_terms)) {
506 add_settings_error(__CLASS__, __CLASS__, esc_html__('No terms provided for merging!', 'simple-tags'), 'error taxopress-notice');
507 return false;
508 }
509
510 $terms = [];
511 $terms_by_id = [];
512 foreach ($old_terms as $term_name) {
513 $term_objects = get_terms([
514 'taxonomy' => $taxonomy,
515 'name' => sanitize_text_field($term_name),
516 'hide_empty' => false,
517 ]);
518
519 if (is_array($term_objects) && count($term_objects) > 1) {
520 foreach ($term_objects as $term_object) {
521 $terms_by_id[(int) $term_object->term_id] = $term_object;
522 }
523 }
524 }
525
526 $terms = array_values($terms_by_id);
527
528 if (empty($terms)) {
529 add_settings_error(__CLASS__, __CLASS__, esc_html__('No terms with the same name found.', 'simple-tags'), 'error taxopress-notice');
530 return false;
531 }
532
533 $sort_term_name = reset($old_terms);
534 usort($terms, function ($a, $b) use ($sort_term_name, $retained_slug_param) {
535 // If a retained slug is provided, sort that term to the top
536 if ($retained_slug_param) {
537 if ($a->slug === $retained_slug_param) {
538 return -1;
539 }
540 if ($b->slug === $retained_slug_param) {
541 return 1;
542 }
543 }
544 // Score based on similarity to term name
545 similar_text($sort_term_name, $a->slug, $similarity_a);
546 similar_text($sort_term_name, $b->slug, $similarity_b);
547
548 if ($similarity_a === $similarity_b) {
549 // If similarity is the same, sort by length (shortest first)
550 return strlen($a->slug) - strlen($b->slug);
551 }
552
553 // Otherwise, sort by similarity (higher first)
554 return $similarity_b - $similarity_a;
555 });
556
557 // Retain the term with the highest similarity and shortest slug (first in sorted array)
558 $retained_term = $terms[0];
559 $retained_slug = $retained_term->slug;
560 $retained_id = $retained_term->term_id;
561
562 // Collect terms to delete
563 $terms_to_delete = array_filter($terms, function ($term) use ($retained_id) {
564 return $term->term_id !== $retained_id;
565 });
566
567 // Reassign objects and delete old terms (existing logic)
568 $terms_id = array_map(function ($term) {
569 return $term->term_id;
570 }, $terms_to_delete);
571
572 $objects_id = get_objects_in_term($terms_id, $taxonomy, ['fields' => 'ids']);
573 foreach ($objects_id as $object_id) {
574 // Check if the object already has the term assigned
575 $current_terms = wp_get_object_terms($object_id, $taxonomy, ['fields' => 'ids']);
576 if (!in_array($retained_id, $current_terms)) {
577 wp_set_object_terms($object_id, $retained_slug, $taxonomy, true);
578 }
579 }
580
581 foreach ($terms_to_delete as $term) {
582 wp_delete_term($term->term_id, $taxonomy);
583 }
584
585 // Clean caches
586 clean_object_term_cache($objects_id, $taxonomy);
587 clean_term_cache($terms_id, $taxonomy);
588
589 add_settings_error(__CLASS__, __CLASS__, sprintf(esc_html__('Merged term(s) with the same name "%s". %d posts updated.', 'simple-tags'), $retained_term->name, count($objects_id)), 'updated taxopress-notice');
590 return [
591 'success' => true,
592 'retained_slug' => $retained_slug,
593 'posts_updated' => count($objects_id),
594 'post_ids' => $objects_id,
595 'terms_merged' => count($terms_to_delete) + 1,
596 ];
597 } else {
598 if (trim(str_replace(',', '', stripslashes($new))) == '' && $merge_type !== 'same_name') {
599 add_settings_error(__CLASS__, __CLASS__, esc_html__('No new term specified!', 'simple-tags'), 'error taxopress-notice');
600
601 return false;
602 }
603
604 // String to array
605 $old_terms = explode(',', $old);
606 $new_terms = explode(',', $new);
607
608 $old_terms = array_map($extractTermName, $old_terms);
609 $new_terms = array_map($extractTermName, $new_terms);
610
611 // Remove empty element and trim
612 $old_terms = array_filter($old_terms, '_delete_empty_element');
613 $new_terms = array_filter($new_terms, '_delete_empty_element');
614 $old_terms = array_values(array_unique(array_map('sanitize_text_field', $old_terms)));
615 $new_terms = array_values(array_unique(array_map('sanitize_text_field', $new_terms)));
616 $common_elements = array_intersect($old_terms, $new_terms);
617
618 // If old/new tag are empty => exit !
619 if (empty($old_terms) || empty($new_terms)) {
620 add_settings_error(__CLASS__, __CLASS__, esc_html__('No new/old valid term specified!', 'simple-tags'), 'error taxopress-notice');
621
622 return false;
623 }
624
625 if (!empty($common_elements) && $merge_type !== 'different_name') {
626 add_settings_error(__CLASS__, __CLASS__, esc_html__('Term to merge and New Term must not contain same term.', 'simple-tags'), 'error taxopress-notice');
627
628 return false;
629 }
630
631 $counter = 0;
632 $unique_objects = [];
633 $terms_id = [];
634 if (count($new_terms) == 1) { // Merge
635 // Set new tag
636 $new_tag = sanitize_text_field($new_terms[0]);
637 if (empty($new_tag)) {
638 add_settings_error(__CLASS__, __CLASS__, esc_html__('No valid new term.', 'simple-tags'), 'error taxopress-notice');
639
640 return false;
641 }
642
643 // Ensure the new term exists or create it
644 $new_term = get_term_by('name', $new_tag, $taxonomy);
645 if (!$new_term) {
646 $new_term_info = wp_insert_term($new_tag, $taxonomy);
647 if (is_wp_error($new_term_info)) {
648 add_settings_error(__CLASS__, __CLASS__, esc_html__('Failed to create the new term.', 'simple-tags'), 'error taxopress-notice');
649 return false;
650 }
651 $new_term_id = $new_term_info['term_id'];
652 $new_term_slug = isset($new_term_info['slug']) ? $new_term_info['slug'] : sanitize_title($new_tag);
653 } else {
654 $new_term_id = $new_term->term_id;
655 $new_term_slug = $new_term->slug;
656 }
657
658 // Get terms ID from old terms names
659 $terms_id = array();
660 $found_terms = array();
661 foreach ((array) $old_terms as $old_tag) {
662 $term = get_term_by('name', addslashes(sanitize_text_field($old_tag)), $taxonomy);
663 if ($term) {
664 $terms_id[] = (int) $term->term_id;
665 $found_terms[] = $term->name;
666 }
667 }
668
669 if (empty($terms_id)) {
670 add_settings_error(__CLASS__, __CLASS__, esc_html__('No matching terms found to merge.', 'simple-tags'), 'error taxopress-notice');
671 return false;
672 }
673
674 // Get objects from terms ID
675 $objects_id = get_objects_in_term($terms_id, $taxonomy, ['fields' => 'ids']);
676
677 // Use a set to track unique post IDs
678 $unique_objects = [];
679
680 // Assign the new term to all posts associated with the old terms
681 foreach ((array) $objects_id as $object_id) {
682 // Check if the object already has the term assigned
683 $current_terms = wp_get_object_terms($object_id, $taxonomy, ['fields' => 'ids']);
684 if (!in_array($new_term_id, $current_terms)) {
685 wp_set_object_terms($object_id, $new_term_slug, $taxonomy, true);
686 }
687 $unique_objects[$object_id] = true; // Add to unique set
688 }
689
690 // Count unique posts
691 $counter = count($unique_objects);
692
693 // Delete old terms
694 foreach ((array) $terms_id as $term_id) {
695 wp_delete_term($term_id, $taxonomy);
696 }
697
698 // Test if term is also a category
699 /*
700 if ( is_term($new_tag, 'category') ) {
701 // Edit the slug to use the new term
702 self::editTermSlug( $new_tag, sanitize_title($new_tag) );
703 }
704 */
705
706 // Clean cache
707 clean_object_term_cache($objects_id, $taxonomy);
708 clean_term_cache($terms_id, $taxonomy);
709
710
711 if (empty($found_terms)) {
712 add_settings_error(__CLASS__, __CLASS__, esc_html__('No terms were merged (terms may not exist).', 'simple-tags'), 'error taxopress-notice');
713 } else {
714 $message = sprintf(
715 esc_html__('Successfully merged %1$d term(s) "%2$s" to "%3$s". %4$s posts updated.', 'simple-tags'),
716 count($found_terms),
717 implode(', ', $found_terms),
718 rtrim($new, ','),
719 $counter
720 );
721 add_settings_error(__CLASS__, __CLASS__, $message, 'updated taxopress-notice');
722 }
723 } else { // Error
724 add_settings_error(__CLASS__, __CLASS__, sprintf(esc_html__('Error. You need to enter a single term to merge to in new term name !', 'simple-tags'), $old), 'error taxopress-notice');
725 return false;
726 }
727
728 return [
729 'success' => true,
730 'merged_into' => $new_tag,
731 'posts_updated' => $counter,
732 'post_ids' => array_keys($unique_objects),
733 'terms_merged' => count($terms_id) + 1,
734 ];
735 }
736 }
737
738 /**
739 * Find terms with same names but different slugs
740 */
741 public static function getSameNameMergeSuggestions($taxonomy = 'post_tag')
742 {
743 global $wpdb;
744
745 $query = $wpdb->prepare("
746 SELECT t.name, COUNT(*) as count, GROUP_CONCAT(CONCAT(t.name, ' (', t.slug, ')') SEPARATOR ', ') as terms
747 FROM {$wpdb->terms} t
748 INNER JOIN {$wpdb->term_taxonomy} tt ON t.term_id = tt.term_id
749 WHERE tt.taxonomy = %s
750 GROUP BY t.name
751 HAVING COUNT(*) > 1
752 ORDER BY count DESC, t.name ASC
753 ", $taxonomy);
754
755 $results = $wpdb->get_results($query);
756
757 $show_slug = (int) SimpleTags_Plugin::get_option_value('enable_merge_terms_slug');
758
759 if (!$show_slug) {
760 foreach ($results as &$result) {
761 $result->terms = preg_replace('/\s*\([^)]+\)/', '', $result->terms);
762 }
763 }
764
765 return $results;
766 }
767
768 /**
769 * Find terms that might be candidates for merging based on similarity
770 */
771 public static function getDifferentNameMergeSuggestions($taxonomy = 'post_tag', $limit = 50)
772 {
773 global $wpdb;
774
775 $limit = min(max((int) $limit, 1), 50);
776
777 $terms = get_terms([
778 'taxonomy' => $taxonomy,
779 'hide_empty' => false,
780 'number' => $limit * 2,
781 'update_term_meta_cache' => false,
782 ]);
783
784 $show_slug = (int) SimpleTags_Plugin::get_option_value('enable_merge_terms_slug');
785
786 $suggestions = [];
787
788 foreach ($terms as $i => $term1) {
789 foreach ($terms as $j => $term2) {
790 if ($i >= $j) {
791 continue;
792 }
793
794 $similarity_score = 0;
795 $reasons = [];
796
797 // 1. Levenshtein distance (for typos)
798 $term1_name = strtolower($term1->name);
799 $term2_name = strtolower($term2->name);
800 if (abs(strlen($term1_name) - strlen($term2_name)) <= 2 && max(strlen($term1_name), strlen($term2_name)) <= 100) {
801 $levenshtein = levenshtein($term1_name, $term2_name);
802 if ($levenshtein <= 2 && $levenshtein > 0) {
803 $similarity_score += 40;
804 $reasons[] = esc_html__('Similar spelling', 'simple-tags');
805 }
806 }
807
808 // 2. Soundex (phonetic similarity)
809 if (soundex($term1->name) === soundex($term2->name)) {
810 $similarity_score += 30;
811 $reasons[] = esc_html__('Sounds similar', 'simple-tags');
812 }
813
814 // 3. Common words/partial matches
815 $words1 = explode(' ', strtolower($term1->name));
816 $words2 = explode(' ', strtolower($term2->name));
817 $common_words = array_intersect($words1, $words2);
818 if (!empty($common_words)) {
819 $similarity_score += count($common_words) * 15;
820 $reasons[] = sprintf(esc_html__('Shared words: %s', 'simple-tags'), implode(', ', $common_words));
821 }
822
823 // 4. Prefix/suffix similarity
824 if (strpos($term1->name, $term2->name) !== false || strpos($term2->name, $term1->name) !== false) {
825 $similarity_score += 25;
826 $reasons[] = esc_html__('One contains the other', 'simple-tags');
827 }
828
829 if ($similarity_score >= 30) {
830 $suggested_name = '';
831
832 // Get clean term names for comparison
833 $term1_clean = strtolower(trim($term1->name));
834 $term2_clean = strtolower(trim($term2->name));
835
836 if (!empty($common_words)) {
837 $suggested_name = ucfirst(implode(' ', $common_words));
838 } else {
839 // Use the shorter term as base
840 $suggested_name = strlen($term1->name) <= strlen($term2->name) ? $term1->name : $term2->name;
841 }
842
843 // let's make sure suggested name is different from both original terms
844 $suggested_clean = strtolower(trim($suggested_name));
845 if ($suggested_clean === $term1_clean || $suggested_clean === $term2_clean) {
846 if (!empty($common_words)) {
847 $suggested_name = ucfirst($common_words[0]);
848 } else {
849 $popular_term = ($term1->count >= $term2->count) ? $term1 : $term2;
850 $suggested_name = $popular_term->name;
851 }
852
853 $suggested_clean = strtolower(trim($suggested_name));
854 }
855
856 $term1_display = $show_slug ? $term1->name . ' (' . $term1->slug . ')' : $term1->name;
857 $term2_display = $show_slug ? $term2->name . ' (' . $term2->slug . ')' : $term2->name;
858
859 $suggestions[] = [
860 'term1' => $term1_display,
861 'term2' => $term2_display,
862 'score' => $similarity_score,
863 'reasons' => implode(', ', $reasons),
864 'suggested_name' => $suggested_name
865 ];
866 }
867 }
868 }
869
870 usort($suggestions, function ($a, $b) {
871 return $b['score'] - $a['score'];
872 });
873
874 return array_slice($suggestions, 0, $limit);
875 }
876
877 /**
878 * AJAX handler for merge suggestions
879 */
880 public function handle_taxopress_merge_suggestions()
881 {
882 if (!current_user_can('simple_tags')) {
883 wp_send_json_error('Permission denied');
884 }
885
886 if (!wp_verify_nonce(wp_unslash($_POST['nonce']), 'simpletags_admin')) {
887 wp_send_json_error('Invalid nonce');
888 }
889
890 $taxonomy = get_option('merge-terms_taxo', 'post_tag');
891 $merge_type = sanitize_text_field(wp_unslash($_POST['merge_type']));
892
893 if ($merge_type === 'same_name') {
894 $suggestions = self::getSameNameMergeSuggestions($taxonomy);
895 wp_send_json_success([
896 'type' => 'same_name',
897 'suggestions' => $suggestions
898 ]);
899 } else {
900 $suggestions = self::getDifferentNameMergeSuggestions($taxonomy);
901 wp_send_json_success([
902 'type' => 'different_name',
903 'suggestions' => $suggestions
904 ]);
905 }
906 }
907
908 private static function get_merge_preflight_term_ids($taxonomy, $old_terms_input, $merge_type)
909 {
910 $extractTermName = function ($term) {
911 return trim(preg_replace('/\s*\(.*?\)$/', '', $term));
912 };
913
914 $term_ids = [];
915
916 foreach ($old_terms_input as $term_name) {
917 $term_name_clean = sanitize_text_field($extractTermName($term_name));
918
919 if (empty($term_name_clean)) {
920 continue;
921 }
922
923 if ($merge_type === 'same_name') {
924 $matching_terms = get_terms([
925 'taxonomy' => $taxonomy,
926 'name' => $term_name_clean,
927 'hide_empty' => false,
928 'fields' => 'ids',
929 'update_term_meta_cache' => false,
930 ]);
931
932 if (!is_wp_error($matching_terms) && !empty($matching_terms)) {
933 foreach ($matching_terms as $term_id) {
934 $term_ids[] = (int) $term_id;
935 }
936
937 continue;
938 }
939 }
940
941 $term = get_term_by('name', $term_name_clean, $taxonomy);
942 if (!$term || is_wp_error($term)) {
943 $term = get_term_by('slug', sanitize_title($term_name_clean), $taxonomy);
944 }
945
946 if ($term && !is_wp_error($term)) {
947 $term_ids[] = (int) $term->term_id;
948 }
949 }
950
951 return array_values(array_unique(array_filter($term_ids)));
952 }
953
954 private static function count_objects_for_merge_terms($taxonomy, $term_ids)
955 {
956 global $wpdb;
957
958 if (empty($term_ids)) {
959 return 0;
960 }
961
962 $term_ids = array_values(array_unique(array_map('intval', $term_ids)));
963 $cache_key = 'merge_preflight_' . md5($taxonomy . ':' . implode(',', $term_ids));
964 $cached_count = wp_cache_get($cache_key, 'taxopress_terms');
965
966 if ($cached_count !== false) {
967 return (int) $cached_count;
968 }
969
970 $term_id_placeholders = implode(',', array_fill(0, count($term_ids), '%d'));
971 // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
972 $term_taxonomy_query = $wpdb->prepare(
973 "SELECT term_taxonomy_id FROM {$wpdb->term_taxonomy} WHERE taxonomy = %s AND term_id IN ({$term_id_placeholders})",
974 array_merge([$taxonomy], $term_ids)
975 );
976
977 // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
978 $term_taxonomy_ids = $wpdb->get_col($term_taxonomy_query);
979
980 if (empty($term_taxonomy_ids)) {
981 wp_cache_set($cache_key, 0, 'taxopress_terms', MINUTE_IN_SECONDS);
982 return 0;
983 }
984
985 $term_taxonomy_ids = array_values(array_unique(array_map('intval', $term_taxonomy_ids)));
986 $term_taxonomy_id_placeholders = implode(',', array_fill(0, count($term_taxonomy_ids), '%d'));
987 $object_count_query = $wpdb->prepare(
988 // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare
989 "SELECT COUNT(DISTINCT object_id) FROM {$wpdb->term_relationships} WHERE term_taxonomy_id IN ({$term_taxonomy_id_placeholders})",
990 $term_taxonomy_ids
991 );
992
993 // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
994 $object_count = (int) $wpdb->get_var($object_count_query);
995
996 wp_cache_set($cache_key, $object_count, 'taxopress_terms', MINUTE_IN_SECONDS);
997
998 return $object_count;
999 }
1000
1001 public static function taxopress_merge_terms_preflight()
1002 {
1003 $nonce = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash($_POST['nonce'])) : '';
1004 if (empty($nonce) || !wp_verify_nonce($nonce, 'st-admin-js')) {
1005 wp_send_json_error(['message' => __('Security check failed.', 'simple-tags')], 403);
1006 wp_die();
1007 }
1008
1009 if (!current_user_can('simple_tags')) {
1010 wp_send_json_error(['message' => __('Permission denied.', 'simple-tags')], 403);
1011 wp_die();
1012 }
1013
1014 $taxonomy = isset($_POST['taxonomy']) ? sanitize_text_field(wp_unslash($_POST['taxonomy'])) : '';
1015 if (empty($taxonomy) || !taxonomy_exists($taxonomy)) {
1016 wp_send_json_error(['message' => __('Invalid taxonomy.', 'simple-tags')], 400);
1017 wp_die();
1018 }
1019
1020 $merge_type = isset($_POST['merge_type']) ? sanitize_text_field(wp_unslash($_POST['merge_type'])) : 'different_name';
1021 $old_terms_input = isset($_POST['old_terms']) ? array_map('sanitize_text_field', (array) wp_unslash($_POST['old_terms'])) : [];
1022
1023 $term_ids = self::get_merge_preflight_term_ids($taxonomy, $old_terms_input, $merge_type);
1024 $affected_posts_count = self::count_objects_for_merge_terms($taxonomy, $term_ids);
1025 $workload = count($term_ids) * $affected_posts_count;
1026 $ajax_workload_threshold = (int) apply_filters('taxopress_merge_terms_ajax_workload_threshold', 20);
1027
1028 wp_send_json_success([
1029 'terms_count' => count($term_ids),
1030 'affected_posts_count' => $affected_posts_count,
1031 'workload' => $workload,
1032 'use_ajax' => $workload > $ajax_workload_threshold,
1033 ]);
1034 }
1035
1036 public static function taxopress_merge_terms_batch()
1037 {
1038 $nonce = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash($_POST['nonce'])) : '';
1039 if (empty($nonce) || !wp_verify_nonce($nonce, 'st-admin-js')) {
1040 wp_send_json_error(['message' => __('Security check failed.', 'simple-tags')], 403);
1041 wp_die();
1042 }
1043
1044 if (!current_user_can('simple_tags')) {
1045 wp_send_json_error(['message' => __('Permission denied.', 'simple-tags')], 403);
1046 wp_die();
1047 }
1048
1049 $taxonomy = isset($_POST['taxonomy']) ? sanitize_text_field(wp_unslash($_POST['taxonomy'])) : '';
1050 $new_term = isset($_POST['new_term']) ? sanitize_text_field(wp_unslash($_POST['new_term'])) : '';
1051 $merge_type = isset($_POST['merge_type']) ? sanitize_text_field(wp_unslash($_POST['merge_type'])) : 'different_name';
1052 $old_terms_input = isset($_POST['old_terms']) ? array_map('sanitize_text_field', (array) wp_unslash($_POST['old_terms'])) : [];
1053
1054 $extractTermName = function ($term) {
1055 return trim(preg_replace('/\s*\(.*?\)$/', '', $term));
1056 };
1057
1058 $old_terms = [];
1059
1060 foreach ($old_terms_input as $term_name) {
1061 $term_name_clean = $extractTermName($term_name);
1062 $term = get_term_by('name', $term_name_clean, $taxonomy);
1063 if (!$term || is_wp_error($term)) {
1064 $term = get_term_by('slug', sanitize_title($term_name_clean), $taxonomy);
1065 }
1066 if ($term && !is_wp_error($term)) {
1067 $old_terms[] = $term->name;
1068 }
1069 }
1070
1071 $old_terms = array_values(array_unique($old_terms));
1072
1073 if (empty($old_terms)) {
1074 wp_send_json_error([
1075 'message' => __('Please enter a valid term. Merge cancelled.', 'simple-tags')
1076 ]);
1077 wp_die();
1078 }
1079
1080 if ($merge_type === 'different_name' && !empty($new_term)) {
1081 $old_terms = array_values(array_filter($old_terms, function ($term_name) use ($new_term) {
1082 return strtolower(trim($term_name)) !== strtolower(trim($new_term));
1083 }));
1084 }
1085
1086
1087 // Execute the merge
1088 $result = SimpleTags_Admin_Manage::mergeTerms($taxonomy, implode(',', $old_terms), $new_term, $merge_type);
1089
1090 if ($result === true || (is_array($result) && !empty($result['success']))) {
1091 $response = ['message' => __('Merge successful', 'simple-tags')];
1092
1093 if (is_array($result)) {
1094 $response = array_merge($response, $result);
1095 }
1096
1097 wp_send_json_success($response);
1098 } else {
1099 global $wp_settings_errors;
1100 $errors = [];
1101
1102 if (!empty($wp_settings_errors)) {
1103 foreach ($wp_settings_errors as $error) {
1104 if (!empty($error['message'])) {
1105 $errors[] = $error['message'];
1106 }
1107 }
1108 }
1109
1110 if (is_wp_error($result)) {
1111 $errors[] = $result->get_error_message();
1112 } elseif (is_array($result) && isset($result['message'])) {
1113 $errors[] = $result['message'];
1114 }
1115
1116 wp_send_json_error(['message' => implode(' ', $errors)]);
1117 }
1118
1119 wp_die();
1120 }
1121
1122 /**
1123 * Method for remove tags
1124 *
1125 * @param string $taxonomy
1126 * @param string $post_type
1127 * @param string $tag
1128 *
1129 * @return boolean
1130 * @author WebFactory Ltd
1131 */
1132 public static function removeTerms($taxonomy = 'post_tag', $post_type = 'posts', $new = '')
1133 {
1134 if (trim(str_replace(',', '', stripslashes($new))) == '') {
1135 add_settings_error(__CLASS__, __CLASS__, esc_html__('No term specified!', 'simple-tags'), 'error taxopress-notice');
1136
1137 return false;
1138 }
1139
1140 // String to array
1141 $new_terms = explode(',', $new);
1142
1143 // Remove empty element and trim
1144 $new_terms = array_filter($new_terms, '_delete_empty_element');
1145
1146 // If new tag are empty => exit !
1147 if (empty($new_terms)) {
1148 add_settings_error(__CLASS__, __CLASS__, esc_html__('No valid term specified!', 'simple-tags'), 'error taxopress-notice');
1149
1150 return false;
1151 }
1152
1153 $counter = 0;
1154 if (count($new_terms) > 0) {
1155 foreach ((array) $new_terms as $term) {
1156 $term = get_term_by('name', sanitize_text_field($term), $taxonomy);
1157 if (empty($term) || !is_object($term)) {
1158 continue;
1159 }
1160
1161 $term = $term->term_id;
1162
1163 $args = array(
1164 'post_type' => $post_type,
1165 'posts_per_page' => 100,
1166 'fields' => 'ids',
1167 'tax_query' => array(
1168 array(
1169 'taxonomy' => $taxonomy,
1170 'field' => 'id',
1171 'terms' => $term,
1172 ),
1173 ),
1174 );
1175
1176 $post_ids = array();
1177 $page = 1;
1178 do {
1179 $args['paged'] = $page;
1180 $batch = get_posts($args);
1181 $post_ids = array_merge($post_ids, $batch);
1182 $page++;
1183 } while (count($batch) === $args['posts_per_page']);
1184
1185 foreach ($post_ids as $post_id) {
1186 $remove = wp_remove_object_terms($post_id, $term, $taxonomy);
1187 if ($remove) {
1188 clean_object_term_cache($post_id, $taxonomy);
1189 clean_term_cache($term, $taxonomy);
1190 $counter++;
1191 }
1192 }
1193 }
1194
1195 if ($counter == 0) {
1196 add_settings_error(__CLASS__, __CLASS__, sprintf(esc_html__('This term is not associated with any %1$s.', 'simple-tags'), SimpleTags_Admin::$post_type_name), 'error taxopress-notice');
1197 } else {
1198 add_settings_error(__CLASS__, __CLASS__, sprintf(esc_html__('Removed term(s) "%1$s" from %2$s %3$s', 'simple-tags'), $new, $counter, SimpleTags_Admin::$post_type_name), 'updated taxopress-notice');
1199 }
1200 } else { // Error
1201 add_settings_error(__CLASS__, __CLASS__, sprintf(esc_html__('Error. No enough terms specified.', 'simple-tags'), $old), 'error taxopress-notice');
1202 }
1203
1204 return true;
1205 }
1206
1207 /**
1208 * Method for rename tags
1209 *
1210 * @param string $taxonomy
1211 * @param string $old
1212 * @param string $new
1213 *
1214 * @return boolean
1215 * @author WebFactory Ltd
1216 */
1217 public static function renameTerms($taxonomy = 'post_tag', $old = '', $new = '')
1218 {
1219 // Helper function to extract term name (ignoring slug in brackets)
1220 $extractTermName = function ($term) {
1221 return trim(preg_replace('/\s*\(.*?\)$/', '', $term));
1222 };
1223
1224 if (trim(str_replace(',', '', stripslashes($new))) == '') {
1225 add_settings_error(__CLASS__, __CLASS__, esc_html__('No new term specified!', 'simple-tags'), 'error taxopress-notice');
1226
1227 return false;
1228 }
1229
1230 // String to array
1231 $old_terms = explode(',', $old);
1232 $new_terms = explode(',', $new);
1233
1234 // Clean up terms by removing slugs
1235 $old_terms = array_map($extractTermName, $old_terms);
1236 $new_terms = array_map($extractTermName, $new_terms);
1237
1238 // Remove empty element and trim
1239 $old_terms = array_filter($old_terms, '_delete_empty_element');
1240 $new_terms = array_filter($new_terms, '_delete_empty_element');
1241
1242 // If old/new tag are empty => exit !
1243 if (empty($old_terms) || empty($new_terms)) {
1244 add_settings_error(__CLASS__, __CLASS__, esc_html__('No new/old valid term specified!', 'simple-tags'), 'error taxopress-notice');
1245
1246 return false;
1247 }
1248
1249 $counter = 0;
1250 if (count($old_terms) === count($new_terms)) { // Rename only
1251 foreach ((array) $old_terms as $i => $old_tag) {
1252 $new_name = sanitize_text_field($new_terms[ $i ]);
1253
1254 // Get term by name
1255 $term = get_term_by('name', sanitize_text_field($old_tag), $taxonomy);
1256 if (! $term) {
1257 continue;
1258 }
1259
1260 // Get objects from term ID
1261 $objects_id = get_objects_in_term($term->term_id, $taxonomy, array( 'fields' => 'all_with_object_id' ));
1262
1263 // Create the new term
1264 if (! $term_info = term_exists($new_name, $taxonomy)) {
1265 $term_info = wp_insert_term($new_name, $taxonomy);
1266 }
1267
1268 // If default category, update the ID for new term...
1269 if ('category' == $taxonomy && $term->term_id == get_option('default_category')) {
1270 update_option('default_category', $term_info['term_id']);
1271 clean_term_cache($term_info['term_id'], $taxonomy);
1272 }
1273
1274 // Delete old term
1275 wp_delete_term($term->term_id, $taxonomy);
1276
1277 // Set objects to new term ! (Append no replace)
1278 foreach ((array) $objects_id as $object_id) {
1279 wp_set_object_terms($object_id, $new_name, $taxonomy, true);
1280 }
1281
1282 // Clean cache
1283 clean_object_term_cache($objects_id, $taxonomy);
1284 clean_term_cache($term->term_id, $taxonomy);
1285
1286 // Increment
1287 $counter++;
1288 }
1289
1290 if ($counter == 0) {
1291 add_settings_error(__CLASS__, __CLASS__, esc_html__('No term renamed.', 'simple-tags'), 'updated taxopress-notice');
1292 } else {
1293 add_settings_error(__CLASS__, __CLASS__, sprintf(esc_html__('Renamed term(s) "%1$s" to "%2$s"', 'simple-tags'), rtrim($old, ','), rtrim($new, ',')), 'updated taxopress-notice');
1294 }
1295 } else { // Error
1296 add_settings_error(__CLASS__, __CLASS__, sprintf(esc_html__('Error. No enough terms for rename.', 'simple-tags'), $old), 'error taxopress-notice');
1297 }
1298
1299 return true;
1300 }
1301
1302 /**
1303 * Method for delete a list of terms
1304 *
1305 * @param string $taxonomy
1306 * @param string $delete
1307 *
1308 * @return boolean
1309 * @author WebFactory Ltd
1310 */
1311 public static function deleteTermsByTermList($taxonomy = 'post_tag', $delete = '')
1312 {
1313 if (trim(str_replace(',', '', stripslashes($delete))) == '') {
1314 add_settings_error(__CLASS__, __CLASS__, esc_html__('No term specified!', 'simple-tags'), 'error taxopress-notice');
1315
1316 return false;
1317 }
1318
1319 // In array + filter
1320 $delete_terms = explode(',', $delete);
1321 $delete_terms = array_filter($delete_terms, '_delete_empty_element');
1322
1323 // Delete tags
1324 $counter = 0;
1325 foreach ((array) $delete_terms as $term) {
1326 $term = get_term_by('name', sanitize_text_field($term), $taxonomy);
1327 $term_id = (int) $term->term_id;
1328
1329 if ($term_id != 0) {
1330 wp_delete_term($term_id, $taxonomy);
1331 clean_term_cache($term_id, $taxonomy);
1332 $counter++;
1333 }
1334 }
1335
1336 if ($counter == 0) {
1337 add_settings_error(__CLASS__, __CLASS__, esc_html__('No term deleted.', 'simple-tags'), 'updated taxopress-notice');
1338 } else {
1339 add_settings_error(__CLASS__, __CLASS__, sprintf(esc_html__('%1s term(s) deleted.', 'simple-tags'), $counter), 'updated taxopress-notice');
1340 }
1341
1342 return true;
1343 }
1344
1345 /**
1346 * Method for add terms for all or specified posts
1347 *
1348 * @param string $taxonomy
1349 * @param string $match
1350 * @param string $new
1351 *
1352 * @return boolean
1353 * @author WebFactory Ltd
1354 */
1355 public static function addMatchTerms($taxonomy = 'post_tag', $match = '', $new = '')
1356 {
1357 // Helper function to extract term name (ignoring slug in brackets)
1358 $extractTermName = function ($term) {
1359 return trim(preg_replace('/\s*\(.*?\)$/', '', $term));
1360 };
1361
1362 if (trim(str_replace(',', '', stripslashes($new))) == '') {
1363 add_settings_error(__CLASS__, __CLASS__, esc_html__('No new term(s) specified!', 'simple-tags'), 'error taxopress-notice');
1364
1365 return false;
1366 }
1367
1368 $match_terms = explode(',', $match);
1369 $new_terms = explode(',', $new);
1370
1371 // Clean up terms by removing slugs
1372 $match_terms = array_map($extractTermName, $match_terms);
1373 $new_terms = array_map($extractTermName, $new_terms);
1374
1375 $match_terms = array_filter($match_terms, '_delete_empty_element');
1376 $new_terms = array_filter($new_terms, '_delete_empty_element');
1377
1378 $counter = 0;
1379 if (! empty($match_terms)) { // Match and add
1380 // Get terms ID from old match names
1381 $terms_id = array();
1382 foreach ((array) $match_terms as $match_term) {
1383 $term = get_term_by('name', sanitize_text_field($match_term), $taxonomy);
1384 $terms_id[] = (int) $term->term_id;
1385 }
1386
1387 // Get object ID with terms ID
1388 $objects_id = get_objects_in_term($terms_id, $taxonomy, array( 'fields' => 'all_with_object_id' ));
1389
1390 // Add new tags for specified post
1391 foreach ((array) $objects_id as $object_id) {
1392 wp_set_object_terms($object_id, $new_terms, $taxonomy, true); // Append terms
1393 $counter++;
1394 }
1395
1396 // Clean cache
1397 clean_object_term_cache($objects_id, $taxonomy);
1398 clean_term_cache($terms_id, $taxonomy);
1399 } else { // Add for all posts
1400 // Get all posts ID
1401 global $wpdb;
1402 $post_type = sanitize_key(SimpleTags_Admin::$post_type);
1403 if (!post_type_exists($post_type)) {
1404 add_settings_error(__CLASS__, __CLASS__, esc_html__('Invalid post type.', 'simple-tags'), 'error taxopress-notice');
1405 return false;
1406 }
1407 SimpleTags_Admin::$post_type = $post_type;
1408 $objects_id = $wpdb->get_col(
1409 $wpdb->prepare(
1410 "SELECT ID FROM {$wpdb->posts} WHERE (post_status = 'publish' OR post_status = 'inherit') AND post_type = %s",
1411 $post_type
1412 )
1413 );
1414
1415 // Add new tags for all posts
1416 foreach ((array) $objects_id as $object_id) {
1417 wp_set_object_terms($object_id, $new_terms, $taxonomy, true); // Append terms
1418 clean_object_term_cache($object_id, $taxonomy);
1419 clean_term_cache($new_terms, $taxonomy);
1420 $counter++;
1421 }
1422
1423 // Clean cache
1424 clean_object_term_cache($objects_id, $taxonomy);
1425 }
1426
1427 if ($counter == 0) {
1428 add_settings_error(__CLASS__, __CLASS__, esc_html__('No term added.', 'simple-tags'), 'updated taxopress-notice');
1429 } else {
1430 add_settings_error(__CLASS__, __CLASS__, sprintf(esc_html__('Term(s) added to %1s %2s.', 'simple-tags'), $counter, SimpleTags_Admin::$post_type_name), 'updated taxopress-notice');
1431 }
1432
1433 return true;
1434 }
1435
1436 /**
1437 * Delete terms when counter if inferior to a specific number
1438 *
1439 * @param string $taxonomy
1440 * @param integer $number
1441 *
1442 * @return boolean
1443 * @author WebFactory Ltd
1444 */
1445 public static function removeRarelyUsed($taxonomy = 'post_tag', $number = 0)
1446 {
1447 global $wpdb;
1448
1449 if ((int) $number > 100) {
1450 wp_die('Tcheater ?');
1451 }
1452
1453 // Get terms with counter inferior to...
1454 $terms_id = $wpdb->get_col($wpdb->prepare("SELECT term_id FROM $wpdb->term_taxonomy WHERE taxonomy = %s AND count < %d", $taxonomy, (int) $number));
1455
1456 // Delete terms
1457 $counter = 0;
1458 foreach ((array) $terms_id as $term_id) {
1459 if ($term_id != 0) {
1460 wp_delete_term($term_id, $taxonomy);
1461 clean_term_cache($term_id, $taxonomy);
1462 $counter++;
1463 }
1464 }
1465
1466 if ($counter == 0) {
1467 add_settings_error(__CLASS__, __CLASS__, esc_html__('No term deleted.', 'simple-tags'), 'updated taxopress-notice');
1468 } else {
1469 add_settings_error(__CLASS__, __CLASS__, sprintf(esc_html__('%1s term(s) deleted.', 'simple-tags'), $counter), 'updated taxopress-notice');
1470 }
1471
1472 return true;
1473 }
1474
1475 public function handle_taxopress_check_delete_terms_ajax()
1476 {
1477
1478 if (!isset($_POST['nonce']) || !wp_verify_nonce(wp_unslash($_POST['nonce']), 'st-admin-js')) {
1479 wp_send_json_error(array('message' => __('Nonce verification failed.', 'simple-tags')));
1480 wp_die();
1481 }
1482
1483 global $wpdb;
1484
1485 $taxonomy = isset($_POST['taxonomy']) ? sanitize_key(wp_unslash($_POST['taxonomy'])) : 'post_tag';
1486 $number = isset($_POST['number']) ? intval($_POST['number']) : 0;
1487
1488 $taxonomy_object = get_taxonomy($taxonomy);
1489 if (
1490 !current_user_can('simple_tags')
1491 || !$taxonomy_object
1492 || empty($taxonomy_object->cap->manage_terms)
1493 || !current_user_can($taxonomy_object->cap->manage_terms)
1494 ) {
1495 wp_send_json_error(array('message' => __('Permission denied.', 'simple-tags')), 403);
1496 }
1497
1498 if ((int) $number > 100) {
1499 wp_die('Tcheater ?');
1500 }
1501
1502 if ($number > 0) {
1503 $terms = $wpdb->get_col($wpdb->prepare(
1504 "
1505 SELECT term_id FROM $wpdb->term_taxonomy
1506 WHERE taxonomy = %s AND count < %d",
1507 $taxonomy,
1508 (int) $number
1509 ));
1510
1511 $term_count = count($terms);
1512
1513 if ($term_count > 0) {
1514 wp_send_json_success(array('message' => sprintf(__('%d terms will be deleted.', 'simple-tags'), $term_count)));
1515 } else {
1516 wp_send_json_error(array('message' => __('No terms will be deleted.', 'simple-tags')));
1517 }
1518 } else {
1519 wp_send_json_error(array('message' => __('Invalid number specified.', 'simple-tags')));
1520 }
1521
1522 wp_die();
1523 }
1524
1525 /**
1526 * Method for removing terms from all or specified posts
1527 *
1528 * @param string $taxonomy
1529 * @param string $match
1530 * @param string $remove
1531 *
1532 * @return boolean
1533 * @author WebFactory Ltd
1534 */
1535 public static function removeMatchTerms($taxonomy = 'post_tag', $match = '', $remove = '')
1536 {
1537 // Helper function to extract term name (ignoring slug in brackets)
1538 $extractTermName = function ($term) {
1539 return trim(preg_replace('/\s*\(.*?\)$/', '', $term));
1540 };
1541
1542 if (trim(str_replace(',', '', stripslashes($remove))) == '') {
1543 add_settings_error(__CLASS__, __CLASS__, esc_html__('No term(s) specified for removal!', 'simple-tags'), 'error taxopress-notice');
1544 return false;
1545 }
1546
1547 $match_terms = explode(',', $match);
1548 $remove_terms = explode(',', $remove);
1549
1550 // Clean up terms by removing slugs
1551 $match_terms = array_map($extractTermName, $match_terms);
1552 $remove_terms = array_map($extractTermName, $remove_terms);
1553
1554 $match_terms = array_filter($match_terms, '_delete_empty_element');
1555 $remove_terms = array_filter($remove_terms, '_delete_empty_element');
1556
1557 // Arrays to track if terms entered is valid
1558 $valid_remove_terms = array();
1559 $invalid_remove_terms = array();
1560
1561 foreach ((array) $remove_terms as $remove_term) {
1562 $term = get_term_by('name', sanitize_text_field($remove_term), $taxonomy);
1563 if ($term) {
1564 $valid_remove_terms[] = $remove_term; // Add to valid list if the term exists
1565 } else {
1566 $invalid_remove_terms[] = $remove_term; // Collect invalid remove terms
1567 }
1568 }
1569
1570 if (empty($valid_remove_terms)) {
1571 add_settings_error(__CLASS__, __CLASS__, esc_html__('Term(s) does not exist.', 'simple-tags'), 'error taxopress-notice');
1572 return false;
1573 }
1574
1575 $counter = 0;
1576 if (!empty($match_terms)) {
1577 // Get terms ID from match terms
1578 $terms_id = array();
1579 foreach ((array) $match_terms as $match_term) {
1580 $term = get_term_by('name', sanitize_text_field($match_term), $taxonomy);
1581 if ($term) {
1582 $terms_id[] = (int) $term->term_id;
1583 }
1584 }
1585
1586 // Get object ID with terms ID
1587 $objects_id = get_objects_in_term($terms_id, $taxonomy, array('fields' => 'all_with_object_id'));
1588
1589 // Remove specified terms from matched posts
1590 foreach ((array) $objects_id as $object_id) {
1591 wp_remove_object_terms($object_id, $valid_remove_terms, $taxonomy);
1592 $counter++;
1593 }
1594
1595 clean_object_term_cache($objects_id, $taxonomy);
1596 clean_term_cache($terms_id, $taxonomy);
1597 } else {
1598 // Get all posts if no match terms were provided
1599 global $wpdb;
1600 $post_type = sanitize_key(SimpleTags_Admin::$post_type);
1601 if (!post_type_exists($post_type)) {
1602 add_settings_error(__CLASS__, __CLASS__, esc_html__('Invalid post type.', 'simple-tags'), 'error taxopress-notice');
1603 return false;
1604 }
1605 SimpleTags_Admin::$post_type = $post_type;
1606 $objects_id = $wpdb->get_col(
1607 $wpdb->prepare(
1608 "SELECT ID FROM {$wpdb->posts} WHERE (post_status = 'publish' OR post_status = 'inherit') AND post_type = %s",
1609 $post_type
1610 )
1611 );
1612
1613 // Remove valid terms for all posts
1614 foreach ((array) $objects_id as $object_id) {
1615 wp_remove_object_terms($object_id, $valid_remove_terms, $taxonomy);
1616 clean_object_term_cache($object_id, $taxonomy);
1617 clean_term_cache($valid_remove_terms, $taxonomy);
1618 $counter++;
1619 }
1620
1621 clean_object_term_cache($objects_id, $taxonomy);
1622 }
1623
1624 if ($counter == 0) {
1625 add_settings_error(__CLASS__, __CLASS__, esc_html__('No matching term found.', 'simple-tags'), 'updated taxopress-notice');
1626 } else {
1627 add_settings_error(__CLASS__, __CLASS__, sprintf(esc_html__('Term(s) removed from %1s %2s.', 'simple-tags'), $counter, SimpleTags_Admin::$post_type_name), 'updated taxopress-notice');
1628 }
1629
1630 return true;
1631 }
1632
1633 public static function handle_taxopress_autocomplete_terms()
1634 {
1635 if (!isset($_POST['nonce']) || !wp_verify_nonce(wp_unslash($_POST['nonce']), 'st-admin-js')) {
1636 wp_send_json_error(['message' => __('Nonce verification failed.', 'simple-tags')]);
1637 wp_die();
1638 }
1639
1640 $taxonomy = isset($_POST['taxonomy']) ? sanitize_key(wp_unslash($_POST['taxonomy'])) : 'post_tag';
1641 $term = isset($_POST['term']) ? sanitize_text_field(wp_unslash($_POST['term'])) : '';
1642
1643 $taxonomy_object = get_taxonomy($taxonomy);
1644 if (
1645 !current_user_can('simple_tags')
1646 || !$taxonomy_object
1647 || empty($taxonomy_object->cap->manage_terms)
1648 || !current_user_can($taxonomy_object->cap->manage_terms)
1649 ) {
1650 wp_send_json_error(['message' => __('Permission denied.', 'simple-tags')], 403);
1651 }
1652
1653 $terms = get_terms([
1654 'taxonomy' => $taxonomy,
1655 'name__like' => $term,
1656 'hide_empty' => false,
1657 'number' => (int) apply_filters('taxopress_autocomplete_terms_limit', 50),
1658 'update_term_meta_cache' => false,
1659 ]);
1660
1661 $results = [];
1662 foreach ($terms as $term) {
1663 $results[] = array(
1664 'name' => html_entity_decode(
1665 $term->name,
1666 ENT_QUOTES,
1667 get_bloginfo('charset')
1668 ),
1669 'slug' => $term->slug,
1670 );
1671 }
1672
1673 wp_send_json($results);
1674 wp_die();
1675 }
1676
1677
1678 /**
1679 * Method for edit one or more terms slug
1680 *
1681 * @param string $taxonomy
1682 * @param string $names
1683 * @param string $slugs
1684 *
1685 * @return boolean
1686 * @author WebFactory Ltd
1687 */
1688 /*
1689 public static function editTermSlug( $taxonomy = 'post_tag', $names = '', $slugs = '') {
1690 if ( trim( str_replace(',', '', stripslashes($slugs)) ) == '' ) {
1691 add_settings_error( __CLASS__, __CLASS__, esc_html__('No new slug(s) specified!', 'simple-tags'), 'error' );
1692 return false;
1693 }
1694
1695 $match_names = explode(',', $names);
1696 $new_slugs = explode(',', $slugs);
1697
1698 $match_names = array_filter($match_names, '_delete_empty_element');
1699 $new_slugs = array_filter($new_slugs, '_delete_empty_element');
1700
1701 if ( count($match_names) != count($new_slugs) ) {
1702 add_settings_error( __CLASS__, __CLASS__, esc_html__('Terms number and slugs number isn\'t the same!', 'simple-tags'), 'error' );
1703 return false;
1704 } else {
1705 $counter = 0;
1706 foreach ( (array) $match_names as $i => $match_name ) {
1707 // Sanitize slug + Escape
1708 $new_slug = sanitize_title($new_slugs[$i]);
1709
1710 // Get term by name
1711 $term = get_term_by('name', $match_name, $taxonomy);
1712 if ( !$term ) {
1713 continue;
1714 }
1715
1716 // Increment
1717 $counter++;
1718
1719 // Update term
1720 wp_update_term($term->term_id, $taxonomy, array('slug' => $new_slug));
1721
1722 // Clean cache
1723 clean_term_cache($term->term_id, $taxonomy);
1724 }
1725 }
1726
1727 if ( $counter == 0 ) {
1728 add_settings_error( __CLASS__, __CLASS__, esc_html__('No slug edited.', 'simple-tags'), 'updated' );
1729 } else {
1730 add_settings_error( __CLASS__, __CLASS__, sprintf(esc_html__('%s slug(s) edited.', 'simple-tags'), $counter), 'updated' );
1731 }
1732
1733 return true;
1734 }
1735 */
1736
1737
1738 /** Singleton instance */
1739 public static function get_instance()
1740 {
1741 if (! isset(self::$instance)) {
1742 self::$instance = new self();
1743 }
1744
1745 return self::$instance;
1746 }
1747 }
1748 ?>
1749