Changeset 3224298
- Timestamp:
- 01/17/2025 02:24:53 PM (15 months ago)
- Location:
- duplicate-title-validate
- Files:
-
- 35 added
- 3 edited
-
tags/1.6 (added)
-
tags/1.6/duplicate-title-validate.php (added)
-
tags/1.6/inc (added)
-
tags/1.6/inc/class-Similar-Titles-Widget.php (added)
-
tags/1.6/inc/class-classic-editor.php (added)
-
tags/1.6/inc/class-duplicate-title-validate.php (added)
-
tags/1.6/inc/class-gutenberg.php (added)
-
tags/1.6/inc/class-rest-api.php (added)
-
tags/1.6/inc/class-settings.php (added)
-
tags/1.6/inc/class-title_checker.php (added)
-
tags/1.6/index.php (added)
-
tags/1.6/js (added)
-
tags/1.6/js/duplicate-title-validate.js (added)
-
tags/1.6/js/gutenberg-duplicate-titles.js (added)
-
tags/1.6/languages (added)
-
tags/1.6/languages/duplicate-title-validate-fa_IR.l10n.php (added)
-
tags/1.6/languages/duplicate-title-validate-fa_IR.mo (added)
-
tags/1.6/languages/duplicate-title-validate-fa_IR.po (added)
-
tags/1.6/languages/duplicate-title-validate.pot (added)
-
tags/1.6/readme.txt (added)
-
tags/1.6/screenshot.png (added)
-
trunk/duplicate-title-validate.php (modified) (2 diffs)
-
trunk/inc (added)
-
trunk/inc/class-Similar-Titles-Widget.php (added)
-
trunk/inc/class-classic-editor.php (added)
-
trunk/inc/class-duplicate-title-validate.php (added)
-
trunk/inc/class-gutenberg.php (added)
-
trunk/inc/class-rest-api.php (added)
-
trunk/inc/class-settings.php (added)
-
trunk/inc/class-title_checker.php (added)
-
trunk/js (added)
-
trunk/js/duplicate-title-validate.js (added)
-
trunk/js/gutenberg-duplicate-titles.js (added)
-
trunk/languages/duplicate-title-validate-fa_IR.l10n.php (added)
-
trunk/languages/duplicate-title-validate-fa_IR.mo (added)
-
trunk/languages/duplicate-title-validate-fa_IR.po (added)
-
trunk/languages/duplicate-title-validate.pot (modified) (1 diff)
-
trunk/readme.txt (modified) (4 diffs)
Legend:
- Unmodified
- Added
- Removed
-
duplicate-title-validate/trunk/duplicate-title-validate.php
r3207970 r3224298 2 2 /* 3 3 * Plugin Name: Duplicate Title Validate 4 * Description: Prevents publishing posts with duplicate titles and identifies conflicting post types or taxonomies. Compatible with Classic Editor and Gutenberg 5 * Version: 1. 54 * Description: Prevents publishing posts with duplicate titles and identifies conflicting post types or taxonomies. Compatible with Classic Editor and Gutenberg. 5 * Version: 1.6 6 6 * Author: Hasan Movahed 7 7 * Text Domain: duplicate-title-validate … … 13 13 } 14 14 15 class Duplicate_Title_Validate {16 15 17 public function __construct() { 18 // Load plugin text domain for translations 19 add_action('plugins_loaded', [$this, 'load_textdomain']); 16 require_once plugin_dir_path(__FILE__) . 'inc/class-duplicate-title-validate.php'; 17 require_once plugin_dir_path(__FILE__) . 'inc/class-Similar-Titles-Widget.php'; 18 require_once plugin_dir_path(__FILE__) . 'inc/class-title_checker.php'; 19 require_once plugin_dir_path(__FILE__) . 'inc/class-settings.php'; 20 require_once plugin_dir_path(__FILE__) . 'inc/class-rest-api.php'; 21 require_once plugin_dir_path(__FILE__) . 'inc/class-classic-editor.php'; 22 require_once plugin_dir_path(__FILE__) . 'inc/class-gutenberg.php'; 20 23 21 // Hooks22 add_filter('rest_pre_insert_post', [$this, 'check_duplicate_title_rest'], 10, 2);23 add_action('publish_post', [$this, 'check_duplicate_title_classic']);24 add_action('admin_notices', [$this, 'not_published_error_notice']);25 add_action('wp_print_scripts', [$this, 'disable_autosave']);26 }27 24 28 /**29 * Load plugin text domain for translations30 */31 public function load_textdomain() {32 load_plugin_textdomain('duplicate-title-validate', false, dirname(plugin_basename(__FILE__)) . '/languages');33 }34 25 35 /**36 * Check for duplicate title in Gutenberg (REST API)37 *38 * @param mixed $prepared_post The prepared post object or array.39 * @param WP_REST_Request $request The current request.40 * @return mixed WP_Error on duplicate, otherwise the prepared post.41 */42 public function check_duplicate_title_rest($prepared_post, $request) {43 // Verify user permissions44 if (!is_user_logged_in() || !current_user_can('edit_posts')) {45 return new WP_Error(46 'rest_forbidden',47 __('You are not allowed to do this.', 'duplicate-title-validate'),48 ['status' => 403]49 );50 }51 52 global $wpdb;53 $title = '';54 $post_id = 0;55 56 // Determine if $prepared_post is an object or array57 if (is_object($prepared_post)) {58 $title = !empty($prepared_post->post_title) ? sanitize_text_field($prepared_post->post_title) : '';59 $post_id = !empty($prepared_post->ID) ? (int)$prepared_post->ID : 0;60 } elseif (is_array($prepared_post)) {61 $title = isset($prepared_post['post_title']) ? sanitize_text_field($prepared_post['post_title']) : '';62 $post_id = !empty($prepared_post['ID']) ? (int)$prepared_post['ID'] : 0;63 }64 65 if (empty($title)) {66 return $prepared_post;67 }68 69 // Check for duplicate posts70 $post_results = $wpdb->get_results($wpdb->prepare(71 "SELECT ID, post_type FROM $wpdb->posts72 WHERE post_status = 'publish' AND post_title = %s AND ID != %d",73 $title, $post_id74 ));75 76 // Check for duplicate terms across all taxonomies77 $taxonomy_results = $wpdb->get_results($wpdb->prepare(78 "SELECT DISTINCT tt.taxonomy79 FROM $wpdb->terms t80 INNER JOIN $wpdb->term_taxonomy tt ON t.term_id = tt.term_id81 WHERE t.name = %s",82 $title83 ));84 85 // If no duplicates in posts or taxonomies, return the post86 if (empty($post_results) && empty($taxonomy_results)) {87 return $prepared_post;88 }89 90 // Collect sources of duplicates91 $sources = [];92 93 // If there are duplicate posts94 if (!empty($post_results)) {95 $post_types_list = $this->get_post_type_labels($post_results);96 $sources[] = $post_types_list;97 }98 99 // If there are duplicate taxonomies100 if (!empty($taxonomy_results)) {101 $taxonomy_names = [];102 foreach ($taxonomy_results as $taxonomy) {103 $tax_obj = get_taxonomy($taxonomy->taxonomy);104 $tax_label = ($tax_obj && !empty($tax_obj->labels->singular_name)) ? $tax_obj->labels->singular_name : $taxonomy->taxonomy;105 $taxonomy_names[] = $tax_label;106 }107 $taxonomies_list = implode(', ', array_unique($taxonomy_names));108 $sources[] = $taxonomies_list;109 }110 111 $combined_sources = implode(', ', $sources);112 113 // Return WP_Error with detailed sources114 return new WP_Error(115 'duplicate_title_error',116 '-- ' . sprintf(__('Duplicate title detected in: %s. Please change the title.', 'duplicate-title-validate'), $combined_sources),117 ['status' => 400]118 );119 }120 121 /**122 * Check for duplicate title in Classic Editor123 *124 * @param int $post_id The ID of the post being published.125 */126 public function check_duplicate_title_classic($post_id) {127 if (!isset($_POST['post_title'])) {128 return;129 }130 131 global $wpdb;132 $title = sanitize_text_field($_POST['post_title']);133 134 // Check for duplicate posts135 $post_results = $wpdb->get_results($wpdb->prepare(136 "SELECT post_title, post_type FROM $wpdb->posts137 WHERE post_status = 'publish' AND post_title = %s AND ID != %d",138 $title, $post_id139 ));140 141 // Check for duplicate terms across all taxonomies142 $taxonomy_results = $wpdb->get_results($wpdb->prepare(143 "SELECT DISTINCT tt.taxonomy144 FROM $wpdb->terms t145 INNER JOIN $wpdb->term_taxonomy tt ON t.term_id = tt.term_id146 WHERE t.name = %s",147 $title148 ));149 150 // If no duplicates found, do nothing151 if (empty($post_results) && empty($taxonomy_results)) {152 return;153 }154 155 // Collect sources of duplicates156 $sources = [];157 158 // If there are duplicate posts159 if (!empty($post_results)) {160 $post_types_list = $this->get_post_type_labels($post_results);161 $sources[] = $post_types_list;162 }163 164 // If there are duplicate taxonomies165 if (!empty($taxonomy_results)) {166 $taxonomy_names = [];167 foreach ($taxonomy_results as $taxonomy) {168 $tax_obj = get_taxonomy($taxonomy->taxonomy);169 $tax_label = ($tax_obj && !empty($tax_obj->labels->singular_name)) ? $tax_obj->labels->singular_name : $taxonomy->taxonomy;170 $taxonomy_names[] = $tax_label;171 }172 $taxonomies_list = implode(', ', array_unique($taxonomy_names));173 $sources[] = $taxonomies_list;174 }175 176 $combined_sources = implode(', ', $sources);177 178 // Prepare the error message179 $error_message = sprintf(180 __('Title used for this post appears to be a duplicate in: %s. Please modify the title.', 'duplicate-title-validate'),181 $combined_sources182 );183 184 // Update the post status to draft185 $wpdb->update($wpdb->posts, ['post_status' => 'draft'], ['ID' => $post_id]);186 187 // Redirect back to the edit page with error message188 $arr_params = ['message' => '10', 'wallfaerror' => '1', 'wallfaerror_msg' => urlencode($error_message)];189 $location = add_query_arg($arr_params, get_edit_post_link($post_id, 'url'));190 wp_redirect($location);191 exit;192 }193 194 /**195 * Display error message in Classic Editor196 */197 public function not_published_error_notice() {198 if (isset($_GET['wallfaerror']) && $_GET['wallfaerror'] == '1') {199 $msg = isset($_GET['wallfaerror_msg']) ? urldecode($_GET['wallfaerror_msg']) : __('Title used for this post appears to be a duplicate. Please modify the title.', 'duplicate-title-validate');200 echo '<div class="error"><p style="color:red">' . esc_html($msg) . '</p></div>';201 }202 }203 204 /**205 * Disable autosave206 */207 public function disable_autosave() {208 wp_deregister_script('autosave');209 }210 211 /**212 * Helper: Get post type labels for displaying in error messages213 *214 * @param array $results Array of post objects.215 * @return string Comma-separated list of post type labels.216 */217 protected function get_post_type_labels($results) {218 $types = [];219 foreach ($results as $r) {220 $pt_obj = get_post_type_object($r->post_type);221 $pt_label = ($pt_obj && !empty($pt_obj->labels->singular_name)) ? $pt_obj->labels->singular_name : $r->post_type;222 $types[] = $pt_label;223 }224 return implode(', ', array_unique($types));225 }226 }227 228 /**229 * Initialize the plugin230 */231 26 function dtv_init() { 232 27 new Duplicate_Title_Validate(); -
duplicate-title-validate/trunk/languages/duplicate-title-validate.pot
r3207787 r3224298 1 #, fuzzy 1 2 msgid "" 2 3 msgstr "" 3 "Project-Id-Version: Duplicate Title Validate 1.4\n"4 "Project-Id-Version: Duplicate Title Validate\n" 4 5 "Report-Msgid-Bugs-To: \n" 5 "POT-Creation-Date: 202 4-12-13 00:00+0000\n"6 "PO-Revision-Date: 2024-12-13 00:00+0000\n"7 "Last-Translator: \n"8 "Language-Team: Persian\n"6 "POT-Creation-Date: 2025-01-17 09:29+0000\n" 7 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 8 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" 9 "Language-Team: \n" 9 10 "Language: \n" 11 "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" 10 12 "MIME-Version: 1.0\n" 11 13 "Content-Type: text/plain; charset=UTF-8\n" 12 14 "Content-Transfer-Encoding: 8bit\n" 15 "X-Generator: Loco https://localise.biz/\n" 16 "X-Loco-Version: 2.6.14; wp-6.7.1\n" 17 "X-Domain: duplicate-title-validate" 13 18 14 #: duplicate-title-validate.php:... 15 msgid "You are not allowed to do this." 19 #. ID 20 #: inc/class-settings.php:50 21 msgid "Allow Duplicate Titles" 16 22 msgstr "" 17 23 18 #: duplicate-title-validate.php:... 24 #: inc/class-settings.php:174 25 msgid "Allow publishing posts with duplicate titles" 26 msgstr "" 27 28 #: inc/class-settings.php:153 29 msgid "Configure the main settings for the Duplicate Title Validate plugin." 30 msgstr "" 31 32 #: inc/class-settings.php:162 33 msgid "Configure the settings for the Dashboard Widget." 34 msgstr "" 35 36 #: inc/class-settings.php:161 37 msgid "Dashboard Widget Settings" 38 msgstr "" 39 40 #: inc/class-settings.php:19 41 msgid "Duplicate Title" 42 msgstr "" 43 44 #: inc/class-gutenberg.php:107 45 msgid "Duplicate Title Checker" 46 msgstr "" 47 48 #: duplicate-title-validate/trunk/duplicate-title-validate.php:116 49 #, php-format 19 50 msgid "Duplicate title detected in: %s. Please change the title." 20 51 msgstr "" 21 52 22 #: duplicate-title-validate.php:... 23 msgid "Title used for this post appears to be a duplicate in: %s. Please modify the title." 53 #: inc/class-gutenberg.php:73 54 #, php-format 55 msgid "Duplicate title detected in: %s. You can still publish the post." 24 56 msgstr "" 25 57 26 #: duplicate-title-validate.php:... 58 #. Name of the plugin 59 msgid "Duplicate Title Validate" 60 msgstr "" 61 62 #: inc/class-settings.php:18 inc/class-settings.php:119 63 msgid "Duplicate Title Validate Settings" 64 msgstr "" 65 66 #: inc/class-gutenberg.php:96 67 msgid "Duplicate titles found:" 68 msgstr "" 69 70 #: inc/class-Similar-Titles-Widget.php:47 71 msgid "Edit" 72 msgstr "" 73 74 #: inc/class-Similar-Titles-Widget.php:50 75 msgid "Edit link not available" 76 msgstr "" 77 78 #: inc/class-Similar-Titles-Widget.php:27 79 msgid "Global Similar Titles" 80 msgstr "" 81 82 #. Author of the plugin 83 msgid "Hasan Movahed" 84 msgstr "" 85 86 #. ID 87 #: inc/class-settings.php:67 88 msgid "Limit for Items" 89 msgstr "" 90 91 #. ID 92 #: inc/class-settings.php:42 93 msgid "Main Settings" 94 msgstr "" 95 96 #. ID 97 #: inc/class-settings.php:83 98 msgid "Max Similar Items" 99 msgstr "" 100 101 #: inc/class-gutenberg.php:97 102 msgid "No duplicate titles found." 103 msgstr "" 104 105 #: inc/class-Similar-Titles-Widget.php:59 106 msgid "No similar titles found." 107 msgstr "" 108 109 #. Description of the plugin 110 msgid "" 111 "Prevents publishing posts with duplicate titles and identifies conflicting " 112 "post types or taxonomies. Compatible with Classic Editor and Gutenberg." 113 msgstr "" 114 115 #: inc/class-settings.php:111 116 msgid "Settings Saved" 117 msgstr "" 118 119 #: inc/class-Similar-Titles-Widget.php:37 120 msgid "Similar Titles Across All Types:" 121 msgstr "" 122 123 #: inc/class-classic-editor.php:46 124 msgid "Similar Titles:" 125 msgstr "" 126 127 #. ID 128 #: inc/class-settings.php:75 129 msgid "Similarity Threshold" 130 msgstr "" 131 132 #: inc/class-gutenberg.php:62 inc/class-gutenberg.php:88 133 msgid "Title is empty." 134 msgstr "" 135 136 #: inc/class-classic-editor.php:108 137 #: duplicate-title-validate/trunk/duplicate-title-validate.php:180 138 #, php-format 139 msgid "" 140 "Title used for this post appears to be a duplicate in: %s. Please modify the " 141 "title." 142 msgstr "" 143 144 #: duplicate-title-validate/trunk/duplicate-title-validate.php:199 145 msgid "" 146 "Title used for this post appears to be a duplicate. Please modify the title." 147 msgstr "" 148 149 #: duplicate-title-validate/trunk/duplicate-title-validate.php:47 27 150 msgid "You are not allowed to do this." 28 151 msgstr "" 29 30 #: duplicate-title-validate.php:...31 msgid "Title is required."32 msgstr ""33 34 #: duplicate-title-validate.php:...35 msgid "You are not allowed to do this."36 msgstr ""37 38 #: duplicate-title-validate.php:...39 msgid "Duplicate title detected. Please change the title."40 msgstr ""41 42 #: duplicate-title-validate.php:...43 msgid "Duplicate title detected in: %s. Please change the title."44 msgstr ""45 46 #: duplicate-title-validate.php:...47 msgid "Duplicate title detected in: %s. Please change the title."48 msgstr ""49 50 #: duplicate-title-validate.php:...51 msgid "Title used for this post appears to be a duplicate in: %s. Please modify the title."52 msgstr "" -
duplicate-title-validate/trunk/readme.txt
r3207967 r3224298 4 4 Tested up to: 6.4 5 5 Requires PHP: 7.0 6 Stable Tag: 1. 56 Stable Tag: 1.6 7 7 License: GPLv2 or later 8 8 License URI: http://www.gnu.org/licenses/gpl-2.0.html … … 21 21 - **Localization Support:** Fully translated into English and Persian, with options to add other languages. 22 22 - **Editor Compatibility:** Functions smoothly with both Gutenberg and Classic Editor. 23 24 ### New in Version 1.6 25 - **Restructured Plugin Architecture:** The plugin has been rebuilt for better performance, scalability, and maintainability. 26 - **Previously Used Titles Display:** Now you can view titles that have been used before, helping you avoid unintentional duplication. 27 - **Dashboard Widget for Similar Titles:** A new dashboard widget identifies similar or potentially duplicate titles based on advanced rules, allowing you to review and edit them easily. 28 - **Enhanced Editor Compatibility:** Improved integration with both Gutenberg and Classic Editor for a seamless editing experience. 23 29 24 30 == Installation == … … 54 60 == Screenshots == 55 61 56 1. screenshot.png 62 1. screenshot.png 57 63 58 64 == Upgrade Notice == 65 66 ### 1.6 67 - **Restructured plugin architecture for better performance and maintainability.** 68 - **Added a dashboard widget to identify and manage similar titles.** 69 - **Introduced a feature to display previously used titles.** 70 - **Improved compatibility with Gutenberg and Classic Editor.** 71 - **Enhanced localization support for English and Persian.** 59 72 60 73 ### 1.5 … … 84 97 85 98 == Changelog == 99 100 ### 1.6 101 - **Restructured plugin architecture for better performance and maintainability.** 102 - **Added a dashboard widget to identify and manage similar titles.** 103 - **Introduced a feature to display previously used titles.** 104 - **Improved compatibility with Gutenberg and Classic Editor.** 105 - **Enhanced localization support for English and Persian.** 86 106 87 107 ### 1.5
Note: See TracChangeset
for help on using the changeset viewer.