Version Description
Download this release
Release Info
Developer | whiteshadow |
Plugin | Broken Link Checker |
Version | 0.5.10 |
Comparing to | |
See all releases |
Code changes from version 0.5.9 to 0.5.10
- broken-link-checker.php +62 -2177
- config-manager.php +85 -0
- core.php +2137 -0
- highlighter-class.php +76 -0
- link-classes.php +12 -10
- readme.txt +17 -2
- utility-class.php +31 -15
broken-link-checker.php
CHANGED
@@ -1,9 +1,10 @@
|
|
1 |
<?php
|
|
|
2 |
/*
|
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.5.
|
7 |
Author: Janis Elsts
|
8 |
Author URI: http://w-shadow.com/blog/
|
9 |
*/
|
@@ -13,33 +14,6 @@ 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 |
-
/**
|
20 |
-
* Simple function to replicate PHP 5 behaviour
|
21 |
-
*/
|
22 |
-
if ( !function_exists('microtime_float') ) {
|
23 |
-
function microtime_float()
|
24 |
-
{
|
25 |
-
list($usec, $sec) = explode(" ", microtime());
|
26 |
-
return ((float)$usec + (float)$sec);
|
27 |
-
}
|
28 |
-
}
|
29 |
-
|
30 |
-
//Make sure some useful constants are defined
|
31 |
-
if ( ! defined( 'WP_CONTENT_URL' ) )
|
32 |
-
define( 'WP_CONTENT_URL', get_option( 'siteurl' ) . '/wp-content' );
|
33 |
-
if ( ! defined( 'WP_CONTENT_DIR' ) )
|
34 |
-
define( 'WP_CONTENT_DIR', ABSPATH . 'wp-content' );
|
35 |
-
if ( ! defined( 'WP_PLUGIN_URL' ) )
|
36 |
-
define( 'WP_PLUGIN_URL', WP_CONTENT_URL. '/plugins' );
|
37 |
-
if ( ! defined( 'WP_PLUGIN_DIR' ) )
|
38 |
-
define( 'WP_PLUGIN_DIR', WP_CONTENT_DIR . '/plugins' );
|
39 |
-
|
40 |
-
define('BLC_CHECKING', 1);
|
41 |
-
define('BLC_TIMEOUT', 2);
|
42 |
-
|
43 |
/*
|
44 |
//FirePHP for debugging
|
45 |
if ( !class_exists('FB') ) {
|
@@ -51,2156 +25,67 @@ if ( !class_exists('FB') ) {
|
|
51 |
//to uncomment : \/\/(\s*FB::) -> $1
|
52 |
//*/
|
53 |
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
var $myfile=''; //should be removed
|
64 |
-
var $myfolder=''; //should be removed
|
65 |
-
var $mybasename=''; //should be removed
|
66 |
-
var $siteurl; //should be removed
|
67 |
-
var $defaults;
|
68 |
-
var $db_version = 1;
|
69 |
-
|
70 |
-
var $execution_start_time; //Used for a simple internal execution timer in start_timer()/execution_time()
|
71 |
-
var $lockfile_handle = null;
|
72 |
-
|
73 |
-
function ws_broken_link_checker() {
|
74 |
-
global $wpdb;
|
75 |
-
|
76 |
-
//set default options
|
77 |
-
$this->defaults = array(
|
78 |
-
'max_execution_time' => 5*60, //How long the worker instance may run, at most.
|
79 |
-
'check_threshold' => 72, //Check each link every 72 hours.
|
80 |
-
'mark_broken_links' => true, //Whether to add the broken_link class to broken links in posts.
|
81 |
-
'broken_link_css' => ".broken_link, a.broken_link {\n\ttext-decoration: line-through;\n}",
|
82 |
-
'exclusion_list' => array(), //Links that contain a substring listed in this array won't be checked.
|
83 |
-
'recheck_count' => 3, //[Internal] How many times a broken link should be re-checked (slightly buggy)
|
84 |
-
|
85 |
-
//These three are currently ignored. Everything is checked by default.
|
86 |
-
'check_posts' => true,
|
87 |
-
'check_custom_fields' => true,
|
88 |
-
'check_blogroll' => true,
|
89 |
-
|
90 |
-
'custom_fields' => array(), //List of custom fields that can contain URLs and should be checked.
|
91 |
-
|
92 |
-
'autoexpand_widget' => true, //Autoexpand the Dashboard widget if broken links are detected
|
93 |
-
|
94 |
-
'need_resynch' => false, //[Internal flag]
|
95 |
-
|
96 |
-
'current_db_version' => 0, //The current version of the plugin's tables
|
97 |
-
);
|
98 |
-
|
99 |
-
$this->load_options();
|
100 |
-
|
101 |
-
$this->siteurl = get_option('siteurl');
|
102 |
-
|
103 |
-
$my_file = str_replace('\\', '/',__FILE__);
|
104 |
-
$my_file = preg_replace('/^.*wp-content[\\\\\/]plugins[\\\\\/]/', '', $my_file);
|
105 |
-
add_action('activate_' . plugin_basename(__FILE__), array(&$this,'activation'));
|
106 |
-
$this->myfile=$my_file;
|
107 |
-
$this->myfolder=basename(dirname(__FILE__));
|
108 |
-
$this->mybasename=plugin_basename(__FILE__);
|
109 |
-
|
110 |
-
add_action('admin_menu', array(&$this,'admin_menu'));
|
111 |
-
|
112 |
-
//These hooks update the plugin's internal records when posts are added, deleted or modified.
|
113 |
-
add_action('delete_post', array(&$this,'post_deleted'));
|
114 |
-
add_action('save_post', array(&$this,'post_saved'));
|
115 |
-
|
116 |
-
//These do the same for (blogroll) links.
|
117 |
-
add_action('add_link', array(&$this,'hook_add_link'));
|
118 |
-
add_action('edit_link', array(&$this,'hook_edit_link'));
|
119 |
-
add_action('delete_link', array(&$this,'hook_delete_link'));
|
120 |
-
|
121 |
-
add_action('admin_footer', array(&$this,'admin_footer'));
|
122 |
-
add_action('admin_print_scripts', array(&$this,'admin_print_scripts'));
|
123 |
-
//The dashboard widget
|
124 |
-
add_action('wp_dashboard_setup', array(&$this, 'hook_wp_dashboard_setup'));
|
125 |
-
|
126 |
-
if ( $this->options['mark_broken_links'] ){
|
127 |
-
add_filter( 'the_content', array(&$this,'the_content') );
|
128 |
-
if ( !empty($this->options['broken_link_css']) ){
|
129 |
-
add_action( 'wp_head', array(&$this,'header_css') );
|
130 |
-
}
|
131 |
-
}
|
132 |
-
|
133 |
-
//AJAXy hooks
|
134 |
-
add_action( 'wp_ajax_blc_full_status', array(&$this,'ajax_full_status') );
|
135 |
-
add_action( 'wp_ajax_blc_dashboard_status', array(&$this,'ajax_dashboard_status') );
|
136 |
-
add_action( 'wp_ajax_blc_work', array(&$this,'ajax_work') );
|
137 |
-
add_action( 'wp_ajax_blc_discard', array(&$this,'ajax_discard') );
|
138 |
-
add_action( 'wp_ajax_blc_edit', array(&$this,'ajax_edit') );
|
139 |
-
add_action( 'wp_ajax_blc_link_details', array(&$this,'ajax_link_details') );
|
140 |
-
add_action( 'wp_ajax_blc_exclude_link', array(&$this,'ajax_exclude_link') );
|
141 |
-
add_action( 'wp_ajax_blc_unlink', array(&$this,'ajax_unlink') );
|
142 |
-
}
|
143 |
-
|
144 |
-
function admin_footer(){
|
145 |
-
?>
|
146 |
-
<!-- wsblc admin footer -->
|
147 |
-
<div id='wsblc_updater_div'></div>
|
148 |
-
<script type='text/javascript'>
|
149 |
-
(function($){
|
150 |
-
|
151 |
-
function blcDoWork(){
|
152 |
-
$.post(
|
153 |
-
"<?php bloginfo( 'wpurl' ); ?>/wp-admin/admin-ajax.php",
|
154 |
-
{
|
155 |
-
'action' : 'blc_work'
|
156 |
-
},
|
157 |
-
function (data, textStatus){
|
158 |
-
|
159 |
-
}
|
160 |
-
);
|
161 |
-
}
|
162 |
-
//Call it the first time
|
163 |
-
blcDoWork();
|
164 |
-
|
165 |
-
//Then call it periodically every X seconds
|
166 |
-
setInterval(blcDoWork, <?php echo (intval($this->options['max_execution_time']) + 1 )*1000; ?>);
|
167 |
-
|
168 |
-
})(jQuery);
|
169 |
-
</script>
|
170 |
-
<!-- /wsblc admin footer -->
|
171 |
-
<?php
|
172 |
-
}
|
173 |
-
|
174 |
-
function header_css(){
|
175 |
-
echo '<style type="text/css">',$this->options['broken_link_css'],'</style>';
|
176 |
-
}
|
177 |
-
|
178 |
-
function the_content($content){
|
179 |
-
global $post, $wpdb;
|
180 |
-
if ( empty($post) ) return $content;
|
181 |
-
|
182 |
-
$q = "
|
183 |
-
SELECT instances.link_text, links.*
|
184 |
-
|
185 |
-
FROM {$wpdb->prefix}blc_instances AS instances, {$wpdb->prefix}blc_links AS links
|
186 |
-
|
187 |
-
WHERE
|
188 |
-
instances.source_id = %d
|
189 |
-
AND instances.source_type = 'post'
|
190 |
-
AND instances.instance_type = 'link'
|
191 |
-
|
192 |
-
AND instances.link_id = links.link_id
|
193 |
-
AND links.check_count > 0
|
194 |
-
AND ( links.http_code < 200 OR links.http_code >= 400 OR links.timeout = 1 )";
|
195 |
-
|
196 |
-
$rows = $wpdb->get_results( $wpdb->prepare( $q, $post->ID ), ARRAY_A );
|
197 |
-
if( $rows ){
|
198 |
-
$this->links_to_remove = array();
|
199 |
-
foreach($rows as $row){
|
200 |
-
$this->links_to_remove[$row['url']] = $row;
|
201 |
-
}
|
202 |
-
$content = preg_replace_callback( blcUtility::link_pattern(), array(&$this,'mark_broken_links'), $content );
|
203 |
-
};
|
204 |
-
|
205 |
-
return $content;
|
206 |
-
}
|
207 |
-
|
208 |
-
function mark_broken_links($matches){
|
209 |
-
//TODO:Tooltip-style popups with more info
|
210 |
-
$url = blcUtility::normalize_url( html_entity_decode( $matches[3] ) );
|
211 |
-
if( isset( $this->links_to_remove[$url] ) ){
|
212 |
-
return $matches[1].$matches[2].$matches[3].$matches[2].' class="broken_link" '.$matches[4].
|
213 |
-
$matches[5].$matches[6];
|
214 |
-
} else {
|
215 |
-
return $matches[0];
|
216 |
-
}
|
217 |
-
}
|
218 |
-
|
219 |
-
|
220 |
-
function is_excluded($url){
|
221 |
-
if (!is_array($this->options['exclusion_list'])) return false;
|
222 |
-
foreach($this->options['exclusion_list'] as $excluded_word){
|
223 |
-
if (stristr($url, $excluded_word)){
|
224 |
-
return true;
|
225 |
-
}
|
226 |
-
}
|
227 |
-
return false;
|
228 |
-
}
|
229 |
-
|
230 |
-
function dashboard_widget(){
|
231 |
-
?>
|
232 |
-
<p id='wsblc_activity_box'>Loading...</p>
|
233 |
-
<script type='text/javascript'>
|
234 |
-
jQuery(function($){
|
235 |
-
var blc_was_autoexpanded = false;
|
236 |
-
|
237 |
-
function blcDashboardStatus(){
|
238 |
-
$.getJSON(
|
239 |
-
"<?php bloginfo( 'wpurl' ); ?>/wp-admin/admin-ajax.php",
|
240 |
-
{
|
241 |
-
'action' : 'blc_dashboard_status'
|
242 |
-
},
|
243 |
-
function (data, textStatus){
|
244 |
-
if ( data && ( typeof(data.text) != 'undefined' ) ) {
|
245 |
-
$('#wsblc_activity_box').html(data.text);
|
246 |
-
<?php if ( $this->options['autoexpand_widget'] ) { ?>
|
247 |
-
//Expand the widget if there are broken links.
|
248 |
-
//Do this only once per pageload so as not to annoy the user.
|
249 |
-
if ( !blc_was_autoexpanded && ( data.status.broken_links > 0 ) ){
|
250 |
-
$('#blc_dashboard_widget.postbox').removeClass('closed');
|
251 |
-
blc_was_autoexpanded = true;
|
252 |
-
};
|
253 |
-
<?php } ?>
|
254 |
-
} else {
|
255 |
-
$('#wsblc_activity_box').html('[ Network error ]');
|
256 |
-
}
|
257 |
-
|
258 |
-
setTimeout( blcDashboardStatus, 120*1000 ); //...update every two minutes
|
259 |
-
}
|
260 |
-
);
|
261 |
-
}
|
262 |
-
|
263 |
-
blcDashboardStatus();//Call it the first time
|
264 |
-
|
265 |
-
});
|
266 |
-
</script>
|
267 |
-
<?php
|
268 |
-
}
|
269 |
-
|
270 |
-
function dashboard_widget_control( $widget_id, $form_inputs = array() ){
|
271 |
-
if ( 'POST' == $_SERVER['REQUEST_METHOD'] && 'blc_dashboard_widget' == $_POST['widget_id'] ) {
|
272 |
-
//It appears $form_inputs isn't used in the current WP version, so lets just use $_POST
|
273 |
-
$this->options['autoexpand_widget'] = !empty($_POST['blc-autoexpand']);
|
274 |
-
$this->save_options();
|
275 |
-
}
|
276 |
-
|
277 |
-
?>
|
278 |
-
<p><label for="blc-autoexpand">
|
279 |
-
<input id="blc-autoexpand" name="blc-autoexpand" type="checkbox" value="1" <?php if ( $this->options['autoexpand_widget'] ) echo 'checked="checked"'; ?> />
|
280 |
-
Automatically expand the widget if broken links have been detected
|
281 |
-
</label></p>
|
282 |
-
<?php
|
283 |
-
}
|
284 |
-
|
285 |
-
function admin_print_scripts(){
|
286 |
-
//jQuery is used for AJAX and effects
|
287 |
-
wp_enqueue_script('jquery');
|
288 |
-
wp_enqueue_script('jquery-ui-core');
|
289 |
-
}
|
290 |
-
|
291 |
-
/**
|
292 |
-
* ws_broken_link_checker::post_deleted()
|
293 |
-
* A hook for post_deleted. Remove link instances associated with that post.
|
294 |
-
*
|
295 |
-
* @param int $post_id
|
296 |
-
* @return void
|
297 |
-
*/
|
298 |
-
function post_deleted($post_id){
|
299 |
-
global $wpdb;
|
300 |
-
|
301 |
-
//FB::log($post_id, "Post deleted");
|
302 |
-
//Remove this post's instances
|
303 |
-
$q = "DELETE FROM {$wpdb->prefix}blc_instances
|
304 |
-
WHERE source_id = %d AND (source_type = 'post' OR source_type='custom_field')";
|
305 |
-
$q = $wpdb->prepare($q, intval($post_id) );
|
306 |
-
|
307 |
-
//FB::log($q, 'Executing query');
|
308 |
-
|
309 |
-
if ( $wpdb->query( $q ) === false ){
|
310 |
-
//FB::error($wpdb->last_error, "Database error");
|
311 |
-
}
|
312 |
-
|
313 |
-
//Remove the synch record
|
314 |
-
$q = "DELETE FROM {$wpdb->prefix}blc_synch
|
315 |
-
WHERE source_id = %d AND source_type = 'post'";
|
316 |
-
$wpdb->query( $wpdb->prepare($q, intval($post_id)) );
|
317 |
-
|
318 |
-
//Remove any dangling link records
|
319 |
-
$this->cleanup_links();
|
320 |
-
}
|
321 |
-
|
322 |
-
function post_saved($post_id){
|
323 |
-
global $wpdb;
|
324 |
-
|
325 |
-
$post = get_post($post_id);
|
326 |
-
//Only check links in posts, not revisions and attachments
|
327 |
-
if ( ($post->post_type != 'post') && ($post->post_type != 'page') ) return null;
|
328 |
-
//Only check published posts
|
329 |
-
if ( $post->post_status != 'publish' ) return null;
|
330 |
-
|
331 |
-
$this->mark_unsynched( $post_id, 'post' );
|
332 |
-
}
|
333 |
-
|
334 |
-
function initiate_recheck(){
|
335 |
-
global $wpdb;
|
336 |
-
|
337 |
-
//Delete all discovered instances
|
338 |
-
$wpdb->query("TRUNCATE {$wpdb->prefix}blc_instances");
|
339 |
-
|
340 |
-
//Delete all discovered links
|
341 |
-
$wpdb->query("TRUNCATE {$wpdb->prefix}blc_links");
|
342 |
-
|
343 |
-
//Mark all posts, custom fields and bookmarks for processing.
|
344 |
-
$this->resynch();
|
345 |
-
}
|
346 |
-
|
347 |
-
function resynch(){
|
348 |
-
global $wpdb;
|
349 |
-
|
350 |
-
//Drop all synchronization records
|
351 |
-
$wpdb->query("TRUNCATE {$wpdb->prefix}blc_synch");
|
352 |
-
|
353 |
-
|
354 |
-
//Create new synchronization records for posts
|
355 |
-
$q = "INSERT INTO {$wpdb->prefix}blc_synch(source_id, source_type, synched)
|
356 |
-
SELECT id, 'post', 0
|
357 |
-
FROM {$wpdb->posts}
|
358 |
-
WHERE
|
359 |
-
{$wpdb->posts}.post_status = 'publish'
|
360 |
-
AND {$wpdb->posts}.post_type IN ('post', 'page')";
|
361 |
-
$wpdb->query( $q );
|
362 |
-
|
363 |
-
//Create new synchronization records for bookmarks (the blogroll)
|
364 |
-
$q = "INSERT INTO {$wpdb->prefix}blc_synch(source_id, source_type, synched)
|
365 |
-
SELECT link_id, 'blogroll', 0
|
366 |
-
FROM {$wpdb->links}
|
367 |
-
WHERE 1";
|
368 |
-
$wpdb->query( $q );
|
369 |
-
|
370 |
-
//Delete invalid instances
|
371 |
-
$this->cleanup_instances();
|
372 |
-
//Delete orphaned links
|
373 |
-
$this->cleanup_links();
|
374 |
-
|
375 |
-
$this->options['need_resynch'] = true;
|
376 |
-
$this->save_options();
|
377 |
-
}
|
378 |
-
|
379 |
-
function mark_unsynched( $source_id, $source_type ){
|
380 |
-
global $wpdb;
|
381 |
-
|
382 |
-
$q = "REPLACE INTO {$wpdb->prefix}blc_synch( source_id, source_type, synched, last_synch)
|
383 |
-
VALUES( %d, %s, %d, NOW() )";
|
384 |
-
$rez = $wpdb->query( $wpdb->prepare( $q, $source_id, $source_type, 0 ) );
|
385 |
-
|
386 |
-
if ( !$this->options['need_resynch'] ){
|
387 |
-
$this->options['need_resynch'] = true;
|
388 |
-
$this->save_options();
|
389 |
-
}
|
390 |
-
|
391 |
-
return $rez;
|
392 |
-
}
|
393 |
-
|
394 |
-
function mark_synched( $source_id, $source_type ){
|
395 |
-
global $wpdb;
|
396 |
-
//FB::log("Marking $source_type $source_id as synched.");
|
397 |
-
$q = "REPLACE INTO {$wpdb->prefix}blc_synch( source_id, source_type, synched, last_synch)
|
398 |
-
VALUES( %d, %s, %d, NOW() )";
|
399 |
-
return $wpdb->query( $wpdb->prepare( $q, $source_id, $source_type, 1 ) );
|
400 |
-
}
|
401 |
-
|
402 |
-
function activation(){
|
403 |
-
//Prepare the database.
|
404 |
-
$this->upgrade_database();
|
405 |
-
|
406 |
-
//Clear the instance table and mark all posts and other parse-able objects as unsynchronized.
|
407 |
-
$this->resynch();
|
408 |
-
|
409 |
-
//Save the default options.
|
410 |
-
$this->save_options();
|
411 |
-
}
|
412 |
-
|
413 |
-
/**
|
414 |
-
* ws_broken_link_checker::upgrade_database()
|
415 |
-
* Create and/or upgrade database tables
|
416 |
-
*
|
417 |
-
* @return void
|
418 |
-
*/
|
419 |
-
function upgrade_database(){
|
420 |
-
global $wpdb;
|
421 |
-
|
422 |
-
//Do we need to upgrade?
|
423 |
-
if ( $this->db_version == $this->options['current_db_version'] ) return;
|
424 |
-
|
425 |
-
//Delete tables used by older versions of the plugin
|
426 |
-
$rez = $wpdb->query( "DROP TABLE IF EXISTS {$wpdb->prefix}blc_linkdata, {$wpdb->prefix}blc_postdata" );
|
427 |
-
if ( $rez === false ){
|
428 |
-
//FB::error($wpdb->last_error, "Database error");
|
429 |
-
return false;
|
430 |
-
}
|
431 |
-
|
432 |
-
require_once (ABSPATH . 'wp-admin/includes/upgrade.php');
|
433 |
-
|
434 |
-
//Create the link table if it doesn't exist yet.
|
435 |
-
$q = "CREATE TABLE {$wpdb->prefix}blc_links (
|
436 |
-
link_id int(20) unsigned NOT NULL auto_increment,
|
437 |
-
url text CHARACTER SET latin1 COLLATE latin1_general_cs NOT NULL,
|
438 |
-
last_check datetime NOT NULL default '0000-00-00 00:00:00',
|
439 |
-
check_count int(2) unsigned NOT NULL default '0',
|
440 |
-
final_url text CHARACTER SET latin1 COLLATE latin1_general_cs NOT NULL,
|
441 |
-
redirect_count smallint(5) unsigned NOT NULL,
|
442 |
-
log text NOT NULL,
|
443 |
-
http_code smallint(6) NOT NULL,
|
444 |
-
request_duration float NOT NULL default '0',
|
445 |
-
timeout tinyint(1) unsigned NOT NULL default '0',
|
446 |
-
|
447 |
-
PRIMARY KEY (link_id),
|
448 |
-
KEY url (url(150)),
|
449 |
-
KEY final_url (final_url(150)),
|
450 |
-
KEY http_code (http_code),
|
451 |
-
KEY timeout (timeout)
|
452 |
-
)";
|
453 |
-
dbDelta( $q );
|
454 |
-
|
455 |
-
//Fix URL fields so that they are collated as case-sensitive (this can't be done via dbDelta)
|
456 |
-
$wpdb->query(
|
457 |
-
"ALTER TABLE {$wpdb->prefix}blc_links
|
458 |
-
MODIFY url text CHARACTER SET latin1 COLLATE latin1_general_cs NOT NULL,
|
459 |
-
MODIFY final_url text CHARACTER SET latin1 COLLATE latin1_general_cs NOT NULL"
|
460 |
-
);
|
461 |
-
|
462 |
-
|
463 |
-
//Create the instance table if it doesn't exist yet.
|
464 |
-
$q = "CREATE TABLE {$wpdb->prefix}blc_instances (
|
465 |
-
instance_id int(10) unsigned NOT NULL auto_increment,
|
466 |
-
link_id int(10) unsigned NOT NULL,
|
467 |
-
source_id int(10) unsigned NOT NULL,
|
468 |
-
source_type enum('post','blogroll','custom_field') NOT NULL default 'post',
|
469 |
-
link_text varchar(250) NOT NULL,
|
470 |
-
instance_type enum('link','image') NOT NULL default 'link',
|
471 |
-
|
472 |
-
PRIMARY KEY (instance_id),
|
473 |
-
KEY link_id (link_id),
|
474 |
-
KEY source_id (source_id,source_type)
|
475 |
-
)";
|
476 |
-
$rez = dbDelta($q);
|
477 |
-
|
478 |
-
//....
|
479 |
-
$q = "CREATE TABLE {$wpdb->prefix}blc_synch (
|
480 |
-
source_id int(20) unsigned NOT NULL,
|
481 |
-
source_type enum('post','blogroll') NOT NULL,
|
482 |
-
synched tinyint(3) unsigned NOT NULL,
|
483 |
-
last_synch datetime NOT NULL,
|
484 |
-
PRIMARY KEY (source_id, source_type),
|
485 |
-
KEY synched (synched)
|
486 |
-
)";
|
487 |
-
dbDelta($q);
|
488 |
-
|
489 |
-
$this->options['current_db_version'] = $this->db_version;
|
490 |
-
$this->save_options();
|
491 |
-
}
|
492 |
-
|
493 |
-
function admin_menu(){
|
494 |
-
add_options_page('Link Checker Settings', 'Link Checker', 'manage_options',
|
495 |
-
'link-checker-settings',array(&$this, 'options_page'));
|
496 |
-
if (current_user_can('manage_options'))
|
497 |
-
add_filter('plugin_action_links', array(&$this, 'plugin_action_links'), 10, 2);
|
498 |
-
|
499 |
-
add_management_page('View Broken Links', 'Broken Links', 'edit_others_posts',
|
500 |
-
'view-broken-links',array(&$this, 'links_page'));
|
501 |
-
}
|
502 |
-
|
503 |
-
/**
|
504 |
-
* plugin_action_links()
|
505 |
-
* Handler for the 'plugin_action_links' hook. Adds a "Settings" link to this plugin's entry
|
506 |
-
* on the plugin list.
|
507 |
-
*
|
508 |
-
* @param array $links
|
509 |
-
* @param string $file
|
510 |
-
* @return array
|
511 |
-
*/
|
512 |
-
function plugin_action_links($links, $file) {
|
513 |
-
if ($file == $this->mybasename)
|
514 |
-
$links[] = "<a href='options-general.php?page=link-checker-settings'>" . __('Settings') . "</a>";
|
515 |
-
return $links;
|
516 |
-
}
|
517 |
-
|
518 |
-
function mytruncate($str, $max_length=50){
|
519 |
-
if(strlen($str)<=$max_length) return $str;
|
520 |
-
return (substr($str, 0, $max_length-3).'...');
|
521 |
-
}
|
522 |
-
|
523 |
-
function options_page(){
|
524 |
-
|
525 |
-
if (isset($_GET['recheck']) && ($_GET['recheck'] == 'true')) {
|
526 |
-
$this->initiate_recheck();
|
527 |
-
}
|
528 |
-
if (isset($_GET['updated']) && ($_GET['updated'] == 'true')) {
|
529 |
-
if(isset($_POST['submit'])) {
|
530 |
-
|
531 |
-
$new_execution_time = intval($_POST['max_execution_time']);
|
532 |
-
if( $new_execution_time > 0 ){
|
533 |
-
$this->options['max_execution_time'] = $new_execution_time;
|
534 |
-
}
|
535 |
-
|
536 |
-
$new_check_threshold=intval($_POST['check_threshold']);
|
537 |
-
if( $new_check_threshold > 0 ){
|
538 |
-
$this->options['check_threshold'] = $new_check_threshold;
|
539 |
-
}
|
540 |
-
|
541 |
-
$this->options['mark_broken_links'] = !empty($_POST['mark_broken_links']);
|
542 |
-
$new_broken_link_css = trim($_POST['broken_link_css']);
|
543 |
-
$this->options['broken_link_css'] = $new_broken_link_css;
|
544 |
-
|
545 |
-
$this->options['exclusion_list']=array_filter( preg_split( '/[\s\r\n]+/',
|
546 |
-
$_POST['exclusion_list'], -1, PREG_SPLIT_NO_EMPTY ) );
|
547 |
-
//TODO: Maybe update affected links when exclusion list changes (expensive).
|
548 |
-
|
549 |
-
|
550 |
-
$new_custom_fields = array_filter( preg_split( '/[\s\r\n]+/',
|
551 |
-
$_POST['blc_custom_fields'], -1, PREG_SPLIT_NO_EMPTY ) );
|
552 |
-
$diff1 = array_diff( $new_custom_fields, $this->options['custom_fields'] );
|
553 |
-
$diff2 = array_diff( $this->options['custom_fields'], $new_custom_fields );
|
554 |
-
$this->options['custom_fields'] = $new_custom_fields;
|
555 |
-
|
556 |
-
$this->save_options();
|
557 |
-
|
558 |
-
/*
|
559 |
-
If the list of custom fields was modified then we MUST resynchronize or
|
560 |
-
custom fields linked with existing posts may not be detected. This is somewhat
|
561 |
-
inefficient.
|
562 |
-
*/
|
563 |
-
if ( ( count($diff1) > 0 ) || ( count($diff2) > 0 ) ){
|
564 |
-
$this->resynch();
|
565 |
-
}
|
566 |
-
}
|
567 |
-
|
568 |
-
}
|
569 |
-
|
570 |
-
?>
|
571 |
-
<div class="wrap"><h2>Broken Link Checker Options</h2>
|
572 |
-
|
573 |
-
<form name="link_checker_options" method="post" action="<?php echo basename($_SERVER['PHP_SELF']); ?>?page=link-checker-settings&updated=true">
|
574 |
-
|
575 |
-
<table class="form-table">
|
576 |
-
|
577 |
-
<tr valign="top">
|
578 |
-
<th scope="row">Status</th>
|
579 |
-
<td>
|
580 |
-
|
581 |
-
|
582 |
-
<div id='wsblc_full_status'>
|
583 |
-
<br/><br/><br/>
|
584 |
-
</div>
|
585 |
-
<script type='text/javascript'>
|
586 |
-
(function($){
|
587 |
-
|
588 |
-
function blcUpdateStatus(){
|
589 |
-
$.getJSON(
|
590 |
-
"<?php bloginfo( 'wpurl' ); ?>/wp-admin/admin-ajax.php",
|
591 |
-
{
|
592 |
-
'action' : 'blc_full_status'
|
593 |
-
},
|
594 |
-
function (data, textStatus){
|
595 |
-
if ( data && ( typeof(data['text']) != 'undefined' ) ){
|
596 |
-
$('#wsblc_full_status').html(data.text);
|
597 |
-
} else {
|
598 |
-
$('#wsblc_full_status').html('[ Network error ]');
|
599 |
-
}
|
600 |
-
|
601 |
-
setTimeout(blcUpdateStatus, 10000); //...update every 10 seconds
|
602 |
-
}
|
603 |
-
);
|
604 |
-
}
|
605 |
-
blcUpdateStatus();//Call it the first time
|
606 |
-
|
607 |
-
})(jQuery);
|
608 |
-
</script>
|
609 |
-
<?php //JHS: Recheck all posts link: ?>
|
610 |
-
<p><input class="button" type="button" name="recheckbutton" value="Re-check all pages" onclick="location.replace('<?php echo basename($_SERVER['PHP_SELF']); ?>?page=link-checker-settings&recheck=true')" /></p>
|
611 |
-
</td>
|
612 |
-
</tr>
|
613 |
-
|
614 |
-
<tr valign="top">
|
615 |
-
<th scope="row">Check each link</th>
|
616 |
-
<td>
|
617 |
-
|
618 |
-
Every <input type="text" name="check_threshold" id="check_threshold"
|
619 |
-
value="<?php echo $this->options['check_threshold']; ?>" size='5' maxlength='3'/>
|
620 |
-
hours
|
621 |
-
<br/>
|
622 |
-
<span class="description">
|
623 |
-
Existing links will be checked this often. New links will usually be checked ASAP.
|
624 |
-
</span>
|
625 |
-
|
626 |
-
</td>
|
627 |
-
</tr>
|
628 |
-
|
629 |
-
<tr valign="top">
|
630 |
-
<th scope="row">Broken link CSS</th>
|
631 |
-
<td>
|
632 |
-
<input type="checkbox" name="mark_broken_links" id="mark_broken_links"
|
633 |
-
<?php if ($this->options['mark_broken_links']) echo ' checked="checked"'; ?>/>
|
634 |
-
<label for='mark_broken_links'>Apply <em>class="broken_link"</em> to broken links</label><br/>
|
635 |
-
<textarea name="broken_link_css" id="broken_link_css" cols='45' rows='4'/><?php
|
636 |
-
if( isset($this->options['broken_link_css']) )
|
637 |
-
echo $this->options['broken_link_css'];
|
638 |
-
?></textarea>
|
639 |
-
|
640 |
-
</td>
|
641 |
-
</tr>
|
642 |
-
|
643 |
-
<tr valign="top">
|
644 |
-
<th scope="row">Exclusion list</th>
|
645 |
-
<td>Don't check links where the URL contains any of these words (one per line) :<br/>
|
646 |
-
<textarea name="exclusion_list" id="exclusion_list" cols='45' rows='4' wrap='off'/><?php
|
647 |
-
if( isset($this->options['exclusion_list']) )
|
648 |
-
echo implode("\n", $this->options['exclusion_list']);
|
649 |
-
?></textarea>
|
650 |
-
|
651 |
-
</td>
|
652 |
-
</tr>
|
653 |
-
|
654 |
-
<tr valign="top">
|
655 |
-
<th scope="row">Custom fields</th>
|
656 |
-
<td>Check URLs entered in these custom fields (one per line) : <br/>
|
657 |
-
<textarea name="blc_custom_fields" id="blc_custom_fields" cols='45' rows='4' /><?php
|
658 |
-
if( isset($this->options['custom_fields']) )
|
659 |
-
echo implode("\n", $this->options['custom_fields']);
|
660 |
-
?></textarea>
|
661 |
-
|
662 |
-
</td>
|
663 |
-
</tr>
|
664 |
-
|
665 |
-
<tr valign="top">
|
666 |
-
<th scope="row">Max. execution time (advanced)</th>
|
667 |
-
<td>
|
668 |
-
|
669 |
-
<input type="text" name="max_execution_time" id="max_execution_time"
|
670 |
-
value="<?php echo $this->options['max_execution_time']; ?>" size='5' maxlength='3'/>
|
671 |
-
seconds
|
672 |
-
<br/><span class="description">
|
673 |
-
The plugin works by periodically creating a background worker instance that parses your posts looking for links,
|
674 |
-
checks the discovered URLs, and performs other time-consuming tasks. Here you can set for how long, at most,
|
675 |
-
the background instance may run each time before stopping.
|
676 |
-
</span>
|
677 |
-
|
678 |
-
</td>
|
679 |
-
</tr>
|
680 |
-
|
681 |
-
</table>
|
682 |
-
|
683 |
-
<p class="submit"><input type="submit" name="submit" class='button-primary' value="<?php _e('Save Changes') ?>" /></p>
|
684 |
-
</form>
|
685 |
-
</div>
|
686 |
-
<?php
|
687 |
-
}
|
688 |
-
|
689 |
-
function links_page(){
|
690 |
-
global $wpdb;
|
691 |
-
|
692 |
-
//Available filters by link type + the appropriate WHERE expressions
|
693 |
-
$filters = array(
|
694 |
-
'broken' => array(
|
695 |
-
'where_expr' => '( http_code < 200 OR http_code >= 400 OR timeout = 1 ) AND ( check_count > 0 ) AND ( http_code <> ' . BLC_CHECKING . ')',
|
696 |
-
'name' => 'Broken',
|
697 |
-
'heading' => 'Broken Links',
|
698 |
-
'heading_zero' => 'No broken links found'
|
699 |
-
),
|
700 |
-
'redirects' => array(
|
701 |
-
'where_expr' => '( redirect_count > 0 )',
|
702 |
-
'name' => 'Redirects',
|
703 |
-
'heading' => 'Redirected Links',
|
704 |
-
'heading_zero' => 'No redirects found'
|
705 |
-
),
|
706 |
-
|
707 |
-
'all' => array(
|
708 |
-
'where_expr' => '1',
|
709 |
-
'name' => 'All',
|
710 |
-
'heading' => 'Detected Links',
|
711 |
-
'heading_zero' => 'No links found (yet)'
|
712 |
-
),
|
713 |
-
);
|
714 |
-
|
715 |
-
$link_type = isset($_GET['link_type'])?$_GET['link_type']:'broken';
|
716 |
-
if ( !isset($filters[$link_type]) ){
|
717 |
-
$link_type = 'broken';
|
718 |
-
}
|
719 |
-
|
720 |
-
//Get the desired page number (must be > 0)
|
721 |
-
$page = isset($_GET['paged'])?intval($_GET['paged']):'1';
|
722 |
-
if ($page < 1) $page = 1;
|
723 |
-
|
724 |
-
//Links per page [1 - 200]
|
725 |
-
$per_page = isset($_GET['per_page'])?intval($_GET['per_page']):'30';
|
726 |
-
if ($per_page < 1){
|
727 |
-
$per_page = 30;
|
728 |
-
} else if ($per_page > 200){
|
729 |
-
$per_page = 200;
|
730 |
-
}
|
731 |
-
|
732 |
-
//calculate the number of various links
|
733 |
-
foreach ($filters as $filter => $data){
|
734 |
-
$filters[$filter]['count'] = $wpdb->get_var(
|
735 |
-
"SELECT COUNT(*) FROM {$wpdb->prefix}blc_links WHERE ".$data['where_expr'] );
|
736 |
-
}
|
737 |
-
$current_filter = $filters[$link_type];
|
738 |
-
$max_pages = ceil($current_filter['count'] / $per_page);
|
739 |
-
|
740 |
-
|
741 |
-
//Select the required links + 1 instance per link.
|
742 |
-
//Note : The query might be somewhat inefficient, but I can't think of any better way to do this.
|
743 |
-
$q = "SELECT
|
744 |
-
links.*,
|
745 |
-
instances.instance_id, instances.source_id, instances.source_type,
|
746 |
-
instances.link_text, instances.instance_type,
|
747 |
-
COUNT(*) as instance_count,
|
748 |
-
posts.post_title,
|
749 |
-
posts.post_date
|
750 |
-
|
751 |
-
FROM
|
752 |
-
{$wpdb->prefix}blc_links AS links,
|
753 |
-
{$wpdb->prefix}blc_instances as instances LEFT JOIN {$wpdb->posts} as posts ON instances.source_id = posts.ID
|
754 |
-
|
755 |
-
WHERE
|
756 |
-
links.link_id = instances.link_id
|
757 |
-
AND ". $current_filter['where_expr'] ."
|
758 |
-
|
759 |
-
GROUP BY links.link_id
|
760 |
-
LIMIT ".( ($page-1) * $per_page ).", $per_page";
|
761 |
-
//echo "<pre>$q</pre>";
|
762 |
-
|
763 |
-
$links = $wpdb->get_results($q, ARRAY_A);
|
764 |
-
if ($links){
|
765 |
-
/*
|
766 |
-
echo '<pre>';
|
767 |
-
print_r($links);
|
768 |
-
echo '</pre>';
|
769 |
-
//*/
|
770 |
-
} else {
|
771 |
-
echo $wpdb->last_error;
|
772 |
-
}
|
773 |
-
?>
|
774 |
-
|
775 |
-
<script type='text/javascript'>
|
776 |
-
var blc_current_filter = '<?php echo $link_type; ?>';
|
777 |
-
</script>
|
778 |
-
|
779 |
-
<style type='text/css'>
|
780 |
-
.blc-link-editor {
|
781 |
-
font-size: 1em;
|
782 |
-
width: 95%;
|
783 |
-
}
|
784 |
-
|
785 |
-
.blc-excluded-link {
|
786 |
-
background-color: #E2E2E2;
|
787 |
-
}
|
788 |
-
|
789 |
-
.blc-small-image {
|
790 |
-
display : block;
|
791 |
-
float: left;
|
792 |
-
padding-top: 2px;
|
793 |
-
margin-right: 3px;
|
794 |
-
}
|
795 |
-
</style>
|
796 |
-
|
797 |
-
<div class="wrap">
|
798 |
-
<h2><?php
|
799 |
-
//Output a header matching the current filter
|
800 |
-
if ( $current_filter['count'] > 0 ){
|
801 |
-
echo "<span class='current-link-count'>{$current_filter[count]}</span> " . $current_filter['heading'];
|
802 |
-
} else {
|
803 |
-
echo "<span class='current-link-count'></span>" . $current_filter['heading_zero'];
|
804 |
-
}
|
805 |
-
?></h2>
|
806 |
-
|
807 |
-
<div class='tablenav'>
|
808 |
-
<ul class="subsubsub">
|
809 |
-
<?php
|
810 |
-
//Construct a submenu of filter types
|
811 |
-
$items = array();
|
812 |
-
foreach ($filters as $filter => $data){
|
813 |
-
$class = $number_class = '';
|
814 |
-
|
815 |
-
if ( $link_type == $filter ) $class = 'class="current"';
|
816 |
-
if ( $link_type == $filter ) $number_class = 'current-link-count';
|
817 |
-
|
818 |
-
$items[] = "<li><a href='tools.php?page=view-broken-links&link_type=$filter' $class>
|
819 |
-
{$data[name]}</a> <span class='count'>(<span class='$number_class'>{$data[count]}</span>)</span>";
|
820 |
-
}
|
821 |
-
echo implode(' |</li>', $items);
|
822 |
-
unset($items);
|
823 |
-
?>
|
824 |
-
</ul>
|
825 |
-
<?php
|
826 |
-
//Display pagination links
|
827 |
-
$page_links = paginate_links( array(
|
828 |
-
'base' => add_query_arg( 'paged', '%#%' ),
|
829 |
-
'format' => '',
|
830 |
-
'prev_text' => __('«'),
|
831 |
-
'next_text' => __('»'),
|
832 |
-
'total' => $max_pages,
|
833 |
-
'current' => $page
|
834 |
-
));
|
835 |
-
|
836 |
-
if ( $page_links ) {
|
837 |
-
echo '<div class="tablenav-pages">';
|
838 |
-
$page_links_text = sprintf( '<span class="displaying-num">' . __( 'Displaying %s–%s of <span class="current-link-count">%s</span>' ) . '</span>%s',
|
839 |
-
number_format_i18n( ( $page - 1 ) * $per_page + 1 ),
|
840 |
-
number_format_i18n( min( $page * $per_page, count($links) ) ),
|
841 |
-
number_format_i18n( $current_filter['count'] ),
|
842 |
-
$page_links
|
843 |
-
);
|
844 |
-
echo $page_links_text;
|
845 |
-
echo '</div>';
|
846 |
-
}
|
847 |
-
?>
|
848 |
-
|
849 |
-
</div>
|
850 |
-
|
851 |
-
|
852 |
-
|
853 |
-
<?php
|
854 |
-
if($links && (count($links)>0)){
|
855 |
-
?>
|
856 |
-
<table class="widefat">
|
857 |
-
<thead>
|
858 |
-
<tr>
|
859 |
-
|
860 |
-
<th scope="col">Source
|
861 |
-
</th>
|
862 |
-
<th scope="col">Link Text</th>
|
863 |
-
<th scope="col">URL</th>
|
864 |
-
|
865 |
-
<?php if ( 'broken' == $link_type ) { ?>
|
866 |
-
<th scope="col"> </th>
|
867 |
-
<?php } ?>
|
868 |
-
|
869 |
-
</tr>
|
870 |
-
</thead>
|
871 |
-
<tbody id="the-list">
|
872 |
-
<?php
|
873 |
-
$rowclass = ''; $rownum = 0;
|
874 |
-
foreach ($links as $link) {
|
875 |
-
$rownum++;
|
876 |
-
|
877 |
-
$rowclass = 'alternate' == $rowclass ? '' : 'alternate';
|
878 |
-
$excluded = $this->is_excluded( $link['url'] );
|
879 |
-
if ( $excluded ) $rowclass .= ' blc-excluded-link';
|
880 |
-
|
881 |
-
?>
|
882 |
-
<tr id='<?php echo "blc-row-$rownum"; ?>' class='blc-row <?php echo $rowclass; ?>'>
|
883 |
-
<td class='post-title column-title'>
|
884 |
-
<span class='blc-link-id' style='display:none;'><?php echo $link['link_id']; ?></span>
|
885 |
-
<?php
|
886 |
-
if ( ('post' == $link['source_type']) || ('custom_field' == $link['source_type']) ){
|
887 |
-
|
888 |
-
echo "<a class='row-title' href='post.php?action=edit&post=$link[source_id]' title='Edit this post'>{$link[post_title]}</a>";
|
889 |
-
|
890 |
-
//Output inline action links (copied from edit-post-rows.php)
|
891 |
-
$actions = array();
|
892 |
-
if ( current_user_can('edit_post', $link['source_id']) ) {
|
893 |
-
$actions['edit'] = '<span class="edit"><a href="' . get_edit_post_link($link['source_id'], true) . '" title="' . attribute_escape(__('Edit this post')) . '">' . __('Edit') . '</a>';
|
894 |
-
$actions['delete'] = "<span class='delete'><a class='submitdelete' title='" . attribute_escape(__('Delete this post')) . "' href='" . wp_nonce_url("post.php?action=delete&post=".$link['source_id'], 'delete-post_' . $link['source_id']) . "' onclick=\"if ( confirm('" . js_escape(sprintf( __("You are about to delete the post '%s'\n 'Cancel' to stop, 'OK' to delete."), $link['post_title'] )) . "') ) { return true;}return false;\">" . __('Delete') . "</a>";
|
895 |
-
}
|
896 |
-
$actions['view'] = '<span class="view"><a href="' . get_permalink($link['source_id']) . '" title="' . attribute_escape(sprintf(__('View "%s"'), $link['post_title'])) . '" rel="permalink">' . __('View') . '</a>';
|
897 |
-
echo '<div class="row-actions">';
|
898 |
-
echo implode(' | </span>', $actions);
|
899 |
-
echo '</div>';
|
900 |
-
|
901 |
-
} elseif ( 'blogroll' == $link['source_type'] ) {
|
902 |
-
|
903 |
-
echo "<a class='row-title' href='link.php?action=edit&link_id=$link[source_id]' title='Edit this bookmark'>{$link[link_text]}</a>";
|
904 |
-
|
905 |
-
//Output inline action links
|
906 |
-
$actions = array();
|
907 |
-
if ( current_user_can('manage_links') ) {
|
908 |
-
$actions['edit'] = '<span class="edit"><a href="link.php?action=edit&link_id=' . $link['source_id'] . '" title="' . attribute_escape(__('Edit this bookmark')) . '">' . __('Edit') . '</a>';
|
909 |
-
$actions['delete'] = "<span class='delete'><a class='submitdelete' href='" . wp_nonce_url("link.php?action=delete&link_id={$link[source_id]}", 'delete-bookmark_' . $link['source_id']) . "' onclick=\"if ( confirm('" . js_escape(sprintf( __("You are about to delete this link '%s'\n 'Cancel' to stop, 'OK' to delete."), $link['link_text'])) . "') ) { return true;}return false;\">" . __('Delete') . "</a>";
|
910 |
-
}
|
911 |
-
|
912 |
-
echo '<div class="row-actions">';
|
913 |
-
echo implode(' | </span>', $actions);
|
914 |
-
echo '</div>';
|
915 |
-
|
916 |
-
} elseif ( empty($link['source_type']) ){
|
917 |
-
|
918 |
-
echo "[An orphaned link! This is a bug.]";
|
919 |
-
|
920 |
-
}
|
921 |
-
?>
|
922 |
-
</td>
|
923 |
-
<td class='blc-link-text'><?php
|
924 |
-
if ( 'post' == $link['source_type'] ){
|
925 |
-
|
926 |
-
if ( 'link' == $link['instance_type'] ) {
|
927 |
-
print strip_tags($link['link_text']);
|
928 |
-
} elseif ( 'image' == $link['instance_type'] ){
|
929 |
-
echo "<img src='" . WP_PLUGIN_URL . "/broken-link-checker/images/image.png' class='blc-small-image' alt='Image' title='Image'> Image";
|
930 |
-
} else {
|
931 |
-
echo '[ ??? ]';
|
932 |
-
}
|
933 |
-
|
934 |
-
} elseif ( 'custom_field' == $link['source_type'] ){
|
935 |
-
|
936 |
-
echo "<img src='" . WP_PLUGIN_URL . "/broken-link-checker/images/script_code.png' class='blc-small-image' title='Custom field' alt='Custom field'> ";
|
937 |
-
echo "<code>".$link['link_text']."</code>";
|
938 |
-
|
939 |
-
} elseif ( 'blogroll' == $link['source_type'] ){
|
940 |
-
//echo $link['link_text'];
|
941 |
-
echo "<img src='" . WP_PLUGIN_URL . "/broken-link-checker/images/link.png' class='blc-small-image' title='Bookmark' alt='Bookmark'> Bookmark";
|
942 |
-
}
|
943 |
-
?>
|
944 |
-
</td>
|
945 |
-
<td class='column-url'>
|
946 |
-
<a href='<?php print $link['url']; ?>' target='_blank' class='blc-link-url'>
|
947 |
-
<?php print $this->mytruncate($link['url']); ?></a>
|
948 |
-
<input type='text' id='link-editor-<?php print $rownum; ?>'
|
949 |
-
value='<?php print attribute_escape($link['url']); ?>'
|
950 |
-
class='blc-link-editor' style='display:none' />
|
951 |
-
<?php
|
952 |
-
//Output inline action links for the link/URL
|
953 |
-
$actions = array();
|
954 |
-
|
955 |
-
$actions['details'] = "<span class='view'><a class='blc-details-button' href='javascript:void(0)' title='Show more info about this link'>Details</a>";
|
956 |
-
|
957 |
-
$actions['delete'] = "<span class='delete'><a class='submitdelete blc-unlink-button' title='Remove this link from all posts' ".
|
958 |
-
"id='unlink-button-$rownum' href='javascript:void(0);'>Unlink</a>";
|
959 |
-
|
960 |
-
if ( $excluded ){
|
961 |
-
$actions['exclude'] = "<span class='delete'>Excluded";
|
962 |
-
} else {
|
963 |
-
$actions['exclude'] = "<span class='delete'><a class='submitdelete blc-exclude-button' title='Add this URL to the exclusion list' ".
|
964 |
-
"id='exclude-button-$rownum' href='javascript:void(0);'>Exclude</a>";
|
965 |
-
}
|
966 |
-
|
967 |
-
$actions['edit'] = "<span class='edit'><a href='javascript:void(0)' class='blc-edit-button' title='Edit link URL'>Edit URL</a>";
|
968 |
-
|
969 |
-
echo '<div class="row-actions">';
|
970 |
-
echo implode(' | </span>', $actions);
|
971 |
-
|
972 |
-
echo "<span style='display:none' class='blc-cancel-button-container'> ",
|
973 |
-
"| <a href='javascript:void(0)' class='blc-cancel-button' title='Cancel URL editing'>Cancel</a></span>";
|
974 |
-
|
975 |
-
echo '</div>';
|
976 |
-
?>
|
977 |
-
</td>
|
978 |
-
<?php if ( 'broken' == $link_type ) { ?>
|
979 |
-
<td><a href='javascript:void(0);'
|
980 |
-
id='discard_button-<?php print $rownum; ?>'
|
981 |
-
class='blc-discard-button'
|
982 |
-
title='Remove this message and mark the link as valid'>Discard</a>
|
983 |
-
</td>
|
984 |
-
<?php } ?>
|
985 |
-
</tr>
|
986 |
-
<!-- Link details -->
|
987 |
-
<tr id='<?php print "link-details-$rownum"; ?>' style='display:none;' class='blc-link-details'>
|
988 |
-
<td colspan='4'><?php $this->link_details_row($link); ?></td>
|
989 |
-
</tr><?php
|
990 |
-
}
|
991 |
-
?></tbody></table><?php
|
992 |
-
|
993 |
-
//Also display pagination links at the bottom
|
994 |
-
if ( $page_links ) {
|
995 |
-
echo '<div class="tablenav"><div class="tablenav-pages">';
|
996 |
-
$page_links_text = sprintf( '<span class="displaying-num">' . __( 'Displaying %s–%s of <span class="current-link-count">%s</span>' ) . '</span>%s',
|
997 |
-
number_format_i18n( ( $page - 1 ) * $per_page + 1 ),
|
998 |
-
number_format_i18n( min( $page * $per_page, count($links) ) ),
|
999 |
-
number_format_i18n( $current_filter['count'] ),
|
1000 |
-
$page_links
|
1001 |
-
);
|
1002 |
-
echo $page_links_text;
|
1003 |
-
echo '</div></div>';
|
1004 |
-
}
|
1005 |
-
};
|
1006 |
-
?>
|
1007 |
-
<?php $this->links_page_js(); ?>
|
1008 |
-
</div>
|
1009 |
-
<?php
|
1010 |
-
}
|
1011 |
-
|
1012 |
-
function links_page_js(){
|
1013 |
-
?>
|
1014 |
-
<script type='text/javascript'>
|
1015 |
|
1016 |
-
|
1017 |
-
|
1018 |
-
|
1019 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1020 |
}
|
1021 |
|
1022 |
-
jQuery(function($){
|
1023 |
-
|
1024 |
-
//The discard button - manually mark the link as valid. The link will be checked again later.
|
1025 |
-
$(".blc-discard-button").click(function () {
|
1026 |
-
var me = this;
|
1027 |
-
$(me).html('Wait...');
|
1028 |
-
|
1029 |
-
var link_id = $(me).parents('.blc-row').find('.blc-link-id').html();
|
1030 |
-
|
1031 |
-
$.post(
|
1032 |
-
"<?php bloginfo( 'wpurl' ); ?>/wp-admin/admin-ajax.php",
|
1033 |
-
{
|
1034 |
-
'action' : 'blc_discard',
|
1035 |
-
'link_id' : link_id
|
1036 |
-
},
|
1037 |
-
function (data, textStatus){
|
1038 |
-
if (data == 'OK'){
|
1039 |
-
var master = $(me).parents('.blc-row');
|
1040 |
-
var details = master.next('.blc-link-details');
|
1041 |
-
|
1042 |
-
details.hide();
|
1043 |
-
//Flash the main row green to indicate success, then hide it.
|
1044 |
-
var oldColor = master.css('background-color');
|
1045 |
-
master.animate({ backgroundColor: "#E0FFB3" }, 200).animate({ backgroundColor: oldColor }, 300, function(){
|
1046 |
-
master.hide();
|
1047 |
-
});
|
1048 |
-
|
1049 |
-
alterLinkCounter(-1);
|
1050 |
-
} else {
|
1051 |
-
$(me).html('Discard');
|
1052 |
-
alert(data);
|
1053 |
-
}
|
1054 |
-
}
|
1055 |
-
);
|
1056 |
-
});
|
1057 |
-
|
1058 |
-
//The details button - display/hide detailed info about a link
|
1059 |
-
$(".blc-details-button, .blc-link-text").click(function () {
|
1060 |
-
$(this).parents('.blc-row').next('.blc-link-details').toggle();
|
1061 |
-
});
|
1062 |
-
|
1063 |
-
//The edit button - edit/save the link's URL
|
1064 |
-
$(".blc-edit-button").click(function () {
|
1065 |
-
var edit_button = $(this);
|
1066 |
-
var master = $(edit_button).parents('.blc-row');
|
1067 |
-
var editor = $(master).find('.blc-link-editor');
|
1068 |
-
var url_el = $(master).find('.blc-link-url');
|
1069 |
-
var cancel_button_container = $(master).find('.blc-cancel-button-container');
|
1070 |
-
|
1071 |
-
//Find the current/original URL
|
1072 |
-
var orig_url = url_el.attr('href');
|
1073 |
-
//Find the link ID
|
1074 |
-
var link_id = $(master).find('.blc-link-id').html();
|
1075 |
-
|
1076 |
-
if ( !$(editor).is(':visible') ){
|
1077 |
-
//Begin editing
|
1078 |
-
url_el.hide();
|
1079 |
-
editor.show();
|
1080 |
-
cancel_button_container.show();
|
1081 |
-
editor.focus();
|
1082 |
-
editor.select();
|
1083 |
-
edit_button.html('Save URL');
|
1084 |
-
} else {
|
1085 |
-
editor.hide();
|
1086 |
-
cancel_button_container.hide();
|
1087 |
-
url_el.show();
|
1088 |
-
|
1089 |
-
new_url = editor.val();
|
1090 |
-
|
1091 |
-
if (new_url != orig_url){
|
1092 |
-
//Save the changed link
|
1093 |
-
url_el.html('Saving changes...');
|
1094 |
-
|
1095 |
-
$.getJSON(
|
1096 |
-
"<?php bloginfo( 'wpurl' ); ?>/wp-admin/admin-ajax.php",
|
1097 |
-
{
|
1098 |
-
'action' : 'blc_edit',
|
1099 |
-
'link_id' : link_id,
|
1100 |
-
'new_url' : new_url
|
1101 |
-
},
|
1102 |
-
function (data, textStatus){
|
1103 |
-
var display_url = '';
|
1104 |
-
|
1105 |
-
if ( data && (typeof(data['error']) != 'undefined') ){
|
1106 |
-
//data.error is an error message
|
1107 |
-
alert(data.error);
|
1108 |
-
display_url = orig_url;
|
1109 |
-
} else {
|
1110 |
-
//data contains info about the performed edit
|
1111 |
-
if ( data.cnt_okay > 0 ){
|
1112 |
-
display_url = new_url;
|
1113 |
-
|
1114 |
-
url_el.attr('href', new_url);
|
1115 |
-
|
1116 |
-
if ( data.cnt_error > 0 ){
|
1117 |
-
var msg = "The link was successfully modifed.";
|
1118 |
-
msg = msg + "\nHowever, "+data.cnt_error+" instances couldn't be edited and still point to the old URL."
|
1119 |
-
alert(msg);
|
1120 |
-
} else {
|
1121 |
-
//Flash the row green to indicate success
|
1122 |
-
var oldColor = master.css('background-color');
|
1123 |
-
master.animate({ backgroundColor: "#E0FFB3" }, 200).animate({ backgroundColor: oldColor }, 300);
|
1124 |
-
|
1125 |
-
//Save the new ID
|
1126 |
-
master.find('.blc-link-id').html(data.new_link_id);
|
1127 |
-
//Load up the new link info (so sue me)
|
1128 |
-
master.next('.blc-link-details').find('td').html('<center>Loading...</center>').load(
|
1129 |
-
"<?php bloginfo( 'wpurl' ); ?>/wp-admin/admin-ajax.php",
|
1130 |
-
{
|
1131 |
-
'action' : 'blc_link_details',
|
1132 |
-
'link_id' : data.new_link_id
|
1133 |
-
}
|
1134 |
-
);
|
1135 |
-
}
|
1136 |
-
} else {
|
1137 |
-
alert("Something went wrong. The plugin failed to edit "+
|
1138 |
-
data.cnt_error + ' instance(s) of this link.');
|
1139 |
-
|
1140 |
-
display_url = orig_url;
|
1141 |
-
}
|
1142 |
-
};
|
1143 |
-
|
1144 |
-
//Shorten the displayed URL if it's > 50 characters
|
1145 |
-
if ( display_url.length > 50 ){
|
1146 |
-
display_url = display_url.substr(0, 47) + '...';
|
1147 |
-
}
|
1148 |
-
url_el.html(display_url);
|
1149 |
-
}
|
1150 |
-
);
|
1151 |
-
|
1152 |
-
} else {
|
1153 |
-
//It's the same URL, so do nothing.
|
1154 |
-
}
|
1155 |
-
edit_button.html('Edit URL');
|
1156 |
-
}
|
1157 |
-
});
|
1158 |
-
|
1159 |
-
$(".blc-cancel-button").click(function () {
|
1160 |
-
var master = $(this).parents('.blc-row');
|
1161 |
-
var url_el = $(master).find('.blc-link-url');
|
1162 |
-
|
1163 |
-
//Hide the cancel button
|
1164 |
-
$(this).parent().hide();
|
1165 |
-
//Show the un-editable URL again
|
1166 |
-
url_el.show();
|
1167 |
-
//reset and hide the editor
|
1168 |
-
master.find('.blc-link-editor').hide().val(url_el.attr('href'));
|
1169 |
-
//Set the edit button to say "Edit URL"
|
1170 |
-
master.find('.blc-edit-button').html('Edit URL');
|
1171 |
-
});
|
1172 |
-
|
1173 |
-
//The unlink button - remove the link/image from all posts, custom fields, etc.
|
1174 |
-
$(".blc-unlink-button").click(function () {
|
1175 |
-
var me = this;
|
1176 |
-
var master = $(me).parents('.blc-row');
|
1177 |
-
$(me).html('Wait...');
|
1178 |
-
|
1179 |
-
var link_id = $(me).parents('.blc-row').find('.blc-link-id').html();
|
1180 |
-
|
1181 |
-
$.post(
|
1182 |
-
"<?php bloginfo( 'wpurl' ); ?>/wp-admin/admin-ajax.php",
|
1183 |
-
{
|
1184 |
-
'action' : 'blc_unlink',
|
1185 |
-
'link_id' : link_id
|
1186 |
-
},
|
1187 |
-
function (data, textStatus){
|
1188 |
-
eval('data = ' + data);
|
1189 |
-
|
1190 |
-
if ( data && ( typeof(data['ok']) != 'undefined') ){
|
1191 |
-
//Hide the details
|
1192 |
-
master.next('.blc-link-details').hide();
|
1193 |
-
//Flash the main row green to indicate success, then hide it.
|
1194 |
-
var oldColor = master.css('background-color');
|
1195 |
-
master.animate({ backgroundColor: "#E0FFB3" }, 200).animate({ backgroundColor: oldColor }, 300, function(){
|
1196 |
-
master.hide();
|
1197 |
-
});
|
1198 |
-
|
1199 |
-
alterLinkCounter(-1);
|
1200 |
-
} else {
|
1201 |
-
$(me).html('Unlink');
|
1202 |
-
//Show the error message
|
1203 |
-
alert(data.error);
|
1204 |
-
}
|
1205 |
-
}
|
1206 |
-
);
|
1207 |
-
});
|
1208 |
-
|
1209 |
-
//The exclude button - Add this link to the exclusion list
|
1210 |
-
$(".blc-exclude-button").click(function () {
|
1211 |
-
var me = this;
|
1212 |
-
var master = $(me).parents('.blc-row');
|
1213 |
-
var details = master.next('.blc-link-details');
|
1214 |
-
$(me).html('Wait...');
|
1215 |
-
|
1216 |
-
var link_id = $(me).parents('.blc-row').find('.blc-link-id').html();
|
1217 |
-
|
1218 |
-
$.post(
|
1219 |
-
"<?php bloginfo( 'wpurl' ); ?>/wp-admin/admin-ajax.php",
|
1220 |
-
{
|
1221 |
-
'action' : 'blc_exclude_link',
|
1222 |
-
'link_id' : link_id
|
1223 |
-
},
|
1224 |
-
function (data, textStatus){
|
1225 |
-
eval('data = ' + data);
|
1226 |
-
|
1227 |
-
if ( data && ( typeof(data['ok']) != 'undefined' ) ){
|
1228 |
-
|
1229 |
-
if ( 'broken' == blc_current_filter ){
|
1230 |
-
//Flash the row green to indicate success, then hide it.
|
1231 |
-
$(me).replaceWith('Excluded');
|
1232 |
-
master.animate({ backgroundColor: "#E0FFB3" }, 200).animate({ backgroundColor: '#E2E2E2' }, 200, function(){
|
1233 |
-
details.hide();
|
1234 |
-
master.hide();
|
1235 |
-
alterLinkCounter(-1);
|
1236 |
-
});
|
1237 |
-
master.addClass('blc-excluded-link');
|
1238 |
-
} else {
|
1239 |
-
//Flash the row green to indicate success and fade to the "excluded link" color
|
1240 |
-
master.animate({ backgroundColor: "#E0FFB3" }, 200).animate({ backgroundColor: '#E2E2E2' }, 300);
|
1241 |
-
master.addClass('blc-excluded-link');
|
1242 |
-
$(me).replaceWith('Excluded');
|
1243 |
-
}
|
1244 |
-
} else {
|
1245 |
-
$(me).html('Exclude');
|
1246 |
-
alert(data.error);
|
1247 |
-
}
|
1248 |
-
}
|
1249 |
-
);
|
1250 |
-
});
|
1251 |
-
|
1252 |
-
});
|
1253 |
-
|
1254 |
-
</script>
|
1255 |
-
<?php
|
1256 |
-
}
|
1257 |
-
|
1258 |
-
function link_details_row($link){
|
1259 |
-
?>
|
1260 |
-
<span id='post_date_full' style='display:none;'><?php
|
1261 |
-
print $link['post_date'];
|
1262 |
-
?></span>
|
1263 |
-
<span id='check_date_full' style='display:none;'><?php
|
1264 |
-
print $link['last_check'];
|
1265 |
-
?></span>
|
1266 |
-
<ol style='list-style-type: none; width: 50%; float: right;'>
|
1267 |
-
<li><strong>Log :</strong>
|
1268 |
-
<span class='blc_log'><?php
|
1269 |
-
print nl2br($link['log']);
|
1270 |
-
?></span></li>
|
1271 |
-
</ol>
|
1272 |
-
|
1273 |
-
<ol style='list-style-type: none; padding-left: 2px;'>
|
1274 |
-
<?php if ( !empty($link['post_date']) ) { ?>
|
1275 |
-
<li><strong>Post published on :</strong>
|
1276 |
-
<span class='post_date'><?php
|
1277 |
-
print strftime("%B %d, %Y",strtotime($link['post_date']));
|
1278 |
-
?></span></li>
|
1279 |
-
<?php } ?>
|
1280 |
-
<li><strong>Link last checked :</strong>
|
1281 |
-
<span class='check_date'><?php
|
1282 |
-
$last_check = strtotime($link['last_check']);
|
1283 |
-
if ( $last_check < strtotime('-10 years') ){
|
1284 |
-
echo 'Never';
|
1285 |
-
} else {
|
1286 |
-
echo strftime( "%B %d, %Y", $last_check );
|
1287 |
-
}
|
1288 |
-
?></span></li>
|
1289 |
-
|
1290 |
-
<li><strong>HTTP code :</strong>
|
1291 |
-
<span class='http_code'><?php
|
1292 |
-
print $link['http_code'];
|
1293 |
-
?></span></li>
|
1294 |
-
|
1295 |
-
<li><strong>Response time :</strong>
|
1296 |
-
<span class='request_duration'><?php
|
1297 |
-
printf('%2.3f seconds', $link['request_duration']);
|
1298 |
-
?></span></li>
|
1299 |
-
|
1300 |
-
<li><strong>Final URL :</strong>
|
1301 |
-
<span class='final_url'><?php
|
1302 |
-
print $link['final_url'];
|
1303 |
-
?></span></li>
|
1304 |
-
|
1305 |
-
<li><strong>Redirect count :</strong>
|
1306 |
-
<span class='redirect_count'><?php
|
1307 |
-
print $link['redirect_count'];
|
1308 |
-
?></span></li>
|
1309 |
-
|
1310 |
-
<li><strong>Instance count :</strong>
|
1311 |
-
<span class='instance_count'><?php
|
1312 |
-
print $link['instance_count'];
|
1313 |
-
?></span></li>
|
1314 |
-
|
1315 |
-
<?php if ( intval( $link['check_count'] ) > 0 ){ ?>
|
1316 |
-
<li><br/>This link has failed
|
1317 |
-
<span class='check_count'><?php
|
1318 |
-
echo $link['check_count'];
|
1319 |
-
if ( intval($link['check_count'])==1 ){
|
1320 |
-
echo ' time';
|
1321 |
-
} else {
|
1322 |
-
echo ' times';
|
1323 |
-
}
|
1324 |
-
?></span>.</li>
|
1325 |
-
<?php } ?>
|
1326 |
-
</ol>
|
1327 |
-
<?php
|
1328 |
-
}
|
1329 |
-
|
1330 |
-
/**
|
1331 |
-
* ws_broken_link_checker::cleanup_links()
|
1332 |
-
* Remove orphaned links that have no corresponding instances
|
1333 |
-
*
|
1334 |
-
* @param int $link_id (optional) Only check this link
|
1335 |
-
* @return bool
|
1336 |
-
*/
|
1337 |
-
function cleanup_links( $link_id = null ){
|
1338 |
-
global $wpdb;
|
1339 |
-
|
1340 |
-
$q = "DELETE FROM {$wpdb->prefix}blc_links
|
1341 |
-
USING {$wpdb->prefix}blc_links LEFT JOIN {$wpdb->prefix}blc_instances
|
1342 |
-
ON {$wpdb->prefix}blc_instances.link_id = {$wpdb->prefix}blc_links.link_id
|
1343 |
-
WHERE
|
1344 |
-
{$wpdb->prefix}blc_instances.link_id IS NULL";
|
1345 |
-
|
1346 |
-
if ( $link_id !==null ) {
|
1347 |
-
$q .= " AND {$wpdb->prefix}blc_links.link_id = " . intval( $link_id );
|
1348 |
-
}
|
1349 |
-
|
1350 |
-
return $wpdb->query( $q );
|
1351 |
-
}
|
1352 |
-
|
1353 |
-
/**
|
1354 |
-
* ws_broken_link_checker::cleanup_instances()
|
1355 |
-
* Remove instances that reference invalid posts or bookmarks
|
1356 |
-
*
|
1357 |
-
* @return bool
|
1358 |
-
*/
|
1359 |
-
function cleanup_instances(){
|
1360 |
-
global $wpdb;
|
1361 |
-
|
1362 |
-
//Delete all instances that reference non-existent posts
|
1363 |
-
$q = "DELETE FROM {$wpdb->prefix}blc_instances
|
1364 |
-
USING {$wpdb->prefix}blc_instances LEFT JOIN {$wpdb->posts} ON {$wpdb->prefix}blc_instances.source_id = {$wpdb->posts}.ID
|
1365 |
-
WHERE
|
1366 |
-
{$wpdb->posts}.ID IS NULL
|
1367 |
-
AND ( ( {$wpdb->prefix}blc_instances.source_type = 'post' ) OR ( {$wpdb->prefix}blc_instances.source_type = 'custom_field' ) )";
|
1368 |
-
$rez = $wpdb->query($q);
|
1369 |
-
|
1370 |
-
//Delete all instances that reference non-existant bookmarks
|
1371 |
-
$q = "DELETE FROM {$wpdb->prefix}blc_instances
|
1372 |
-
USING {$wpdb->prefix}blc_instances LEFT JOIN {$wpdb->links} ON {$wpdb->prefix}blc_instances.source_id = {$wpdb->links}.link_id
|
1373 |
-
WHERE
|
1374 |
-
{$wpdb->links}.link_id IS NULL
|
1375 |
-
AND {$wpdb->prefix}blc_instances.source_type = 'blogroll' ";
|
1376 |
-
$rez2 = $wpdb->query($q);
|
1377 |
-
|
1378 |
-
return $rez and $rez2;
|
1379 |
-
}
|
1380 |
-
|
1381 |
-
function load_options(){
|
1382 |
-
$this->options = get_option($this->options_name);
|
1383 |
-
if( !is_array( $this->options ) ){
|
1384 |
-
$this->options = $this->defaults;
|
1385 |
-
} else {
|
1386 |
-
$this->options = array_merge( $this->defaults, $this->options );
|
1387 |
-
}
|
1388 |
-
}
|
1389 |
-
|
1390 |
-
function save_options(){
|
1391 |
-
update_option($this->options_name,$this->options);
|
1392 |
-
}
|
1393 |
-
|
1394 |
-
/**
|
1395 |
-
* ws_broken_link_checker::parse_post()
|
1396 |
-
* Parse a post for links and save them to the DB.
|
1397 |
-
*
|
1398 |
-
* @param string $content Post content
|
1399 |
-
* @param int $post_id Post ID
|
1400 |
-
* @return void
|
1401 |
-
*/
|
1402 |
-
function parse_post($content, $post_id){
|
1403 |
-
//remove all <code></code> blocks first
|
1404 |
-
$content = preg_replace('/<code>.+?<\/code>/i', ' ', $content);
|
1405 |
-
//remove all <pre></pre> blocks as well
|
1406 |
-
$content = preg_replace('/<pre[^>]*>.+?<\/pre>/i', ' ', $content);
|
1407 |
-
//Get the post permalink - it's used to resolve relative URLs
|
1408 |
-
$permalink = get_permalink( $post_id );
|
1409 |
-
|
1410 |
-
//Find links
|
1411 |
-
if(preg_match_all(blcUtility::link_pattern(), $content, $matches, PREG_SET_ORDER)){
|
1412 |
-
foreach($matches as $link){
|
1413 |
-
$url = $link[3];
|
1414 |
-
$text = strip_tags( $link[5] );
|
1415 |
-
//FB::log($url, "Found link");
|
1416 |
-
|
1417 |
-
$url = blcUtility::normalize_url($url, $permalink);
|
1418 |
-
//Skip invalid links
|
1419 |
-
if ( !$url || (strlen($url)<6) ) continue;
|
1420 |
-
|
1421 |
-
//Create or load the link
|
1422 |
-
$link_obj = new blcLink($url);
|
1423 |
-
//Add & save a new instance
|
1424 |
-
$link_obj->add_instance($post_id, 'post', $text, 'link');
|
1425 |
-
}
|
1426 |
-
};
|
1427 |
-
|
1428 |
-
//Find images (<img src=...>)
|
1429 |
-
if(preg_match_all(blcUtility::img_pattern(), $content, $matches, PREG_SET_ORDER)){
|
1430 |
-
foreach($matches as $img){
|
1431 |
-
$url = $img[3];
|
1432 |
-
//FB::log($url, "Found image");
|
1433 |
-
|
1434 |
-
$url = blcUtility::normalize_url($url, $permalink);
|
1435 |
-
if ( !$url || (strlen($url)<6) ) continue; //skip invalid URLs
|
1436 |
-
|
1437 |
-
//Create or load the link
|
1438 |
-
$link = new blcLink($url);
|
1439 |
-
//Add & save a new image instance
|
1440 |
-
$link->add_instance($post_id, 'post', '', 'image');
|
1441 |
-
}
|
1442 |
-
};
|
1443 |
-
}
|
1444 |
-
|
1445 |
-
/**
|
1446 |
-
* ws_broken_link_checker::parse_post_meta()
|
1447 |
-
* Parse a post's custom fields for links and save them in the DB.
|
1448 |
-
*
|
1449 |
-
* @param id $post_id
|
1450 |
-
* @return void
|
1451 |
-
*/
|
1452 |
-
function parse_post_meta($post_id){
|
1453 |
-
//Get all custom fields of this post
|
1454 |
-
$custom_fields = get_post_custom( $post_id );
|
1455 |
-
//FB::log($custom_fields, "Custom fields loaded");
|
1456 |
-
|
1457 |
-
//Parse the enabled fields
|
1458 |
-
foreach( $this->options['custom_fields'] as $field ){
|
1459 |
-
if ( !isset($custom_fields[$field]) ) continue;
|
1460 |
-
|
1461 |
-
//FB::log($field, "Parsing field");
|
1462 |
-
|
1463 |
-
$values = $custom_fields[$field];
|
1464 |
-
if ( !is_array( $values ) ) $values = array($values);
|
1465 |
-
|
1466 |
-
foreach( $values as $value ){
|
1467 |
-
|
1468 |
-
//Attempt to parse the $value as URL
|
1469 |
-
$url = blcUtility::normalize_url($value);
|
1470 |
-
if ( empty($url) ){
|
1471 |
-
//FB::warn($value, "Invalid URL in custom field ".$field);
|
1472 |
-
continue;
|
1473 |
-
}
|
1474 |
-
|
1475 |
-
//FB::log($url, "Found URL");
|
1476 |
-
$link = new blcLink( $url );
|
1477 |
-
//FB::log($link, 'Created/loaded link');
|
1478 |
-
$inst = $link->add_instance( $post_id, 'custom_field', $field, 'link' );
|
1479 |
-
//FB::log($inst, 'Created instance');
|
1480 |
-
}
|
1481 |
-
}
|
1482 |
-
|
1483 |
-
}
|
1484 |
-
|
1485 |
-
function parse_blogroll_link( $the_link ){
|
1486 |
-
//FB::log($the_link, "Parsing blogroll link");
|
1487 |
-
|
1488 |
-
//Attempt to parse the URL
|
1489 |
-
$url = blcUtility::normalize_url( $the_link['link_url'] );
|
1490 |
-
if ( empty($url) ){
|
1491 |
-
//FB::warn( $the_link['link_url'], "Invalid URL in for a blogroll link".$the_link['link_name'] );
|
1492 |
-
return false;
|
1493 |
-
}
|
1494 |
-
|
1495 |
-
//FB::log($url, "Found URL");
|
1496 |
-
$link = new blcLink( $url );
|
1497 |
-
return $link->add_instance( $the_link['link_id'], 'blogroll', $the_link['link_name'], 'link' );
|
1498 |
-
}
|
1499 |
-
|
1500 |
-
function start_timer(){
|
1501 |
-
$this->execution_start_time = microtime_float();
|
1502 |
-
}
|
1503 |
-
|
1504 |
-
function execution_time(){
|
1505 |
-
return microtime_float() - $this->execution_start_time;
|
1506 |
-
}
|
1507 |
-
|
1508 |
-
/**
|
1509 |
-
* ws_broken_link_checker::work()
|
1510 |
-
* The main worker function that does all kinds of things.
|
1511 |
-
*
|
1512 |
-
* @return void
|
1513 |
-
*/
|
1514 |
-
function work(){
|
1515 |
-
global $wpdb;
|
1516 |
-
|
1517 |
-
if ( !$this->acquire_lock() ){
|
1518 |
-
//FB::warn("Another instance of BLC is already working. Stop.");
|
1519 |
-
return false;
|
1520 |
-
}
|
1521 |
-
|
1522 |
-
$this->start_timer();
|
1523 |
-
|
1524 |
-
$max_execution_time = $this->options['max_execution_time'];
|
1525 |
-
|
1526 |
-
/*****************************************
|
1527 |
-
Preparation
|
1528 |
-
******************************************/
|
1529 |
-
// Check for safe mode
|
1530 |
-
if( ini_get('safe_mode') ){
|
1531 |
-
// Do it the safe mode way
|
1532 |
-
$t=ini_get('max_execution_time');
|
1533 |
-
if ($t && ($t < $max_execution_time))
|
1534 |
-
$max_execution_time = $t-1;
|
1535 |
-
} else {
|
1536 |
-
// Do it the regular way
|
1537 |
-
@set_time_limit( $max_execution_time * 2 ); //x2 should be plenty, running any longer would mean a glitch.
|
1538 |
-
}
|
1539 |
-
@ignore_user_abort(true);
|
1540 |
-
|
1541 |
-
$check_threshold = date('Y-m-d H:i:s', strtotime('-'.$this->options['check_threshold'].' hours'));
|
1542 |
-
$recheck_threshold = date('Y-m-d H:i:s', strtotime('-20 minutes'));
|
1543 |
-
|
1544 |
-
$orphans_possible = false;
|
1545 |
-
|
1546 |
-
$still_need_resynch = $this->options['need_resynch'];
|
1547 |
-
|
1548 |
-
/*****************************************
|
1549 |
-
Parse posts and bookmarks
|
1550 |
-
******************************************/
|
1551 |
-
|
1552 |
-
if ( $this->options['need_resynch'] ) {
|
1553 |
-
|
1554 |
-
//FB::log("Looking for posts and bookmarks that need parsing...");
|
1555 |
-
|
1556 |
-
$tsynch = $wpdb->prefix.'blc_synch';
|
1557 |
-
$tposts = $wpdb->posts;
|
1558 |
-
$tlinks = $wpdb->links;
|
1559 |
-
|
1560 |
-
$synch_q = "SELECT $tsynch.source_id, $tsynch.source_type, $tposts.post_content, $tlinks.link_url, $tlinks.link_id, $tlinks.link_name
|
1561 |
-
|
1562 |
-
FROM
|
1563 |
-
$tsynch LEFT JOIN $tposts
|
1564 |
-
ON ($tposts.id = $tsynch.source_id AND $tsynch.source_type='post')
|
1565 |
-
LEFT JOIN $tlinks
|
1566 |
-
ON ($tlinks.link_id = $tsynch.source_id AND $tsynch.source_type='blogroll')
|
1567 |
-
|
1568 |
-
WHERE
|
1569 |
-
$tsynch.synched = 0
|
1570 |
-
|
1571 |
-
LIMIT 50";
|
1572 |
-
|
1573 |
-
while ( $rows = $wpdb->get_results($synch_q, ARRAY_A) ) {
|
1574 |
-
|
1575 |
-
//FB::log("Found ".count($rows)." items to analyze.");
|
1576 |
-
|
1577 |
-
foreach ($rows as $row) {
|
1578 |
-
|
1579 |
-
if ( $row['source_type'] == 'post' ){
|
1580 |
-
|
1581 |
-
//FB::log("Parsing post ".$row['source_id']);
|
1582 |
-
|
1583 |
-
//Remove instances associated with this post
|
1584 |
-
$q = "DELETE FROM {$wpdb->prefix}blc_instances
|
1585 |
-
WHERE source_id = %d AND (source_type = 'post' OR source_type='custom_field')";
|
1586 |
-
$q = $wpdb->prepare($q, intval($row['source_id']));
|
1587 |
-
|
1588 |
-
//FB::log($q, "Executing query");
|
1589 |
-
|
1590 |
-
if ( $wpdb->query( $q ) === false ){
|
1591 |
-
//FB::error($wpdb->last_error, "Database error");
|
1592 |
-
}
|
1593 |
-
|
1594 |
-
//Gather links and images from the post
|
1595 |
-
$this->parse_post( $row['post_content'], $row['source_id'] );
|
1596 |
-
//Gather links from custom fields
|
1597 |
-
$this->parse_post_meta( $row['source_id'] );
|
1598 |
-
|
1599 |
-
//Some link records might be orhpaned now
|
1600 |
-
$orphans_possible = true;
|
1601 |
-
|
1602 |
-
} else {
|
1603 |
-
|
1604 |
-
//FB::log("Parsing bookmark ".$row['source_id']);
|
1605 |
-
|
1606 |
-
//Remove instances associated with this bookmark
|
1607 |
-
$q = "DELETE FROM {$wpdb->prefix}blc_instances
|
1608 |
-
WHERE source_id = %d AND source_type = 'blogroll'";
|
1609 |
-
$q = $wpdb->prepare($q, intval($row['source_id']));
|
1610 |
-
//FB::log($q, "Executing query");
|
1611 |
-
|
1612 |
-
if ( $wpdb->query( $q ) === false ){
|
1613 |
-
//FB::error($wpdb->last_error, "Database error");
|
1614 |
-
}
|
1615 |
-
|
1616 |
-
//(Re)add the instance and link
|
1617 |
-
$this->parse_blogroll_link( $row );
|
1618 |
-
|
1619 |
-
//Some link records might be orhpaned now
|
1620 |
-
$orphans_possible = true;
|
1621 |
-
|
1622 |
-
}
|
1623 |
-
|
1624 |
-
//Update the table to indicate the item has been parsed
|
1625 |
-
$this->mark_synched( $row['source_id'], $row['source_type'] );
|
1626 |
-
|
1627 |
-
//Check if we still have some execution time left
|
1628 |
-
if( $this->execution_time() > $max_execution_time ){
|
1629 |
-
//FB::log('The alloted execution time has run out');
|
1630 |
-
$this->cleanup_links();
|
1631 |
-
$this->release_lock();
|
1632 |
-
return;
|
1633 |
-
}
|
1634 |
-
|
1635 |
-
}
|
1636 |
-
|
1637 |
-
}
|
1638 |
-
|
1639 |
-
//FB::log('No unparsed items found.');
|
1640 |
-
$still_need_resynch = false;
|
1641 |
-
|
1642 |
-
if ( $wpdb->last_error ){
|
1643 |
-
//FB::error($wpdb->last_error, "Database error");
|
1644 |
-
}
|
1645 |
-
|
1646 |
-
} else {
|
1647 |
-
//FB::log('Resynch not required.');
|
1648 |
-
}
|
1649 |
-
|
1650 |
-
/******************************************
|
1651 |
-
Resynch done?
|
1652 |
-
*******************************************/
|
1653 |
-
if ( $this->options['need_resynch'] && !$still_need_resynch ){
|
1654 |
-
$this->options['need_resynch'] = $still_need_resynch;
|
1655 |
-
$this->save_options();
|
1656 |
-
}
|
1657 |
-
|
1658 |
-
/******************************************
|
1659 |
-
Remove orphaned links
|
1660 |
-
*******************************************/
|
1661 |
-
|
1662 |
-
if ( $orphans_possible ) {
|
1663 |
-
//FB::log('Cleaning up the link table.');
|
1664 |
-
$this->cleanup_links();
|
1665 |
-
}
|
1666 |
-
|
1667 |
-
//Check if we still have some execution time left
|
1668 |
-
if( $this->execution_time() > $max_execution_time ){
|
1669 |
-
//FB::log('The alloted execution time has run out');
|
1670 |
-
$this->release_lock();
|
1671 |
-
return;
|
1672 |
-
}
|
1673 |
-
|
1674 |
-
/*****************************************
|
1675 |
-
Check links
|
1676 |
-
******************************************/
|
1677 |
-
//FB::log('Looking for links to check (threshold : '.$check_threshold.')...');
|
1678 |
-
|
1679 |
-
//Select some links that haven't been checked for a long time or
|
1680 |
-
//that are broken and need to be re-checked again.
|
1681 |
-
|
1682 |
-
//Note : This is a slow query, but AFAIK there is no way to speed it up.
|
1683 |
-
//I could put an index on last_check, but that value is almost certainly unique
|
1684 |
-
//for each row so it wouldn't be much better than a full table scan.
|
1685 |
-
$q = "SELECT *, ( last_check < %s ) AS meets_check_threshold
|
1686 |
-
FROM {$wpdb->prefix}blc_links
|
1687 |
-
WHERE
|
1688 |
-
( last_check < %s )
|
1689 |
-
OR
|
1690 |
-
(
|
1691 |
-
( http_code >= 400 OR http_code < 200 OR timeout = 1)
|
1692 |
-
AND check_count < %d
|
1693 |
-
AND check_count > 0
|
1694 |
-
AND last_check < %s
|
1695 |
-
)
|
1696 |
-
ORDER BY last_check ASC
|
1697 |
-
LIMIT 50";
|
1698 |
-
$link_q = $wpdb->prepare($q, $check_threshold, $check_threshold, $this->options['recheck_count'], $recheck_threshold);
|
1699 |
-
//FB::log($link_q);
|
1700 |
-
|
1701 |
-
while ( $links = $wpdb->get_results($link_q, ARRAY_A) ){
|
1702 |
-
|
1703 |
-
//some unchecked links found
|
1704 |
-
//FB::log("Checking ".count($links)." link(s)");
|
1705 |
-
|
1706 |
-
foreach ($links as $link) {
|
1707 |
-
$link_obj = new blcLink($link);
|
1708 |
-
|
1709 |
-
//Does this link need to be checked?
|
1710 |
-
if ( !$this->is_excluded( $link['url'] ) ) {
|
1711 |
-
//Yes, do it
|
1712 |
-
//FB::log("Checking link {$link[link_id]}");
|
1713 |
-
$link_obj->check();
|
1714 |
-
$link_obj->save();
|
1715 |
-
} else {
|
1716 |
-
//Nope, mark it as already checked.
|
1717 |
-
//FB::info("The URL {$link_obj->url} is excluded, marking link {$link_obj->link_id} as already checked.");
|
1718 |
-
$link_obj->last_check = date('Y-m-d H:i:s');
|
1719 |
-
$link_obj->http_code = 200; //Use a fake code so that the link doesn't show up in queries looking for broken links.
|
1720 |
-
$link_obj->timeout = false;
|
1721 |
-
$link_obj->request_duration = 0;
|
1722 |
-
$link_obj->log = "This link wasn't checked because a matching keyword was found on your exclusion list.";
|
1723 |
-
$link_obj->save();
|
1724 |
-
}
|
1725 |
-
|
1726 |
-
//Check if we still have some execution time left
|
1727 |
-
if( $this->execution_time() > $max_execution_time ){
|
1728 |
-
//FB::log('The alloted execution time has run out');
|
1729 |
-
$this->release_lock();
|
1730 |
-
return;
|
1731 |
-
}
|
1732 |
-
}
|
1733 |
-
}
|
1734 |
-
//FB::log('No links need to be checked right now.');
|
1735 |
-
|
1736 |
-
$this->release_lock();
|
1737 |
-
//FB::log('All done.');
|
1738 |
-
}
|
1739 |
-
|
1740 |
-
function ajax_full_status( ){
|
1741 |
-
$status = $this->get_status();
|
1742 |
-
$text = $this->status_text( $status );
|
1743 |
-
|
1744 |
-
echo json_encode( array(
|
1745 |
-
'text' => $text,
|
1746 |
-
'status' => $status,
|
1747 |
-
) );
|
1748 |
-
|
1749 |
-
die();
|
1750 |
-
}
|
1751 |
-
|
1752 |
-
/**
|
1753 |
-
* ws_broken_link_checker::status_text()
|
1754 |
-
* Generates a status message based on the status info in $status
|
1755 |
-
*
|
1756 |
-
* @param array $status
|
1757 |
-
* @return string
|
1758 |
-
*/
|
1759 |
-
function status_text( $status ){
|
1760 |
-
$text = '';
|
1761 |
-
|
1762 |
-
if( $status['broken_links'] > 0 ){
|
1763 |
-
$text .= sprintf( "<a href='%stools.php?page=view-broken-links' title='View broken links'><strong>Found %d broken link%s</strong></a>",
|
1764 |
-
get_option('wpurl'), $status['broken_links'], ( $status['broken_links'] == 1 )?'':'s' );
|
1765 |
-
} else {
|
1766 |
-
$text .= "No broken links found.";
|
1767 |
-
}
|
1768 |
-
|
1769 |
-
$text .= "<br/>";
|
1770 |
-
|
1771 |
-
if( $status['unchecked_links'] > 0) {
|
1772 |
-
$text .= sprintf( '%d URL%s in the work queue', $status['unchecked_links'], ($status['unchecked_links'] == 1)?'':'s' );
|
1773 |
-
} else {
|
1774 |
-
$text .= "No URLs in the work queue.";
|
1775 |
-
}
|
1776 |
-
|
1777 |
-
$text .= "<br/>";
|
1778 |
-
if ( $status['known_links'] > 0 ){
|
1779 |
-
$text .= sprintf( "Detected %d unique URL%s in %d link%s",
|
1780 |
-
$status['known_links'], $status['known_links'] == 1 ? '' : 's',
|
1781 |
-
$status['known_instances'], $status['known_instances'] == 1 ? '' : 's'
|
1782 |
-
);
|
1783 |
-
if ($this->options['need_resynch']){
|
1784 |
-
$text .= ' and still searching...';
|
1785 |
-
} else {
|
1786 |
-
$text .= '.';
|
1787 |
-
}
|
1788 |
-
} else {
|
1789 |
-
if ($this->options['need_resynch']){
|
1790 |
-
$text .= 'Searching your blog for links...';
|
1791 |
-
} else {
|
1792 |
-
$text .= 'No links detected.';
|
1793 |
-
}
|
1794 |
-
}
|
1795 |
-
|
1796 |
-
return $text;
|
1797 |
-
}
|
1798 |
-
|
1799 |
-
function ajax_dashboard_status(){
|
1800 |
-
//Just display the full status.
|
1801 |
-
$this->ajax_full_status( false );
|
1802 |
-
/*
|
1803 |
-
global $wpdb;
|
1804 |
-
|
1805 |
-
//displays a notification if broken links have been found
|
1806 |
-
$q = "SELECT count(*) FROM {$wpdb->prefix}blc_links
|
1807 |
-
WHERE check_count > 0 AND ( http_code < 200 OR http_code >= 400 OR timeout = 1 )";
|
1808 |
-
$broken_links = $wpdb->get_var($q);
|
1809 |
-
|
1810 |
-
|
1811 |
-
if($broken_links>0){
|
1812 |
-
printf( "<a href='%stools.php?page=view-broken-links' title='View broken links'><strong>Found %d broken link%s</strong></a>",
|
1813 |
-
get_option('wpurl'), $broken_links, ($broken_links==1)?'':'s' );
|
1814 |
-
} else {
|
1815 |
-
echo "No broken links found.";
|
1816 |
-
}
|
1817 |
-
die();
|
1818 |
-
*/
|
1819 |
-
}
|
1820 |
-
|
1821 |
-
/**
|
1822 |
-
* ws_broken_link_checker::get_status()
|
1823 |
-
* Returns an array with various status information about the plugin. Array key reference:
|
1824 |
-
* check_threshold - date/time; links checked before this threshold should be checked again.
|
1825 |
-
* recheck_threshold - date/time; broken links checked before this threshold should be re-checked.
|
1826 |
-
* known_links - the number of detected unique URLs (a misleading name, yes).
|
1827 |
-
* known_instances - the number of detected link instances, i.e. actual link elements in posts and other places.
|
1828 |
-
* broken_links - the number of detected broken links.
|
1829 |
-
* unchecked_links - the number of URLs that need to be checked ASAP; based on check_threshold and recheck_threshold.
|
1830 |
-
*
|
1831 |
-
* @return array
|
1832 |
-
*/
|
1833 |
-
function get_status(){
|
1834 |
-
global $wpdb;
|
1835 |
-
|
1836 |
-
$check_threshold=date('Y-m-d H:i:s', strtotime('-'.$this->options['check_threshold'].' hours'));
|
1837 |
-
$recheck_threshold=date('Y-m-d H:i:s', strtotime('-20 minutes'));
|
1838 |
-
|
1839 |
-
$q = "SELECT count(*) FROM {$wpdb->prefix}blc_links WHERE 1";
|
1840 |
-
$known_links = $wpdb->get_var($q);
|
1841 |
-
|
1842 |
-
$q = "SELECT count(*) FROM {$wpdb->prefix}blc_instances WHERE 1";
|
1843 |
-
$known_instances = $wpdb->get_var($q);
|
1844 |
-
|
1845 |
-
$q = "SELECT count(*) FROM {$wpdb->prefix}blc_links
|
1846 |
-
WHERE check_count > 0 AND ( http_code < 200 OR http_code >= 400 OR timeout = 1 ) AND ( http_code <> ".BLC_CHECKING." )";
|
1847 |
-
$broken_links = $wpdb->get_var($q);
|
1848 |
-
|
1849 |
-
$q = "SELECT count(*) FROM {$wpdb->prefix}blc_links
|
1850 |
-
WHERE
|
1851 |
-
( ( last_check < '$check_threshold' ) OR
|
1852 |
-
(
|
1853 |
-
( http_code >= 400 OR http_code < 200 )
|
1854 |
-
AND check_count < 3
|
1855 |
-
AND last_check < '$recheck_threshold' )
|
1856 |
-
)";
|
1857 |
-
$unchecked_links = $wpdb->get_var($q);
|
1858 |
-
|
1859 |
-
return array(
|
1860 |
-
'check_threshold' => $check_threshold,
|
1861 |
-
'recheck_threshold' => $recheck_threshold,
|
1862 |
-
'known_links' => $known_links,
|
1863 |
-
'known_instances' => $known_instances,
|
1864 |
-
'broken_links' => $broken_links,
|
1865 |
-
'unchecked_links' => $unchecked_links,
|
1866 |
-
);
|
1867 |
-
}
|
1868 |
-
|
1869 |
-
function ajax_work(){
|
1870 |
-
//Run the worker function
|
1871 |
-
$this->work();
|
1872 |
-
die();
|
1873 |
-
}
|
1874 |
-
|
1875 |
-
function ajax_discard(){
|
1876 |
-
//TODO:Rewrite to use JSON instead of plaintext
|
1877 |
-
if (!current_user_can('edit_others_posts')){
|
1878 |
-
die( "You're not allowed to do that!" );
|
1879 |
-
}
|
1880 |
-
|
1881 |
-
if ( isset($_POST['link_id']) ){
|
1882 |
-
//Load the link
|
1883 |
-
$link = new blcLink( intval($_POST['link_id']) );
|
1884 |
-
|
1885 |
-
if ( !$link->valid() ){
|
1886 |
-
die("Oops, I can't find the link ".intval($_POST['link_id']) );
|
1887 |
-
}
|
1888 |
-
//Make it appear "not broken"
|
1889 |
-
$link->last_check = date('Y-m-d H:i:s');
|
1890 |
-
$link->http_code = 200;
|
1891 |
-
$link->timeout = 0;
|
1892 |
-
$link->check_count = 0;
|
1893 |
-
$link->log = "This link was manually marked as working by the user.";
|
1894 |
-
|
1895 |
-
//Save the changes
|
1896 |
-
if ( $link->save() ){
|
1897 |
-
die("OK");
|
1898 |
-
} else {
|
1899 |
-
die("Oops, couldn't modify the link!");
|
1900 |
-
}
|
1901 |
-
} else {
|
1902 |
-
die("Error : link_id not specified");
|
1903 |
-
}
|
1904 |
-
}
|
1905 |
-
|
1906 |
-
function ajax_edit(){
|
1907 |
-
if (!current_user_can('edit_others_posts')){
|
1908 |
-
die( json_encode( array(
|
1909 |
-
'error' => "You're not allowed to do that!"
|
1910 |
-
)));
|
1911 |
-
}
|
1912 |
-
|
1913 |
-
if ( isset($_GET['link_id']) && !empty($_GET['new_url']) ){
|
1914 |
-
//Load the link
|
1915 |
-
$link = new blcLink( intval($_GET['link_id']) );
|
1916 |
-
|
1917 |
-
if ( !$link->valid() ){
|
1918 |
-
die( json_encode( array(
|
1919 |
-
'error' => "Oops, I can't find the link ".intval($_GET['link_id'])
|
1920 |
-
)));
|
1921 |
-
}
|
1922 |
-
|
1923 |
-
$new_url = blcUtility::normalize_url($_GET['new_url']);
|
1924 |
-
if ( !$new_url ){
|
1925 |
-
die( json_encode( array(
|
1926 |
-
'error' => "Oops, the new URL is invalid!"
|
1927 |
-
)));
|
1928 |
-
}
|
1929 |
-
|
1930 |
-
//Try and edit the link
|
1931 |
-
$rez = $link->edit($new_url);
|
1932 |
-
|
1933 |
-
if ( $rez == false ){
|
1934 |
-
die( json_encode( array(
|
1935 |
-
'error' => "An unexpected error occured!"
|
1936 |
-
)));
|
1937 |
-
} else {
|
1938 |
-
$rez['ok'] = 'OK';
|
1939 |
-
die( json_encode($rez) );
|
1940 |
-
}
|
1941 |
-
|
1942 |
-
} else {
|
1943 |
-
die( json_encode( array(
|
1944 |
-
'error' => "Error : link_id or new_url not specified"
|
1945 |
-
)));
|
1946 |
-
}
|
1947 |
-
}
|
1948 |
-
|
1949 |
-
function ajax_unlink(){
|
1950 |
-
if (!current_user_can('edit_others_posts')){
|
1951 |
-
die( json_encode( array(
|
1952 |
-
'error' => "You're not allowed to do that!"
|
1953 |
-
)));
|
1954 |
-
}
|
1955 |
-
|
1956 |
-
if ( isset($_POST['link_id']) ){
|
1957 |
-
//Load the link
|
1958 |
-
$link = new blcLink( intval($_POST['link_id']) );
|
1959 |
-
|
1960 |
-
if ( !$link->valid() ){
|
1961 |
-
die( json_encode( array(
|
1962 |
-
'error' => "Oops, I can't find the link ".intval($_POST['link_id'])
|
1963 |
-
)));
|
1964 |
-
}
|
1965 |
-
|
1966 |
-
//Try and unlink it
|
1967 |
-
if ( $link->unlink() ){
|
1968 |
-
die( json_encode( array(
|
1969 |
-
'ok' => "URL {$link->url} was removed."
|
1970 |
-
)));
|
1971 |
-
} else {
|
1972 |
-
die( json_encode( array(
|
1973 |
-
'error' => "The plugin failed to remove the link."
|
1974 |
-
)));
|
1975 |
-
}
|
1976 |
-
|
1977 |
-
} else {
|
1978 |
-
die( json_encode( array(
|
1979 |
-
'error' => "Error : link_id not specified"
|
1980 |
-
)));
|
1981 |
-
}
|
1982 |
-
}
|
1983 |
-
|
1984 |
-
function ajax_link_details(){
|
1985 |
-
global $wpdb;
|
1986 |
-
|
1987 |
-
if (!current_user_can('edit_others_posts')){
|
1988 |
-
die("You don't have sufficient privileges to access this information!");
|
1989 |
-
}
|
1990 |
-
|
1991 |
-
//FB::log("Loading link details via AJAX");
|
1992 |
-
|
1993 |
-
if ( isset($_GET['link_id']) ){
|
1994 |
-
//FB::info("Link ID found in GET");
|
1995 |
-
$link_id = intval($_GET['link_id']);
|
1996 |
-
} else if ( isset($_POST['link_id']) ){
|
1997 |
-
//FB::info("Link ID found in POST");
|
1998 |
-
$link_id = intval($_POST['link_id']);
|
1999 |
-
} else {
|
2000 |
-
//FB::error('Link ID not specified, you hacking bastard.');
|
2001 |
-
die('Error : link ID not specified');
|
2002 |
-
}
|
2003 |
-
|
2004 |
-
//Load the link. link_details_row needs it as an array, so
|
2005 |
-
//we'll have to do this the long way.
|
2006 |
-
$q = "SELECT
|
2007 |
-
links.*,
|
2008 |
-
COUNT(*) as instance_count
|
2009 |
-
|
2010 |
-
FROM
|
2011 |
-
{$wpdb->prefix}blc_links AS links,
|
2012 |
-
{$wpdb->prefix}blc_instances as instances
|
2013 |
-
|
2014 |
-
WHERE
|
2015 |
-
links.link_id = %d
|
2016 |
-
|
2017 |
-
GROUP BY links.link_id";
|
2018 |
-
|
2019 |
-
$link = $wpdb->get_row( $wpdb->prepare($q, $link_id), ARRAY_A );
|
2020 |
-
if ( is_array($link) ){
|
2021 |
-
//FB::info($link, 'Link loaded');
|
2022 |
-
$this->link_details_row($link);
|
2023 |
-
die();
|
2024 |
-
} else {
|
2025 |
-
die ("Failed to load link details (" . $wpdb->last_error . ")");
|
2026 |
-
}
|
2027 |
-
}
|
2028 |
-
|
2029 |
-
function ajax_exclude_link(){
|
2030 |
-
if ( !current_user_can('manage_options') ){
|
2031 |
-
die( json_encode( array(
|
2032 |
-
'error' => "You're not allowed to do that!"
|
2033 |
-
)));
|
2034 |
-
}
|
2035 |
-
|
2036 |
-
if ( isset($_POST['link_id']) ){
|
2037 |
-
//Load the link
|
2038 |
-
$link = new blcLink( intval($_POST['link_id']) );
|
2039 |
-
|
2040 |
-
if ( !$link->valid() ){
|
2041 |
-
die( json_encode( array(
|
2042 |
-
'error' => "Oops, I can't find the link ".intval($_POST['link_id'])
|
2043 |
-
)));
|
2044 |
-
}
|
2045 |
-
|
2046 |
-
//Add the URL to the exclusion list
|
2047 |
-
if ( !in_array( $link->url, $this->options['exclusion_list'] ) ){
|
2048 |
-
$this->options['exclusion_list'][] = $link->url;
|
2049 |
-
//Also mark it as already checked so that it doesn't show up with other broken links.
|
2050 |
-
//FB::info("The URL {$link->url} is excluded, marking link {$link->link_id} as already checked.");
|
2051 |
-
$link->last_check = date('Y-m-d H:i:s');
|
2052 |
-
$link->http_code = 200; //Use a fake code so that the link doesn't show up in queries looking for broken links.
|
2053 |
-
$link->timeout = false;
|
2054 |
-
$link->request_duration = 0;
|
2055 |
-
$link->log = "This link wasn't checked because a matching keyword was found on your exclusion list.";
|
2056 |
-
$link->save();
|
2057 |
-
}
|
2058 |
-
|
2059 |
-
$this->save_options();
|
2060 |
-
|
2061 |
-
die( json_encode( array(
|
2062 |
-
'ok' => "URL {$link->url} added to the exclusion list"
|
2063 |
-
)));
|
2064 |
-
} else {
|
2065 |
-
die( json_encode( array(
|
2066 |
-
'error' => "Link ID not specified"
|
2067 |
-
)));
|
2068 |
-
}
|
2069 |
-
}
|
2070 |
-
|
2071 |
-
/**
|
2072 |
-
* ws_broken_link_checker::acquire_lock()
|
2073 |
-
* Create and lock a temporary file.
|
2074 |
-
*
|
2075 |
-
* @return bool
|
2076 |
-
*/
|
2077 |
-
function acquire_lock(){
|
2078 |
-
//Maybe we already have the lock?
|
2079 |
-
if ( $this->lockfile_handle ){
|
2080 |
-
return true;
|
2081 |
-
}
|
2082 |
-
|
2083 |
-
$fn = $this->lockfile_name();
|
2084 |
-
if ( $fn ){
|
2085 |
-
//Open the lockfile
|
2086 |
-
$this->lockfile_handle = fopen($fn, 'w+');
|
2087 |
-
if ( $this->lockfile_handle ){
|
2088 |
-
//Do an exclusive lock
|
2089 |
-
if (flock($this->lockfile_handle, LOCK_EX | LOCK_NB)) {
|
2090 |
-
//File locked successfully
|
2091 |
-
return true;
|
2092 |
-
} else {
|
2093 |
-
//Something went wrong
|
2094 |
-
fclose($this->lockfile_handle);
|
2095 |
-
$this->lockfile_handle = null;
|
2096 |
-
return false;
|
2097 |
-
}
|
2098 |
-
} else {
|
2099 |
-
//Can't open the file, fail.
|
2100 |
-
return false;
|
2101 |
-
}
|
2102 |
-
} else {
|
2103 |
-
//Uh oh, can't generate a lockfile name. Locking will be disabled.
|
2104 |
-
return true;
|
2105 |
-
};
|
2106 |
-
}
|
2107 |
-
|
2108 |
-
/**
|
2109 |
-
* ws_broken_link_checker::release_lock()
|
2110 |
-
* Unlock and delete the temporary file
|
2111 |
-
*
|
2112 |
-
* @return bool
|
2113 |
-
*/
|
2114 |
-
function release_lock(){
|
2115 |
-
if ( $this->lockfile_handle ){
|
2116 |
-
//Close the file (implicitly releasing the lock)
|
2117 |
-
fclose( $this->lockfile_handle );
|
2118 |
-
//Delete the file
|
2119 |
-
$fn = $this->lockfile_name();
|
2120 |
-
if ( file_exists( $fn ) ) {
|
2121 |
-
unlink( $fn );
|
2122 |
-
}
|
2123 |
-
$this->lockfile_handle = null;
|
2124 |
-
return true;
|
2125 |
-
} else {
|
2126 |
-
//We didn't have the lock anyway...
|
2127 |
-
return false;
|
2128 |
-
}
|
2129 |
-
}
|
2130 |
-
|
2131 |
-
/**
|
2132 |
-
* ws_broken_link_checker::lockfile_name()
|
2133 |
-
* Generate system-specific lockfile filename
|
2134 |
-
*
|
2135 |
-
* @return string A filename or FALSE on error
|
2136 |
-
*/
|
2137 |
-
function lockfile_name(){
|
2138 |
-
//Try the plugin's directory first.
|
2139 |
-
if ( is_writable( dirname(__FILE__) ) ){
|
2140 |
-
return dirname(__FILE__) . '/wp_blc_lock';
|
2141 |
-
} else {
|
2142 |
-
//Try to find the temp directory.
|
2143 |
-
$dummy = tempnam('asdf', 'blc');
|
2144 |
-
if ( $dummy ) {
|
2145 |
-
$path = dirname($dummy);
|
2146 |
-
//Kill the dummy file
|
2147 |
-
if ( file_exists( $dummy ) ) unlink($dummy);
|
2148 |
-
return $path . '/wp_blc_lock';
|
2149 |
-
} else {
|
2150 |
-
//Try /tmp as a last resort
|
2151 |
-
if ( is_writable('/tmp') ){
|
2152 |
-
return '/tmp/wp_blc_lock';
|
2153 |
-
} else {
|
2154 |
-
return false;
|
2155 |
-
}
|
2156 |
-
}
|
2157 |
-
}
|
2158 |
-
}
|
2159 |
-
|
2160 |
-
function hook_add_link( $link_id ){
|
2161 |
-
$this->mark_unsynched( $link_id, 'blogroll' );
|
2162 |
-
}
|
2163 |
-
|
2164 |
-
function hook_edit_link( $link_id ){
|
2165 |
-
$this->mark_unsynched( $link_id, 'blogroll' );
|
2166 |
-
}
|
2167 |
-
|
2168 |
-
function hook_delete_link( $link_id ){
|
2169 |
-
global $wpdb;
|
2170 |
-
//Delete the synch record
|
2171 |
-
$wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->prefix}blc_synch WHERE source_id = %d AND source_type='blogroll'", $link_id ) );
|
2172 |
-
|
2173 |
-
//Get the matching instance record.
|
2174 |
-
$inst = $wpdb->get_row( $wpdb->prepare("SELECT * FROM {$wpdb->prefix}blc_instances WHERE source_id = %d AND source_type = 'blogroll'", $link_id), ARRAY_A );
|
2175 |
-
|
2176 |
-
if ( !$inst ) {
|
2177 |
-
//No instance record? No problem.
|
2178 |
-
return;
|
2179 |
-
}
|
2180 |
-
|
2181 |
-
//Remove it
|
2182 |
-
$wpdb->query( $wpdb->prepare("DELETE FROM {$wpdb->prefix}blc_instances WHERE instance_id = %d", $inst['instance_id']) );
|
2183 |
-
|
2184 |
-
//Remove the link that was associated with this instance if it has no more related instances.
|
2185 |
-
$this->cleanup_links( $inst['link_id'] );
|
2186 |
-
}
|
2187 |
-
|
2188 |
-
function hook_wp_dashboard_setup(){
|
2189 |
-
if ( function_exists( 'wp_add_dashboard_widget' ) ) {
|
2190 |
-
wp_add_dashboard_widget(
|
2191 |
-
'blc_dashboard_widget',
|
2192 |
-
'Broken Link Checker',
|
2193 |
-
array( &$this, 'dashboard_widget' ),
|
2194 |
-
array( &$this, 'dashboard_widget_control' )
|
2195 |
-
);
|
2196 |
-
}
|
2197 |
-
}
|
2198 |
-
|
2199 |
-
}//class ends here
|
2200 |
-
|
2201 |
-
} // if class_exists...
|
2202 |
-
|
2203 |
-
if ( !isset($ws_link_checker) )
|
2204 |
-
$ws_link_checker = new ws_broken_link_checker();
|
2205 |
-
|
2206 |
?>
|
1 |
<?php
|
2 |
+
|
3 |
/*
|
4 |
Plugin Name: Broken Link Checker
|
5 |
Plugin URI: http://w-shadow.com/blog/2007/08/05/broken-link-checker-for-wordpress/
|
6 |
Description: Checks your posts for broken links and missing images and notifies you on the dashboard if any are found.
|
7 |
+
Version: 0.5.10
|
8 |
Author: Janis Elsts
|
9 |
Author URI: http://w-shadow.com/blog/
|
10 |
*/
|
14 |
MySQL 4.0 compatibility by Jeroen (www.yukka.eu)
|
15 |
*/
|
16 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
17 |
/*
|
18 |
//FirePHP for debugging
|
19 |
if ( !class_exists('FB') ) {
|
25 |
//to uncomment : \/\/(\s*FB::) -> $1
|
26 |
//*/
|
27 |
|
28 |
+
//Make sure some useful constants are defined
|
29 |
+
if ( ! defined( 'WP_CONTENT_URL' ) )
|
30 |
+
define( 'WP_CONTENT_URL', get_option( 'siteurl' ) . '/wp-content' );
|
31 |
+
if ( ! defined( 'WP_CONTENT_DIR' ) )
|
32 |
+
define( 'WP_CONTENT_DIR', ABSPATH . 'wp-content' );
|
33 |
+
if ( ! defined( 'WP_PLUGIN_URL' ) )
|
34 |
+
define( 'WP_PLUGIN_URL', WP_CONTENT_URL. '/plugins' );
|
35 |
+
if ( ! defined( 'WP_PLUGIN_DIR' ) )
|
36 |
+
define( 'WP_PLUGIN_DIR', WP_CONTENT_DIR . '/plugins' );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
37 |
|
38 |
+
//The HTTP code of a link record can be set to one of these in some special circumstances
|
39 |
+
if ( ! defined('BLC_CHECKING') )
|
40 |
+
define('BLC_CHECKING', 1); //The link is currently being checked. If this state persists, suspect a glitch.
|
41 |
+
if ( ! defined('BLC_TIMEOUT') )
|
42 |
+
define('BLC_TIMEOUT', 0); //The code used for links that timed out and didn't return an actual response.
|
43 |
+
|
44 |
+
//Load and initialize the plugin's configuration
|
45 |
+
require 'config-manager.php';
|
46 |
+
$blc_config_manager = new blcConfigurationManager(
|
47 |
+
//Save the plugin's configuration into this DB option
|
48 |
+
'wsblc_options',
|
49 |
+
//Initialize default settings
|
50 |
+
array(
|
51 |
+
'max_execution_time' => 5*60, //How long the worker instance may run, at most.
|
52 |
+
'check_threshold' => 72, //Check each link every 72 hours.
|
53 |
+
'mark_broken_links' => true, //Whether to add the broken_link class to broken links in posts.
|
54 |
+
'broken_link_css' => ".broken_link, a.broken_link {\n\ttext-decoration: line-through;\n}",
|
55 |
+
'exclusion_list' => array(), //Links that contain a substring listed in this array won't be checked.
|
56 |
+
'recheck_count' => 3, //[Internal] How many times a broken link should be re-checked (slightly buggy)
|
57 |
+
|
58 |
+
//These three are currently ignored. Everything is checked by default.
|
59 |
+
'check_posts' => true,
|
60 |
+
'check_custom_fields' => true,
|
61 |
+
'check_blogroll' => true,
|
62 |
+
|
63 |
+
'custom_fields' => array(), //List of custom fields that can contain URLs and should be checked.
|
64 |
+
|
65 |
+
'autoexpand_widget' => true, //Autoexpand the Dashboard widget if broken links are detected
|
66 |
+
|
67 |
+
'need_resynch' => false, //[Internal flag]
|
68 |
+
'current_db_version' => 0, //The current version of the plugin's tables
|
69 |
+
)
|
70 |
+
);
|
71 |
+
|
72 |
+
|
73 |
+
if ( !is_admin() ){
|
74 |
+
//This is user-side request, so the only thing we may need to do is run the broken link highlighter.
|
75 |
+
if ( $blc_config_manager->options['mark_broken_links'] ){
|
76 |
+
//Load some utilities (used by the higlighter) and the highlighter itself
|
77 |
+
require 'utility-class.php';
|
78 |
+
require 'highlighter-class.php';
|
79 |
+
$blc_link_highlighter = new blcLinkHighlighter( $blc_config_manager->options['broken_link_css'] );
|
80 |
+
}
|
81 |
+
} else {
|
82 |
+
//Load everything
|
83 |
+
require 'utility-class.php';
|
84 |
+
require 'instance-classes.php';
|
85 |
+
require 'link-classes.php';
|
86 |
+
require 'core.php';
|
87 |
+
|
88 |
+
$ws_link_checker = new wsBrokenLinkChecker( __FILE__ , $blc_config_manager );
|
89 |
}
|
90 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
91 |
?>
|
config-manager.php
ADDED
@@ -0,0 +1,85 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* @author W-Shadow
|
5 |
+
* @copyright 2009
|
6 |
+
*/
|
7 |
+
|
8 |
+
class blcConfigurationManager {
|
9 |
+
|
10 |
+
var $option_name;
|
11 |
+
|
12 |
+
var $options;
|
13 |
+
var $defaults;
|
14 |
+
var $loaded_values;
|
15 |
+
|
16 |
+
function blcConfigurationManager( $option_name = '', $default_settings = null ){
|
17 |
+
$this->option_name = $option_name;
|
18 |
+
|
19 |
+
if ( is_array($default_settings) ){
|
20 |
+
$this->defaults = $default_settings;
|
21 |
+
} else {
|
22 |
+
$this->defaults = array();
|
23 |
+
}
|
24 |
+
$this->loaded_values = array();
|
25 |
+
|
26 |
+
$this->options = $this->defaults;
|
27 |
+
|
28 |
+
if ( !empty( $this->option_name ) )
|
29 |
+
$this->load_options();
|
30 |
+
}
|
31 |
+
|
32 |
+
function set_defaults( $default_settings = null ){
|
33 |
+
if ( is_array($default_settings) ){
|
34 |
+
$this->defaults = array();
|
35 |
+
} else {
|
36 |
+
$this->defaults = $default_settings;
|
37 |
+
}
|
38 |
+
$this->options = array_merge($this->defaults, $this->loaded_values);
|
39 |
+
}
|
40 |
+
|
41 |
+
/**
|
42 |
+
* blcOptionManager::load_options()
|
43 |
+
* Load plugin options from the database. The current $options values are not affected
|
44 |
+
* if this function fails.
|
45 |
+
*
|
46 |
+
* @param string $option_name
|
47 |
+
* @return bool True if options were loaded, false otherwise.
|
48 |
+
*/
|
49 |
+
function load_options( $option_name = '' ){
|
50 |
+
if ( !empty($option_name) ){
|
51 |
+
$this->option_name = $option_name;
|
52 |
+
}
|
53 |
+
|
54 |
+
if ( empty($this->option_name) ) return false;
|
55 |
+
|
56 |
+
$new_options = get_option($this->option_name);
|
57 |
+
if( !is_array( $new_options ) ){
|
58 |
+
return false;
|
59 |
+
} else {
|
60 |
+
$this->loaded_values = $new_options;
|
61 |
+
$this->options = array_merge( $this->defaults, $this->loaded_values );
|
62 |
+
return true;
|
63 |
+
}
|
64 |
+
}
|
65 |
+
|
66 |
+
/**
|
67 |
+
* blcOptionManager::save_options()
|
68 |
+
* Save plugin options to the databse.
|
69 |
+
*
|
70 |
+
* @param string $option_name (Optional) Save the options under this name
|
71 |
+
* @return bool True on success, false on failure
|
72 |
+
*/
|
73 |
+
function save_options( $option_name = '' ){
|
74 |
+
if ( !empty($option_name) ){
|
75 |
+
$this->option_name = $option_name;
|
76 |
+
}
|
77 |
+
|
78 |
+
if ( empty($this->option_name) ) return false;
|
79 |
+
|
80 |
+
update_option( $this->option_name, $this->options );
|
81 |
+
return true;
|
82 |
+
}
|
83 |
+
}
|
84 |
+
|
85 |
+
?>
|
core.php
ADDED
@@ -0,0 +1,2137 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
//The plugin will use Snoopy in case CURL is not available
|
4 |
+
if (!class_exists('Snoopy')) require_once(ABSPATH.'/wp-includes/class-snoopy.php');
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Simple function to replicate PHP 5 behaviour
|
8 |
+
*/
|
9 |
+
if ( !function_exists( 'microtime_float' ) ) {
|
10 |
+
function microtime_float()
|
11 |
+
{
|
12 |
+
list($usec, $sec) = explode(" ", microtime());
|
13 |
+
return ((float)$usec + (float)$sec);
|
14 |
+
}
|
15 |
+
}
|
16 |
+
|
17 |
+
if (!class_exists('ws_broken_link_checker')) {
|
18 |
+
|
19 |
+
class wsBrokenLinkChecker {
|
20 |
+
var $conf;
|
21 |
+
|
22 |
+
var $loader;
|
23 |
+
var $my_basename = '';
|
24 |
+
|
25 |
+
var $db_version = 2;
|
26 |
+
|
27 |
+
var $execution_start_time; //Used for a simple internal execution timer in start_timer()/execution_time()
|
28 |
+
var $lockfile_handle = null;
|
29 |
+
|
30 |
+
/**
|
31 |
+
* wsBrokenLinkChecker::wsBrokenLinkChecker()
|
32 |
+
* Class constructor
|
33 |
+
*
|
34 |
+
* @param string $loader The fully qualified filename of the loader script that WP identifies as the "main" plugin file.
|
35 |
+
* @param blcConfigurationManager $conf An instance of the configuration manager
|
36 |
+
* @return void
|
37 |
+
*/
|
38 |
+
function wsBrokenLinkChecker ( $loader, $conf ) {
|
39 |
+
global $wpdb;
|
40 |
+
|
41 |
+
$this->loader = $loader;
|
42 |
+
$this->conf = $conf;
|
43 |
+
|
44 |
+
add_action('activate_' . plugin_basename( $this->loader ), array(&$this,'activation'));
|
45 |
+
$this->my_basename = plugin_basename( $this->loader );
|
46 |
+
|
47 |
+
add_action('admin_menu', array(&$this,'admin_menu'));
|
48 |
+
|
49 |
+
//These hooks update the plugin's internal records when posts are added, deleted or modified.
|
50 |
+
add_action('delete_post', array(&$this,'post_deleted'));
|
51 |
+
add_action('save_post', array(&$this,'post_saved'));
|
52 |
+
|
53 |
+
//These do the same for (blogroll) links.
|
54 |
+
add_action('add_link', array(&$this,'hook_add_link'));
|
55 |
+
add_action('edit_link', array(&$this,'hook_edit_link'));
|
56 |
+
add_action('delete_link', array(&$this,'hook_delete_link'));
|
57 |
+
|
58 |
+
//Load jQuery on Dashboard pages (possibly redundant as WP already does that)
|
59 |
+
add_action('admin_print_scripts', array(&$this,'admin_print_scripts'));
|
60 |
+
|
61 |
+
//The dashboard widget
|
62 |
+
add_action('wp_dashboard_setup', array(&$this, 'hook_wp_dashboard_setup'));
|
63 |
+
|
64 |
+
//AJAXy hooks
|
65 |
+
//TODO: Check nonces in AJAX hooks
|
66 |
+
add_action( 'wp_ajax_blc_full_status', array(&$this,'ajax_full_status') );
|
67 |
+
add_action( 'wp_ajax_blc_dashboard_status', array(&$this,'ajax_dashboard_status') );
|
68 |
+
add_action( 'wp_ajax_blc_work', array(&$this,'ajax_work') );
|
69 |
+
add_action( 'wp_ajax_blc_discard', array(&$this,'ajax_discard') );
|
70 |
+
add_action( 'wp_ajax_blc_edit', array(&$this,'ajax_edit') );
|
71 |
+
add_action( 'wp_ajax_blc_link_details', array(&$this,'ajax_link_details') );
|
72 |
+
add_action( 'wp_ajax_blc_exclude_link', array(&$this,'ajax_exclude_link') );
|
73 |
+
add_action( 'wp_ajax_blc_unlink', array(&$this,'ajax_unlink') );
|
74 |
+
|
75 |
+
//Check if it's possible to create a lockfile and nag the user about it if not.
|
76 |
+
if ( $this->lockfile_name() ){
|
77 |
+
//Lockfiles work, so it's safe to enable the footer hook that will call the worker
|
78 |
+
//function via AJAX.
|
79 |
+
add_action('admin_footer', array(&$this,'admin_footer'));
|
80 |
+
} else {
|
81 |
+
//No lockfiles, nag nag nag!
|
82 |
+
add_action( 'admin_notices', array( &$this, 'lockfile_warning' ) );
|
83 |
+
}
|
84 |
+
|
85 |
+
}
|
86 |
+
|
87 |
+
function admin_footer(){
|
88 |
+
?>
|
89 |
+
<!-- wsblc admin footer -->
|
90 |
+
<div id='wsblc_updater_div'></div>
|
91 |
+
<script type='text/javascript'>
|
92 |
+
(function($){
|
93 |
+
|
94 |
+
function blcDoWork(){
|
95 |
+
$.post(
|
96 |
+
"<?php bloginfo( 'wpurl' ); ?>/wp-admin/admin-ajax.php",
|
97 |
+
{
|
98 |
+
'action' : 'blc_work'
|
99 |
+
},
|
100 |
+
function (data, textStatus){
|
101 |
+
|
102 |
+
}
|
103 |
+
);
|
104 |
+
}
|
105 |
+
//Call it the first time
|
106 |
+
blcDoWork();
|
107 |
+
|
108 |
+
//Then call it periodically every X seconds
|
109 |
+
setInterval(blcDoWork, <?php echo (intval($this->conf->options['max_execution_time']) + 1 )*1000; ?>);
|
110 |
+
|
111 |
+
})(jQuery);
|
112 |
+
</script>
|
113 |
+
<!-- /wsblc admin footer -->
|
114 |
+
<?php
|
115 |
+
}
|
116 |
+
|
117 |
+
function is_excluded($url){
|
118 |
+
if (!is_array($this->conf->options['exclusion_list'])) return false;
|
119 |
+
foreach($this->conf->options['exclusion_list'] as $excluded_word){
|
120 |
+
if (stristr($url, $excluded_word)){
|
121 |
+
return true;
|
122 |
+
}
|
123 |
+
}
|
124 |
+
return false;
|
125 |
+
}
|
126 |
+
|
127 |
+
function dashboard_widget(){
|
128 |
+
?>
|
129 |
+
<p id='wsblc_activity_box'>Loading...</p>
|
130 |
+
<script type='text/javascript'>
|
131 |
+
jQuery(function($){
|
132 |
+
var blc_was_autoexpanded = false;
|
133 |
+
|
134 |
+
function blcDashboardStatus(){
|
135 |
+
$.getJSON(
|
136 |
+
"<?php bloginfo( 'wpurl' ); ?>/wp-admin/admin-ajax.php",
|
137 |
+
{
|
138 |
+
'action' : 'blc_dashboard_status'
|
139 |
+
},
|
140 |
+
function (data, textStatus){
|
141 |
+
if ( data && ( typeof(data.text) != 'undefined' ) ) {
|
142 |
+
$('#wsblc_activity_box').html(data.text);
|
143 |
+
<?php if ( $this->conf->options['autoexpand_widget'] ) { ?>
|
144 |
+
//Expand the widget if there are broken links.
|
145 |
+
//Do this only once per pageload so as not to annoy the user.
|
146 |
+
if ( !blc_was_autoexpanded && ( data.status.broken_links > 0 ) ){
|
147 |
+
$('#blc_dashboard_widget.postbox').removeClass('closed');
|
148 |
+
blc_was_autoexpanded = true;
|
149 |
+
};
|
150 |
+
<?php } ?>
|
151 |
+
} else {
|
152 |
+
$('#wsblc_activity_box').html('[ Network error ]');
|
153 |
+
}
|
154 |
+
|
155 |
+
setTimeout( blcDashboardStatus, 120*1000 ); //...update every two minutes
|
156 |
+
}
|
157 |
+
);
|
158 |
+
}
|
159 |
+
|
160 |
+
blcDashboardStatus();//Call it the first time
|
161 |
+
|
162 |
+
});
|
163 |
+
</script>
|
164 |
+
<?php
|
165 |
+
}
|
166 |
+
|
167 |
+
function dashboard_widget_control( $widget_id, $form_inputs = array() ){
|
168 |
+
if ( 'POST' == $_SERVER['REQUEST_METHOD'] && 'blc_dashboard_widget' == $_POST['widget_id'] ) {
|
169 |
+
//It appears $form_inputs isn't used in the current WP version, so lets just use $_POST
|
170 |
+
$this->conf->options['autoexpand_widget'] = !empty($_POST['blc-autoexpand']);
|
171 |
+
$this->conf->save_options();
|
172 |
+
}
|
173 |
+
|
174 |
+
?>
|
175 |
+
<p><label for="blc-autoexpand">
|
176 |
+
<input id="blc-autoexpand" name="blc-autoexpand" type="checkbox" value="1" <?php if ( $this->conf->options['autoexpand_widget'] ) echo 'checked="checked"'; ?> />
|
177 |
+
Automatically expand the widget if broken links have been detected
|
178 |
+
</label></p>
|
179 |
+
<?php
|
180 |
+
}
|
181 |
+
|
182 |
+
function admin_print_scripts(){
|
183 |
+
//jQuery is used for AJAX and effects
|
184 |
+
wp_enqueue_script('jquery');
|
185 |
+
wp_enqueue_script('jquery-ui-core');
|
186 |
+
}
|
187 |
+
|
188 |
+
/**
|
189 |
+
* ws_broken_link_checker::post_deleted()
|
190 |
+
* A hook for post_deleted. Remove link instances associated with that post.
|
191 |
+
*
|
192 |
+
* @param int $post_id
|
193 |
+
* @return void
|
194 |
+
*/
|
195 |
+
function post_deleted($post_id){
|
196 |
+
global $wpdb;
|
197 |
+
|
198 |
+
//FB::log($post_id, "Post deleted");
|
199 |
+
//Remove this post's instances
|
200 |
+
$q = "DELETE FROM {$wpdb->prefix}blc_instances
|
201 |
+
WHERE source_id = %d AND (source_type = 'post' OR source_type='custom_field')";
|
202 |
+
$q = $wpdb->prepare($q, intval($post_id) );
|
203 |
+
|
204 |
+
//FB::log($q, 'Executing query');
|
205 |
+
|
206 |
+
if ( $wpdb->query( $q ) === false ){
|
207 |
+
//FB::error($wpdb->last_error, "Database error");
|
208 |
+
}
|
209 |
+
|
210 |
+
//Remove the synch record
|
211 |
+
$q = "DELETE FROM {$wpdb->prefix}blc_synch
|
212 |
+
WHERE source_id = %d AND source_type = 'post'";
|
213 |
+
$wpdb->query( $wpdb->prepare($q, intval($post_id)) );
|
214 |
+
|
215 |
+
//Remove any dangling link records
|
216 |
+
$this->cleanup_links();
|
217 |
+
}
|
218 |
+
|
219 |
+
function post_saved($post_id){
|
220 |
+
global $wpdb;
|
221 |
+
|
222 |
+
$post = get_post($post_id);
|
223 |
+
//Only check links in posts, not revisions and attachments
|
224 |
+
if ( ($post->post_type != 'post') && ($post->post_type != 'page') ) return null;
|
225 |
+
//Only check published posts
|
226 |
+
if ( $post->post_status != 'publish' ) return null;
|
227 |
+
|
228 |
+
$this->mark_unsynched( $post_id, 'post' );
|
229 |
+
}
|
230 |
+
|
231 |
+
function initiate_recheck(){
|
232 |
+
global $wpdb;
|
233 |
+
|
234 |
+
//Delete all discovered instances
|
235 |
+
$wpdb->query("TRUNCATE {$wpdb->prefix}blc_instances");
|
236 |
+
|
237 |
+
//Delete all discovered links
|
238 |
+
$wpdb->query("TRUNCATE {$wpdb->prefix}blc_links");
|
239 |
+
|
240 |
+
//Mark all posts, custom fields and bookmarks for processing.
|
241 |
+
$this->resynch();
|
242 |
+
}
|
243 |
+
|
244 |
+
function resynch(){
|
245 |
+
global $wpdb;
|
246 |
+
|
247 |
+
//Drop all synchronization records
|
248 |
+
$wpdb->query("TRUNCATE {$wpdb->prefix}blc_synch");
|
249 |
+
|
250 |
+
|
251 |
+
//Create new synchronization records for posts
|
252 |
+
$q = "INSERT INTO {$wpdb->prefix}blc_synch(source_id, source_type, synched)
|
253 |
+
SELECT id, 'post', 0
|
254 |
+
FROM {$wpdb->posts}
|
255 |
+
WHERE
|
256 |
+
{$wpdb->posts}.post_status = 'publish'
|
257 |
+
AND {$wpdb->posts}.post_type IN ('post', 'page')";
|
258 |
+
$wpdb->query( $q );
|
259 |
+
|
260 |
+
//Create new synchronization records for bookmarks (the blogroll)
|
261 |
+
$q = "INSERT INTO {$wpdb->prefix}blc_synch(source_id, source_type, synched)
|
262 |
+
SELECT link_id, 'blogroll', 0
|
263 |
+
FROM {$wpdb->links}
|
264 |
+
WHERE 1";
|
265 |
+
$wpdb->query( $q );
|
266 |
+
|
267 |
+
//Delete invalid instances
|
268 |
+
$this->cleanup_instances();
|
269 |
+
//Delete orphaned links
|
270 |
+
$this->cleanup_links();
|
271 |
+
|
272 |
+
$this->conf->options['need_resynch'] = true;
|
273 |
+
$this->conf->save_options();
|
274 |
+
}
|
275 |
+
|
276 |
+
function mark_unsynched( $source_id, $source_type ){
|
277 |
+
global $wpdb;
|
278 |
+
|
279 |
+
$q = "REPLACE INTO {$wpdb->prefix}blc_synch( source_id, source_type, synched, last_synch)
|
280 |
+
VALUES( %d, %s, %d, NOW() )";
|
281 |
+
$rez = $wpdb->query( $wpdb->prepare( $q, $source_id, $source_type, 0 ) );
|
282 |
+
|
283 |
+
if ( !$this->conf->options['need_resynch'] ){
|
284 |
+
$this->conf->options['need_resynch'] = true;
|
285 |
+
$this->conf->save_options();
|
286 |
+
}
|
287 |
+
|
288 |
+
return $rez;
|
289 |
+
}
|
290 |
+
|
291 |
+
function mark_synched( $source_id, $source_type ){
|
292 |
+
global $wpdb;
|
293 |
+
//FB::log("Marking $source_type $source_id as synched.");
|
294 |
+
$q = "REPLACE INTO {$wpdb->prefix}blc_synch( source_id, source_type, synched, last_synch)
|
295 |
+
VALUES( %d, %s, %d, NOW() )";
|
296 |
+
return $wpdb->query( $wpdb->prepare( $q, $source_id, $source_type, 1 ) );
|
297 |
+
}
|
298 |
+
|
299 |
+
function activation(){
|
300 |
+
//Prepare the database.
|
301 |
+
$this->upgrade_database();
|
302 |
+
|
303 |
+
//Clear the instance table and mark all posts and other parse-able objects as unsynchronized.
|
304 |
+
$this->resynch();
|
305 |
+
|
306 |
+
//Save the default options.
|
307 |
+
$this->conf->save_options();
|
308 |
+
|
309 |
+
//And optimize my DB tables, too (for good measure)
|
310 |
+
$this->optimize_database();
|
311 |
+
}
|
312 |
+
|
313 |
+
/**
|
314 |
+
* ws_broken_link_checker::upgrade_database()
|
315 |
+
* Create and/or upgrade database tables
|
316 |
+
*
|
317 |
+
* @return void
|
318 |
+
*/
|
319 |
+
function upgrade_database(){
|
320 |
+
global $wpdb;
|
321 |
+
|
322 |
+
//Do we need to upgrade?
|
323 |
+
//[ Disabled for now, was causing issues when the user manually deletes the plugin ]
|
324 |
+
//if ( $this->db_version == $this->conf->options['current_db_version'] ) return;
|
325 |
+
|
326 |
+
//Delete tables used by older versions of the plugin
|
327 |
+
$rez = $wpdb->query( "DROP TABLE IF EXISTS {$wpdb->prefix}blc_linkdata, {$wpdb->prefix}blc_postdata" );
|
328 |
+
if ( $rez === false ){
|
329 |
+
//FB::error($wpdb->last_error, "Database error");
|
330 |
+
return false;
|
331 |
+
}
|
332 |
+
|
333 |
+
require_once (ABSPATH . 'wp-admin/includes/upgrade.php');
|
334 |
+
|
335 |
+
//Create the link table if it doesn't exist yet.
|
336 |
+
$q = "CREATE TABLE IF NOT EXISTS {$wpdb->prefix}blc_links (
|
337 |
+
link_id int(20) unsigned NOT NULL auto_increment,
|
338 |
+
url text CHARACTER SET latin1 COLLATE latin1_general_cs NOT NULL,
|
339 |
+
last_check datetime NOT NULL default '0000-00-00 00:00:00',
|
340 |
+
check_count int(2) unsigned NOT NULL default '0',
|
341 |
+
final_url text CHARACTER SET latin1 COLLATE latin1_general_cs NOT NULL,
|
342 |
+
redirect_count smallint(5) unsigned NOT NULL,
|
343 |
+
log text NOT NULL,
|
344 |
+
http_code smallint(6) NOT NULL,
|
345 |
+
request_duration float NOT NULL default '0',
|
346 |
+
timeout tinyint(1) unsigned NOT NULL default '0',
|
347 |
+
|
348 |
+
PRIMARY KEY (link_id),
|
349 |
+
KEY url (url(150)),
|
350 |
+
KEY final_url (final_url(150)),
|
351 |
+
KEY http_code (http_code),
|
352 |
+
KEY timeout (timeout)
|
353 |
+
)";
|
354 |
+
if ( $wpdb->query( $q ) === false ){
|
355 |
+
//FB::error($wpdb->last_error, "Database error");
|
356 |
+
return false;
|
357 |
+
};
|
358 |
+
|
359 |
+
//Fix URL fields so that they are collated as case-sensitive (this can't be done via dbDelta)
|
360 |
+
$q = "ALTER TABLE {$wpdb->prefix}blc_links
|
361 |
+
MODIFY url text CHARACTER SET latin1 COLLATE latin1_general_cs NOT NULL,
|
362 |
+
MODIFY final_url text CHARACTER SET latin1 COLLATE latin1_general_cs NOT NULL";
|
363 |
+
if ( $wpdb->query( $q ) === false ){
|
364 |
+
//FB::error($wpdb->last_error, "Database error");
|
365 |
+
return false;
|
366 |
+
};
|
367 |
+
|
368 |
+
//Create the instance table if it doesn't exist yet.
|
369 |
+
$q = "CREATE TABLE IF NOT EXISTS {$wpdb->prefix}blc_instances (
|
370 |
+
instance_id int(10) unsigned NOT NULL auto_increment,
|
371 |
+
link_id int(10) unsigned NOT NULL,
|
372 |
+
source_id int(10) unsigned NOT NULL,
|
373 |
+
source_type enum('post','blogroll','custom_field') NOT NULL default 'post',
|
374 |
+
link_text varchar(250) NOT NULL,
|
375 |
+
instance_type enum('link','image') NOT NULL default 'link',
|
376 |
+
|
377 |
+
PRIMARY KEY (instance_id),
|
378 |
+
KEY link_id (link_id),
|
379 |
+
KEY source_id (source_id,source_type)
|
380 |
+
)";
|
381 |
+
if ( $wpdb->query( $q ) === false ){
|
382 |
+
//FB::error($wpdb->last_error, "Database error");
|
383 |
+
return false;
|
384 |
+
};
|
385 |
+
|
386 |
+
//....
|
387 |
+
$q = "CREATE TABLE IF NOT EXISTS {$wpdb->prefix}blc_synch (
|
388 |
+
source_id int(20) unsigned NOT NULL,
|
389 |
+
source_type enum('post','blogroll') NOT NULL,
|
390 |
+
synched tinyint(3) unsigned NOT NULL,
|
391 |
+
last_synch datetime NOT NULL,
|
392 |
+
PRIMARY KEY (source_id, source_type),
|
393 |
+
KEY synched (synched)
|
394 |
+
)";
|
395 |
+
if ( $wpdb->query( $q ) === false ){
|
396 |
+
//FB::error($wpdb->last_error, "Database error");
|
397 |
+
return false;
|
398 |
+
};
|
399 |
+
|
400 |
+
$this->conf->options['current_db_version'] = $this->db_version;
|
401 |
+
$this->conf->save_options();
|
402 |
+
|
403 |
+
return true;
|
404 |
+
}
|
405 |
+
|
406 |
+
/**
|
407 |
+
* wsBrokenLinkChecker::optimize_database()
|
408 |
+
* Optimize the plugin's tables
|
409 |
+
*
|
410 |
+
* @return void
|
411 |
+
*/
|
412 |
+
function optimize_database(){
|
413 |
+
global $wpdb;
|
414 |
+
|
415 |
+
$wpdb->query("OPTIMIZE TABLE {$wpdb->prefix}blc_links, {$wpdb->prefix}blc_instances, {$wpdb->prefix}blc_synch");
|
416 |
+
}
|
417 |
+
|
418 |
+
function admin_menu(){
|
419 |
+
add_options_page('Link Checker Settings', 'Link Checker', 'manage_options',
|
420 |
+
'link-checker-settings',array(&$this, 'options_page'));
|
421 |
+
if (current_user_can('manage_options'))
|
422 |
+
add_filter('plugin_action_links', array(&$this, 'plugin_action_links'), 10, 2);
|
423 |
+
|
424 |
+
add_management_page('View Broken Links', 'Broken Links', 'edit_others_posts',
|
425 |
+
'view-broken-links',array(&$this, 'links_page'));
|
426 |
+
}
|
427 |
+
|
428 |
+
/**
|
429 |
+
* plugin_action_links()
|
430 |
+
* Handler for the 'plugin_action_links' hook. Adds a "Settings" link to this plugin's entry
|
431 |
+
* on the plugin list.
|
432 |
+
*
|
433 |
+
* @param array $links
|
434 |
+
* @param string $file
|
435 |
+
* @return array
|
436 |
+
*/
|
437 |
+
function plugin_action_links($links, $file) {
|
438 |
+
if ($file == $this->my_basename)
|
439 |
+
$links[] = "<a href='options-general.php?page=link-checker-settings'>" . __('Settings') . "</a>";
|
440 |
+
return $links;
|
441 |
+
}
|
442 |
+
|
443 |
+
function mytruncate($str, $max_length=50){
|
444 |
+
if(strlen($str)<=$max_length) return $str;
|
445 |
+
return (substr($str, 0, $max_length-3).'...');
|
446 |
+
}
|
447 |
+
|
448 |
+
function options_page(){
|
449 |
+
if (isset($_GET['recheck']) && ($_GET['recheck'] == 'true')) {
|
450 |
+
$this->initiate_recheck();
|
451 |
+
}
|
452 |
+
if (isset($_GET['updated']) && ($_GET['updated'] == 'true')) {
|
453 |
+
if(isset($_POST['submit'])) {
|
454 |
+
check_admin_referer('link-checker-options');
|
455 |
+
|
456 |
+
$new_execution_time = intval($_POST['max_execution_time']);
|
457 |
+
if( $new_execution_time > 0 ){
|
458 |
+
$this->conf->options['max_execution_time'] = $new_execution_time;
|
459 |
+
}
|
460 |
+
|
461 |
+
$new_check_threshold=intval($_POST['check_threshold']);
|
462 |
+
if( $new_check_threshold > 0 ){
|
463 |
+
$this->conf->options['check_threshold'] = $new_check_threshold;
|
464 |
+
}
|
465 |
+
|
466 |
+
$this->conf->options['mark_broken_links'] = !empty($_POST['mark_broken_links']);
|
467 |
+
$new_broken_link_css = trim($_POST['broken_link_css']);
|
468 |
+
$this->conf->options['broken_link_css'] = $new_broken_link_css;
|
469 |
+
|
470 |
+
$this->conf->options['exclusion_list']=array_filter( preg_split( '/[\s\r\n]+/',
|
471 |
+
$_POST['exclusion_list'], -1, PREG_SPLIT_NO_EMPTY ) );
|
472 |
+
//TODO: Maybe update affected links when exclusion list changes (expensive).
|
473 |
+
|
474 |
+
|
475 |
+
$new_custom_fields = array_filter( preg_split( '/[\s\r\n]+/',
|
476 |
+
$_POST['blc_custom_fields'], -1, PREG_SPLIT_NO_EMPTY ) );
|
477 |
+
$diff1 = array_diff( $new_custom_fields, $this->conf->options['custom_fields'] );
|
478 |
+
$diff2 = array_diff( $this->conf->options['custom_fields'], $new_custom_fields );
|
479 |
+
$this->conf->options['custom_fields'] = $new_custom_fields;
|
480 |
+
|
481 |
+
$this->conf->save_options();
|
482 |
+
|
483 |
+
/*
|
484 |
+
If the list of custom fields was modified then we MUST resynchronize or
|
485 |
+
custom fields linked with existing posts may not be detected. This is somewhat
|
486 |
+
inefficient.
|
487 |
+
*/
|
488 |
+
if ( ( count($diff1) > 0 ) || ( count($diff2) > 0 ) ){
|
489 |
+
$this->resynch();
|
490 |
+
}
|
491 |
+
}
|
492 |
+
|
493 |
+
}
|
494 |
+
|
495 |
+
?>
|
496 |
+
<div class="wrap"><h2>Broken Link Checker Options</h2>
|
497 |
+
|
498 |
+
<form name="link_checker_options" method="post" action="<?php echo basename($_SERVER['PHP_SELF']); ?>?page=link-checker-settings&updated=true">
|
499 |
+
<?php
|
500 |
+
wp_nonce_field('link-checker-options');
|
501 |
+
?>
|
502 |
+
|
503 |
+
<table class="form-table">
|
504 |
+
|
505 |
+
<tr valign="top">
|
506 |
+
<th scope="row">Status</th>
|
507 |
+
<td>
|
508 |
+
|
509 |
+
|
510 |
+
<div id='wsblc_full_status'>
|
511 |
+
<br/><br/><br/>
|
512 |
+
</div>
|
513 |
+
<script type='text/javascript'>
|
514 |
+
(function($){
|
515 |
+
|
516 |
+
function blcUpdateStatus(){
|
517 |
+
$.getJSON(
|
518 |
+
"<?php bloginfo( 'wpurl' ); ?>/wp-admin/admin-ajax.php",
|
519 |
+
{
|
520 |
+
'action' : 'blc_full_status'
|
521 |
+
},
|
522 |
+
function (data, textStatus){
|
523 |
+
if ( data && ( typeof(data['text']) != 'undefined' ) ){
|
524 |
+
$('#wsblc_full_status').html(data.text);
|
525 |
+
} else {
|
526 |
+
$('#wsblc_full_status').html('[ Network error ]');
|
527 |
+
}
|
528 |
+
|
529 |
+
setTimeout(blcUpdateStatus, 10000); //...update every 10 seconds
|
530 |
+
}
|
531 |
+
);
|
532 |
+
}
|
533 |
+
blcUpdateStatus();//Call it the first time
|
534 |
+
|
535 |
+
})(jQuery);
|
536 |
+
</script>
|
537 |
+
<?php //JHS: Recheck all posts link: ?>
|
538 |
+
<p><input class="button" type="button" name="recheckbutton" value="Re-check all pages" onclick="location.replace('<?php echo basename($_SERVER['PHP_SELF']); ?>?page=link-checker-settings&recheck=true')" /></p>
|
539 |
+
</td>
|
540 |
+
</tr>
|
541 |
+
|
542 |
+
<tr valign="top">
|
543 |
+
<th scope="row">Check each link</th>
|
544 |
+
<td>
|
545 |
+
|
546 |
+
Every <input type="text" name="check_threshold" id="check_threshold"
|
547 |
+
value="<?php echo $this->conf->options['check_threshold']; ?>" size='5' maxlength='3'/>
|
548 |
+
hours
|
549 |
+
<br/>
|
550 |
+
<span class="description">
|
551 |
+
Existing links will be checked this often. New links will usually be checked ASAP.
|
552 |
+
</span>
|
553 |
+
|
554 |
+
</td>
|
555 |
+
</tr>
|
556 |
+
|
557 |
+
<tr valign="top">
|
558 |
+
<th scope="row">Broken link CSS</th>
|
559 |
+
<td>
|
560 |
+
<input type="checkbox" name="mark_broken_links" id="mark_broken_links"
|
561 |
+
<?php if ($this->conf->options['mark_broken_links']) echo ' checked="checked"'; ?>/>
|
562 |
+
<label for='mark_broken_links'>Apply <em>class="broken_link"</em> to broken links</label><br/>
|
563 |
+
<textarea name="broken_link_css" id="broken_link_css" cols='45' rows='4'/><?php
|
564 |
+
if( isset($this->conf->options['broken_link_css']) )
|
565 |
+
echo $this->conf->options['broken_link_css'];
|
566 |
+
?></textarea>
|
567 |
+
|
568 |
+
</td>
|
569 |
+
</tr>
|
570 |
+
|
571 |
+
<tr valign="top">
|
572 |
+
<th scope="row">Exclusion list</th>
|
573 |
+
<td>Don't check links where the URL contains any of these words (one per line) :<br/>
|
574 |
+
<textarea name="exclusion_list" id="exclusion_list" cols='45' rows='4' wrap='off'/><?php
|
575 |
+
if( isset($this->conf->options['exclusion_list']) )
|
576 |
+
echo implode("\n", $this->conf->options['exclusion_list']);
|
577 |
+
?></textarea>
|
578 |
+
|
579 |
+
</td>
|
580 |
+
</tr>
|
581 |
+
|
582 |
+
<tr valign="top">
|
583 |
+
<th scope="row">Custom fields</th>
|
584 |
+
<td>Check URLs entered in these custom fields (one per line) : <br/>
|
585 |
+
<textarea name="blc_custom_fields" id="blc_custom_fields" cols='45' rows='4' /><?php
|
586 |
+
if( isset($this->conf->options['custom_fields']) )
|
587 |
+
echo implode("\n", $this->conf->options['custom_fields']);
|
588 |
+
?></textarea>
|
589 |
+
|
590 |
+
</td>
|
591 |
+
</tr>
|
592 |
+
|
593 |
+
<tr valign="top">
|
594 |
+
<th scope="row">Max. execution time (advanced)</th>
|
595 |
+
<td>
|
596 |
+
|
597 |
+
<input type="text" name="max_execution_time" id="max_execution_time"
|
598 |
+
value="<?php echo $this->conf->options['max_execution_time']; ?>" size='5' maxlength='3'/>
|
599 |
+
seconds
|
600 |
+
<br/><span class="description">
|
601 |
+
The plugin works by periodically creating a background worker instance that parses your posts looking for links,
|
602 |
+
checks the discovered URLs, and performs other time-consuming tasks. Here you can set for how long, at most,
|
603 |
+
the background instance may run each time before stopping.
|
604 |
+
</span>
|
605 |
+
|
606 |
+
</td>
|
607 |
+
</tr>
|
608 |
+
|
609 |
+
</table>
|
610 |
+
|
611 |
+
<p class="submit"><input type="submit" name="submit" class='button-primary' value="<?php _e('Save Changes') ?>" /></p>
|
612 |
+
</form>
|
613 |
+
</div>
|
614 |
+
<?php
|
615 |
+
}
|
616 |
+
|
617 |
+
function links_page(){
|
618 |
+
global $wpdb;
|
619 |
+
|
620 |
+
//Available filters by link type + the appropriate WHERE expressions
|
621 |
+
$filters = array(
|
622 |
+
'broken' => array(
|
623 |
+
'where_expr' => '( http_code < 200 OR http_code >= 400 OR timeout = 1 ) AND ( check_count > 0 ) AND ( http_code <> ' . BLC_CHECKING . ')',
|
624 |
+
'name' => 'Broken',
|
625 |
+
'heading' => 'Broken Links',
|
626 |
+
'heading_zero' => 'No broken links found'
|
627 |
+
),
|
628 |
+
'redirects' => array(
|
629 |
+
'where_expr' => '( redirect_count > 0 )',
|
630 |
+
'name' => 'Redirects',
|
631 |
+
'heading' => 'Redirected Links',
|
632 |
+
'heading_zero' => 'No redirects found'
|
633 |
+
),
|
634 |
+
|
635 |
+
'all' => array(
|
636 |
+
'where_expr' => '1',
|
637 |
+
'name' => 'All',
|
638 |
+
'heading' => 'Detected Links',
|
639 |
+
'heading_zero' => 'No links found (yet)'
|
640 |
+
),
|
641 |
+
);
|
642 |
+
|
643 |
+
$link_type = isset($_GET['link_type'])?$_GET['link_type']:'broken';
|
644 |
+
if ( !isset($filters[$link_type]) ){
|
645 |
+
$link_type = 'broken';
|
646 |
+
}
|
647 |
+
|
648 |
+
//Get the desired page number (must be > 0)
|
649 |
+
$page = isset($_GET['paged'])?intval($_GET['paged']):'1';
|
650 |
+
if ($page < 1) $page = 1;
|
651 |
+
|
652 |
+
//Links per page [1 - 200]
|
653 |
+
$per_page = isset($_GET['per_page'])?intval($_GET['per_page']):'30';
|
654 |
+
if ($per_page < 1){
|
655 |
+
$per_page = 30;
|
656 |
+
} else if ($per_page > 200){
|
657 |
+
$per_page = 200;
|
658 |
+
}
|
659 |
+
|
660 |
+
//calculate the number of various links
|
661 |
+
foreach ($filters as $filter => $data){
|
662 |
+
$filters[$filter]['count'] = $wpdb->get_var(
|
663 |
+
"SELECT COUNT(*) FROM {$wpdb->prefix}blc_links WHERE ".$data['where_expr'] );
|
664 |
+
}
|
665 |
+
$current_filter = $filters[$link_type];
|
666 |
+
$max_pages = ceil($current_filter['count'] / $per_page);
|
667 |
+
|
668 |
+
|
669 |
+
//Select the required links + 1 instance per link.
|
670 |
+
//Note : The query might be somewhat inefficient, but I can't think of any better way to do this.
|
671 |
+
$q = "SELECT
|
672 |
+
links.*,
|
673 |
+
instances.instance_id, instances.source_id, instances.source_type,
|
674 |
+
instances.link_text, instances.instance_type,
|
675 |
+
COUNT(*) as instance_count,
|
676 |
+
posts.post_title,
|
677 |
+
posts.post_date
|
678 |
+
|
679 |
+
FROM
|
680 |
+
{$wpdb->prefix}blc_links AS links,
|
681 |
+
{$wpdb->prefix}blc_instances as instances LEFT JOIN {$wpdb->posts} as posts ON instances.source_id = posts.ID
|
682 |
+
|
683 |
+
WHERE
|
684 |
+
links.link_id = instances.link_id
|
685 |
+
AND ". $current_filter['where_expr'] ."
|
686 |
+
|
687 |
+
GROUP BY links.link_id
|
688 |
+
LIMIT ".( ($page-1) * $per_page ).", $per_page";
|
689 |
+
//echo "<pre>$q</pre>";
|
690 |
+
|
691 |
+
$links = $wpdb->get_results($q, ARRAY_A);
|
692 |
+
if ($links){
|
693 |
+
/*
|
694 |
+
echo '<pre>';
|
695 |
+
print_r($links);
|
696 |
+
echo '</pre>';
|
697 |
+
//*/
|
698 |
+
} else {
|
699 |
+
echo $wpdb->last_error;
|
700 |
+
}
|
701 |
+
?>
|
702 |
+
|
703 |
+
<script type='text/javascript'>
|
704 |
+
var blc_current_filter = '<?php echo $link_type; ?>';
|
705 |
+
</script>
|
706 |
+
|
707 |
+
<style type='text/css'>
|
708 |
+
.blc-link-editor {
|
709 |
+
font-size: 1em;
|
710 |
+
width: 95%;
|
711 |
+
}
|
712 |
+
|
713 |
+
.blc-excluded-link {
|
714 |
+
background-color: #E2E2E2;
|
715 |
+
}
|
716 |
+
|
717 |
+
.blc-small-image {
|
718 |
+
display : block;
|
719 |
+
float: left;
|
720 |
+
padding-top: 2px;
|
721 |
+
margin-right: 3px;
|
722 |
+
}
|
723 |
+
</style>
|
724 |
+
|
725 |
+
<div class="wrap">
|
726 |
+
<h2><?php
|
727 |
+
//Output a header matching the current filter
|
728 |
+
if ( $current_filter['count'] > 0 ){
|
729 |
+
echo "<span class='current-link-count'>{$current_filter[count]}</span> " . $current_filter['heading'];
|
730 |
+
} else {
|
731 |
+
echo "<span class='current-link-count'></span>" . $current_filter['heading_zero'];
|
732 |
+
}
|
733 |
+
?></h2>
|
734 |
+
|
735 |
+
<div class='tablenav'>
|
736 |
+
<ul class="subsubsub">
|
737 |
+
<?php
|
738 |
+
//Construct a submenu of filter types
|
739 |
+
$items = array();
|
740 |
+
foreach ($filters as $filter => $data){
|
741 |
+
$class = $number_class = '';
|
742 |
+
|
743 |
+
if ( $link_type == $filter ) $class = 'class="current"';
|
744 |
+
if ( $link_type == $filter ) $number_class = 'current-link-count';
|
745 |
+
|
746 |
+
$items[] = "<li><a href='tools.php?page=view-broken-links&link_type=$filter' $class>
|
747 |
+
{$data[name]}</a> <span class='count'>(<span class='$number_class'>{$data[count]}</span>)</span>";
|
748 |
+
}
|
749 |
+
echo implode(' |</li>', $items);
|
750 |
+
unset($items);
|
751 |
+
?>
|
752 |
+
</ul>
|
753 |
+
<?php
|
754 |
+
//Display pagination links
|
755 |
+
$page_links = paginate_links( array(
|
756 |
+
'base' => add_query_arg( 'paged', '%#%' ),
|
757 |
+
'format' => '',
|
758 |
+
'prev_text' => __('«'),
|
759 |
+
'next_text' => __('»'),
|
760 |
+
'total' => $max_pages,
|
761 |
+
'current' => $page
|
762 |
+
));
|
763 |
+
|
764 |
+
if ( $page_links ) {
|
765 |
+
echo '<div class="tablenav-pages">';
|
766 |
+
$page_links_text = sprintf( '<span class="displaying-num">' . __( 'Displaying %s–%s of <span class="current-link-count">%s</span>' ) . '</span>%s',
|
767 |
+
number_format_i18n( ( $page - 1 ) * $per_page + 1 ),
|
768 |
+
number_format_i18n( min( $page * $per_page, count($links) ) ),
|
769 |
+
number_format_i18n( $current_filter['count'] ),
|
770 |
+
$page_links
|
771 |
+
);
|
772 |
+
echo $page_links_text;
|
773 |
+
echo '</div>';
|
774 |
+
}
|
775 |
+
?>
|
776 |
+
|
777 |
+
</div>
|
778 |
+
|
779 |
+
|
780 |
+
|
781 |
+
<?php
|
782 |
+
if($links && (count($links)>0)){
|
783 |
+
?>
|
784 |
+
<table class="widefat">
|
785 |
+
<thead>
|
786 |
+
<tr>
|
787 |
+
|
788 |
+
<th scope="col">Source
|
789 |
+
</th>
|
790 |
+
<th scope="col">Link Text</th>
|
791 |
+
<th scope="col">URL</th>
|
792 |
+
|
793 |
+
<?php if ( 'broken' == $link_type ) { ?>
|
794 |
+
<th scope="col"> </th>
|
795 |
+
<?php } ?>
|
796 |
+
|
797 |
+
</tr>
|
798 |
+
</thead>
|
799 |
+
<tbody id="the-list">
|
800 |
+
<?php
|
801 |
+
$rowclass = ''; $rownum = 0;
|
802 |
+
foreach ($links as $link) {
|
803 |
+
$rownum++;
|
804 |
+
|
805 |
+
$rowclass = 'alternate' == $rowclass ? '' : 'alternate';
|
806 |
+
$excluded = $this->is_excluded( $link['url'] );
|
807 |
+
if ( $excluded ) $rowclass .= ' blc-excluded-link';
|
808 |
+
|
809 |
+
?>
|
810 |
+
<tr id='<?php echo "blc-row-$rownum"; ?>' class='blc-row <?php echo $rowclass; ?>'>
|
811 |
+
<td class='post-title column-title'>
|
812 |
+
<span class='blc-link-id' style='display:none;'><?php echo $link['link_id']; ?></span>
|
813 |
+
<?php
|
814 |
+
if ( ('post' == $link['source_type']) || ('custom_field' == $link['source_type']) ){
|
815 |
+
|
816 |
+
echo "<a class='row-title' href='post.php?action=edit&post=$link[source_id]' title='Edit this post'>{$link[post_title]}</a>";
|
817 |
+
|
818 |
+
//Output inline action links (copied from edit-post-rows.php)
|
819 |
+
$actions = array();
|
820 |
+
if ( current_user_can('edit_post', $link['source_id']) ) {
|
821 |
+
$actions['edit'] = '<span class="edit"><a href="' . get_edit_post_link($link['source_id'], true) . '" title="' . attribute_escape(__('Edit this post')) . '">' . __('Edit') . '</a>';
|
822 |
+
$actions['delete'] = "<span class='delete'><a class='submitdelete' title='" . attribute_escape(__('Delete this post')) . "' href='" . wp_nonce_url("post.php?action=delete&post=".$link['source_id'], 'delete-post_' . $link['source_id']) . "' onclick=\"if ( confirm('" . js_escape(sprintf( __("You are about to delete the post '%s'\n 'Cancel' to stop, 'OK' to delete."), $link['post_title'] )) . "') ) { return true;}return false;\">" . __('Delete') . "</a>";
|
823 |
+
}
|
824 |
+
$actions['view'] = '<span class="view"><a href="' . get_permalink($link['source_id']) . '" title="' . attribute_escape(sprintf(__('View "%s"'), $link['post_title'])) . '" rel="permalink">' . __('View') . '</a>';
|
825 |
+
echo '<div class="row-actions">';
|
826 |
+
echo implode(' | </span>', $actions);
|
827 |
+
echo '</div>';
|
828 |
+
|
829 |
+
} elseif ( 'blogroll' == $link['source_type'] ) {
|
830 |
+
|
831 |
+
echo "<a class='row-title' href='link.php?action=edit&link_id=$link[source_id]' title='Edit this bookmark'>{$link[link_text]}</a>";
|
832 |
+
|
833 |
+
//Output inline action links
|
834 |
+
$actions = array();
|
835 |
+
if ( current_user_can('manage_links') ) {
|
836 |
+
$actions['edit'] = '<span class="edit"><a href="link.php?action=edit&link_id=' . $link['source_id'] . '" title="' . attribute_escape(__('Edit this bookmark')) . '">' . __('Edit') . '</a>';
|
837 |
+
$actions['delete'] = "<span class='delete'><a class='submitdelete' href='" . wp_nonce_url("link.php?action=delete&link_id={$link[source_id]}", 'delete-bookmark_' . $link['source_id']) . "' onclick=\"if ( confirm('" . js_escape(sprintf( __("You are about to delete this link '%s'\n 'Cancel' to stop, 'OK' to delete."), $link['link_text'])) . "') ) { return true;}return false;\">" . __('Delete') . "</a>";
|
838 |
+
}
|
839 |
+
|
840 |
+
echo '<div class="row-actions">';
|
841 |
+
echo implode(' | </span>', $actions);
|
842 |
+
echo '</div>';
|
843 |
+
|
844 |
+
} elseif ( empty($link['source_type']) ){
|
845 |
+
|
846 |
+
echo "[An orphaned link! This is a bug.]";
|
847 |
+
|
848 |
+
}
|
849 |
+
?>
|
850 |
+
</td>
|
851 |
+
<td class='blc-link-text'><?php
|
852 |
+
if ( 'post' == $link['source_type'] ){
|
853 |
+
|
854 |
+
if ( 'link' == $link['instance_type'] ) {
|
855 |
+
print strip_tags($link['link_text']);
|
856 |
+
} elseif ( 'image' == $link['instance_type'] ){
|
857 |
+
echo "<img src='" . WP_PLUGIN_URL . "/broken-link-checker/images/image.png' class='blc-small-image' alt='Image' title='Image'> Image";
|
858 |
+
} else {
|
859 |
+
echo '[ ??? ]';
|
860 |
+
}
|
861 |
+
|
862 |
+
} elseif ( 'custom_field' == $link['source_type'] ){
|
863 |
+
|
864 |
+
echo "<img src='" . WP_PLUGIN_URL . "/broken-link-checker/images/script_code.png' class='blc-small-image' title='Custom field' alt='Custom field'> ";
|
865 |
+
echo "<code>".$link['link_text']."</code>";
|
866 |
+
|
867 |
+
} elseif ( 'blogroll' == $link['source_type'] ){
|
868 |
+
//echo $link['link_text'];
|
869 |
+
echo "<img src='" . WP_PLUGIN_URL . "/broken-link-checker/images/link.png' class='blc-small-image' title='Bookmark' alt='Bookmark'> Bookmark";
|
870 |
+
}
|
871 |
+
?>
|
872 |
+
</td>
|
873 |
+
<td class='column-url'>
|
874 |
+
<a href='<?php print $link['url']; ?>' target='_blank' class='blc-link-url'>
|
875 |
+
<?php print $this->mytruncate($link['url']); ?></a>
|
876 |
+
<input type='text' id='link-editor-<?php print $rownum; ?>'
|
877 |
+
value='<?php print attribute_escape($link['url']); ?>'
|
878 |
+
class='blc-link-editor' style='display:none' />
|
879 |
+
<?php
|
880 |
+
//Output inline action links for the link/URL
|
881 |
+
$actions = array();
|
882 |
+
|
883 |
+
$actions['details'] = "<span class='view'><a class='blc-details-button' href='javascript:void(0)' title='Show more info about this link'>Details</a>";
|
884 |
+
|
885 |
+
$actions['delete'] = "<span class='delete'><a class='submitdelete blc-unlink-button' title='Remove this link from all posts' ".
|
886 |
+
"id='unlink-button-$rownum' href='javascript:void(0);'>Unlink</a>";
|
887 |
+
|
888 |
+
if ( $excluded ){
|
889 |
+
$actions['exclude'] = "<span class='delete'>Excluded";
|
890 |
+
} else {
|
891 |
+
$actions['exclude'] = "<span class='delete'><a class='submitdelete blc-exclude-button' title='Add this URL to the exclusion list' ".
|
892 |
+
"id='exclude-button-$rownum' href='javascript:void(0);'>Exclude</a>";
|
893 |
+
}
|
894 |
+
|
895 |
+
$actions['edit'] = "<span class='edit'><a href='javascript:void(0)' class='blc-edit-button' title='Edit link URL'>Edit URL</a>";
|
896 |
+
|
897 |
+
echo '<div class="row-actions">';
|
898 |
+
echo implode(' | </span>', $actions);
|
899 |
+
|
900 |
+
echo "<span style='display:none' class='blc-cancel-button-container'> ",
|
901 |
+
"| <a href='javascript:void(0)' class='blc-cancel-button' title='Cancel URL editing'>Cancel</a></span>";
|
902 |
+
|
903 |
+
echo '</div>';
|
904 |
+
?>
|
905 |
+
</td>
|
906 |
+
<?php if ( 'broken' == $link_type ) { ?>
|
907 |
+
<td><a href='javascript:void(0);'
|
908 |
+
id='discard_button-<?php print $rownum; ?>'
|
909 |
+
class='blc-discard-button'
|
910 |
+
title='Remove this message and mark the link as valid'>Discard</a>
|
911 |
+
</td>
|
912 |
+
<?php } ?>
|
913 |
+
</tr>
|
914 |
+
<!-- Link details -->
|
915 |
+
<tr id='<?php print "link-details-$rownum"; ?>' style='display:none;' class='blc-link-details'>
|
916 |
+
<td colspan='4'><?php $this->link_details_row($link); ?></td>
|
917 |
+
</tr><?php
|
918 |
+
}
|
919 |
+
?></tbody></table><?php
|
920 |
+
|
921 |
+
//Also display pagination links at the bottom
|
922 |
+
if ( $page_links ) {
|
923 |
+
echo '<div class="tablenav"><div class="tablenav-pages">';
|
924 |
+
$page_links_text = sprintf( '<span class="displaying-num">' . __( 'Displaying %s–%s of <span class="current-link-count">%s</span>' ) . '</span>%s',
|
925 |
+
number_format_i18n( ( $page - 1 ) * $per_page + 1 ),
|
926 |
+
number_format_i18n( min( $page * $per_page, count($links) ) ),
|
927 |
+
number_format_i18n( $current_filter['count'] ),
|
928 |
+
$page_links
|
929 |
+
);
|
930 |
+
echo $page_links_text;
|
931 |
+
echo '</div></div>';
|
932 |
+
}
|
933 |
+
};
|
934 |
+
?>
|
935 |
+
<?php $this->links_page_js(); ?>
|
936 |
+
</div>
|
937 |
+
<?php
|
938 |
+
}
|
939 |
+
|
940 |
+
function links_page_js(){
|
941 |
+
?>
|
942 |
+
<script type='text/javascript'>
|
943 |
+
|
944 |
+
function alterLinkCounter(factor){
|
945 |
+
cnt = parseInt(jQuery('.current-link-count').eq(0).html());
|
946 |
+
cnt = cnt + factor;
|
947 |
+
jQuery('.current-link-count').html(cnt);
|
948 |
+
}
|
949 |
+
|
950 |
+
jQuery(function($){
|
951 |
+
|
952 |
+
//The discard button - manually mark the link as valid. The link will be checked again later.
|
953 |
+
$(".blc-discard-button").click(function () {
|
954 |
+
var me = this;
|
955 |
+
$(me).html('Wait...');
|
956 |
+
|
957 |
+
var link_id = $(me).parents('.blc-row').find('.blc-link-id').html();
|
958 |
+
|
959 |
+
$.post(
|
960 |
+
"<?php bloginfo( 'wpurl' ); ?>/wp-admin/admin-ajax.php",
|
961 |
+
{
|
962 |
+
'action' : 'blc_discard',
|
963 |
+
'link_id' : link_id
|
964 |
+
},
|
965 |
+
function (data, textStatus){
|
966 |
+
if (data == 'OK'){
|
967 |
+
var master = $(me).parents('.blc-row');
|
968 |
+
var details = master.next('.blc-link-details');
|
969 |
+
|
970 |
+
details.hide();
|
971 |
+
//Flash the main row green to indicate success, then hide it.
|
972 |
+
var oldColor = master.css('background-color');
|
973 |
+
master.animate({ backgroundColor: "#E0FFB3" }, 200).animate({ backgroundColor: oldColor }, 300, function(){
|
974 |
+
master.hide();
|
975 |
+
});
|
976 |
+
|
977 |
+
alterLinkCounter(-1);
|
978 |
+
} else {
|
979 |
+
$(me).html('Discard');
|
980 |
+
alert(data);
|
981 |
+
}
|
982 |
+
}
|
983 |
+
);
|
984 |
+
});
|
985 |
+
|
986 |
+
//The details button - display/hide detailed info about a link
|
987 |
+
$(".blc-details-button, .blc-link-text").click(function () {
|
988 |
+
$(this).parents('.blc-row').next('.blc-link-details').toggle();
|
989 |
+
});
|
990 |
+
|
991 |
+
//The edit button - edit/save the link's URL
|
992 |
+
$(".blc-edit-button").click(function () {
|
993 |
+
var edit_button = $(this);
|
994 |
+
var master = $(edit_button).parents('.blc-row');
|
995 |
+
var editor = $(master).find('.blc-link-editor');
|
996 |
+
var url_el = $(master).find('.blc-link-url');
|
997 |
+
var cancel_button_container = $(master).find('.blc-cancel-button-container');
|
998 |
+
|
999 |
+
//Find the current/original URL
|
1000 |
+
var orig_url = url_el.attr('href');
|
1001 |
+
//Find the link ID
|
1002 |
+
var link_id = $(master).find('.blc-link-id').html();
|
1003 |
+
|
1004 |
+
if ( !$(editor).is(':visible') ){
|
1005 |
+
//Begin editing
|
1006 |
+
url_el.hide();
|
1007 |
+
editor.show();
|
1008 |
+
cancel_button_container.show();
|
1009 |
+
editor.focus();
|
1010 |
+
editor.select();
|
1011 |
+
edit_button.html('Save URL');
|
1012 |
+
} else {
|
1013 |
+
editor.hide();
|
1014 |
+
cancel_button_container.hide();
|
1015 |
+
url_el.show();
|
1016 |
+
|
1017 |
+
new_url = editor.val();
|
1018 |
+
|
1019 |
+
if (new_url != orig_url){
|
1020 |
+
//Save the changed link
|
1021 |
+
url_el.html('Saving changes...');
|
1022 |
+
|
1023 |
+
$.getJSON(
|
1024 |
+
"<?php bloginfo( 'wpurl' ); ?>/wp-admin/admin-ajax.php",
|
1025 |
+
{
|
1026 |
+
'action' : 'blc_edit',
|
1027 |
+
'link_id' : link_id,
|
1028 |
+
'new_url' : new_url
|
1029 |
+
},
|
1030 |
+
function (data, textStatus){
|
1031 |
+
var display_url = '';
|
1032 |
+
|
1033 |
+
if ( data && (typeof(data['error']) != 'undefined') ){
|
1034 |
+
//data.error is an error message
|
1035 |
+
alert(data.error);
|
1036 |
+
display_url = orig_url;
|
1037 |
+
} else {
|
1038 |
+
//data contains info about the performed edit
|
1039 |
+
if ( data.cnt_okay > 0 ){
|
1040 |
+
display_url = new_url;
|
1041 |
+
|
1042 |
+
url_el.attr('href', new_url);
|
1043 |
+
|
1044 |
+
if ( data.cnt_error > 0 ){
|
1045 |
+
var msg = "The link was successfully modifed.";
|
1046 |
+
msg = msg + "\nHowever, "+data.cnt_error+" instances couldn't be edited and still point to the old URL."
|
1047 |
+
alert(msg);
|
1048 |
+
} else {
|
1049 |
+
//Flash the row green to indicate success
|
1050 |
+
var oldColor = master.css('background-color');
|
1051 |
+
master.animate({ backgroundColor: "#E0FFB3" }, 200).animate({ backgroundColor: oldColor }, 300);
|
1052 |
+
|
1053 |
+
//Save the new ID
|
1054 |
+
master.find('.blc-link-id').html(data.new_link_id);
|
1055 |
+
//Load up the new link info (so sue me)
|
1056 |
+
master.next('.blc-link-details').find('td').html('<center>Loading...</center>').load(
|
1057 |
+
"<?php bloginfo( 'wpurl' ); ?>/wp-admin/admin-ajax.php",
|
1058 |
+
{
|
1059 |
+
'action' : 'blc_link_details',
|
1060 |
+
'link_id' : data.new_link_id
|
1061 |
+
}
|
1062 |
+
);
|
1063 |
+
}
|
1064 |
+
} else {
|
1065 |
+
alert("Something went wrong. The plugin failed to edit "+
|
1066 |
+
data.cnt_error + ' instance(s) of this link.');
|
1067 |
+
|
1068 |
+
display_url = orig_url;
|
1069 |
+
}
|
1070 |
+
};
|
1071 |
+
|
1072 |
+
//Shorten the displayed URL if it's > 50 characters
|
1073 |
+
if ( display_url.length > 50 ){
|
1074 |
+
display_url = display_url.substr(0, 47) + '...';
|
1075 |
+
}
|
1076 |
+
url_el.html(display_url);
|
1077 |
+
}
|
1078 |
+
);
|
1079 |
+
|
1080 |
+
} else {
|
1081 |
+
//It's the same URL, so do nothing.
|
1082 |
+
}
|
1083 |
+
edit_button.html('Edit URL');
|
1084 |
+
}
|
1085 |
+
});
|
1086 |
+
|
1087 |
+
$(".blc-cancel-button").click(function () {
|
1088 |
+
var master = $(this).parents('.blc-row');
|
1089 |
+
var url_el = $(master).find('.blc-link-url');
|
1090 |
+
|
1091 |
+
//Hide the cancel button
|
1092 |
+
$(this).parent().hide();
|
1093 |
+
//Show the un-editable URL again
|
1094 |
+
url_el.show();
|
1095 |
+
//reset and hide the editor
|
1096 |
+
master.find('.blc-link-editor').hide().val(url_el.attr('href'));
|
1097 |
+
//Set the edit button to say "Edit URL"
|
1098 |
+
master.find('.blc-edit-button').html('Edit URL');
|
1099 |
+
});
|
1100 |
+
|
1101 |
+
//The unlink button - remove the link/image from all posts, custom fields, etc.
|
1102 |
+
$(".blc-unlink-button").click(function () {
|
1103 |
+
var me = this;
|
1104 |
+
var master = $(me).parents('.blc-row');
|
1105 |
+
$(me).html('Wait...');
|
1106 |
+
|
1107 |
+
var link_id = $(me).parents('.blc-row').find('.blc-link-id').html();
|
1108 |
+
|
1109 |
+
$.post(
|
1110 |
+
"<?php bloginfo( 'wpurl' ); ?>/wp-admin/admin-ajax.php",
|
1111 |
+
{
|
1112 |
+
'action' : 'blc_unlink',
|
1113 |
+
'link_id' : link_id
|
1114 |
+
},
|
1115 |
+
function (data, textStatus){
|
1116 |
+
eval('data = ' + data);
|
1117 |
+
|
1118 |
+
if ( data && ( typeof(data['ok']) != 'undefined') ){
|
1119 |
+
//Hide the details
|
1120 |
+
master.next('.blc-link-details').hide();
|
1121 |
+
//Flash the main row green to indicate success, then hide it.
|
1122 |
+
var oldColor = master.css('background-color');
|
1123 |
+
master.animate({ backgroundColor: "#E0FFB3" }, 200).animate({ backgroundColor: oldColor }, 300, function(){
|
1124 |
+
master.hide();
|
1125 |
+
});
|
1126 |
+
|
1127 |
+
alterLinkCounter(-1);
|
1128 |
+
} else {
|
1129 |
+
$(me).html('Unlink');
|
1130 |
+
//Show the error message
|
1131 |
+
alert(data.error);
|
1132 |
+
}
|
1133 |
+
}
|
1134 |
+
);
|
1135 |
+
});
|
1136 |
+
|
1137 |
+
//The exclude button - Add this link to the exclusion list
|
1138 |
+
$(".blc-exclude-button").click(function () {
|
1139 |
+
var me = this;
|
1140 |
+
var master = $(me).parents('.blc-row');
|
1141 |
+
var details = master.next('.blc-link-details');
|
1142 |
+
$(me).html('Wait...');
|
1143 |
+
|
1144 |
+
var link_id = $(me).parents('.blc-row').find('.blc-link-id').html();
|
1145 |
+
|
1146 |
+
$.post(
|
1147 |
+
"<?php bloginfo( 'wpurl' ); ?>/wp-admin/admin-ajax.php",
|
1148 |
+
{
|
1149 |
+
'action' : 'blc_exclude_link',
|
1150 |
+
'link_id' : link_id
|
1151 |
+
},
|
1152 |
+
function (data, textStatus){
|
1153 |
+
eval('data = ' + data);
|
1154 |
+
|
1155 |
+
if ( data && ( typeof(data['ok']) != 'undefined' ) ){
|
1156 |
+
|
1157 |
+
if ( 'broken' == blc_current_filter ){
|
1158 |
+
//Flash the row green to indicate success, then hide it.
|
1159 |
+
$(me).replaceWith('Excluded');
|
1160 |
+
master.animate({ backgroundColor: "#E0FFB3" }, 200).animate({ backgroundColor: '#E2E2E2' }, 200, function(){
|
1161 |
+
details.hide();
|
1162 |
+
master.hide();
|
1163 |
+
alterLinkCounter(-1);
|
1164 |
+
});
|
1165 |
+
master.addClass('blc-excluded-link');
|
1166 |
+
} else {
|
1167 |
+
//Flash the row green to indicate success and fade to the "excluded link" color
|
1168 |
+
master.animate({ backgroundColor: "#E0FFB3" }, 200).animate({ backgroundColor: '#E2E2E2' }, 300);
|
1169 |
+
master.addClass('blc-excluded-link');
|
1170 |
+
$(me).replaceWith('Excluded');
|
1171 |
+
}
|
1172 |
+
} else {
|
1173 |
+
$(me).html('Exclude');
|
1174 |
+
alert(data.error);
|
1175 |
+
}
|
1176 |
+
}
|
1177 |
+
);
|
1178 |
+
});
|
1179 |
+
|
1180 |
+
});
|
1181 |
+
|
1182 |
+
</script>
|
1183 |
+
<?php
|
1184 |
+
}
|
1185 |
+
|
1186 |
+
function link_details_row($link){
|
1187 |
+
?>
|
1188 |
+
<span id='post_date_full' style='display:none;'><?php
|
1189 |
+
print $link['post_date'];
|
1190 |
+
?></span>
|
1191 |
+
<span id='check_date_full' style='display:none;'><?php
|
1192 |
+
print $link['last_check'];
|
1193 |
+
?></span>
|
1194 |
+
<ol style='list-style-type: none; width: 50%; float: right;'>
|
1195 |
+
<li><strong>Log :</strong>
|
1196 |
+
<span class='blc_log'><?php
|
1197 |
+
print nl2br($link['log']);
|
1198 |
+
?></span></li>
|
1199 |
+
</ol>
|
1200 |
+
|
1201 |
+
<ol style='list-style-type: none; padding-left: 2px;'>
|
1202 |
+
<?php if ( !empty($link['post_date']) ) { ?>
|
1203 |
+
<li><strong>Post published on :</strong>
|
1204 |
+
<span class='post_date'><?php
|
1205 |
+
print strftime("%B %d, %Y",strtotime($link['post_date']));
|
1206 |
+
?></span></li>
|
1207 |
+
<?php } ?>
|
1208 |
+
<li><strong>Link last checked :</strong>
|
1209 |
+
<span class='check_date'><?php
|
1210 |
+
$last_check = strtotime($link['last_check']);
|
1211 |
+
if ( $last_check < strtotime('-10 years') ){
|
1212 |
+
echo 'Never';
|
1213 |
+
} else {
|
1214 |
+
echo strftime( "%B %d, %Y", $last_check );
|
1215 |
+
}
|
1216 |
+
?></span></li>
|
1217 |
+
|
1218 |
+
<li><strong>HTTP code :</strong>
|
1219 |
+
<span class='http_code'><?php
|
1220 |
+
print $link['http_code'];
|
1221 |
+
?></span></li>
|
1222 |
+
|
1223 |
+
<li><strong>Response time :</strong>
|
1224 |
+
<span class='request_duration'><?php
|
1225 |
+
printf('%2.3f seconds', $link['request_duration']);
|
1226 |
+
?></span></li>
|
1227 |
+
|
1228 |
+
<li><strong>Final URL :</strong>
|
1229 |
+
<span class='final_url'><?php
|
1230 |
+
print $link['final_url'];
|
1231 |
+
?></span></li>
|
1232 |
+
|
1233 |
+
<li><strong>Redirect count :</strong>
|
1234 |
+
<span class='redirect_count'><?php
|
1235 |
+
print $link['redirect_count'];
|
1236 |
+
?></span></li>
|
1237 |
+
|
1238 |
+
<li><strong>Instance count :</strong>
|
1239 |
+
<span class='instance_count'><?php
|
1240 |
+
print $link['instance_count'];
|
1241 |
+
?></span></li>
|
1242 |
+
|
1243 |
+
<?php if ( intval( $link['check_count'] ) > 0 ){ ?>
|
1244 |
+
<li><br/>This link has failed
|
1245 |
+
<span class='check_count'><?php
|
1246 |
+
echo $link['check_count'];
|
1247 |
+
if ( intval($link['check_count'])==1 ){
|
1248 |
+
echo ' time';
|
1249 |
+
} else {
|
1250 |
+
echo ' times';
|
1251 |
+
}
|
1252 |
+
?></span>.</li>
|
1253 |
+
<?php } ?>
|
1254 |
+
</ol>
|
1255 |
+
<?php
|
1256 |
+
}
|
1257 |
+
|
1258 |
+
/**
|
1259 |
+
* ws_broken_link_checker::cleanup_links()
|
1260 |
+
* Remove orphaned links that have no corresponding instances
|
1261 |
+
*
|
1262 |
+
* @param int $link_id (optional) Only check this link
|
1263 |
+
* @return bool
|
1264 |
+
*/
|
1265 |
+
function cleanup_links( $link_id = null ){
|
1266 |
+
global $wpdb;
|
1267 |
+
|
1268 |
+
$q = "DELETE FROM {$wpdb->prefix}blc_links
|
1269 |
+
USING {$wpdb->prefix}blc_links LEFT JOIN {$wpdb->prefix}blc_instances
|
1270 |
+
ON {$wpdb->prefix}blc_instances.link_id = {$wpdb->prefix}blc_links.link_id
|
1271 |
+
WHERE
|
1272 |
+
{$wpdb->prefix}blc_instances.link_id IS NULL";
|
1273 |
+
|
1274 |
+
if ( $link_id !==null ) {
|
1275 |
+
$q .= " AND {$wpdb->prefix}blc_links.link_id = " . intval( $link_id );
|
1276 |
+
}
|
1277 |
+
|
1278 |
+
return $wpdb->query( $q );
|
1279 |
+
}
|
1280 |
+
|
1281 |
+
/**
|
1282 |
+
* ws_broken_link_checker::cleanup_instances()
|
1283 |
+
* Remove instances that reference invalid posts or bookmarks
|
1284 |
+
*
|
1285 |
+
* @return bool
|
1286 |
+
*/
|
1287 |
+
function cleanup_instances(){
|
1288 |
+
global $wpdb;
|
1289 |
+
|
1290 |
+
//Delete all instances that reference non-existent posts
|
1291 |
+
$q = "DELETE FROM {$wpdb->prefix}blc_instances
|
1292 |
+
USING {$wpdb->prefix}blc_instances LEFT JOIN {$wpdb->posts} ON {$wpdb->prefix}blc_instances.source_id = {$wpdb->posts}.ID
|
1293 |
+
WHERE
|
1294 |
+
{$wpdb->posts}.ID IS NULL
|
1295 |
+
AND ( ( {$wpdb->prefix}blc_instances.source_type = 'post' ) OR ( {$wpdb->prefix}blc_instances.source_type = 'custom_field' ) )";
|
1296 |
+
$rez = $wpdb->query($q);
|
1297 |
+
|
1298 |
+
//Delete all instances that reference non-existant bookmarks
|
1299 |
+
$q = "DELETE FROM {$wpdb->prefix}blc_instances
|
1300 |
+
USING {$wpdb->prefix}blc_instances LEFT JOIN {$wpdb->links} ON {$wpdb->prefix}blc_instances.source_id = {$wpdb->links}.link_id
|
1301 |
+
WHERE
|
1302 |
+
{$wpdb->links}.link_id IS NULL
|
1303 |
+
AND {$wpdb->prefix}blc_instances.source_type = 'blogroll' ";
|
1304 |
+
$rez2 = $wpdb->query($q);
|
1305 |
+
|
1306 |
+
return $rez and $rez2;
|
1307 |
+
}
|
1308 |
+
|
1309 |
+
/**
|
1310 |
+
* ws_broken_link_checker::parse_post()
|
1311 |
+
* Parse a post for links and save them to the DB.
|
1312 |
+
*
|
1313 |
+
* @param string $content Post content
|
1314 |
+
* @param int $post_id Post ID
|
1315 |
+
* @return void
|
1316 |
+
*/
|
1317 |
+
function parse_post($content, $post_id){
|
1318 |
+
//remove all <code></code> blocks first
|
1319 |
+
$content = preg_replace('/<code[^>]*>.+?<\/code>/si', ' ', $content);
|
1320 |
+
//Get the post permalink - it's used to resolve relative URLs
|
1321 |
+
$permalink = get_permalink( $post_id );
|
1322 |
+
|
1323 |
+
//Find links
|
1324 |
+
if(preg_match_all(blcUtility::link_pattern(), $content, $matches, PREG_SET_ORDER)){
|
1325 |
+
foreach($matches as $link){
|
1326 |
+
$url = $link[3];
|
1327 |
+
$text = strip_tags( $link[5] );
|
1328 |
+
//FB::log($url, "Found link");
|
1329 |
+
|
1330 |
+
$url = blcUtility::normalize_url($url, $permalink);
|
1331 |
+
//Skip invalid links
|
1332 |
+
if ( !$url || (strlen($url)<6) ) continue;
|
1333 |
+
|
1334 |
+
//Create or load the link
|
1335 |
+
$link_obj = new blcLink($url);
|
1336 |
+
//Add & save a new instance
|
1337 |
+
$link_obj->add_instance($post_id, 'post', $text, 'link');
|
1338 |
+
}
|
1339 |
+
};
|
1340 |
+
|
1341 |
+
//Find images (<img src=...>)
|
1342 |
+
if(preg_match_all(blcUtility::img_pattern(), $content, $matches, PREG_SET_ORDER)){
|
1343 |
+
foreach($matches as $img){
|
1344 |
+
$url = $img[3];
|
1345 |
+
//FB::log($url, "Found image");
|
1346 |
+
|
1347 |
+
$url = blcUtility::normalize_url($url, $permalink);
|
1348 |
+
if ( !$url || (strlen($url)<6) ) continue; //skip invalid URLs
|
1349 |
+
|
1350 |
+
//Create or load the link
|
1351 |
+
$link = new blcLink($url);
|
1352 |
+
//Add & save a new image instance
|
1353 |
+
$link->add_instance($post_id, 'post', '', 'image');
|
1354 |
+
}
|
1355 |
+
};
|
1356 |
+
}
|
1357 |
+
|
1358 |
+
/**
|
1359 |
+
* ws_broken_link_checker::parse_post_meta()
|
1360 |
+
* Parse a post's custom fields for links and save them in the DB.
|
1361 |
+
*
|
1362 |
+
* @param id $post_id
|
1363 |
+
* @return void
|
1364 |
+
*/
|
1365 |
+
function parse_post_meta($post_id){
|
1366 |
+
//Get all custom fields of this post
|
1367 |
+
$custom_fields = get_post_custom( $post_id );
|
1368 |
+
//FB::log($custom_fields, "Custom fields loaded");
|
1369 |
+
|
1370 |
+
//Parse the enabled fields
|
1371 |
+
foreach( $this->conf->options['custom_fields'] as $field ){
|
1372 |
+
if ( !isset($custom_fields[$field]) ) continue;
|
1373 |
+
|
1374 |
+
//FB::log($field, "Parsing field");
|
1375 |
+
|
1376 |
+
$values = $custom_fields[$field];
|
1377 |
+
if ( !is_array( $values ) ) $values = array($values);
|
1378 |
+
|
1379 |
+
foreach( $values as $value ){
|
1380 |
+
|
1381 |
+
//Attempt to parse the $value as URL
|
1382 |
+
$url = blcUtility::normalize_url($value);
|
1383 |
+
if ( empty($url) ){
|
1384 |
+
//FB::warn($value, "Invalid URL in custom field ".$field);
|
1385 |
+
continue;
|
1386 |
+
}
|
1387 |
+
|
1388 |
+
//FB::log($url, "Found URL");
|
1389 |
+
$link = new blcLink( $url );
|
1390 |
+
//FB::log($link, 'Created/loaded link');
|
1391 |
+
$inst = $link->add_instance( $post_id, 'custom_field', $field, 'link' );
|
1392 |
+
//FB::log($inst, 'Created instance');
|
1393 |
+
}
|
1394 |
+
}
|
1395 |
+
|
1396 |
+
}
|
1397 |
+
|
1398 |
+
function parse_blogroll_link( $the_link ){
|
1399 |
+
//FB::log($the_link, "Parsing blogroll link");
|
1400 |
+
|
1401 |
+
//Attempt to parse the URL
|
1402 |
+
$url = blcUtility::normalize_url( $the_link['link_url'] );
|
1403 |
+
if ( empty($url) ){
|
1404 |
+
//FB::warn( $the_link['link_url'], "Invalid URL in for a blogroll link".$the_link['link_name'] );
|
1405 |
+
return false;
|
1406 |
+
}
|
1407 |
+
|
1408 |
+
//FB::log($url, "Found URL");
|
1409 |
+
$link = new blcLink( $url );
|
1410 |
+
return $link->add_instance( $the_link['link_id'], 'blogroll', $the_link['link_name'], 'link' );
|
1411 |
+
}
|
1412 |
+
|
1413 |
+
function start_timer(){
|
1414 |
+
$this->execution_start_time = microtime_float();
|
1415 |
+
}
|
1416 |
+
|
1417 |
+
function execution_time(){
|
1418 |
+
return microtime_float() - $this->execution_start_time;
|
1419 |
+
}
|
1420 |
+
|
1421 |
+
/**
|
1422 |
+
* ws_broken_link_checker::work()
|
1423 |
+
* The main worker function that does all kinds of things.
|
1424 |
+
*
|
1425 |
+
* @return void
|
1426 |
+
*/
|
1427 |
+
function work(){
|
1428 |
+
global $wpdb;
|
1429 |
+
|
1430 |
+
if ( !$this->acquire_lock() ){
|
1431 |
+
//FB::warn("Another instance of BLC is already working. Stop.");
|
1432 |
+
return false;
|
1433 |
+
}
|
1434 |
+
|
1435 |
+
$this->start_timer();
|
1436 |
+
|
1437 |
+
$max_execution_time = $this->conf->options['max_execution_time'];
|
1438 |
+
|
1439 |
+
/*****************************************
|
1440 |
+
Preparation
|
1441 |
+
******************************************/
|
1442 |
+
// Check for safe mode
|
1443 |
+
if( ini_get('safe_mode') ){
|
1444 |
+
// Do it the safe mode way
|
1445 |
+
$t=ini_get('max_execution_time');
|
1446 |
+
if ($t && ($t < $max_execution_time))
|
1447 |
+
$max_execution_time = $t-1;
|
1448 |
+
} else {
|
1449 |
+
// Do it the regular way
|
1450 |
+
@set_time_limit( $max_execution_time * 2 ); //x2 should be plenty, running any longer would mean a glitch.
|
1451 |
+
}
|
1452 |
+
@ignore_user_abort(true);
|
1453 |
+
|
1454 |
+
$check_threshold = date('Y-m-d H:i:s', strtotime('-'.$this->conf->options['check_threshold'].' hours'));
|
1455 |
+
$recheck_threshold = date('Y-m-d H:i:s', strtotime('-20 minutes'));
|
1456 |
+
|
1457 |
+
$orphans_possible = false;
|
1458 |
+
|
1459 |
+
$still_need_resynch = $this->conf->options['need_resynch'];
|
1460 |
+
|
1461 |
+
/*****************************************
|
1462 |
+
Parse posts and bookmarks
|
1463 |
+
******************************************/
|
1464 |
+
|
1465 |
+
if ( $this->conf->options['need_resynch'] ) {
|
1466 |
+
|
1467 |
+
//FB::log("Looking for posts and bookmarks that need parsing...");
|
1468 |
+
|
1469 |
+
$tsynch = $wpdb->prefix.'blc_synch';
|
1470 |
+
$tposts = $wpdb->posts;
|
1471 |
+
$tlinks = $wpdb->links;
|
1472 |
+
|
1473 |
+
$synch_q = "SELECT $tsynch.source_id, $tsynch.source_type, $tposts.post_content, $tlinks.link_url, $tlinks.link_id, $tlinks.link_name
|
1474 |
+
|
1475 |
+
FROM
|
1476 |
+
$tsynch LEFT JOIN $tposts
|
1477 |
+
ON ($tposts.id = $tsynch.source_id AND $tsynch.source_type='post')
|
1478 |
+
LEFT JOIN $tlinks
|
1479 |
+
ON ($tlinks.link_id = $tsynch.source_id AND $tsynch.source_type='blogroll')
|
1480 |
+
|
1481 |
+
WHERE
|
1482 |
+
$tsynch.synched = 0
|
1483 |
+
|
1484 |
+
LIMIT 50";
|
1485 |
+
|
1486 |
+
while ( $rows = $wpdb->get_results($synch_q, ARRAY_A) ) {
|
1487 |
+
|
1488 |
+
//FB::log("Found ".count($rows)." items to analyze.");
|
1489 |
+
|
1490 |
+
foreach ($rows as $row) {
|
1491 |
+
|
1492 |
+
if ( $row['source_type'] == 'post' ){
|
1493 |
+
|
1494 |
+
//FB::log("Parsing post ".$row['source_id']);
|
1495 |
+
|
1496 |
+
//Remove instances associated with this post
|
1497 |
+
$q = "DELETE FROM {$wpdb->prefix}blc_instances
|
1498 |
+
WHERE source_id = %d AND (source_type = 'post' OR source_type='custom_field')";
|
1499 |
+
$q = $wpdb->prepare($q, intval($row['source_id']));
|
1500 |
+
|
1501 |
+
//FB::log($q, "Executing query");
|
1502 |
+
|
1503 |
+
if ( $wpdb->query( $q ) === false ){
|
1504 |
+
//FB::error($wpdb->last_error, "Database error");
|
1505 |
+
}
|
1506 |
+
|
1507 |
+
//Gather links and images from the post
|
1508 |
+
$this->parse_post( $row['post_content'], $row['source_id'] );
|
1509 |
+
//Gather links from custom fields
|
1510 |
+
$this->parse_post_meta( $row['source_id'] );
|
1511 |
+
|
1512 |
+
//Some link records might be orhpaned now
|
1513 |
+
$orphans_possible = true;
|
1514 |
+
|
1515 |
+
} else {
|
1516 |
+
|
1517 |
+
//FB::log("Parsing bookmark ".$row['source_id']);
|
1518 |
+
|
1519 |
+
//Remove instances associated with this bookmark
|
1520 |
+
$q = "DELETE FROM {$wpdb->prefix}blc_instances
|
1521 |
+
WHERE source_id = %d AND source_type = 'blogroll'";
|
1522 |
+
$q = $wpdb->prepare($q, intval($row['source_id']));
|
1523 |
+
//FB::log($q, "Executing query");
|
1524 |
+
|
1525 |
+
if ( $wpdb->query( $q ) === false ){
|
1526 |
+
//FB::error($wpdb->last_error, "Database error");
|
1527 |
+
}
|
1528 |
+
|
1529 |
+
//(Re)add the instance and link
|
1530 |
+
$this->parse_blogroll_link( $row );
|
1531 |
+
|
1532 |
+
//Some link records might be orhpaned now
|
1533 |
+
$orphans_possible = true;
|
1534 |
+
|
1535 |
+
}
|
1536 |
+
|
1537 |
+
//Update the table to indicate the item has been parsed
|
1538 |
+
$this->mark_synched( $row['source_id'], $row['source_type'] );
|
1539 |
+
|
1540 |
+
//Check if we still have some execution time left
|
1541 |
+
if( $this->execution_time() > $max_execution_time ){
|
1542 |
+
//FB::log('The alloted execution time has run out');
|
1543 |
+
$this->cleanup_links();
|
1544 |
+
$this->release_lock();
|
1545 |
+
return;
|
1546 |
+
}
|
1547 |
+
|
1548 |
+
}
|
1549 |
+
|
1550 |
+
}
|
1551 |
+
|
1552 |
+
//FB::log('No unparsed items found.');
|
1553 |
+
$still_need_resynch = false;
|
1554 |
+
|
1555 |
+
if ( $wpdb->last_error ){
|
1556 |
+
//FB::error($wpdb->last_error, "Database error");
|
1557 |
+
}
|
1558 |
+
|
1559 |
+
} else {
|
1560 |
+
//FB::log('Resynch not required.');
|
1561 |
+
}
|
1562 |
+
|
1563 |
+
/******************************************
|
1564 |
+
Resynch done?
|
1565 |
+
*******************************************/
|
1566 |
+
if ( $this->conf->options['need_resynch'] && !$still_need_resynch ){
|
1567 |
+
$this->conf->options['need_resynch'] = $still_need_resynch;
|
1568 |
+
$this->conf->save_options();
|
1569 |
+
}
|
1570 |
+
|
1571 |
+
/******************************************
|
1572 |
+
Remove orphaned links
|
1573 |
+
*******************************************/
|
1574 |
+
|
1575 |
+
if ( $orphans_possible ) {
|
1576 |
+
//FB::log('Cleaning up the link table.');
|
1577 |
+
$this->cleanup_links();
|
1578 |
+
}
|
1579 |
+
|
1580 |
+
//Check if we still have some execution time left
|
1581 |
+
if( $this->execution_time() > $max_execution_time ){
|
1582 |
+
//FB::log('The alloted execution time has run out');
|
1583 |
+
$this->release_lock();
|
1584 |
+
return;
|
1585 |
+
}
|
1586 |
+
|
1587 |
+
/*****************************************
|
1588 |
+
Check links
|
1589 |
+
******************************************/
|
1590 |
+
//FB::log('Looking for links to check (threshold : '.$check_threshold.')...');
|
1591 |
+
|
1592 |
+
//Select some links that haven't been checked for a long time or
|
1593 |
+
//that are broken and need to be re-checked again.
|
1594 |
+
|
1595 |
+
//Note : This is a slow query, but AFAIK there is no way to speed it up.
|
1596 |
+
//I could put an index on last_check, but that value is almost certainly unique
|
1597 |
+
//for each row so it wouldn't be much better than a full table scan.
|
1598 |
+
$q = "SELECT *, ( last_check < %s ) AS meets_check_threshold
|
1599 |
+
FROM {$wpdb->prefix}blc_links
|
1600 |
+
WHERE
|
1601 |
+
( last_check < %s )
|
1602 |
+
OR
|
1603 |
+
(
|
1604 |
+
( http_code >= 400 OR http_code < 200 OR timeout = 1)
|
1605 |
+
AND check_count < %d
|
1606 |
+
AND check_count > 0
|
1607 |
+
AND last_check < %s
|
1608 |
+
)
|
1609 |
+
ORDER BY last_check ASC
|
1610 |
+
LIMIT 50";
|
1611 |
+
$link_q = $wpdb->prepare($q, $check_threshold, $check_threshold, $this->conf->options['recheck_count'], $recheck_threshold);
|
1612 |
+
//FB::log($link_q);
|
1613 |
+
|
1614 |
+
while ( $links = $wpdb->get_results($link_q, ARRAY_A) ){
|
1615 |
+
|
1616 |
+
//some unchecked links found
|
1617 |
+
//FB::log("Checking ".count($links)." link(s)");
|
1618 |
+
|
1619 |
+
foreach ($links as $link) {
|
1620 |
+
$link_obj = new blcLink($link);
|
1621 |
+
|
1622 |
+
//Does this link need to be checked?
|
1623 |
+
if ( !$this->is_excluded( $link['url'] ) ) {
|
1624 |
+
//Yes, do it
|
1625 |
+
//FB::log("Checking link {$link[link_id]}");
|
1626 |
+
$link_obj->check();
|
1627 |
+
$link_obj->save();
|
1628 |
+
} else {
|
1629 |
+
//Nope, mark it as already checked.
|
1630 |
+
//FB::info("The URL {$link_obj->url} is excluded, marking link {$link_obj->link_id} as already checked.");
|
1631 |
+
$link_obj->last_check = date('Y-m-d H:i:s');
|
1632 |
+
$link_obj->http_code = 200; //Use a fake code so that the link doesn't show up in queries looking for broken links.
|
1633 |
+
$link_obj->timeout = false;
|
1634 |
+
$link_obj->request_duration = 0;
|
1635 |
+
$link_obj->log = "This link wasn't checked because a matching keyword was found on your exclusion list.";
|
1636 |
+
$link_obj->save();
|
1637 |
+
}
|
1638 |
+
|
1639 |
+
//Check if we still have some execution time left
|
1640 |
+
if( $this->execution_time() > $max_execution_time ){
|
1641 |
+
//FB::log('The alloted execution time has run out');
|
1642 |
+
$this->release_lock();
|
1643 |
+
return;
|
1644 |
+
}
|
1645 |
+
}
|
1646 |
+
}
|
1647 |
+
//FB::log('No links need to be checked right now.');
|
1648 |
+
|
1649 |
+
$this->release_lock();
|
1650 |
+
//FB::log('All done.');
|
1651 |
+
}
|
1652 |
+
|
1653 |
+
function ajax_full_status( ){
|
1654 |
+
$status = $this->get_status();
|
1655 |
+
$text = $this->status_text( $status );
|
1656 |
+
|
1657 |
+
echo json_encode( array(
|
1658 |
+
'text' => $text,
|
1659 |
+
'status' => $status,
|
1660 |
+
) );
|
1661 |
+
|
1662 |
+
die();
|
1663 |
+
}
|
1664 |
+
|
1665 |
+
/**
|
1666 |
+
* ws_broken_link_checker::status_text()
|
1667 |
+
* Generates a status message based on the status info in $status
|
1668 |
+
*
|
1669 |
+
* @param array $status
|
1670 |
+
* @return string
|
1671 |
+
*/
|
1672 |
+
function status_text( $status ){
|
1673 |
+
$text = '';
|
1674 |
+
|
1675 |
+
if( $status['broken_links'] > 0 ){
|
1676 |
+
$text .= sprintf( "<a href='%stools.php?page=view-broken-links' title='View broken links'><strong>Found %d broken link%s</strong></a>",
|
1677 |
+
get_option('wpurl'), $status['broken_links'], ( $status['broken_links'] == 1 )?'':'s' );
|
1678 |
+
} else {
|
1679 |
+
$text .= "No broken links found.";
|
1680 |
+
}
|
1681 |
+
|
1682 |
+
$text .= "<br/>";
|
1683 |
+
|
1684 |
+
if( $status['unchecked_links'] > 0) {
|
1685 |
+
$text .= sprintf( '%d URL%s in the work queue', $status['unchecked_links'], ($status['unchecked_links'] == 1)?'':'s' );
|
1686 |
+
} else {
|
1687 |
+
$text .= "No URLs in the work queue.";
|
1688 |
+
}
|
1689 |
+
|
1690 |
+
$text .= "<br/>";
|
1691 |
+
if ( $status['known_links'] > 0 ){
|
1692 |
+
$text .= sprintf( "Detected %d unique URL%s in %d link%s",
|
1693 |
+
$status['known_links'], $status['known_links'] == 1 ? '' : 's',
|
1694 |
+
$status['known_instances'], $status['known_instances'] == 1 ? '' : 's'
|
1695 |
+
);
|
1696 |
+
if ($this->conf->options['need_resynch']){
|
1697 |
+
$text .= ' and still searching...';
|
1698 |
+
} else {
|
1699 |
+
$text .= '.';
|
1700 |
+
}
|
1701 |
+
} else {
|
1702 |
+
if ($this->conf->options['need_resynch']){
|
1703 |
+
$text .= 'Searching your blog for links...';
|
1704 |
+
} else {
|
1705 |
+
$text .= 'No links detected.';
|
1706 |
+
}
|
1707 |
+
}
|
1708 |
+
|
1709 |
+
return $text;
|
1710 |
+
}
|
1711 |
+
|
1712 |
+
function ajax_dashboard_status(){
|
1713 |
+
//Just display the full status.
|
1714 |
+
$this->ajax_full_status( false );
|
1715 |
+
/*
|
1716 |
+
global $wpdb;
|
1717 |
+
|
1718 |
+
//displays a notification if broken links have been found
|
1719 |
+
$q = "SELECT count(*) FROM {$wpdb->prefix}blc_links
|
1720 |
+
WHERE check_count > 0 AND ( http_code < 200 OR http_code >= 400 OR timeout = 1 )";
|
1721 |
+
$broken_links = $wpdb->get_var($q);
|
1722 |
+
|
1723 |
+
|
1724 |
+
if($broken_links>0){
|
1725 |
+
printf( "<a href='%stools.php?page=view-broken-links' title='View broken links'><strong>Found %d broken link%s</strong></a>",
|
1726 |
+
get_option('wpurl'), $broken_links, ($broken_links==1)?'':'s' );
|
1727 |
+
} else {
|
1728 |
+
echo "No broken links found.";
|
1729 |
+
}
|
1730 |
+
die();
|
1731 |
+
*/
|
1732 |
+
}
|
1733 |
+
|
1734 |
+
/**
|
1735 |
+
* ws_broken_link_checker::get_status()
|
1736 |
+
* Returns an array with various status information about the plugin. Array key reference:
|
1737 |
+
* check_threshold - date/time; links checked before this threshold should be checked again.
|
1738 |
+
* recheck_threshold - date/time; broken links checked before this threshold should be re-checked.
|
1739 |
+
* known_links - the number of detected unique URLs (a misleading name, yes).
|
1740 |
+
* known_instances - the number of detected link instances, i.e. actual link elements in posts and other places.
|
1741 |
+
* broken_links - the number of detected broken links.
|
1742 |
+
* unchecked_links - the number of URLs that need to be checked ASAP; based on check_threshold and recheck_threshold.
|
1743 |
+
*
|
1744 |
+
* @return array
|
1745 |
+
*/
|
1746 |
+
function get_status(){
|
1747 |
+
global $wpdb;
|
1748 |
+
|
1749 |
+
$check_threshold=date('Y-m-d H:i:s', strtotime('-'.$this->conf->options['check_threshold'].' hours'));
|
1750 |
+
$recheck_threshold=date('Y-m-d H:i:s', strtotime('-20 minutes'));
|
1751 |
+
|
1752 |
+
$q = "SELECT count(*) FROM {$wpdb->prefix}blc_links WHERE 1";
|
1753 |
+
$known_links = $wpdb->get_var($q);
|
1754 |
+
|
1755 |
+
$q = "SELECT count(*) FROM {$wpdb->prefix}blc_instances WHERE 1";
|
1756 |
+
$known_instances = $wpdb->get_var($q);
|
1757 |
+
|
1758 |
+
$q = "SELECT count(*) FROM {$wpdb->prefix}blc_links
|
1759 |
+
WHERE check_count > 0 AND ( http_code < 200 OR http_code >= 400 OR timeout = 1 ) AND ( http_code <> ".BLC_CHECKING." )";
|
1760 |
+
$broken_links = $wpdb->get_var($q);
|
1761 |
+
|
1762 |
+
$q = "SELECT count(*) FROM {$wpdb->prefix}blc_links
|
1763 |
+
WHERE
|
1764 |
+
( ( last_check < '$check_threshold' ) OR
|
1765 |
+
(
|
1766 |
+
( http_code >= 400 OR http_code < 200 )
|
1767 |
+
AND check_count < 3
|
1768 |
+
AND last_check < '$recheck_threshold' )
|
1769 |
+
)";
|
1770 |
+
$unchecked_links = $wpdb->get_var($q);
|
1771 |
+
|
1772 |
+
return array(
|
1773 |
+
'check_threshold' => $check_threshold,
|
1774 |
+
'recheck_threshold' => $recheck_threshold,
|
1775 |
+
'known_links' => $known_links,
|
1776 |
+
'known_instances' => $known_instances,
|
1777 |
+
'broken_links' => $broken_links,
|
1778 |
+
'unchecked_links' => $unchecked_links,
|
1779 |
+
);
|
1780 |
+
}
|
1781 |
+
|
1782 |
+
function ajax_work(){
|
1783 |
+
//Run the worker function
|
1784 |
+
$this->work();
|
1785 |
+
die();
|
1786 |
+
}
|
1787 |
+
|
1788 |
+
function ajax_discard(){
|
1789 |
+
//TODO:Rewrite to use JSON instead of plaintext
|
1790 |
+
if (!current_user_can('edit_others_posts')){
|
1791 |
+
die( "You're not allowed to do that!" );
|
1792 |
+
}
|
1793 |
+
|
1794 |
+
if ( isset($_POST['link_id']) ){
|
1795 |
+
//Load the link
|
1796 |
+
$link = new blcLink( intval($_POST['link_id']) );
|
1797 |
+
|
1798 |
+
if ( !$link->valid() ){
|
1799 |
+
die("Oops, I can't find the link ".intval($_POST['link_id']) );
|
1800 |
+
}
|
1801 |
+
//Make it appear "not broken"
|
1802 |
+
$link->last_check = date('Y-m-d H:i:s');
|
1803 |
+
$link->http_code = 200;
|
1804 |
+
$link->timeout = 0;
|
1805 |
+
$link->check_count = 0;
|
1806 |
+
$link->log = "This link was manually marked as working by the user.";
|
1807 |
+
|
1808 |
+
//Save the changes
|
1809 |
+
if ( $link->save() ){
|
1810 |
+
die("OK");
|
1811 |
+
} else {
|
1812 |
+
die("Oops, couldn't modify the link!");
|
1813 |
+
}
|
1814 |
+
} else {
|
1815 |
+
die("Error : link_id not specified");
|
1816 |
+
}
|
1817 |
+
}
|
1818 |
+
|
1819 |
+
function ajax_edit(){
|
1820 |
+
if (!current_user_can('edit_others_posts')){
|
1821 |
+
die( json_encode( array(
|
1822 |
+
'error' => "You're not allowed to do that!"
|
1823 |
+
)));
|
1824 |
+
}
|
1825 |
+
|
1826 |
+
if ( isset($_GET['link_id']) && !empty($_GET['new_url']) ){
|
1827 |
+
//Load the link
|
1828 |
+
$link = new blcLink( intval($_GET['link_id']) );
|
1829 |
+
|
1830 |
+
if ( !$link->valid() ){
|
1831 |
+
die( json_encode( array(
|
1832 |
+
'error' => "Oops, I can't find the link ".intval($_GET['link_id'])
|
1833 |
+
)));
|
1834 |
+
}
|
1835 |
+
|
1836 |
+
$new_url = blcUtility::normalize_url($_GET['new_url']);
|
1837 |
+
if ( !$new_url ){
|
1838 |
+
die( json_encode( array(
|
1839 |
+
'error' => "Oops, the new URL is invalid!"
|
1840 |
+
)));
|
1841 |
+
}
|
1842 |
+
|
1843 |
+
//Try and edit the link
|
1844 |
+
$rez = $link->edit($new_url);
|
1845 |
+
|
1846 |
+
if ( $rez == false ){
|
1847 |
+
die( json_encode( array(
|
1848 |
+
'error' => "An unexpected error occured!"
|
1849 |
+
)));
|
1850 |
+
} else {
|
1851 |
+
$rez['ok'] = 'OK';
|
1852 |
+
die( json_encode($rez) );
|
1853 |
+
}
|
1854 |
+
|
1855 |
+
} else {
|
1856 |
+
die( json_encode( array(
|
1857 |
+
'error' => "Error : link_id or new_url not specified"
|
1858 |
+
)));
|
1859 |
+
}
|
1860 |
+
}
|
1861 |
+
|
1862 |
+
function ajax_unlink(){
|
1863 |
+
if (!current_user_can('edit_others_posts')){
|
1864 |
+
die( json_encode( array(
|
1865 |
+
'error' => "You're not allowed to do that!"
|
1866 |
+
)));
|
1867 |
+
}
|
1868 |
+
|
1869 |
+
if ( isset($_POST['link_id']) ){
|
1870 |
+
//Load the link
|
1871 |
+
$link = new blcLink( intval($_POST['link_id']) );
|
1872 |
+
|
1873 |
+
if ( !$link->valid() ){
|
1874 |
+
die( json_encode( array(
|
1875 |
+
'error' => "Oops, I can't find the link ".intval($_POST['link_id'])
|
1876 |
+
)));
|
1877 |
+
}
|
1878 |
+
|
1879 |
+
//Try and unlink it
|
1880 |
+
if ( $link->unlink() ){
|
1881 |
+
die( json_encode( array(
|
1882 |
+
'ok' => "URL {$link->url} was removed."
|
1883 |
+
)));
|
1884 |
+
} else {
|
1885 |
+
die( json_encode( array(
|
1886 |
+
'error' => "The plugin failed to remove the link."
|
1887 |
+
)));
|
1888 |
+
}
|
1889 |
+
|
1890 |
+
} else {
|
1891 |
+
die( json_encode( array(
|
1892 |
+
'error' => "Error : link_id not specified"
|
1893 |
+
)));
|
1894 |
+
}
|
1895 |
+
}
|
1896 |
+
|
1897 |
+
function ajax_link_details(){
|
1898 |
+
global $wpdb;
|
1899 |
+
|
1900 |
+
if (!current_user_can('edit_others_posts')){
|
1901 |
+
die("You don't have sufficient privileges to access this information!");
|
1902 |
+
}
|
1903 |
+
|
1904 |
+
//FB::log("Loading link details via AJAX");
|
1905 |
+
|
1906 |
+
if ( isset($_GET['link_id']) ){
|
1907 |
+
//FB::info("Link ID found in GET");
|
1908 |
+
$link_id = intval($_GET['link_id']);
|
1909 |
+
} else if ( isset($_POST['link_id']) ){
|
1910 |
+
//FB::info("Link ID found in POST");
|
1911 |
+
$link_id = intval($_POST['link_id']);
|
1912 |
+
} else {
|
1913 |
+
//FB::error('Link ID not specified, you hacking bastard.');
|
1914 |
+
die('Error : link ID not specified');
|
1915 |
+
}
|
1916 |
+
|
1917 |
+
//Load the link. link_details_row needs it as an array, so
|
1918 |
+
//we'll have to do this the long way.
|
1919 |
+
$q = "SELECT
|
1920 |
+
links.*,
|
1921 |
+
COUNT(*) as instance_count
|
1922 |
+
|
1923 |
+
FROM
|
1924 |
+
{$wpdb->prefix}blc_links AS links,
|
1925 |
+
{$wpdb->prefix}blc_instances as instances
|
1926 |
+
|
1927 |
+
WHERE
|
1928 |
+
links.link_id = %d
|
1929 |
+
|
1930 |
+
GROUP BY links.link_id";
|
1931 |
+
|
1932 |
+
$link = $wpdb->get_row( $wpdb->prepare($q, $link_id), ARRAY_A );
|
1933 |
+
if ( is_array($link) ){
|
1934 |
+
//FB::info($link, 'Link loaded');
|
1935 |
+
$this->link_details_row($link);
|
1936 |
+
die();
|
1937 |
+
} else {
|
1938 |
+
die ("Failed to load link details (" . $wpdb->last_error . ")");
|
1939 |
+
}
|
1940 |
+
}
|
1941 |
+
|
1942 |
+
function ajax_exclude_link(){
|
1943 |
+
if ( !current_user_can('manage_options') ){
|
1944 |
+
die( json_encode( array(
|
1945 |
+
'error' => "You're not allowed to do that!"
|
1946 |
+
)));
|
1947 |
+
}
|
1948 |
+
|
1949 |
+
if ( isset($_POST['link_id']) ){
|
1950 |
+
//Load the link
|
1951 |
+
$link = new blcLink( intval($_POST['link_id']) );
|
1952 |
+
|
1953 |
+
if ( !$link->valid() ){
|
1954 |
+
die( json_encode( array(
|
1955 |
+
'error' => "Oops, I can't find the link ".intval($_POST['link_id'])
|
1956 |
+
)));
|
1957 |
+
}
|
1958 |
+
|
1959 |
+
//Add the URL to the exclusion list
|
1960 |
+
if ( !in_array( $link->url, $this->conf->options['exclusion_list'] ) ){
|
1961 |
+
$this->conf->options['exclusion_list'][] = $link->url;
|
1962 |
+
//Also mark it as already checked so that it doesn't show up with other broken links.
|
1963 |
+
//FB::info("The URL {$link->url} is excluded, marking link {$link->link_id} as already checked.");
|
1964 |
+
$link->last_check = date('Y-m-d H:i:s');
|
1965 |
+
$link->http_code = 200; //Use a fake code so that the link doesn't show up in queries looking for broken links.
|
1966 |
+
$link->timeout = false;
|
1967 |
+
$link->request_duration = 0;
|
1968 |
+
$link->log = "This link wasn't checked because a matching keyword was found on your exclusion list.";
|
1969 |
+
$link->save();
|
1970 |
+
}
|
1971 |
+
|
1972 |
+
$this->conf->save_options();
|
1973 |
+
|
1974 |
+
die( json_encode( array(
|
1975 |
+
'ok' => "URL {$link->url} added to the exclusion list"
|
1976 |
+
)));
|
1977 |
+
} else {
|
1978 |
+
die( json_encode( array(
|
1979 |
+
'error' => "Link ID not specified"
|
1980 |
+
)));
|
1981 |
+
}
|
1982 |
+
}
|
1983 |
+
|
1984 |
+
/**
|
1985 |
+
* ws_broken_link_checker::acquire_lock()
|
1986 |
+
* Create and lock a temporary file.
|
1987 |
+
*
|
1988 |
+
* @return bool
|
1989 |
+
*/
|
1990 |
+
function acquire_lock(){
|
1991 |
+
//Maybe we already have the lock?
|
1992 |
+
if ( $this->lockfile_handle ){
|
1993 |
+
return true;
|
1994 |
+
}
|
1995 |
+
|
1996 |
+
$fn = $this->lockfile_name();
|
1997 |
+
if ( $fn ){
|
1998 |
+
//Open the lockfile
|
1999 |
+
$this->lockfile_handle = fopen($fn, 'w+');
|
2000 |
+
if ( $this->lockfile_handle ){
|
2001 |
+
//Do an exclusive lock
|
2002 |
+
if (flock($this->lockfile_handle, LOCK_EX | LOCK_NB)) {
|
2003 |
+
//File locked successfully
|
2004 |
+
return true;
|
2005 |
+
} else {
|
2006 |
+
//Something went wrong
|
2007 |
+
fclose($this->lockfile_handle);
|
2008 |
+
$this->lockfile_handle = null;
|
2009 |
+
return false;
|
2010 |
+
}
|
2011 |
+
} else {
|
2012 |
+
//Can't open the file, fail.
|
2013 |
+
return false;
|
2014 |
+
}
|
2015 |
+
} else {
|
2016 |
+
//Uh oh, can't generate a lockfile name. This is bad.
|
2017 |
+
//FB::error("Can't find a writable directory to use for my lock file!");
|
2018 |
+
return false;
|
2019 |
+
};
|
2020 |
+
}
|
2021 |
+
|
2022 |
+
/**
|
2023 |
+
* ws_broken_link_checker::release_lock()
|
2024 |
+
* Unlock and delete the temporary file
|
2025 |
+
*
|
2026 |
+
* @return bool
|
2027 |
+
*/
|
2028 |
+
function release_lock(){
|
2029 |
+
if ( $this->lockfile_handle ){
|
2030 |
+
//Close the file (implicitly releasing the lock)
|
2031 |
+
fclose( $this->lockfile_handle );
|
2032 |
+
//Delete the file
|
2033 |
+
$fn = $this->lockfile_name();
|
2034 |
+
if ( file_exists( $fn ) ) {
|
2035 |
+
unlink( $fn );
|
2036 |
+
}
|
2037 |
+
$this->lockfile_handle = null;
|
2038 |
+
return true;
|
2039 |
+
} else {
|
2040 |
+
//We didn't have the lock anyway...
|
2041 |
+
return false;
|
2042 |
+
}
|
2043 |
+
}
|
2044 |
+
|
2045 |
+
/**
|
2046 |
+
* ws_broken_link_checker::lockfile_name()
|
2047 |
+
* Generate system-specific lockfile filename
|
2048 |
+
*
|
2049 |
+
* @return string A filename or FALSE on error
|
2050 |
+
*/
|
2051 |
+
function lockfile_name(){
|
2052 |
+
//Try to find the temp directory.
|
2053 |
+
$path = sys_get_temp_dir();
|
2054 |
+
if ( $path && is_writable($path)){
|
2055 |
+
return trailingslashit($path) . '/wp_blc_lock';
|
2056 |
+
} else {
|
2057 |
+
//Try the plugin's directory.
|
2058 |
+
if ( is_writable( dirname(__FILE__) ) ){
|
2059 |
+
return dirname(__FILE__) . '/wp_blc_lock';
|
2060 |
+
//Try the wp-content directory
|
2061 |
+
} else if ( is_writable( WP_CONTENT_DIR ) ){
|
2062 |
+
return WP_CONTENT_DIR . '/wp_blc_lock';
|
2063 |
+
} else {
|
2064 |
+
//Fail.
|
2065 |
+
return false;
|
2066 |
+
}
|
2067 |
+
}
|
2068 |
+
}
|
2069 |
+
|
2070 |
+
function hook_add_link( $link_id ){
|
2071 |
+
$this->mark_unsynched( $link_id, 'blogroll' );
|
2072 |
+
}
|
2073 |
+
|
2074 |
+
function hook_edit_link( $link_id ){
|
2075 |
+
$this->mark_unsynched( $link_id, 'blogroll' );
|
2076 |
+
}
|
2077 |
+
|
2078 |
+
function hook_delete_link( $link_id ){
|
2079 |
+
global $wpdb;
|
2080 |
+
//Delete the synch record
|
2081 |
+
$wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->prefix}blc_synch WHERE source_id = %d AND source_type='blogroll'", $link_id ) );
|
2082 |
+
|
2083 |
+
//Get the matching instance record.
|
2084 |
+
$inst = $wpdb->get_row( $wpdb->prepare("SELECT * FROM {$wpdb->prefix}blc_instances WHERE source_id = %d AND source_type = 'blogroll'", $link_id), ARRAY_A );
|
2085 |
+
|
2086 |
+
if ( !$inst ) {
|
2087 |
+
//No instance record? No problem.
|
2088 |
+
return;
|
2089 |
+
}
|
2090 |
+
|
2091 |
+
//Remove it
|
2092 |
+
$wpdb->query( $wpdb->prepare("DELETE FROM {$wpdb->prefix}blc_instances WHERE instance_id = %d", $inst['instance_id']) );
|
2093 |
+
|
2094 |
+
//Remove the link that was associated with this instance if it has no more related instances.
|
2095 |
+
$this->cleanup_links( $inst['link_id'] );
|
2096 |
+
}
|
2097 |
+
|
2098 |
+
function hook_wp_dashboard_setup(){
|
2099 |
+
if ( function_exists( 'wp_add_dashboard_widget' ) ) {
|
2100 |
+
wp_add_dashboard_widget(
|
2101 |
+
'blc_dashboard_widget',
|
2102 |
+
'Broken Link Checker',
|
2103 |
+
array( &$this, 'dashboard_widget' ),
|
2104 |
+
array( &$this, 'dashboard_widget_control' )
|
2105 |
+
);
|
2106 |
+
}
|
2107 |
+
}
|
2108 |
+
|
2109 |
+
function lockfile_warning(){
|
2110 |
+
$my_dir = '/plugins/' . basename(dirname(__FILE__)) . '/';
|
2111 |
+
$settings_page = admin_url( 'options-general.php?page=link-checker-settings#lockfile_directory' );
|
2112 |
+
|
2113 |
+
echo sprintf('
|
2114 |
+
<div id="blc-lockfile-warning" class="error"><p>
|
2115 |
+
<strong>Broken Link Checker can\'t create a lockfile.</strong>
|
2116 |
+
Please make the directory <code>%s</code> writable by plugins.
|
2117 |
+
|
2118 |
+
<a href="javascript:void(0)" onclick="jQuery(\'#blc-lockfile-details\').toggle()">Details</a>
|
2119 |
+
</p>
|
2120 |
+
|
2121 |
+
<div id="blc-lockfile-details" style="display:none;"><p>
|
2122 |
+
The plugin uses a file-based locking mechanism to ensure that only one instance of the
|
2123 |
+
resource-heavy link checking algorithm is running at any given time. Unfortunately,
|
2124 |
+
BLC can\'t find a writable directory where it could store the lockfile - it failed to
|
2125 |
+
detect the location of your server\'s temporary directory, and the plugin\'s own directory
|
2126 |
+
isn\'t writable by PHP. To fix this problem, please make the plugin\'s directory writable.
|
2127 |
+
</p>
|
2128 |
+
</div>
|
2129 |
+
</div>',
|
2130 |
+
$my_dir, $settings_page);
|
2131 |
+
}
|
2132 |
+
|
2133 |
+
}//class ends here
|
2134 |
+
|
2135 |
+
} // if class_exists...
|
2136 |
+
|
2137 |
+
?>
|
highlighter-class.php
ADDED
@@ -0,0 +1,76 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* @author W-Shadow
|
5 |
+
* @copyright 2009
|
6 |
+
*
|
7 |
+
* @requires blcUtility
|
8 |
+
*/
|
9 |
+
|
10 |
+
class blcLinkHighlighter {
|
11 |
+
|
12 |
+
var $links_to_remove;
|
13 |
+
var $broken_link_css;
|
14 |
+
var $current_permalink;
|
15 |
+
|
16 |
+
function blcLinkHighlighter( $broken_link_css = '' ) {
|
17 |
+
if ( !empty( $broken_link_css ) ){
|
18 |
+
$this->broken_link_css = $broken_link_css;
|
19 |
+
add_action( 'wp_head', array(&$this,'hook_wp_head') );
|
20 |
+
}
|
21 |
+
|
22 |
+
add_filter( 'the_content', array(&$this,'hook_the_content') );
|
23 |
+
$this->current_permalink = '';
|
24 |
+
}
|
25 |
+
|
26 |
+
function hook_the_content($content){
|
27 |
+
global $post, $wpdb;
|
28 |
+
if ( empty($post) ) return $content;
|
29 |
+
|
30 |
+
//Get the post permalink - it's used to resolve relative URLs
|
31 |
+
$this->current_permalink = get_permalink( $post->ID );
|
32 |
+
|
33 |
+
$q = "
|
34 |
+
SELECT instances.link_text, links.*
|
35 |
+
|
36 |
+
FROM {$wpdb->prefix}blc_instances AS instances, {$wpdb->prefix}blc_links AS links
|
37 |
+
|
38 |
+
WHERE
|
39 |
+
instances.source_id = %d
|
40 |
+
AND instances.source_type = 'post'
|
41 |
+
AND instances.instance_type = 'link'
|
42 |
+
|
43 |
+
AND instances.link_id = links.link_id
|
44 |
+
AND links.check_count > 0
|
45 |
+
AND ( links.http_code < 200 OR links.http_code >= 400 OR links.timeout = 1 )
|
46 |
+
AND links.http_code <> " . BLC_CHECKING;
|
47 |
+
|
48 |
+
$rows = $wpdb->get_results( $wpdb->prepare( $q, $post->ID ), ARRAY_A );
|
49 |
+
if( $rows ){
|
50 |
+
$this->links_to_remove = array();
|
51 |
+
foreach($rows as $row){
|
52 |
+
$this->links_to_remove[$row['url']] = $row;
|
53 |
+
}
|
54 |
+
$content = preg_replace_callback( blcUtility::link_pattern(), array(&$this,'mark_broken_links'), $content );
|
55 |
+
};
|
56 |
+
|
57 |
+
return $content;
|
58 |
+
}
|
59 |
+
|
60 |
+
function mark_broken_links($matches){
|
61 |
+
//TODO: Tooltip-style popups with more info
|
62 |
+
$url = blcUtility::normalize_url( html_entity_decode( $matches[3], $this->current_permalink ) );
|
63 |
+
if( isset( $this->links_to_remove[$url] ) ){
|
64 |
+
return $matches[1].$matches[2].$matches[3].$matches[2].' class="broken_link" '.$matches[4].
|
65 |
+
$matches[5].$matches[6];
|
66 |
+
} else {
|
67 |
+
return $matches[0];
|
68 |
+
}
|
69 |
+
}
|
70 |
+
|
71 |
+
function hook_wp_head(){
|
72 |
+
echo '<style type="text/css">',$this->broken_link_css,'</style>';
|
73 |
+
}
|
74 |
+
}
|
75 |
+
|
76 |
+
?>
|
link-classes.php
CHANGED
@@ -96,17 +96,22 @@ class blcLink {
|
|
96 |
*/
|
97 |
function check(){
|
98 |
if ( !$this->valid() ) return false;
|
|
|
|
|
|
|
|
|
|
|
99 |
/*
|
100 |
Check for problematic (though not necessarily "broken") links.
|
101 |
If a link has been checked multiple times and still hasn't been marked as
|
102 |
timed-out or broken then probably the checking algorithm is having problems with
|
103 |
that link. Mark it as timed-out and hope the user sorts it out.
|
104 |
*/
|
105 |
-
if ( ($this->check_count >= 3) && ( !$this->timeout ) && (
|
106 |
-
$this->timeout =
|
|
|
107 |
$this->last_check = date('Y-m-d H:i:s');
|
108 |
$this->log .= "\r\n[A weird error was detected. This should never happen.]";
|
109 |
-
$this->save();
|
110 |
return false;
|
111 |
}
|
112 |
|
@@ -132,7 +137,8 @@ class blcLink {
|
|
132 |
$parts = parse_url($url);
|
133 |
//Only HTTP links are checked. All others are automatically considered okay.
|
134 |
if ( ($parts['scheme'] != 'http') && ($parts['scheme'] != 'https') ) {
|
135 |
-
$this->log .= "URL protocol ($parts[scheme]) is not HTTP. This link won't be checked.\n";
|
|
|
136 |
return true;
|
137 |
}
|
138 |
|
@@ -174,12 +180,11 @@ class blcLink {
|
|
174 |
}
|
175 |
curl_setopt($ch, CURLOPT_PROXYUSERPWD, $auth);
|
176 |
}
|
177 |
-
|
178 |
|
179 |
curl_setopt($ch, CURLOPT_FAILONERROR, false);
|
180 |
|
181 |
$nobody=false;
|
182 |
-
if($parts['scheme']=='https'){
|
183 |
//TODO: Redirects don't work with HTTPS
|
184 |
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
|
185 |
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
|
@@ -189,11 +194,8 @@ class blcLink {
|
|
189 |
//curl_setopt($ch, CURLOPT_RANGE, '0-1023');
|
190 |
}
|
191 |
|
192 |
-
//We definitely want headers.
|
193 |
curl_setopt($ch, CURLOPT_HEADER, true);
|
194 |
//register a callback function which will process the headers
|
195 |
-
//this assumes your code is into a class method, and uses $this->readHeader
|
196 |
-
//as the callback function.
|
197 |
curl_setopt($ch, CURLOPT_HEADERFUNCTION, array(&$this,'read_header'));
|
198 |
|
199 |
//Execute the request
|
@@ -221,7 +223,7 @@ class blcLink {
|
|
221 |
$this->log .= $this->last_headers."\n";
|
222 |
}
|
223 |
|
224 |
-
$this->http_code = $code!=0 ? $code : BLC_TIMEOUT;
|
225 |
$this->final_url = $info['url'];
|
226 |
$this->request_duration = $info['total_time'];
|
227 |
$this->redirect_count = $info['redirect_count'];
|
96 |
*/
|
97 |
function check(){
|
98 |
if ( !$this->valid() ) return false;
|
99 |
+
|
100 |
+
//General note : there is usually no need to save() the result of the check
|
101 |
+
//in this method because it will be typically called from wsBrokenLinkChecker::work()
|
102 |
+
//that will call the save() method for us.
|
103 |
+
|
104 |
/*
|
105 |
Check for problematic (though not necessarily "broken") links.
|
106 |
If a link has been checked multiple times and still hasn't been marked as
|
107 |
timed-out or broken then probably the checking algorithm is having problems with
|
108 |
that link. Mark it as timed-out and hope the user sorts it out.
|
109 |
*/
|
110 |
+
if ( ($this->check_count >= 3) && ( !$this->timeout ) && ( $this->http_code == BLC_CHECKING ) ) {
|
111 |
+
$this->timeout = true;
|
112 |
+
$this->http_code = BLC_TIMEOUT;
|
113 |
$this->last_check = date('Y-m-d H:i:s');
|
114 |
$this->log .= "\r\n[A weird error was detected. This should never happen.]";
|
|
|
115 |
return false;
|
116 |
}
|
117 |
|
137 |
$parts = parse_url($url);
|
138 |
//Only HTTP links are checked. All others are automatically considered okay.
|
139 |
if ( ($parts['scheme'] != 'http') && ($parts['scheme'] != 'https') ) {
|
140 |
+
$this->log .= "URL protocol ($parts[scheme]) is not HTTP(S). This link won't be checked.\n";
|
141 |
+
$this->http_code = 200;
|
142 |
return true;
|
143 |
}
|
144 |
|
180 |
}
|
181 |
curl_setopt($ch, CURLOPT_PROXYUSERPWD, $auth);
|
182 |
}
|
|
|
183 |
|
184 |
curl_setopt($ch, CURLOPT_FAILONERROR, false);
|
185 |
|
186 |
$nobody=false;
|
187 |
+
if( $parts['scheme'] == 'https' ){
|
188 |
//TODO: Redirects don't work with HTTPS
|
189 |
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
|
190 |
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
|
194 |
//curl_setopt($ch, CURLOPT_RANGE, '0-1023');
|
195 |
}
|
196 |
|
|
|
197 |
curl_setopt($ch, CURLOPT_HEADER, true);
|
198 |
//register a callback function which will process the headers
|
|
|
|
|
199 |
curl_setopt($ch, CURLOPT_HEADERFUNCTION, array(&$this,'read_header'));
|
200 |
|
201 |
//Execute the request
|
223 |
$this->log .= $this->last_headers."\n";
|
224 |
}
|
225 |
|
226 |
+
$this->http_code = $code != 0 ? $code : BLC_TIMEOUT;
|
227 |
$this->final_url = $info['url'];
|
228 |
$this->request_duration = $info['total_time'];
|
229 |
$this->redirect_count = $info['redirect_count'];
|
readme.txt
CHANGED
@@ -3,7 +3,7 @@ Contributors: whiteshadow
|
|
3 |
Tags: links, broken, maintenance, blogroll, custom fields, admin
|
4 |
Requires at least: 2.7.0
|
5 |
Tested up to: 2.9
|
6 |
-
Stable tag: 0.5.
|
7 |
|
8 |
This plugin will check your posts, custom fields and the blogroll for broken links and missing images and notify you if any are found.
|
9 |
|
@@ -58,6 +58,15 @@ To upgrade your installation
|
|
58 |
|
59 |
== Changelog ==
|
60 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
61 |
= 0.5.8.1 =
|
62 |
* Added partial proxy support when CURL is available. Proxies will be fully supported in a later version.
|
63 |
|
@@ -72,6 +81,7 @@ To upgrade your installation
|
|
72 |
* Improved relative URL parsing. The plugin now uses the permalink as the base URL when processing posts.
|
73 |
|
74 |
= 0.5.5 =
|
|
|
75 |
* URLs with spaces (and some other special characters) are now handled better and won't get marked as "broken" all the time.
|
76 |
* Links that contain quote characters are parsed properly.
|
77 |
|
@@ -92,7 +102,8 @@ To upgrade your installation
|
|
92 |
* Fix a bug when the plugin creates a DB table with the wrong prefix.
|
93 |
|
94 |
= 0.5 =
|
95 |
-
* This is a near-complete rewrite with a lot of new features.
|
|
|
96 |
|
97 |
= 0.4.14 =
|
98 |
* Fix false positives when the URL contains an #anchor
|
@@ -125,8 +136,10 @@ To upgrade your installation
|
|
125 |
* New and improved (TM) regexps for finding links and images.
|
126 |
* A "Settings" link added to plugin's action links.
|
127 |
* And probably other stuff I forgot!
|
|
|
128 |
|
129 |
= 0.4.7 =
|
|
|
130 |
* Autoselect link URL after the user clicks "Edit".
|
131 |
* Make sure only HTTP and HTTPS links are checked.
|
132 |
* More substantive improvements will hopefully follow next week.
|
@@ -135,6 +148,7 @@ To upgrade your installation
|
|
135 |
* Minor compatibility enhancement in wsblc\_ajax.php - don't load wpdb if it's already loaded.
|
136 |
|
137 |
= 0.4.5 =
|
|
|
138 |
* Revisions don't get added to the work queue anymore.
|
139 |
* Workaround for rare cURL timeout bug.
|
140 |
* Improved WP 2.6 compatibility.
|
@@ -214,3 +228,4 @@ To upgrade your installation
|
|
214 |
|
215 |
= 0.1 =
|
216 |
* *There are no release notes for this version*
|
|
3 |
Tags: links, broken, maintenance, blogroll, custom fields, admin
|
4 |
Requires at least: 2.7.0
|
5 |
Tested up to: 2.9
|
6 |
+
Stable tag: 0.5.10
|
7 |
|
8 |
This plugin will check your posts, custom fields and the blogroll for broken links and missing images and notify you if any are found.
|
9 |
|
58 |
|
59 |
== Changelog ==
|
60 |
|
61 |
+
*This is an automatically generated changelog*
|
62 |
+
|
63 |
+
= 0.5.9 =
|
64 |
+
* Added an autogenerated changelog.
|
65 |
+
* Added a workaround to make this plugin compatible with the SimplePress forum.
|
66 |
+
* Fixed <pre> block parsing, again.
|
67 |
+
* Fixed a bug where URLs that only differ in character case would be treated as equivalent.
|
68 |
+
* Improved the database upgrade routine.
|
69 |
+
|
70 |
= 0.5.8.1 =
|
71 |
* Added partial proxy support when CURL is available. Proxies will be fully supported in a later version.
|
72 |
|
81 |
* Improved relative URL parsing. The plugin now uses the permalink as the base URL when processing posts.
|
82 |
|
83 |
= 0.5.5 =
|
84 |
+
* Minor bugfixes
|
85 |
* URLs with spaces (and some other special characters) are now handled better and won't get marked as "broken" all the time.
|
86 |
* Links that contain quote characters are parsed properly.
|
87 |
|
102 |
* Fix a bug when the plugin creates a DB table with the wrong prefix.
|
103 |
|
104 |
= 0.5 =
|
105 |
+
* This is a near-complete rewrite with a lot of new features.
|
106 |
+
* See �http://w-shadow.com/blog/2009/05/22/broken-link-checker-05/ for details.
|
107 |
|
108 |
= 0.4.14 =
|
109 |
* Fix false positives when the URL contains an #anchor
|
136 |
* New and improved (TM) regexps for finding links and images.
|
137 |
* A "Settings" link added to plugin's action links.
|
138 |
* And probably other stuff I forgot!
|
139 |
+
* Grr :P
|
140 |
|
141 |
= 0.4.7 =
|
142 |
+
* Minor enhancements :
|
143 |
* Autoselect link URL after the user clicks "Edit".
|
144 |
* Make sure only HTTP and HTTPS links are checked.
|
145 |
* More substantive improvements will hopefully follow next week.
|
148 |
* Minor compatibility enhancement in wsblc\_ajax.php - don't load wpdb if it's already loaded.
|
149 |
|
150 |
= 0.4.5 =
|
151 |
+
* Bugfixes. Nothing more, nothing less.
|
152 |
* Revisions don't get added to the work queue anymore.
|
153 |
* Workaround for rare cURL timeout bug.
|
154 |
* Improved WP 2.6 compatibility.
|
228 |
|
229 |
= 0.1 =
|
230 |
* *There are no release notes for this version*
|
231 |
+
|
utility-class.php
CHANGED
@@ -6,7 +6,7 @@
|
|
6 |
*/
|
7 |
|
8 |
|
9 |
-
if (is_admin() && !function_exists('json_encode')){
|
10 |
//Load JSON functions for PHP < 5.2
|
11 |
if (!class_exists('Services_JSON')){
|
12 |
require 'JSON.php';
|
@@ -19,20 +19,33 @@ if (is_admin() && !function_exists('json_encode')){
|
|
19 |
}
|
20 |
}
|
21 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
22 |
if ( !class_exists('blcUtility') ){
|
23 |
|
24 |
class blcUtility {
|
25 |
|
26 |
//A regxp for images
|
27 |
function img_pattern(){
|
28 |
-
// \1 \2
|
29 |
-
return '/(<img[\s]+[^>]*src\s*=\s*)([\"\'])([
|
30 |
}
|
31 |
|
32 |
//A regexp for links
|
33 |
function link_pattern(){
|
34 |
-
// \1 \2 \3 URL
|
35 |
-
return '/(<a[\s]+[^>]*href\s*=\s*)([\"\'])([
|
36 |
}
|
37 |
|
38 |
/**
|
@@ -43,8 +56,11 @@ class blcUtility {
|
|
43 |
* @return string A normalized URL or FALSE if the URL is invalid
|
44 |
*/
|
45 |
function normalize_url($url, $base_url = ''){
|
46 |
-
|
47 |
-
|
|
|
|
|
|
|
48 |
|
49 |
if(isset($parts['scheme'])) {
|
50 |
//Only HTTP(S) links are checked. Other protocols are not supported.
|
@@ -54,17 +70,17 @@ class blcUtility {
|
|
54 |
|
55 |
$url = html_entity_decode($url);
|
56 |
$url = preg_replace(
|
57 |
-
array('/([\?&]PHPSESSID=\w+)$/i',
|
58 |
-
'/(#[^\/]*)$/',
|
59 |
-
'/&/',
|
60 |
-
'/^(javascript:.*)/i',
|
61 |
-
'/([\?&]sid=\w+)$/i'
|
62 |
),
|
63 |
array('','','&','',''),
|
64 |
$url);
|
65 |
-
$url=trim($url);
|
66 |
|
67 |
-
if($url=='') return false;
|
68 |
|
69 |
// turn relative URLs into absolute URLs
|
70 |
if ( empty($base_url) ) $base_url = get_option('siteurl');
|
@@ -86,7 +102,7 @@ class blcUtility {
|
|
86 |
//WTF? $relative is a seriously malformed URL
|
87 |
return false;
|
88 |
}
|
89 |
-
if(isset($p["scheme"])) return $relative;
|
90 |
|
91 |
$parts=(parse_url($absolute));
|
92 |
|
6 |
*/
|
7 |
|
8 |
|
9 |
+
if ( is_admin() && !function_exists('json_encode') ){
|
10 |
//Load JSON functions for PHP < 5.2
|
11 |
if (!class_exists('Services_JSON')){
|
12 |
require 'JSON.php';
|
19 |
}
|
20 |
}
|
21 |
|
22 |
+
if ( !function_exists('sys_get_temp_dir')) {
|
23 |
+
function sys_get_temp_dir() {
|
24 |
+
if (!empty($_ENV['TMP'])) { return realpath($_ENV['TMP']); }
|
25 |
+
if (!empty($_ENV['TMPDIR'])) { return realpath( $_ENV['TMPDIR']); }
|
26 |
+
if (!empty($_ENV['TEMP'])) { return realpath( $_ENV['TEMP']); }
|
27 |
+
$tempfile=tempnam(uniqid(rand(),TRUE),'');
|
28 |
+
if (file_exists($tempfile)) {
|
29 |
+
unlink($tempfile);
|
30 |
+
return realpath(dirname($tempfile));
|
31 |
+
}
|
32 |
+
}
|
33 |
+
}
|
34 |
+
|
35 |
if ( !class_exists('blcUtility') ){
|
36 |
|
37 |
class blcUtility {
|
38 |
|
39 |
//A regxp for images
|
40 |
function img_pattern(){
|
41 |
+
// \1 \2 \3 URL \4
|
42 |
+
return '/(<img[\s]+[^>]*src\s*=\s*)([\"\'])([^>]+?)\2([^<>]*>)/i';
|
43 |
}
|
44 |
|
45 |
//A regexp for links
|
46 |
function link_pattern(){
|
47 |
+
// \1 \2 \3 URL \4 \5 Text \6
|
48 |
+
return '/(<a[\s]+[^>]*href\s*=\s*)([\"\'])([^>]+?)\2([^<>]*>)((?sU).*)(<\/a>)/i';
|
49 |
}
|
50 |
|
51 |
/**
|
56 |
* @return string A normalized URL or FALSE if the URL is invalid
|
57 |
*/
|
58 |
function normalize_url($url, $base_url = ''){
|
59 |
+
//Sometimes links may contain shortcodes. Parse them.
|
60 |
+
$url = do_shortcode($url);
|
61 |
+
|
62 |
+
$parts = @parse_url($url);
|
63 |
+
if(!$parts) return false; //Invalid URL
|
64 |
|
65 |
if(isset($parts['scheme'])) {
|
66 |
//Only HTTP(S) links are checked. Other protocols are not supported.
|
70 |
|
71 |
$url = html_entity_decode($url);
|
72 |
$url = preg_replace(
|
73 |
+
array('/([\?&]PHPSESSID=\w+)$/i', //remove session ID
|
74 |
+
'/(#[^\/]*)$/', //and anchors/fragments
|
75 |
+
'/&/', //convert improper HTML entities
|
76 |
+
'/^(javascript:.*)/i', //treat links that contain JS as links with an empty URL
|
77 |
+
'/([\?&]sid=\w+)$/i' //remove another flavour of session ID
|
78 |
),
|
79 |
array('','','&','',''),
|
80 |
$url);
|
81 |
+
$url = trim($url);
|
82 |
|
83 |
+
if ( $url=='' ) return false;
|
84 |
|
85 |
// turn relative URLs into absolute URLs
|
86 |
if ( empty($base_url) ) $base_url = get_option('siteurl');
|
102 |
//WTF? $relative is a seriously malformed URL
|
103 |
return false;
|
104 |
}
|
105 |
+
if( isset($p["scheme"]) ) return $relative;
|
106 |
|
107 |
$parts=(parse_url($absolute));
|
108 |
|