'; } $active_tab_slug = isset($_GET['tab']) ? sanitize_text_field($_GET['tab']) : 'add-terms'; // Default order if (! isset($_GET['order'])) { $_GET['order'] = 'name-asc'; } settings_errors(__CLASS__); ?>


*/ ?>













Slug definition', 'simple-tags'); ?>




$taxonomy, 'name' => sanitize_text_field($term_name), 'hide_empty' => false, ]); if (is_array($term_objects) && count($term_objects) > 1) { $terms = array_merge($terms, $term_objects); } } if (empty($terms)) { add_settings_error(__CLASS__, __CLASS__, esc_html__('No terms with the same name found.', 'simple-tags'), 'error taxopress-notice'); return false; } usort($terms, function ($a, $b) use ($term_name, $retained_slug_param) { // If a retained slug is provided, sort that term to the top if ($retained_slug_param) { if ($a->slug === $retained_slug_param) return -1; if ($b->slug === $retained_slug_param) return 1; } // Score based on similarity to term name similar_text($term_name, $a->slug, $similarity_a); similar_text($term_name, $b->slug, $similarity_b); if ($similarity_a === $similarity_b) { // If similarity is the same, sort by length (shortest first) return strlen($a->slug) - strlen($b->slug); } // Otherwise, sort by similarity (higher first) return $similarity_b - $similarity_a; }); // Retain the term with the highest similarity and shortest slug (first in sorted array) $retained_term = $terms[0]; $retained_slug = $retained_term->slug; $retained_id = $retained_term->term_id; // Collect terms to delete $terms_to_delete = array_filter($terms, function ($term) use ($retained_id) { return $term->term_id !== $retained_id; }); // Reassign objects and delete old terms (existing logic) $terms_id = array_map(function ($term) { return $term->term_id; }, $terms_to_delete); $objects_id = get_objects_in_term($terms_id, $taxonomy, ['fields' => 'ids']); foreach ($objects_id as $object_id) { // Check if the object already has the term assigned $current_terms = wp_get_object_terms($object_id, $taxonomy, ['fields' => 'ids']); if (!in_array($retained_id, $current_terms)) { wp_set_object_terms($object_id, $retained_slug, $taxonomy, true); } } foreach ($terms_to_delete as $term) { wp_delete_term($term->term_id, $taxonomy); } // Clean caches clean_object_term_cache($objects_id, $taxonomy); clean_term_cache($terms_id, $taxonomy); 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'); return [ 'success' => true, 'retained_slug' => $retained_slug, 'posts_updated' => count($objects_id), 'post_ids' => $objects_id, 'terms_merged' => count($terms_to_delete) + 1, ]; } else { if (trim(str_replace(',', '', stripslashes($new))) == '' && $merge_type !== 'same_name' ) { add_settings_error(__CLASS__, __CLASS__, esc_html__('No new term specified!', 'simple-tags'), 'error taxopress-notice'); return false; } // String to array $old_terms = explode(',', $old); $new_terms = explode(',', $new); $old_terms = array_map($extractTermName, $old_terms); $new_terms = array_map($extractTermName, $new_terms); // Remove empty element and trim $old_terms = array_filter($old_terms, '_delete_empty_element'); $new_terms = array_filter($new_terms, '_delete_empty_element'); $common_elements = array_intersect($old_terms, $new_terms); // If old/new tag are empty => exit ! if (empty($old_terms) || empty($new_terms)) { add_settings_error(__CLASS__, __CLASS__, esc_html__('No new/old valid term specified!', 'simple-tags'), 'error taxopress-notice'); return false; } if (!empty($common_elements) && $merge_type !== 'different_name') { add_settings_error(__CLASS__, __CLASS__, esc_html__('Term to merge and New Term must not contain same term.', 'simple-tags'), 'error taxopress-notice'); return false; } $counter = 0; if (count($new_terms) == 1) { // Merge // Set new tag $new_tag = sanitize_text_field($new_terms[0]); if (empty($new_tag)) { add_settings_error(__CLASS__, __CLASS__, esc_html__('No valid new term.', 'simple-tags'), 'error taxopress-notice'); return false; } // Ensure the new term exists or create it $new_term = get_term_by('name', $new_tag, $taxonomy); if (!$new_term) { $new_term_info = wp_insert_term($new_tag, $taxonomy); if (is_wp_error($new_term_info)) { add_settings_error(__CLASS__, __CLASS__, esc_html__('Failed to create the new term.', 'simple-tags'), 'error taxopress-notice'); return false; } $new_term_id = $new_term_info['term_id']; $new_term_slug = isset($new_term_info['slug']) ? $new_term_info['slug'] : sanitize_title($new_tag); } else { $new_term_id = $new_term->term_id; $new_term_slug = $new_term->slug; } // Get terms ID from old terms names $terms_id = array(); $found_terms = array(); foreach ((array) $old_terms as $old_tag) { $term = get_term_by('name', addslashes(sanitize_text_field($old_tag)), $taxonomy); if ($term) { $terms_id[] = (int) $term->term_id; $found_terms[] = $term->name; } } if (empty($terms_id)) { add_settings_error(__CLASS__, __CLASS__, esc_html__('No matching terms found to merge.', 'simple-tags'), 'error taxopress-notice'); return false; } // Get objects from terms ID $objects_id = get_objects_in_term($terms_id, $taxonomy, ['fields' => 'ids']); // Use a set to track unique post IDs $unique_objects = []; // Assign the new term to all posts associated with the old terms foreach ((array) $objects_id as $object_id) { // Check if the object already has the term assigned $current_terms = wp_get_object_terms($object_id, $taxonomy, ['fields' => 'ids']); if (!in_array($new_term_id, $current_terms)) { wp_set_object_terms($object_id, $new_term_slug, $taxonomy, true); } $unique_objects[$object_id] = true; // Add to unique set } // Count unique posts $counter = count($unique_objects); // Delete old terms foreach ((array) $terms_id as $term_id) { wp_delete_term($term_id, $taxonomy); } // Test if term is also a category /* if ( is_term($new_tag, 'category') ) { // Edit the slug to use the new term self::editTermSlug( $new_tag, sanitize_title($new_tag) ); } */ // Clean cache clean_object_term_cache($objects_id, $taxonomy); clean_term_cache($terms_id, $taxonomy); if (empty($found_terms)) { add_settings_error(__CLASS__, __CLASS__, esc_html__('No terms were merged (terms may not exist).', 'simple-tags'), 'error taxopress-notice'); } else { $message = sprintf( esc_html__('Successfully merged %1$d term(s) "%2$s" to "%3$s". %4$s posts updated.', 'simple-tags'), count($found_terms), implode(', ', $found_terms), rtrim($new, ','), $counter ); add_settings_error(__CLASS__, __CLASS__, $message, 'updated taxopress-notice'); } } else { // Error 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'); } return [ 'success' => true, 'merged_into' => $new_tag, 'posts_updated' => $counter, 'post_ids' => array_keys($unique_objects), 'terms_merged' => count($terms_id) + 1, ]; } } /** * Find terms with same names but different slugs */ public static function getSameNameMergeSuggestions($taxonomy = 'post_tag') { global $wpdb; $query = $wpdb->prepare(" SELECT t.name, COUNT(*) as count, GROUP_CONCAT(CONCAT(t.name, ' (', t.slug, ')') SEPARATOR ', ') as terms FROM {$wpdb->terms} t INNER JOIN {$wpdb->term_taxonomy} tt ON t.term_id = tt.term_id WHERE tt.taxonomy = %s GROUP BY t.name HAVING COUNT(*) > 1 ORDER BY count DESC, t.name ASC ", $taxonomy); $results = $wpdb->get_results($query); $show_slug = (int) SimpleTags_Plugin::get_option_value('enable_merge_terms_slug'); if (!$show_slug) { foreach ($results as &$result) { $result->terms = preg_replace('/\s*\([^)]+\)/', '', $result->terms); } } return $results; } /** * Find terms that might be candidates for merging based on similarity */ public static function getDifferentNameMergeSuggestions($taxonomy = 'post_tag', $limit = 50) { global $wpdb; $terms = get_terms([ 'taxonomy' => $taxonomy, 'hide_empty' => false, 'number' => $limit * 2, ]); $show_slug = (int) SimpleTags_Plugin::get_option_value('enable_merge_terms_slug'); $suggestions = []; foreach ($terms as $i => $term1) { foreach ($terms as $j => $term2) { if ($i >= $j) continue; $similarity_score = 0; $reasons = []; // 1. Levenshtein distance (for typos) $levenshtein = levenshtein(strtolower($term1->name), strtolower($term2->name)); if ($levenshtein <= 2 && $levenshtein > 0) { $similarity_score += 40; $reasons[] = esc_html__('Similar spelling', 'simple-tags'); } // 2. Soundex (phonetic similarity) if (soundex($term1->name) === soundex($term2->name)) { $similarity_score += 30; $reasons[] = esc_html__('Sounds similar', 'simple-tags'); } // 3. Common words/partial matches $words1 = explode(' ', strtolower($term1->name)); $words2 = explode(' ', strtolower($term2->name)); $common_words = array_intersect($words1, $words2); if (!empty($common_words)) { $similarity_score += count($common_words) * 15; $reasons[] = sprintf(esc_html__('Shared words: %s', 'simple-tags'), implode(', ', $common_words)); } // 4. Prefix/suffix similarity if (strpos($term1->name, $term2->name) !== false || strpos($term2->name, $term1->name) !== false) { $similarity_score += 25; $reasons[] = esc_html__('One contains the other', 'simple-tags'); } if ($similarity_score >= 30) { $suggested_name = ''; // Get clean term names for comparison $term1_clean = strtolower(trim($term1->name)); $term2_clean = strtolower(trim($term2->name)); if (!empty($common_words)) { $suggested_name = ucfirst(implode(' ', $common_words)); } else { // Use the shorter term as base $suggested_name = strlen($term1->name) <= strlen($term2->name) ? $term1->name : $term2->name; } // let's make sure suggested name is different from both original terms $suggested_clean = strtolower(trim($suggested_name)); if ($suggested_clean === $term1_clean || $suggested_clean === $term2_clean) { if (!empty($common_words)) { $suggested_name = ucfirst($common_words[0]); } else { $popular_term = ($term1->count >= $term2->count) ? $term1 : $term2; $suggested_name = $popular_term->name; } $suggested_clean = strtolower(trim($suggested_name)); } $term1_display = $show_slug ? $term1->name . ' (' . $term1->slug . ')' : $term1->name; $term2_display = $show_slug ? $term2->name . ' (' . $term2->slug . ')' : $term2->name; $suggestions[] = [ 'term1' => $term1_display, 'term2' => $term2_display, 'score' => $similarity_score, 'reasons' => implode(', ', $reasons), 'suggested_name' => $suggested_name ]; } } } usort($suggestions, function($a, $b) { return $b['score'] - $a['score']; }); return array_slice($suggestions, 0, $limit); } /** * AJAX handler for merge suggestions */ public function handle_taxopress_merge_suggestions() { if (!current_user_can('simple_tags')) { wp_send_json_error('Permission denied'); } if (!wp_verify_nonce($_POST['nonce'], 'simpletags_admin')) { wp_send_json_error('Invalid nonce'); } $taxonomy = get_option('merge-terms_taxo', 'post_tag'); $merge_type = sanitize_text_field($_POST['merge_type']); if ($merge_type === 'same_name') { $suggestions = self::getSameNameMergeSuggestions($taxonomy); wp_send_json_success([ 'type' => 'same_name', 'suggestions' => $suggestions ]); } else { $suggestions = self::getDifferentNameMergeSuggestions($taxonomy); wp_send_json_success([ 'type' => 'different_name', 'suggestions' => $suggestions ]); } } public static function taxopress_merge_terms_batch() { $nonce = isset($_POST['nonce']) ? sanitize_text_field($_POST['nonce']) : ''; if (empty($nonce) || !wp_verify_nonce($nonce, 'st-admin-js')) { wp_send_json_error(['message' => __('Security check failed.', 'simple-tags')], 403); wp_die(); } if (!current_user_can('simple_tags')) { wp_send_json_error(['message' => __('Permission denied.', 'simple-tags')], 403); wp_die(); } $taxonomy = isset($_POST['taxonomy']) ? sanitize_text_field($_POST['taxonomy']) : ''; $new_term = isset($_POST['new_term']) ? sanitize_text_field($_POST['new_term']) : ''; $merge_type = isset($_POST['merge_type']) ? sanitize_text_field($_POST['merge_type']) : 'different_name'; $old_terms_input = isset($_POST['old_terms']) ? array_map('sanitize_text_field', (array) $_POST['old_terms']) : []; $extractTermName = function ($term) { return trim(preg_replace('/\s*\(.*?\)$/', '', $term)); }; $old_terms = []; foreach ($old_terms_input as $term_name) { $term_name_clean = $extractTermName($term_name); $term = get_term_by('name', $term_name_clean, $taxonomy); if ($term && !is_wp_error($term)) { $old_terms[] = $term_name_clean; } } if (empty($old_terms)) { wp_send_json_error([ 'message' => __('Please enter a valid term. Merge cancelled.', 'simple-tags') ]); wp_die(); } if ($merge_type === 'different_name' && !empty($new_term)) { $old_terms = array_values(array_filter($old_terms, function($term_name) use ($new_term) { return strtolower(trim($term_name)) !== strtolower(trim($new_term)); })); } // Execute the merge $result = SimpleTags_Admin_Manage::mergeTerms($taxonomy, implode(',', $old_terms), $new_term, $merge_type); if ($result === true || (is_array($result) && !empty($result['success']))) { $response = ['message' => __('Merge successful', 'simple-tags')]; if (is_array($result)) { $response = array_merge($response, $result); } wp_send_json_success($response); } else { global $wp_settings_errors; $errors = []; if (!empty($wp_settings_errors)) { foreach ($wp_settings_errors as $error) { if (!empty($error['message'])) { $errors[] = $error['message']; } } } if (is_wp_error($result)) { $errors[] = $result->get_error_message(); } elseif (is_array($result) && isset($result['message'])) { $errors[] = $result['message']; } wp_send_json_error(['message' => implode(' ', $errors)]); } wp_die(); } /** * Method for remove tags * * @param string $taxonomy * @param string $post_type * @param string $tag * * @return boolean * @author WebFactory Ltd */ public static function removeTerms($taxonomy = 'post_tag', $post_type = 'posts', $new = '') { if (trim(str_replace(',', '', stripslashes($new))) == '') { add_settings_error(__CLASS__, __CLASS__, esc_html__('No term specified!', 'simple-tags'), 'error taxopress-notice'); return false; } // String to array $new_terms = explode(',', $new); // Remove empty element and trim $new_terms = array_filter($new_terms, '_delete_empty_element'); // If new tag are empty => exit ! if (empty($new_terms)) { add_settings_error(__CLASS__, __CLASS__, esc_html__('No valid term specified!', 'simple-tags'), 'error taxopress-notice'); return false; } $counter = 0; if (count($new_terms) > 0) { foreach ((array) $new_terms as $term) { $term = get_term_by('name', sanitize_text_field($term), $taxonomy); if (empty($term) || !is_object($term)) { continue; } $term = $term->term_id; $args = array( 'post_type' => $post_type, // post_type 'posts_per_page' => -1, 'tax_query' => array( array( 'taxonomy' => $taxonomy, 'field' => 'id', 'terms' => $term ) ) ); $posts = get_posts($args); foreach ($posts as $post) { $remove = wp_remove_object_terms($post->ID, $term, $taxonomy); if ($remove) { clean_object_term_cache($post->ID, $taxonomy); clean_term_cache($term, $taxonomy); $counter ++; } } } if ($counter == 0) { 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'); } else { 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'); } } else { // Error add_settings_error(__CLASS__, __CLASS__, sprintf(esc_html__('Error. No enough terms specified.', 'simple-tags'), $old), 'error taxopress-notice'); } return true; } /** * Method for rename tags * * @param string $taxonomy * @param string $old * @param string $new * * @return boolean * @author WebFactory Ltd */ public static function renameTerms($taxonomy = 'post_tag', $old = '', $new = '') { // Helper function to extract term name (ignoring slug in brackets) $extractTermName = function ($term) { return trim(preg_replace('/\s*\(.*?\)$/', '', $term)); }; if (trim(str_replace(',', '', stripslashes($new))) == '') { add_settings_error(__CLASS__, __CLASS__, esc_html__('No new term specified!', 'simple-tags'), 'error taxopress-notice'); return false; } // String to array $old_terms = explode(',', $old); $new_terms = explode(',', $new); // Clean up terms by removing slugs $old_terms = array_map($extractTermName, $old_terms); $new_terms = array_map($extractTermName, $new_terms); // Remove empty element and trim $old_terms = array_filter($old_terms, '_delete_empty_element'); $new_terms = array_filter($new_terms, '_delete_empty_element'); // If old/new tag are empty => exit ! if (empty($old_terms) || empty($new_terms)) { add_settings_error(__CLASS__, __CLASS__, esc_html__('No new/old valid term specified!', 'simple-tags'), 'error taxopress-notice'); return false; } $counter = 0; if (count($old_terms) === count($new_terms)) { // Rename only foreach ((array) $old_terms as $i => $old_tag) { $new_name = sanitize_text_field($new_terms[ $i ]); // Get term by name $term = get_term_by('name', sanitize_text_field($old_tag), $taxonomy); if (! $term) { continue; } // Get objects from term ID $objects_id = get_objects_in_term($term->term_id, $taxonomy, array( 'fields' => 'all_with_object_id' )); // Create the new term if (! $term_info = term_exists($new_name, $taxonomy)) { $term_info = wp_insert_term($new_name, $taxonomy); } // If default category, update the ID for new term... if ('category' == $taxonomy && $term->term_id == get_option('default_category')) { update_option('default_category', $term_info['term_id']); clean_term_cache($term_info['term_id'], $taxonomy); } // Delete old term wp_delete_term($term->term_id, $taxonomy); // Set objects to new term ! (Append no replace) foreach ((array) $objects_id as $object_id) { wp_set_object_terms($object_id, $new_name, $taxonomy, true); } // Clean cache clean_object_term_cache($objects_id, $taxonomy); clean_term_cache($term->term_id, $taxonomy); // Increment $counter ++; } if ($counter == 0) { add_settings_error(__CLASS__, __CLASS__, esc_html__('No term renamed.', 'simple-tags'), 'updated taxopress-notice'); } else { 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'); } } else { // Error add_settings_error(__CLASS__, __CLASS__, sprintf(esc_html__('Error. No enough terms for rename.', 'simple-tags'), $old), 'error taxopress-notice'); } return true; } /** * Method for delete a list of terms * * @param string $taxonomy * @param string $delete * * @return boolean * @author WebFactory Ltd */ public static function deleteTermsByTermList($taxonomy = 'post_tag', $delete = '') { if (trim(str_replace(',', '', stripslashes($delete))) == '') { add_settings_error(__CLASS__, __CLASS__, esc_html__('No term specified!', 'simple-tags'), 'error taxopress-notice'); return false; } // In array + filter $delete_terms = explode(',', $delete); $delete_terms = array_filter($delete_terms, '_delete_empty_element'); // Delete tags $counter = 0; foreach ((array) $delete_terms as $term) { $term = get_term_by('name', sanitize_text_field($term), $taxonomy); $term_id = (int) $term->term_id; if ($term_id != 0) { wp_delete_term($term_id, $taxonomy); clean_term_cache($term_id, $taxonomy); $counter ++; } } if ($counter == 0) { add_settings_error(__CLASS__, __CLASS__, esc_html__('No term deleted.', 'simple-tags'), 'updated taxopress-notice'); } else { add_settings_error(__CLASS__, __CLASS__, sprintf(esc_html__('%1s term(s) deleted.', 'simple-tags'), $counter), 'updated taxopress-notice'); } return true; } /** * Method for add terms for all or specified posts * * @param string $taxonomy * @param string $match * @param string $new * * @return boolean * @author WebFactory Ltd */ public static function addMatchTerms($taxonomy = 'post_tag', $match = '', $new = '') { // Helper function to extract term name (ignoring slug in brackets) $extractTermName = function ($term) { return trim(preg_replace('/\s*\(.*?\)$/', '', $term)); }; if (trim(str_replace(',', '', stripslashes($new))) == '') { add_settings_error(__CLASS__, __CLASS__, esc_html__('No new term(s) specified!', 'simple-tags'), 'error taxopress-notice'); return false; } $match_terms = explode(',', $match); $new_terms = explode(',', $new); // Clean up terms by removing slugs $match_terms = array_map($extractTermName, $match_terms); $new_terms = array_map($extractTermName, $new_terms); $match_terms = array_filter($match_terms, '_delete_empty_element'); $new_terms = array_filter($new_terms, '_delete_empty_element'); $counter = 0; if (! empty($match_terms)) { // Match and add // Get terms ID from old match names $terms_id = array(); foreach ((array) $match_terms as $match_term) { $term = get_term_by('name', sanitize_text_field($match_term), $taxonomy); $terms_id[] = (int) $term->term_id; } // Get object ID with terms ID $objects_id = get_objects_in_term($terms_id, $taxonomy, array( 'fields' => 'all_with_object_id' )); // Add new tags for specified post foreach ((array) $objects_id as $object_id) { wp_set_object_terms($object_id, $new_terms, $taxonomy, true); // Append terms $counter ++; } // Clean cache clean_object_term_cache($objects_id, $taxonomy); clean_term_cache($terms_id, $taxonomy); } else { // Add for all posts // Page or not ? $post_type_sql = "(post_status = 'publish' OR post_status = 'inherit') AND post_type = '".SimpleTags_Admin::$post_type."'"; // Get all posts ID global $wpdb; $objects_id = $wpdb->get_col("SELECT ID FROM {$wpdb->posts} WHERE {$post_type_sql}"); // Add new tags for all posts foreach ((array) $objects_id as $object_id) { wp_set_object_terms($object_id, $new_terms, $taxonomy, true); // Append terms clean_object_term_cache($object_id, $taxonomy); clean_term_cache($new_terms, $taxonomy); $counter ++; } // Clean cache clean_object_term_cache($objects_id, $taxonomy); } if ($counter == 0) { add_settings_error(__CLASS__, __CLASS__, esc_html__('No term added.', 'simple-tags'), 'updated taxopress-notice'); } else { 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'); } return true; } /** * Delete terms when counter if inferior to a specific number * * @param string $taxonomy * @param integer $number * * @return boolean * @author WebFactory Ltd */ public static function removeRarelyUsed($taxonomy = 'post_tag', $number = 0) { global $wpdb; if ((int) $number > 100) { wp_die('Tcheater ?'); } // Get terms with counter inferior to... $terms_id = $wpdb->get_col($wpdb->prepare("SELECT term_id FROM $wpdb->term_taxonomy WHERE taxonomy = %s AND count < %d", $taxonomy, (int) $number)); // Delete terms $counter = 0; foreach ((array) $terms_id as $term_id) { if ($term_id != 0) { wp_delete_term($term_id, $taxonomy); clean_term_cache($term_id, $taxonomy); $counter ++; } } if ($counter == 0) { add_settings_error(__CLASS__, __CLASS__, esc_html__('No term deleted.', 'simple-tags'), 'updated taxopress-notice'); } else { add_settings_error(__CLASS__, __CLASS__, sprintf(esc_html__('%1s term(s) deleted.', 'simple-tags'), $counter), 'updated taxopress-notice'); } return true; } function handle_taxopress_check_delete_terms_ajax() { if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'st-admin-js')) { wp_send_json_error(array('message' => __('Nonce verification failed.', 'simple-tags'))); wp_die(); } global $wpdb; $taxonomy = isset($_POST['taxonomy']) ? sanitize_text_field($_POST['taxonomy']) : 'post_tag'; $number = isset($_POST['number']) ? intval($_POST['number']) : 0; if ((int) $number > 100) { wp_die('Tcheater ?'); } if ($number > 0) { $terms = $wpdb->get_col($wpdb->prepare(" SELECT term_id FROM $wpdb->term_taxonomy WHERE taxonomy = %s AND count < %d", $taxonomy, (int) $number)); $term_count = count($terms); if ($term_count > 0) { wp_send_json_success(array('message' => sprintf(__('%d terms will be deleted.', 'simple-tags'), $term_count))); } else { wp_send_json_error(array('message' => __('No terms will be deleted.', 'simple-tags'))); } } else { wp_send_json_error(array('message' => __('Invalid number specified.', 'simple-tags'))); } wp_die(); } /** * Method for removing terms from all or specified posts * * @param string $taxonomy * @param string $match * @param string $remove * * @return boolean * @author WebFactory Ltd */ public static function removeMatchTerms($taxonomy = 'post_tag', $match = '', $remove = '') { // Helper function to extract term name (ignoring slug in brackets) $extractTermName = function ($term) { return trim(preg_replace('/\s*\(.*?\)$/', '', $term)); }; if (trim(str_replace(',', '', stripslashes($remove))) == '') { add_settings_error(__CLASS__, __CLASS__, esc_html__('No term(s) specified for removal!', 'simple-tags'), 'error taxopress-notice'); return false; } $match_terms = explode(',', $match); $remove_terms = explode(',', $remove); // Clean up terms by removing slugs $match_terms = array_map($extractTermName, $match_terms); $remove_terms = array_map($extractTermName, $remove_terms); $match_terms = array_filter($match_terms, '_delete_empty_element'); $remove_terms = array_filter($remove_terms, '_delete_empty_element'); // Arrays to track if terms entered is valid $valid_remove_terms = array(); $invalid_remove_terms = array(); foreach ((array) $remove_terms as $remove_term) { $term = get_term_by('name', sanitize_text_field($remove_term), $taxonomy); if ($term) { $valid_remove_terms[] = $remove_term; // Add to valid list if the term exists } else { $invalid_remove_terms[] = $remove_term; // Collect invalid remove terms } } if (empty($valid_remove_terms)) { add_settings_error(__CLASS__, __CLASS__, esc_html__('Term(s) does not exist.', 'simple-tags'), 'error taxopress-notice'); return false; } $counter = 0; if (!empty($match_terms)) { // Get terms ID from match terms $terms_id = array(); foreach ((array) $match_terms as $match_term) { $term = get_term_by('name', sanitize_text_field($match_term), $taxonomy); if ($term) { $terms_id[] = (int) $term->term_id; } } // Get object ID with terms ID $objects_id = get_objects_in_term($terms_id, $taxonomy, array('fields' => 'all_with_object_id')); // Remove specified terms from matched posts foreach ((array) $objects_id as $object_id) { wp_remove_object_terms($object_id, $valid_remove_terms, $taxonomy); $counter++; } clean_object_term_cache($objects_id, $taxonomy); clean_term_cache($terms_id, $taxonomy); } else { // Get all posts if no match terms were provided global $wpdb; $post_type_sql = "(post_status = 'publish' OR post_status = 'inherit') AND post_type = '".SimpleTags_Admin::$post_type."'"; $objects_id = $wpdb->get_col("SELECT ID FROM {$wpdb->posts} WHERE {$post_type_sql}"); // Remove valid terms for all posts foreach ((array) $objects_id as $object_id) { wp_remove_object_terms($object_id, $valid_remove_terms, $taxonomy); clean_object_term_cache($object_id, $taxonomy); clean_term_cache($valid_remove_terms, $taxonomy); $counter++; } clean_object_term_cache($objects_id, $taxonomy); } if ($counter == 0) { add_settings_error(__CLASS__, __CLASS__, esc_html__('No matching term found.', 'simple-tags'), 'updated taxopress-notice'); } else { 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'); } return true; } public static function handle_taxopress_autocomplete_terms() { if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'st-admin-js')) { wp_send_json_error(['message' => __('Nonce verification failed.', 'simple-tags')]); wp_die(); } $taxonomy = isset($_POST['taxonomy']) ? sanitize_text_field($_POST['taxonomy']) : 'post_tag'; $term = isset($_POST['term']) ? sanitize_text_field($_POST['term']) : ''; $terms = get_terms([ 'taxonomy' => $taxonomy, 'name__like' => $term, 'hide_empty' => false, ]); $results = []; foreach ( $terms as $term ) { $results[] = array( 'name' => html_entity_decode( $term->name, ENT_QUOTES, get_bloginfo( 'charset' ) ), 'slug' => $term->slug, ); } wp_send_json($results); wp_die(); } /** * Method for edit one or more terms slug * * @param string $taxonomy * @param string $names * @param string $slugs * * @return boolean * @author WebFactory Ltd */ /* public static function editTermSlug( $taxonomy = 'post_tag', $names = '', $slugs = '') { if ( trim( str_replace(',', '', stripslashes($slugs)) ) == '' ) { add_settings_error( __CLASS__, __CLASS__, esc_html__('No new slug(s) specified!', 'simple-tags'), 'error' ); return false; } $match_names = explode(',', $names); $new_slugs = explode(',', $slugs); $match_names = array_filter($match_names, '_delete_empty_element'); $new_slugs = array_filter($new_slugs, '_delete_empty_element'); if ( count($match_names) != count($new_slugs) ) { add_settings_error( __CLASS__, __CLASS__, esc_html__('Terms number and slugs number isn\'t the same!', 'simple-tags'), 'error' ); return false; } else { $counter = 0; foreach ( (array) $match_names as $i => $match_name ) { // Sanitize slug + Escape $new_slug = sanitize_title($new_slugs[$i]); // Get term by name $term = get_term_by('name', $match_name, $taxonomy); if ( !$term ) { continue; } // Increment $counter++; // Update term wp_update_term($term->term_id, $taxonomy, array('slug' => $new_slug)); // Clean cache clean_term_cache($term->term_id, $taxonomy); } } if ( $counter == 0 ) { add_settings_error( __CLASS__, __CLASS__, esc_html__('No slug edited.', 'simple-tags'), 'updated' ); } else { add_settings_error( __CLASS__, __CLASS__, sprintf(esc_html__('%s slug(s) edited.', 'simple-tags'), $counter), 'updated' ); } return true; } */ /** Singleton instance */ public static function get_instance() { if (! isset(self::$instance)) { self::$instance = new self(); } return self::$instance; } } ?>