PluginProbe
Tag, Category, and Taxonomy Manager – Autotagger Automatically Add Terms / 3.40.0
Tag, Category, and Taxonomy Manager – Autotagger Automatically Add Terms v3.40.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.40.0, at inc/class.admin.manage.php

1,503 lines 70.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 class SimpleTags_Admin_Manage
4 {
5 const MENU_SLUG = 'st_options';
6
7 // class instance
8 public static $instance;
9
10 /**
11 * Constructor
12 *
13 * @return void
14 * @author WebFactory Ltd
15 */
16 public function __construct()
17 {
18 add_filter('set-screen-option', [ __CLASS__, 'set_screen' ], 10, 3);
19 // Admin menu
20 add_action('admin_menu', array( $this, 'admin_menu' ));
21
22 // Register taxo, parent method...
23 SimpleTags_Admin::register_taxonomy();
24
25 // Javascript
26 add_action('admin_enqueue_scripts', array( __CLASS__, 'admin_enqueue_scripts' ), 11);
27
28 //load ajax
29 add_action('wp_ajax_taxopress_check_delete_terms', array( $this, 'handle_taxopress_check_delete_terms_ajax'));
30 add_action('wp_ajax_taxopress_autocomplete_terms', [ $this, 'handle_taxopress_autocomplete_terms']);
31 add_action('wp_ajax_taxopress_merge_terms_batch', [$this, 'taxopress_merge_terms_batch']);
32 add_action('wp_ajax_taxopress_merge_suggestions', [$this, 'handle_taxopress_merge_suggestions']);
33
34 }
35
36 /**
37 * Init somes JS and CSS need for this feature
38 *
39 * @return void
40 * @author WebFactory Ltd
41 */
42 public static function admin_enqueue_scripts()
43 {
44 wp_register_script('st-helper-manage', STAGS_URL . '/assets/js/helper-manage.js', array( 'jquery' ), STAGS_VERSION);
45
46 // add JS for manage click tags
47 if (isset($_GET['page']) && $_GET['page'] == 'st_manage') {
48 wp_enqueue_script('st-helper-manage');
49 }
50 }
51
52 public static function set_screen($status, $option, $value)
53 {
54 return $value;
55 }
56
57 /**
58 * Add WP admin menu for Tags
59 *
60 * @return void
61 * @author WebFactory Ltd
62 */
63 public function admin_menu()
64 {
65 $hook = add_submenu_page(
66 self::MENU_SLUG,
67 __('TaxoPress: Manage Terms', 'simple-tags'),
68 __('Manage Terms', 'simple-tags'),
69 'simple_tags',
70 'st_manage',
71 array(
72 $this,
73 'page_manage_tags',
74 )
75 );
76 }
77
78 /**
79 * Method for build the page HTML manage tags
80 *
81 * @return void
82 * @author WebFactory Ltd
83 */
84 public function page_manage_tags()
85 {
86 $default_tab = '';
87 // Control Post data
88 if (isset($_POST['term_action'])) {
89 if (!current_user_can('simple_tags')) {
90 add_settings_error(__CLASS__, __CLASS__, esc_html__('Permission denied!', 'simple-tags'), 'error taxopress-notice');
91 } elseif (! wp_verify_nonce(sanitize_text_field($_POST['term_nonce']), 'simpletags_admin')) { // Origination and intention
92
93 add_settings_error(__CLASS__, __CLASS__, esc_html__('Security problem. Try again.', 'simple-tags'), 'error taxopress-notice');
94 } elseif (! isset(SimpleTags_Admin::$taxonomy) || ! taxonomy_exists(SimpleTags_Admin::$taxonomy)) { // Valid taxo ?
95
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($_POST['current_taxo']) : 'post_tag';
99 $post_type = isset($_POST['current_cpt']) ? sanitize_text_field($_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($_POST['renameterm_old']) : '';
105 $newtag = isset($_POST['renameterm_new']) ? sanitize_text_field($_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($_POST['current_taxo']) : 'post_tag';
110 $post_type = isset($_POST['current_cpt']) ? sanitize_text_field($_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($_POST['renameterm_old']) : '';
116 $newtag = isset($_POST['renameterm_new']) ? sanitize_text_field($_POST['renameterm_new']) : '';
117 $merge_type = isset($_POST['mergeterm_type']) ? sanitize_text_field($_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($_POST['current_taxo']) : 'post_tag';
122 $post_type = isset($_POST['current_cpt']) ? sanitize_text_field($_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($_POST['addterm_match']) : '';
128 $newtag = isset($_POST['addterm_new']) ? sanitize_text_field($_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($_POST['current_taxo']) : 'post_tag';
133 $post_type = isset($_POST['current_cpt']) ? sanitize_text_field($_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($_POST['removeterm_match']) : '';
139 $removetag = isset($_POST['remove_term']) ? sanitize_text_field($_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($_POST['current_taxo']) : 'post_tag';
144 $post_type = isset($_POST['current_cpt']) ? sanitize_text_field($_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($_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
437
438 </div>
439 </div>
440
441
442 <div class="clear"></div>
443
444 </div>
445 </div>
446 <div class="taxopress-right-sidebar admin-settings-sidebar">
447 <?php do_action('taxopress_admin_after_sidebar'); ?>
448 </div>
449
450 </div>
451
452
453 <?php SimpleTags_Admin::printAdminFooter(); ?>
454
455
456
457 <?php
458 do_action('simpletags-manage_terms', SimpleTags_Admin::$taxonomy);
459 }
460
461 /**
462 * Method to merge tags
463 *
464 * @param string $taxonomy
465 * @param string $old
466 * @param string $new
467 *
468 * @return boolean
469 * @author olatechpro
470 */
471 public static function mergeTerms($taxonomy = 'post_tag', $old = '', $new = '', $merge_type = '')
472 {
473 // Helper function to extract term name (ignoring slug in brackets)
474 $extractTermName = function ($term) {
475 return trim(preg_replace('/\s*\(.*?\)$/', '', $term));
476 };
477
478 if ($merge_type === 'same_name') {
479 $old_terms = explode(',', $old);
480 $old_terms = array_map($extractTermName, $old_terms);
481 $old_terms = array_filter($old_terms, '_delete_empty_element');
482 $retained_slug_param = isset($_POST['retained_slug']) ? sanitize_title($_POST['retained_slug']) : '';
483
484 if (empty($old_terms)) {
485 add_settings_error(__CLASS__, __CLASS__, esc_html__('No terms provided for merging!', 'simple-tags'), 'error taxopress-notice');
486 return false;
487 }
488
489 $terms =[];
490 foreach ($old_terms as $term_name) {
491 $term_objects = get_terms([
492 'taxonomy' => $taxonomy,
493 'name' => sanitize_text_field($term_name),
494 'hide_empty' => false,
495 ]);
496
497 if (is_array($term_objects) && count($term_objects) > 1) {
498 $terms = array_merge($terms, $term_objects);
499 }
500 }
501
502 if (empty($terms)) {
503 add_settings_error(__CLASS__, __CLASS__, esc_html__('No terms with the same name found.', 'simple-tags'), 'error taxopress-notice');
504 return false;
505 }
506
507 usort($terms, function ($a, $b) use ($term_name, $retained_slug_param) {
508 // If a retained slug is provided, sort that term to the top
509 if ($retained_slug_param) {
510 if ($a->slug === $retained_slug_param) return -1;
511 if ($b->slug === $retained_slug_param) return 1;
512 }
513 // Score based on similarity to term name
514 similar_text($term_name, $a->slug, $similarity_a);
515 similar_text($term_name, $b->slug, $similarity_b);
516
517 if ($similarity_a === $similarity_b) {
518 // If similarity is the same, sort by length (shortest first)
519 return strlen($a->slug) - strlen($b->slug);
520 }
521
522 // Otherwise, sort by similarity (higher first)
523 return $similarity_b - $similarity_a;
524 });
525
526 // Retain the term with the highest similarity and shortest slug (first in sorted array)
527 $retained_term = $terms[0];
528 $retained_slug = $retained_term->slug;
529 $retained_id = $retained_term->term_id;
530
531 // Collect terms to delete
532 $terms_to_delete = array_filter($terms, function ($term) use ($retained_id) {
533 return $term->term_id !== $retained_id;
534 });
535
536 // Reassign objects and delete old terms (existing logic)
537 $terms_id = array_map(function ($term) {
538 return $term->term_id;
539 }, $terms_to_delete);
540
541 $objects_id = get_objects_in_term($terms_id, $taxonomy, ['fields' => 'ids']);
542 foreach ($objects_id as $object_id) {
543 // Check if the object already has the term assigned
544 $current_terms = wp_get_object_terms($object_id, $taxonomy, ['fields' => 'ids']);
545 if (!in_array($retained_id, $current_terms)) {
546 wp_set_object_terms($object_id, $retained_slug, $taxonomy, true);
547 }
548 }
549
550 foreach ($terms_to_delete as $term) {
551 wp_delete_term($term->term_id, $taxonomy);
552 }
553
554 // Clean caches
555 clean_object_term_cache($objects_id, $taxonomy);
556 clean_term_cache($terms_id, $taxonomy);
557
558 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');
559 return [
560 'success' => true,
561 'retained_slug' => $retained_slug,
562 'posts_updated' => count($objects_id),
563 'post_ids' => $objects_id,
564 'terms_merged' => count($terms_to_delete) + 1,
565 ];
566
567 } else {
568
569 if (trim(str_replace(',', '', stripslashes($new))) == '' && $merge_type !== 'same_name' ) {
570 add_settings_error(__CLASS__, __CLASS__, esc_html__('No new term specified!', 'simple-tags'), 'error taxopress-notice');
571
572 return false;
573 }
574
575 // String to array
576 $old_terms = explode(',', $old);
577 $new_terms = explode(',', $new);
578
579 $old_terms = array_map($extractTermName, $old_terms);
580 $new_terms = array_map($extractTermName, $new_terms);
581
582 // Remove empty element and trim
583 $old_terms = array_filter($old_terms, '_delete_empty_element');
584 $new_terms = array_filter($new_terms, '_delete_empty_element');
585 $common_elements = array_intersect($old_terms, $new_terms);
586
587 // If old/new tag are empty => exit !
588 if (empty($old_terms) || empty($new_terms)) {
589 add_settings_error(__CLASS__, __CLASS__, esc_html__('No new/old valid term specified!', 'simple-tags'), 'error taxopress-notice');
590
591 return false;
592 }
593
594 if (!empty($common_elements)) {
595 add_settings_error(__CLASS__, __CLASS__, esc_html__('Term to merge and New Term must not contain same term.', 'simple-tags'), 'error taxopress-notice');
596
597 return false;
598 }
599
600 $counter = 0;
601 if (count($new_terms) == 1) { // Merge
602 // Set new tag
603 $new_tag = sanitize_text_field($new_terms[0]);
604 if (empty($new_tag)) {
605 add_settings_error(__CLASS__, __CLASS__, esc_html__('No valid new term.', 'simple-tags'), 'error taxopress-notice');
606
607 return false;
608 }
609
610 // Ensure the new term exists or create it
611 $new_term = get_term_by('name', $new_tag, $taxonomy);
612 if (!$new_term) {
613 $new_term_info = wp_insert_term($new_tag, $taxonomy);
614 if (is_wp_error($new_term_info)) {
615 add_settings_error(__CLASS__, __CLASS__, esc_html__('Failed to create the new term.', 'simple-tags'), 'error taxopress-notice');
616 return false;
617 }
618 $new_term_id = $new_term_info['term_id'];
619 $new_term_slug = isset($new_term_info['slug']) ? $new_term_info['slug'] : sanitize_title($new_tag);
620 } else {
621 $new_term_id = $new_term->term_id;
622 $new_term_slug = $new_term->slug;
623 }
624
625 // Get terms ID from old terms names
626 $terms_id = array();
627 $found_terms = array();
628 foreach ((array) $old_terms as $old_tag) {
629 $term = get_term_by('name', addslashes(sanitize_text_field($old_tag)), $taxonomy);
630 if ($term) {
631 $terms_id[] = (int) $term->term_id;
632 $found_terms[] = $term->name;
633 }
634 }
635
636 if (empty($terms_id)) {
637 add_settings_error(__CLASS__, __CLASS__, esc_html__('No matching terms found to merge.', 'simple-tags'), 'error taxopress-notice');
638 return false;
639 }
640
641 // Get objects from terms ID
642 $objects_id = get_objects_in_term($terms_id, $taxonomy, ['fields' => 'ids']);
643
644 // Use a set to track unique post IDs
645 $unique_objects = [];
646
647 // Assign the new term to all posts associated with the old terms
648 foreach ((array) $objects_id as $object_id) {
649 // Check if the object already has the term assigned
650 $current_terms = wp_get_object_terms($object_id, $taxonomy, ['fields' => 'ids']);
651 if (!in_array($new_term_id, $current_terms)) {
652 wp_set_object_terms($object_id, $new_term_slug, $taxonomy, true);
653 }
654 $unique_objects[$object_id] = true; // Add to unique set
655 }
656
657 // Count unique posts
658 $counter = count($unique_objects);
659
660 // Delete old terms
661 foreach ((array) $terms_id as $term_id) {
662 wp_delete_term($term_id, $taxonomy);
663 }
664
665 // Test if term is also a category
666 /*
667 if ( is_term($new_tag, 'category') ) {
668 // Edit the slug to use the new term
669 self::editTermSlug( $new_tag, sanitize_title($new_tag) );
670 }
671 */
672
673 // Clean cache
674 clean_object_term_cache($objects_id, $taxonomy);
675 clean_term_cache($terms_id, $taxonomy);
676
677
678 if (empty($found_terms)) {
679 add_settings_error(__CLASS__, __CLASS__, esc_html__('No terms were merged (terms may not exist).', 'simple-tags'), 'error taxopress-notice');
680 } else {
681 $message = sprintf(
682 esc_html__('Successfully merged %1$d term(s) "%2$s" to "%3$s". %4$s posts updated.', 'simple-tags'),
683 count($found_terms),
684 implode(', ', $found_terms),
685 rtrim($new, ','),
686 $counter
687 );
688 add_settings_error(__CLASS__, __CLASS__, $message, 'updated taxopress-notice');
689 }
690 } else { // Error
691 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');
692 }
693 return [
694 'success' => true,
695 'merged_into' => $new_tag,
696 'posts_updated' => $counter,
697 'post_ids' => array_keys($unique_objects),
698 'terms_merged' => count($terms_id) + 1,
699 ];
700
701
702 }
703 }
704
705 /**
706 * Find terms with same names but different slugs
707 */
708 public static function getSameNameMergeSuggestions($taxonomy = 'post_tag') {
709 global $wpdb;
710
711 $query = $wpdb->prepare("
712 SELECT t.name, COUNT(*) as count, GROUP_CONCAT(CONCAT(t.name, ' (', t.slug, ')') SEPARATOR ', ') as terms
713 FROM {$wpdb->terms} t
714 INNER JOIN {$wpdb->term_taxonomy} tt ON t.term_id = tt.term_id
715 WHERE tt.taxonomy = %s
716 GROUP BY t.name
717 HAVING COUNT(*) > 1
718 ORDER BY count DESC, t.name ASC
719 ", $taxonomy);
720
721 $results = $wpdb->get_results($query);
722
723 $show_slug = (int) SimpleTags_Plugin::get_option_value('enable_merge_terms_slug');
724
725 if (!$show_slug) {
726 foreach ($results as &$result) {
727 $result->terms = preg_replace('/\s*\([^)]+\)/', '', $result->terms);
728 }
729 }
730
731 return $results;
732 }
733
734 /**
735 * Find terms that might be candidates for merging based on similarity
736 */
737 public static function getDifferentNameMergeSuggestions($taxonomy = 'post_tag', $limit = 50) {
738 global $wpdb;
739
740 $terms = get_terms([
741 'taxonomy' => $taxonomy,
742 'hide_empty' => false,
743 'number' => $limit * 2,
744 ]);
745
746 $show_slug = (int) SimpleTags_Plugin::get_option_value('enable_merge_terms_slug');
747
748 $suggestions = [];
749
750 foreach ($terms as $i => $term1) {
751 foreach ($terms as $j => $term2) {
752 if ($i >= $j) continue;
753
754 $similarity_score = 0;
755 $reasons = [];
756
757 // 1. Levenshtein distance (for typos)
758 $levenshtein = levenshtein(strtolower($term1->name), strtolower($term2->name));
759 if ($levenshtein <= 2 && $levenshtein > 0) {
760 $similarity_score += 40;
761 $reasons[] = esc_html__('Similar spelling', 'simple-tags');
762 }
763
764 // 2. Soundex (phonetic similarity)
765 if (soundex($term1->name) === soundex($term2->name)) {
766 $similarity_score += 30;
767 $reasons[] = esc_html__('Sounds similar', 'simple-tags');
768 }
769
770 // 3. Common words/partial matches
771 $words1 = explode(' ', strtolower($term1->name));
772 $words2 = explode(' ', strtolower($term2->name));
773 $common_words = array_intersect($words1, $words2);
774 if (!empty($common_words)) {
775 $similarity_score += count($common_words) * 15;
776 $reasons[] = sprintf(esc_html__('Shared words: %s', 'simple-tags'), implode(', ', $common_words));
777 }
778
779 // 4. Prefix/suffix similarity
780 if (strpos($term1->name, $term2->name) !== false || strpos($term2->name, $term1->name) !== false) {
781 $similarity_score += 25;
782 $reasons[] = esc_html__('One contains the other', 'simple-tags');
783 }
784
785 if ($similarity_score >= 30) {
786 $suggested_name = '';
787
788 // Get clean term names for comparison
789 $term1_clean = strtolower(trim($term1->name));
790 $term2_clean = strtolower(trim($term2->name));
791
792 if (!empty($common_words)) {
793 $suggested_name = ucfirst(implode(' ', $common_words));
794 } else {
795 // Use the shorter term as base
796 $suggested_name = strlen($term1->name) <= strlen($term2->name) ? $term1->name : $term2->name;
797 }
798
799 // let's make sure suggested name is different from both original terms
800 $suggested_clean = strtolower(trim($suggested_name));
801 if ($suggested_clean === $term1_clean || $suggested_clean === $term2_clean) {
802 if (!empty($common_words) && count($common_words) > 1) {
803 // Use first common word only
804 $suggested_name = ucfirst($common_words[0]);
805 } else {
806 // Create a generic merged name
807 $suggested_name = esc_html__('Merged Term Name', 'simple-tags');
808 }
809 }
810
811 $final_suggested_clean = strtolower(trim($suggested_name));
812 if ($final_suggested_clean === $term1_clean || $final_suggested_clean === $term2_clean) {
813 $suggested_name = 'Combined ' . $suggested_name;
814 }
815
816 $term1_display = $show_slug ? $term1->name . ' (' . $term1->slug . ')' : $term1->name;
817 $term2_display = $show_slug ? $term2->name . ' (' . $term2->slug . ')' : $term2->name;
818
819 $suggestions[] = [
820 'term1' => $term1_display,
821 'term2' => $term2_display,
822 'score' => $similarity_score,
823 'reasons' => implode(', ', $reasons),
824 'suggested_name' => $suggested_name
825 ];
826
827 }
828 }
829 }
830
831 usort($suggestions, function($a, $b) {
832 return $b['score'] - $a['score'];
833 });
834
835 return array_slice($suggestions, 0, $limit);
836 }
837
838 /**
839 * AJAX handler for merge suggestions
840 */
841 public function handle_taxopress_merge_suggestions() {
842 if (!current_user_can('simple_tags')) {
843 wp_send_json_error('Permission denied');
844 }
845
846 if (!wp_verify_nonce($_POST['nonce'], 'simpletags_admin')) {
847 wp_send_json_error('Invalid nonce');
848 }
849
850 $taxonomy = get_option('merge-terms_taxo', 'post_tag');
851 $merge_type = sanitize_text_field($_POST['merge_type']);
852
853 if ($merge_type === 'same_name') {
854 $suggestions = self::getSameNameMergeSuggestions($taxonomy);
855 wp_send_json_success([
856 'type' => 'same_name',
857 'suggestions' => $suggestions
858 ]);
859 } else {
860 $suggestions = self::getDifferentNameMergeSuggestions($taxonomy);
861 wp_send_json_success([
862 'type' => 'different_name',
863 'suggestions' => $suggestions
864 ]);
865 }
866 }
867
868 public static function taxopress_merge_terms_batch() {
869 if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'st-admin-js')) {
870 wp_send_json_error(['message' => __('Security check failed.', 'simple-tags')]);
871 wp_die();
872 }
873
874 $taxonomy = isset($_POST['taxonomy']) ? sanitize_text_field($_POST['taxonomy']) : '';
875 $new_term = isset($_POST['new_term']) ? sanitize_text_field($_POST['new_term']) : '';
876 $merge_type = isset($_POST['merge_type']) ? sanitize_text_field($_POST['merge_type']) : 'different_name';
877 $old_terms_input = isset($_POST['old_terms']) ? array_map('sanitize_text_field', (array) $_POST['old_terms']) : [];
878
879 $extractTermName = function ($term) {
880 return trim(preg_replace('/\s*\(.*?\)$/', '', $term));
881 };
882
883 $old_terms = [];
884
885 foreach ($old_terms_input as $term_name) {
886 $term_name_clean = $extractTermName($term_name);
887 $term = get_term_by('name', $term_name_clean, $taxonomy);
888 if ($term && !is_wp_error($term)) {
889 $old_terms[] = $term_name_clean;
890 }
891 }
892
893 if (empty($old_terms)) {
894 wp_send_json_error([
895 'message' => __('Please enter a valid term. Merge cancelled.', 'simple-tags')
896 ]);
897 wp_die();
898 }
899
900
901 // Execute the merge
902 $result = SimpleTags_Admin_Manage::mergeTerms($taxonomy, implode(',', $old_terms), $new_term, $merge_type);
903
904 if ($result === true || (is_array($result) && !empty($result['success']))) {
905 $response = ['message' => __('Merge successful', 'simple-tags')];
906
907 if (is_array($result)) {
908 $response = array_merge($response, $result);
909 }
910
911 wp_send_json_success($response);
912 } else {
913 global $wp_settings_errors;
914 $errors = [];
915
916 if (!empty($wp_settings_errors)) {
917 foreach ($wp_settings_errors as $error) {
918 if (!empty($error['message'])) {
919 $errors[] = $error['message'];
920 }
921 }
922 }
923
924 if (is_wp_error($result)) {
925 $errors[] = $result->get_error_message();
926 } elseif (is_array($result) && isset($result['message'])) {
927 $errors[] = $result['message'];
928 }
929
930 wp_send_json_error(['message' => implode(' ', $errors)]);
931 }
932
933 wp_die();
934 }
935
936 /**
937 * Method for remove tags
938 *
939 * @param string $taxonomy
940 * @param string $post_type
941 * @param string $tag
942 *
943 * @return boolean
944 * @author WebFactory Ltd
945 */
946 public static function removeTerms($taxonomy = 'post_tag', $post_type = 'posts', $new = '')
947 {
948 if (trim(str_replace(',', '', stripslashes($new))) == '') {
949 add_settings_error(__CLASS__, __CLASS__, esc_html__('No term specified!', 'simple-tags'), 'error taxopress-notice');
950
951 return false;
952 }
953
954 // String to array
955 $new_terms = explode(',', $new);
956
957 // Remove empty element and trim
958 $new_terms = array_filter($new_terms, '_delete_empty_element');
959
960 // If new tag are empty => exit !
961 if (empty($new_terms)) {
962 add_settings_error(__CLASS__, __CLASS__, esc_html__('No valid term specified!', 'simple-tags'), 'error taxopress-notice');
963
964 return false;
965 }
966
967 $counter = 0;
968 if (count($new_terms) > 0) {
969 foreach ((array) $new_terms as $term) {
970 $term = get_term_by('name', sanitize_text_field($term), $taxonomy);
971 if (empty($term) || !is_object($term)) {
972 continue;
973 }
974
975 $term = $term->term_id;
976
977 $args = array(
978 'post_type' => $post_type, // post_type
979 'posts_per_page' => -1,
980 'tax_query' => array(
981 array(
982 'taxonomy' => $taxonomy,
983 'field' => 'id',
984 'terms' => $term
985 )
986 )
987 );
988 $posts = get_posts($args);
989 foreach ($posts as $post) {
990 $remove = wp_remove_object_terms($post->ID, $term, $taxonomy);
991 if ($remove) {
992 clean_object_term_cache($post->ID, $taxonomy);
993 clean_term_cache($term, $taxonomy);
994 $counter ++;
995 }
996 }
997 }
998
999 if ($counter == 0) {
1000 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');
1001 } else {
1002 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');
1003 }
1004 } else { // Error
1005 add_settings_error(__CLASS__, __CLASS__, sprintf(esc_html__('Error. No enough terms specified.', 'simple-tags'), $old), 'error taxopress-notice');
1006 }
1007
1008 return true;
1009 }
1010
1011 /**
1012 * Method for rename tags
1013 *
1014 * @param string $taxonomy
1015 * @param string $old
1016 * @param string $new
1017 *
1018 * @return boolean
1019 * @author WebFactory Ltd
1020 */
1021 public static function renameTerms($taxonomy = 'post_tag', $old = '', $new = '')
1022 {
1023 // Helper function to extract term name (ignoring slug in brackets)
1024 $extractTermName = function ($term) {
1025 return trim(preg_replace('/\s*\(.*?\)$/', '', $term));
1026 };
1027
1028 if (trim(str_replace(',', '', stripslashes($new))) == '') {
1029 add_settings_error(__CLASS__, __CLASS__, esc_html__('No new term specified!', 'simple-tags'), 'error taxopress-notice');
1030
1031 return false;
1032 }
1033
1034 // String to array
1035 $old_terms = explode(',', $old);
1036 $new_terms = explode(',', $new);
1037
1038 // Clean up terms by removing slugs
1039 $old_terms = array_map($extractTermName, $old_terms);
1040 $new_terms = array_map($extractTermName, $new_terms);
1041
1042 // Remove empty element and trim
1043 $old_terms = array_filter($old_terms, '_delete_empty_element');
1044 $new_terms = array_filter($new_terms, '_delete_empty_element');
1045
1046 // If old/new tag are empty => exit !
1047 if (empty($old_terms) || empty($new_terms)) {
1048 add_settings_error(__CLASS__, __CLASS__, esc_html__('No new/old valid term specified!', 'simple-tags'), 'error taxopress-notice');
1049
1050 return false;
1051 }
1052
1053 $counter = 0;
1054 if (count($old_terms) === count($new_terms)) { // Rename only
1055 foreach ((array) $old_terms as $i => $old_tag) {
1056 $new_name = sanitize_text_field($new_terms[ $i ]);
1057
1058 // Get term by name
1059 $term = get_term_by('name', sanitize_text_field($old_tag), $taxonomy);
1060 if (! $term) {
1061 continue;
1062 }
1063
1064 // Get objects from term ID
1065 $objects_id = get_objects_in_term($term->term_id, $taxonomy, array( 'fields' => 'all_with_object_id' ));
1066
1067 // Create the new term
1068 if (! $term_info = term_exists($new_name, $taxonomy)) {
1069 $term_info = wp_insert_term($new_name, $taxonomy);
1070 }
1071
1072 // If default category, update the ID for new term...
1073 if ('category' == $taxonomy && $term->term_id == get_option('default_category')) {
1074 update_option('default_category', $term_info['term_id']);
1075 clean_term_cache($term_info['term_id'], $taxonomy);
1076 }
1077
1078 // Delete old term
1079 wp_delete_term($term->term_id, $taxonomy);
1080
1081 // Set objects to new term ! (Append no replace)
1082 foreach ((array) $objects_id as $object_id) {
1083 wp_set_object_terms($object_id, $new_name, $taxonomy, true);
1084 }
1085
1086 // Clean cache
1087 clean_object_term_cache($objects_id, $taxonomy);
1088 clean_term_cache($term->term_id, $taxonomy);
1089
1090 // Increment
1091 $counter ++;
1092 }
1093
1094 if ($counter == 0) {
1095 add_settings_error(__CLASS__, __CLASS__, esc_html__('No term renamed.', 'simple-tags'), 'updated taxopress-notice');
1096 } else {
1097 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');
1098 }
1099 } else { // Error
1100 add_settings_error(__CLASS__, __CLASS__, sprintf(esc_html__('Error. No enough terms for rename.', 'simple-tags'), $old), 'error taxopress-notice');
1101 }
1102
1103 return true;
1104 }
1105
1106 /**
1107 * Method for delete a list of terms
1108 *
1109 * @param string $taxonomy
1110 * @param string $delete
1111 *
1112 * @return boolean
1113 * @author WebFactory Ltd
1114 */
1115 public static function deleteTermsByTermList($taxonomy = 'post_tag', $delete = '')
1116 {
1117 if (trim(str_replace(',', '', stripslashes($delete))) == '') {
1118 add_settings_error(__CLASS__, __CLASS__, esc_html__('No term specified!', 'simple-tags'), 'error taxopress-notice');
1119
1120 return false;
1121 }
1122
1123 // In array + filter
1124 $delete_terms = explode(',', $delete);
1125 $delete_terms = array_filter($delete_terms, '_delete_empty_element');
1126
1127 // Delete tags
1128 $counter = 0;
1129 foreach ((array) $delete_terms as $term) {
1130 $term = get_term_by('name', sanitize_text_field($term), $taxonomy);
1131 $term_id = (int) $term->term_id;
1132
1133 if ($term_id != 0) {
1134 wp_delete_term($term_id, $taxonomy);
1135 clean_term_cache($term_id, $taxonomy);
1136 $counter ++;
1137 }
1138 }
1139
1140 if ($counter == 0) {
1141 add_settings_error(__CLASS__, __CLASS__, esc_html__('No term deleted.', 'simple-tags'), 'updated taxopress-notice');
1142 } else {
1143 add_settings_error(__CLASS__, __CLASS__, sprintf(esc_html__('%1s term(s) deleted.', 'simple-tags'), $counter), 'updated taxopress-notice');
1144 }
1145
1146 return true;
1147 }
1148
1149 /**
1150 * Method for add terms for all or specified posts
1151 *
1152 * @param string $taxonomy
1153 * @param string $match
1154 * @param string $new
1155 *
1156 * @return boolean
1157 * @author WebFactory Ltd
1158 */
1159 public static function addMatchTerms($taxonomy = 'post_tag', $match = '', $new = '')
1160 {
1161 // Helper function to extract term name (ignoring slug in brackets)
1162 $extractTermName = function ($term) {
1163 return trim(preg_replace('/\s*\(.*?\)$/', '', $term));
1164 };
1165
1166 if (trim(str_replace(',', '', stripslashes($new))) == '') {
1167 add_settings_error(__CLASS__, __CLASS__, esc_html__('No new term(s) specified!', 'simple-tags'), 'error taxopress-notice');
1168
1169 return false;
1170 }
1171
1172 $match_terms = explode(',', $match);
1173 $new_terms = explode(',', $new);
1174
1175 // Clean up terms by removing slugs
1176 $match_terms = array_map($extractTermName, $match_terms);
1177 $new_terms = array_map($extractTermName, $new_terms);
1178
1179 $match_terms = array_filter($match_terms, '_delete_empty_element');
1180 $new_terms = array_filter($new_terms, '_delete_empty_element');
1181
1182 $counter = 0;
1183 if (! empty($match_terms)) { // Match and add
1184 // Get terms ID from old match names
1185 $terms_id = array();
1186 foreach ((array) $match_terms as $match_term) {
1187 $term = get_term_by('name', sanitize_text_field($match_term), $taxonomy);
1188 $terms_id[] = (int) $term->term_id;
1189 }
1190
1191 // Get object ID with terms ID
1192 $objects_id = get_objects_in_term($terms_id, $taxonomy, array( 'fields' => 'all_with_object_id' ));
1193
1194 // Add new tags for specified post
1195 foreach ((array) $objects_id as $object_id) {
1196 wp_set_object_terms($object_id, $new_terms, $taxonomy, true); // Append terms
1197 $counter ++;
1198 }
1199
1200 // Clean cache
1201 clean_object_term_cache($objects_id, $taxonomy);
1202 clean_term_cache($terms_id, $taxonomy);
1203 } else { // Add for all posts
1204 // Page or not ?
1205 $post_type_sql = "(post_status = 'publish' OR post_status = 'inherit') AND post_type = '".SimpleTags_Admin::$post_type."'";
1206
1207 // Get all posts ID
1208 global $wpdb;
1209 $objects_id = $wpdb->get_col("SELECT ID FROM {$wpdb->posts} WHERE {$post_type_sql}");
1210
1211 // Add new tags for all posts
1212 foreach ((array) $objects_id as $object_id) {
1213 wp_set_object_terms($object_id, $new_terms, $taxonomy, true); // Append terms
1214 clean_object_term_cache($object_id, $taxonomy);
1215 clean_term_cache($new_terms, $taxonomy);
1216 $counter ++;
1217 }
1218
1219 // Clean cache
1220 clean_object_term_cache($objects_id, $taxonomy);
1221 }
1222
1223 if ($counter == 0) {
1224 add_settings_error(__CLASS__, __CLASS__, esc_html__('No term added.', 'simple-tags'), 'updated taxopress-notice');
1225 } else {
1226 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');
1227 }
1228
1229 return true;
1230 }
1231
1232 /**
1233 * Delete terms when counter if inferior to a specific number
1234 *
1235 * @param string $taxonomy
1236 * @param integer $number
1237 *
1238 * @return boolean
1239 * @author WebFactory Ltd
1240 */
1241 public static function removeRarelyUsed($taxonomy = 'post_tag', $number = 0)
1242 {
1243 global $wpdb;
1244
1245 if ((int) $number > 100) {
1246 wp_die('Tcheater ?');
1247 }
1248
1249 // Get terms with counter inferior to...
1250 $terms_id = $wpdb->get_col($wpdb->prepare("SELECT term_id FROM $wpdb->term_taxonomy WHERE taxonomy = %s AND count < %d", $taxonomy, (int) $number));
1251
1252 // Delete terms
1253 $counter = 0;
1254 foreach ((array) $terms_id as $term_id) {
1255 if ($term_id != 0) {
1256 wp_delete_term($term_id, $taxonomy);
1257 clean_term_cache($term_id, $taxonomy);
1258 $counter ++;
1259 }
1260 }
1261
1262 if ($counter == 0) {
1263 add_settings_error(__CLASS__, __CLASS__, esc_html__('No term deleted.', 'simple-tags'), 'updated taxopress-notice');
1264 } else {
1265 add_settings_error(__CLASS__, __CLASS__, sprintf(esc_html__('%1s term(s) deleted.', 'simple-tags'), $counter), 'updated taxopress-notice');
1266 }
1267
1268 return true;
1269 }
1270
1271 function handle_taxopress_check_delete_terms_ajax() {
1272
1273 if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'st-admin-js')) {
1274 wp_send_json_error(array('message' => __('Nonce verification failed.', 'simple-tags')));
1275 wp_die();
1276 }
1277
1278 global $wpdb;
1279
1280 $taxonomy = isset($_POST['taxonomy']) ? sanitize_text_field($_POST['taxonomy']) : 'post_tag';
1281 $number = isset($_POST['number']) ? intval($_POST['number']) : 0;
1282
1283 if ((int) $number > 100) {
1284 wp_die('Tcheater ?');
1285 }
1286
1287 if ($number > 0) {
1288 $terms = $wpdb->get_col($wpdb->prepare("
1289 SELECT term_id FROM $wpdb->term_taxonomy
1290 WHERE taxonomy = %s AND count < %d",
1291 $taxonomy, (int) $number));
1292
1293 $term_count = count($terms);
1294
1295 if ($term_count > 0) {
1296 wp_send_json_success(array('message' => sprintf(__('%d terms will be deleted.', 'simple-tags'), $term_count)));
1297 } else {
1298 wp_send_json_error(array('message' => __('No terms will be deleted.', 'simple-tags')));
1299 }
1300 } else {
1301 wp_send_json_error(array('message' => __('Invalid number specified.', 'simple-tags')));
1302 }
1303
1304 wp_die();
1305 }
1306
1307 /**
1308 * Method for removing terms from all or specified posts
1309 *
1310 * @param string $taxonomy
1311 * @param string $match
1312 * @param string $remove
1313 *
1314 * @return boolean
1315 * @author WebFactory Ltd
1316 */
1317 public static function removeMatchTerms($taxonomy = 'post_tag', $match = '', $remove = '')
1318 {
1319 // Helper function to extract term name (ignoring slug in brackets)
1320 $extractTermName = function ($term) {
1321 return trim(preg_replace('/\s*\(.*?\)$/', '', $term));
1322 };
1323
1324 if (trim(str_replace(',', '', stripslashes($remove))) == '') {
1325 add_settings_error(__CLASS__, __CLASS__, esc_html__('No term(s) specified for removal!', 'simple-tags'), 'error taxopress-notice');
1326 return false;
1327 }
1328
1329 $match_terms = explode(',', $match);
1330 $remove_terms = explode(',', $remove);
1331
1332 // Clean up terms by removing slugs
1333 $match_terms = array_map($extractTermName, $match_terms);
1334 $remove_terms = array_map($extractTermName, $remove_terms);
1335
1336 $match_terms = array_filter($match_terms, '_delete_empty_element');
1337 $remove_terms = array_filter($remove_terms, '_delete_empty_element');
1338
1339 // Arrays to track if terms entered is valid
1340 $valid_remove_terms = array();
1341 $invalid_remove_terms = array();
1342
1343 foreach ((array) $remove_terms as $remove_term) {
1344 $term = get_term_by('name', sanitize_text_field($remove_term), $taxonomy);
1345 if ($term) {
1346 $valid_remove_terms[] = $remove_term; // Add to valid list if the term exists
1347 } else {
1348 $invalid_remove_terms[] = $remove_term; // Collect invalid remove terms
1349 }
1350 }
1351
1352 if (empty($valid_remove_terms)) {
1353 add_settings_error(__CLASS__, __CLASS__, esc_html__('Term(s) does not exist.', 'simple-tags'), 'error taxopress-notice');
1354 return false;
1355 }
1356
1357 $counter = 0;
1358 if (!empty($match_terms)) {
1359 // Get terms ID from match terms
1360 $terms_id = array();
1361 foreach ((array) $match_terms as $match_term) {
1362 $term = get_term_by('name', sanitize_text_field($match_term), $taxonomy);
1363 if ($term) {
1364 $terms_id[] = (int) $term->term_id;
1365 }
1366 }
1367
1368 // Get object ID with terms ID
1369 $objects_id = get_objects_in_term($terms_id, $taxonomy, array('fields' => 'all_with_object_id'));
1370
1371 // Remove specified terms from matched posts
1372 foreach ((array) $objects_id as $object_id) {
1373 wp_remove_object_terms($object_id, $valid_remove_terms, $taxonomy);
1374 $counter++;
1375 }
1376
1377 clean_object_term_cache($objects_id, $taxonomy);
1378 clean_term_cache($terms_id, $taxonomy);
1379 } else {
1380 // Get all posts if no match terms were provided
1381 global $wpdb;
1382 $post_type_sql = "(post_status = 'publish' OR post_status = 'inherit') AND post_type = '".SimpleTags_Admin::$post_type."'";
1383 $objects_id = $wpdb->get_col("SELECT ID FROM {$wpdb->posts} WHERE {$post_type_sql}");
1384
1385 // Remove valid terms for all posts
1386 foreach ((array) $objects_id as $object_id) {
1387 wp_remove_object_terms($object_id, $valid_remove_terms, $taxonomy);
1388 clean_object_term_cache($object_id, $taxonomy);
1389 clean_term_cache($valid_remove_terms, $taxonomy);
1390 $counter++;
1391 }
1392
1393 clean_object_term_cache($objects_id, $taxonomy);
1394 }
1395
1396 if ($counter == 0) {
1397 add_settings_error(__CLASS__, __CLASS__, esc_html__('No matching term found.', 'simple-tags'), 'updated taxopress-notice');
1398 } else {
1399 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');
1400 }
1401
1402 return true;
1403 }
1404
1405 public static function handle_taxopress_autocomplete_terms() {
1406 if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'st-admin-js')) {
1407 wp_send_json_error(['message' => __('Nonce verification failed.', 'simple-tags')]);
1408 wp_die();
1409 }
1410
1411 $taxonomy = isset($_POST['taxonomy']) ? sanitize_text_field($_POST['taxonomy']) : 'post_tag';
1412 $term = isset($_POST['term']) ? sanitize_text_field($_POST['term']) : '';
1413
1414 $terms = get_terms([
1415 'taxonomy' => $taxonomy,
1416 'name__like' => $term,
1417 'hide_empty' => false,
1418 ]);
1419
1420 $results = [];
1421 foreach ($terms as $term) {
1422 $results[] = [
1423 'name' => $term->name,
1424 'slug' => $term->slug
1425 ];
1426 }
1427
1428 wp_send_json($results);
1429 wp_die();
1430 }
1431
1432
1433 /**
1434 * Method for edit one or more terms slug
1435 *
1436 * @param string $taxonomy
1437 * @param string $names
1438 * @param string $slugs
1439 *
1440 * @return boolean
1441 * @author WebFactory Ltd
1442 */
1443 /*
1444 public static function editTermSlug( $taxonomy = 'post_tag', $names = '', $slugs = '') {
1445 if ( trim( str_replace(',', '', stripslashes($slugs)) ) == '' ) {
1446 add_settings_error( __CLASS__, __CLASS__, esc_html__('No new slug(s) specified!', 'simple-tags'), 'error' );
1447 return false;
1448 }
1449
1450 $match_names = explode(',', $names);
1451 $new_slugs = explode(',', $slugs);
1452
1453 $match_names = array_filter($match_names, '_delete_empty_element');
1454 $new_slugs = array_filter($new_slugs, '_delete_empty_element');
1455
1456 if ( count($match_names) != count($new_slugs) ) {
1457 add_settings_error( __CLASS__, __CLASS__, esc_html__('Terms number and slugs number isn\'t the same!', 'simple-tags'), 'error' );
1458 return false;
1459 } else {
1460 $counter = 0;
1461 foreach ( (array) $match_names as $i => $match_name ) {
1462 // Sanitize slug + Escape
1463 $new_slug = sanitize_title($new_slugs[$i]);
1464
1465 // Get term by name
1466 $term = get_term_by('name', $match_name, $taxonomy);
1467 if ( !$term ) {
1468 continue;
1469 }
1470
1471 // Increment
1472 $counter++;
1473
1474 // Update term
1475 wp_update_term($term->term_id, $taxonomy, array('slug' => $new_slug));
1476
1477 // Clean cache
1478 clean_term_cache($term->term_id, $taxonomy);
1479 }
1480 }
1481
1482 if ( $counter == 0 ) {
1483 add_settings_error( __CLASS__, __CLASS__, esc_html__('No slug edited.', 'simple-tags'), 'updated' );
1484 } else {
1485 add_settings_error( __CLASS__, __CLASS__, sprintf(esc_html__('%s slug(s) edited.', 'simple-tags'), $counter), 'updated' );
1486 }
1487
1488 return true;
1489 }
1490 */
1491
1492
1493 /** Singleton instance */
1494 public static function get_instance()
1495 {
1496 if (! isset(self::$instance)) {
1497 self::$instance = new self();
1498 }
1499
1500 return self::$instance;
1501 }
1502 }
1503 ?>