| 1 |
<?php |
| 2 |
|
| 3 |
/* |
| 4 |
Plugin Name: Delete Duplicate Posts |
| 5 |
Plugin Script: delete-duplicate-posts.php |
| 6 |
Plugin URI: https://cleverplugins.com |
| 7 |
Description: Remove duplicate blogposts on your blog! Searches and removes duplicate posts and their post meta tags. You can delete posts, pages and other Custom Post Types enabled on your website. |
| 8 |
Version: 5.0.3 |
| 9 |
Author: cleverplugins.com |
| 10 |
Author URI: https://cleverplugins.com |
| 11 |
Min WP Version: 4.7 |
| 12 |
Max WP Version: 6.9.1 |
| 13 |
Text Domain: delete-duplicate-posts |
| 14 |
Domain Path: /languages |
| 15 |
*/ |
| 16 |
namespace DeleteDuplicatePosts; |
| 17 |
|
| 18 |
if ( !defined( 'ABSPATH' ) ) { |
| 19 |
exit; |
| 20 |
} |
| 21 |
require_once __DIR__ . '/vendor/autoload.php'; |
| 22 |
if ( function_exists( '\\DeleteDuplicatePosts\\ddp_fs' ) ) { |
| 23 |
\DeleteDuplicatePosts\ddp_fs()->set_basename( false, __FILE__ ); |
| 24 |
} else { |
| 25 |
// DO NOT REMOVE THIS IF, IT IS ESSENTIAL FOR THE `function_exists` CALL ABOVE TO PROPERLY WORK. |
| 26 |
if ( !function_exists( '\\DeleteDuplicatePosts\\ddp_fs' ) ) { |
| 27 |
// Create a helper function for easy SDK access. |
| 28 |
function ddp_fs() { |
| 29 |
global $ddp_fs; |
| 30 |
if ( !isset( $ddp_fs ) ) { |
| 31 |
// Activate multisite network integration. |
| 32 |
if ( !defined( 'WP_FS__PRODUCT_925_MULTISITE' ) ) { |
| 33 |
define( 'WP_FS__PRODUCT_925_MULTISITE', true ); |
| 34 |
} |
| 35 |
// Include Freemius SDK. |
| 36 |
// SDK is auto-loaded through composer |
| 37 |
$ddp_fs = fs_dynamic_init( array( |
| 38 |
'id' => '925', |
| 39 |
'slug' => 'delete-duplicate-posts', |
| 40 |
'type' => 'plugin', |
| 41 |
'public_key' => 'pk_0af9f9e83f00e23728a55430a57dd', |
| 42 |
'is_premium' => false, |
| 43 |
'premium_suffix' => 'Pro', |
| 44 |
'has_addons' => false, |
| 45 |
'has_paid_plans' => true, |
| 46 |
'menu' => array( |
| 47 |
'slug' => 'delete-duplicate-posts', |
| 48 |
'first-path' => 'tools.php?page=delete-duplicate-posts&welcome-message=true', |
| 49 |
'contact' => false, |
| 50 |
'support' => false, |
| 51 |
'parent' => array( |
| 52 |
'slug' => 'tools.php', |
| 53 |
), |
| 54 |
), |
| 55 |
'is_live' => true, |
| 56 |
'is_org_compliant' => true, |
| 57 |
) ); |
| 58 |
} |
| 59 |
return $ddp_fs; |
| 60 |
} |
| 61 |
|
| 62 |
// Init Freemius. |
| 63 |
ddp_fs(); |
| 64 |
// Signal that SDK was initiated. |
| 65 |
do_action( 'ddp_fs_loaded' ); |
| 66 |
} |
| 67 |
ddp_fs()->add_action( 'after_uninstall', 'ddp_fs_uninstall_cleanup' ); |
| 68 |
} |
| 69 |
/** |
| 70 |
* Cleans up when uninstalling |
| 71 |
* |
| 72 |
* @author Lars Koudal |
| 73 |
* @since v0.0.1 |
| 74 |
* @version v1.0.0 Tuesday, January 12th, 2021. |
| 75 |
* @return void |
| 76 |
*/ |
| 77 |
function ddp_fs_uninstall_cleanup() { |
| 78 |
global $wpdb; |
| 79 |
$wpdb->query( 'DROP TABLE IF EXISTS ' . $wpdb->prefix . 'ddp_log' ); |
| 80 |
$wpdb->query( 'DROP TABLE IF EXISTS ' . $wpdb->prefix . 'ddp_redirects' ); |
| 81 |
delete_option( 'ddp_deleted_duplicates' ); |
| 82 |
delete_option( 'delete_duplicate_posts_options_v4' ); |
| 83 |
} |
| 84 |
|
| 85 |
if ( !class_exists( __NAMESPACE__ . '\\Delete_Duplicate_Posts' ) ) { |
| 86 |
class Delete_Duplicate_Posts { |
| 87 |
public static $options_name = 'delete_duplicate_posts_options_v4'; |
| 88 |
|
| 89 |
public $localization_domain = 'delete-duplicate-posts'; |
| 90 |
|
| 91 |
public static $options = null; |
| 92 |
|
| 93 |
public function __construct() { |
| 94 |
// Adds extra permissions to Freemius |
| 95 |
if ( function_exists( 'ddp_fs' ) ) { |
| 96 |
ddp_fs()->add_filter( 'permission_list', array(__CLASS__, 'add_freemius_extra_permission') ); |
| 97 |
} |
| 98 |
global $ddp_fs; |
| 99 |
add_action( |
| 100 |
'admin_head', |
| 101 |
array(__CLASS__, 'set_custom_help_content'), |
| 102 |
1, |
| 103 |
2 |
| 104 |
); |
| 105 |
self::get_options(); |
| 106 |
add_action( 'wp_ajax_ddp_get_loglines', array(__CLASS__, 'return_loglines_ajax') ); |
| 107 |
add_action( 'wp_ajax_ddp_get_duplicates', array(__CLASS__, 'return_duplicates_ajax') ); |
| 108 |
add_action( 'init', array(__CLASS__, 'do_init') ); |
| 109 |
add_action( 'wp_ajax_ddp_delete_duplicates', array(__CLASS__, 'delete_duplicates_ajax') ); |
| 110 |
// loads admin notices |
| 111 |
add_action( 'admin_menu', array($this, 'admin_menu_link') ); |
| 112 |
add_action( 'admin_enqueue_scripts', array($this, 'admin_enqueue_scripts') ); |
| 113 |
add_action( |
| 114 |
'wp_insert_site', |
| 115 |
array($this, 'on_create_blog'), |
| 116 |
99999, |
| 117 |
1 |
| 118 |
); |
| 119 |
add_filter( 'wpmu_drop_tables', array($this, 'on_delete_blog') ); |
| 120 |
register_activation_hook( __FILE__, array($this, 'install') ); |
| 121 |
add_action( 'ddp_cron', array($this, 'cleandupes') ); |
| 122 |
add_action( 'cron_schedules', array($this, 'add_cron_intervals') ); |
| 123 |
} |
| 124 |
|
| 125 |
/** |
| 126 |
* do_init. |
| 127 |
* |
| 128 |
* @author Lars Koudal |
| 129 |
* @since v0.0.1 |
| 130 |
* @version v1.0.0 Monday, October 28th, 2024. |
| 131 |
* @access public static |
| 132 |
* @return void |
| 133 |
*/ |
| 134 |
public static function do_init() { |
| 135 |
global $ddp_fs; |
| 136 |
$determine_locale = determine_locale(); |
| 137 |
} |
| 138 |
|
| 139 |
/** |
| 140 |
* delete_duplicates_ajax. |
| 141 |
* |
| 142 |
* @author Lars Koudal |
| 143 |
* @author Unknown |
| 144 |
* @since v0.0.1 |
| 145 |
* @version v1.0.0 Tuesday, January 12th, 2021. |
| 146 |
* @version v1.0.1 Tuesday, October 31st, 2023. |
| 147 |
* @version v1.0.2 Wednesday, November 1st, 2023. |
| 148 |
* @version v1.0.3 Tuesday, April 2nd, 2024. |
| 149 |
* @version v1.0.4 Tuesday, May 7th, 2024. |
| 150 |
* @access public static |
| 151 |
* @param boolean $return_data Default: false |
| 152 |
* @return mixed |
| 153 |
*/ |
| 154 |
public static function delete_duplicates_ajax( $return_data = false ) { |
| 155 |
// Check user permissions |
| 156 |
if ( !current_user_can( 'manage_options' ) ) { |
| 157 |
wp_send_json_error( __( 'You do not have sufficient permissions to perform this action.', 'delete-duplicate-posts' ) ); |
| 158 |
return; |
| 159 |
} |
| 160 |
// Verify the AJAX request, to prevent processing requests external of the site. |
| 161 |
check_ajax_referer( 'cp_ddp_delete_loglines' ); |
| 162 |
// Log the cleaning action |
| 163 |
self::log( __( 'Cleaning duplicates', 'delete-duplicate-posts' ) ); |
| 164 |
// Validate the POST data |
| 165 |
$checked_posts = $_POST['checked_posts'] ?? null; |
| 166 |
if ( empty( $checked_posts ) || !is_array( $checked_posts ) ) { |
| 167 |
wp_send_json_error( __( 'No duplicates were selected?', 'delete-duplicate-posts' ) ); |
| 168 |
return; |
| 169 |
} |
| 170 |
// Process and sanitize the checked posts |
| 171 |
$cleaned_posts = array(); |
| 172 |
foreach ( $checked_posts as $cp ) { |
| 173 |
if ( !empty( $cp['ID'] ) && !empty( $cp['orgID'] ) && is_numeric( $cp['ID'] ) && is_numeric( $cp['orgID'] ) ) { |
| 174 |
$cleaned_posts[] = array( |
| 175 |
'ID' => intval( $cp['ID'] ), |
| 176 |
'orgID' => intval( $cp['orgID'] ), |
| 177 |
); |
| 178 |
} |
| 179 |
} |
| 180 |
// Check if any valid posts were found |
| 181 |
if ( empty( $cleaned_posts ) ) { |
| 182 |
wp_send_json_error( __( 'Invalid duplicates selected.', 'delete-duplicate-posts' ) ); |
| 183 |
return; |
| 184 |
} |
| 185 |
// Attempt to clean duplicates and handle possible failures |
| 186 |
$result = self::cleandupes( true, $cleaned_posts ); |
| 187 |
if ( !$result ) { |
| 188 |
$errorMessage = __( 'Error deleting duplicates.', 'delete-duplicate-posts' ); |
| 189 |
// Assuming $result is an array or object that could be serialized safely: |
| 190 |
if ( is_array( $result ) || is_object( $result ) ) { |
| 191 |
$errorData = array( |
| 192 |
'additional_info' => json_encode( $result ), |
| 193 |
); |
| 194 |
wp_send_json_error( $errorMessage, $errorData ); |
| 195 |
} elseif ( is_string( $result ) ) { |
| 196 |
// Sanitize the string to be safe for output |
| 197 |
$errorData = array( |
| 198 |
'additional_info' => esc_html( $result ), |
| 199 |
); |
| 200 |
wp_send_json_error( $errorMessage, $errorData ); |
| 201 |
} else { |
| 202 |
// If result is not an array, object, or string, or if you want to keep the message generic |
| 203 |
wp_send_json_error( $errorMessage ); |
| 204 |
} |
| 205 |
return; |
| 206 |
} |
| 207 |
// Optionally return success data |
| 208 |
if ( $return_data ) { |
| 209 |
wp_send_json_success( array( |
| 210 |
'message' => __( 'Duplicates deleted successfully.', 'delete-duplicate-posts' ), |
| 211 |
) ); |
| 212 |
} |
| 213 |
} |
| 214 |
|
| 215 |
/** |
| 216 |
* Returns log lines via AJAX. |
| 217 |
* |
| 218 |
* @author Lars Koudal |
| 219 |
* @author Unknown |
| 220 |
* @since v0.0.1 |
| 221 |
* @version v1.0.0 Tuesday, January 12th, 2021. |
| 222 |
* @version v1.0.1 Wednesday, November 1st, 2023. |
| 223 |
* @access public static |
| 224 |
* @param boolean $return Default: false |
| 225 |
* @return void|array |
| 226 |
*/ |
| 227 |
public static function return_loglines_ajax( $return_data = false ) { |
| 228 |
if ( !current_user_can( 'manage_options' ) ) { |
| 229 |
wp_send_json_error( __( 'You do not have sufficient permissions to perform this action.', 'delete-duplicate-posts' ) ); |
| 230 |
return; |
| 231 |
} |
| 232 |
check_ajax_referer( 'cp_ddp_return_loglines' ); |
| 233 |
$currstep = ( filter_input( INPUT_POST, 'step', FILTER_SANITIZE_NUMBER_INT ) ?: 0 ); |
| 234 |
++$currstep; |
| 235 |
$json_response = array( |
| 236 |
'step' => $currstep, |
| 237 |
); |
| 238 |
global $wpdb; |
| 239 |
$loglines = $wpdb->get_results( "SELECT datime, note FROM {$wpdb->prefix}ddp_log ORDER BY datime DESC LIMIT 100;" ); |
| 240 |
if ( !empty( $loglines ) ) { |
| 241 |
$json_response['results'] = $loglines; |
| 242 |
} else { |
| 243 |
$json_response['msg'] = __( 'Error: Log is empty.. do something :-)', 'delete-duplicate-posts' ); |
| 244 |
if ( $return_data ) { |
| 245 |
return $json_response; |
| 246 |
} |
| 247 |
wp_send_json_error( $json_response ); |
| 248 |
return; |
| 249 |
// Make sure to exit here to prevent sending multiple responses. |
| 250 |
} |
| 251 |
if ( $return_data ) { |
| 252 |
return $json_response; |
| 253 |
} |
| 254 |
wp_send_json_success( $json_response ); |
| 255 |
} |
| 256 |
|
| 257 |
/** |
| 258 |
* return_duplicates_ajax. |
| 259 |
* |
| 260 |
* @author Lars Koudal |
| 261 |
* @author Unknown |
| 262 |
* @since v0.0.1 |
| 263 |
* @version v1.0.0 Tuesday, January 12th, 2021. |
| 264 |
* @version v1.0.1 Wednesday, November 1st, 2023. |
| 265 |
* @access public static |
| 266 |
* @return void |
| 267 |
*/ |
| 268 |
public static function return_duplicates_ajax() { |
| 269 |
check_ajax_referer( 'cp_ddp_return_duplicates', true ); |
| 270 |
if ( !current_user_can( 'manage_options' ) ) { |
| 271 |
wp_send_json_error( __( 'You do not have sufficient permissions to perform this action.', 'delete-duplicate-posts' ) ); |
| 272 |
return; |
| 273 |
} |
| 274 |
// Get duplicates |
| 275 |
$duplicates = self::return_duplicates( true ); |
| 276 |
// Initialize DataTables response array |
| 277 |
$response = array( |
| 278 |
'draw' => ( isset( $_POST['draw'] ) ? intval( $_POST['draw'] ) : 0 ), |
| 279 |
'recordsTotal' => 0, |
| 280 |
'recordsFiltered' => 0, |
| 281 |
'data' => array(), |
| 282 |
); |
| 283 |
if ( !empty( $duplicates ) && isset( $duplicates['dupes'] ) ) { |
| 284 |
$response['recordsTotal'] = $duplicates['dupescount']; |
| 285 |
$response['recordsFiltered'] = $duplicates['dupescount']; |
| 286 |
foreach ( $duplicates['dupes'] as $dupe ) { |
| 287 |
$title = ( '' === $dupe['title'] ? '-empty-' : $dupe['title'] ); |
| 288 |
$orgTitle = ( '' === $dupe['orgtitle'] ? '-empty-' : $dupe['orgtitle'] ); |
| 289 |
$permalink = esc_url( get_permalink( $dupe['ID'] ) ); |
| 290 |
$orgPermalink = esc_url( get_permalink( $dupe['orgID'] ) ); |
| 291 |
$response['data'][] = array( |
| 292 |
'ID' => esc_html( $dupe['ID'] ), |
| 293 |
'orgID' => esc_html( $dupe['orgID'] ), |
| 294 |
'duplicate' => sprintf( |
| 295 |
'<a href="%s" target="_blank">%s</a> (ID #%s) <br><small>%s Type: %s Status: %s</small>', |
| 296 |
esc_url( $permalink ), |
| 297 |
esc_html( $title ), |
| 298 |
esc_html( $dupe['ID'] ), |
| 299 |
esc_html( $dupe['why'] ), |
| 300 |
esc_html( $dupe['type'] ), |
| 301 |
esc_html( $dupe['status'] ) |
| 302 |
), |
| 303 |
'original' => sprintf( |
| 304 |
'<a href="%s" target="_blank">%s</a> (ID #%s)', |
| 305 |
esc_url( $orgPermalink ), |
| 306 |
esc_html( $orgTitle ), |
| 307 |
esc_html( $dupe['orgID'] ) |
| 308 |
), |
| 309 |
); |
| 310 |
} |
| 311 |
} |
| 312 |
wp_send_json( $response ); |
| 313 |
exit; |
| 314 |
} |
| 315 |
|
| 316 |
/** |
| 317 |
* Converts a number to relevant unit size |
| 318 |
* |
| 319 |
* @author Lars Koudal |
| 320 |
* @since v0.0.1 |
| 321 |
* @version v1.0.0 Thursday, June 24th, 2021. |
| 322 |
* @param mixed $size |
| 323 |
* @return void |
| 324 |
*/ |
| 325 |
public static function pretty_value( $size ) { |
| 326 |
if ( $size <= 0 || !is_numeric( $size ) ) { |
| 327 |
return '0 b'; |
| 328 |
} |
| 329 |
$unit = array( |
| 330 |
'b', |
| 331 |
'kb', |
| 332 |
'mb', |
| 333 |
'gb', |
| 334 |
'tb', |
| 335 |
'pb' |
| 336 |
); |
| 337 |
$log = log( $size, 1024 ); |
| 338 |
$i = floor( $log ); |
| 339 |
$i = max( 0, min( (int) $i, count( $unit ) - 1 ) ); |
| 340 |
$num = $size / pow( 1024, $i ); |
| 341 |
$calc = round( $num, 2 ) . ' ' . $unit[$i]; |
| 342 |
return $calc; |
| 343 |
} |
| 344 |
|
| 345 |
/** |
| 346 |
* Returns duplicates based on current settings - internal, not used via AJAX |
| 347 |
* |
| 348 |
* @author Lars Koudal |
| 349 |
* @author Unknown |
| 350 |
* @since v0.0.1 |
| 351 |
* @version v1.0.0 Tuesday, January 12th, 2021. |
| 352 |
* @version v1.0.1 Wednesday, November 1st, 2023. |
| 353 |
* @access public static |
| 354 |
* @param boolean $return Default: false |
| 355 |
* @return void |
| 356 |
*/ |
| 357 |
public static function return_duplicates( $return = false ) { |
| 358 |
self::timerstart( 'return_duplicates' ); |
| 359 |
$options = self::get_options(); |
| 360 |
$comparemethod = 'titlecompare'; |
| 361 |
$return_duplicates_time = false; |
| 362 |
global $ddp_fs; |
| 363 |
$json_response = array(); |
| 364 |
// @ check compare method - maybe change lookup routine? |
| 365 |
global $wpdb; |
| 366 |
$table_name = $wpdb->prefix . 'posts'; |
| 367 |
$resultslimit = $options['ddp_resultslimit']; |
| 368 |
$viewlimit = intval( $resultslimit ); |
| 369 |
if ( 0 === $viewlimit ) { |
| 370 |
$viewlimit = 9999; |
| 371 |
} |
| 372 |
$ddp_pts_arr = $options['ddp_pts']; |
| 373 |
if ( isset( $ddp_pts_arr ) && is_array( $ddp_pts_arr ) ) { |
| 374 |
$ddp_pts = '"' . implode( '","', $ddp_pts_arr ) . '"'; |
| 375 |
} else { |
| 376 |
$ddp_pts = ''; |
| 377 |
} |
| 378 |
$ddp_pts = rtrim( $ddp_pts, ',' ); |
| 379 |
$post_stati = '"publish"'; |
| 380 |
$order = $options['ddp_keep']; |
| 381 |
// verify default value has been set |
| 382 |
if ( 'oldest' !== $order ) { |
| 383 |
// two choices, if its not the first its the second... |
| 384 |
$options['ddp_keep'] = 'latest'; |
| 385 |
$order = 'latest'; |
| 386 |
} |
| 387 |
if ( 'oldest' === $order ) { |
| 388 |
$minmax = 'MIN(id)'; |
| 389 |
} |
| 390 |
if ( 'latest' === $order ) { |
| 391 |
$minmax = 'MAX(id)'; |
| 392 |
} |
| 393 |
$ddpstatuscnt = array(); |
| 394 |
$dupescount = 0; |
| 395 |
if ( '' !== $ddp_pts ) { |
| 396 |
$thisquery = false; |
| 397 |
// **** Compare by title **** |
| 398 |
if ( 'titlecompare' === $comparemethod ) { |
| 399 |
$limit = ( isset( $_POST['length'] ) ? intval( $_POST['length'] ) : 10 ); |
| 400 |
// Default to 10 if not set |
| 401 |
$offset = ( isset( $_POST['start'] ) ? intval( $_POST['start'] ) : 0 ); |
| 402 |
// Default to 0 if not set |
| 403 |
if ( !wp_doing_ajax() && $return ) { |
| 404 |
$limit = $options['ddp_resultslimit']; |
| 405 |
// for returning results for cron job. |
| 406 |
} |
| 407 |
$wpdb->query( 'SET SQL_BIG_SELECTS=1' ); |
| 408 |
$resultsoutput = ' LIMIT ' . intval( $limit ) . ' OFFSET ' . intval( $offset ); |
| 409 |
if ( $options['ddp_debug'] ) { |
| 410 |
self::log( 'DEBUG: SQL - Setting SET SQL_BIG_SELECTS=1' ); |
| 411 |
} |
| 412 |
$thisquery = "SELECT * FROM (\n\t\t\t\t\t\t\t\t\t\t\t\t\tSELECT t1.ID, t1.post_title, t1.post_type, t1.post_status, save_this_post_id \n\t\t\t\t\t\t\t\t\t\t\t\t\tFROM {$table_name} AS t1 \n\t\t\t\t\t\t\t\t\t\t\t\t\tINNER JOIN ( \n\t\t\t\t\t\t\t\t\t\t\t\t\t\tSELECT post_title, {$minmax} AS save_this_post_id \n\t\t\t\t\t\t\t\t\t\t\t\t\t\tFROM {$table_name} \n\t\t\t\t\t\t\t\t\t\t\t\t\t\tWHERE post_type IN ( {$ddp_pts} ) \n\t\t\t\t\t\t\t\t\t\t\t\t\t\tAND post_status = 'publish' \n\t\t\t\t\t\t\t\t\t\t\t\t\t\tGROUP BY post_title \n\t\t\t\t\t\t\t\t\t\t\t\t\t\tHAVING COUNT(*) > 1 \n\t\t\t\t\t\t\t\t\t\t\t\t\t\t) AS t2 ON t1.post_title = t2.post_title \n\t\t\t\t\t\t\t\t\t\t\t\t\t\tWHERE t1.post_status = 'publish'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tORDER BY t1.post_title, t1.post_date DESC\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t) AS derived_table\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tWHERE ID != save_this_post_id\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{$resultsoutput}"; |
| 413 |
if ( $options['ddp_debug'] ) { |
| 414 |
self::log( 'DEBUG: SQL ' . esc_attr( $thisquery ) ); |
| 415 |
} |
| 416 |
$json_response['lookup_query'] = $thisquery; |
| 417 |
$dupes = $wpdb->get_results( $thisquery, ARRAY_A ); |
| 418 |
// here we get total dupes - not cute, but the other approach not working. |
| 419 |
$total_dupes_query = "SELECT COUNT(*) FROM (\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tSELECT t1.ID, t1.post_title, t1.post_type, t1.post_status, save_this_post_id \n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tFROM {$table_name} AS t1 \n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tINNER JOIN ( \n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tSELECT post_title, {$minmax} AS save_this_post_id \n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tFROM {$table_name} \n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tWHERE post_type IN ( {$ddp_pts} ) \n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAND post_type NOT IN ('nav_menu_item') \n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAND post_status IN ( {$post_stati} ) \n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tGROUP BY post_title \n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tHAVING COUNT(*)>1 \n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t) AS t2 \n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tON t1.post_title = t2.post_title \n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAND post_status IN ( {$post_stati} )\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t) AS derived_table\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tWHERE ID != save_this_post_id"; |
| 420 |
$total_dupes = $wpdb->get_var( $total_dupes_query ); |
| 421 |
if ( $options['ddp_debug'] ) { |
| 422 |
self::log( 'DEBUG: SQL total_dupes_query ' . esc_attr( $total_dupes_query ) ); |
| 423 |
} |
| 424 |
if ( '' !== $wpdb->last_error ) { |
| 425 |
$last_error = htmlspecialchars( $wpdb->last_error, ENT_QUOTES ); |
| 426 |
$json_response['lookup_error'] = htmlspecialchars( $wpdb->last_error, ENT_QUOTES ); |
| 427 |
self::log( sprintf( |
| 428 |
/* translators: 1: Database error message, 2: SQL query */ |
| 429 |
__( 'Look up error: %1$s %2$s', 'delete-duplicate-posts' ), |
| 430 |
$last_error, |
| 431 |
$total_dupes_query |
| 432 |
) ); |
| 433 |
} |
| 434 |
if ( $dupes ) { |
| 435 |
$json_response['dupescount'] = $total_dupes; |
| 436 |
$stepcount = 0; |
| 437 |
foreach ( $dupes as $dupe ) { |
| 438 |
$mystatus = $dupe['post_status']; |
| 439 |
if ( isset( $ddpstatuscnt[$mystatus] ) ) { |
| 440 |
$ddpstatuscnt[$mystatus] = $ddpstatuscnt[$mystatus] + 1; |
| 441 |
} else { |
| 442 |
$ddpstatuscnt[$mystatus] = 1; |
| 443 |
} |
| 444 |
// Only save the dupes |
| 445 |
if ( $dupe['ID'] !== $dupe['save_this_post_id'] ) { |
| 446 |
$dupedetails = array( |
| 447 |
'ID' => $dupe['ID'], |
| 448 |
'permalink' => get_permalink( $dupe['ID'] ), |
| 449 |
'title' => $dupe['post_title'], |
| 450 |
'type' => $dupe['post_type'], |
| 451 |
'orgID' => $dupe['save_this_post_id'], |
| 452 |
'orgtitle' => $dupe['post_title'], |
| 453 |
'orgpermalink' => get_permalink( $dupe['save_this_post_id'] ), |
| 454 |
'status' => $dupe['post_status'], |
| 455 |
'why' => sprintf( __( 'Post ID %1$s has the same title as Post ID %2$s', 'delete-duplicate-posts' ), $dupe['ID'], $dupe['save_this_post_id'] ), |
| 456 |
); |
| 457 |
$json_response['dupes'][] = $dupedetails; |
| 458 |
} |
| 459 |
++$stepcount; |
| 460 |
} |
| 461 |
} |
| 462 |
} |
| 463 |
$statusdata = ''; |
| 464 |
if ( is_array( $ddpstatuscnt ) && count( $ddpstatuscnt ) > 1 ) { |
| 465 |
$statusdata .= '('; |
| 466 |
foreach ( $ddpstatuscnt as $key => $dsc ) { |
| 467 |
$statusdata .= $key . ': ' . number_format_i18n( $dsc ) . ', '; |
| 468 |
} |
| 469 |
$statusdata = rtrim( $statusdata, ', ' ); |
| 470 |
$statusdata .= ')'; |
| 471 |
} |
| 472 |
$return_duplicates_time = self::timerstop( 'return_duplicates' ); |
| 473 |
if ( $options['ddp_debug'] ) { |
| 474 |
$max = 5; |
| 475 |
if ( isset( $json_response['dupes'] ) ) { |
| 476 |
$idlist = array(); |
| 477 |
$step = 0; |
| 478 |
foreach ( $json_response['dupes'] as $dupe ) { |
| 479 |
if ( $step <= $max ) { |
| 480 |
$details = ''; |
| 481 |
if ( isset( $dupe['ID'] ) ) { |
| 482 |
$details .= 'ID: ' . $dupe['ID'] . ' '; |
| 483 |
} |
| 484 |
if ( isset( $dupe['title'] ) ) { |
| 485 |
$details .= ' title: "' . $dupe['title'] . '" '; |
| 486 |
} |
| 487 |
if ( isset( $dupe['permalink'] ) ) { |
| 488 |
$details .= 'Permalink: ' . $dupe['permalink'] . ' '; |
| 489 |
} |
| 490 |
if ( isset( $dupe['status'] ) ) { |
| 491 |
$details .= 'Status: ' . $dupe['status'] . ' '; |
| 492 |
} |
| 493 |
if ( isset( $dupe['type'] ) ) { |
| 494 |
$details .= 'Type: ' . $dupe['type'] . ' '; |
| 495 |
} |
| 496 |
if ( isset( $dupe['orgID'] ) ) { |
| 497 |
$details .= 'orgID: ' . $dupe['orgID'] . ' '; |
| 498 |
} |
| 499 |
if ( isset( $dupe['orgtitle'] ) ) { |
| 500 |
$details .= 'orgtitle: ' . $dupe['orgtitle'] . ' '; |
| 501 |
} |
| 502 |
if ( isset( $dupe['orgpermalink'] ) ) { |
| 503 |
$details .= ' orgpermalink: ' . $dupe['orgpermalink'] . ' '; |
| 504 |
} |
| 505 |
self::log( $details ); |
| 506 |
} |
| 507 |
++$step; |
| 508 |
} |
| 509 |
} |
| 510 |
} |
| 511 |
if ( isset( $json_response['dupes'] ) ) { |
| 512 |
self::log( sprintf( |
| 513 |
/* translators: 1: Number of duplicates, 2: Time in seconds, 3: Status data, 4: Memory usage */ |
| 514 |
__( 'Duplicates found: %1$d, Time: %2$s sec. %3$s Mem usage: %4$s', 'delete-duplicate-posts' ), |
| 515 |
count( $json_response['dupes'] ), |
| 516 |
$return_duplicates_time, |
| 517 |
$statusdata, |
| 518 |
self::pretty_value( memory_get_peak_usage( true ) ) |
| 519 |
) ); |
| 520 |
} |
| 521 |
} else { |
| 522 |
$json_response['msg'] = __( 'Error: Choose post types to check.', 'delete-duplicate-posts' ); |
| 523 |
$return_duplicates_time = self::timerstop( 'return_duplicates' ); |
| 524 |
$json_response['time'] = $return_duplicates_time . ' sec'; |
| 525 |
if ( $return ) { |
| 526 |
return $json_response; |
| 527 |
} |
| 528 |
wp_send_json_error( $json_response ); |
| 529 |
} |
| 530 |
if ( !$return_duplicates_time ) { |
| 531 |
$return_duplicates_time = self::timerstop( 'return_duplicates' ); |
| 532 |
} |
| 533 |
if ( isset( $json_response['dupescount'] ) ) { |
| 534 |
$json_response['msg'] = sprintf( __( 'Duplicates found: %1$s. Time: %2$s sec.', 'delete-duplicate-posts' ), number_format_i18n( $json_response['dupescount'] ), esc_html( $return_duplicates_time ) ); |
| 535 |
} |
| 536 |
if ( $return ) { |
| 537 |
return $json_response; |
| 538 |
} |
| 539 |
wp_send_json_success( $json_response ); |
| 540 |
} |
| 541 |
|
| 542 |
/** |
| 543 |
* create_redirect. |
| 544 |
* |
| 545 |
* @author Lars Koudal |
| 546 |
* @since v0.0.1 |
| 547 |
* @version v1.0.0 Friday, July 2nd, 2021. |
| 548 |
* @version v1.0.1 Tuesday, October 18th, 2022. |
| 549 |
* @access public static |
| 550 |
* @param mixed $inurl |
| 551 |
* @param mixed $targeturl |
| 552 |
* @param integer $code Default: 301 |
| 553 |
* @return void |
| 554 |
*/ |
| 555 |
public static function create_redirect( $inurl, $targeturl, $code = 301 ) { |
| 556 |
global $wpdb, $ddp_fs; |
| 557 |
} |
| 558 |
|
| 559 |
/** |
| 560 |
* Return default options |
| 561 |
* |
| 562 |
* @author Lars Koudal |
| 563 |
* @since v0.0.1 |
| 564 |
* @version v1.0.0 Friday, July 2nd, 2021. |
| 565 |
* @access public static |
| 566 |
* @return mixed |
| 567 |
*/ |
| 568 |
public static function default_options() { |
| 569 |
$defaults = array( |
| 570 |
'ddp_running' => 'false', |
| 571 |
'ddp_keep' => 'oldest', |
| 572 |
'ddp_limit' => 50, |
| 573 |
'ddp_pts' => array('post', 'page'), |
| 574 |
'ddp_statusmail_recipient' => '', |
| 575 |
'ddp_statusmail' => 0, |
| 576 |
'ddp_resultslimit' => 0, |
| 577 |
'ddp_enabled' => 0, |
| 578 |
'ddp_pstati' => array('publish'), |
| 579 |
'ddp_debug' => 0, |
| 580 |
'ddp_redirects' => 0, |
| 581 |
); |
| 582 |
return $defaults; |
| 583 |
} |
| 584 |
|
| 585 |
/** |
| 586 |
* get plugin's options |
| 587 |
* |
| 588 |
* @author Lars Koudal |
| 589 |
* @since v0.0.1 |
| 590 |
* @version v1.0.0 Thursday, June 9th, 2022. |
| 591 |
* @access public static |
| 592 |
* @return mixed |
| 593 |
*/ |
| 594 |
public static function get_options() { |
| 595 |
if ( null !== self::$options ) { |
| 596 |
return self::$options; |
| 597 |
} |
| 598 |
$options = get_option( self::$options_name, array() ); |
| 599 |
if ( !is_array( $options ) ) { |
| 600 |
$options = array(); |
| 601 |
} |
| 602 |
$options = array_merge( self::default_options(), $options ); |
| 603 |
return $options; |
| 604 |
} |
| 605 |
|
| 606 |
/** |
| 607 |
* add_freemius_extra_permission. |
| 608 |
* |
| 609 |
* @author Lars Koudal |
| 610 |
* @since v0.0.1 |
| 611 |
* @version v1.0.0 Thursday, June 9th, 2022. |
| 612 |
* @access public static |
| 613 |
* @param mixed $permissions |
| 614 |
* @return mixed |
| 615 |
*/ |
| 616 |
public static function add_freemius_extra_permission( $permissions ) { |
| 617 |
$permissions['helpscout'] = array( |
| 618 |
'icon-class' => 'dashicons dashicons-sos', |
| 619 |
'label' => 'Help Scout', |
| 620 |
'desc' => __( 'Rendering Help Scouts beacon for easy help and support', 'delete-duplicate-posts' ), |
| 621 |
'priority' => 16, |
| 622 |
); |
| 623 |
$permissions['newsletter'] = array( |
| 624 |
'icon-class' => 'dashicons dashicons-email-alt2', |
| 625 |
'label' => 'Newsletter', |
| 626 |
'desc' => __( 'Your email is added to cleverplugins.com newsletter. Unsubscribe any time.', 'delete-duplicate-posts' ), |
| 627 |
'priority' => 18, |
| 628 |
); |
| 629 |
return $permissions; |
| 630 |
} |
| 631 |
|
| 632 |
/** |
| 633 |
* Fetch plugin version from plugin PHP header |
| 634 |
* |
| 635 |
* @author Lars Koudal |
| 636 |
* @since v0.0.1 |
| 637 |
* @version v1.0.0 Thursday, June 9th, 2022. |
| 638 |
* @access public static |
| 639 |
* @return mixed |
| 640 |
*/ |
| 641 |
public static function get_plugin_version() { |
| 642 |
$plugin_data = get_file_data( __FILE__, array( |
| 643 |
'version' => 'Version', |
| 644 |
), 'plugin' ); |
| 645 |
return $plugin_data['version']; |
| 646 |
} |
| 647 |
|
| 648 |
/** |
| 649 |
* timerstart. |
| 650 |
* |
| 651 |
* @author Lars Koudal |
| 652 |
* @since v0.0.1 |
| 653 |
* @version v1.0.0 Thursday, June 9th, 2022. |
| 654 |
* @access public static |
| 655 |
* @param mixed $watchname |
| 656 |
* @return void |
| 657 |
*/ |
| 658 |
public static function timerstart( $watchname ) { |
| 659 |
set_transient( 'ddp_' . $watchname, microtime( true ), 60 * 60 * 1 ); |
| 660 |
} |
| 661 |
|
| 662 |
/** |
| 663 |
* timerstop. |
| 664 |
* |
| 665 |
* @author Lars Koudal |
| 666 |
* @since v0.0.1 |
| 667 |
* @version v1.0.0 Thursday, June 9th, 2022. |
| 668 |
* @access public static |
| 669 |
* @param mixed $watchname |
| 670 |
* @param integer $digits Default: 3 |
| 671 |
* @return mixed |
| 672 |
*/ |
| 673 |
public static function timerstop( $watchname, $digits = 3 ) { |
| 674 |
$return = round( microtime( true ) - get_transient( 'ddp_' . $watchname ), $digits ); |
| 675 |
delete_transient( 'ddp_' . $watchname ); |
| 676 |
return $return; |
| 677 |
} |
| 678 |
|
| 679 |
/** |
| 680 |
* Clean duplicates - not AJAX version |
| 681 |
* |
| 682 |
* @author Lars Koudal |
| 683 |
* @since v0.0.1 |
| 684 |
* @version v1.0.0 Thursday, June 9th, 2022. |
| 685 |
* @access public static |
| 686 |
* @param boolean $manualrun Default: false |
| 687 |
* @param mixed $to_delete Default: array() |
| 688 |
* @return void |
| 689 |
*/ |
| 690 |
public static function cleandupes( $manualrun = false, $to_delete = array() ) { |
| 691 |
global $wpdb, $ddp_fs; |
| 692 |
self::timerstart( 'ddp_totaltime' ); |
| 693 |
// start total timer |
| 694 |
$options = self::get_options(); |
| 695 |
$options['ddp_running'] = true; |
| 696 |
self::save_options( $options ); |
| 697 |
if ( !$manualrun ) { |
| 698 |
self::log( __( 'Automatic CRON job running.', 'delete-duplicate-posts' ) ); |
| 699 |
} else { |
| 700 |
self::log( __( 'Manually cleaning.', 'delete-duplicate-posts' ) ); |
| 701 |
} |
| 702 |
// what to do with a manual run - no notices |
| 703 |
if ( count( $to_delete ) > 0 ) { |
| 704 |
$lookup_arr = array(); |
| 705 |
foreach ( $to_delete as $td ) { |
| 706 |
$new_item = array(); |
| 707 |
$new_item['ID'] = $td['ID']; |
| 708 |
$new_item['orgID'] = $td['orgID']; |
| 709 |
$new_item['type'] = get_post_type( $td['ID'] ); |
| 710 |
$new_item['title'] = get_the_title( $td['ID'] ); |
| 711 |
$lookup_arr['dupes'][] = $new_item; |
| 712 |
} |
| 713 |
$dupes = $lookup_arr; |
| 714 |
} else { |
| 715 |
$dupes = self::return_duplicates( true ); |
| 716 |
} |
| 717 |
$resultnote = ''; |
| 718 |
$dispcount = 0; |
| 719 |
if ( isset( $dupes['dupes'] ) ) { |
| 720 |
foreach ( $dupes['dupes'] as $dupe ) { |
| 721 |
$postid = $dupe['ID']; |
| 722 |
$title = substr( $dupe['title'], 0, 35 ); |
| 723 |
if ( $postid ) { |
| 724 |
self::timerstart( 'deletepost_' . $postid ); |
| 725 |
/* @todo - implement a premium option to permanently delete or just use the WP setting. |
| 726 |
|
| 727 |
Options: Use WP setting (default), Delete Permantently, Trash posts (if enabled in WP) |
| 728 |
|
| 729 |
*/ |
| 730 |
$deleteresult = wp_trash_post( $postid ); |
| 731 |
$timespent = self::timerstop( 'deletepost_' . $postid ); |
| 732 |
++$dispcount; |
| 733 |
$totaldeleted = get_option( 'ddp_deleted_duplicates' ); |
| 734 |
if ( false !== $totaldeleted ) { |
| 735 |
++$totaldeleted; |
| 736 |
update_option( 'ddp_deleted_duplicates', $totaldeleted, false ); |
| 737 |
} else { |
| 738 |
update_option( 'ddp_deleted_duplicates', 1, false ); |
| 739 |
} |
| 740 |
if ( $options['ddp_debug'] ) { |
| 741 |
// translators: Debug notice. 1: type of duplicate. 2: The title of the post. 3: The ID. 4: Time spent deleting. |
| 742 |
self::log( sprintf( |
| 743 |
__( 'DEBUG: Deleted %1$s %2$s (id: %3$s) in %4$s sec.', 'delete-duplicate-posts' ), |
| 744 |
$dupe['type'], |
| 745 |
$title, |
| 746 |
$postid, |
| 747 |
$timespent |
| 748 |
) ); |
| 749 |
} |
| 750 |
} |
| 751 |
} |
| 752 |
} |
| 753 |
$totaltimespent = self::timerstop( 'ddp_totaltime' ); |
| 754 |
self::log( sprintf( __( 'A total of %1$s duplicate posts were deleted in %2$s sec.', 'delete-duplicate-posts' ), $dispcount, $totaltimespent ) ); |
| 755 |
$json_response = array( |
| 756 |
'totaltimespent' => $totaltimespent, |
| 757 |
'deleted' => $dispcount, |
| 758 |
); |
| 759 |
// Mail logic... |
| 760 |
if ( 0 < $dispcount && $options['ddp_statusmail'] ) { |
| 761 |
$blogurl = esc_url( site_url() ); |
| 762 |
$recipient = sanitize_email( $options['ddp_statusmail_recipient'] ); |
| 763 |
// translators: 1: Number of deleted posts, 2: Blog URL. |
| 764 |
$messagebody = sprintf( __( 'Hi Admin, I have deleted <strong>%1$d</strong> duplicated posts on your blog, %2$s.', 'delete-duplicate-posts' ), $dispcount, $blogurl ); |
| 765 |
$messagebody .= '<br><br>' . esc_html__( 'You are receiving this e-mail because you have turned on e-mail notifications by the plugin', 'delete-duplicate-posts' ); |
| 766 |
$messagebody .= ' <a href="https://cleverplugins.com/delete-duplicate-posts/" target="_blank" rel="noopener noreferrer">' . esc_html__( 'Delete Duplicate Posts', 'delete-duplicate-posts' ) . '</a>'; |
| 767 |
$messagebody .= '<br><br>' . esc_html__( 'Made by', 'delete-duplicate-posts' ) . " <a href='https://cleverplugins.com' target='_blank' rel='noopener noreferrer'>" . esc_html__( 'cleverplugins.com', 'delete-duplicate-posts' ) . '</a>'; |
| 768 |
$mailstatus = false; |
| 769 |
if ( is_email( $recipient ) ) { |
| 770 |
$subject = __( 'Deleted Duplicate Posts Status', 'delete-duplicate-posts' ); |
| 771 |
$mailstatus = wp_mail( $recipient, $subject, wp_kses_post( $messagebody ) ); |
| 772 |
if ( $options['ddp_debug'] ) { |
| 773 |
// translators: %s: Email recipient. |
| 774 |
self::log( sprintf( __( 'DEBUG: Sending email to: %s', 'delete-duplicate-posts' ), $recipient ) ); |
| 775 |
} |
| 776 |
if ( $mailstatus ) { |
| 777 |
// translators: %s: Email recipient. |
| 778 |
self::log( sprintf( __( 'Status email sent to %s.', 'delete-duplicate-posts' ), $recipient ) ); |
| 779 |
} |
| 780 |
} else { |
| 781 |
// translators: %s: Email address. |
| 782 |
self::log( sprintf( __( 'Not a valid email %s.', 'delete-duplicate-posts' ), $recipient ) ); |
| 783 |
} |
| 784 |
} |
| 785 |
$options['ddp_running'] = false; |
| 786 |
self::save_options( $options ); |
| 787 |
// Lets return a response |
| 788 |
if ( 0 === $manualrun && !wp_doing_ajax() ) { |
| 789 |
$json_response = array( |
| 790 |
'msg' => sprintf( esc_html__( 'A total of %s duplicates were deleted.', 'delete-duplicate-posts' ), intval( $dispcount ) ), |
| 791 |
); |
| 792 |
} |
| 793 |
wp_send_json_success( $json_response ); |
| 794 |
} |
| 795 |
|
| 796 |
/** |
| 797 |
* add_cron_intervals. |
| 798 |
* |
| 799 |
* @author Lars Koudal |
| 800 |
* @since v0.0.1 |
| 801 |
* @version v1.0.0 Thursday, June 9th, 2022. |
| 802 |
* @access public static |
| 803 |
* @param mixed $schedules |
| 804 |
* @return mixed |
| 805 |
*/ |
| 806 |
public static function add_cron_intervals( $schedules ) { |
| 807 |
$schedules['5min'] = array( |
| 808 |
'interval' => 300, |
| 809 |
'display' => __( 'Every 5 minutes', 'delete-duplicate-posts' ), |
| 810 |
); |
| 811 |
$schedules['10min'] = array( |
| 812 |
'interval' => 600, |
| 813 |
'display' => __( 'Every 10 minutes', 'delete-duplicate-posts' ), |
| 814 |
); |
| 815 |
$schedules['15min'] = array( |
| 816 |
'interval' => 900, |
| 817 |
'display' => __( 'Every 15 minutes', 'delete-duplicate-posts' ), |
| 818 |
); |
| 819 |
$schedules['30min'] = array( |
| 820 |
'interval' => 1800, |
| 821 |
'display' => __( 'Every 30 minutes', 'delete-duplicate-posts' ), |
| 822 |
); |
| 823 |
return $schedules; |
| 824 |
} |
| 825 |
|
| 826 |
/** |
| 827 |
* Log a notification to the database |
| 828 |
* |
| 829 |
* @author Lars Koudal |
| 830 |
* @since v0.0.1 |
| 831 |
* @version v1.0.0 Monday, January 11th, 2021. |
| 832 |
* @access public static |
| 833 |
* @param mixed $text |
| 834 |
* @return void |
| 835 |
*/ |
| 836 |
public static function log( $text ) { |
| 837 |
global $wpdb; |
| 838 |
$ddp_logtable = $wpdb->prefix . 'ddp_log'; |
| 839 |
// Insert log entry |
| 840 |
$insert_result = $wpdb->insert( $ddp_logtable, array( |
| 841 |
'datime' => current_time( 'mysql' ), |
| 842 |
'note' => $text, |
| 843 |
), array('%s', '%s') ); |
| 844 |
if ( false === $insert_result ) { |
| 845 |
// Handle error appropriately (e.g., log or throw exception) |
| 846 |
return; |
| 847 |
} |
| 848 |
// Efficiently check if row count exceeds 1000 and delete old entries |
| 849 |
$row_count = $wpdb->get_var( "SELECT COUNT(*) FROM {$ddp_logtable};" ); |
| 850 |
if ( $row_count > 1000 ) { |
| 851 |
$wpdb->query( "DELETE FROM {$ddp_logtable} WHERE id NOT IN (\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tSELECT id FROM (\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tSELECT id FROM {$ddp_logtable} ORDER BY datime DESC LIMIT 500\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t) AS sub\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t)" ); |
| 852 |
} |
| 853 |
} |
| 854 |
|
| 855 |
/** |
| 856 |
* Enqueues scripts and styles |
| 857 |
* |
| 858 |
* @author Lars Koudal |
| 859 |
* @since v0.0.1 |
| 860 |
* @version v1.0.0 Monday, January 11th, 2021. |
| 861 |
* @access public static |
| 862 |
* @return void |
| 863 |
*/ |
| 864 |
public static function admin_enqueue_scripts() { |
| 865 |
$screen = get_current_screen(); |
| 866 |
if ( is_object( $screen ) && 'tools_page_delete-duplicate-posts' === $screen->id ) { |
| 867 |
$pluginver = self::get_plugin_version(); |
| 868 |
wp_enqueue_script( 'jquery' ); |
| 869 |
wp_enqueue_style( |
| 870 |
'delete-duplicate-posts', |
| 871 |
plugins_url( '/css/delete-duplicate-posts.css', __FILE__ ), |
| 872 |
array(), |
| 873 |
$pluginver |
| 874 |
); |
| 875 |
wp_enqueue_script( |
| 876 |
'dataTables', |
| 877 |
// Unique handle for your script |
| 878 |
plugin_dir_url( __FILE__ ) . 'js/DataTables/datatables.js', |
| 879 |
// Path to your script file |
| 880 |
array('jquery'), |
| 881 |
// Dependencies, if any. This script depends on jQuery |
| 882 |
$pluginver, |
| 883 |
array( |
| 884 |
'in_footer' => true, |
| 885 |
) |
| 886 |
); |
| 887 |
wp_enqueue_style( |
| 888 |
'dataTables', |
| 889 |
plugins_url( '/js/DataTables/datatables.css', __FILE__ ), |
| 890 |
array(), |
| 891 |
$pluginver |
| 892 |
); |
| 893 |
wp_register_script( |
| 894 |
'delete-duplicate-posts', |
| 895 |
plugins_url( '/js/delete-duplicate-posts.js', __FILE__ ), |
| 896 |
array('jquery', 'dataTables'), |
| 897 |
$pluginver, |
| 898 |
true |
| 899 |
); |
| 900 |
$js_vars = array( |
| 901 |
'nonce' => wp_create_nonce( 'cp_ddp_return_duplicates' ), |
| 902 |
'loglines_nonce' => wp_create_nonce( 'cp_ddp_return_loglines' ), |
| 903 |
'deletedupes_nonce' => wp_create_nonce( 'cp_ddp_delete_loglines' ), |
| 904 |
'text_areyousure' => __( 'Are you sure you want to delete duplicates? There is no undo feature.', 'delete-duplicate-posts' ), |
| 905 |
'text_selectsomething' => __( 'You have to select which duplicates to delete. Tip: You can click the top or bottom checkbox to select all.', 'delete-duplicate-posts' ), |
| 906 |
'fromUrlTitle' => __( 'From URL', 'delete-duplicate-posts' ), |
| 907 |
'targetUrlTitle' => __( 'Target URL', 'delete-duplicate-posts' ), |
| 908 |
'refreshingText' => __( 'Refreshing...', 'delete-duplicate-posts' ), |
| 909 |
'refreshText' => __( 'Refresh', 'delete-duplicate-posts' ), |
| 910 |
'errorDetailsText' => __( 'Error details: ', 'delete-duplicate-posts' ), |
| 911 |
'redirectsErrorText' => __( 'Redirects DataTables error occurred. ', 'delete-duplicate-posts' ), |
| 912 |
'processingMessage' => __( 'Looking for duplicates', 'delete-duplicate-posts' ), |
| 913 |
'requestTimeText' => __( 'Request: ', 'delete-duplicate-posts' ), |
| 914 |
'failedToLoadDataText' => __( 'Failed to load data. ', 'delete-duplicate-posts' ), |
| 915 |
'duplicateTitle' => __( 'Duplicate', 'delete-duplicate-posts' ), |
| 916 |
'originalTitle' => __( 'Original', 'delete-duplicate-posts' ), |
| 917 |
'selectRowAlert' => __( 'Please select at least one row to delete.', 'delete-duplicate-posts' ), |
| 918 |
'serverResponseText' => __( 'Response from the server: ', 'delete-duplicate-posts' ), |
| 919 |
'errorOccurredText' => __( 'An error occurred: ', 'delete-duplicate-posts' ), |
| 920 |
'deleteSelectedText' => __( 'Delete Selected', 'delete-duplicate-posts' ), |
| 921 |
'selectVisibleText' => __( 'Select Visible', 'delete-duplicate-posts' ), |
| 922 |
'selectNoneText' => __( 'Select None', 'delete-duplicate-posts' ), |
| 923 |
'somethingWentWrongText' => __( 'Something went wrong.', 'delete-duplicate-posts' ), |
| 924 |
); |
| 925 |
wp_localize_script( 'delete-duplicate-posts', 'cp_ddp', $js_vars ); |
| 926 |
wp_enqueue_script( 'delete-duplicate-posts' ); |
| 927 |
} |
| 928 |
} |
| 929 |
|
| 930 |
/** |
| 931 |
* Create plugin tables |
| 932 |
* |
| 933 |
* @author Lars Koudal |
| 934 |
* @author Unknown |
| 935 |
* @since v0.0.1 |
| 936 |
* @version v1.0.0 Monday, January 11th, 2021. |
| 937 |
* @version v1.0.1 Sunday, July 17th, 2022. |
| 938 |
* @version v1.0.2 Sunday, December 3rd, 2023. |
| 939 |
* @access public static |
| 940 |
* @return void |
| 941 |
*/ |
| 942 |
public static function create_table() { |
| 943 |
global $wpdb, $ddp_fs; |
| 944 |
require_once ABSPATH . 'wp-admin/includes/upgrade.php'; |
| 945 |
$log_table_name = $wpdb->prefix . 'ddp_log'; |
| 946 |
$sql_log = "CREATE TABLE {$log_table_name} (\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tid bigint(20) NOT NULL AUTO_INCREMENT,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdatime timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnote tinytext NOT NULL,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tPRIMARY KEY (id)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t) ENGINE=InnoDB DEFAULT CHARSET=utf8;"; |
| 947 |
dbDelta( $sql_log ); |
| 948 |
$redirects_table_name = $wpdb->prefix . 'ddp_redirects'; |
| 949 |
$sql_redirects = "CREATE TABLE {$redirects_table_name} (\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tid bigint(20) NOT NULL AUTO_INCREMENT,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdatime timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tinurl varchar(1024) NOT NULL,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttargeturl varchar(1024) NOT NULL,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\thttpcode varchar(3) DEFAULT NULL,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tPRIMARY KEY (id)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t) ENGINE=InnoDB DEFAULT CHARSET=utf8;"; |
| 950 |
dbDelta( $sql_redirects ); |
| 951 |
// Additional plugin setup |
| 952 |
$options = self::get_options(); |
| 953 |
self::save_options( $options ); |
| 954 |
wp_clear_scheduled_hook( 'ddp_cron' ); |
| 955 |
self::log( __( 'Plugin activated.', 'delete-duplicate-posts' ) ); |
| 956 |
} |
| 957 |
|
| 958 |
/** |
| 959 |
* Install routines - create database and default options |
| 960 |
* |
| 961 |
* @author Lars Koudal |
| 962 |
* @since v0.0.1 |
| 963 |
* @version v1.0.0 Thursday, June 9th, 2022. |
| 964 |
* @access public static |
| 965 |
* @param mixed $network_wide |
| 966 |
* @return void |
| 967 |
*/ |
| 968 |
public static function install( $network_wide ) { |
| 969 |
global $wpdb; |
| 970 |
require_once ABSPATH . '/wp-admin/includes/upgrade.php'; |
| 971 |
if ( is_multisite() && $network_wide ) { |
| 972 |
// Get all blogs in the network and activate plugin on each one |
| 973 |
$blog_ids = $wpdb->get_col( "SELECT blog_id FROM {$wpdb->blogs}" ); |
| 974 |
foreach ( $blog_ids as $blog_id ) { |
| 975 |
switch_to_blog( $blog_id ); |
| 976 |
self::create_table(); |
| 977 |
restore_current_blog(); |
| 978 |
} |
| 979 |
} else { |
| 980 |
self::create_table(); |
| 981 |
} |
| 982 |
} |
| 983 |
|
| 984 |
/** |
| 985 |
* Creating table when a new blog is created |
| 986 |
* https://sudarmuthu.com/blog/how-to-properly-create-tables-in-wordpress-multisite-plugins/ |
| 987 |
* |
| 988 |
* @author Lars Koudal |
| 989 |
* @since v0.0.1 |
| 990 |
* @version v1.0.0 Thursday, June 9th, 2022. |
| 991 |
* @version v1.0.1 Sunday, May 14th, 2023. |
| 992 |
* @access public static |
| 993 |
* @param mixed $new_site |
| 994 |
* @return void |
| 995 |
*/ |
| 996 |
public static function on_create_blog( $new_site ) { |
| 997 |
if ( is_plugin_active_for_network( 'delete-duplicate-posts/delete-duplicate-posts.php' ) ) { |
| 998 |
switch_to_blog( $new_site->blog_id ); |
| 999 |
self::create_table(); |
| 1000 |
restore_current_blog(); |
| 1001 |
} |
| 1002 |
} |
| 1003 |
|
| 1004 |
/** |
| 1005 |
* Deleting the table whenever a blog is deleted |
| 1006 |
* |
| 1007 |
* @author Lars Koudal |
| 1008 |
* @since v0.0.1 |
| 1009 |
* @version v1.0.0 Thursday, June 9th, 2022. |
| 1010 |
* @access public static |
| 1011 |
* @param mixed $tables |
| 1012 |
* @return mixed |
| 1013 |
*/ |
| 1014 |
public static function on_delete_blog( $tables ) { |
| 1015 |
global $wpdb, $ddp_fs; |
| 1016 |
$tables[] = $wpdb->prefix . 'ddp_log'; |
| 1017 |
return $tables; |
| 1018 |
} |
| 1019 |
|
| 1020 |
/** |
| 1021 |
* Saves options |
| 1022 |
* |
| 1023 |
* @author Lars Koudal |
| 1024 |
* @since v0.0.1 |
| 1025 |
* @version v1.0.0 Thursday, June 9th, 2022. |
| 1026 |
* @access public static |
| 1027 |
* @param mixed $newoptions |
| 1028 |
* @return mixed |
| 1029 |
*/ |
| 1030 |
public static function save_options( $newoptions ) { |
| 1031 |
self::$options = null; |
| 1032 |
return update_option( 'delete_duplicate_posts_options_v4', $newoptions ); |
| 1033 |
} |
| 1034 |
|
| 1035 |
/** |
| 1036 |
* Adds link to menu under Tools |
| 1037 |
* |
| 1038 |
* @author Lars Koudal |
| 1039 |
* @since v0.0.1 |
| 1040 |
* @version v1.0.0 Thursday, June 9th, 2022. |
| 1041 |
* @access public static |
| 1042 |
* @return void |
| 1043 |
*/ |
| 1044 |
public static function admin_menu_link() { |
| 1045 |
// only for admins |
| 1046 |
if ( !current_user_can( 'manage_options' ) ) { |
| 1047 |
return; |
| 1048 |
} |
| 1049 |
add_management_page( |
| 1050 |
'Delete Duplicate Posts', |
| 1051 |
'Delete Duplicate Posts', |
| 1052 |
'edit_posts', |
| 1053 |
'delete-duplicate-posts', |
| 1054 |
array(__NAMESPACE__ . '\\Delete_Duplicate_Posts', 'admin_options_page'), |
| 1055 |
41 |
| 1056 |
); |
| 1057 |
add_filter( |
| 1058 |
'plugin_action_links_' . plugin_basename( __FILE__ ), |
| 1059 |
array(__NAMESPACE__ . '\\Delete_Duplicate_Posts', 'filter_plugin_actions'), |
| 1060 |
10, |
| 1061 |
2 |
| 1062 |
); |
| 1063 |
} |
| 1064 |
|
| 1065 |
/** |
| 1066 |
* filter_plugin_actions. |
| 1067 |
* |
| 1068 |
* @author Lars Koudal |
| 1069 |
* @since v0.0.1 |
| 1070 |
* @version v1.0.0 Thursday, June 9th, 2022. |
| 1071 |
* @access public static |
| 1072 |
* @param mixed $links |
| 1073 |
* @param mixed $file |
| 1074 |
* @return mixed |
| 1075 |
*/ |
| 1076 |
public static function filter_plugin_actions( $links, $file ) { |
| 1077 |
$settings_link = '<a href="tools.php?page=delete-duplicate-posts">' . __( 'Settings', 'delete-duplicate-posts' ) . '</a>'; |
| 1078 |
array_unshift( $links, $settings_link ); |
| 1079 |
// before other links |
| 1080 |
return $links; |
| 1081 |
} |
| 1082 |
|
| 1083 |
/** |
| 1084 |
* Adds help content to plugin page |
| 1085 |
* |
| 1086 |
* @author Lars Koudal |
| 1087 |
* @since v0.0.1 |
| 1088 |
* @version v1.0.0 Thursday, June 9th, 2022. |
| 1089 |
* @access public static |
| 1090 |
* @return void |
| 1091 |
*/ |
| 1092 |
public static function set_custom_help_content() { |
| 1093 |
$screen = get_current_screen(); |
| 1094 |
if ( 'tools_page_delete-duplicate-posts' === $screen->id ) { |
| 1095 |
$screen->add_help_tab( array( |
| 1096 |
'id' => 'ddp_help', |
| 1097 |
'title' => __( 'Usage and FAQ', 'delete-duplicate-posts' ), |
| 1098 |
'content' => '<h4>' . __( 'What does this plugin do?', 'delete-duplicate-posts' ) . '</h4><p>' . __( 'Helps you clean duplicate posts from your blog. The plugin checks for blogposts on your blog with the same title.', 'delete-duplicate-posts' ) . '</p><p>' . __( "It can run automatically via WordPress's own internal CRON-system, or you can run it automatically.", 'delete-duplicate-posts' ) . '</p><p>' . __( 'It also has a nice feature that can send you an e-mail when Delete Duplicate Posts finds and deletes something (if you have turned on the CRON feature).', 'delete-duplicate-posts' ) . '</p><h4>' . __( 'Help! Something was deleted that was not supposed to be deleted!', 'delete-duplicate-posts' ) . '</h4><p>' . __( 'I am sorry for that, I can only recommend you restore the database you took just before you ran this plugin.', 'delete-duplicate-posts' ) . '</p><p>' . __( 'If you run this plugin, manually or automatically, it is at your OWN risk!', 'delete-duplicate-posts' ) . '</p><p>' . __( 'We have done our best to avoid deleting something that should not be deleted, but if it happens, there is nothing we can do to help you.', 'delete-duplicate-posts' ) . "</p><p><a href='https://cleverplugins.com' target='_blank'>cleverplugins.com</a>.</p>", |
| 1099 |
) ); |
| 1100 |
} |
| 1101 |
} |
| 1102 |
|
| 1103 |
/** |
| 1104 |
* admin_options_page. |
| 1105 |
* |
| 1106 |
* @author Lars Koudal |
| 1107 |
* @since v0.0.1 |
| 1108 |
* @version v1.0.0 Thursday, June 9th, 2022. |
| 1109 |
* @version v1.0.1 Thursday, June 9th, 2022. |
| 1110 |
* @access public static |
| 1111 |
* @return void |
| 1112 |
*/ |
| 1113 |
public static function admin_options_page() { |
| 1114 |
global $ddp_fs, $wpdb; |
| 1115 |
// DELETE NOW |
| 1116 |
if ( isset( $_POST['deleteduplicateposts_delete'] ) && isset( $_POST['_wpnonce'] ) ) { |
| 1117 |
if ( wp_verify_nonce( $_POST['_wpnonce'], 'ddp-clean-now' ) ) { |
| 1118 |
self::cleandupes( 1 ); |
| 1119 |
// use the value 1 to indicate it is being run manually. |
| 1120 |
} |
| 1121 |
} |
| 1122 |
// RUN NOW!! |
| 1123 |
if ( isset( $_POST['ddp_runnow'] ) ) { |
| 1124 |
if ( !wp_verify_nonce( $_POST['_wpnonce'], 'ddp-update-options' ) ) { |
| 1125 |
die( esc_html( __( 'Whoops! Some error occured, try again, please!', 'delete-duplicate-posts' ) ) ); |
| 1126 |
} |
| 1127 |
} |
| 1128 |
// SAVING OPTIONS |
| 1129 |
if ( isset( $_POST['delete_duplicate_posts_save'] ) ) { |
| 1130 |
if ( !wp_verify_nonce( $_POST['_wpnonce'], 'ddp-update-options' ) ) { |
| 1131 |
die( esc_html( __( 'Whoops! There was a problem with the data you posted. Please go back and try again.', 'delete-duplicate-posts' ) ) ); |
| 1132 |
} |
| 1133 |
$posttypes = array(); |
| 1134 |
if ( isset( $_POST['ddp_pts'] ) ) { |
| 1135 |
$option_array = $_POST['ddp_pts']; |
| 1136 |
$option_count = count( $option_array ); |
| 1137 |
for ($i = 0; $i < $option_count; $i++) { |
| 1138 |
$posttypes[] = sanitize_text_field( $option_array[$i] ); |
| 1139 |
} |
| 1140 |
} |
| 1141 |
if ( isset( $_POST['ddp_enabled'] ) ) { |
| 1142 |
$options['ddp_enabled'] = ( 'on' === $_POST['ddp_enabled'] ? true : false ); |
| 1143 |
} else { |
| 1144 |
$options['ddp_enabled'] = false; |
| 1145 |
} |
| 1146 |
$options['ddp_statusmail'] = ( isset( $_POST['ddp_statusmail'] ) && 'on' === $_POST['ddp_statusmail'] ? true : false ); |
| 1147 |
$options['ddp_debug'] = ( isset( $_POST['ddp_debug'] ) && 'on' === $_POST['ddp_debug'] ? true : false ); |
| 1148 |
if ( isset( $_POST['ddp_statusmail_recipient'] ) ) { |
| 1149 |
$options['ddp_statusmail_recipient'] = sanitize_text_field( $_POST['ddp_statusmail_recipient'] ); |
| 1150 |
} |
| 1151 |
if ( isset( $_POST['ddp_schedule'] ) ) { |
| 1152 |
$options['ddp_schedule'] = sanitize_text_field( $_POST['ddp_schedule'] ); |
| 1153 |
} |
| 1154 |
if ( isset( $_POST['ddp_keep'] ) ) { |
| 1155 |
$options['ddp_keep'] = sanitize_text_field( $_POST['ddp_keep'] ); |
| 1156 |
} |
| 1157 |
if ( isset( $_POST['ddp_method'] ) ) { |
| 1158 |
$options['ddp_method'] = sanitize_text_field( $_POST['ddp_method'] ); |
| 1159 |
} |
| 1160 |
if ( isset( $_POST['ddp_resultslimit'] ) ) { |
| 1161 |
$options['ddp_resultslimit'] = sanitize_text_field( $_POST['ddp_resultslimit'] ); |
| 1162 |
} |
| 1163 |
// 301 redirects |
| 1164 |
if ( isset( $_POST['ddp_redirects'] ) ) { |
| 1165 |
$options['ddp_redirects'] = ( 'on' === $_POST['ddp_redirects'] ? true : false ); |
| 1166 |
} else { |
| 1167 |
$options['ddp_redirects'] = false; |
| 1168 |
} |
| 1169 |
$options['ddp_pts'] = $posttypes; |
| 1170 |
// Previously sanitized |
| 1171 |
if ( isset( $_POST['ddp_limit'] ) ) { |
| 1172 |
$options['ddp_limit'] = sanitize_text_field( $_POST['ddp_limit'] ); |
| 1173 |
} |
| 1174 |
self::save_options( $options ); |
| 1175 |
if ( isset( self::$options['ddp_enabled'] ) ) { |
| 1176 |
wp_clear_scheduled_hook( 'ddp_cron' ); |
| 1177 |
$interval = self::$options['ddp_schedule']; |
| 1178 |
if ( !$interval ) { |
| 1179 |
$interval = 'hourly'; |
| 1180 |
} |
| 1181 |
$nextscheduled = wp_next_scheduled( 'ddp_cron' ); |
| 1182 |
if ( !$nextscheduled ) { |
| 1183 |
wp_schedule_event( time(), $interval, 'ddp_cron' ); |
| 1184 |
} |
| 1185 |
} |
| 1186 |
echo '<div class="notice notice-success is-dismissible"><p>' . esc_html( __( 'Settings saved.', 'delete-duplicate-posts' ) ) . '</p></div>'; |
| 1187 |
} |
| 1188 |
// CLEARING THE LOG |
| 1189 |
if ( isset( $_POST['ddp_clearlog'] ) ) { |
| 1190 |
if ( !wp_verify_nonce( $_POST['_wpnonce'], 'ddp_clearlog_nonce' ) ) { |
| 1191 |
die( esc_html( __( 'Whoops! Some error occured, try again, please!', 'delete-duplicate-posts' ) ) ); |
| 1192 |
} |
| 1193 |
$table_name_log = $wpdb->prefix . 'ddp_log'; |
| 1194 |
$wpdb->query( "TRUNCATE {$table_name_log};" ); |
| 1195 |
//phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared |
| 1196 |
echo '<div class="updated"><p>' . esc_html( __( 'The log was cleared.', 'delete-duplicate-posts' ) ) . '</p></div>'; |
| 1197 |
} |
| 1198 |
// REACTIVATE THE DATABASE |
| 1199 |
if ( isset( $_POST['ddp_reactivate'] ) ) { |
| 1200 |
if ( !wp_verify_nonce( $_POST['_wpnonce'], 'ddp_reactivate_nonce' ) ) { |
| 1201 |
die( esc_html( __( 'Whoops! Some error occured, try again, please!', 'delete-duplicate-posts' ) ) ); |
| 1202 |
} |
| 1203 |
self::install( false ); |
| 1204 |
self::log( 'Reinstalled databases' ); |
| 1205 |
} |
| 1206 |
$options = self::get_options(); |
| 1207 |
$css_classes = ' free'; |
| 1208 |
$display_ads = true; |
| 1209 |
?> |
| 1210 |
|
| 1211 |
<div class="wrap fs-section <?php |
| 1212 |
echo esc_attr( $css_classes ); |
| 1213 |
?>"> |
| 1214 |
<h1>Delete Duplicate Posts <span>v. <?php |
| 1215 |
echo esc_html( self::get_plugin_version() ); |
| 1216 |
?></span></h1> |
| 1217 |
<?php |
| 1218 |
$totaldeleted = get_option( 'ddp_deleted_duplicates' ); |
| 1219 |
// check the code for get param 'welcome-message' er sat til true |
| 1220 |
?> |
| 1221 |
|
| 1222 |
<h2 class="nav-tab-wrapper"> |
| 1223 |
<a href="#duplicates-tab" class="nav-tab fs-tab nav-tab-active home"><?php |
| 1224 |
esc_html_e( 'Duplicates', 'delete-duplicate-posts' ); |
| 1225 |
?></a> |
| 1226 |
<a href="#log-tab" class="nav-tab"><?php |
| 1227 |
esc_html_e( 'Log', 'delete-duplicate-posts' ); |
| 1228 |
?></a> |
| 1229 |
<a href="#settings-tab" class="nav-tab"><?php |
| 1230 |
esc_html_e( 'Settings', 'delete-duplicate-posts' ); |
| 1231 |
?></a> |
| 1232 |
<a href="#redirects-tab" class="nav-tab<?php |
| 1233 |
echo ( $ddp_fs->is__premium_only() && $ddp_fs->can_use_premium_code() ? '' : ' pro' ); |
| 1234 |
?>"><?php |
| 1235 |
esc_html_e( 'Redirects', 'delete-duplicate-posts' ); |
| 1236 |
?></a> |
| 1237 |
</h2> |
| 1238 |
|
| 1239 |
<div class="ddp_content_wrapper"> |
| 1240 |
<div class="ddp_content_cell"> |
| 1241 |
<div id="delete-duplicate-posts-tabs"> |
| 1242 |
<div id="duplicates-tab" class="tab-content"> |
| 1243 |
<div id="ddp-dashboard"> |
| 1244 |
<?php |
| 1245 |
if ( $options['ddp_enabled'] ) { |
| 1246 |
$interval = $options['ddp_schedule']; |
| 1247 |
if ( !$interval ) { |
| 1248 |
$interval = 'hourly'; |
| 1249 |
} |
| 1250 |
$nextscheduled = wp_next_scheduled( 'ddp_cron' ); |
| 1251 |
if ( !$nextscheduled ) { |
| 1252 |
// plugin active, but the cron needs to be activated also.. |
| 1253 |
$options['last_interval'] = $interval; |
| 1254 |
self::save_options( $options ); |
| 1255 |
wp_schedule_event( time(), $interval, 'ddp_cron' ); |
| 1256 |
//} |
| 1257 |
} |
| 1258 |
} else { |
| 1259 |
wp_unschedule_hook( 'ddp_cron' ); |
| 1260 |
} |
| 1261 |
$totaldeleted = get_option( 'ddp_deleted_duplicates' ); |
| 1262 |
?> |
| 1263 |
<div class="statusdiv"> |
| 1264 |
<div class="statusmessage"></div> |
| 1265 |
<div class="errormessage"></div> |
| 1266 |
<div class="dupelist"> |
| 1267 |
<div id="requestTime"></div> |
| 1268 |
<table id="ddp_dupetable" class="wp-list-table widefat fixed striped table-view-list"></table> |
| 1269 |
</div> |
| 1270 |
</div> |
| 1271 |
<?php |
| 1272 |
if ( false !== $totaldeleted && 0 < $totaldeleted && $display_ads ) { |
| 1273 |
$totaldeleted = number_format_i18n( $totaldeleted ); |
| 1274 |
?> |
| 1275 |
<div id="cp-ddp-reviewlink" data-dismissible="ddp-leavereview-180" class="updated notice notice-success"> |
| 1276 |
<h3> |
| 1277 |
<?php |
| 1278 |
/* translators: %s: Total number of deleted duplicates */ |
| 1279 |
printf( esc_html__( '%s duplicates deleted!', 'delete-duplicate-posts' ), esc_html( $totaldeleted ) ); |
| 1280 |
?> |
| 1281 |
</h3> |
| 1282 |
<p> |
| 1283 |
<?php |
| 1284 |
/* translators: %s: Total number of deleted duplicates */ |
| 1285 |
printf( esc_html__( "Hey, I noticed this plugin has deleted %s duplicate posts for you - that's awesome! Could you please do me a BIG favor and give it a 5-star rating on WordPress? Just to help us spread the word and boost our motivation.", 'delete-duplicate-posts' ), esc_html( $totaldeleted ) ); |
| 1286 |
?> |
| 1287 |
</p> |
| 1288 |
<p> |
| 1289 |
<a href="https://wordpress.org/support/plugin/delete-duplicate-posts/reviews/?filter=5#new-post" class="button-secondary button button-small" target="_blank" rel="noopener"><?php |
| 1290 |
esc_html_e( 'Ok, you deserve it', 'delete-duplicate-posts' ); |
| 1291 |
?></a> |
| 1292 |
</p> |
| 1293 |
</div> |
| 1294 |
<?php |
| 1295 |
} |
| 1296 |
?> |
| 1297 |
<?php |
| 1298 |
$my_current_user = wp_get_current_user(); |
| 1299 |
?> |
| 1300 |
|
| 1301 |
<div class="ddpnewsletter"> |
| 1302 |
<p>Sign up to our newsletter to receive the latest tips and updates directly to your inbox. Ensure your WordPress site remains efficient and duplicate-free!</p> |
| 1303 |
<form class="ml-block-form" action="https://assets.mailerlite.com/jsonp/16490/forms/106309157552916248/subscribe" data-code="" method="post" target="_blank"> |
| 1304 |
<input type="text" class="form-control" data-inputmask="" name="fields[name]" placeholder="Name" autocomplete="given-name" value="<?php |
| 1305 |
echo esc_html( $my_current_user->display_name ); |
| 1306 |
?>" required="required"> |
| 1307 |
<input type="email" class="form-control" data-inputmask="" name="fields[email]" placeholder="Email" autocomplete="email" value="<?php |
| 1308 |
echo esc_html( $my_current_user->user_email ); |
| 1309 |
?>" required="required"> |
| 1310 |
<input type="hidden" name="fields[signupsource]" value="PluginInstall"> |
| 1311 |
<input type="hidden" name="ml-submit" value="1"> |
| 1312 |
<input type="hidden" name="anticsrf" value="true"> |
| 1313 |
<button type="submit" class="button button-secondary">Subscribe</button> |
| 1314 |
|
| 1315 |
</form> |
| 1316 |
<p class="ppolicy">You can unsubscribe anytime. For more details, review our <a href="https://cleverplugins.com/privacy-policy/" target="_blank" class="privacy-policy" rel="noopener">Privacy Policy</a>.</p> |
| 1317 |
</div> |
| 1318 |
|
| 1319 |
|
| 1320 |
|
| 1321 |
|
| 1322 |
|
| 1323 |
|
| 1324 |
|
| 1325 |
|
| 1326 |
|
| 1327 |
<?php |
| 1328 |
$display_promotion = true; |
| 1329 |
if ( $display_promotion ) { |
| 1330 |
?> |
| 1331 |
<div class="innerpromotion ddppro"> |
| 1332 |
<h3><span class="dashicons dashicons-star-filled"></span> Upgrade to Delete Duplicate Posts Pro <span class="dashicons dashicons-star-filled"></span></h3> |
| 1333 |
<ul class="linklist"> |
| 1334 |
<li><strong>301 Redirects for Deleted Posts:</strong> Seamlessly redirect deleted posts to original pages.</li> |
| 1335 |
<li><strong>Search by Duplicate Post Meta:</strong> Advanced search options for identifying duplicates based on post metadata.</li> |
| 1336 |
<li><strong>Automatic Deletion:</strong> Automatic management of duplicate posts.</li> |
| 1337 |
<li><strong>Email Notifications:</strong> Alerts when duplicates are found and removed.</li> |
| 1338 |
<li><strong>Search Any Post Status:</strong> Deeper search capabilities, including unpublished posts.</li> |
| 1339 |
<li><strong>Filter and Delete Unpublished Duplicates:</strong> Proactively manage and prevent unseen duplicates.</li> |
| 1340 |
<li><strong>WooCommerce Compatibility:</strong> Look for and delete duplicate products with same SKUs. (Custom post meta)</li> |
| 1341 |
<li><strong>Support: </strong> We appreciate your support!</li> |
| 1342 |
</ul> |
| 1343 |
<p><strong></strong></p> |
| 1344 |
|
| 1345 |
<?php |
| 1346 |
$target_url = 'https://checkout.freemius.com/mode/dialog/plugin/925/plan/9473/licenses/1/?billing_cycle=annually'; |
| 1347 |
$lifetime_url = 'https://checkout.freemius.com/mode/dialog/plugin/925/plan/9473/licenses/1/?billing_cycle=lifetime'; |
| 1348 |
if ( $my_current_user->user_email ) { |
| 1349 |
$target_url = add_query_arg( 'user_email', $my_current_user->user_email, $target_url ); |
| 1350 |
$lifetime_url = add_query_arg( 'user_email', $my_current_user->user_email, $lifetime_url ); |
| 1351 |
} |
| 1352 |
?> |
| 1353 |
<a href="<?php |
| 1354 |
echo esc_url( $target_url ); |
| 1355 |
?>" class="ddpprobutton button button-primary button-hero" target="_blank">Only $29.99 /year - <?php |
| 1356 |
esc_html_e( 'Click here', 'delete-duplicate-posts' ); |
| 1357 |
?></a> |
| 1358 |
<p> |
| 1359 |
<center><em>OR get <a href="<?php |
| 1360 |
echo esc_url( $lifetime_url ); |
| 1361 |
?>" target="_blank">a lifetime license for only $59.99</a></em> - You can transfer your license to other websites.</center> |
| 1362 |
</p> |
| 1363 |
<div class="moneybackguarantee"> |
| 1364 |
<p><strong>Money Back Guarantee!</strong></p> |
| 1365 |
<p>You are fully protected by our 100% Money Back Guarantee. If during the next 30 days you experience an issue that makes the plugin unusable and we are unable to resolve it, we'll happily consider offering a full refund of your money.</p> |
| 1366 |
</div> |
| 1367 |
</div><!-- .sidebarrow --> |
| 1368 |
|
| 1369 |
<?php |
| 1370 |
} |
| 1371 |
?> |
| 1372 |
|
| 1373 |
|
| 1374 |
|
| 1375 |
|
| 1376 |
|
| 1377 |
|
| 1378 |
|
| 1379 |
|
| 1380 |
|
| 1381 |
|
| 1382 |
</div><!-- #dashboard --> |
| 1383 |
</div> |
| 1384 |
|
| 1385 |
<div id="log-tab" class="tab-content" style="display: none;"> |
| 1386 |
<div id="log"> |
| 1387 |
<h3><?php |
| 1388 |
esc_html_e( 'The Log', 'delete-duplicate-posts' ); |
| 1389 |
?></h3> |
| 1390 |
<div class="spinner is-active"></div> |
| 1391 |
<ul class="large-text" name="ddp_log" id="ddp_log"></ul> |
| 1392 |
</div> |
| 1393 |
<p> |
| 1394 |
<form method="post" id="ddp_clearlog"> |
| 1395 |
<?php |
| 1396 |
wp_nonce_field( 'ddp_clearlog_nonce' ); |
| 1397 |
?> |
| 1398 |
<input class="button-secondary" type="submit" name="ddp_clearlog" value="<?php |
| 1399 |
esc_html_e( 'Reset log', 'delete-duplicate-posts' ); |
| 1400 |
?>" /> |
| 1401 |
</form> |
| 1402 |
</p> |
| 1403 |
</div> |
| 1404 |
|
| 1405 |
<div id="settings-tab" class="tab-content" style="display: none;"> |
| 1406 |
<div id="ddp-configuration"> |
| 1407 |
<h3><?php |
| 1408 |
esc_html_e( 'Settings', 'delete-duplicate-posts' ); |
| 1409 |
?></h3> |
| 1410 |
<p> |
| 1411 |
<?php |
| 1412 |
$nextscheduled = wp_next_scheduled( 'ddp_cron' ); |
| 1413 |
if ( $nextscheduled ) { |
| 1414 |
?> |
| 1415 |
<div class="notice notice-info is-dismissible"> |
| 1416 |
<h3><span class="dashicons dashicons-saved"></span> <?php |
| 1417 |
esc_html_e( 'Automatically Deleting Duplicates', 'delete-duplicate-posts' ); |
| 1418 |
?></h3> |
| 1419 |
<?php |
| 1420 |
echo '<p class="cronstatus center">' . esc_html__( 'You have enabled automatic deletion, so I am running on automatic. I will take care of everything...', 'delete-duplicate-posts' ) . '</p>'; |
| 1421 |
echo '<p class="center">'; |
| 1422 |
printf( |
| 1423 |
// translators: Showing when the next check happens and what the current time is |
| 1424 |
esc_html( __( 'Next automated check %1$s. Current time %2$s', 'delete-duplicate-posts' ) ), |
| 1425 |
'<strong>' . esc_html( date_i18n( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ), $nextscheduled ) ) . '</strong>', |
| 1426 |
'<strong>' . esc_html( date_i18n( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ), time() ) ) . '</strong>' |
| 1427 |
); |
| 1428 |
echo '</p>'; |
| 1429 |
?> |
| 1430 |
</div> |
| 1431 |
<?php |
| 1432 |
} |
| 1433 |
?> |
| 1434 |
</p> |
| 1435 |
<form method="post" id="delete_duplicate_posts_options"> |
| 1436 |
<?php |
| 1437 |
wp_nonce_field( 'ddp-update-options' ); |
| 1438 |
?> |
| 1439 |
<table width="100%" cellspacing="2" cellpadding="5" class="form-table"> |
| 1440 |
<tr valign="top"> |
| 1441 |
<th><label for="ddp_pts"><?php |
| 1442 |
esc_html_e( 'Which post types?:', 'delete-duplicate-posts' ); |
| 1443 |
?></label> |
| 1444 |
</th> |
| 1445 |
<td> |
| 1446 |
<?php |
| 1447 |
$builtin = array('post', 'page', 'attachment'); |
| 1448 |
$args = array( |
| 1449 |
'public' => true, |
| 1450 |
'_builtin' => false, |
| 1451 |
); |
| 1452 |
$output = 'names'; |
| 1453 |
$operator = 'and'; |
| 1454 |
$post_types = get_post_types( $args, $output, $operator ); |
| 1455 |
$post_types = array_merge( $builtin, $post_types ); |
| 1456 |
$checked_post_types = $options['ddp_pts']; |
| 1457 |
if ( $post_types ) { |
| 1458 |
?> |
| 1459 |
<ul class="radio"> |
| 1460 |
<?php |
| 1461 |
$step = 0; |
| 1462 |
if ( !is_array( $checked_post_types ) ) { |
| 1463 |
$checked_post_types = array(); |
| 1464 |
} |
| 1465 |
foreach ( $post_types as $pt ) { |
| 1466 |
$checked = array_search( $pt, $checked_post_types, true ); |
| 1467 |
?> |
| 1468 |
<li><input type="checkbox" name="ddp_pts[]" id="ddp_pt-<?php |
| 1469 |
echo esc_attr( $step ); |
| 1470 |
?>" value="<?php |
| 1471 |
echo esc_html( $pt ); |
| 1472 |
?>" |
| 1473 |
<?php |
| 1474 |
if ( false !== $checked ) { |
| 1475 |
echo ' checked'; |
| 1476 |
} |
| 1477 |
?> |
| 1478 |
/> |
| 1479 |
<label for="ddp_pt-<?php |
| 1480 |
echo esc_attr( $step ); |
| 1481 |
?>"><?php |
| 1482 |
echo esc_html( $pt ); |
| 1483 |
?></label> |
| 1484 |
<?php |
| 1485 |
// Count for each post type |
| 1486 |
$postinfo = wp_count_posts( $pt ); |
| 1487 |
$othercount = 0; |
| 1488 |
foreach ( $postinfo as $pi ) { |
| 1489 |
$othercount = $othercount + intval( $pi ); |
| 1490 |
} |
| 1491 |
// translators: Total number of deleted duplicates |
| 1492 |
echo '<small>' . sprintf( esc_html__( '(%s total found)', 'delete-duplicate-posts' ), esc_html( number_format_i18n( $othercount ) ) ) . '</small>'; |
| 1493 |
?> |
| 1494 |
</li> |
| 1495 |
<?php |
| 1496 |
++$step; |
| 1497 |
} |
| 1498 |
?> |
| 1499 |
</ul> |
| 1500 |
<?php |
| 1501 |
} |
| 1502 |
?> |
| 1503 |
<p class="description"> |
| 1504 |
<?php |
| 1505 |
esc_html_e( 'Choose which post types to scan for duplicates.', 'delete-duplicate-posts' ); |
| 1506 |
?> |
| 1507 |
</p> |
| 1508 |
</td> |
| 1509 |
</tr> |
| 1510 |
|
| 1511 |
<tr> |
| 1512 |
<th><label for="ddp_pstati"><?php |
| 1513 |
esc_html_e( 'Post status', 'delete-duplicate-posts' ); |
| 1514 |
?></label> |
| 1515 |
</th> |
| 1516 |
<td> |
| 1517 |
<?php |
| 1518 |
$stati = array( |
| 1519 |
'publish' => (object) array( |
| 1520 |
'label' => 'Published', |
| 1521 |
'show_in_admin_status_list' => true, |
| 1522 |
), |
| 1523 |
); |
| 1524 |
$checked_post_stati = $options['ddp_pstati']; |
| 1525 |
if ( $stati ) { |
| 1526 |
?> |
| 1527 |
<ul class="checkbox"> |
| 1528 |
<?php |
| 1529 |
$staticount = count( $stati ); |
| 1530 |
foreach ( $stati as $key => $st ) { |
| 1531 |
if ( $st->show_in_admin_status_list ) { |
| 1532 |
$checked = array_search( $key, $checked_post_stati, true ); |
| 1533 |
?> |
| 1534 |
<li> |
| 1535 |
<input type="checkbox" name="ddp_pstati[]" id="ddp_pstatus-<?php |
| 1536 |
echo esc_attr( $key ); |
| 1537 |
?>" value="<?php |
| 1538 |
echo esc_attr( $key ); |
| 1539 |
?>" |
| 1540 |
<?php |
| 1541 |
if ( false !== $checked ) { |
| 1542 |
echo ' checked'; |
| 1543 |
} |
| 1544 |
if ( 1 === $staticount ) { |
| 1545 |
echo ' disabled'; |
| 1546 |
} |
| 1547 |
?> |
| 1548 |
/><label for="ddp_pstatus-<?php |
| 1549 |
echo esc_attr( $key ); |
| 1550 |
?>"> |
| 1551 |
<?php |
| 1552 |
echo esc_html( $key . ' (' . $st->label . ')' ); |
| 1553 |
if ( 'trash' === $key ) { |
| 1554 |
echo ' <small>' . esc_html__( 'Warning: Enabling this can give false results. Only enable if you know what you are doing.', 'delete-duplicate-posts' ) . '</small>'; |
| 1555 |
} |
| 1556 |
?> |
| 1557 |
</label> |
| 1558 |
</li> |
| 1559 |
<?php |
| 1560 |
++$step; |
| 1561 |
} |
| 1562 |
} |
| 1563 |
?> |
| 1564 |
</ul> |
| 1565 |
<?php |
| 1566 |
} |
| 1567 |
?> |
| 1568 |
</td> |
| 1569 |
|
| 1570 |
|
| 1571 |
</tr> |
| 1572 |
<?php |
| 1573 |
$comparemethod = 'titlecompare'; |
| 1574 |
global $ddp_fs; |
| 1575 |
?> |
| 1576 |
<tr valign="top"> |
| 1577 |
<th><?php |
| 1578 |
esc_html_e( 'Comparison Method', 'delete-duplicate-posts' ); |
| 1579 |
?></th> |
| 1580 |
<td> |
| 1581 |
<ul class="ddpcomparemethod"> |
| 1582 |
|
| 1583 |
<li> |
| 1584 |
<label> |
| 1585 |
<input type="radio" name="ddp_method" value="titlecompare" <?php |
| 1586 |
checked( 'titlecompare', $comparemethod ); |
| 1587 |
?> /> |
| 1588 |
<?php |
| 1589 |
esc_html_e( 'Compare by title (default)', 'delete-duplicate-posts' ); |
| 1590 |
?> |
| 1591 |
<span class="optiondesc"><?php |
| 1592 |
esc_html_e( 'Looks at the title of the post itself.', 'delete-duplicate-posts' ); |
| 1593 |
?></span> |
| 1594 |
</label> |
| 1595 |
|
| 1596 |
</li> |
| 1597 |
|
| 1598 |
<?php |
| 1599 |
global $ddp_fs; |
| 1600 |
?> |
| 1601 |
</ul> |
| 1602 |
</td> |
| 1603 |
</tr> |
| 1604 |
<tr> |
| 1605 |
<th><label for="ddp_keep"><?php |
| 1606 |
esc_html_e( 'Delete which posts?:', 'delete-duplicate-posts' ); |
| 1607 |
?></label></th> |
| 1608 |
<td> |
| 1609 |
|
| 1610 |
<select name="ddp_keep" id="ddp_keep"> |
| 1611 |
<option value="oldest" |
| 1612 |
<?php |
| 1613 |
if ( 'oldest' === $options['ddp_keep'] ) { |
| 1614 |
echo 'selected="selected"'; |
| 1615 |
} |
| 1616 |
?> |
| 1617 |
><?php |
| 1618 |
esc_html_e( 'Keep oldest', 'delete-duplicate-posts' ); |
| 1619 |
?></option> |
| 1620 |
<option value="latest" |
| 1621 |
<?php |
| 1622 |
if ( 'latest' === $options['ddp_keep'] ) { |
| 1623 |
echo 'selected="selected"'; |
| 1624 |
} |
| 1625 |
?> |
| 1626 |
><?php |
| 1627 |
esc_html_e( 'Keep latest', 'delete-duplicate-posts' ); |
| 1628 |
?></option> |
| 1629 |
</select> |
| 1630 |
<p class="description"> |
| 1631 |
<?php |
| 1632 |
esc_html_e( 'Keep the oldest or the latest version of duplicates? Default is keeping the oldest, and deleting any subsequent duplicate posts', 'delete-duplicate-posts' ); |
| 1633 |
?> |
| 1634 |
</p> |
| 1635 |
</td> |
| 1636 |
</tr> |
| 1637 |
|
| 1638 |
|
| 1639 |
|
| 1640 |
|
| 1641 |
|
| 1642 |
|
| 1643 |
|
| 1644 |
|
| 1645 |
<?php |
| 1646 |
?> |
| 1647 |
<tr> |
| 1648 |
<td colspan="2"> |
| 1649 |
<hr> |
| 1650 |
<h3><?php |
| 1651 |
esc_html_e( 'Delete Duplicates Automatically', 'delete-duplicate-posts' ); |
| 1652 |
?></h3> |
| 1653 |
</td> |
| 1654 |
</tr> |
| 1655 |
|
| 1656 |
<tr valign="top"> |
| 1657 |
<th><?php |
| 1658 |
esc_html_e( 'Enable automatic deletion?:', 'delete-duplicate-posts' ); |
| 1659 |
?> |
| 1660 |
</th> |
| 1661 |
<td><label for="ddp_enabled"> |
| 1662 |
<input type="checkbox" id="ddp_enabled" name="ddp_enabled" |
| 1663 |
<?php |
| 1664 |
if ( true === $options['ddp_enabled'] ) { |
| 1665 |
echo 'checked="checked"'; |
| 1666 |
} |
| 1667 |
?> |
| 1668 |
> |
| 1669 |
<p class="description"> |
| 1670 |
<?php |
| 1671 |
esc_html_e( 'Clean duplicates automatically.', 'delete-duplicate-posts' ); |
| 1672 |
?></p> |
| 1673 |
</label> |
| 1674 |
</td> |
| 1675 |
</tr> |
| 1676 |
|
| 1677 |
<tr> |
| 1678 |
<th><label for="ddp_resultslimit"><?php |
| 1679 |
esc_html_e( 'How many:', 'delete-duplicate-posts' ); |
| 1680 |
?></label> |
| 1681 |
</th> |
| 1682 |
<td> |
| 1683 |
|
| 1684 |
<?php |
| 1685 |
$dupe_options = array( |
| 1686 |
0 => __( 'No limit', 'delete-duplicate-posts' ), |
| 1687 |
10000 => number_format_i18n( '10000' ), |
| 1688 |
5000 => number_format_i18n( '5000' ), |
| 1689 |
2500 => number_format_i18n( '2500' ), |
| 1690 |
1000 => number_format_i18n( '1000' ), |
| 1691 |
500 => '500', |
| 1692 |
250 => '250', |
| 1693 |
100 => '100', |
| 1694 |
50 => '50', |
| 1695 |
10 => '10', |
| 1696 |
); |
| 1697 |
?> |
| 1698 |
<select name="ddp_resultslimit" id="ddp_resultslimit"> |
| 1699 |
<?php |
| 1700 |
foreach ( $dupe_options as $key => $label ) { |
| 1701 |
?> |
| 1702 |
<option value="<?php |
| 1703 |
echo esc_attr( $key ); |
| 1704 |
?>" <?php |
| 1705 |
selected( $options['ddp_resultslimit'], $key ); |
| 1706 |
?>> |
| 1707 |
<?php |
| 1708 |
echo esc_attr( $label ); |
| 1709 |
?></option> |
| 1710 |
<?php |
| 1711 |
} |
| 1712 |
?> |
| 1713 |
</select> |
| 1714 |
|
| 1715 |
<p class="description"> |
| 1716 |
<?php |
| 1717 |
esc_html_e( 'If you have many duplicates, the plugin might time out before finding them all. Try limiting the amount of duplicates here. Default: Unlimited.', 'delete-duplicate-posts' ); |
| 1718 |
?><br> |
| 1719 |
<strong><?php |
| 1720 |
esc_html_e( 'This only applies to automatic (CRON) jobs.', 'delete-duplicate-posts' ); |
| 1721 |
?></strong> |
| 1722 |
</p> |
| 1723 |
</td> |
| 1724 |
</tr> |
| 1725 |
|
| 1726 |
<tr> |
| 1727 |
<th><label for="ddp_schedule"><?php |
| 1728 |
esc_html_e( 'How often?:', 'delete-duplicate-posts' ); |
| 1729 |
?></label> |
| 1730 |
</th> |
| 1731 |
<td> |
| 1732 |
|
| 1733 |
<select name="ddp_schedule" id="ddp_schedule"> |
| 1734 |
<?php |
| 1735 |
$schedules = wp_get_schedules(); |
| 1736 |
if ( $schedules ) { |
| 1737 |
foreach ( $schedules as $key => $sch ) { |
| 1738 |
?> |
| 1739 |
<option value="<?php |
| 1740 |
echo esc_attr( $key ); |
| 1741 |
?>" |
| 1742 |
<?php |
| 1743 |
if ( isset( $options['ddp_schedule'] ) && esc_attr( $key ) === $options['ddp_schedule'] ) { |
| 1744 |
echo esc_html( 'selected="selected"' ); |
| 1745 |
} |
| 1746 |
?> |
| 1747 |
><?php |
| 1748 |
echo esc_html( $sch['display'] ); |
| 1749 |
?></option> |
| 1750 |
<?php |
| 1751 |
} |
| 1752 |
} |
| 1753 |
?> |
| 1754 |
</select> |
| 1755 |
<p class="description"> |
| 1756 |
<?php |
| 1757 |
esc_html_e( 'How often should the cron job run?', 'delete-duplicate-posts' ); |
| 1758 |
?></p> |
| 1759 |
</td> |
| 1760 |
</tr> |
| 1761 |
<tr> |
| 1762 |
<td colspan="2"> |
| 1763 |
<hr> |
| 1764 |
</td> |
| 1765 |
</tr> |
| 1766 |
|
| 1767 |
<tr> |
| 1768 |
<th><?php |
| 1769 |
esc_html_e( 'Send status mail?:', 'delete-duplicate-posts' ); |
| 1770 |
?></th> |
| 1771 |
<td> |
| 1772 |
<label for="ddp_statusmail"> |
| 1773 |
<input type="checkbox" id="ddp_statusmail" name="ddp_statusmail" |
| 1774 |
<?php |
| 1775 |
if ( isset( $options['ddp_statusmail'] ) && true === $options['ddp_statusmail'] ) { |
| 1776 |
?> |
| 1777 |
checked="checked" |
| 1778 |
<?php |
| 1779 |
} |
| 1780 |
?> |
| 1781 |
> |
| 1782 |
<p class="description"> |
| 1783 |
<?php |
| 1784 |
esc_html_e( 'Sends a status email if duplicates have been found.', 'delete-duplicate-posts' ); |
| 1785 |
?> |
| 1786 |
</p> |
| 1787 |
</label> |
| 1788 |
</td> |
| 1789 |
</tr> |
| 1790 |
|
| 1791 |
<tr> |
| 1792 |
<th><?php |
| 1793 |
esc_html_e( 'Email recipient:', 'delete-duplicate-posts' ); |
| 1794 |
?></th> |
| 1795 |
<td> |
| 1796 |
<label for="ddp_statusmail_recipient"> |
| 1797 |
|
| 1798 |
<input type="text" class="regular-text" id="ddp_statusmail_recipient" name="ddp_statusmail_recipient" value="<?php |
| 1799 |
echo esc_html( $options['ddp_statusmail_recipient'] ); |
| 1800 |
?>"> |
| 1801 |
<p class="description"> |
| 1802 |
<?php |
| 1803 |
esc_html_e( 'Who should get the notification email.', 'delete-duplicate-posts' ); |
| 1804 |
?></p> |
| 1805 |
</label> |
| 1806 |
</td> |
| 1807 |
</tr> |
| 1808 |
|
| 1809 |
|
| 1810 |
|
| 1811 |
<tr> |
| 1812 |
<td colspan="2"> |
| 1813 |
<hr> |
| 1814 |
</td> |
| 1815 |
</tr> |
| 1816 |
|
| 1817 |
<tr> |
| 1818 |
<th><?php |
| 1819 |
esc_html_e( 'Enable debug logging?:', 'delete-duplicate-posts' ); |
| 1820 |
?></th> |
| 1821 |
<td> |
| 1822 |
<label for="ddp_debug"> |
| 1823 |
<input type="checkbox" id="ddp_debug" name="ddp_debug" |
| 1824 |
<?php |
| 1825 |
if ( isset( $options['ddp_debug'] ) && true === $options['ddp_debug'] ) { |
| 1826 |
echo 'checked="checked"'; |
| 1827 |
} |
| 1828 |
?> |
| 1829 |
> |
| 1830 |
<p class="description"> |
| 1831 |
<?php |
| 1832 |
esc_html_e( 'Should only be enabled if debugging a problem.', 'delete-duplicate-posts' ); |
| 1833 |
?> |
| 1834 |
</p> |
| 1835 |
</label> |
| 1836 |
</td> |
| 1837 |
</tr> |
| 1838 |
<th colspan=2><input type="submit" class="button-primary" name="delete_duplicate_posts_save" value="<?php |
| 1839 |
esc_html_e( 'Save Settings', 'delete-duplicate-posts' ); |
| 1840 |
?>" /></th> |
| 1841 |
</tr> |
| 1842 |
</table> |
| 1843 |
</form> |
| 1844 |
</div><!-- #configuration --> |
| 1845 |
</div> |
| 1846 |
|
| 1847 |
|
| 1848 |
<div id="redirects-tab" class="tab-content<?php |
| 1849 |
echo ( $ddp_fs->is__premium_only() && $ddp_fs->can_use_premium_code() ? '' : ' pro' ); |
| 1850 |
?>" style="display: none;"> |
| 1851 |
<?php |
| 1852 |
if ( $ddp_fs->is__premium_only() && $ddp_fs->can_use_premium_code() ) { |
| 1853 |
?> |
| 1854 |
<h3><?php |
| 1855 |
esc_html_e( 'Redirects', 'delete-duplicate-posts' ); |
| 1856 |
?></h3> |
| 1857 |
<p><?php |
| 1858 |
esc_html_e( 'This table shows all redirects created by the plugin.', 'delete-duplicate-posts' ); |
| 1859 |
?></p> |
| 1860 |
<table id="ddp_redirtable" class="wp-list-table widefat fixed striped table-view-list"> |
| 1861 |
<thead> |
| 1862 |
<tr> |
| 1863 |
<th><?php |
| 1864 |
esc_html_e( 'ID', 'delete-duplicate-posts' ); |
| 1865 |
?></th> |
| 1866 |
<th><?php |
| 1867 |
esc_html_e( 'From URL', 'delete-duplicate-posts' ); |
| 1868 |
?></th> |
| 1869 |
<th><?php |
| 1870 |
esc_html_e( 'Target URL', 'delete-duplicate-posts' ); |
| 1871 |
?></th> |
| 1872 |
</tr> |
| 1873 |
</thead> |
| 1874 |
<tbody> |
| 1875 |
<!-- DataTables will populate this --> |
| 1876 |
</tbody> |
| 1877 |
</table> |
| 1878 |
<?php |
| 1879 |
} else { |
| 1880 |
?> |
| 1881 |
<h3><?php |
| 1882 |
esc_html_e( 'Redirects', 'delete-duplicate-posts' ); |
| 1883 |
?></h3> |
| 1884 |
<p><?php |
| 1885 |
esc_html_e( 'Redirects are a premium feature. Please upgrade to access this functionality.', 'delete-duplicate-posts' ); |
| 1886 |
?></p> |
| 1887 |
<?php |
| 1888 |
} |
| 1889 |
?> |
| 1890 |
</div> |
| 1891 |
|
| 1892 |
</div> |
| 1893 |
</div> |
| 1894 |
|
| 1895 |
<?php |
| 1896 |
include_once 'sidebar.php'; |
| 1897 |
if ( function_exists( 'ddp_fs' ) ) { |
| 1898 |
global $ddp_fs; |
| 1899 |
} |
| 1900 |
?> |
| 1901 |
</div> |
| 1902 |
|
| 1903 |
</div> |
| 1904 |
|
| 1905 |
|
| 1906 |
|
| 1907 |
<script> |
| 1908 |
jQuery(document).ready(function($) { |
| 1909 |
const navTabWrapper = $('.nav-tab-wrapper'); |
| 1910 |
const currentTabs = $('.nav-tab-wrapper a'); |
| 1911 |
|
| 1912 |
currentTabs.each(function() { |
| 1913 |
$(this).on('click', function(e) { |
| 1914 |
const href = $(this).attr('href'); |
| 1915 |
console.log('Clicked tab href:', href); |
| 1916 |
|
| 1917 |
try { |
| 1918 |
if (!href.startsWith('#')) { |
| 1919 |
e.preventDefault(); // Prevent default tab behavior for full URLs |
| 1920 |
window.location.href = href; // Load the page to the URL in the same window |
| 1921 |
} else { |
| 1922 |
e.preventDefault(); // Prevent default anchor behavior |
| 1923 |
console.log('Switching to tab:', href); |
| 1924 |
|
| 1925 |
// Switch as a regular tab for href starting with '#' |
| 1926 |
currentTabs.removeClass('nav-tab-active'); |
| 1927 |
$(this).addClass('nav-tab-active'); |
| 1928 |
|
| 1929 |
// Hide all tab content |
| 1930 |
$('.tab-content').hide(); |
| 1931 |
|
| 1932 |
// Show the content for the clicked tab |
| 1933 |
$(href).show(); |
| 1934 |
} |
| 1935 |
} catch (error) { |
| 1936 |
console.error('Error occurred:', error); |
| 1937 |
} |
| 1938 |
}); |
| 1939 |
}); |
| 1940 |
|
| 1941 |
// Initially hide all tab content except the active one |
| 1942 |
$('.tab-content').hide(); |
| 1943 |
$('.nav-tab-active').each(function() { |
| 1944 |
const activeHref = $(this).attr('href'); |
| 1945 |
$(activeHref).show(); |
| 1946 |
}); |
| 1947 |
}); |
| 1948 |
</script> |
| 1949 |
|
| 1950 |
|
| 1951 |
|
| 1952 |
|
| 1953 |
|
| 1954 |
|
| 1955 |
|
| 1956 |
|
| 1957 |
<?php |
| 1958 |
} |
| 1959 |
|
| 1960 |
} |
| 1961 |
|
| 1962 |
//End Class |
| 1963 |
} |
| 1964 |
if ( class_exists( __NAMESPACE__ . '\\Delete_Duplicate_Posts' ) ) { |
| 1965 |
$delete_duplicate_posts_var = new Delete_Duplicate_Posts(); |
| 1966 |
} |