Plugin Directory

Changeset 3481394


Ignore:
Timestamp:
03/12/2026 05:59:22 PM (2 weeks ago)
Author:
apos37
Message:

1.3.7.2

  • Tweak: Remove links on rescan by ID instead of link hash lookup
  • Tweak: Added actual error message to front-end scan if there is an error so we can figure out what the actual problem is
Location:
broken-link-notifier
Files:
56 added
6 edited

Legend:

Unmodified
Added
Removed
  • broken-link-notifier/trunk/broken-link-notifier.php

    r3481332 r3481394  
    44 * Plugin URI:          https://pluginrx.com/plugin/broken-link-notifier/
    55 * Description:         Get notified when someone loads a page with a broken link
    6  * Version:             1.3.7.1
     6 * Version:             1.3.7.2
    77 * Requires at least:   5.9
    88 * Tested up to:        6.9
  • broken-link-notifier/trunk/includes/js/results-back.js

    r3475895 r3481394  
    183183                action: 'blnotifier_replace_link',
    184184                nonce: nonceReplace,
     185                linkID: linkID,
    185186                oldLink: oldLink,
    186187                newLink: newLink,
     
    241242        let button = $( this );
    242243        let link = button.data( 'link' );
     244        let linkID = $( this ).closest( 'tr' ).data( 'link-id' );
    243245
    244246        $.ajax( {
     
    249251                action: 'blnotifier_delete_result',
    250252                nonce: nonceDelete,
    251                 link: link
     253                link: link,
     254                linkID: linkID
    252255            },
    253256            success: function ( response ) {
  • broken-link-notifier/trunk/includes/js/results-front.js

    r3143920 r3481394  
    133133                // Failure
    134134                } else if ( response.type == 'error' ) {
    135                     console.log( 'Scan failed. Please contact plugin developer.' );
     135                    var errorMsg = response.msg ? response.msg : 'Unknown error occurred.';
     136                    console.error( 'Scan failed: ' + errorMsg );
    136137                }
    137138            }
  • broken-link-notifier/trunk/includes/js/results-front.min.js

    r3283556 r3481394  
    1 jQuery(o=>{const n=blnotifier_front_end.elements,e=window.location.search,i=new URLSearchParams(e);if(i.has("blink")){console.log("Looking for highlights; checking for broken links paused.");const e=i.get("blink");o.each(n,function(n,i){o(n).not("#wpadminbar "+n).each(function(n){const t=o(this).attr(i);if(void 0!==t&&t.includes(e))if(o(this).addClass("glowText"),o(this).is(":hidden")){var r="It looks like one or more of the links are hidden. To find them, try searching for it in your browser's Developer console.";console.log(r),alert(r)}else console.log("The element should glow yellow if it is visible on the page. If you do not see it on the page, then it is hidden somewhere. Check any JavaScript elements, too. You can try searching for it in your browser's Developer console.")})})}else{blnotifier_front_end.show_in_console&&console.log("%c Fetching links using the Broken Link Notifier Plugin... ","background: #2570AC; color: white");var t=[],r=[],s=[];o.each(n,function(n,e){o(n).each(function(n){const i=o(this).attr(e),l=o(this).parents("#wpadminbar").length,c=o(this).parents("header").length,a=o(this).parents("footer").length;void 0===i||l||(blnotifier_front_end.scan_header&&c?t.push(i):blnotifier_front_end.scan_footer&&a?s.push(i):c||a||r.push(i))})}),blnotifier_front_end.show_in_console&&(blnotifier_front_end.scan_header&&(console.log("%c Header links found: ","background: #222; color: #bada55"),console.log(t)),console.log("%c Content links found: ","background: #222; color: #bada55"),console.log(r),blnotifier_front_end.scan_header&&(console.log("%c Footer links found: ","background: #222; color: #bada55"),console.log(s)),console.log("%c Scanning for broken links... please wait. This may take a few minutes if there are a lot of links.","background: #2570AC; color: white"));var l=blnotifier_front_end.nonce;o.ajax({type:"post",dataType:"json",url:blnotifier_front_end.ajaxurl,data:{action:"blnotifier_blinks",nonce:l,scan_header:blnotifier_front_end.scan_header,scan_footer:blnotifier_front_end.scan_footer,source_url:window.location.href,header_links:t,content_links:r,footer_links:s},success:function(e){"success"==e.type?(blnotifier_front_end.show_in_console&&(console.log("%c Broken Link Scan Results: ","background: #2570AC; color: white"),e.notify?console.error(e.notify):console.info("No broken links found. :)"),console.log(`%c ${e.timing} `,"background: #2570AC; color: white")),i.has("blinks")&&"true"==i.get("blinks")&&o.each(e.notify,function(e,i){o.each(i,function(e,i){o.each(n,function(n,e){o(n).each(function(n){var t=o(this).attr(e);i.link==t&&o(this).addClass("glowText")})})})})):"error"==e.type&&console.log("Scan failed. Please contact plugin developer.")}})}});
     1jQuery(o=>{const n=blnotifier_front_end.elements,e=window.location.search,i=new URLSearchParams(e);if(i.has("blink")){console.log("Looking for highlights; checking for broken links paused.");const e=i.get("blink");o.each(n,function(n,i){o(n).not("#wpadminbar "+n).each(function(n){const t=o(this).attr(i);if(void 0!==t&&t.includes(e))if(o(this).addClass("glowText"),o(this).is(":hidden")){var r="It looks like one or more of the links are hidden. To find them, try searching for it in your browser's Developer console.";console.log(r),alert(r)}else console.log("The element should glow yellow if it is visible on the page. If you do not see it on the page, then it is hidden somewhere. Check any JavaScript elements, too. You can try searching for it in your browser's Developer console.")})})}else{blnotifier_front_end.show_in_console&&console.log("%c Fetching links using the Broken Link Notifier Plugin... ","background: #2570AC; color: white");var t=[],r=[],s=[];o.each(n,function(n,e){o(n).each(function(n){const i=o(this).attr(e),l=o(this).parents("#wpadminbar").length,c=o(this).parents("header").length,a=o(this).parents("footer").length;void 0===i||l||(blnotifier_front_end.scan_header&&c?t.push(i):blnotifier_front_end.scan_footer&&a?s.push(i):c||a||r.push(i))})}),blnotifier_front_end.show_in_console&&(blnotifier_front_end.scan_header&&(console.log("%c Header links found: ","background: #222; color: #bada55"),console.log(t)),console.log("%c Content links found: ","background: #222; color: #bada55"),console.log(r),blnotifier_front_end.scan_header&&(console.log("%c Footer links found: ","background: #222; color: #bada55"),console.log(s)),console.log("%c Scanning for broken links... please wait. This may take a few minutes if there are a lot of links.","background: #2570AC; color: white"));var l=blnotifier_front_end.nonce;o.ajax({type:"post",dataType:"json",url:blnotifier_front_end.ajaxurl,data:{action:"blnotifier_blinks",nonce:l,scan_header:blnotifier_front_end.scan_header,scan_footer:blnotifier_front_end.scan_footer,source_url:window.location.href,header_links:t,content_links:r,footer_links:s},success:function(e){if("success"==e.type)blnotifier_front_end.show_in_console&&(console.log("%c Broken Link Scan Results: ","background: #2570AC; color: white"),e.notify?console.error(e.notify):console.info("No broken links found. :)"),console.log(`%c ${e.timing} `,"background: #2570AC; color: white")),i.has("blinks")&&"true"==i.get("blinks")&&o.each(e.notify,function(e,i){o.each(i,function(e,i){o.each(n,function(n,e){o(n).each(function(n){var t=o(this).attr(e);i.link==t&&o(this).addClass("glowText")})})})});else if("error"==e.type){var t=e.msg?e.msg:"Unknown error occurred.";console.error("Scan failed: "+t)}}})}});
  • broken-link-notifier/trunk/includes/results.php

    r3481332 r3481394  
    377377    /**
    378378     * Remove a broken or warning link
    379      *
    380      * @param string $link
     379     *
     380     * @param string   $link
     381     * @param int|bool $id Optional link ID for direct deletion
    381382     * @return boolean
    382383     */
    383     public function remove( $link ) {
     384    public function remove( $link, $id = false ) {
    384385        global $wpdb;
    385386        $table_name = $wpdb->prefix . $this->table_name;
    386         $link = sanitize_text_field( $link );
    387 
    388         // 1. New Normalized Hash
    389         $url_parts = explode( '?', $link );
    390         $url_parts[ 0 ] = untrailingslashit( $url_parts[ 0 ] );
    391         $new_hash = md5( strtolower( implode( '?', $url_parts ) ) );
    392 
    393         // 2. Old Logic Hash (Legacy)
    394         $old_hash = md5( strtolower( untrailingslashit( $link ) ) );
    395 
    396         // Try to delete by either hash
    397         $deleted = $wpdb->query(
    398             $wpdb->prepare(
    399                 "DELETE FROM $table_name WHERE link_hash = %s OR link_hash = %s",
    400                 $new_hash,
    401                 $old_hash
    402             )
    403         );
     387        $deleted    = 0;
     388
     389        // 1. Try deleting by ID first if provided
     390        if ( $id ) {
     391            $deleted = $wpdb->delete(
     392                $table_name,
     393                [ 'id' => absint( $id ) ],
     394                [ '%d' ]
     395            );
     396        }
     397
     398        // 2. Fallback to hash lookup if ID didn't work or wasn't provided
     399        if ( ! $deleted ) {
     400            $link = sanitize_text_field( $link );
     401
     402            // New Normalized Hash
     403            $url_parts = explode( '?', $link );
     404            $url_parts[0] = untrailingslashit( $url_parts[0] );
     405            $new_hash = md5( strtolower( implode( '?', $url_parts ) ) );
     406
     407            // Old Logic Hash (Legacy)
     408            $old_hash = md5( strtolower( untrailingslashit( $link ) ) );
     409
     410            $deleted = $wpdb->query(
     411                $wpdb->prepare(
     412                    "DELETE FROM $table_name WHERE link_hash = %s OR link_hash = %s",
     413                    $new_hash,
     414                    $old_hash
     415                )
     416            );
     417        }
    404418
    405419        return ( false !== $deleted && $deleted > 0 );
     
    583597        // Public endpoint: allow guests, but validate capability for logged-in users.
    584598        if ( is_user_logged_in() && ! current_user_can( 'read' ) ) {
    585             wp_send_json_error( 'Permission denied' );
     599            $result = [
     600                'type' => 'error',
     601                'msg'  => 'Permission denied'
     602            ];
     603            self::send_ajax_or_redirect( $result );
    586604        }
    587605   
     
    592610        $footer_links  = isset( $_REQUEST[ 'footer_links' ] ) ? wp_unslash( $_REQUEST[ 'footer_links' ] ) : []; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
    593611
     612        // Permissions
     613        $user_can_manage = (new BLNOTIFIER_HELPERS)->user_can_manage_broken_links();
     614
    594615        // Enforce max links per page
    595616        $max_links = absint( get_option( 'blnotifier_max_links_per_page', 200 ) );
    596617        $total_links = count( $header_links ) + count( $content_links ) + count( $footer_links );
    597618        if ( $total_links > $max_links ) {
     619            $error_msg = sprintf( __( 'Too many links in one scan. Max allowed: %d.', 'broken-link-notifier' ), $max_links );
     620
     621            // If the user is an admin/manager, append the instruction
     622            if ( $user_can_manage ) {
     623                $error_msg .= ' ' . __( 'You can increase this limit in the plugin settings.', 'broken-link-notifier' );
     624            }
     625
    598626            $result = [
    599627                'type' => 'error',
    600                 'msg'  => sprintf( 'Too many links in one scan. Max allowed: %d', $max_links )
     628                'msg'  => $error_msg
    601629            ];
     630           
    602631            self::send_ajax_or_redirect( $result );
    603632        }
    604633
    605634        // Rate limit per IP only for non-link-managers
    606         if ( !(new BLNOTIFIER_HELPERS)->user_can_manage_broken_links() ) {
     635        if ( !$user_can_manage ) {
    607636            $ip = $_SERVER[ 'REMOTE_ADDR' ];
    608637            $transient_key = 'bln_rate_' . md5( $ip );
     
    622651            // Only allow webpages, not file:///, etc.
    623652            if ( !str_starts_with( $source_url, 'http' ) ) {
    624                 wp_send_json_error( 'Invalid source: ' . $source_url );
     653                $result = [
     654                    'type' => 'error',
     655                    'msg'  => 'Invalid source: ' . $source_url
     656                ];
     657                self::send_ajax_or_redirect( $result );
    625658            }
    626659
     
    628661            $site_url = site_url();
    629662            if ( ! str_starts_with( $source_url, $site_url ) ) {
    630                 wp_send_json_error( 'External source URLs are not permitted.' );
     663                $result = [
     664                    'type' => 'error',
     665                    'msg'  => 'External source URLs are not permitted.'
     666                ];
     667                self::send_ajax_or_redirect( $result );
    631668            }
    632669
     
    642679            // If it's not a post/page and it's not the homepage, it's likely a 404 or invalid
    643680            if ( ! $post_id && $source_url !== trailingslashit( $site_url ) && $source_url !== $site_url ) {
    644                 wp_send_json_error( 'Source URL does not exist on this site.' );
     681                $result = [
     682                    'type' => 'error',
     683                    'msg'  => 'Source URL does not exist on this site.'
     684                ];
     685                self::send_ajax_or_redirect( $result );
    645686            }
    646687
     
    804845            // If the source no longer exists, auto remove it
    805846            if ( !$source_id || !get_post( $source_id ) ) {
    806                 $remove = $this->remove( $HELPERS->str_replace_on_link( $link ) );
     847                $remove = $this->remove( $HELPERS->str_replace_on_link( $link ), $link_id );
    807848                $status = [
    808849                    'type' => 'n/a',
     
    819860                } else {
    820861                    $result[ 'type' ] = 'error';
    821                     $result[ 'msg' ] = __( 'Could not auto-remove link.', 'broken-link-notifier' );
     862                    $result[ 'msg' ] = __( 'Could not auto-remove link. Link not found in DB.', 'broken-link-notifier' );
    822863                }
    823864
     
    830871                // If it's good now, remove the old post
    831872                if ( $status[ 'type' ] == 'good' || $status[ 'type' ] == 'omitted' ) {
    832                     $remove = $this->remove( $HELPERS->str_replace_on_link( $link ) );
     873                    $remove = $this->remove( $HELPERS->str_replace_on_link( $link ), $link_id );
    833874                    if ( $remove ) {
    834875                        $result[ 'type' ] = 'success';
     
    846887                // If it's still not good, but doesn't have the same code or type, update it
    847888                } elseif ( $code !== $status[ 'code' ] || $type !== $status[ 'type' ] ) {
    848                     $remove = $this->remove( $HELPERS->str_replace_on_link( $link ) );
     889                    $remove = $this->remove( $HELPERS->str_replace_on_link( $link ), $link_id );
    849890                    if ( $remove ) {
    850891                        $result[ 'type' ] = 'success';
     
    904945   
    905946        // Get the vars
     947        $link_id    = isset( $_REQUEST[ 'linkID' ] ) ? absint( wp_unslash( $_REQUEST[ 'linkID' ] ) ) : false;
    906948        $oldLink    = isset( $_REQUEST[ 'oldLink' ] ) ? sanitize_text_field( wp_unslash( $_REQUEST[ 'oldLink' ] ) ) : false;
    907949        $newLink    = isset( $_REQUEST[ 'newLink' ] ) ? sanitize_text_field( wp_unslash( $_REQUEST[ 'newLink' ] ) ) : false;
     
    927969
    928970                // Let's also delete the result
    929                 $this->remove( $HELPERS->str_replace_on_link( $oldLink ) );
     971                $this->remove( $HELPERS->str_replace_on_link( $oldLink ), $link_id );
    930972
    931973                // Respond
     
    9591001        // Remove the link
    9601002        $link = isset( $_REQUEST[ 'link' ] ) ? sanitize_text_field( wp_unslash( $_REQUEST[ 'link' ] ) ) : false;
     1003        $link_id = isset( $_REQUEST[ 'linkID' ] ) ? absint( wp_unslash( $_REQUEST[ 'linkID' ] ) ) : false;
     1004       
    9611005        if ( $link ) {
    962             $this->remove( $HELPERS->str_replace_on_link( $link ) );
     1006            $this->remove( $HELPERS->str_replace_on_link( $link ), $link_id );
    9631007            wp_send_json_success();
    9641008        }
  • broken-link-notifier/trunk/readme.txt

    r3481332 r3481394  
    55Tested up to: 6.9
    66Requires PHP: 7.4
    7 Stable tag: 1.3.7.1
     7Stable tag: 1.3.7.2
    88License: GPLv2 or later
    99License URI: http://www.gnu.org/licenses/gpl-2.0.txt
     
    127127
    128128== Changelog ==
     129= 1.3.7.2 =
     130* Tweak: Remove links on rescan by ID instead of link hash lookup
     131* Tweak: Added actual error message to front-end scan if there is an error so we can figure out what the actual problem is
     132
    129133= 1.3.7.1 =
    130134* Tweak: Skipping internal pagination and reply links
Note: See TracChangeset for help on using the changeset viewer.