Plugin Directory

Changeset 3224298


Ignore:
Timestamp:
01/17/2025 02:24:53 PM (15 months ago)
Author:
wallfa_hm
Message:

Version 1.6 Released

Location:
duplicate-title-validate
Files:
35 added
3 edited

Legend:

Unmodified
Added
Removed
  • duplicate-title-validate/trunk/duplicate-title-validate.php

    r3207970 r3224298  
    22/*
    33* 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.5
     4* Description: Prevents publishing posts with duplicate titles and identifies conflicting post types or taxonomies. Compatible with Classic Editor and Gutenberg.
     5* Version: 1.6
    66* Author: Hasan Movahed
    77* Text Domain: duplicate-title-validate
     
    1313}
    1414
    15 class Duplicate_Title_Validate {
    1615
    17     public function __construct() {
    18         // Load plugin text domain for translations
    19         add_action('plugins_loaded', [$this, 'load_textdomain']);
     16require_once plugin_dir_path(__FILE__) . 'inc/class-duplicate-title-validate.php';
     17require_once plugin_dir_path(__FILE__) . 'inc/class-Similar-Titles-Widget.php';
     18require_once plugin_dir_path(__FILE__) . 'inc/class-title_checker.php';
     19require_once plugin_dir_path(__FILE__) . 'inc/class-settings.php';
     20require_once plugin_dir_path(__FILE__) . 'inc/class-rest-api.php';
     21require_once plugin_dir_path(__FILE__) . 'inc/class-classic-editor.php';
     22require_once plugin_dir_path(__FILE__) . 'inc/class-gutenberg.php';
    2023
    21         // Hooks
    22         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     }
    2724
    28     /**
    29      * Load plugin text domain for translations
    30      */
    31     public function load_textdomain() {
    32         load_plugin_textdomain('duplicate-title-validate', false, dirname(plugin_basename(__FILE__)) . '/languages');
    33     }
    3425
    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 permissions
    44         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 array
    57         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 posts
    70         $post_results = $wpdb->get_results($wpdb->prepare(
    71             "SELECT ID, post_type FROM $wpdb->posts
    72              WHERE post_status = 'publish' AND post_title = %s AND ID != %d",
    73             $title, $post_id
    74         ));
    75 
    76         // Check for duplicate terms across all taxonomies
    77         $taxonomy_results = $wpdb->get_results($wpdb->prepare(
    78             "SELECT DISTINCT tt.taxonomy
    79              FROM $wpdb->terms t
    80              INNER JOIN $wpdb->term_taxonomy tt ON t.term_id = tt.term_id
    81              WHERE t.name = %s",
    82             $title
    83         ));
    84 
    85         // If no duplicates in posts or taxonomies, return the post
    86         if (empty($post_results) && empty($taxonomy_results)) {
    87             return $prepared_post;
    88         }
    89 
    90         // Collect sources of duplicates
    91         $sources = [];
    92 
    93         // If there are duplicate posts
    94         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 taxonomies
    100         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 sources
    114         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 Editor
    123      *
    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 posts
    135         $post_results = $wpdb->get_results($wpdb->prepare(
    136             "SELECT post_title, post_type FROM $wpdb->posts
    137              WHERE post_status = 'publish' AND post_title = %s AND ID != %d",
    138             $title, $post_id
    139         ));
    140 
    141         // Check for duplicate terms across all taxonomies
    142         $taxonomy_results = $wpdb->get_results($wpdb->prepare(
    143             "SELECT DISTINCT tt.taxonomy
    144              FROM $wpdb->terms t
    145              INNER JOIN $wpdb->term_taxonomy tt ON t.term_id = tt.term_id
    146              WHERE t.name = %s",
    147             $title
    148         ));
    149 
    150         // If no duplicates found, do nothing
    151         if (empty($post_results) && empty($taxonomy_results)) {
    152             return;
    153         }
    154 
    155         // Collect sources of duplicates
    156         $sources = [];
    157 
    158         // If there are duplicate posts
    159         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 taxonomies
    165         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 message
    179         $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_sources
    182         );
    183 
    184         // Update the post status to draft
    185         $wpdb->update($wpdb->posts, ['post_status' => 'draft'], ['ID' => $post_id]);
    186 
    187         // Redirect back to the edit page with error message
    188         $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 Editor
    196      */
    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 autosave
    206      */
    207     public function disable_autosave() {
    208         wp_deregister_script('autosave');
    209     }
    210 
    211     /**
    212      * Helper: Get post type labels for displaying in error messages
    213      *
    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 plugin
    230  */
    23126function dtv_init() {
    23227    new Duplicate_Title_Validate();
  • duplicate-title-validate/trunk/languages/duplicate-title-validate.pot

    r3207787 r3224298  
     1#, fuzzy
    12msgid ""
    23msgstr ""
    3 "Project-Id-Version: Duplicate Title Validate 1.4\n"
     4"Project-Id-Version: Duplicate Title Validate\n"
    45"Report-Msgid-Bugs-To: \n"
    5 "POT-Creation-Date: 2024-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"
    910"Language: \n"
     11"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
    1012"MIME-Version: 1.0\n"
    1113"Content-Type: text/plain; charset=UTF-8\n"
    1214"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"
    1318
    14 #: duplicate-title-validate.php:...
    15 msgid "You are not allowed to do this."
     19#. ID
     20#: inc/class-settings.php:50
     21msgid "Allow Duplicate Titles"
    1622msgstr ""
    1723
    18 #: duplicate-title-validate.php:...
     24#: inc/class-settings.php:174
     25msgid "Allow publishing posts with duplicate titles"
     26msgstr ""
     27
     28#: inc/class-settings.php:153
     29msgid "Configure the main settings for the Duplicate Title Validate plugin."
     30msgstr ""
     31
     32#: inc/class-settings.php:162
     33msgid "Configure the settings for the Dashboard Widget."
     34msgstr ""
     35
     36#: inc/class-settings.php:161
     37msgid "Dashboard Widget Settings"
     38msgstr ""
     39
     40#: inc/class-settings.php:19
     41msgid "Duplicate Title"
     42msgstr ""
     43
     44#: inc/class-gutenberg.php:107
     45msgid "Duplicate Title Checker"
     46msgstr ""
     47
     48#: duplicate-title-validate/trunk/duplicate-title-validate.php:116
     49#, php-format
    1950msgid "Duplicate title detected in: %s. Please change the title."
    2051msgstr ""
    2152
    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
     55msgid "Duplicate title detected in: %s. You can still publish the post."
    2456msgstr ""
    2557
    26 #: duplicate-title-validate.php:...
     58#. Name of the plugin
     59msgid "Duplicate Title Validate"
     60msgstr ""
     61
     62#: inc/class-settings.php:18 inc/class-settings.php:119
     63msgid "Duplicate Title Validate Settings"
     64msgstr ""
     65
     66#: inc/class-gutenberg.php:96
     67msgid "Duplicate titles found:"
     68msgstr ""
     69
     70#: inc/class-Similar-Titles-Widget.php:47
     71msgid "Edit"
     72msgstr ""
     73
     74#: inc/class-Similar-Titles-Widget.php:50
     75msgid "Edit link not available"
     76msgstr ""
     77
     78#: inc/class-Similar-Titles-Widget.php:27
     79msgid "Global Similar Titles"
     80msgstr ""
     81
     82#. Author of the plugin
     83msgid "Hasan Movahed"
     84msgstr ""
     85
     86#. ID
     87#: inc/class-settings.php:67
     88msgid "Limit for Items"
     89msgstr ""
     90
     91#. ID
     92#: inc/class-settings.php:42
     93msgid "Main Settings"
     94msgstr ""
     95
     96#. ID
     97#: inc/class-settings.php:83
     98msgid "Max Similar Items"
     99msgstr ""
     100
     101#: inc/class-gutenberg.php:97
     102msgid "No duplicate titles found."
     103msgstr ""
     104
     105#: inc/class-Similar-Titles-Widget.php:59
     106msgid "No similar titles found."
     107msgstr ""
     108
     109#. Description of the plugin
     110msgid ""
     111"Prevents publishing posts with duplicate titles and identifies conflicting "
     112"post types or taxonomies. Compatible with Classic Editor and Gutenberg."
     113msgstr ""
     114
     115#: inc/class-settings.php:111
     116msgid "Settings Saved"
     117msgstr ""
     118
     119#: inc/class-Similar-Titles-Widget.php:37
     120msgid "Similar Titles Across All Types:"
     121msgstr ""
     122
     123#: inc/class-classic-editor.php:46
     124msgid "Similar Titles:"
     125msgstr ""
     126
     127#. ID
     128#: inc/class-settings.php:75
     129msgid "Similarity Threshold"
     130msgstr ""
     131
     132#: inc/class-gutenberg.php:62 inc/class-gutenberg.php:88
     133msgid "Title is empty."
     134msgstr ""
     135
     136#: inc/class-classic-editor.php:108
     137#: duplicate-title-validate/trunk/duplicate-title-validate.php:180
     138#, php-format
     139msgid ""
     140"Title used for this post appears to be a duplicate in: %s. Please modify the "
     141"title."
     142msgstr ""
     143
     144#: duplicate-title-validate/trunk/duplicate-title-validate.php:199
     145msgid ""
     146"Title used for this post appears to be a duplicate. Please modify the title."
     147msgstr ""
     148
     149#: duplicate-title-validate/trunk/duplicate-title-validate.php:47
    27150msgid "You are not allowed to do this."
    28151msgstr ""
    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  
    44Tested up to: 6.4
    55Requires PHP: 7.0
    6 Stable Tag: 1.5
     6Stable Tag: 1.6
    77License: GPLv2 or later
    88License URI: http://www.gnu.org/licenses/gpl-2.0.html
     
    2121- **Localization Support:** Fully translated into English and Persian, with options to add other languages.
    2222- **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.
    2329
    2430== Installation ==
     
    5460== Screenshots ==
    5561
    56 1. screenshot.png 
     621. screenshot.png
    5763
    5864== 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.**
    5972
    6073### 1.5
     
    8497
    8598== 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.**
    86106
    87107### 1.5
Note: See TracChangeset for help on using the changeset viewer.