Broken Link Checker - Version 0.4.8

Version Description

Download this release

Release Info

Developer whiteshadow
Plugin Icon 128x128 Broken Link Checker
Version 0.4.8
Comparing to
See all releases

Code changes from version 0.4.7 to 0.4.8

Files changed (3) hide show
  1. broken-link-checker.php +252 -14
  2. readme.txt +4 -4
  3. wsblc_ajax.php +82 -44
broken-link-checker.php CHANGED
@@ -3,7 +3,7 @@
3
  Plugin Name: Broken Link Checker
4
  Plugin URI: http://w-shadow.com/blog/2007/08/05/broken-link-checker-for-wordpress/
5
  Description: Checks your posts for broken links and missing images and notifies you on the dashboard if any are found.
6
- Version: 0.4.7
7
  Author: Janis Elsts
8
  Author URI: http://w-shadow.com/blog/
9
  */
@@ -13,6 +13,9 @@ Created by Janis Elsts (email : whiteshadow@w-shadow.com)
13
  MySQL 4.0 compatibility by Jeroen (www.yukka.eu)
14
  */
15
 
 
 
 
16
  if (!class_exists('ws_broken_link_checker')) {
17
 
18
  class ws_broken_link_checker {
@@ -20,7 +23,7 @@ class ws_broken_link_checker {
20
  var $options_name='wsblc_options';
21
  var $postdata_name;
22
  var $linkdata_name;
23
- var $version='0.4.7';
24
  var $myfile='';
25
  var $myfolder='';
26
  var $mybasename='';
@@ -125,12 +128,25 @@ class ws_broken_link_checker {
125
  }
126
  }
127
 
 
 
 
 
 
 
 
128
  function normalize_url($url){
129
  $parts=@parse_url($url);
130
  if(!$parts) return false;
131
 
 
 
 
 
 
 
132
  $url = html_entity_decode($url);
133
- $url=preg_replace(
134
  array('/([\?&]PHPSESSID=\w+)$/i',
135
  '/(#[^\/]*)$/',
136
  '/&/',
@@ -141,7 +157,6 @@ class ws_broken_link_checker {
141
  $url);
142
  $url=trim($url);
143
 
144
- if (strpos($url, 'mailto:')!==false) return false;
145
  if($url=='') return false;
146
 
147
  // turn relative URLs into absolute URLs
@@ -204,6 +219,180 @@ class ws_broken_link_checker {
204
  return $url;
205
  }
206
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
207
  function is_excluded($url){
208
  if (!is_array($this->options['exclusion_list'])) return false;
209
  foreach($this->options['exclusion_list'] as $excluded_word){
@@ -306,9 +495,6 @@ class ws_broken_link_checker {
306
  function activation(){
307
  global $wpdb;
308
 
309
- //option default were already set in the constructor
310
- update_option($this->options_name, $this->options);
311
-
312
  require_once(ABSPATH . 'wp-admin/upgrade-functions.php');
313
 
314
  if (($wpdb->get_var("show tables like '".($this->postdata_name)."'") != $this->postdata_name)
@@ -328,26 +514,56 @@ class ws_broken_link_checker {
328
  id BIGINT( 20 ) UNSIGNED NOT NULL AUTO_INCREMENT ,
329
  post_id BIGINT( 20 ) NOT NULL ,
330
  url TEXT NOT NULL ,
 
331
  link_text VARCHAR( 50 ) NOT NULL ,
332
  broken TINYINT( 1 ) UNSIGNED DEFAULT '0' NOT NULL,
333
  last_check DATETIME NOT NULL ,
334
- check_count TINYINT( 2 ) UNSIGNED DEFAULT '0' NOT NULL,
 
 
 
335
  PRIMARY KEY id (id)
336
  );";
337
 
338
  dbDelta($sql);
 
 
 
 
339
  }
340
 
341
  $this->sync_posts_to_db();
 
 
 
 
342
  }
343
 
344
  function options_menu(){
345
  add_options_page('Link Checker Settings', 'Link Checker', 'manage_options',
346
  __FILE__,array(&$this, 'options_page'));
 
 
 
347
  add_management_page('View Broken Links', 'Broken Links', 'manage_options',
348
  __FILE__,array(&$this, 'broken_links_page'));
349
  }
350
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
351
  function mytruncate($str, $max_length=50){
352
  if(strlen($str)<=$max_length) return $str;
353
  return (substr($str, 0, $max_length-3).'...');
@@ -391,10 +607,15 @@ class ws_broken_link_checker {
391
  echo $reminder;
392
  ?>
393
  <div class="wrap"><h2>Broken Link Checker Options</h2>
394
- <?php if(!function_exists('curl_init')){ ?>
 
 
 
395
  <strong>Error: <a href='http://curl.haxx.se/libcurl/php/'>CURL library</a>
396
  is not installed. This plugin won't work.</strong><br/>
397
- <?php }; ?>
 
 
398
  <form name="link_checker_options" method="post" action="<?php echo $_SERVER['PHP_SELF']; ?>?page=<?php echo plugin_basename(__FILE__); ?>&amp;updated=true">
399
  <p class="submit"><input type="submit" name="Submit" value="Update Options &raquo;" /></p>
400
 
@@ -535,7 +756,7 @@ class ws_broken_link_checker {
535
  $rownumber++;
536
  echo "<tr id='link-$link->id' class='alternate'>
537
  <th scope='row' style='text-align: center'>$rownumber</th>
538
- <td>$link->post_title</td>
539
 
540
  <td>$link->link_text</td>
541
  <td>
@@ -546,7 +767,7 @@ class ws_broken_link_checker {
546
  <input type='text' size='50' id='link-editor-$link->id' value='$link->url'
547
  class='link-editor' style='display:none' />
548
  </td>
549
- <td><a href='".(get_permalink($link->post_id))."' class='edit'>View</a></td>
550
 
551
  <td><a href='post.php?action=edit&amp;post=$link->post_id' class='edit'>Edit Post</a></td>";
552
 
@@ -558,12 +779,21 @@ class ws_broken_link_checker {
558
  }
559
 
560
  echo "<td><a href='javascript:void(0);' class='delete' id='discard_button-$link->id'
561
- onclick='discardLinkMessage($link->id);return false;' );' title='Discard This Message'>Discard</a></td>
562
 
563
  <td><a href='javascript:void(0);' class='delete' id='unlink_button-$link->id'
564
- onclick='removeLinkFromPost($link->id);return false;' );' title='Remove the link from the post'>Unlink</a></td>
565
  </tr>";
566
 
 
 
 
 
 
 
 
 
 
567
  }
568
 
569
  echo '</tbody></table>';
@@ -594,6 +824,7 @@ class ws_broken_link_checker {
594
  var response = transport.responseText || "";
595
  if (re.test(response)){
596
  $('link-'+link_id).hide();
 
597
  alterLinkCounter(-1);
598
  } else {
599
  $('discard_button-'+link_id).innerHTML = 'Discard';
@@ -618,6 +849,7 @@ class ws_broken_link_checker {
618
  var response = transport.responseText || "";
619
  if (re.test(response)){
620
  $('link-'+link_id).hide();
 
621
  alterLinkCounter(-1);
622
  } else {
623
  $('unlink_button-'+link_id).innerHTML = 'Unlink';
@@ -650,6 +882,7 @@ class ws_broken_link_checker {
650
  var response = transport.responseText || "";
651
  if (re.test(response)){
652
  $('link-'+link_id).hide();
 
653
  alterLinkCounter(-1);
654
  //alert(response);
655
  } else {
@@ -663,6 +896,11 @@ class ws_broken_link_checker {
663
  $('link-editor-button-'+link_id).innerHTML = 'Edit';
664
  }
665
  }
 
 
 
 
 
666
  </script>
667
  </div>
668
  <?php
3
  Plugin Name: Broken Link Checker
4
  Plugin URI: http://w-shadow.com/blog/2007/08/05/broken-link-checker-for-wordpress/
5
  Description: Checks your posts for broken links and missing images and notifies you on the dashboard if any are found.
6
+ Version: 0.4.8
7
  Author: Janis Elsts
8
  Author URI: http://w-shadow.com/blog/
9
  */
13
  MySQL 4.0 compatibility by Jeroen (www.yukka.eu)
14
  */
15
 
16
+ //The plugin will use Snoopy in case CURL is not available
17
+ if (!class_exists('Snoopy')) require_once(ABSPATH.'/wp-includes/class-snoopy.php');
18
+
19
  if (!class_exists('ws_broken_link_checker')) {
20
 
21
  class ws_broken_link_checker {
23
  var $options_name='wsblc_options';
24
  var $postdata_name;
25
  var $linkdata_name;
26
+ var $version='0.4.8';
27
  var $myfile='';
28
  var $myfolder='';
29
  var $mybasename='';
128
  }
129
  }
130
 
131
+ /**
132
+ * ws_broken_link_checker::normalize_url()
133
+ *
134
+ *
135
+ * @param string $url
136
+ * @return string or FALSE for invalid/unsupported URLs
137
+ */
138
  function normalize_url($url){
139
  $parts=@parse_url($url);
140
  if(!$parts) return false;
141
 
142
+ if(isset($parts['scheme'])) {
143
+ //Only HTTP(S) links are checked. Other protocols are not supported.
144
+ if ( ($parts['scheme'] != 'http') && ($parts['scheme'] != 'https') )
145
+ return false;
146
+ }
147
+
148
  $url = html_entity_decode($url);
149
+ $url = preg_replace(
150
  array('/([\?&]PHPSESSID=\w+)$/i',
151
  '/(#[^\/]*)$/',
152
  '/&amp;/',
157
  $url);
158
  $url=trim($url);
159
 
 
160
  if($url=='') return false;
161
 
162
  // turn relative URLs into absolute URLs
219
  return $url;
220
  }
221
 
222
+ function page_exists($url){
223
+ //echo "Checking $url...<br/>";
224
+ $result = array('final_url'=>$url, 'log'=>'', 'http_code'=>'', 'okay' => false);
225
+
226
+ $parts=parse_url($url);
227
+ if(!$parts) {
228
+ $result['log'] .= "Invalid link URL (doesn't parse).";
229
+ return $result;
230
+ }
231
+
232
+ if(!isset($parts['scheme'])) {
233
+ $url='http://'.$url;
234
+ $parts['scheme'] = 'http';
235
+ $result['log'] .= "Protocol not specified, assuming HTTP.\n";
236
+ }
237
+
238
+ //Only HTTP links are checked. All others are automatically considered okay.
239
+ if ( ($parts['scheme'] != 'http') && ($parts['scheme'] != 'https') ) {
240
+ $result['log'] .= "URL protocol ($parts[scheme]) is not HTTP. This link won't be checked.\n";
241
+ return $result;
242
+ }
243
+
244
+ //******* Use CURL if available ***********
245
+ if (function_exists('curl_init')) {
246
+
247
+ $ch = curl_init();
248
+ curl_setopt($ch, CURLOPT_URL, $url);
249
+ curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)');
250
+ //curl_setopt($ch, CURLOPT_USERAGENT, 'WordPress/Broken Link Checker (bot)');
251
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);
252
+
253
+ @curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
254
+ curl_setopt($ch, CURLOPT_MAXREDIRS, 10);
255
+
256
+ curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 20);
257
+ curl_setopt($ch, CURLOPT_TIMEOUT, 30);
258
+
259
+ curl_setopt($ch, CURLOPT_FAILONERROR, false);
260
+
261
+ $nobody=false;
262
+ if($parts['scheme']=='https'){
263
+ curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
264
+ curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
265
+ } else {
266
+ $nobody=true;
267
+ curl_setopt($ch, CURLOPT_NOBODY, true);
268
+ //curl_setopt($ch, CURLOPT_RANGE, '0-1023');
269
+ }
270
+ curl_setopt($ch, CURLOPT_HEADER, true);
271
+
272
+ $response = curl_exec($ch);
273
+ //echo 'Response 1 : <pre>',$response,'</pre>';
274
+ $code=intval(curl_getinfo($ch, CURLINFO_HTTP_CODE));
275
+ //echo "Code 1 : $code<br/>";
276
+
277
+ $header = '';
278
+ if (preg_match('/(.+?)\r\n\r\n/s', $response, $matches)){
279
+ $header = $matches[1];
280
+ }
281
+
282
+ $result['log'] .= "=== First try : ".($response?"$code ===\n$header":"No response. ===")."\n\n";
283
+
284
+ if ( (($code<200) || ($code>=400)) && $nobody) {
285
+ $result['log'] .= "Trying a second time with different settings...\n";
286
+ curl_setopt($ch, CURLOPT_NOBODY, false);
287
+ curl_setopt($ch, CURLOPT_HTTPGET, true);
288
+ curl_setopt($ch, CURLOPT_RANGE, '0-2047');
289
+ $response = curl_exec($ch);
290
+ //echo 'Response 2 : <pre>',$response,'</pre>';
291
+ $code=intval(curl_getinfo($ch, CURLINFO_HTTP_CODE));
292
+ //echo "Code 2 : $code<br/>";
293
+ if (preg_match('/(.+?)\r\n\r\n/s', $response, $matches)){
294
+ $header = $matches[1];
295
+ }
296
+
297
+ $result['log'] .= "=== Second try : ".($response?"$code ===\n$header":"No response. ===")."\n\n";
298
+ }
299
+
300
+ $result['final_url'] = curl_getinfo($ch, CURLINFO_EFFECTIVE_URL);
301
+
302
+ curl_close($ch);
303
+
304
+ } elseif (class_exists('Snoopy')) {
305
+ //******** Use Snoopy if CURL is not available *********
306
+ //Note : Snoopy doesn't work too well with HTTPS URLs.
307
+ $result['log'] .= "<em>(Using Snoopy)</em>\n";
308
+
309
+ $snoopy = new Snoopy;
310
+ $snoopy->read_timeout = 60; //read timeout in seconds
311
+ $snoopy->fetch($url);
312
+
313
+ $code = $snoopy->status; //HTTP status code
314
+
315
+ if ($snoopy->error)
316
+ $result['log'] .= $snoopy->error."\n";
317
+ if ($snoopy->timed_out)
318
+ $result['log'] .= "Request timed out.\n";
319
+
320
+ $result['log'] .= join("", $snoopy->headers)."\n"; //those headers already contain newlines
321
+
322
+ //$result['log'] .= print_r($snoopy, true);
323
+
324
+ if ($snoopy->lastredirectaddr)
325
+ $result['final_url'] = $snoopy->lastredirectaddr;
326
+ }
327
+
328
+ $result['http_code'] = $code;
329
+
330
+ /*"Good" response codes are anything in the 2XX range (e.g "200 OK") and redirects - the 3XX range.
331
+ HTTP 401 Unauthorized is a special case that is considered OK as well. Other errors - the 4XX range -
332
+ are treated as "page doesn't exist'". */
333
+ $result['okay'] = (($code>=200) && ($code<400)) || ($code == 401);
334
+ $result['log'] .= "Link is ".($result['okay']?'valid':'broken').".";
335
+
336
+ return $result;
337
+ }
338
+
339
+ /**
340
+ * ws_broken_link_checker::check_link()
341
+ * Checks the link described by the object $link and udpates the DB accordingly.
342
+ *
343
+ * @param object $link
344
+ * @return boolean
345
+ */
346
+ function check_link($link){
347
+ global $wpdb;
348
+
349
+ /*
350
+ Check for problematic (though not necessarily "broken") links.
351
+ If a link has been checked multiple times and still hasn't been marked as broken
352
+ or removed from the queue then probably the checking algorithm is having problems
353
+ with that link. Mark it as broken and hope the user sorts it out.
354
+ */
355
+ if ($link->check_count >=5){
356
+ $wpdb->query("UPDATE {$this->linkdata_name}
357
+ SET broken=1, log=CONCAT(log, '\nProblematic link, checking times out.')
358
+ WHERE id={$link->id}");
359
+ //can afford to skip the $max_execution_time check here, the above op. should be very fast.
360
+ return false;
361
+ }
362
+
363
+ //Update the check_count & last_check fields before actually performing the check.
364
+ //Useful if something goes terribly wrong in page_exists() with this particular URL.
365
+ $wpdb->query("UPDATE $this->linkdata_name SET last_check=NOW(), check_count=check_count+1
366
+ WHERE id=$link->id");
367
+
368
+ //Verify that the link should be checked
369
+ if ( !$this->is_excluded($link->url) ){
370
+ $rez = $this->page_exists($link->url);
371
+
372
+ if ( $rez['okay'] ) {
373
+ //Link is fine; remove it from the queue.
374
+ $wpdb->query("DELETE FROM $this->linkdata_name WHERE id=$link->id");
375
+ return true;
376
+ } else {
377
+ //Link is broken.
378
+ $wpdb->query(
379
+ "UPDATE $this->linkdata_name
380
+ SET broken=1, http_code=$rez[http_code], log='".$wpdb->escape($rez['log'])."',
381
+ final_url = '".$wpdb->escape($rez['final_url'])."'
382
+ WHERE id=$link->id"
383
+ );
384
+
385
+ return false;
386
+ }
387
+
388
+ } else {
389
+ //This link doesn't need to be checked because it is excluded.
390
+ $wpdb->query("DELETE FROM $this->linkdata_name WHERE id=$link->id");
391
+ return true;
392
+ }
393
+
394
+ }
395
+
396
  function is_excluded($url){
397
  if (!is_array($this->options['exclusion_list'])) return false;
398
  foreach($this->options['exclusion_list'] as $excluded_word){
495
  function activation(){
496
  global $wpdb;
497
 
 
 
 
498
  require_once(ABSPATH . 'wp-admin/upgrade-functions.php');
499
 
500
  if (($wpdb->get_var("show tables like '".($this->postdata_name)."'") != $this->postdata_name)
514
  id BIGINT( 20 ) UNSIGNED NOT NULL AUTO_INCREMENT ,
515
  post_id BIGINT( 20 ) NOT NULL ,
516
  url TEXT NOT NULL ,
517
+ final_url TEXT NOT NULL,
518
  link_text VARCHAR( 50 ) NOT NULL ,
519
  broken TINYINT( 1 ) UNSIGNED DEFAULT '0' NOT NULL,
520
  last_check DATETIME NOT NULL ,
521
+ check_count TINYINT( 2 ) UNSIGNED DEFAULT '0' NOT NULL,
522
+ type ENUM('link', 'image') DEFAULT 'link' NOT NULL,
523
+ log TEXT NOT NULL,
524
+ http_code SMALLINT NOT NULL,
525
  PRIMARY KEY id (id)
526
  );";
527
 
528
  dbDelta($sql);
529
+
530
+ //upgrade to 0.4.8
531
+ $wpdb->query("UPDATE ".$this->linkdata_name." SET type='image' WHERE link_text='[image]'");
532
+ $wpdb->query("UPDATE ".$this->linkdata_name." SET final_url=url WHERE final_url=''");
533
  }
534
 
535
  $this->sync_posts_to_db();
536
+
537
+ //Update the options
538
+ $this->options['version'] = $this->version;
539
+ update_option($this->options_name,$this->options);
540
  }
541
 
542
  function options_menu(){
543
  add_options_page('Link Checker Settings', 'Link Checker', 'manage_options',
544
  __FILE__,array(&$this, 'options_page'));
545
+ if (current_user_can('manage_options'))
546
+ add_filter('plugin_action_links', array(&$this, 'plugin_action_links'), 10, 2);
547
+
548
  add_management_page('View Broken Links', 'Broken Links', 'manage_options',
549
  __FILE__,array(&$this, 'broken_links_page'));
550
  }
551
 
552
+ /**
553
+ * plugin_action_links()
554
+ * Handler for the 'plugin_action_links' hook. Adds a "Settings" link to this plugin's entry
555
+ * on the plugin list.
556
+ *
557
+ * @param array $links
558
+ * @param string $file
559
+ * @return array
560
+ */
561
+ function plugin_action_links($links, $file) {
562
+ if ($file == $this->mybasename)
563
+ $links[] = "<a href='options-general.php?page=broken-link-checker/broken-link-checker.php'>" . __('Settings') . "</a>";
564
+ return $links;
565
+ }
566
+
567
  function mytruncate($str, $max_length=50){
568
  if(strlen($str)<=$max_length) return $str;
569
  return (substr($str, 0, $max_length-3).'...');
607
  echo $reminder;
608
  ?>
609
  <div class="wrap"><h2>Broken Link Checker Options</h2>
610
+ <?php
611
+ //This check isn't required anymore. Can now use Snoopy when CURL is not available.
612
+ /*
613
+ if(!function_exists('curl_init')){ ?>
614
  <strong>Error: <a href='http://curl.haxx.se/libcurl/php/'>CURL library</a>
615
  is not installed. This plugin won't work.</strong><br/>
616
+ <?php };
617
+ */
618
+ ?>
619
  <form name="link_checker_options" method="post" action="<?php echo $_SERVER['PHP_SELF']; ?>?page=<?php echo plugin_basename(__FILE__); ?>&amp;updated=true">
620
  <p class="submit"><input type="submit" name="Submit" value="Update Options &raquo;" /></p>
621
 
756
  $rownumber++;
757
  echo "<tr id='link-$link->id' class='alternate'>
758
  <th scope='row' style='text-align: center'>$rownumber</th>
759
+ <td><a href='".(get_permalink($link->post_id))."' title='View post'>$link->post_title</a></td>
760
 
761
  <td>$link->link_text</td>
762
  <td>
767
  <input type='text' size='50' id='link-editor-$link->id' value='$link->url'
768
  class='link-editor' style='display:none' />
769
  </td>
770
+ <td><a href='javascript:void(0);' onclick='toggleLinkDetails($link->id);return false;' class='edit'>Details</a></td>
771
 
772
  <td><a href='post.php?action=edit&amp;post=$link->post_id' class='edit'>Edit Post</a></td>";
773
 
779
  }
780
 
781
  echo "<td><a href='javascript:void(0);' class='delete' id='discard_button-$link->id'
782
+ onclick='discardLinkMessage($link->id);return false;' title='Discard This Message'>Discard</a></td>
783
 
784
  <td><a href='javascript:void(0);' class='delete' id='unlink_button-$link->id'
785
+ onclick='removeLinkFromPost($link->id);return false;' title='Remove the link from the post'>Unlink</a></td>
786
  </tr>";
787
 
788
+ //Link details
789
+ echo "
790
+ <tr id='link-details-$link->id' style='display:none;'><td colspan='8'>
791
+ <strong>Last checked :</strong> $link->last_check <br />
792
+ <strong>Final URL : </strong> $link->final_url <br />
793
+ <strong>HTTP code : </strong> $link->http_code<br />
794
+ <strong>Log : <br /> </strong> ".nl2br($link->log)."<br />
795
+ </td></tr>";
796
+
797
  }
798
 
799
  echo '</tbody></table>';
824
  var response = transport.responseText || "";
825
  if (re.test(response)){
826
  $('link-'+link_id).hide();
827
+ $('link-details-'+link_id).hide();
828
  alterLinkCounter(-1);
829
  } else {
830
  $('discard_button-'+link_id).innerHTML = 'Discard';
849
  var response = transport.responseText || "";
850
  if (re.test(response)){
851
  $('link-'+link_id).hide();
852
+ $('link-details-'+link_id).hide();
853
  alterLinkCounter(-1);
854
  } else {
855
  $('unlink_button-'+link_id).innerHTML = 'Unlink';
882
  var response = transport.responseText || "";
883
  if (re.test(response)){
884
  $('link-'+link_id).hide();
885
+ $('link-details-'+link_id).hide();
886
  alterLinkCounter(-1);
887
  //alert(response);
888
  } else {
896
  $('link-editor-button-'+link_id).innerHTML = 'Edit';
897
  }
898
  }
899
+
900
+ function toggleLinkDetails(link_id){
901
+ //alert('showlinkdetails '+link_id);
902
+ $('link-details-'+link_id).toggle();
903
+ }
904
  </script>
905
  </div>
906
  <?php
readme.txt CHANGED
@@ -2,8 +2,8 @@
2
  Contributors: whiteshadow
3
  Tags: links, broken, maintenance
4
  Requires at least: 2.0.2
5
- Tested up to: 2.6.1
6
- Stable tag: 0.4.7
7
 
8
  This plugin will check your posts for broken links and missing images in background and notify you on the dashboard if any are found.
9
 
@@ -16,12 +16,12 @@ This plugin is will monitor your blog looking for broken links and let you know
16
  * Makes broken links display differently in posts (optional).
17
  * Link checking intervals can be configured.
18
  * New/modified posts are checked ASAP.
19
- * You can unlink or edit broken links in the *Manage -> Broken Links* tab (experimental).
20
 
21
  **How To Use It**
22
  The broken links, if any are found, will show up in a new tab of WP admin panel - Manage -> Broken Links. A notification will also appear on the Dashboard.
23
 
24
- There are several buttons for each broken link - "View" and "Edit Post" do exactly what they say and "Discard" will remove the message about a broken link, but not the link itself (so it will show up again later unless you fix it). Use "Unlink" to actually remove the link from the post. If references to missing images are found, they will be listed along with the links, with "[image]" in place of link text.
25
 
26
  You can modify the available options at Options -> Link Checker. You can also see the current checking status there - e.g. how many posts need to be checked and how many links are in the queue. The plugin runs while you have any page of the WordPress admin panel open.
27
 
2
  Contributors: whiteshadow
3
  Tags: links, broken, maintenance
4
  Requires at least: 2.0.2
5
+ Tested up to: 2.6.2
6
+ Stable tag: 0.4.8
7
 
8
  This plugin will check your posts for broken links and missing images in background and notify you on the dashboard if any are found.
9
 
16
  * Makes broken links display differently in posts (optional).
17
  * Link checking intervals can be configured.
18
  * New/modified posts are checked ASAP.
19
+ * You can unlink or edit broken links in the *Manage -> Broken Links* tab.
20
 
21
  **How To Use It**
22
  The broken links, if any are found, will show up in a new tab of WP admin panel - Manage -> Broken Links. A notification will also appear on the Dashboard.
23
 
24
+ There are several buttons for each broken link - "Details" shows more info about why the link is considered "broken", "Edit Post" does exactly what it says and "Discard" will remove the message about a broken link, but not the link itself (so it will show up again later unless you fix it). Use "Unlink" to actually remove the link from the post. If references to missing images are found, they will be listed along with the links, with "[image]" in place of link text.
25
 
26
  You can modify the available options at Options -> Link Checker. You can also see the current checking status there - e.g. how many posts need to be checked and how many links are in the queue. The plugin runs while you have any page of the WordPress admin panel open.
27
 
wsblc_ajax.php CHANGED
@@ -25,7 +25,12 @@
25
  };
26
 
27
  //Regexp for HTML links
28
- $url_pattern='/<a[\s]+[^>]*href\s*=\s*([\"\']+)([^\'\" >]+)\1[^<>]*>((?sU).*)<\/a>/i';
 
 
 
 
 
29
  $url_to_replace = '';
30
 
31
  $postdata_name=$wpdb->prefix . "blc_postdata";
@@ -129,29 +134,7 @@
129
  //some unchecked links found
130
  echo "<!-- checking ".count($links)." links (rand : ".rand(1,1000).") -->";
131
  foreach ($links as $link) {
132
- /*
133
- Check for problematic (though not necessarily "broken") links.
134
- If a link has been checked multiple times and still hasn't been marked as broken
135
- or removed from the queue then probably the checking algorithm is having problems
136
- with that link. Mark it as broken and hope the user sorts it out.
137
- */
138
- if ($link->check_count >=5){
139
- $wpdb->query("UPDATE $linkdata_name SET broken=1 WHERE id=$link->id");
140
- //can afford to skip the $max_execution_time check here, the above op. should be very fast.
141
- continue;
142
- }
143
-
144
- //Update the check_count & last_check fields before actually performing the check.
145
- //Useful if something goes terribly wrong in page_exists_simple() with this particular URL.
146
- $wpdb->query("UPDATE $linkdata_name SET last_check=NOW(), check_count=check_count+1
147
- WHERE id=$link->id");
148
-
149
- if( $ws_link_checker->is_excluded($link->url) || page_exists_simple($link->url) ){
150
- //link OK, remove from queue
151
- $wpdb->query("DELETE FROM $linkdata_name WHERE id=$link->id");
152
- } else {
153
- $wpdb->query("UPDATE $linkdata_name SET broken=1 WHERE id=$link->id");
154
- };
155
 
156
  if(execution_time()>$max_execution_time){
157
  die('<!-- url loop timeout -->');
@@ -190,7 +173,13 @@
190
  die('Error: Post not found');
191
  }
192
 
193
- $new_content = unlink_the_link($the_post['post_content'], $the_link->url);
 
 
 
 
 
 
194
  $new_content = $wpdb->escape($new_content);
195
  $wpdb->query("UPDATE $wpdb->posts SET post_content = '$new_content' WHERE id = $the_link->post_id");
196
  if($wpdb->rows_affected<1){
@@ -222,7 +211,12 @@
222
  die('Error: Post not found');
223
  }
224
 
225
- $new_content = edit_the_link($the_post['post_content'], $the_link->url, $new_url);
 
 
 
 
 
226
  if (function_exists('mysql_real_escape_string')){
227
  $new_content = mysql_real_escape_string($new_content);
228
  } else {
@@ -245,16 +239,18 @@
245
  function parse_link($matches, $post_id){
246
  global $wpdb, $linkdata_name, $ws_link_checker;
247
 
248
- $url = $matches[2];
249
- $text = $matches[3];
250
 
251
  $url = $ws_link_checker->normalize_url($url);
252
  if (!$url) return false;
253
 
254
  if(strlen($url)>5){
255
  $wpdb->query(
256
- "INSERT INTO $linkdata_name(post_id, url, link_text)
257
- VALUES($post_id, '".$wpdb->escape($url)."', '".$wpdb->escape(strip_tags($text))."')"
 
 
258
  );
259
  };
260
 
@@ -264,14 +260,14 @@
264
  function parse_image($matches, $post_id){
265
  global $wpdb, $linkdata_name, $ws_link_checker;
266
 
267
- $url=$matches[2];
268
  $url = $ws_link_checker->normalize_url($url);
269
  if(!$url) return false;
270
 
271
  if(strlen($url)>3){
272
  $wpdb->query(
273
- "INSERT INTO $linkdata_name(post_id, url, link_text)
274
- VALUES($post_id, '".$wpdb->escape($url)."', '[image]')"
275
  );
276
  };
277
 
@@ -280,20 +276,21 @@
280
 
281
  function gather_and_save_links($content, $post_id){
282
  //gather links (<a href=...>)
283
- global $url_pattern;
284
 
285
  //remove all <code></code> blocks first
286
  $content = preg_replace('/<code>.+?<\/code>/i', ' ', $content);
287
 
 
 
288
  if(preg_match_all($url_pattern, $content, $matches, PREG_SET_ORDER)){
289
  foreach($matches as $link){
 
290
  parse_link($link, $post_id);
291
  }
292
  };
293
 
294
  //gather images (<img src=...>)
295
- $img_pattern='/(<img[\s]+[^>]*src\s*=\s*[\"\']?)([^\'\" >]+)([\'\"]+[^<>]*>)/i';
296
-
297
  if(preg_match_all($img_pattern, $content, $matches, PREG_SET_ORDER)){
298
  foreach($matches as $img){
299
  parse_image($img, $post_id);
@@ -305,6 +302,7 @@
305
 
306
  function page_exists_simple($url){
307
  //echo "Checking $url...<br/>";
 
308
  $parts=parse_url($url);
309
  if(!$parts) return false;
310
 
@@ -365,18 +363,37 @@
365
  return (($code>=200) && ($code<400)) || ($code == 401);
366
  }
367
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
368
  function unlink_the_link($content, $url){
369
  global $url_pattern, $url_to_replace;
370
  $url_to_replace = $url;
371
- $url_pattern='/(<a[\s]+[^>]*href\s*=\s*[\"\']?)([^\'\" >]+)([\'\"]+[^<>]*>)((?sU).*)(<\/a>)/i';
372
  $content = preg_replace_callback($url_pattern, unlink_link_callback, $content);
373
  return $content;
374
  }
375
 
376
  function unlink_link_callback($matches){
377
  global $url_to_replace, $ws_link_checker;
378
- $url = $ws_link_checker->normalize_url($matches[2]);
379
- $text = $matches[4];
380
 
381
  //echo "$url || $url_to_replace\n";
382
  if ($url == $url_to_replace){
@@ -389,23 +406,44 @@
389
  }
390
 
391
  function edit_the_link($content, $url, $newurl){
392
- global /*$url_pattern, */$url_to_replace, $new_url;
393
  $url_to_replace = $url;
394
  $new_url = $newurl;
395
- $url_pattern='/(<a[\s]+[^>]*href\s*=\s*[\"\']?)([^\'\" >]+)([\'\"]+[^<>]*>)((?sU).*)(<\/a>)/i';
396
  $content = preg_replace_callback($url_pattern, edit_link_callback, $content);
397
  return $content;
398
  }
399
 
400
  function edit_link_callback($matches){
401
  global $url_to_replace, $new_url, $ws_link_checker;
402
- $url = $ws_link_checker->normalize_url($matches[2]);
403
- $text = $matches[4];
404
 
405
  //echo "$url || $url_to_replace\n";
406
  if ($url == $url_to_replace){
407
  //return $text;
408
- return $matches[1].$new_url.$matches[3].$text.$matches[5];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
409
  } else {
410
  return $matches[0];
411
  }
25
  };
26
 
27
  //Regexp for HTML links
28
+ // \1 \2 \3 \4 \5 \6
29
+ $url_pattern='/(<a[\s]+[^>]*href\s*=\s*)([\"\']+)([^\'\">]+)\2([^<>]*>)((?sU).*)(<\/a>)/i';
30
+ //Regexp for IMG tags
31
+ // \1 \2 \3 \4
32
+ $img_pattern='/(<img[\s]+[^>]*src\s*=\s*)([\"\']?)([^\'\">]+)\2([^<>]*>)/i';
33
+
34
  $url_to_replace = '';
35
 
36
  $postdata_name=$wpdb->prefix . "blc_postdata";
134
  //some unchecked links found
135
  echo "<!-- checking ".count($links)." links (rand : ".rand(1,1000).") -->";
136
  foreach ($links as $link) {
137
+ $ws_link_checker->check_link($link);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
138
 
139
  if(execution_time()>$max_execution_time){
140
  die('<!-- url loop timeout -->');
173
  die('Error: Post not found');
174
  }
175
 
176
+ //Remove a link or an image from the post HTML
177
+ if ($the_link->type == 'link')
178
+ $new_content = unlink_the_link($the_post['post_content'], $the_link->url);
179
+ elseif ($the_link->type == 'image')
180
+ $new_content = unlink_image($the_post['post_content'], $the_link->url);
181
+
182
+ //Update database
183
  $new_content = $wpdb->escape($new_content);
184
  $wpdb->query("UPDATE $wpdb->posts SET post_content = '$new_content' WHERE id = $the_link->post_id");
185
  if($wpdb->rows_affected<1){
211
  die('Error: Post not found');
212
  }
213
 
214
+ if ($the_link->type == 'link')
215
+ $new_content = edit_the_link($the_post['post_content'], $the_link->url, $new_url);
216
+ elseif ($the_link->type == 'image')
217
+ $new_content = edit_image($the_post['post_content'], $the_link->url, $new_url);
218
+
219
+
220
  if (function_exists('mysql_real_escape_string')){
221
  $new_content = mysql_real_escape_string($new_content);
222
  } else {
239
  function parse_link($matches, $post_id){
240
  global $wpdb, $linkdata_name, $ws_link_checker;
241
 
242
+ $url = $matches[3];
243
+ $text = $matches[5];
244
 
245
  $url = $ws_link_checker->normalize_url($url);
246
  if (!$url) return false;
247
 
248
  if(strlen($url)>5){
249
  $wpdb->query(
250
+ "INSERT INTO $linkdata_name(post_id, url, link_text, type, final_url)
251
+ VALUES($post_id, '".$wpdb->escape($url)."',
252
+ '".$wpdb->escape(strip_tags($text))."', 'link',
253
+ '".$wpdb->escape($url)."')"
254
  );
255
  };
256
 
260
  function parse_image($matches, $post_id){
261
  global $wpdb, $linkdata_name, $ws_link_checker;
262
 
263
+ $url=$matches[3];
264
  $url = $ws_link_checker->normalize_url($url);
265
  if(!$url) return false;
266
 
267
  if(strlen($url)>3){
268
  $wpdb->query(
269
+ "INSERT INTO $linkdata_name(post_id, url, link_text, type, final_url)
270
+ VALUES($post_id, '".$wpdb->escape($url)."', '[image]', 'image','".$wpdb->escape($url)."')"
271
  );
272
  };
273
 
276
 
277
  function gather_and_save_links($content, $post_id){
278
  //gather links (<a href=...>)
279
+ global $url_pattern, $img_pattern;
280
 
281
  //remove all <code></code> blocks first
282
  $content = preg_replace('/<code>.+?<\/code>/i', ' ', $content);
283
 
284
+ //echo "Analyzing post $post_id<br>Content = ".htmlspecialchars($content)."<br>";
285
+
286
  if(preg_match_all($url_pattern, $content, $matches, PREG_SET_ORDER)){
287
  foreach($matches as $link){
288
+ //echo "Found link : ".print_r($link,true)."<br>";
289
  parse_link($link, $post_id);
290
  }
291
  };
292
 
293
  //gather images (<img src=...>)
 
 
294
  if(preg_match_all($img_pattern, $content, $matches, PREG_SET_ORDER)){
295
  foreach($matches as $img){
296
  parse_image($img, $post_id);
302
 
303
  function page_exists_simple($url){
304
  //echo "Checking $url...<br/>";
305
+
306
  $parts=parse_url($url);
307
  if(!$parts) return false;
308
 
363
  return (($code>=200) && ($code<400)) || ($code == 401);
364
  }
365
 
366
+ function unlink_image($content, $url){
367
+ global $img_pattern, $url_to_replace;
368
+ $url_to_replace = $url;
369
+ //$url_pattern='/(<a[\s]+[^>]*href\s*=\s*[\"\']?)([^\'\">]+)([\'\"]+[^<>]*>)((?sU).*)(<\/a>)/i';
370
+ $content = preg_replace_callback($img_pattern, unlink_image_callback, $content);
371
+ return $content;
372
+ }
373
+
374
+ function unlink_image_callback($matches){
375
+ global $url_to_replace, $ws_link_checker;
376
+ $url = $ws_link_checker->normalize_url($matches[3]);
377
+
378
+ if ($url == $url_to_replace){
379
+ return ''; //completely remove the IMG tag
380
+ } else {
381
+ return $matches[0];
382
+ }
383
+ }
384
+
385
  function unlink_the_link($content, $url){
386
  global $url_pattern, $url_to_replace;
387
  $url_to_replace = $url;
388
+ //$url_pattern='/(<a[\s]+[^>]*href\s*=\s*[\"\']?)([^\'\">]+)([\'\"]+[^<>]*>)((?sU).*)(<\/a>)/i';
389
  $content = preg_replace_callback($url_pattern, unlink_link_callback, $content);
390
  return $content;
391
  }
392
 
393
  function unlink_link_callback($matches){
394
  global $url_to_replace, $ws_link_checker;
395
+ $url = $ws_link_checker->normalize_url($matches[3]);
396
+ $text = $matches[5];
397
 
398
  //echo "$url || $url_to_replace\n";
399
  if ($url == $url_to_replace){
406
  }
407
 
408
  function edit_the_link($content, $url, $newurl){
409
+ global $url_pattern, $url_to_replace, $new_url;
410
  $url_to_replace = $url;
411
  $new_url = $newurl;
412
+ //$url_pattern='/(<a[\s]+[^>]*href\s*=\s*[\"\']?)([^\'\" >]+)\2([^<>]*>)((?sU).*)(<\/a>)/i';
413
  $content = preg_replace_callback($url_pattern, edit_link_callback, $content);
414
  return $content;
415
  }
416
 
417
  function edit_link_callback($matches){
418
  global $url_to_replace, $new_url, $ws_link_checker;
419
+ $url = $ws_link_checker->normalize_url($matches[3]);
420
+ $text = $matches[5];
421
 
422
  //echo "$url || $url_to_replace\n";
423
  if ($url == $url_to_replace){
424
  //return $text;
425
+ // \<a.. \",' \",' \...> \</a>
426
+ return $matches[1].$matches[2].$new_url.$matches[2].$matches[4].$text.$matches[6];
427
+ } else {
428
+ return $matches[0];
429
+ }
430
+ }
431
+
432
+ function edit_image($content, $url, $newurl){
433
+ global $img_pattern, $url_to_replace, $new_url;
434
+ $url_to_replace = $url;
435
+ $new_url = $newurl;
436
+ $content = preg_replace_callback($img_pattern, edit_link_callback, $content);
437
+ return $content;
438
+ }
439
+
440
+ function edit_image_callback($matches){
441
+ global $url_to_replace, $new_url, $ws_link_checker;
442
+ $url = $ws_link_checker->normalize_url($matches[3]);
443
+
444
+ if ($url == $url_to_replace){
445
+ // \<img... \",' \url \",' \...>
446
+ return $matches[1].$matches[2].$new_url.$matches[3].$matches[4];
447
  } else {
448
  return $matches[0];
449
  }