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

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