Broken Link Checker - Version 0.4.9

Version Description

Download this release

Release Info

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

Code changes from version 0.4.8 to 0.4.9

Files changed (2) hide show
  1. broken-link-checker.php +996 -817
  2. readme.txt +1 -1
broken-link-checker.php CHANGED
@@ -3,191 +3,191 @@
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
  */
10
 
11
  /*
12
- Created by Janis Elsts (email : whiteshadow@w-shadow.com)
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 {
22
- var $options;
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='';
30
- var $siteurl;
31
- var $defaults;
32
-
33
- function ws_broken_link_checker() {
34
- global $wpdb;
35
-
36
- //set default options
37
- $this->defaults = array(
38
- 'version' => $this->version,
39
- 'max_work_session' => 27,
40
- 'check_treshold' => 72,
41
- 'mark_broken_links' => true,
42
- 'broken_link_css' => ".broken_link, a.broken_link {\n\ttext-decoration: line-through;\n}",
43
- 'exclusion_list' => array(),
44
- 'delete_post_button' => false
45
- );
46
- //load options
47
- $this->options=get_option($this->options_name);
48
- if(!is_array($this->options)){
49
- $this->options = $this->defaults;
50
- } else {
51
- $this->options = array_merge($this->defaults, $this->options);
52
- }
53
-
54
- $this->postdata_name=$wpdb->prefix . "blc_postdata";
55
- $this->linkdata_name=$wpdb->prefix . "blc_linkdata";
56
- $this->siteurl = get_option('siteurl');
57
-
58
- $my_file = str_replace('\\', '/',__FILE__);
59
- $my_file = preg_replace('/^.*wp-content[\\\\\/]plugins[\\\\\/]/', '', $my_file);
60
- add_action('activate_'.$my_file, array(&$this,'activation'));
61
- $this->myfile=$my_file;
62
- $this->myfolder=basename(dirname(__FILE__));
63
- $this->mybasename=plugin_basename(__FILE__);
64
-
65
- add_action('admin_menu', array(&$this,'options_menu'));
66
-
67
- add_action('delete_post', array(&$this,'post_deleted'));
68
- add_action('save_post', array(&$this,'post_saved'));
69
- add_action('admin_footer', array(&$this,'admin_footer'));
70
- add_action('admin_print_scripts', array(&$this,'admin_print_scripts'));
71
- add_action('activity_box_end', array(&$this,'activity_box'));
72
-
73
- if (!empty($this->options['mark_broken_links'])){
74
- add_filter('the_content', array(&$this,'the_content'));
75
- if (!empty($this->options['broken_link_css'])){
76
- add_action('wp_head', array(&$this,'header_css'));
77
- }
78
- }
79
- }
80
-
81
- function admin_footer(){
82
- ?>
83
- <!-- wsblc admin footer -->
84
- <div id='wsblc_updater_div'></div>
85
- <script type='text/javascript'>
86
- new Ajax.PeriodicalUpdater('wsblc_updater_div', '<?php
87
- echo get_option( "siteurl" ).'/wp-content/plugins/'.$this->myfolder.'/wsblc_ajax.php?action=run_check' ; ?>',
88
- {
89
- method: 'get',
90
- frequency: <?php echo ($this->options['max_work_session']-1); ?>,
91
- decay: 1.3
92
- });
93
- </script>
94
- <!-- /wsblc admin footer -->
95
- <?php
96
- }
97
-
98
- function header_css(){
99
- echo '<style type="text/css">',$this->options['broken_link_css'],'</style>';
100
- }
101
-
102
- function the_content($content){
103
- global $post, $wpdb;
104
- if (empty($post)) return $content;
105
-
106
- $sql="SELECT url from $this->linkdata_name WHERE post_id = $post->ID AND broken<>0";
107
- $rows=$wpdb->get_results($sql, ARRAY_A);
108
- if($rows && (count($rows)>0)){
109
- //some rows found
110
- $this->links_to_remove = array_map(
111
- create_function('$elem', 'return $elem["url"];'),
112
- $rows);
113
- $url_pattern='/(<a[\s]+[^>]*href\s*=\s*[\"\']?)([^\'\" >]+)([\'\"]+[^<>]*)(>)((?sU).*)(<\/a>)/i';
114
- $content = preg_replace_callback($url_pattern, array(&$this,'mark_broken_links'), $content);
115
- };
116
-
117
- //print_r($post);
118
- return $content;
119
- }
120
-
121
- function mark_broken_links($matches){
122
- $url = $this->normalize_url(html_entity_decode($matches[2])) ;
123
- if(in_array($url, $this->links_to_remove)){
124
- return $matches[1].$matches[2].$matches[3].' class="broken_link"'.$matches[4].
125
- $matches[5].$matches[6];
126
- } else {
127
- return $matches[0];
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;/',
153
- '/^(javascript:.*)/i',
154
- '/([\?&]sid=\w+)$/i'
155
- ),
156
- array('','','&','',''),
157
- $url);
158
- $url=trim($url);
159
-
160
- if($url=='') return false;
161
-
162
  // turn relative URLs into absolute URLs
163
  $url = $this->relative2absolute($this->siteurl, $url);
164
  return $url;
165
- }
166
-
167
- function relative2absolute($absolute, $relative) {
168
  $p = @parse_url($relative);
169
  if(!$p) {
170
- //WTF? $relative is a seriously malformed URL
171
- return false;
172
  }
173
  if(isset($p["scheme"])) return $relative;
174
-
175
  $parts=(parse_url($absolute));
176
-
177
  if(substr($relative,0,1)=='/') {
178
  $cparts = (explode("/", $relative));
179
  array_shift($cparts);
180
  } else {
181
- if(isset($parts['path'])){
182
- $aparts=explode('/',$parts['path']);
183
- array_pop($aparts);
184
- $aparts=array_filter($aparts);
185
- } else {
186
- $aparts=array();
187
- }
188
-
189
- $rparts = (explode("/", $relative));
190
-
191
  $cparts = array_merge($aparts, $rparts);
192
  foreach($cparts as $i => $part) {
193
  if($part == '.') {
@@ -199,7 +199,7 @@ class ws_broken_link_checker {
199
  }
200
  }
201
  $path = implode("/", $cparts);
202
-
203
  $url = '';
204
  if($parts['scheme']) {
205
  $url = "$parts[scheme]://";
@@ -215,127 +215,127 @@ class ws_broken_link_checker {
215
  $url .= $parts['host']."/";
216
  }
217
  $url .= $path;
218
-
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.
@@ -343,568 +343,747 @@ class ws_broken_link_checker {
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){
399
- if (stristr($url, $excluded_word)){
400
- return true;
401
- }
402
- }
403
- return false;
404
- }
405
-
406
- function activity_box(){
407
- ?>
408
- <!-- wsblc activity box -->
409
- <div id='wsblc_activity_box'></div>
410
- <script type='text/javascript'>
411
- new Ajax.Updater('wsblc_activity_box', '<?php
412
- echo get_option( "siteurl" ).'/wp-content/plugins/'.$this->myfolder.'/wsblc_ajax.php?action=dashboard_status' ; ?>');
413
- </script>
414
- <!-- /wsblc activity box -->
415
- <?php
416
- }
417
-
418
- function admin_print_scripts(){
419
- // use JavaScript Prototype library for AJAX
420
- wp_enqueue_script('prototype');
421
- }
422
-
423
- function post_deleted($post_id){
424
- global $wpdb;
425
- $sql="DELETE FROM ".$this->linksdata_name." WHERE post_id=$post_id";
426
- $wpdb->query($sql);
427
- $sql="DELETE FROM ".$this->postdata_name." WHERE post_id=$post_id";
428
- $wpdb->query($sql);
429
- }
430
-
431
- function post_saved($post_id){
432
- global $wpdb;
433
-
434
- $post = get_post($post_id);
435
- //Only check links in posts, not revisions and attachments
436
- if ( ($post->post_type != 'post') && ($post->post_type != 'page') ) return null;
437
- //Only check published posts
438
- if ( $post->post_status != 'publish' ) return null;
439
-
440
- $found=$wpdb->get_var("SELECT post_id FROM $this->postdata_name WHERE post_id=$post_id LIMIT 1");
441
- if($found===NULL){
442
- //this post hasn't been saved previously, save the additional data now
443
- $wpdb->query("INSERT INTO $this->postdata_name (post_id, last_check) VALUES($post_id, '00-00-0000 00:00:00')");
444
- } else {
445
- //mark the post as not checked
446
- $wpdb->query("UPDATE $this->postdata_name SET last_check='00-00-0000 00:00:00' WHERE post_id=$post_id");
447
- //delete the previously extracted links - they are possibly no longer in the post
448
- $wpdb->query("DELETE FROM $this->linkdata_name WHERE post_id=$post_id");
449
- }
450
- }
451
-
452
-
453
- function sync_posts_to_db(){
454
- global $wpdb;
455
-
456
- /* JHS: This query does not work on mySQL 4.0 (4.0 does not support subqueries).
457
- // However, this one is faster, so I'll leave it here (forward compatibility)
458
- $sql="INSERT INTO ".$this->postdata_name."( post_id, last_check )
459
- SELECT id, '00-00-0000 00:00:00'
460
- FROM $wpdb->posts b
461
- WHERE NOT EXISTS (
462
- SELECT post_id
463
- FROM ".$this->postdata_name." a
464
- WHERE a.post_id = b.id
465
- )"; */
466
- //JHS: This one also works on mySQL 4.0:
467
- $sql="INSERT INTO ".$this->postdata_name."(post_id, last_check)
468
- SELECT ".$wpdb->posts.".id, '00-00-0000 00:00:00' FROM ".$wpdb->posts."
469
- LEFT JOIN ".$this->postdata_name." ON ".$wpdb->posts.".id=".$this->postdata_name.".post_id
470
- WHERE
471
- ".$this->postdata_name.".post_id IS NULL
472
- AND ".$wpdb->posts.".post_status = 'publish'
473
- AND ".$wpdb->posts.".post_type IN ('post', 'page') ";
474
- $wpdb->query($sql);
475
- }
476
-
477
- //JHS: Clears all blc tables and initiates a new fresh recheck
478
- function recheck_all_posts(){
479
- global $wpdb;
480
-
481
- //Empty blc_linkdata
482
- $sql="TRUNCATE TABLE ".$this->linkdata_name;
483
- $wpdb->query($sql);
484
-
485
- //Empty table [aggressive approach]
486
- $sql="TRUNCATE TABLE ".$this->postdata_name;
487
- //Reset all dates to zero [less aggressive approach, I like the above one better, it's cleaner ;)]
488
- //$sql="UPDATE $this->postdata_name SET last_check='00-00-0000 00:00:00' WHERE 1";
489
-
490
- $wpdb->query($sql);
491
-
492
- $this->sync_posts_to_db();
493
- }
494
-
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)
501
- || ($this->options['version'] != $this->version ) ) {
502
- $sql="CREATE TABLE ".$this->postdata_name." (
503
- post_id BIGINT( 20 ) NOT NULL ,
504
- last_check DATETIME NOT NULL ,
505
- UNIQUE KEY post_id (post_id)
506
- );";
507
-
508
- dbDelta($sql);
509
- }
510
-
511
- if (($wpdb->get_var("show tables like '".($this->linkdata_name)."'") != $this->linkdata_name)
512
- || ($this->options['version'] != $this->version ) ) {
513
- $sql="CREATE TABLE ".$this->linkdata_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).'...');
570
- }
571
-
572
- function options_page(){
573
-
574
- $this->options = get_option('wsblc_options');
575
- $reminder = '';
576
- //JHS: recheck all posts if asked for:
577
- if (isset($_GET['recheck']) && ($_GET['recheck'] == 'true')) {
578
- $this->recheck_all_posts();
579
- }
580
- if (isset($_GET['updated']) && ($_GET['updated'] == 'true')) {
581
- if(isset($_POST['Submit'])) {
582
-
583
- $new_session_length=intval($_POST['max_work_session']);
584
- if( $new_session_length >0 ){
585
- $this->options['max_work_session']=$new_session_length;
586
- }
587
-
588
- $new_check_treshold=intval($_POST['check_treshold']);
589
- if( $new_check_treshold > 0 ){
590
- $this->options['check_treshold']=$new_check_treshold;
591
- }
592
-
593
- $new_broken_link_css = trim($_POST['broken_link_css']);
594
- $this->options['broken_link_css'] = $new_broken_link_css;
595
-
596
- $this->options['mark_broken_links'] = !empty($_POST['mark_broken_links']);
597
-
598
- $this->options['delete_post_button'] = !empty($_POST['delete_post_button']);
599
-
600
- $this->options['exclusion_list']=array_filter(preg_split('/[\s,\r\n]+/',
601
- $_POST['exclusion_list']));
602
-
603
- update_option($this->options_name,$this->options);
604
- }
605
-
606
- }
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
-
622
- <table class="optiontable">
623
-
624
- <tr valign="top">
625
- <th scope="row">Status:</th>
626
- <td>
627
-
628
-
629
- <div id='wsblc_full_status'>
630
- <br/><br/>
631
- </div>
632
- <script type='text/javascript'>
633
- new Ajax.PeriodicalUpdater('wsblc_full_status', '<?php
634
- echo get_option( "siteurl" ).'/wp-content/plugins/'.$this->myfolder.'/wsblc_ajax.php?action=full_status' ; ?>',
635
- {
636
- method: 'get',
637
- frequency: 10,
638
- decay: 2
639
- });
640
- </script>
641
- <?php //JHS: Recheck all posts link: ?>
642
- <p><input class="button" type="button" name="recheckbutton" value="Re-check all pages" onclick="location.replace('<?php echo $_SERVER['PHP_SELF']; ?>?page=<?php echo plugin_basename(__FILE__); ?>&amp;recheck=true')" /></p>
643
- </td>
644
- </tr>
645
-
646
- <tr valign="top">
647
- <th scope="row">Check Every Post:</th>
648
- <td>
649
-
650
- Every <input type="text" name="check_treshold" id="check_treshold"
651
- value="<?php echo $this->options['check_treshold']; ?>" size='5' maxlength='3'/>
652
- hours
653
- <br/>
654
- Links in old posts will be re-checked this often. New posts will be usually checked ASAP.
655
-
656
- </td>
657
- </tr>
658
-
659
- <tr valign="top">
660
- <th scope="row">Broken Link CSS:</th>
661
- <td>
662
- <input type="checkbox" name="mark_broken_links" id="mark_broken_links"
663
- <?php if ($this->options['mark_broken_links']) echo ' checked="checked"'; ?>/>
664
- <label for='mark_broken_links'>Apply <em>class="broken_link"</em> to broken links</label><br/>
665
- <textarea type="text" name="broken_link_css" id="broken_link_css" cols='40' rows='4'/><?php
666
- if( isset($this->options['broken_link_css']) )
667
- echo $this->options['broken_link_css'];
668
- ?></textarea>
669
-
670
- </td>
671
- </tr>
672
-
673
- <tr valign="top">
674
- <th scope="row">Exclusion list:</th>
675
- <td>Don't check links where the URL contains any of these words (one per line):<br/>
676
- <textarea type="text" name="exclusion_list" id="exclusion_list" cols='40' rows='4'/><?php
677
- if( isset($this->options['exclusion_list']) )
678
- echo implode("\n", $this->options['exclusion_list']);
679
- ?></textarea>
680
-
681
- </td>
682
- </tr>
683
-
684
- <tr valign="top">
685
- <th scope="row">Work Session Length:</th>
686
- <td>
687
-
688
- <input type="text" name="max_work_session" id="max_work_session"
689
- value="<?php echo $this->options['max_work_session']; ?>" size='5' maxlength='3'/>
690
- seconds
691
- <br/>
692
- The link checker does its work in short "sessions" while any page of the WP admin panel is open.
693
- Typically you won't need to change this value.
694
-
695
- </td>
696
- </tr>
697
-
698
- <tr valign="top">
699
- <th scope="row">"Delete Post" option:</th>
700
- <td>
701
-
702
- <input type="checkbox" name="delete_post_button" id="delete_post_button"
703
- <?php if ($this->options['delete_post_button']) echo " checked='checked'"; ?>/>
704
- <label for='delete_post_button'>
705
- Display a "Delete Post" link in every row at the broken link list
706
- (<em>Manage -&gt; Broken Links</em>). Not recommended.</label>
707
-
708
- </td>
709
- </tr>
710
-
711
- </table>
712
-
713
- <p class="submit"><input type="submit" name="Submit" value="Update Options &raquo;" /></p>
714
- </form>
715
- </div>
716
- <?php
717
- }
718
-
719
- function broken_links_page(){
720
- global $wpdb;
721
- $sql="SELECT count(*) FROM $this->linkdata_name WHERE broken=1";
722
- $broken_links=$wpdb->get_var($sql);
723
-
724
- ?>
725
  <div class="wrap">
726
  <h2><?php
727
- echo ($broken_links>0)?"<span id='broken_link_count'>$broken_links</span> Broken Links":
728
- "No broken links found";
729
  ?></h2>
730
- <br style="clear:both;" />
 
 
 
 
 
 
 
731
  <?php
732
- $sql="SELECT b.post_title, a.*, b.guid FROM $this->linkdata_name a, $wpdb->posts b
733
- WHERE a.post_id=b.id AND a.broken=1 ORDER BY a.last_check DESC";
734
- $links=$wpdb->get_results($sql, OBJECT);
735
- if($links && (count($links)>0)){
736
- ?>
737
- <table class="widefat">
738
- <thead>
739
- <tr>
740
-
741
- <th scope="col"><div style="text-align: center">#</div></th>
742
-
743
- <th scope="col">Post</th>
744
- <th scope="col">Link Text</th>
745
- <th scope="col">URL</th>
746
-
747
- <th scope="col" colspan='<?php echo ($this->options['delete_post_button'])?'5':'4';x ?>'>Action</th>
748
-
749
- </tr>
750
- </thead>
751
- <tbody id="the-list">
752
- <?php
753
-
754
- $rownumber=0;
755
- foreach ($links as $link) {
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>
763
- <a href='$link->url' target='_blank'>".$this->mytruncate($link->url)."</a>
764
- | <a href='javascript:editBrokenLink($link->id, \"$link->url\")'
765
- id='link-editor-button-$link->id'>Edit</a>
766
- <br />
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
-
774
- //the ""Delete Post"" button - optional
775
- if ($this->options['delete_post_button']){
776
- $deletion_url = "post.php?action=delete&post=$link->post_id";
777
- $deletion_url = wp_nonce_url($deletion_url, "delete-post_$link->post_id");
778
- echo "<td><a href='$deletion_url'>Delete Post</a></td>";
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>';
800
- };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
801
  ?>
802
  <style type='text/css'>
803
  .link-editor {
804
- font-size: 1em;
805
  }
806
  </style>
807
 
808
  <script type='text/javascript'>
809
- function alterLinkCounter(factor){
810
- cnt = parseInt($('broken_link_count').innerHTML);
811
- cnt = cnt + factor;
812
- $('broken_link_count').innerHTML = cnt;
813
- }
814
-
815
- function discardLinkMessage(link_id){
816
- $('discard_button-'+link_id).innerHTML = 'Wait...';
817
- new Ajax.Request('<?php
818
- echo get_option( "siteurl" ).'/wp-content/plugins/'.$this->myfolder.'/wsblc_ajax.php?';
819
- ?>action=discard_link&id='+link_id,
820
- {
821
- method:'get',
822
- onSuccess: function(transport){
823
- var re = /OK:.*/i
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';
831
- alert(response);
832
- }
833
- }
834
- }
835
- );
836
-
837
- }
838
- function removeLinkFromPost(link_id){
839
- $('unlink_button-'+link_id).innerHTML = 'Wait...';
840
-
841
- new Ajax.Request(
842
- '<?php
843
- echo get_option( "siteurl" ).'/wp-content/plugins/'.$this->myfolder.'/wsblc_ajax.php?';
844
- ?>action=remove_link&id='+link_id,
845
- {
846
- method:'get',
847
- onSuccess: function(transport){
848
- var re = /OK:.*/i
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';
856
- alert(response);
857
- }
858
- }
859
- }
860
- );
861
- }
862
-
863
- function editBrokenLink(link_id, orig_link){
864
- if ($('link-editor-button-'+link_id).innerHTML == 'Edit'){
865
- $('link-editor-'+link_id).show();
866
- $('link-editor-'+link_id).focus();
867
- $('link-editor-'+link_id).select();
868
- $('link-editor-button-'+link_id).innerHTML = 'Save';
869
- } else {
870
- $('link-editor-'+link_id).hide();
871
- new_url = $('link-editor-'+link_id).value;
872
- if (new_url != orig_link){
873
- //Save the changed link
874
- new Ajax.Request(
875
- '<?php
876
- echo get_option( "siteurl" ).'/wp-content/plugins/'.$this->myfolder.'/wsblc_ajax.php?';
877
- ?>action=edit_link&id='+link_id+'&new_url='+escape(new_url),
878
- {
879
- method:'post',
880
- onSuccess: function(transport){
881
- var re = /OK:.*/i
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 {
889
- alert(response);
890
- }
891
- }
892
- }
893
- );
894
-
895
- }
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
907
- }
908
 
909
  }//class ends here
910
 
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.9
7
  Author: Janis Elsts
8
  Author URI: http://w-shadow.com/blog/
9
  */
10
 
11
  /*
12
+ Created by Janis Elsts (email : whiteshadow@w-shadow.com)
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 {
22
+ var $options;
23
+ var $options_name='wsblc_options';
24
+ var $postdata_name;
25
+ var $linkdata_name;
26
+ var $version='0.4.9';
27
+ var $myfile='';
28
+ var $myfolder='';
29
+ var $mybasename='';
30
+ var $siteurl;
31
+ var $defaults;
32
+
33
+ function ws_broken_link_checker() {
34
+ global $wpdb;
35
+
36
+ //set default options
37
+ $this->defaults = array(
38
+ 'version' => $this->version,
39
+ 'max_work_session' => 27,
40
+ 'check_treshold' => 72,
41
+ 'mark_broken_links' => true,
42
+ 'broken_link_css' => ".broken_link, a.broken_link {\n\ttext-decoration: line-through;\n}",
43
+ 'exclusion_list' => array(),
44
+ 'delete_post_button' => false
45
+ );
46
+ //load options
47
+ $this->options=get_option($this->options_name);
48
+ if(!is_array($this->options)){
49
+ $this->options = $this->defaults;
50
+ } else {
51
+ $this->options = array_merge($this->defaults, $this->options);
52
+ }
53
+
54
+ $this->postdata_name=$wpdb->prefix . "blc_postdata";
55
+ $this->linkdata_name=$wpdb->prefix . "blc_linkdata";
56
+ $this->siteurl = get_option('siteurl');
57
+
58
+ $my_file = str_replace('\\', '/',__FILE__);
59
+ $my_file = preg_replace('/^.*wp-content[\\\\\/]plugins[\\\\\/]/', '', $my_file);
60
+ add_action('activate_'.$my_file, array(&$this,'activation'));
61
+ $this->myfile=$my_file;
62
+ $this->myfolder=basename(dirname(__FILE__));
63
+ $this->mybasename=plugin_basename(__FILE__);
64
+
65
+ add_action('admin_menu', array(&$this,'options_menu'));
66
+
67
+ add_action('delete_post', array(&$this,'post_deleted'));
68
+ add_action('save_post', array(&$this,'post_saved'));
69
+ add_action('admin_footer', array(&$this,'admin_footer'));
70
+ add_action('admin_print_scripts', array(&$this,'admin_print_scripts'));
71
+ add_action('activity_box_end', array(&$this,'activity_box'));
72
+
73
+ if (!empty($this->options['mark_broken_links'])){
74
+ add_filter('the_content', array(&$this,'the_content'));
75
+ if (!empty($this->options['broken_link_css'])){
76
+ add_action('wp_head', array(&$this,'header_css'));
77
+ }
78
+ }
79
+ }
80
+
81
+ function admin_footer(){
82
+ ?>
83
+ <!-- wsblc admin footer -->
84
+ <div id='wsblc_updater_div'></div>
85
+ <script type='text/javascript'>
86
+ new Ajax.PeriodicalUpdater('wsblc_updater_div', '<?php
87
+ echo get_option( "siteurl" ).'/wp-content/plugins/'.$this->myfolder.'/wsblc_ajax.php?action=run_check' ; ?>',
88
+ {
89
+ method: 'get',
90
+ frequency: <?php echo ($this->options['max_work_session']-1); ?>,
91
+ decay: 1.3
92
+ });
93
+ </script>
94
+ <!-- /wsblc admin footer -->
95
+ <?php
96
+ }
97
+
98
+ function header_css(){
99
+ echo '<style type="text/css">',$this->options['broken_link_css'],'</style>';
100
+ }
101
+
102
+ function the_content($content){
103
+ global $post, $wpdb;
104
+ if (empty($post)) return $content;
105
+
106
+ $sql="SELECT url from $this->linkdata_name WHERE post_id = $post->ID AND broken<>0";
107
+ $rows=$wpdb->get_results($sql, ARRAY_A);
108
+ if($rows && (count($rows)>0)){
109
+ //some rows found
110
+ $this->links_to_remove = array_map(
111
+ create_function('$elem', 'return $elem["url"];'),
112
+ $rows);
113
+ $url_pattern='/(<a[\s]+[^>]*href\s*=\s*[\"\']?)([^\'\" >]+)([\'\"]+[^<>]*)(>)((?sU).*)(<\/a>)/i';
114
+ $content = preg_replace_callback($url_pattern, array(&$this,'mark_broken_links'), $content);
115
+ };
116
+
117
+ //print_r($post);
118
+ return $content;
119
+ }
120
+
121
+ function mark_broken_links($matches){
122
+ $url = $this->normalize_url(html_entity_decode($matches[2])) ;
123
+ if(in_array($url, $this->links_to_remove)){
124
+ return $matches[1].$matches[2].$matches[3].' class="broken_link"'.$matches[4].
125
+ $matches[5].$matches[6];
126
+ } else {
127
+ return $matches[0];
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;/',
153
+ '/^(javascript:.*)/i',
154
+ '/([\?&]sid=\w+)$/i'
155
+ ),
156
+ array('','','&','',''),
157
+ $url);
158
+ $url=trim($url);
159
+
160
+ if($url=='') return false;
161
+
162
  // turn relative URLs into absolute URLs
163
  $url = $this->relative2absolute($this->siteurl, $url);
164
  return $url;
165
+ }
166
+
167
+ function relative2absolute($absolute, $relative) {
168
  $p = @parse_url($relative);
169
  if(!$p) {
170
+ //WTF? $relative is a seriously malformed URL
171
+ return false;
172
  }
173
  if(isset($p["scheme"])) return $relative;
174
+
175
  $parts=(parse_url($absolute));
176
+
177
  if(substr($relative,0,1)=='/') {
178
  $cparts = (explode("/", $relative));
179
  array_shift($cparts);
180
  } else {
181
+ if(isset($parts['path'])){
182
+ $aparts=explode('/',$parts['path']);
183
+ array_pop($aparts);
184
+ $aparts=array_filter($aparts);
185
+ } else {
186
+ $aparts=array();
187
+ }
188
+
189
+ $rparts = (explode("/", $relative));
190
+
191
  $cparts = array_merge($aparts, $rparts);
192
  foreach($cparts as $i => $part) {
193
  if($part == '.') {
199
  }
200
  }
201
  $path = implode("/", $cparts);
202
+
203
  $url = '';
204
  if($parts['scheme']) {
205
  $url = "$parts[scheme]://";
215
  $url .= $parts['host']."/";
216
  }
217
  $url .= $path;
218
+
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.
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){
399
+ if (stristr($url, $excluded_word)){
400
+ return true;
401
+ }
402
+ }
403
+ return false;
404
+ }
405
+
406
+ function activity_box(){
407
+ ?>
408
+ <!-- wsblc activity box -->
409
+ <div id='wsblc_activity_box'></div>
410
+ <script type='text/javascript'>
411
+ new Ajax.Updater('wsblc_activity_box', '<?php
412
+ echo get_option( "siteurl" ).'/wp-content/plugins/'.$this->myfolder.'/wsblc_ajax.php?action=dashboard_status' ; ?>');
413
+ </script>
414
+ <!-- /wsblc activity box -->
415
+ <?php
416
+ }
417
+
418
+ function admin_print_scripts(){
419
+ // use JavaScript Prototype library for AJAX
420
+ wp_enqueue_script('prototype');
421
+ }
422
+
423
+ function post_deleted($post_id){
424
+ global $wpdb;
425
+ $sql="DELETE FROM ".$this->linksdata_name." WHERE post_id=$post_id";
426
+ $wpdb->query($sql);
427
+ $sql="DELETE FROM ".$this->postdata_name." WHERE post_id=$post_id";
428
+ $wpdb->query($sql);
429
+ }
430
+
431
+ function post_saved($post_id){
432
+ global $wpdb;
433
+
434
+ $post = get_post($post_id);
435
+ //Only check links in posts, not revisions and attachments
436
+ if ( ($post->post_type != 'post') && ($post->post_type != 'page') ) return null;
437
+ //Only check published posts
438
+ if ( $post->post_status != 'publish' ) return null;
439
+
440
+ $found=$wpdb->get_var("SELECT post_id FROM $this->postdata_name WHERE post_id=$post_id LIMIT 1");
441
+ if($found===NULL){
442
+ //this post hasn't been saved previously, save the additional data now
443
+ $wpdb->query("INSERT INTO $this->postdata_name (post_id, last_check) VALUES($post_id, '00-00-0000 00:00:00')");
444
+ } else {
445
+ //mark the post as not checked
446
+ $wpdb->query("UPDATE $this->postdata_name SET last_check='00-00-0000 00:00:00' WHERE post_id=$post_id");
447
+ //delete the previously extracted links - they are possibly no longer in the post
448
+ $wpdb->query("DELETE FROM $this->linkdata_name WHERE post_id=$post_id");
449
+ }
450
+ }
451
+
452
+
453
+ function sync_posts_to_db(){
454
+ global $wpdb;
455
+
456
+ /* JHS: This query does not work on mySQL 4.0 (4.0 does not support subqueries).
457
+ // However, this one is faster, so I'll leave it here (forward compatibility)
458
+ $sql="INSERT INTO ".$this->postdata_name."( post_id, last_check )
459
+ SELECT id, '00-00-0000 00:00:00'
460
+ FROM $wpdb->posts b
461
+ WHERE NOT EXISTS (
462
+ SELECT post_id
463
+ FROM ".$this->postdata_name." a
464
+ WHERE a.post_id = b.id
465
+ )"; */
466
+ //JHS: This one also works on mySQL 4.0:
467
+ $sql="INSERT INTO ".$this->postdata_name."(post_id, last_check)
468
+ SELECT ".$wpdb->posts.".id, '00-00-0000 00:00:00' FROM ".$wpdb->posts."
469
+ LEFT JOIN ".$this->postdata_name." ON ".$wpdb->posts.".id=".$this->postdata_name.".post_id
470
+ WHERE
471
+ ".$this->postdata_name.".post_id IS NULL
472
+ AND ".$wpdb->posts.".post_status = 'publish'
473
+ AND ".$wpdb->posts.".post_type IN ('post', 'page') ";
474
+ $wpdb->query($sql);
475
+ }
476
+
477
+ //JHS: Clears all blc tables and initiates a new fresh recheck
478
+ function recheck_all_posts(){
479
+ global $wpdb;
480
+
481
+ //Empty blc_linkdata
482
+ $sql="TRUNCATE TABLE ".$this->linkdata_name;
483
+ $wpdb->query($sql);
484
+
485
+ //Empty table [aggressive approach]
486
+ $sql="TRUNCATE TABLE ".$this->postdata_name;
487
+ //Reset all dates to zero [less aggressive approach, I like the above one better, it's cleaner ;)]
488
+ //$sql="UPDATE $this->postdata_name SET last_check='00-00-0000 00:00:00' WHERE 1";
489
+
490
+ $wpdb->query($sql);
491
+
492
+ $this->sync_posts_to_db();
493
+ }
494
+
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)
501
+ || ($this->options['version'] != $this->version ) ) {
502
+ $sql="CREATE TABLE ".$this->postdata_name." (
503
+ post_id BIGINT( 20 ) NOT NULL ,
504
+ last_check DATETIME NOT NULL ,
505
+ UNIQUE KEY post_id (post_id)
506
+ );";
507
+
508
+ dbDelta($sql);
509
+ }
510
+
511
+ if (($wpdb->get_var("show tables like '".($this->linkdata_name)."'") != $this->linkdata_name)
512
+ || ($this->options['version'] != $this->version ) ) {
513
+ $sql="CREATE TABLE ".$this->linkdata_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).'...');
570
+ }
571
+
572
+ function options_page(){
573
+
574
+ $this->options = get_option('wsblc_options');
575
+ $reminder = '';
576
+ //JHS: recheck all posts if asked for:
577
+ if (isset($_GET['recheck']) && ($_GET['recheck'] == 'true')) {
578
+ $this->recheck_all_posts();
579
+ }
580
+ if (isset($_GET['updated']) && ($_GET['updated'] == 'true')) {
581
+ if(isset($_POST['Submit'])) {
582
+
583
+ $new_session_length=intval($_POST['max_work_session']);
584
+ if( $new_session_length >0 ){
585
+ $this->options['max_work_session']=$new_session_length;
586
+ }
587
+
588
+ $new_check_treshold=intval($_POST['check_treshold']);
589
+ if( $new_check_treshold > 0 ){
590
+ $this->options['check_treshold']=$new_check_treshold;
591
+ }
592
+
593
+ $new_broken_link_css = trim($_POST['broken_link_css']);
594
+ $this->options['broken_link_css'] = $new_broken_link_css;
595
+
596
+ $this->options['mark_broken_links'] = !empty($_POST['mark_broken_links']);
597
+
598
+ $this->options['delete_post_button'] = !empty($_POST['delete_post_button']);
599
+
600
+ $this->options['exclusion_list']=array_filter(preg_split('/[\s,\r\n]+/',
601
+ $_POST['exclusion_list']));
602
+
603
+ update_option($this->options_name,$this->options);
604
+ }
605
+
606
+ }
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
+
622
+ <table class="optiontable">
623
+
624
+ <tr valign="top">
625
+ <th scope="row">Status:</th>
626
+ <td>
627
+
628
+
629
+ <div id='wsblc_full_status'>
630
+ <br/><br/>
631
+ </div>
632
+ <script type='text/javascript'>
633
+ new Ajax.PeriodicalUpdater('wsblc_full_status', '<?php
634
+ echo get_option( "siteurl" ).'/wp-content/plugins/'.$this->myfolder.'/wsblc_ajax.php?action=full_status' ; ?>',
635
+ {
636
+ method: 'get',
637
+ frequency: 10,
638
+ decay: 2
639
+ });
640
+ </script>
641
+ <?php //JHS: Recheck all posts link: ?>
642
+ <p><input class="button" type="button" name="recheckbutton" value="Re-check all pages" onclick="location.replace('<?php echo $_SERVER['PHP_SELF']; ?>?page=<?php echo plugin_basename(__FILE__); ?>&amp;recheck=true')" /></p>
643
+ </td>
644
+ </tr>
645
+
646
+ <tr valign="top">
647
+ <th scope="row">Check Every Post:</th>
648
+ <td>
649
+
650
+ Every <input type="text" name="check_treshold" id="check_treshold"
651
+ value="<?php echo $this->options['check_treshold']; ?>" size='5' maxlength='3'/>
652
+ hours
653
+ <br/>
654
+ Links in old posts will be re-checked this often. New posts will be usually checked ASAP.
655
+
656
+ </td>
657
+ </tr>
658
+
659
+ <tr valign="top">
660
+ <th scope="row">Broken Link CSS:</th>
661
+ <td>
662
+ <input type="checkbox" name="mark_broken_links" id="mark_broken_links"
663
+ <?php if ($this->options['mark_broken_links']) echo ' checked="checked"'; ?>/>
664
+ <label for='mark_broken_links'>Apply <em>class="broken_link"</em> to broken links</label><br/>
665
+ <textarea type="text" name="broken_link_css" id="broken_link_css" cols='40' rows='4'/><?php
666
+ if( isset($this->options['broken_link_css']) )
667
+ echo $this->options['broken_link_css'];
668
+ ?></textarea>
669
+
670
+ </td>
671
+ </tr>
672
+
673
+ <tr valign="top">
674
+ <th scope="row">Exclusion list:</th>
675
+ <td>Don't check links where the URL contains any of these words (one per line):<br/>
676
+ <textarea type="text" name="exclusion_list" id="exclusion_list" cols='40' rows='4'/><?php
677
+ if( isset($this->options['exclusion_list']) )
678
+ echo implode("\n", $this->options['exclusion_list']);
679
+ ?></textarea>
680
+
681
+ </td>
682
+ </tr>
683
+
684
+ <tr valign="top">
685
+ <th scope="row">Work Session Length:</th>
686
+ <td>
687
+
688
+ <input type="text" name="max_work_session" id="max_work_session"
689
+ value="<?php echo $this->options['max_work_session']; ?>" size='5' maxlength='3'/>
690
+ seconds
691
+ <br/>
692
+ The link checker does its work in short "sessions" while any page of the WP admin panel is open.
693
+ Typically you won't need to change this value.
694
+
695
+ </td>
696
+ </tr>
697
+
698
+ <tr valign="top">
699
+ <th scope="row">"Delete Post" option:</th>
700
+ <td>
701
+
702
+ <input type="checkbox" name="delete_post_button" id="delete_post_button"
703
+ <?php if ($this->options['delete_post_button']) echo " checked='checked'"; ?>/>
704
+ <label for='delete_post_button'>
705
+ Display a "Delete Post" link in every row at the broken link list
706
+ (<em>Manage -&gt; Broken Links</em>). Not recommended.</label>
707
+
708
+ </td>
709
+ </tr>
710
+
711
+ </table>
712
+
713
+ <p class="submit"><input type="submit" name="Submit" value="Update Options &raquo;" /></p>
714
+ </form>
715
+ </div>
716
+ <?php
717
+ }
718
+
719
+ function broken_links_page(){
720
+ global $wpdb;
721
+ $sql="SELECT count(*) FROM $this->linkdata_name WHERE broken=1";
722
+ $broken_links=$wpdb->get_var($sql);
723
+
724
+ ?>
725
  <div class="wrap">
726
  <h2><?php
727
+ echo ($broken_links>0)?"<span id='broken_link_count'>$broken_links</span> Broken Links":
728
+ "No broken links found";
729
  ?></h2>
730
+ <form style="font-size: x-small;" action="javascript: void(0);" method="">
731
+ <label for="sort_order">Sort by:</label><select id="sort_order" name="sort_order"
732
+ onchange="sortTheList(this.options[this.selectedIndex].value); return false;">
733
+ <option value="check_date" selected="selected">Default (Last checked)</option>
734
+ <option value="post_date">By Date</option>
735
+ </select>
736
+ </form>
737
+ <br style="clear:both;" />
738
  <?php
739
+ $sql="SELECT b.post_title, b.post_date, a.*, b.guid FROM $this->linkdata_name a, $wpdb->posts b
740
+ WHERE a.post_id=b.id AND a.broken=1 ORDER BY a.last_check DESC";
741
+ $links=$wpdb->get_results($sql, OBJECT);
742
+ if($links && (count($links)>0)){
743
+ ?>
744
+ <table class="widefat">
745
+ <thead>
746
+ <tr>
747
+
748
+ <th scope="col"><div style="text-align: center">#</div></th>
749
+
750
+ <th scope="col">Post
751
+ </th>
752
+ <th scope="col">Link Text</th>
753
+ <th scope="col">URL</th>
754
+
755
+ <th scope="col" colspan='<?php echo ($this->options['delete_post_button'])?'5':'4';x ?>'>Action</th>
756
+
757
+ </tr>
758
+ </thead>
759
+ <tbody id="the-list">
760
+ <?php
761
+
762
+ $rownumber=0;
763
+ /* reformatted this section - changed a few ids, made the php excerpts
764
+ explicit to take advantage of syntax highlighting. Most notably, the
765
+ link details section is changed here.*/
766
+ foreach ($links as $link) {
767
+ $rownumber++;
768
+ ?>
769
+ <tr id='<?php print "link-$link->id" ?>' class='alternate'>
770
+ <th scope='row' style='text-align: center'><?php print $rownumber; ?></th>
771
+ <td>
772
+ <a href='<?php print get_permalink($link->post_id); ?>'
773
+ title='View post'><?php print $link->post_title; ?></a></td>
774
+
775
+ <td><?php print $link->link_text; ?></td>
776
+ <td>
777
+ <a href='<?php print $link->url; ?>' target='_blank'>
778
+ <?php print $this->mytruncate($link->url); ?></a>
779
+ | <a href='javascript:editBrokenLink(<?php print "$link->id, \"$link->url\""; ?>)'
780
+ id='link-editor-button-<?php print $link->id; ?>'>Edit</a>
781
+ <br />
782
+ <input type='text' size='50' id='link-editor-<?php print $link->id; ?>'
783
+ value='<?php print $link->url; ?>'
784
+ class='link-editor' style='display:none' />
785
+ </td>
786
+ <td><a href='javascript:void(0);' onclick='
787
+ toggleLinkDetails(<?php print $link->id; ?>);
788
+ return false;' class='edit'>Details</a></td>
789
+
790
+ <td><a href='post.php?action=edit&amp;post=<?php print $link->post_id; ?>'
791
+ class='edit'>Edit Post</a></td>
792
+ <?php
793
+ //the ""Delete Post"" button - optional
794
+ if ($this->options['delete_post_button']){
795
+ $deletion_url = "post.php?action=delete&post=$link->post_id";
796
+ $deletion_url = wp_nonce_url($deletion_url, "delete-post_$link->post_id");
797
+ echo "<td><a href='$deletion_url'>Delete Post</a></td>";
798
+ }
799
+ ?><td><a href='javascript:void(0);' class='delete'
800
+ id='discard_button-<?php print $link->id; ?>'
801
+ onclick='discardLinkMessage(<?php print $link->id; ?>);return false;'
802
+ title='Discard This Message'>Discard</a></td>
803
+
804
+ <td><a href='javascript:void(0);' class='delete' id='unlink_button-<?php print $link->id; ?>'
805
+ onclick='removeLinkFromPost(<?php print $link->id; ?>);return false;'
806
+ title='Remove the link from the post'>Unlink</a></td>
807
+ </tr>
808
+ <!-- Link details -->
809
+ <tr id='<?php print "link-details-$link->id"; ?>' style='display:none;'>
810
+ <span id='post_date_full' style='display:none;'><?php
811
+ print $link->post_date;
812
+ ?></span>
813
+ <span id='check_date_full' style='display:none;'><?php
814
+ print $link->last_check;
815
+ ?></span>
816
+ <td colspan='8'>
817
+ <ol style='list-style-type: none; width: 50%; float: right;'>
818
+ <li><strong>Log :</strong>
819
+ <span id='blc_log'><?php
820
+ print nl2br($link->log);
821
+ ?></span></li></ol>
822
+ <ol style='list-style-type: none; padding-left: 2px;'>
823
+ <li><strong>Published On :</strong>
824
+ <span id='post_date'><?php
825
+ print strftime("%B %d, %Y",strtotime($link->post_date));
826
+ ?></span></li>
827
+ <li><strong>Last Checked :</strong>
828
+ <span id='check_date'><?php
829
+ print strftime("%B %d, %Y",strtotime($link->last_check));
830
+ ?></span></li>
831
+ <li><strong>Final URL :</strong>
832
+ <span id='final_url'><?php
833
+ print "$link->final_url";
834
+ ?></span></li>
835
+ <li><strong>HTTP Code :</strong>
836
+ <span id='http_code'><?php
837
+ print "$link->http_code";
838
+ ?></span></li></ol>
839
+ </td></tr><?php
840
+ }
841
+ ?></tbody></table><?php
842
+ };
843
  ?>
844
  <style type='text/css'>
845
  .link-editor {
846
+ font-size: 1em;
847
  }
848
  </style>
849
 
850
  <script type='text/javascript'>
851
+ /** for debugging only **/
852
+ var flg = 1;
853
+ function msg(text){
854
+ if(flg){
855
+ if(window.dump !== undefined){
856
+ dump(text);
857
+ }
858
+ if(window.console !== undefined){
859
+ console.log(text);
860
+ }
861
+ flg++;
862
+ if(flg > 200){
863
+ flg = false;
864
+ }
865
+ }
866
+ }
867
+ function alterLinkCounter(factor){
868
+ cnt = parseInt($('broken_link_count').innerHTML);
869
+ cnt = cnt + factor;
870
+ $('broken_link_count').innerHTML = cnt;
871
+ }
872
+
873
+ function discardLinkMessage(link_id){
874
+ $('discard_button-'+link_id).innerHTML = 'Wait...';
875
+ new Ajax.Request('<?php
876
+ echo get_option( "siteurl" ).'/wp-content/plugins/'.$this->myfolder.'/wsblc_ajax.php?';
877
+ ?>action=discard_link&id='+link_id,
878
+ {
879
+ method:'get',
880
+ onSuccess: function(transport){
881
+ var re = /OK:.*/i
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
+ } else {
888
+ $('discard_button-'+link_id).innerHTML = 'Discard';
889
+ alert(response);
890
+ }
891
+ }
892
+ }
893
+ );
894
+
895
+ }
896
+ function removeLinkFromPost(link_id){
897
+ $('unlink_button-'+link_id).innerHTML = 'Wait...';
898
+
899
+ new Ajax.Request(
900
+ '<?php
901
+ echo get_option( "siteurl" ).'/wp-content/plugins/'.$this->myfolder.'/wsblc_ajax.php?';
902
+ ?>action=remove_link&id='+link_id,
903
+ {
904
+ method:'get',
905
+ onSuccess: function(transport){
906
+ var re = /OK:.*/i
907
+ var response = transport.responseText || "";
908
+ if (re.test(response)){
909
+ $('link-'+link_id).hide();
910
+ $('link-details-'+link_id).hide();
911
+ alterLinkCounter(-1);
912
+ } else {
913
+ $('unlink_button-'+link_id).innerHTML = 'Unlink';
914
+ alert(response);
915
+ }
916
+ }
917
+ }
918
+ );
919
+ }
920
+ /* sorting function added here */
921
+ function sortTheList(sort_type){
922
+ var sort_types = {
923
+ "post_date": true,
924
+ "check_date": true
925
+ };
926
+ if(sort_types[sort_type] === undefined){
927
+ return false;
928
+ }
929
+ function rSearch(e, sTarget){
930
+ if(e === null){
931
+ return;
932
+ }
933
+ if(e.id !== undefined){
934
+ if(e.id.toString() == sTarget){
935
+ return e;
936
+ }
937
+ }
938
+ return rSearch(e.firstChild, sTarget) || rSearch(e.nextSibling, sTarget);
939
+ }
940
+ function mergesort(list, i){
941
+ i = (i === undefined) ? 0 : i;
942
+ var left, right, result;
943
+ if (list.length <= 1){
944
+ return list;
945
+ }
946
+ var middle = Math.floor(list.length / 2);
947
+ left = mergesort(list.slice(0, middle), (i+1));
948
+ right = mergesort(list.slice(middle), (i+1));
949
+ result = merge(left, right);
950
+ return result;
951
+ }
952
+ function merge(left, right){
953
+ var result = [];
954
+ var lindex = 0;
955
+ var rindex = 0;
956
+ var str = '';
957
+ for(var item in left){
958
+ if(left.hasOwnProperty(item)){
959
+ str += " " + left[item].id + ", ";
960
+ }
961
+ }
962
+ var str2 = '';
963
+ for(var item in right){
964
+ if(right.hasOwnProperty(item)){
965
+ str += " " + right[item].id + ", ";
966
+ }
967
+ }
968
+ while(left.length > lindex && right.length > rindex){
969
+ if(left[lindex].date < right[rindex].date){
970
+ result.push(left[lindex]);
971
+ lindex++;
972
+ }
973
+ else{
974
+ result.push(right[rindex]);
975
+ rindex++;
976
+ }
977
+ }
978
+ if(left.length > lindex){
979
+ result = result.concat(left.slice(lindex));
980
+ }
981
+ else{
982
+ result = result.concat(right.slice(rindex));
983
+ }
984
+ return result;
985
+ }
986
+ var theList = document.getElementById('the-list');
987
+ if (theList === null || theList.hasChildNodes() === false){
988
+ return;
989
+ }
990
+ var re = /^link-(\d+)$/;
991
+ var myid = 0;
992
+ var links = [];
993
+ var children = theList.childNodes;
994
+ var child = null;
995
+ for (var i = 0; i < children.length; i++){
996
+ child = children[i];
997
+ if(child === undefined){
998
+ continue;
999
+ }
1000
+ if(child === undefined || child.nodeType !== Node.ELEMENT_NODE ||
1001
+ child.nodeName !== 'TR'){
1002
+ continue;
1003
+ }
1004
+ if(child.id && child.id.match(re)){
1005
+ myid = (child.id.match(re))[1];
1006
+ mydetails = child;
1007
+ var mydate = false;
1008
+ while ( mydetails !== undefined &&
1009
+ mydetails !== null &&
1010
+ mydetails.id != 'link-details-'+myid){
1011
+ mydetails = mydetails.nextSibling;
1012
+ }
1013
+ var node = rSearch(child, sort_type+'_full').firstChild;
1014
+ if(node){
1015
+ mydate = new Date(node.nodeValue.toString());
1016
+ links.push({
1017
+ id: myid,
1018
+ date: mydate,
1019
+ details: mydetails === null ? null : mydetails.cloneNode(true),
1020
+ link: child === null ? null : child.cloneNode(true)
1021
+ });
1022
+ }
1023
+ }
1024
+ }
1025
+ while(theList.hasChildNodes()){
1026
+ try{
1027
+ theList.removeChild(theList.firstChild);
1028
+ }
1029
+ catch(e){
1030
+ break;
1031
+ }
1032
+ }
1033
+ links = mergesort(links);
1034
+ for (var i = 0; i < links.length; i++){
1035
+ var obj = links[i];
1036
+ theList.appendChild(obj.link);
1037
+ theList.appendChild(obj.details);
1038
+ }
1039
+ }
1040
+ /* end of js changes */
1041
+
1042
+ function editBrokenLink(link_id, orig_link){
1043
+ if ($('link-editor-button-'+link_id).innerHTML == 'Edit'){
1044
+ $('link-editor-'+link_id).show();
1045
+ $('link-editor-'+link_id).focus();
1046
+ $('link-editor-'+link_id).select();
1047
+ $('link-editor-button-'+link_id).innerHTML = 'Save';
1048
+ } else {
1049
+ $('link-editor-'+link_id).hide();
1050
+ new_url = $('link-editor-'+link_id).value;
1051
+ if (new_url != orig_link){
1052
+ //Save the changed link
1053
+ new Ajax.Request(
1054
+ '<?php
1055
+ echo get_option( "siteurl" ).'/wp-content/plugins/'.$this->myfolder.'/wsblc_ajax.php?';
1056
+ ?>action=edit_link&id='+link_id+'&new_url='+escape(new_url),
1057
+ {
1058
+ method:'post',
1059
+ onSuccess: function(transport){
1060
+ var re = /OK:.*/i
1061
+ var response = transport.responseText || "";
1062
+ if (re.test(response)){
1063
+ $('link-'+link_id).hide();
1064
+ $('link-details-'+link_id).hide();
1065
+ alterLinkCounter(-1);
1066
+ //alert(response);
1067
+ } else {
1068
+ alert(response);
1069
+ }
1070
+ }
1071
+ }
1072
+ );
1073
+
1074
+ }
1075
+ $('link-editor-button-'+link_id).innerHTML = 'Edit';
1076
+ }
1077
+ }
1078
+
1079
+ function toggleLinkDetails(link_id){
1080
+ //alert('showlinkdetails '+link_id);
1081
+ $('link-details-'+link_id).toggle();
1082
+ }
1083
  </script>
1084
  </div>
1085
+ <?php
1086
+ }
1087
 
1088
  }//class ends here
1089
 
readme.txt CHANGED
@@ -3,7 +3,7 @@ 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
 
3
  Tags: links, broken, maintenance
4
  Requires at least: 2.0.2
5
  Tested up to: 2.6.2
6
+ Stable tag: 0.4.9
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