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

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

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