Version Description
Download this release
Release Info
Developer | whiteshadow |
Plugin | Broken Link Checker |
Version | 0.5 |
Comparing to | |
See all releases |
Code changes from version 0.4.14 to 0.5
- JSON.php +805 -0
- broken-link-checker.php +1912 -815
- images/image.png +0 -0
- images/link.png +0 -0
- images/script_code.png +0 -0
- instance-classes.php +465 -0
- link-classes.php +541 -0
- readme.txt +25 -12
- uninstall.php +22 -0
- utility-class.php +140 -0
- wsblc_ajax.php +2 -448
JSON.php
ADDED
@@ -0,0 +1,805 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
|
3 |
+
|
4 |
+
/**
|
5 |
+
* Converts to and from JSON format.
|
6 |
+
*
|
7 |
+
* JSON (JavaScript Object Notation) is a lightweight data-interchange
|
8 |
+
* format. It is easy for humans to read and write. It is easy for machines
|
9 |
+
* to parse and generate. It is based on a subset of the JavaScript
|
10 |
+
* Programming Language, Standard ECMA-262 3rd Edition - December 1999.
|
11 |
+
* This feature can also be found in Python. JSON is a text format that is
|
12 |
+
* completely language independent but uses conventions that are familiar
|
13 |
+
* to programmers of the C-family of languages, including C, C++, C#, Java,
|
14 |
+
* JavaScript, Perl, TCL, and many others. These properties make JSON an
|
15 |
+
* ideal data-interchange language.
|
16 |
+
*
|
17 |
+
* This package provides a simple encoder and decoder for JSON notation. It
|
18 |
+
* is intended for use with client-side Javascript applications that make
|
19 |
+
* use of HTTPRequest to perform server communication functions - data can
|
20 |
+
* be encoded into JSON notation for use in a client-side javascript, or
|
21 |
+
* decoded from incoming Javascript requests. JSON format is native to
|
22 |
+
* Javascript, and can be directly eval()'ed with no further parsing
|
23 |
+
* overhead
|
24 |
+
*
|
25 |
+
* All strings should be in ASCII or UTF-8 format!
|
26 |
+
*
|
27 |
+
* LICENSE: Redistribution and use in source and binary forms, with or
|
28 |
+
* without modification, are permitted provided that the following
|
29 |
+
* conditions are met: Redistributions of source code must retain the
|
30 |
+
* above copyright notice, this list of conditions and the following
|
31 |
+
* disclaimer. Redistributions in binary form must reproduce the above
|
32 |
+
* copyright notice, this list of conditions and the following disclaimer
|
33 |
+
* in the documentation and/or other materials provided with the
|
34 |
+
* distribution.
|
35 |
+
*
|
36 |
+
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
37 |
+
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
38 |
+
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
|
39 |
+
* NO EVENT SHALL CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
40 |
+
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
41 |
+
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
42 |
+
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
43 |
+
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
|
44 |
+
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
45 |
+
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
|
46 |
+
* DAMAGE.
|
47 |
+
*
|
48 |
+
* @category
|
49 |
+
* @package Services_JSON
|
50 |
+
* @author Michal Migurski <mike-json@teczno.com>
|
51 |
+
* @author Matt Knapp <mdknapp[at]gmail[dot]com>
|
52 |
+
* @author Brett Stimmerman <brettstimmerman[at]gmail[dot]com>
|
53 |
+
* @copyright 2005 Michal Migurski
|
54 |
+
* @version CVS: $Id: JSON.php,v 1.31 2006/06/28 05:54:17 migurski Exp $
|
55 |
+
* @license http://www.opensource.org/licenses/bsd-license.php
|
56 |
+
* @link http://pear.php.net/pepr/pepr-proposal-show.php?id=198
|
57 |
+
*/
|
58 |
+
|
59 |
+
/**
|
60 |
+
* Marker constant for Services_JSON::decode(), used to flag stack state
|
61 |
+
*/
|
62 |
+
define('SERVICES_JSON_SLICE', 1);
|
63 |
+
|
64 |
+
/**
|
65 |
+
* Marker constant for Services_JSON::decode(), used to flag stack state
|
66 |
+
*/
|
67 |
+
define('SERVICES_JSON_IN_STR', 2);
|
68 |
+
|
69 |
+
/**
|
70 |
+
* Marker constant for Services_JSON::decode(), used to flag stack state
|
71 |
+
*/
|
72 |
+
define('SERVICES_JSON_IN_ARR', 3);
|
73 |
+
|
74 |
+
/**
|
75 |
+
* Marker constant for Services_JSON::decode(), used to flag stack state
|
76 |
+
*/
|
77 |
+
define('SERVICES_JSON_IN_OBJ', 4);
|
78 |
+
|
79 |
+
/**
|
80 |
+
* Marker constant for Services_JSON::decode(), used to flag stack state
|
81 |
+
*/
|
82 |
+
define('SERVICES_JSON_IN_CMT', 5);
|
83 |
+
|
84 |
+
/**
|
85 |
+
* Behavior switch for Services_JSON::decode()
|
86 |
+
*/
|
87 |
+
define('SERVICES_JSON_LOOSE_TYPE', 16);
|
88 |
+
|
89 |
+
/**
|
90 |
+
* Behavior switch for Services_JSON::decode()
|
91 |
+
*/
|
92 |
+
define('SERVICES_JSON_SUPPRESS_ERRORS', 32);
|
93 |
+
|
94 |
+
/**
|
95 |
+
* Converts to and from JSON format.
|
96 |
+
*
|
97 |
+
* Brief example of use:
|
98 |
+
*
|
99 |
+
* <code>
|
100 |
+
* // create a new instance of Services_JSON
|
101 |
+
* $json = new Services_JSON();
|
102 |
+
*
|
103 |
+
* // convert a complexe value to JSON notation, and send it to the browser
|
104 |
+
* $value = array('foo', 'bar', array(1, 2, 'baz'), array(3, array(4)));
|
105 |
+
* $output = $json->encode($value);
|
106 |
+
*
|
107 |
+
* print($output);
|
108 |
+
* // prints: ["foo","bar",[1,2,"baz"],[3,[4]]]
|
109 |
+
*
|
110 |
+
* // accept incoming POST data, assumed to be in JSON notation
|
111 |
+
* $input = file_get_contents('php://input', 1000000);
|
112 |
+
* $value = $json->decode($input);
|
113 |
+
* </code>
|
114 |
+
*/
|
115 |
+
class Services_JSON
|
116 |
+
{
|
117 |
+
/**
|
118 |
+
* constructs a new JSON instance
|
119 |
+
*
|
120 |
+
* @param int $use object behavior flags; combine with boolean-OR
|
121 |
+
*
|
122 |
+
* possible values:
|
123 |
+
* - SERVICES_JSON_LOOSE_TYPE: loose typing.
|
124 |
+
* "{...}" syntax creates associative arrays
|
125 |
+
* instead of objects in decode().
|
126 |
+
* - SERVICES_JSON_SUPPRESS_ERRORS: error suppression.
|
127 |
+
* Values which can't be encoded (e.g. resources)
|
128 |
+
* appear as NULL instead of throwing errors.
|
129 |
+
* By default, a deeply-nested resource will
|
130 |
+
* bubble up with an error, so all return values
|
131 |
+
* from encode() should be checked with isError()
|
132 |
+
*/
|
133 |
+
function Services_JSON($use = 0)
|
134 |
+
{
|
135 |
+
$this->use = $use;
|
136 |
+
}
|
137 |
+
|
138 |
+
/**
|
139 |
+
* convert a string from one UTF-16 char to one UTF-8 char
|
140 |
+
*
|
141 |
+
* Normally should be handled by mb_convert_encoding, but
|
142 |
+
* provides a slower PHP-only method for installations
|
143 |
+
* that lack the multibye string extension.
|
144 |
+
*
|
145 |
+
* @param string $utf16 UTF-16 character
|
146 |
+
* @return string UTF-8 character
|
147 |
+
* @access private
|
148 |
+
*/
|
149 |
+
function utf162utf8($utf16)
|
150 |
+
{
|
151 |
+
// oh please oh please oh please oh please oh please
|
152 |
+
if(function_exists('mb_convert_encoding')) {
|
153 |
+
return mb_convert_encoding($utf16, 'UTF-8', 'UTF-16');
|
154 |
+
}
|
155 |
+
|
156 |
+
$bytes = (ord($utf16{0}) << 8) | ord($utf16{1});
|
157 |
+
|
158 |
+
switch(true) {
|
159 |
+
case ((0x7F & $bytes) == $bytes):
|
160 |
+
// this case should never be reached, because we are in ASCII range
|
161 |
+
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
162 |
+
return chr(0x7F & $bytes);
|
163 |
+
|
164 |
+
case (0x07FF & $bytes) == $bytes:
|
165 |
+
// return a 2-byte UTF-8 character
|
166 |
+
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
167 |
+
return chr(0xC0 | (($bytes >> 6) & 0x1F))
|
168 |
+
. chr(0x80 | ($bytes & 0x3F));
|
169 |
+
|
170 |
+
case (0xFFFF & $bytes) == $bytes:
|
171 |
+
// return a 3-byte UTF-8 character
|
172 |
+
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
173 |
+
return chr(0xE0 | (($bytes >> 12) & 0x0F))
|
174 |
+
. chr(0x80 | (($bytes >> 6) & 0x3F))
|
175 |
+
. chr(0x80 | ($bytes & 0x3F));
|
176 |
+
}
|
177 |
+
|
178 |
+
// ignoring UTF-32 for now, sorry
|
179 |
+
return '';
|
180 |
+
}
|
181 |
+
|
182 |
+
/**
|
183 |
+
* convert a string from one UTF-8 char to one UTF-16 char
|
184 |
+
*
|
185 |
+
* Normally should be handled by mb_convert_encoding, but
|
186 |
+
* provides a slower PHP-only method for installations
|
187 |
+
* that lack the multibye string extension.
|
188 |
+
*
|
189 |
+
* @param string $utf8 UTF-8 character
|
190 |
+
* @return string UTF-16 character
|
191 |
+
* @access private
|
192 |
+
*/
|
193 |
+
function utf82utf16($utf8)
|
194 |
+
{
|
195 |
+
// oh please oh please oh please oh please oh please
|
196 |
+
if(function_exists('mb_convert_encoding')) {
|
197 |
+
return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8');
|
198 |
+
}
|
199 |
+
|
200 |
+
switch(strlen($utf8)) {
|
201 |
+
case 1:
|
202 |
+
// this case should never be reached, because we are in ASCII range
|
203 |
+
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
204 |
+
return $utf8;
|
205 |
+
|
206 |
+
case 2:
|
207 |
+
// return a UTF-16 character from a 2-byte UTF-8 char
|
208 |
+
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
209 |
+
return chr(0x07 & (ord($utf8{0}) >> 2))
|
210 |
+
. chr((0xC0 & (ord($utf8{0}) << 6))
|
211 |
+
| (0x3F & ord($utf8{1})));
|
212 |
+
|
213 |
+
case 3:
|
214 |
+
// return a UTF-16 character from a 3-byte UTF-8 char
|
215 |
+
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
216 |
+
return chr((0xF0 & (ord($utf8{0}) << 4))
|
217 |
+
| (0x0F & (ord($utf8{1}) >> 2)))
|
218 |
+
. chr((0xC0 & (ord($utf8{1}) << 6))
|
219 |
+
| (0x7F & ord($utf8{2})));
|
220 |
+
}
|
221 |
+
|
222 |
+
// ignoring UTF-32 for now, sorry
|
223 |
+
return '';
|
224 |
+
}
|
225 |
+
|
226 |
+
/**
|
227 |
+
* encodes an arbitrary variable into JSON format
|
228 |
+
*
|
229 |
+
* @param mixed $var any number, boolean, string, array, or object to be encoded.
|
230 |
+
* see argument 1 to Services_JSON() above for array-parsing behavior.
|
231 |
+
* if var is a strng, note that encode() always expects it
|
232 |
+
* to be in ASCII or UTF-8 format!
|
233 |
+
*
|
234 |
+
* @return mixed JSON string representation of input var or an error if a problem occurs
|
235 |
+
* @access public
|
236 |
+
*/
|
237 |
+
function encode($var)
|
238 |
+
{
|
239 |
+
switch (gettype($var)) {
|
240 |
+
case 'boolean':
|
241 |
+
return $var ? 'true' : 'false';
|
242 |
+
|
243 |
+
case 'NULL':
|
244 |
+
return 'null';
|
245 |
+
|
246 |
+
case 'integer':
|
247 |
+
return (int) $var;
|
248 |
+
|
249 |
+
case 'double':
|
250 |
+
case 'float':
|
251 |
+
return (float) $var;
|
252 |
+
|
253 |
+
case 'string':
|
254 |
+
// STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT
|
255 |
+
$ascii = '';
|
256 |
+
$strlen_var = strlen($var);
|
257 |
+
|
258 |
+
/*
|
259 |
+
* Iterate over every character in the string,
|
260 |
+
* escaping with a slash or encoding to UTF-8 where necessary
|
261 |
+
*/
|
262 |
+
for ($c = 0; $c < $strlen_var; ++$c) {
|
263 |
+
|
264 |
+
$ord_var_c = ord($var{$c});
|
265 |
+
|
266 |
+
switch (true) {
|
267 |
+
case $ord_var_c == 0x08:
|
268 |
+
$ascii .= '\b';
|
269 |
+
break;
|
270 |
+
case $ord_var_c == 0x09:
|
271 |
+
$ascii .= '\t';
|
272 |
+
break;
|
273 |
+
case $ord_var_c == 0x0A:
|
274 |
+
$ascii .= '\n';
|
275 |
+
break;
|
276 |
+
case $ord_var_c == 0x0C:
|
277 |
+
$ascii .= '\f';
|
278 |
+
break;
|
279 |
+
case $ord_var_c == 0x0D:
|
280 |
+
$ascii .= '\r';
|
281 |
+
break;
|
282 |
+
|
283 |
+
case $ord_var_c == 0x22:
|
284 |
+
case $ord_var_c == 0x2F:
|
285 |
+
case $ord_var_c == 0x5C:
|
286 |
+
// double quote, slash, slosh
|
287 |
+
$ascii .= '\\'.$var{$c};
|
288 |
+
break;
|
289 |
+
|
290 |
+
case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)):
|
291 |
+
// characters U-00000000 - U-0000007F (same as ASCII)
|
292 |
+
$ascii .= $var{$c};
|
293 |
+
break;
|
294 |
+
|
295 |
+
case (($ord_var_c & 0xE0) == 0xC0):
|
296 |
+
// characters U-00000080 - U-000007FF, mask 110XXXXX
|
297 |
+
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
298 |
+
$char = pack('C*', $ord_var_c, ord($var{$c + 1}));
|
299 |
+
$c += 1;
|
300 |
+
$utf16 = $this->utf82utf16($char);
|
301 |
+
$ascii .= sprintf('\u%04s', bin2hex($utf16));
|
302 |
+
break;
|
303 |
+
|
304 |
+
case (($ord_var_c & 0xF0) == 0xE0):
|
305 |
+
// characters U-00000800 - U-0000FFFF, mask 1110XXXX
|
306 |
+
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
307 |
+
$char = pack('C*', $ord_var_c,
|
308 |
+
ord($var{$c + 1}),
|
309 |
+
ord($var{$c + 2}));
|
310 |
+
$c += 2;
|
311 |
+
$utf16 = $this->utf82utf16($char);
|
312 |
+
$ascii .= sprintf('\u%04s', bin2hex($utf16));
|
313 |
+
break;
|
314 |
+
|
315 |
+
case (($ord_var_c & 0xF8) == 0xF0):
|
316 |
+
// characters U-00010000 - U-001FFFFF, mask 11110XXX
|
317 |
+
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
318 |
+
$char = pack('C*', $ord_var_c,
|
319 |
+
ord($var{$c + 1}),
|
320 |
+
ord($var{$c + 2}),
|
321 |
+
ord($var{$c + 3}));
|
322 |
+
$c += 3;
|
323 |
+
$utf16 = $this->utf82utf16($char);
|
324 |
+
$ascii .= sprintf('\u%04s', bin2hex($utf16));
|
325 |
+
break;
|
326 |
+
|
327 |
+
case (($ord_var_c & 0xFC) == 0xF8):
|
328 |
+
// characters U-00200000 - U-03FFFFFF, mask 111110XX
|
329 |
+
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
330 |
+
$char = pack('C*', $ord_var_c,
|
331 |
+
ord($var{$c + 1}),
|
332 |
+
ord($var{$c + 2}),
|
333 |
+
ord($var{$c + 3}),
|
334 |
+
ord($var{$c + 4}));
|
335 |
+
$c += 4;
|
336 |
+
$utf16 = $this->utf82utf16($char);
|
337 |
+
$ascii .= sprintf('\u%04s', bin2hex($utf16));
|
338 |
+
break;
|
339 |
+
|
340 |
+
case (($ord_var_c & 0xFE) == 0xFC):
|
341 |
+
// characters U-04000000 - U-7FFFFFFF, mask 1111110X
|
342 |
+
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
343 |
+
$char = pack('C*', $ord_var_c,
|
344 |
+
ord($var{$c + 1}),
|
345 |
+
ord($var{$c + 2}),
|
346 |
+
ord($var{$c + 3}),
|
347 |
+
ord($var{$c + 4}),
|
348 |
+
ord($var{$c + 5}));
|
349 |
+
$c += 5;
|
350 |
+
$utf16 = $this->utf82utf16($char);
|
351 |
+
$ascii .= sprintf('\u%04s', bin2hex($utf16));
|
352 |
+
break;
|
353 |
+
}
|
354 |
+
}
|
355 |
+
|
356 |
+
return '"'.$ascii.'"';
|
357 |
+
|
358 |
+
case 'array':
|
359 |
+
/*
|
360 |
+
* As per JSON spec if any array key is not an integer
|
361 |
+
* we must treat the the whole array as an object. We
|
362 |
+
* also try to catch a sparsely populated associative
|
363 |
+
* array with numeric keys here because some JS engines
|
364 |
+
* will create an array with empty indexes up to
|
365 |
+
* max_index which can cause memory issues and because
|
366 |
+
* the keys, which may be relevant, will be remapped
|
367 |
+
* otherwise.
|
368 |
+
*
|
369 |
+
* As per the ECMA and JSON specification an object may
|
370 |
+
* have any string as a property. Unfortunately due to
|
371 |
+
* a hole in the ECMA specification if the key is a
|
372 |
+
* ECMA reserved word or starts with a digit the
|
373 |
+
* parameter is only accessible using ECMAScript's
|
374 |
+
* bracket notation.
|
375 |
+
*/
|
376 |
+
|
377 |
+
// treat as a JSON object
|
378 |
+
if (is_array($var) && count($var) && (array_keys($var) !== range(0, sizeof($var) - 1))) {
|
379 |
+
$properties = array_map(array($this, 'name_value'),
|
380 |
+
array_keys($var),
|
381 |
+
array_values($var));
|
382 |
+
|
383 |
+
foreach($properties as $property) {
|
384 |
+
if(Services_JSON::isError($property)) {
|
385 |
+
return $property;
|
386 |
+
}
|
387 |
+
}
|
388 |
+
|
389 |
+
return '{' . join(',', $properties) . '}';
|
390 |
+
}
|
391 |
+
|
392 |
+
// treat it like a regular array
|
393 |
+
$elements = array_map(array($this, 'encode'), $var);
|
394 |
+
|
395 |
+
foreach($elements as $element) {
|
396 |
+
if(Services_JSON::isError($element)) {
|
397 |
+
return $element;
|
398 |
+
}
|
399 |
+
}
|
400 |
+
|
401 |
+
return '[' . join(',', $elements) . ']';
|
402 |
+
|
403 |
+
case 'object':
|
404 |
+
$vars = get_object_vars($var);
|
405 |
+
|
406 |
+
$properties = array_map(array($this, 'name_value'),
|
407 |
+
array_keys($vars),
|
408 |
+
array_values($vars));
|
409 |
+
|
410 |
+
foreach($properties as $property) {
|
411 |
+
if(Services_JSON::isError($property)) {
|
412 |
+
return $property;
|
413 |
+
}
|
414 |
+
}
|
415 |
+
|
416 |
+
return '{' . join(',', $properties) . '}';
|
417 |
+
|
418 |
+
default:
|
419 |
+
return ($this->use & SERVICES_JSON_SUPPRESS_ERRORS)
|
420 |
+
? 'null'
|
421 |
+
: new Services_JSON_Error(gettype($var)." can not be encoded as JSON string");
|
422 |
+
}
|
423 |
+
}
|
424 |
+
|
425 |
+
/**
|
426 |
+
* array-walking function for use in generating JSON-formatted name-value pairs
|
427 |
+
*
|
428 |
+
* @param string $name name of key to use
|
429 |
+
* @param mixed $value reference to an array element to be encoded
|
430 |
+
*
|
431 |
+
* @return string JSON-formatted name-value pair, like '"name":value'
|
432 |
+
* @access private
|
433 |
+
*/
|
434 |
+
function name_value($name, $value)
|
435 |
+
{
|
436 |
+
$encoded_value = $this->encode($value);
|
437 |
+
|
438 |
+
if(Services_JSON::isError($encoded_value)) {
|
439 |
+
return $encoded_value;
|
440 |
+
}
|
441 |
+
|
442 |
+
return $this->encode(strval($name)) . ':' . $encoded_value;
|
443 |
+
}
|
444 |
+
|
445 |
+
/**
|
446 |
+
* reduce a string by removing leading and trailing comments and whitespace
|
447 |
+
*
|
448 |
+
* @param $str string string value to strip of comments and whitespace
|
449 |
+
*
|
450 |
+
* @return string string value stripped of comments and whitespace
|
451 |
+
* @access private
|
452 |
+
*/
|
453 |
+
function reduce_string($str)
|
454 |
+
{
|
455 |
+
$str = preg_replace(array(
|
456 |
+
|
457 |
+
// eliminate single line comments in '// ...' form
|
458 |
+
'#^\s*//(.+)$#m',
|
459 |
+
|
460 |
+
// eliminate multi-line comments in '/* ... */' form, at start of string
|
461 |
+
'#^\s*/\*(.+)\*/#Us',
|
462 |
+
|
463 |
+
// eliminate multi-line comments in '/* ... */' form, at end of string
|
464 |
+
'#/\*(.+)\*/\s*$#Us'
|
465 |
+
|
466 |
+
), '', $str);
|
467 |
+
|
468 |
+
// eliminate extraneous space
|
469 |
+
return trim($str);
|
470 |
+
}
|
471 |
+
|
472 |
+
/**
|
473 |
+
* decodes a JSON string into appropriate variable
|
474 |
+
*
|
475 |
+
* @param string $str JSON-formatted string
|
476 |
+
*
|
477 |
+
* @return mixed number, boolean, string, array, or object
|
478 |
+
* corresponding to given JSON input string.
|
479 |
+
* See argument 1 to Services_JSON() above for object-output behavior.
|
480 |
+
* Note that decode() always returns strings
|
481 |
+
* in ASCII or UTF-8 format!
|
482 |
+
* @access public
|
483 |
+
*/
|
484 |
+
function decode($str)
|
485 |
+
{
|
486 |
+
$str = $this->reduce_string($str);
|
487 |
+
|
488 |
+
switch (strtolower($str)) {
|
489 |
+
case 'true':
|
490 |
+
return true;
|
491 |
+
|
492 |
+
case 'false':
|
493 |
+
return false;
|
494 |
+
|
495 |
+
case 'null':
|
496 |
+
return null;
|
497 |
+
|
498 |
+
default:
|
499 |
+
$m = array();
|
500 |
+
|
501 |
+
if (is_numeric($str)) {
|
502 |
+
// Lookie-loo, it's a number
|
503 |
+
|
504 |
+
// This would work on its own, but I'm trying to be
|
505 |
+
// good about returning integers where appropriate:
|
506 |
+
// return (float)$str;
|
507 |
+
|
508 |
+
// Return float or int, as appropriate
|
509 |
+
return ((float)$str == (integer)$str)
|
510 |
+
? (integer)$str
|
511 |
+
: (float)$str;
|
512 |
+
|
513 |
+
} elseif (preg_match('/^("|\').*(\1)$/s', $str, $m) && $m[1] == $m[2]) {
|
514 |
+
// STRINGS RETURNED IN UTF-8 FORMAT
|
515 |
+
$delim = substr($str, 0, 1);
|
516 |
+
$chrs = substr($str, 1, -1);
|
517 |
+
$utf8 = '';
|
518 |
+
$strlen_chrs = strlen($chrs);
|
519 |
+
|
520 |
+
for ($c = 0; $c < $strlen_chrs; ++$c) {
|
521 |
+
|
522 |
+
$substr_chrs_c_2 = substr($chrs, $c, 2);
|
523 |
+
$ord_chrs_c = ord($chrs{$c});
|
524 |
+
|
525 |
+
switch (true) {
|
526 |
+
case $substr_chrs_c_2 == '\b':
|
527 |
+
$utf8 .= chr(0x08);
|
528 |
+
++$c;
|
529 |
+
break;
|
530 |
+
case $substr_chrs_c_2 == '\t':
|
531 |
+
$utf8 .= chr(0x09);
|
532 |
+
++$c;
|
533 |
+
break;
|
534 |
+
case $substr_chrs_c_2 == '\n':
|
535 |
+
$utf8 .= chr(0x0A);
|
536 |
+
++$c;
|
537 |
+
break;
|
538 |
+
case $substr_chrs_c_2 == '\f':
|
539 |
+
$utf8 .= chr(0x0C);
|
540 |
+
++$c;
|
541 |
+
break;
|
542 |
+
case $substr_chrs_c_2 == '\r':
|
543 |
+
$utf8 .= chr(0x0D);
|
544 |
+
++$c;
|
545 |
+
break;
|
546 |
+
|
547 |
+
case $substr_chrs_c_2 == '\\"':
|
548 |
+
case $substr_chrs_c_2 == '\\\'':
|
549 |
+
case $substr_chrs_c_2 == '\\\\':
|
550 |
+
case $substr_chrs_c_2 == '\\/':
|
551 |
+
if (($delim == '"' && $substr_chrs_c_2 != '\\\'') ||
|
552 |
+
($delim == "'" && $substr_chrs_c_2 != '\\"')) {
|
553 |
+
$utf8 .= $chrs{++$c};
|
554 |
+
}
|
555 |
+
break;
|
556 |
+
|
557 |
+
case preg_match('/\\\u[0-9A-F]{4}/i', substr($chrs, $c, 6)):
|
558 |
+
// single, escaped unicode character
|
559 |
+
$utf16 = chr(hexdec(substr($chrs, ($c + 2), 2)))
|
560 |
+
. chr(hexdec(substr($chrs, ($c + 4), 2)));
|
561 |
+
$utf8 .= $this->utf162utf8($utf16);
|
562 |
+
$c += 5;
|
563 |
+
break;
|
564 |
+
|
565 |
+
case ($ord_chrs_c >= 0x20) && ($ord_chrs_c <= 0x7F):
|
566 |
+
$utf8 .= $chrs{$c};
|
567 |
+
break;
|
568 |
+
|
569 |
+
case ($ord_chrs_c & 0xE0) == 0xC0:
|
570 |
+
// characters U-00000080 - U-000007FF, mask 110XXXXX
|
571 |
+
//see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
572 |
+
$utf8 .= substr($chrs, $c, 2);
|
573 |
+
++$c;
|
574 |
+
break;
|
575 |
+
|
576 |
+
case ($ord_chrs_c & 0xF0) == 0xE0:
|
577 |
+
// characters U-00000800 - U-0000FFFF, mask 1110XXXX
|
578 |
+
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
579 |
+
$utf8 .= substr($chrs, $c, 3);
|
580 |
+
$c += 2;
|
581 |
+
break;
|
582 |
+
|
583 |
+
case ($ord_chrs_c & 0xF8) == 0xF0:
|
584 |
+
// characters U-00010000 - U-001FFFFF, mask 11110XXX
|
585 |
+
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
586 |
+
$utf8 .= substr($chrs, $c, 4);
|
587 |
+
$c += 3;
|
588 |
+
break;
|
589 |
+
|
590 |
+
case ($ord_chrs_c & 0xFC) == 0xF8:
|
591 |
+
// characters U-00200000 - U-03FFFFFF, mask 111110XX
|
592 |
+
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
593 |
+
$utf8 .= substr($chrs, $c, 5);
|
594 |
+
$c += 4;
|
595 |
+
break;
|
596 |
+
|
597 |
+
case ($ord_chrs_c & 0xFE) == 0xFC:
|
598 |
+
// characters U-04000000 - U-7FFFFFFF, mask 1111110X
|
599 |
+
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
600 |
+
$utf8 .= substr($chrs, $c, 6);
|
601 |
+
$c += 5;
|
602 |
+
break;
|
603 |
+
|
604 |
+
}
|
605 |
+
|
606 |
+
}
|
607 |
+
|
608 |
+
return $utf8;
|
609 |
+
|
610 |
+
} elseif (preg_match('/^\[.*\]$/s', $str) || preg_match('/^\{.*\}$/s', $str)) {
|
611 |
+
// array, or object notation
|
612 |
+
|
613 |
+
if ($str{0} == '[') {
|
614 |
+
$stk = array(SERVICES_JSON_IN_ARR);
|
615 |
+
$arr = array();
|
616 |
+
} else {
|
617 |
+
if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
|
618 |
+
$stk = array(SERVICES_JSON_IN_OBJ);
|
619 |
+
$obj = array();
|
620 |
+
} else {
|
621 |
+
$stk = array(SERVICES_JSON_IN_OBJ);
|
622 |
+
$obj = new stdClass();
|
623 |
+
}
|
624 |
+
}
|
625 |
+
|
626 |
+
array_push($stk, array('what' => SERVICES_JSON_SLICE,
|
627 |
+
'where' => 0,
|
628 |
+
'delim' => false));
|
629 |
+
|
630 |
+
$chrs = substr($str, 1, -1);
|
631 |
+
$chrs = $this->reduce_string($chrs);
|
632 |
+
|
633 |
+
if ($chrs == '') {
|
634 |
+
if (reset($stk) == SERVICES_JSON_IN_ARR) {
|
635 |
+
return $arr;
|
636 |
+
|
637 |
+
} else {
|
638 |
+
return $obj;
|
639 |
+
|
640 |
+
}
|
641 |
+
}
|
642 |
+
|
643 |
+
//print("\nparsing {$chrs}\n");
|
644 |
+
|
645 |
+
$strlen_chrs = strlen($chrs);
|
646 |
+
|
647 |
+
for ($c = 0; $c <= $strlen_chrs; ++$c) {
|
648 |
+
|
649 |
+
$top = end($stk);
|
650 |
+
$substr_chrs_c_2 = substr($chrs, $c, 2);
|
651 |
+
|
652 |
+
if (($c == $strlen_chrs) || (($chrs{$c} == ',') && ($top['what'] == SERVICES_JSON_SLICE))) {
|
653 |
+
// found a comma that is not inside a string, array, etc.,
|
654 |
+
// OR we've reached the end of the character list
|
655 |
+
$slice = substr($chrs, $top['where'], ($c - $top['where']));
|
656 |
+
array_push($stk, array('what' => SERVICES_JSON_SLICE, 'where' => ($c + 1), 'delim' => false));
|
657 |
+
//print("Found split at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
|
658 |
+
|
659 |
+
if (reset($stk) == SERVICES_JSON_IN_ARR) {
|
660 |
+
// we are in an array, so just push an element onto the stack
|
661 |
+
array_push($arr, $this->decode($slice));
|
662 |
+
|
663 |
+
} elseif (reset($stk) == SERVICES_JSON_IN_OBJ) {
|
664 |
+
// we are in an object, so figure
|
665 |
+
// out the property name and set an
|
666 |
+
// element in an associative array,
|
667 |
+
// for now
|
668 |
+
$parts = array();
|
669 |
+
|
670 |
+
if (preg_match('/^\s*(["\'].*[^\\\]["\'])\s*:\s*(\S.*),?$/Uis', $slice, $parts)) {
|
671 |
+
// "name":value pair
|
672 |
+
$key = $this->decode($parts[1]);
|
673 |
+
$val = $this->decode($parts[2]);
|
674 |
+
|
675 |
+
if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
|
676 |
+
$obj[$key] = $val;
|
677 |
+
} else {
|
678 |
+
$obj->$key = $val;
|
679 |
+
}
|
680 |
+
} elseif (preg_match('/^\s*(\w+)\s*:\s*(\S.*),?$/Uis', $slice, $parts)) {
|
681 |
+
// name:value pair, where name is unquoted
|
682 |
+
$key = $parts[1];
|
683 |
+
$val = $this->decode($parts[2]);
|
684 |
+
|
685 |
+
if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
|
686 |
+
$obj[$key] = $val;
|
687 |
+
} else {
|
688 |
+
$obj->$key = $val;
|
689 |
+
}
|
690 |
+
}
|
691 |
+
|
692 |
+
}
|
693 |
+
|
694 |
+
} elseif ((($chrs{$c} == '"') || ($chrs{$c} == "'")) && ($top['what'] != SERVICES_JSON_IN_STR)) {
|
695 |
+
// found a quote, and we are not inside a string
|
696 |
+
array_push($stk, array('what' => SERVICES_JSON_IN_STR, 'where' => $c, 'delim' => $chrs{$c}));
|
697 |
+
//print("Found start of string at {$c}\n");
|
698 |
+
|
699 |
+
} elseif (($chrs{$c} == $top['delim']) &&
|
700 |
+
($top['what'] == SERVICES_JSON_IN_STR) &&
|
701 |
+
((strlen(substr($chrs, 0, $c)) - strlen(rtrim(substr($chrs, 0, $c), '\\'))) % 2 != 1)) {
|
702 |
+
// found a quote, we're in a string, and it's not escaped
|
703 |
+
// we know that it's not escaped becase there is _not_ an
|
704 |
+
// odd number of backslashes at the end of the string so far
|
705 |
+
array_pop($stk);
|
706 |
+
//print("Found end of string at {$c}: ".substr($chrs, $top['where'], (1 + 1 + $c - $top['where']))."\n");
|
707 |
+
|
708 |
+
} elseif (($chrs{$c} == '[') &&
|
709 |
+
in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
|
710 |
+
// found a left-bracket, and we are in an array, object, or slice
|
711 |
+
array_push($stk, array('what' => SERVICES_JSON_IN_ARR, 'where' => $c, 'delim' => false));
|
712 |
+
//print("Found start of array at {$c}\n");
|
713 |
+
|
714 |
+
} elseif (($chrs{$c} == ']') && ($top['what'] == SERVICES_JSON_IN_ARR)) {
|
715 |
+
// found a right-bracket, and we're in an array
|
716 |
+
array_pop($stk);
|
717 |
+
//print("Found end of array at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
|
718 |
+
|
719 |
+
} elseif (($chrs{$c} == '{') &&
|
720 |
+
in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
|
721 |
+
// found a left-brace, and we are in an array, object, or slice
|
722 |
+
array_push($stk, array('what' => SERVICES_JSON_IN_OBJ, 'where' => $c, 'delim' => false));
|
723 |
+
//print("Found start of object at {$c}\n");
|
724 |
+
|
725 |
+
} elseif (($chrs{$c} == '}') && ($top['what'] == SERVICES_JSON_IN_OBJ)) {
|
726 |
+
// found a right-brace, and we're in an object
|
727 |
+
array_pop($stk);
|
728 |
+
//print("Found end of object at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
|
729 |
+
|
730 |
+
} elseif (($substr_chrs_c_2 == '/*') &&
|
731 |
+
in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
|
732 |
+
// found a comment start, and we are in an array, object, or slice
|
733 |
+
array_push($stk, array('what' => SERVICES_JSON_IN_CMT, 'where' => $c, 'delim' => false));
|
734 |
+
$c++;
|
735 |
+
//print("Found start of comment at {$c}\n");
|
736 |
+
|
737 |
+
} elseif (($substr_chrs_c_2 == '*/') && ($top['what'] == SERVICES_JSON_IN_CMT)) {
|
738 |
+
// found a comment end, and we're in one now
|
739 |
+
array_pop($stk);
|
740 |
+
$c++;
|
741 |
+
|
742 |
+
for ($i = $top['where']; $i <= $c; ++$i)
|
743 |
+
$chrs = substr_replace($chrs, ' ', $i, 1);
|
744 |
+
|
745 |
+
//print("Found end of comment at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
|
746 |
+
|
747 |
+
}
|
748 |
+
|
749 |
+
}
|
750 |
+
|
751 |
+
if (reset($stk) == SERVICES_JSON_IN_ARR) {
|
752 |
+
return $arr;
|
753 |
+
|
754 |
+
} elseif (reset($stk) == SERVICES_JSON_IN_OBJ) {
|
755 |
+
return $obj;
|
756 |
+
|
757 |
+
}
|
758 |
+
|
759 |
+
}
|
760 |
+
}
|
761 |
+
}
|
762 |
+
|
763 |
+
/**
|
764 |
+
* @todo Ultimately, this should just call PEAR::isError()
|
765 |
+
*/
|
766 |
+
function isError($data, $code = null)
|
767 |
+
{
|
768 |
+
if (class_exists('pear')) {
|
769 |
+
return PEAR::isError($data, $code);
|
770 |
+
} elseif (is_object($data) && (get_class($data) == 'services_json_error' ||
|
771 |
+
is_subclass_of($data, 'services_json_error'))) {
|
772 |
+
return true;
|
773 |
+
}
|
774 |
+
|
775 |
+
return false;
|
776 |
+
}
|
777 |
+
}
|
778 |
+
|
779 |
+
if (class_exists('PEAR_Error')) {
|
780 |
+
|
781 |
+
class Services_JSON_Error extends PEAR_Error
|
782 |
+
{
|
783 |
+
function Services_JSON_Error($message = 'unknown error', $code = null,
|
784 |
+
$mode = null, $options = null, $userinfo = null)
|
785 |
+
{
|
786 |
+
parent::PEAR_Error($message, $code, $mode, $options, $userinfo);
|
787 |
+
}
|
788 |
+
}
|
789 |
+
|
790 |
+
} else {
|
791 |
+
|
792 |
+
/**
|
793 |
+
* @todo Ultimately, this class shall be descended from PEAR_Error
|
794 |
+
*/
|
795 |
+
class Services_JSON_Error
|
796 |
+
{
|
797 |
+
function Services_JSON_Error($message = 'unknown error', $code = null,
|
798 |
+
$mode = null, $options = null, $userinfo = null)
|
799 |
+
{
|
800 |
+
|
801 |
+
}
|
802 |
+
}
|
803 |
+
|
804 |
+
}
|
805 |
+
?>
|
broken-link-checker.php
CHANGED
@@ -3,7 +3,7 @@
|
|
3 |
Plugin Name: Broken Link Checker
|
4 |
Plugin URI: http://w-shadow.com/blog/2007/08/05/broken-link-checker-for-wordpress/
|
5 |
Description: Checks your posts for broken links and missing images and notifies you on the dashboard if any are found.
|
6 |
-
Version: 0.
|
7 |
Author: Janis Elsts
|
8 |
Author URI: http://w-shadow.com/blog/
|
9 |
*/
|
@@ -16,66 +16,124 @@ MySQL 4.0 compatibility by Jeroen (www.yukka.eu)
|
|
16 |
//The plugin will use Snoopy in case CURL is not available
|
17 |
if (!class_exists('Snoopy')) require_once(ABSPATH.'/wp-includes/class-snoopy.php');
|
18 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
19 |
if (!class_exists('ws_broken_link_checker')) {
|
20 |
|
21 |
class ws_broken_link_checker {
|
22 |
var $options;
|
23 |
var $options_name='wsblc_options';
|
24 |
-
var $postdata_name;
|
25 |
-
var $linkdata_name;
|
26 |
-
var $version='0.4.14';
|
27 |
var $myfile='';
|
28 |
var $myfolder='';
|
29 |
var $mybasename='';
|
30 |
var $siteurl;
|
31 |
var $defaults;
|
|
|
|
|
|
|
32 |
|
33 |
function ws_broken_link_checker() {
|
34 |
global $wpdb;
|
35 |
|
36 |
//set default options
|
37 |
$this->defaults = array(
|
38 |
-
'
|
39 |
-
'
|
40 |
-
'
|
41 |
-
'mark_broken_links' => true,
|
42 |
'broken_link_css' => ".broken_link, a.broken_link {\n\ttext-decoration: line-through;\n}",
|
43 |
-
'exclusion_list' => array(),
|
44 |
-
'
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
45 |
);
|
46 |
-
|
47 |
-
$this->
|
48 |
-
if(!is_array($this->options)){
|
49 |
-
$this->options = $this->defaults;
|
50 |
-
} else {
|
51 |
-
$this->options = array_merge($this->defaults, $this->options);
|
52 |
-
}
|
53 |
|
54 |
-
$this->postdata_name=$wpdb->prefix . "blc_postdata";
|
55 |
-
$this->linkdata_name=$wpdb->prefix . "blc_linkdata";
|
56 |
$this->siteurl = get_option('siteurl');
|
57 |
|
58 |
$my_file = str_replace('\\', '/',__FILE__);
|
59 |
$my_file = preg_replace('/^.*wp-content[\\\\\/]plugins[\\\\\/]/', '', $my_file);
|
60 |
-
add_action('activate_'
|
61 |
$this->myfile=$my_file;
|
62 |
$this->myfolder=basename(dirname(__FILE__));
|
63 |
$this->mybasename=plugin_basename(__FILE__);
|
|
|
|
|
64 |
|
65 |
-
|
66 |
-
|
67 |
-
add_action('delete_post', array(&$this,'post_deleted'));
|
68 |
add_action('save_post', array(&$this,'post_saved'));
|
|
|
|
|
|
|
|
|
|
|
|
|
69 |
add_action('admin_footer', array(&$this,'admin_footer'));
|
70 |
add_action('admin_print_scripts', array(&$this,'admin_print_scripts'));
|
71 |
-
|
|
|
72 |
|
73 |
-
if (
|
74 |
-
add_filter('the_content', array(&$this,'the_content'));
|
75 |
-
if (!empty($this->options['broken_link_css'])){
|
76 |
-
add_action('wp_head', array(&$this,'header_css'));
|
77 |
}
|
78 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
79 |
}
|
80 |
|
81 |
function admin_footer(){
|
@@ -83,13 +141,23 @@ class ws_broken_link_checker {
|
|
83 |
<!-- wsblc admin footer -->
|
84 |
<div id='wsblc_updater_div'></div>
|
85 |
<script type='text/javascript'>
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
93 |
</script>
|
94 |
<!-- /wsblc admin footer -->
|
95 |
<?php
|
@@ -101,307 +169,45 @@ class ws_broken_link_checker {
|
|
101 |
|
102 |
function the_content($content){
|
103 |
global $post, $wpdb;
|
104 |
-
if (empty($post)) return $content;
|
105 |
-
|
106 |
-
$
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
115 |
};
|
116 |
-
|
117 |
-
//print_r($post);
|
118 |
return $content;
|
119 |
}
|
120 |
|
121 |
function mark_broken_links($matches){
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
|
|
126 |
} else {
|
127 |
return $matches[0];
|
128 |
}
|
129 |
}
|
130 |
|
131 |
-
/**
|
132 |
-
* ws_broken_link_checker::normalize_url()
|
133 |
-
*
|
134 |
-
*
|
135 |
-
* @param string $url
|
136 |
-
* @return string or FALSE for invalid/unsupported URLs
|
137 |
-
*/
|
138 |
-
function normalize_url($url){
|
139 |
-
$parts=@parse_url($url);
|
140 |
-
if(!$parts) return false;
|
141 |
-
|
142 |
-
if(isset($parts['scheme'])) {
|
143 |
-
//Only HTTP(S) links are checked. Other protocols are not supported.
|
144 |
-
if ( ($parts['scheme'] != 'http') && ($parts['scheme'] != 'https') )
|
145 |
-
return false;
|
146 |
-
}
|
147 |
-
|
148 |
-
$url = html_entity_decode($url);
|
149 |
-
$url = preg_replace(
|
150 |
-
array('/([\?&]PHPSESSID=\w+)$/i',
|
151 |
-
'/(#[^\/]*)$/',
|
152 |
-
'/&/',
|
153 |
-
'/^(javascript:.*)/i',
|
154 |
-
'/([\?&]sid=\w+)$/i'
|
155 |
-
),
|
156 |
-
array('','','&','',''),
|
157 |
-
$url);
|
158 |
-
$url=trim($url);
|
159 |
-
|
160 |
-
if($url=='') return false;
|
161 |
-
|
162 |
-
// turn relative URLs into absolute URLs
|
163 |
-
$url = $this->relative2absolute($this->siteurl, $url);
|
164 |
-
return $url;
|
165 |
-
}
|
166 |
-
|
167 |
-
function relative2absolute($absolute, $relative) {
|
168 |
-
$p = @parse_url($relative);
|
169 |
-
if(!$p) {
|
170 |
-
//WTF? $relative is a seriously malformed URL
|
171 |
-
return false;
|
172 |
-
}
|
173 |
-
if(isset($p["scheme"])) return $relative;
|
174 |
-
|
175 |
-
$parts=(parse_url($absolute));
|
176 |
-
|
177 |
-
if(substr($relative,0,1)=='/') {
|
178 |
-
$cparts = (explode("/", $relative));
|
179 |
-
array_shift($cparts);
|
180 |
-
} else {
|
181 |
-
if(isset($parts['path'])){
|
182 |
-
$aparts=explode('/',$parts['path']);
|
183 |
-
array_pop($aparts);
|
184 |
-
$aparts=array_filter($aparts);
|
185 |
-
} else {
|
186 |
-
$aparts=array();
|
187 |
-
}
|
188 |
-
|
189 |
-
$rparts = (explode("/", $relative));
|
190 |
-
|
191 |
-
$cparts = array_merge($aparts, $rparts);
|
192 |
-
foreach($cparts as $i => $part) {
|
193 |
-
if($part == '.') {
|
194 |
-
unset($cparts[$i]);
|
195 |
-
} else if($part == '..') {
|
196 |
-
unset($cparts[$i]);
|
197 |
-
unset($cparts[$i-1]);
|
198 |
-
}
|
199 |
-
}
|
200 |
-
}
|
201 |
-
$path = implode("/", $cparts);
|
202 |
-
|
203 |
-
$url = '';
|
204 |
-
if($parts['scheme']) {
|
205 |
-
$url = "$parts[scheme]://";
|
206 |
-
}
|
207 |
-
if(isset($parts['user'])) {
|
208 |
-
$url .= $parts['user'];
|
209 |
-
if(isset($parts['pass'])) {
|
210 |
-
$url .= ":".$parts['pass'];
|
211 |
-
}
|
212 |
-
$url .= "@";
|
213 |
-
}
|
214 |
-
if(isset($parts['host'])) {
|
215 |
-
$url .= $parts['host']."/";
|
216 |
-
}
|
217 |
-
$url .= $path;
|
218 |
-
|
219 |
-
return $url;
|
220 |
-
}
|
221 |
-
|
222 |
-
function page_exists($url){
|
223 |
-
//echo "Checking $url...<br/>";
|
224 |
-
$result = array('final_url'=>$url, 'log'=>'', 'http_code'=>'', 'okay' => false);
|
225 |
-
|
226 |
-
$parts=parse_url($url);
|
227 |
-
if(!$parts) {
|
228 |
-
$result['log'] .= "Invalid link URL (doesn't parse).";
|
229 |
-
return $result;
|
230 |
-
}
|
231 |
-
|
232 |
-
if(!isset($parts['scheme'])) {
|
233 |
-
$url='http://'.$url;
|
234 |
-
$parts['scheme'] = 'http';
|
235 |
-
$result['log'] .= "Protocol not specified, assuming HTTP.\n";
|
236 |
-
}
|
237 |
-
|
238 |
-
//Only HTTP links are checked. All others are automatically considered okay.
|
239 |
-
if ( ($parts['scheme'] != 'http') && ($parts['scheme'] != 'https') ) {
|
240 |
-
$result['log'] .= "URL protocol ($parts[scheme]) is not HTTP. This link won't be checked.\n";
|
241 |
-
return $result;
|
242 |
-
}
|
243 |
-
|
244 |
-
//Kill the #anchor if it's present
|
245 |
-
$anchor_start = strpos($url, '#');
|
246 |
-
if ( $anchor_start !== false ){
|
247 |
-
$url = substr($url, 0, $anchor_start);
|
248 |
-
}
|
249 |
-
|
250 |
-
//******* Use CURL if available ***********
|
251 |
-
if (function_exists('curl_init')) {
|
252 |
-
|
253 |
-
$ch = curl_init();
|
254 |
-
curl_setopt($ch, CURLOPT_URL, $url);
|
255 |
-
//Masquerade as Internet explorer
|
256 |
-
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)');
|
257 |
-
//Add a semi-plausible referer header to avoid tripping up some bot traps
|
258 |
-
curl_setopt($ch, CURLOPT_REFERER, get_option('home'));
|
259 |
-
|
260 |
-
curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);
|
261 |
-
|
262 |
-
@curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
|
263 |
-
curl_setopt($ch, CURLOPT_MAXREDIRS, 10);
|
264 |
-
|
265 |
-
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 20);
|
266 |
-
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
|
267 |
-
|
268 |
-
curl_setopt($ch, CURLOPT_FAILONERROR, false);
|
269 |
-
|
270 |
-
$nobody=false;
|
271 |
-
if($parts['scheme']=='https'){
|
272 |
-
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
|
273 |
-
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
|
274 |
-
} else {
|
275 |
-
$nobody=true;
|
276 |
-
curl_setopt($ch, CURLOPT_NOBODY, true);
|
277 |
-
//curl_setopt($ch, CURLOPT_RANGE, '0-1023');
|
278 |
-
}
|
279 |
-
curl_setopt($ch, CURLOPT_HEADER, true);
|
280 |
-
|
281 |
-
$response = curl_exec($ch);
|
282 |
-
//echo 'Response 1 : <pre>',$response,'</pre>';
|
283 |
-
$code=intval(curl_getinfo($ch, CURLINFO_HTTP_CODE));
|
284 |
-
//echo "Code 1 : $code<br/>";
|
285 |
-
|
286 |
-
$header = '';
|
287 |
-
if (preg_match('/(.+?)\r\n\r\n/s', $response, $matches)){
|
288 |
-
$header = $matches[1];
|
289 |
-
}
|
290 |
-
|
291 |
-
$result['log'] .= "=== First try : ".($response?"$code ===\n$header":"No response. ===")."\n\n";
|
292 |
-
|
293 |
-
if ( (($code<200) || ($code>=400)) && $nobody) {
|
294 |
-
$result['log'] .= "Trying a second time with different settings...\n";
|
295 |
-
curl_setopt($ch, CURLOPT_NOBODY, false);
|
296 |
-
curl_setopt($ch, CURLOPT_HTTPGET, true);
|
297 |
-
curl_setopt($ch, CURLOPT_RANGE, '0-2047');
|
298 |
-
$response = curl_exec($ch);
|
299 |
-
//echo 'Response 2 : <pre>',$response,'</pre>';
|
300 |
-
$code=intval(curl_getinfo($ch, CURLINFO_HTTP_CODE));
|
301 |
-
//echo "Code 2 : $code<br/>";
|
302 |
-
if (preg_match('/(.+?)\r\n\r\n/s', $response, $matches)){
|
303 |
-
$header = $matches[1];
|
304 |
-
}
|
305 |
-
|
306 |
-
$result['log'] .= "=== Second try : ".($response?"$code ===\n$header":"No response. ===")."\n\n";
|
307 |
-
}
|
308 |
-
|
309 |
-
$result['final_url'] = curl_getinfo($ch, CURLINFO_EFFECTIVE_URL);
|
310 |
-
|
311 |
-
curl_close($ch);
|
312 |
-
|
313 |
-
} elseif (class_exists('Snoopy')) {
|
314 |
-
//******** Use Snoopy if CURL is not available *********
|
315 |
-
//Note : Snoopy doesn't work too well with HTTPS URLs.
|
316 |
-
$result['log'] .= "<em>(Using Snoopy)</em>\n";
|
317 |
-
|
318 |
-
$snoopy = new Snoopy;
|
319 |
-
$snoopy->read_timeout = 60; //read timeout in seconds
|
320 |
-
$snoopy->fetch($url);
|
321 |
-
|
322 |
-
$code = $snoopy->status; //HTTP status code
|
323 |
-
|
324 |
-
if ($snoopy->error)
|
325 |
-
$result['log'] .= $snoopy->error."\n";
|
326 |
-
if ($snoopy->timed_out)
|
327 |
-
$result['log'] .= "Request timed out.\n";
|
328 |
-
|
329 |
-
if (is_array($snoopy->headers))
|
330 |
-
$result['log'] .= implode("", $snoopy->headers)."\n"; //those headers already contain newlines
|
331 |
-
|
332 |
-
//$result['log'] .= print_r($snoopy, true);
|
333 |
-
|
334 |
-
if ($snoopy->lastredirectaddr)
|
335 |
-
$result['final_url'] = $snoopy->lastredirectaddr;
|
336 |
-
}
|
337 |
-
|
338 |
-
$result['http_code'] = $code;
|
339 |
-
|
340 |
-
/*"Good" response codes are anything in the 2XX range (e.g "200 OK") and redirects - the 3XX range.
|
341 |
-
HTTP 401 Unauthorized is a special case that is considered OK as well. Other errors - the 4XX range -
|
342 |
-
are treated as "page doesn't exist'". */
|
343 |
-
$result['okay'] = (($code>=200) && ($code<400)) || ($code == 401);
|
344 |
-
$result['log'] .= "Link is ".($result['okay']?'valid':'broken').".";
|
345 |
-
|
346 |
-
return $result;
|
347 |
-
}
|
348 |
-
|
349 |
-
/**
|
350 |
-
* ws_broken_link_checker::check_link()
|
351 |
-
* Checks the link described by the object $link and udpates the DB accordingly.
|
352 |
-
*
|
353 |
-
* @param object $link
|
354 |
-
* @return boolean
|
355 |
-
*/
|
356 |
-
function check_link($link){
|
357 |
-
global $wpdb;
|
358 |
-
|
359 |
-
/*
|
360 |
-
Check for problematic (though not necessarily "broken") links.
|
361 |
-
If a link has been checked multiple times and still hasn't been marked as broken
|
362 |
-
or removed from the queue then probably the checking algorithm is having problems
|
363 |
-
with that link. Mark it as broken and hope the user sorts it out.
|
364 |
-
*/
|
365 |
-
if ($link->check_count >=5){
|
366 |
-
$wpdb->query("UPDATE {$this->linkdata_name}
|
367 |
-
SET broken=1, log=CONCAT(log, '\nProblematic link, checking times out.')
|
368 |
-
WHERE id={$link->id}");
|
369 |
-
//can afford to skip the $max_execution_time check here, the above op. should be very fast.
|
370 |
-
return false;
|
371 |
-
}
|
372 |
-
|
373 |
-
//Update the check_count & last_check fields before actually performing the check.
|
374 |
-
//Useful if something goes terribly wrong in page_exists() with this particular URL.
|
375 |
-
$wpdb->query("UPDATE $this->linkdata_name SET last_check=NOW(), check_count=check_count+1
|
376 |
-
WHERE id=$link->id");
|
377 |
-
|
378 |
-
//Verify that the link should be checked
|
379 |
-
if ( !$this->is_excluded($link->url) ){
|
380 |
-
$rez = $this->page_exists($link->url);
|
381 |
-
|
382 |
-
if ( $rez['okay'] ) {
|
383 |
-
//Link is fine; remove it from the queue.
|
384 |
-
$wpdb->query("DELETE FROM $this->linkdata_name WHERE id=$link->id");
|
385 |
-
return true;
|
386 |
-
} else {
|
387 |
-
//Link is broken.
|
388 |
-
$wpdb->query(
|
389 |
-
"UPDATE $this->linkdata_name
|
390 |
-
SET broken=1, http_code=$rez[http_code], log='".$wpdb->escape($rez['log'])."',
|
391 |
-
final_url = '".$wpdb->escape($rez['final_url'])."'
|
392 |
-
WHERE id=$link->id"
|
393 |
-
);
|
394 |
-
|
395 |
-
return false;
|
396 |
-
}
|
397 |
-
|
398 |
-
} else {
|
399 |
-
//This link doesn't need to be checked because it is excluded.
|
400 |
-
$wpdb->query("DELETE FROM $this->linkdata_name WHERE id=$link->id");
|
401 |
-
return true;
|
402 |
-
}
|
403 |
-
|
404 |
-
}
|
405 |
|
406 |
function is_excluded($url){
|
407 |
if (!is_array($this->options['exclusion_list'])) return false;
|
@@ -413,29 +219,95 @@ class ws_broken_link_checker {
|
|
413 |
return false;
|
414 |
}
|
415 |
|
416 |
-
function
|
417 |
?>
|
418 |
-
|
419 |
-
<div id='wsblc_activity_box'></div>
|
420 |
<script type='text/javascript'>
|
421 |
-
|
422 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
423 |
</script>
|
424 |
-
<!-- /wsblc activity box -->
|
425 |
<?php
|
426 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
427 |
|
428 |
function admin_print_scripts(){
|
429 |
-
//
|
430 |
-
wp_enqueue_script('
|
|
|
431 |
}
|
432 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
433 |
function post_deleted($post_id){
|
434 |
global $wpdb;
|
435 |
-
|
436 |
-
|
437 |
-
|
438 |
-
$wpdb->
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
439 |
}
|
440 |
|
441 |
function post_saved($post_id){
|
@@ -446,117 +318,178 @@ class ws_broken_link_checker {
|
|
446 |
if ( ($post->post_type != 'post') && ($post->post_type != 'page') ) return null;
|
447 |
//Only check published posts
|
448 |
if ( $post->post_status != 'publish' ) return null;
|
449 |
-
|
450 |
-
$
|
451 |
-
if($found===NULL){
|
452 |
-
//this post hasn't been saved previously, save the additional data now
|
453 |
-
$wpdb->query("INSERT INTO $this->postdata_name (post_id, last_check) VALUES($post_id, '00-00-0000 00:00:00')");
|
454 |
-
} else {
|
455 |
-
//mark the post as not checked
|
456 |
-
$wpdb->query("UPDATE $this->postdata_name SET last_check='00-00-0000 00:00:00' WHERE post_id=$post_id");
|
457 |
-
//delete the previously extracted links - they are possibly no longer in the post
|
458 |
-
$wpdb->query("DELETE FROM $this->linkdata_name WHERE post_id=$post_id");
|
459 |
-
}
|
460 |
-
}
|
461 |
-
|
462 |
-
|
463 |
-
function sync_posts_to_db(){
|
464 |
-
global $wpdb;
|
465 |
-
|
466 |
-
/* JHS: This query does not work on mySQL 4.0 (4.0 does not support subqueries).
|
467 |
-
// However, this one is faster, so I'll leave it here (forward compatibility)
|
468 |
-
$sql="INSERT INTO ".$this->postdata_name."( post_id, last_check )
|
469 |
-
SELECT id, '00-00-0000 00:00:00'
|
470 |
-
FROM $wpdb->posts b
|
471 |
-
WHERE NOT EXISTS (
|
472 |
-
SELECT post_id
|
473 |
-
FROM ".$this->postdata_name." a
|
474 |
-
WHERE a.post_id = b.id
|
475 |
-
)"; */
|
476 |
-
//JHS: This one also works on mySQL 4.0:
|
477 |
-
$sql="INSERT INTO ".$this->postdata_name."(post_id, last_check)
|
478 |
-
SELECT ".$wpdb->posts.".id, '00-00-0000 00:00:00' FROM ".$wpdb->posts."
|
479 |
-
LEFT JOIN ".$this->postdata_name." ON ".$wpdb->posts.".id=".$this->postdata_name.".post_id
|
480 |
-
WHERE
|
481 |
-
".$this->postdata_name.".post_id IS NULL
|
482 |
-
AND ".$wpdb->posts.".post_status = 'publish'
|
483 |
-
AND ".$wpdb->posts.".post_type IN ('post', 'page') ";
|
484 |
-
$wpdb->query($sql);
|
485 |
-
}
|
486 |
-
|
487 |
-
//JHS: Clears all blc tables and initiates a new fresh recheck
|
488 |
-
function recheck_all_posts(){
|
489 |
-
global $wpdb;
|
490 |
-
|
491 |
-
//Empty blc_linkdata
|
492 |
-
$sql="TRUNCATE TABLE ".$this->linkdata_name;
|
493 |
-
$wpdb->query($sql);
|
494 |
-
|
495 |
-
//Empty table [aggressive approach]
|
496 |
-
$sql="TRUNCATE TABLE ".$this->postdata_name;
|
497 |
-
//Reset all dates to zero [less aggressive approach, I like the above one better, it's cleaner ;)]
|
498 |
-
//$sql="UPDATE $this->postdata_name SET last_check='00-00-0000 00:00:00' WHERE 1";
|
499 |
-
|
500 |
-
$wpdb->query($sql);
|
501 |
-
|
502 |
-
$this->sync_posts_to_db();
|
503 |
}
|
504 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
505 |
function activation(){
|
506 |
-
|
507 |
-
|
508 |
-
require_once(ABSPATH . 'wp-admin/upgrade-functions.php');
|
509 |
|
510 |
-
|
511 |
-
|
512 |
-
$sql="CREATE TABLE ".$this->postdata_name." (
|
513 |
-
post_id BIGINT( 20 ) NOT NULL ,
|
514 |
-
last_check DATETIME NOT NULL ,
|
515 |
-
UNIQUE KEY post_id (post_id)
|
516 |
-
);";
|
517 |
-
|
518 |
-
dbDelta($sql);
|
519 |
-
}
|
520 |
|
521 |
-
|
522 |
-
|
523 |
-
$sql="CREATE TABLE ".$this->linkdata_name." (
|
524 |
-
id BIGINT( 20 ) UNSIGNED NOT NULL AUTO_INCREMENT ,
|
525 |
-
post_id BIGINT( 20 ) NOT NULL ,
|
526 |
-
url TEXT NOT NULL ,
|
527 |
-
final_url TEXT NOT NULL,
|
528 |
-
link_text VARCHAR( 50 ) NOT NULL ,
|
529 |
-
broken TINYINT( 1 ) UNSIGNED DEFAULT '0' NOT NULL,
|
530 |
-
last_check DATETIME NOT NULL ,
|
531 |
-
check_count TINYINT( 2 ) UNSIGNED DEFAULT '0' NOT NULL,
|
532 |
-
type ENUM('link', 'image') DEFAULT 'link' NOT NULL,
|
533 |
-
log TEXT NOT NULL,
|
534 |
-
http_code SMALLINT NOT NULL,
|
535 |
-
PRIMARY KEY id (id)
|
536 |
-
);";
|
537 |
-
|
538 |
-
dbDelta($sql);
|
539 |
-
|
540 |
-
//upgrade to 0.4.8
|
541 |
-
$wpdb->query("UPDATE ".$this->linkdata_name." SET type='image' WHERE link_text='[image]'");
|
542 |
-
$wpdb->query("UPDATE ".$this->linkdata_name." SET final_url=url WHERE final_url=''");
|
543 |
-
}
|
544 |
-
|
545 |
-
$this->sync_posts_to_db();
|
546 |
-
|
547 |
-
//Update the options
|
548 |
-
$this->options['version'] = $this->version;
|
549 |
-
update_option($this->options_name,$this->options);
|
550 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
551 |
|
552 |
-
function
|
553 |
add_options_page('Link Checker Settings', 'Link Checker', 'manage_options',
|
554 |
-
|
555 |
if (current_user_can('manage_options'))
|
556 |
add_filter('plugin_action_links', array(&$this, 'plugin_action_links'), 10, 2);
|
557 |
|
558 |
add_management_page('View Broken Links', 'Broken Links', 'edit_others_posts',
|
559 |
-
|
560 |
}
|
561 |
|
562 |
/**
|
@@ -570,7 +503,7 @@ class ws_broken_link_checker {
|
|
570 |
*/
|
571 |
function plugin_action_links($links, $file) {
|
572 |
if ($file == $this->mybasename)
|
573 |
-
$links[] = "<a href='options-general.php?page=
|
574 |
return $links;
|
575 |
}
|
576 |
|
@@ -581,98 +514,117 @@ class ws_broken_link_checker {
|
|
581 |
|
582 |
function options_page(){
|
583 |
|
584 |
-
$this->options = get_option('wsblc_options');
|
585 |
-
$reminder = '';
|
586 |
-
//JHS: recheck all posts if asked for:
|
587 |
if (isset($_GET['recheck']) && ($_GET['recheck'] == 'true')) {
|
588 |
-
$this->
|
589 |
}
|
590 |
if (isset($_GET['updated']) && ($_GET['updated'] == 'true')) {
|
591 |
-
if(isset($_POST['
|
592 |
|
593 |
-
$
|
594 |
-
if( $
|
595 |
-
$this->options['
|
596 |
}
|
597 |
|
598 |
-
$
|
599 |
-
if( $
|
600 |
-
$this->options['
|
601 |
}
|
602 |
-
|
|
|
603 |
$new_broken_link_css = trim($_POST['broken_link_css']);
|
604 |
$this->options['broken_link_css'] = $new_broken_link_css;
|
605 |
|
606 |
-
$this->options['
|
607 |
-
|
608 |
-
|
609 |
-
|
610 |
-
|
611 |
-
|
612 |
-
|
613 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
614 |
}
|
615 |
|
616 |
}
|
617 |
-
|
618 |
?>
|
619 |
<div class="wrap"><h2>Broken Link Checker Options</h2>
|
620 |
-
<?php
|
621 |
-
//This check isn't required anymore. Can now use Snoopy when CURL is not available.
|
622 |
-
/*
|
623 |
-
if(!function_exists('curl_init')){ ?>
|
624 |
-
<strong>Error: <a href='http://curl.haxx.se/libcurl/php/'>CURL library</a>
|
625 |
-
is not installed. This plugin won't work.</strong><br/>
|
626 |
-
<?php };
|
627 |
-
*/
|
628 |
-
?>
|
629 |
-
<form name="link_checker_options" method="post" action="<?php echo $_SERVER['PHP_SELF']; ?>?page=<?php echo plugin_basename(__FILE__); ?>&updated=true">
|
630 |
-
<p class="submit"><input type="submit" name="Submit" value="Update Options »" /></p>
|
631 |
|
632 |
-
<
|
|
|
|
|
633 |
|
634 |
<tr valign="top">
|
635 |
-
<th scope="row">Status
|
636 |
<td>
|
637 |
|
638 |
|
639 |
<div id='wsblc_full_status'>
|
640 |
-
<br/><br/>
|
641 |
</div>
|
642 |
<script type='text/javascript'>
|
643 |
-
|
644 |
-
|
645 |
-
|
646 |
-
|
647 |
-
|
648 |
-
|
649 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
650 |
</script>
|
651 |
<?php //JHS: Recheck all posts link: ?>
|
652 |
-
<p><input class="button" type="button" name="recheckbutton" value="Re-check all pages" onclick="location.replace('<?php echo $_SERVER['PHP_SELF']; ?>?page
|
653 |
</td>
|
654 |
</tr>
|
655 |
|
656 |
<tr valign="top">
|
657 |
-
<th scope="row">Check
|
658 |
<td>
|
659 |
|
660 |
-
Every <input type="text" name="
|
661 |
-
value="<?php echo $this->options['
|
662 |
hours
|
663 |
<br/>
|
664 |
-
|
|
|
|
|
665 |
|
666 |
</td>
|
667 |
</tr>
|
668 |
|
669 |
<tr valign="top">
|
670 |
-
<th scope="row">Broken
|
671 |
<td>
|
672 |
<input type="checkbox" name="mark_broken_links" id="mark_broken_links"
|
673 |
<?php if ($this->options['mark_broken_links']) echo ' checked="checked"'; ?>/>
|
674 |
<label for='mark_broken_links'>Apply <em>class="broken_link"</em> to broken links</label><br/>
|
675 |
-
<textarea
|
676 |
if( isset($this->options['broken_link_css']) )
|
677 |
echo $this->options['broken_link_css'];
|
678 |
?></textarea>
|
@@ -681,229 +633,619 @@ class ws_broken_link_checker {
|
|
681 |
</tr>
|
682 |
|
683 |
<tr valign="top">
|
684 |
-
<th scope="row">Exclusion list
|
685 |
-
<td>Don't check links where the URL contains any of these words (one per line):<br/>
|
686 |
-
<textarea
|
687 |
if( isset($this->options['exclusion_list']) )
|
688 |
echo implode("\n", $this->options['exclusion_list']);
|
689 |
?></textarea>
|
690 |
|
691 |
</td>
|
692 |
</tr>
|
693 |
-
|
694 |
<tr valign="top">
|
695 |
-
<th scope="row">
|
696 |
-
<td>
|
697 |
-
|
698 |
-
|
699 |
-
|
700 |
-
|
701 |
-
<br/>
|
702 |
-
The link checker does its work in short "sessions" while any page of the WP admin panel is open.
|
703 |
-
Typically you won't need to change this value.
|
704 |
|
705 |
</td>
|
706 |
</tr>
|
707 |
|
708 |
<tr valign="top">
|
709 |
-
<th scope="row">
|
710 |
<td>
|
711 |
|
712 |
-
<input type="
|
713 |
-
|
714 |
-
|
715 |
-
|
716 |
-
|
|
|
|
|
|
|
717 |
|
718 |
</td>
|
719 |
</tr>
|
720 |
|
721 |
</table>
|
722 |
|
723 |
-
<p class="submit"><input type="submit" name="
|
724 |
</form>
|
725 |
</div>
|
726 |
<?php
|
727 |
}
|
728 |
|
729 |
-
function
|
730 |
global $wpdb;
|
731 |
-
|
732 |
-
|
733 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
734 |
?>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
735 |
<div class="wrap">
|
736 |
<h2><?php
|
737 |
-
|
738 |
-
|
|
|
|
|
|
|
|
|
739 |
?></h2>
|
740 |
-
|
741 |
-
|
742 |
-
|
743 |
-
|
744 |
-
|
745 |
-
|
746 |
-
|
747 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
748 |
<?php
|
749 |
-
$sql="SELECT b.post_title, b.post_date, a.*, b.guid FROM $this->linkdata_name a, $wpdb->posts b
|
750 |
-
WHERE a.post_id=b.id AND a.broken=1 ORDER BY a.last_check DESC";
|
751 |
-
$links=$wpdb->get_results($sql, OBJECT);
|
752 |
if($links && (count($links)>0)){
|
753 |
?>
|
754 |
<table class="widefat">
|
755 |
<thead>
|
756 |
<tr>
|
757 |
|
758 |
-
<th scope="col"
|
759 |
-
|
760 |
-
<th scope="col">Post
|
761 |
</th>
|
762 |
<th scope="col">Link Text</th>
|
763 |
<th scope="col">URL</th>
|
764 |
|
765 |
-
|
|
|
|
|
766 |
|
767 |
</tr>
|
768 |
</thead>
|
769 |
<tbody id="the-list">
|
770 |
<?php
|
771 |
-
|
772 |
-
$rownumber=0;
|
773 |
-
/* reformatted this section - changed a few ids, made the php excerpts
|
774 |
-
explicit to take advantage of syntax highlighting. Most notably, the
|
775 |
-
link details section is changed here.*/
|
776 |
foreach ($links as $link) {
|
777 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
778 |
?>
|
779 |
-
<tr id='<?php print "link-$link->id" ?>' class='alternate'>
|
780 |
-
<th scope='row' style='text-align: center'><?php print $rownumber; ?></th>
|
781 |
-
<td>
|
782 |
-
<a href='<?php print get_permalink($link->post_id); ?>'
|
783 |
-
title='View post'><?php print $link->post_title; ?></a></td>
|
784 |
-
|
785 |
-
<td><?php print $link->link_text; ?></td>
|
786 |
-
<td>
|
787 |
-
<a href='<?php print $link->url; ?>' target='_blank'>
|
788 |
-
<?php print $this->mytruncate($link->url); ?></a>
|
789 |
-
| <a href='javascript:editBrokenLink(<?php print "$link->id, \"$link->url\""; ?>)'
|
790 |
-
id='link-editor-button-<?php print $link->id; ?>'>Edit</a>
|
791 |
-
<br />
|
792 |
-
<input type='text' size='50' id='link-editor-<?php print $link->id; ?>'
|
793 |
-
value='<?php print $link->url; ?>'
|
794 |
-
class='link-editor' style='display:none' />
|
795 |
</td>
|
796 |
-
|
797 |
-
|
798 |
-
|
799 |
-
|
800 |
-
|
801 |
-
|
802 |
-
|
803 |
-
//the ""Delete Post"" button - optional
|
804 |
-
if ($this->options['delete_post_button']){
|
805 |
-
$deletion_url = "post.php?action=delete&post=$link->post_id";
|
806 |
-
$deletion_url = wp_nonce_url($deletion_url, "delete-post_$link->post_id");
|
807 |
-
echo "<td><a href='$deletion_url'>Delete Post</a></td>";
|
808 |
-
}
|
809 |
-
?><td><a href='javascript:void(0);' class='delete'
|
810 |
-
id='discard_button-<?php print $link->id; ?>'
|
811 |
-
onclick='discardLinkMessage(<?php print $link->id; ?>);return false;'
|
812 |
-
title='Discard This Message'>Discard</a></td>
|
813 |
-
|
814 |
-
<td><a href='javascript:void(0);' class='delete' id='unlink_button-<?php print $link->id; ?>'
|
815 |
-
onclick='removeLinkFromPost(<?php print $link->id; ?>);return false;'
|
816 |
-
title='Remove the link from the post'>Unlink</a></td>
|
817 |
</tr>
|
818 |
<!-- Link details -->
|
819 |
-
<tr id='<?php print "link-details-$
|
820 |
-
|
821 |
-
|
822 |
-
?></span>
|
823 |
-
<span id='check_date_full' style='display:none;'><?php
|
824 |
-
print $link->last_check;
|
825 |
-
?></span>
|
826 |
-
<td colspan='8'>
|
827 |
-
<ol style='list-style-type: none; width: 50%; float: right;'>
|
828 |
-
<li><strong>Log :</strong>
|
829 |
-
<span id='blc_log'><?php
|
830 |
-
print nl2br($link->log);
|
831 |
-
?></span></li></ol>
|
832 |
-
<ol style='list-style-type: none; padding-left: 2px;'>
|
833 |
-
<li><strong>Published On :</strong>
|
834 |
-
<span id='post_date'><?php
|
835 |
-
print strftime("%B %d, %Y",strtotime($link->post_date));
|
836 |
-
?></span></li>
|
837 |
-
<li><strong>Last Checked :</strong>
|
838 |
-
<span id='check_date'><?php
|
839 |
-
print strftime("%B %d, %Y",strtotime($link->last_check));
|
840 |
-
?></span></li>
|
841 |
-
<li><strong>Final URL :</strong>
|
842 |
-
<span id='final_url'><?php
|
843 |
-
print "$link->final_url";
|
844 |
-
?></span></li>
|
845 |
-
<li><strong>HTTP Code :</strong>
|
846 |
-
<span id='http_code'><?php
|
847 |
-
print "$link->http_code";
|
848 |
-
?></span></li></ol>
|
849 |
-
</td></tr><?php
|
850 |
}
|
851 |
?></tbody></table><?php
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
852 |
};
|
853 |
?>
|
854 |
-
|
855 |
-
|
856 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
857 |
}
|
858 |
-
</style>
|
859 |
|
860 |
-
|
861 |
-
|
862 |
-
|
863 |
-
|
864 |
-
|
865 |
-
|
866 |
-
|
867 |
-
|
868 |
-
|
869 |
-
|
870 |
-
|
871 |
-
|
872 |
-
|
873 |
-
|
874 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
875 |
}
|
876 |
-
}
|
877 |
-
|
878 |
-
|
879 |
-
|
880 |
-
|
881 |
-
|
882 |
-
|
883 |
-
|
884 |
-
|
885 |
-
|
886 |
-
|
887 |
-
|
888 |
-
|
889 |
-
|
890 |
-
|
891 |
-
|
892 |
-
|
893 |
-
|
894 |
-
|
895 |
-
|
896 |
-
|
897 |
-
|
898 |
-
|
899 |
-
|
900 |
-
|
901 |
-
|
902 |
-
|
903 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
904 |
|
905 |
-
}
|
906 |
function removeLinkFromPost(link_id){
|
|
|
|
|
907 |
$('unlink_button-'+link_id).innerHTML = 'Wait...';
|
908 |
|
909 |
new Ajax.Request(
|
@@ -927,178 +1269,933 @@ class ws_broken_link_checker {
|
|
927 |
}
|
928 |
);
|
929 |
}
|
930 |
-
|
931 |
-
|
932 |
-
|
933 |
-
|
934 |
-
|
935 |
-
|
936 |
-
|
937 |
-
|
938 |
-
|
939 |
-
|
940 |
-
|
941 |
-
|
942 |
-
|
943 |
-
|
944 |
-
|
945 |
-
|
946 |
-
|
947 |
-
|
948 |
-
|
949 |
-
|
950 |
-
|
951 |
-
|
952 |
-
|
953 |
-
|
954 |
-
|
955 |
-
|
956 |
-
|
957 |
-
|
958 |
-
|
959 |
-
|
960 |
-
|
961 |
-
|
962 |
-
|
963 |
-
|
964 |
-
|
965 |
-
|
966 |
-
|
967 |
-
|
968 |
-
|
969 |
-
|
970 |
-
|
971 |
-
|
972 |
-
|
973 |
-
|
974 |
-
|
975 |
-
|
976 |
-
|
977 |
-
|
978 |
-
|
979 |
-
|
980 |
-
|
981 |
-
|
982 |
-
|
983 |
-
|
984 |
-
|
985 |
-
|
986 |
-
|
987 |
-
|
988 |
-
|
989 |
-
|
990 |
-
|
991 |
-
|
992 |
-
|
993 |
-
|
994 |
-
|
995 |
-
|
996 |
-
|
997 |
-
|
998 |
-
|
999 |
-
|
1000 |
-
|
1001 |
-
|
1002 |
-
|
1003 |
-
|
1004 |
-
|
1005 |
-
for (var i = 0; i < children.length; i++){
|
1006 |
-
child = children[i];
|
1007 |
-
if(child === undefined){
|
1008 |
-
continue;
|
1009 |
-
}
|
1010 |
-
if(child === undefined || child.nodeType !== Node.ELEMENT_NODE ||
|
1011 |
-
child.nodeName !== 'TR'){
|
1012 |
-
continue;
|
1013 |
-
}
|
1014 |
-
if(child.id && child.id.match(re)){
|
1015 |
-
myid = (child.id.match(re))[1];
|
1016 |
-
mydetails = child;
|
1017 |
-
var mydate = false;
|
1018 |
-
while ( mydetails !== undefined &&
|
1019 |
-
mydetails !== null &&
|
1020 |
-
mydetails.id != 'link-details-'+myid){
|
1021 |
-
mydetails = mydetails.nextSibling;
|
1022 |
-
}
|
1023 |
-
var node = rSearch(child, sort_type+'_full').firstChild;
|
1024 |
-
if(node){
|
1025 |
-
mydate = new Date(node.nodeValue.toString());
|
1026 |
-
links.push({
|
1027 |
-
id: myid,
|
1028 |
-
date: mydate,
|
1029 |
-
details: mydetails === null ? null : mydetails.cloneNode(true),
|
1030 |
-
link: child === null ? null : child.cloneNode(true)
|
1031 |
-
});
|
1032 |
-
}
|
1033 |
-
}
|
1034 |
-
}
|
1035 |
-
while(theList.hasChildNodes()){
|
1036 |
-
try{
|
1037 |
-
theList.removeChild(theList.firstChild);
|
1038 |
-
}
|
1039 |
-
catch(e){
|
1040 |
-
break;
|
1041 |
-
}
|
1042 |
-
}
|
1043 |
-
links = mergesort(links);
|
1044 |
-
for (var i = 0; i < links.length; i++){
|
1045 |
-
var obj = links[i];
|
1046 |
-
theList.appendChild(obj.link);
|
1047 |
-
theList.appendChild(obj.details);
|
1048 |
-
}
|
1049 |
-
}
|
1050 |
-
/* end of js changes */
|
1051 |
|
1052 |
-
|
1053 |
-
|
1054 |
-
|
1055 |
-
|
1056 |
-
|
1057 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1058 |
} else {
|
1059 |
-
$(
|
1060 |
-
new_url = $('link-editor-'+link_id).value;
|
1061 |
-
if (new_url != orig_link){
|
1062 |
-
//Save the changed link
|
1063 |
-
new Ajax.Request(
|
1064 |
-
'<?php
|
1065 |
-
echo get_option( "siteurl" ).'/wp-content/plugins/'.$this->myfolder.'/wsblc_ajax.php?';
|
1066 |
-
?>action=edit_link&id='+link_id+'&new_url='+escape(new_url),
|
1067 |
-
{
|
1068 |
-
method:'post',
|
1069 |
-
onSuccess: function(transport){
|
1070 |
-
var re = /OK:.*/i
|
1071 |
-
var response = transport.responseText || "";
|
1072 |
-
if (re.test(response)){
|
1073 |
-
$('link-'+link_id).hide();
|
1074 |
-
$('link-details-'+link_id).hide();
|
1075 |
-
alterLinkCounter(-1);
|
1076 |
-
//alert(response);
|
1077 |
-
} else {
|
1078 |
-
alert(response);
|
1079 |
-
}
|
1080 |
-
}
|
1081 |
-
}
|
1082 |
-
);
|
1083 |
-
|
1084 |
-
}
|
1085 |
-
$('link-editor-button-'+link_id).innerHTML = 'Edit';
|
1086 |
}
|
1087 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1088 |
|
1089 |
-
|
1090 |
-
|
1091 |
-
|
1092 |
-
|
1093 |
-
|
1094 |
-
|
1095 |
-
|
1096 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1097 |
|
1098 |
}//class ends here
|
1099 |
|
1100 |
} // if class_exists...
|
1101 |
|
1102 |
-
|
|
|
1103 |
|
1104 |
?>
|
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 |
*/
|
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 |
+
/*
|
41 |
+
//FirePHP for debugging
|
42 |
+
if ( !class_exists('FB') ) {
|
43 |
+
require 'FirePHPCore/fb.php';
|
44 |
+
}
|
45 |
+
//FB::setEnabled(false);
|
46 |
+
|
47 |
+
//to comment out all calls : (^[^\/]*)(FB::) -> $1\/\/$2
|
48 |
+
//to uncomment : \/\/(\s*FB::) -> $1
|
49 |
+
//*/
|
50 |
+
|
51 |
+
require 'utility-class.php';
|
52 |
+
require 'instance-classes.php';
|
53 |
+
require 'link-classes.php';
|
54 |
+
|
55 |
if (!class_exists('ws_broken_link_checker')) {
|
56 |
|
57 |
class ws_broken_link_checker {
|
58 |
var $options;
|
59 |
var $options_name='wsblc_options';
|
|
|
|
|
|
|
60 |
var $myfile='';
|
61 |
var $myfolder='';
|
62 |
var $mybasename='';
|
63 |
var $siteurl;
|
64 |
var $defaults;
|
65 |
+
|
66 |
+
var $execution_start_time;
|
67 |
+
var $lockfile_handle = null;
|
68 |
|
69 |
function ws_broken_link_checker() {
|
70 |
global $wpdb;
|
71 |
|
72 |
//set default options
|
73 |
$this->defaults = array(
|
74 |
+
'max_execution_time' => 5*60, //How long the worker instance may run, at most.
|
75 |
+
'check_threshold' => 72, //Check each link every 72 hours.
|
76 |
+
'mark_broken_links' => true, //Whether to add the broken_link class to broken links in posts.
|
|
|
77 |
'broken_link_css' => ".broken_link, a.broken_link {\n\ttext-decoration: line-through;\n}",
|
78 |
+
'exclusion_list' => array(), //Links that contain a substring listed in this array won't be checked.
|
79 |
+
'recheck_count' => 3, //[Internal] How many times a broken link should be re-checked (slightly buggy)
|
80 |
+
|
81 |
+
//These are currently ignored. Everything is checked by default.
|
82 |
+
'check_posts' => true,
|
83 |
+
'check_custom_fields' => true,
|
84 |
+
'check_blogroll' => true,
|
85 |
+
|
86 |
+
'custom_fields' => array(), //List of custom fields that can contain URLs and should be checked.
|
87 |
+
|
88 |
+
'autoexpand_widget' => true,
|
89 |
+
|
90 |
+
'need_resynch' => false, //[Internal flag]
|
91 |
+
|
92 |
);
|
93 |
+
|
94 |
+
$this->load_options();
|
|
|
|
|
|
|
|
|
|
|
95 |
|
|
|
|
|
96 |
$this->siteurl = get_option('siteurl');
|
97 |
|
98 |
$my_file = str_replace('\\', '/',__FILE__);
|
99 |
$my_file = preg_replace('/^.*wp-content[\\\\\/]plugins[\\\\\/]/', '', $my_file);
|
100 |
+
add_action('activate_' . plugin_basename(__FILE__), array(&$this,'activation'));
|
101 |
$this->myfile=$my_file;
|
102 |
$this->myfolder=basename(dirname(__FILE__));
|
103 |
$this->mybasename=plugin_basename(__FILE__);
|
104 |
+
|
105 |
+
add_action('admin_menu', array(&$this,'admin_menu'));
|
106 |
|
107 |
+
//These hooks update the plugin's internal records when posts are added, deleted or modified.
|
108 |
+
add_action('delete_post', array(&$this,'post_deleted'));
|
|
|
109 |
add_action('save_post', array(&$this,'post_saved'));
|
110 |
+
|
111 |
+
//These do the same for (blogroll) links.
|
112 |
+
add_action('add_link', array(&$this,'hook_add_link'));
|
113 |
+
add_action('edit_link', array(&$this,'hook_edit_link'));
|
114 |
+
add_action('delete_link', array(&$this,'hook_delete_link'));
|
115 |
+
|
116 |
add_action('admin_footer', array(&$this,'admin_footer'));
|
117 |
add_action('admin_print_scripts', array(&$this,'admin_print_scripts'));
|
118 |
+
//The dashboard widget
|
119 |
+
add_action('wp_dashboard_setup', array(&$this, 'hook_wp_dashboard_setup'));
|
120 |
|
121 |
+
if ( $this->options['mark_broken_links'] ){
|
122 |
+
add_filter( 'the_content', array(&$this,'the_content') );
|
123 |
+
if ( !empty($this->options['broken_link_css']) ){
|
124 |
+
add_action( 'wp_head', array(&$this,'header_css') );
|
125 |
}
|
126 |
}
|
127 |
+
|
128 |
+
//AJAXy hooks
|
129 |
+
add_action( 'wp_ajax_blc_full_status', array(&$this,'ajax_full_status') );
|
130 |
+
add_action( 'wp_ajax_blc_dashboard_status', array(&$this,'ajax_dashboard_status') );
|
131 |
+
add_action( 'wp_ajax_blc_work', array(&$this,'ajax_work') );
|
132 |
+
add_action( 'wp_ajax_blc_discard', array(&$this,'ajax_discard') );
|
133 |
+
add_action( 'wp_ajax_blc_edit', array(&$this,'ajax_edit') );
|
134 |
+
add_action( 'wp_ajax_blc_link_details', array(&$this,'ajax_link_details') );
|
135 |
+
add_action( 'wp_ajax_blc_exclude_link', array(&$this,'ajax_exclude_link') );
|
136 |
+
add_action( 'wp_ajax_blc_unlink', array(&$this,'ajax_unlink') );
|
137 |
}
|
138 |
|
139 |
function admin_footer(){
|
141 |
<!-- wsblc admin footer -->
|
142 |
<div id='wsblc_updater_div'></div>
|
143 |
<script type='text/javascript'>
|
144 |
+
(function($){
|
145 |
+
|
146 |
+
function blcDoWork(){
|
147 |
+
$.post(
|
148 |
+
"<?php bloginfo( 'wpurl' ); ?>/wp-admin/admin-ajax.php",
|
149 |
+
{
|
150 |
+
'action' : 'blc_work'
|
151 |
+
},
|
152 |
+
function (data, textStatus){}
|
153 |
+
);
|
154 |
+
}
|
155 |
+
//Call it the first time
|
156 |
+
blcDoWork();
|
157 |
+
//...and every max_execution_time seconds
|
158 |
+
setInterval(blcDoWork, <?php echo ($this->options['max_execution_time'] + 1 )*1000; ?>);
|
159 |
+
|
160 |
+
})(jQuery);
|
161 |
</script>
|
162 |
<!-- /wsblc admin footer -->
|
163 |
<?php
|
169 |
|
170 |
function the_content($content){
|
171 |
global $post, $wpdb;
|
172 |
+
if ( empty($post) ) return $content;
|
173 |
+
|
174 |
+
$q = "
|
175 |
+
SELECT instances.link_text, links.*
|
176 |
+
|
177 |
+
FROM {$wpdb->prefix}blc_instances AS instances, {$wpdb->prefix}blc_links AS links
|
178 |
+
|
179 |
+
WHERE
|
180 |
+
instances.source_id = %d
|
181 |
+
AND instances.source_type = 'post'
|
182 |
+
AND instances.instance_type = 'link'
|
183 |
+
|
184 |
+
AND instances.link_id = links.link_id
|
185 |
+
AND links.check_count > 0
|
186 |
+
AND ( links.http_code < 200 OR links.http_code >= 400 OR links.timeout = 1 )";
|
187 |
+
|
188 |
+
$rows = $wpdb->get_results( $wpdb->prepare( $q, $post->ID ), ARRAY_A );
|
189 |
+
if( $rows ){
|
190 |
+
$this->links_to_remove = array();
|
191 |
+
foreach($rows as $row){
|
192 |
+
$this->links_to_remove[$row['url']] = $row;
|
193 |
+
}
|
194 |
+
$content = preg_replace_callback( blcUtility::link_pattern(), array(&$this,'mark_broken_links'), $content );
|
195 |
};
|
196 |
+
|
|
|
197 |
return $content;
|
198 |
}
|
199 |
|
200 |
function mark_broken_links($matches){
|
201 |
+
//TODO:Tooltip-style popups with more info
|
202 |
+
$url = blcUtility::normalize_url( html_entity_decode( $matches[3] ) );
|
203 |
+
if( isset( $this->links_to_remove[$url] ) ){
|
204 |
+
return $matches[1].$matches[2].$matches[3].$matches[2].' class="broken_link" '.$matches[4].
|
205 |
+
$matches[5].$matches[6];
|
206 |
} else {
|
207 |
return $matches[0];
|
208 |
}
|
209 |
}
|
210 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
211 |
|
212 |
function is_excluded($url){
|
213 |
if (!is_array($this->options['exclusion_list'])) return false;
|
219 |
return false;
|
220 |
}
|
221 |
|
222 |
+
function dashboard_widget(){
|
223 |
?>
|
224 |
+
<div id='wsblc_activity_box' style="line-height : 140%">Loading...</div>
|
|
|
225 |
<script type='text/javascript'>
|
226 |
+
jQuery(function($){
|
227 |
+
var blc_was_autoexpanded = false;
|
228 |
+
|
229 |
+
function blcDashboardStatus(){
|
230 |
+
$.getJSON(
|
231 |
+
"<?php bloginfo( 'wpurl' ); ?>/wp-admin/admin-ajax.php",
|
232 |
+
{
|
233 |
+
'action' : 'blc_dashboard_status'
|
234 |
+
},
|
235 |
+
function (data, textStatus){
|
236 |
+
if ( typeof(data['text']) != 'undefined'){
|
237 |
+
$('#wsblc_activity_box').html(data.text);
|
238 |
+
<?php if ( $this->options['autoexpand_widget'] ) { ?>
|
239 |
+
//Expand the widget if there are broken links.
|
240 |
+
//Do this only once per pageload so as not to annoy the user.
|
241 |
+
if ( !blc_was_autoexpanded && ( data.status.broken_links > 0 ) ){
|
242 |
+
$('#blc_dashboard_widget.postbox').removeClass('closed');
|
243 |
+
blc_was_autoexpanded = true;
|
244 |
+
};
|
245 |
+
<?php } ?>
|
246 |
+
} else {
|
247 |
+
$('#wsblc_activity_box').html('[ Network error ]');
|
248 |
+
}
|
249 |
+
|
250 |
+
setTimeout( blcDashboardStatus, 120*1000 ); //...update every two minutes
|
251 |
+
}
|
252 |
+
);
|
253 |
+
}
|
254 |
+
blcDashboardStatus();//Call it the first time
|
255 |
+
|
256 |
+
});
|
257 |
</script>
|
|
|
258 |
<?php
|
259 |
}
|
260 |
+
|
261 |
+
function dashboard_widget_control( $widget_id, $form_inputs = array() ){
|
262 |
+
if ( 'POST' == $_SERVER['REQUEST_METHOD'] && 'blc_dashboard_widget' == $_POST['widget_id'] ) {
|
263 |
+
//It appears $form_inputs isn't used in the current WP version, so lets just use $_POST
|
264 |
+
$this->options['autoexpand_widget'] = !empty($_POST['blc-autoexpand']);
|
265 |
+
$this->save_options();
|
266 |
+
}
|
267 |
+
|
268 |
+
?>
|
269 |
+
<p><label for="blc-autoexpand">
|
270 |
+
<input id="blc-autoexpand" name="blc-autoexpand" type="checkbox" value="1" <?php if ( $this->options['autoexpand_widget'] ) echo 'checked="checked"'; ?> />
|
271 |
+
Automatically expand the widget if broken links have been detected
|
272 |
+
</label></p>
|
273 |
+
<?php
|
274 |
+
}
|
275 |
|
276 |
function admin_print_scripts(){
|
277 |
+
//jQuery is used for AJAX and effects
|
278 |
+
wp_enqueue_script('jquery');
|
279 |
+
wp_enqueue_script('jquery-ui-core');
|
280 |
}
|
281 |
|
282 |
+
/**
|
283 |
+
* ws_broken_link_checker::post_deleted()
|
284 |
+
* A hook for post_deleted. Remove link instances associated with that post.
|
285 |
+
*
|
286 |
+
* @param int $post_id
|
287 |
+
* @return void
|
288 |
+
*/
|
289 |
function post_deleted($post_id){
|
290 |
global $wpdb;
|
291 |
+
|
292 |
+
//FB::log($post_id, "Post deleted");
|
293 |
+
//Remove this post's instances
|
294 |
+
$q = "DELETE FROM {$wpdb->prefix}blc_instances
|
295 |
+
WHERE source_id = %d AND (source_type = 'post' OR source_type='custom_field')";
|
296 |
+
$q = $wpdb->prepare($q, intval($post_id) );
|
297 |
+
|
298 |
+
//FB::log($q, 'Executing query');
|
299 |
+
|
300 |
+
if ( $wpdb->query( $q ) === false ){
|
301 |
+
//FB::error($wpdb->last_error, "Database error");
|
302 |
+
}
|
303 |
+
|
304 |
+
//Remove the synch record
|
305 |
+
$q = "DELETE FROM {$wpdb->prefix}blc_synch
|
306 |
+
WHERE source_id = %d AND source_type = 'post'";
|
307 |
+
$wpdb->query( $wpdb->prepare($q, intval($post_id)) );
|
308 |
+
|
309 |
+
//Remove any dangling link records
|
310 |
+
$this->cleanup_links();
|
311 |
}
|
312 |
|
313 |
function post_saved($post_id){
|
318 |
if ( ($post->post_type != 'post') && ($post->post_type != 'page') ) return null;
|
319 |
//Only check published posts
|
320 |
if ( $post->post_status != 'publish' ) return null;
|
321 |
+
|
322 |
+
$this->mark_unsynched( $post_id, 'post' );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
323 |
}
|
324 |
+
|
325 |
+
function initiate_recheck(){
|
326 |
+
global $wpdb;
|
327 |
+
|
328 |
+
//Delete all discovered instances
|
329 |
+
$wpdb->query("TRUNCATE {$wpdb->prefix}blc_instances");
|
330 |
+
|
331 |
+
//Delete all discovered links
|
332 |
+
$wpdb->query("TRUNCATE {$wpdb->prefix}blc_links");
|
333 |
+
|
334 |
+
//Mark all posts, custom fields and bookmarks for processing.
|
335 |
+
$this->resynch();
|
336 |
+
}
|
337 |
+
|
338 |
+
function resynch(){
|
339 |
+
global $wpdb;
|
340 |
+
|
341 |
+
//Drop all synchronization records
|
342 |
+
$wpdb->query("TRUNCATE {$wpdb->prefix}blc_synch");
|
343 |
+
|
344 |
+
|
345 |
+
//Create new synchronization records for posts
|
346 |
+
$q = "INSERT INTO {$wpdb->prefix}blc_synch(source_id, source_type, synched)
|
347 |
+
SELECT id, 'post', 0
|
348 |
+
FROM {$wpdb->posts}
|
349 |
+
WHERE
|
350 |
+
{$wpdb->posts}.post_status = 'publish'
|
351 |
+
AND {$wpdb->posts}.post_type IN ('post', 'page')";
|
352 |
+
$wpdb->query( $q );
|
353 |
+
|
354 |
+
//Create new synchronization records for bookmarks (the blogroll)
|
355 |
+
$q = "INSERT INTO {$wpdb->prefix}blc_synch(source_id, source_type, synched)
|
356 |
+
SELECT link_id, 'blogroll', 0
|
357 |
+
FROM {$wpdb->links}
|
358 |
+
WHERE 1";
|
359 |
+
$wpdb->query( $q );
|
360 |
+
|
361 |
+
//Delete invalid instances
|
362 |
+
$this->cleanup_instances();
|
363 |
+
//Delete orphaned links
|
364 |
+
$this->cleanup_links();
|
365 |
+
|
366 |
+
$this->options['need_resynch'] = true;
|
367 |
+
$this->save_options();
|
368 |
+
}
|
369 |
+
|
370 |
+
function mark_unsynched( $source_id, $source_type ){
|
371 |
+
global $wpdb;
|
372 |
+
|
373 |
+
$q = "REPLACE INTO {$wpdb->prefix}blc_synch( source_id, source_type, synched, last_synch)
|
374 |
+
VALUES( %d, %s, %d, NOW() )";
|
375 |
+
$rez = $wpdb->query( $wpdb->prepare( $q, $source_id, $source_type, 0 ) );
|
376 |
+
|
377 |
+
if ( !$this->options['need_resynch'] ){
|
378 |
+
$this->options['need_resynch'] = true;
|
379 |
+
$this->save_options();
|
380 |
+
}
|
381 |
+
|
382 |
+
return $rez;
|
383 |
+
}
|
384 |
+
|
385 |
+
function mark_synched( $source_id, $source_type ){
|
386 |
+
global $wpdb;
|
387 |
+
//FB::log("Marking $source_type $source_id as synched.");
|
388 |
+
$q = "REPLACE INTO {$wpdb->prefix}blc_synch( source_id, source_type, synched, last_synch)
|
389 |
+
VALUES( %d, %s, %d, NOW() )";
|
390 |
+
return $wpdb->query( $wpdb->prepare( $q, $source_id, $source_type, 1 ) );
|
391 |
+
}
|
392 |
+
|
393 |
function activation(){
|
394 |
+
//Prepare the database.
|
395 |
+
$this->upgrade_database();
|
|
|
396 |
|
397 |
+
//Clear the instance table and mark all posts and other parse-able objects as unsynchronized.
|
398 |
+
$this->resynch();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
399 |
|
400 |
+
//Save the default options.
|
401 |
+
$this->save_options();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
402 |
}
|
403 |
+
|
404 |
+
/**
|
405 |
+
* ws_broken_link_checker::upgrade_database()
|
406 |
+
* Create and/or upgrade database tables
|
407 |
+
*
|
408 |
+
* @return bool
|
409 |
+
*/
|
410 |
+
function upgrade_database(){
|
411 |
+
global $wpdb;
|
412 |
+
|
413 |
+
//Delete tables used by older versions of the plugin
|
414 |
+
$rez = $wpdb->query( "DROP TABLE IF EXISTS {$wpdb->prefix}blc_linkdata, {$wpdb->prefix}blc_postdata" );
|
415 |
+
if ( $rez === false ){
|
416 |
+
//FB::error($wpdb->last_error, "Database error");
|
417 |
+
return false;
|
418 |
+
}
|
419 |
+
|
420 |
+
//Create the link table if it doesn't exist yet.
|
421 |
+
$rez = $wpdb->query(
|
422 |
+
"CREATE TABLE IF NOT EXISTS {$wpdb->prefix}blc_links (
|
423 |
+
link_id int(20) unsigned NOT NULL auto_increment,
|
424 |
+
url text NOT NULL,
|
425 |
+
last_check datetime NOT NULL default '0000-00-00 00:00:00',
|
426 |
+
check_count int(2) unsigned NOT NULL default '0',
|
427 |
+
final_url text NOT NULL,
|
428 |
+
redirect_count smallint(5) unsigned NOT NULL,
|
429 |
+
log text NOT NULL,
|
430 |
+
http_code smallint(6) NOT NULL,
|
431 |
+
request_duration float NOT NULL default '0',
|
432 |
+
timeout tinyint(1) unsigned NOT NULL default '0',
|
433 |
+
|
434 |
+
PRIMARY KEY (link_id),
|
435 |
+
KEY url (url(150)),
|
436 |
+
KEY final_url (final_url(150)),
|
437 |
+
KEY http_code (http_code),
|
438 |
+
KEY timeout (timeout)
|
439 |
+
)"
|
440 |
+
);
|
441 |
+
if ( $rez === false ){
|
442 |
+
//FB::error($wpdb->last_error, "Database error");
|
443 |
+
return false;
|
444 |
+
}
|
445 |
+
|
446 |
+
//Create the instance table if it doesn't exist yet.
|
447 |
+
$wpdb->query(
|
448 |
+
"CREATE TABLE IF NOT EXISTS wp_blc_instances (
|
449 |
+
instance_id int(10) unsigned NOT NULL auto_increment,
|
450 |
+
link_id int(10) unsigned NOT NULL,
|
451 |
+
source_id int(10) unsigned NOT NULL,
|
452 |
+
source_type enum('post','blogroll','custom_field') NOT NULL default 'post',
|
453 |
+
link_text varchar(250) NOT NULL,
|
454 |
+
instance_type enum('link','image') NOT NULL default 'link',
|
455 |
+
|
456 |
+
PRIMARY KEY (instance_id),
|
457 |
+
KEY link_id (link_id),
|
458 |
+
KEY source_id (source_id,source_type)
|
459 |
+
)"
|
460 |
+
);
|
461 |
+
if ( $rez === false ){
|
462 |
+
//FB::error($wpdb->last_error, "Database error");
|
463 |
+
return false;
|
464 |
+
}
|
465 |
+
|
466 |
+
//....
|
467 |
+
$wpdb->query(
|
468 |
+
"CREATE TABLE IF NOT EXISTS {$wpdb->prefix}blc_synch (
|
469 |
+
source_id int(20) unsigned NOT NULL,
|
470 |
+
source_type enum('post','blogroll') NOT NULL,
|
471 |
+
synched tinyint(3) unsigned NOT NULL,
|
472 |
+
last_synch datetime NOT NULL,
|
473 |
+
PRIMARY KEY (source_id, source_type),
|
474 |
+
KEY synched (synched)
|
475 |
+
)"
|
476 |
+
);
|
477 |
+
if ( $rez === false ){
|
478 |
+
//FB::error($wpdb->last_error, "Database error");
|
479 |
+
return false;
|
480 |
+
}
|
481 |
+
|
482 |
+
return true;
|
483 |
+
}
|
484 |
|
485 |
+
function admin_menu(){
|
486 |
add_options_page('Link Checker Settings', 'Link Checker', 'manage_options',
|
487 |
+
'link-checker-settings',array(&$this, 'options_page'));
|
488 |
if (current_user_can('manage_options'))
|
489 |
add_filter('plugin_action_links', array(&$this, 'plugin_action_links'), 10, 2);
|
490 |
|
491 |
add_management_page('View Broken Links', 'Broken Links', 'edit_others_posts',
|
492 |
+
'view-broken-links',array(&$this, 'links_page'));
|
493 |
}
|
494 |
|
495 |
/**
|
503 |
*/
|
504 |
function plugin_action_links($links, $file) {
|
505 |
if ($file == $this->mybasename)
|
506 |
+
$links[] = "<a href='options-general.php?page=link-checker-settings'>" . __('Settings') . "</a>";
|
507 |
return $links;
|
508 |
}
|
509 |
|
514 |
|
515 |
function options_page(){
|
516 |
|
|
|
|
|
|
|
517 |
if (isset($_GET['recheck']) && ($_GET['recheck'] == 'true')) {
|
518 |
+
$this->initiate_recheck();
|
519 |
}
|
520 |
if (isset($_GET['updated']) && ($_GET['updated'] == 'true')) {
|
521 |
+
if(isset($_POST['submit'])) {
|
522 |
|
523 |
+
$new_execution_time = intval($_POST['max_execution_time']);
|
524 |
+
if( $new_execution_time > 0 ){
|
525 |
+
$this->options['max_execution_time'] = $new_execution_time;
|
526 |
}
|
527 |
|
528 |
+
$new_check_threshold=intval($_POST['check_threshold']);
|
529 |
+
if( $new_check_threshold > 0 ){
|
530 |
+
$this->options['check_threshold'] = $new_check_threshold;
|
531 |
}
|
532 |
+
|
533 |
+
$this->options['mark_broken_links'] = !empty($_POST['mark_broken_links']);
|
534 |
$new_broken_link_css = trim($_POST['broken_link_css']);
|
535 |
$this->options['broken_link_css'] = $new_broken_link_css;
|
536 |
|
537 |
+
$this->options['exclusion_list']=array_filter( preg_split( '/[\s\r\n]+/',
|
538 |
+
$_POST['exclusion_list'], -1, PREG_SPLIT_NO_EMPTY ) );
|
539 |
+
//TODO: Maybe update affected links when exclusion list changes (expensive).
|
540 |
+
|
541 |
+
|
542 |
+
$new_custom_fields = array_filter( preg_split( '/[\s\r\n]+/',
|
543 |
+
$_POST['blc_custom_fields'], -1, PREG_SPLIT_NO_EMPTY ) );
|
544 |
+
$diff1 = array_diff( $new_custom_fields, $this->options['custom_fields'] );
|
545 |
+
$diff2 = array_diff( $this->options['custom_fields'], $new_custom_fields );
|
546 |
+
$this->options['custom_fields'] = $new_custom_fields;
|
547 |
+
|
548 |
+
$this->save_options();
|
549 |
+
|
550 |
+
/*
|
551 |
+
If the list of custom fields was modified then we MUST resynchronize or
|
552 |
+
custom fields linked with existing posts may not be detected. This is somewhat
|
553 |
+
inefficient.
|
554 |
+
*/
|
555 |
+
if ( ( count($diff1) > 0 ) || ( count($diff2) > 0 ) ){
|
556 |
+
$this->resynch();
|
557 |
+
}
|
558 |
}
|
559 |
|
560 |
}
|
561 |
+
|
562 |
?>
|
563 |
<div class="wrap"><h2>Broken Link Checker Options</h2>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
564 |
|
565 |
+
<form name="link_checker_options" method="post" action="<?php echo $_SERVER['PHP_SELF']; ?>?page=link-checker-settings&updated=true">
|
566 |
+
|
567 |
+
<table class="form-table">
|
568 |
|
569 |
<tr valign="top">
|
570 |
+
<th scope="row">Status</th>
|
571 |
<td>
|
572 |
|
573 |
|
574 |
<div id='wsblc_full_status'>
|
575 |
+
<br/><br/><br/>
|
576 |
</div>
|
577 |
<script type='text/javascript'>
|
578 |
+
(function($){
|
579 |
+
|
580 |
+
function blcUpdateStatus(){
|
581 |
+
$.getJSON(
|
582 |
+
"<?php bloginfo( 'wpurl' ); ?>/wp-admin/admin-ajax.php",
|
583 |
+
{
|
584 |
+
'action' : 'blc_full_status'
|
585 |
+
},
|
586 |
+
function (data, textStatus){
|
587 |
+
if ( typeof(data['text']) != 'undefined'){
|
588 |
+
$('#wsblc_full_status').html(data.text);
|
589 |
+
} else {
|
590 |
+
$('#wsblc_full_status').html('[ Network error ]');
|
591 |
+
}
|
592 |
+
|
593 |
+
setTimeout(blcUpdateStatus, 10000); //...update every 10 seconds
|
594 |
+
}
|
595 |
+
);
|
596 |
+
}
|
597 |
+
blcUpdateStatus();//Call it the first time
|
598 |
+
|
599 |
+
})(jQuery);
|
600 |
</script>
|
601 |
<?php //JHS: Recheck all posts link: ?>
|
602 |
+
<p><input class="button" type="button" name="recheckbutton" value="Re-check all pages" onclick="location.replace('<?php echo $_SERVER['PHP_SELF']; ?>?page=link-checker-settings&recheck=true')" /></p>
|
603 |
</td>
|
604 |
</tr>
|
605 |
|
606 |
<tr valign="top">
|
607 |
+
<th scope="row">Check each link</th>
|
608 |
<td>
|
609 |
|
610 |
+
Every <input type="text" name="check_threshold" id="check_threshold"
|
611 |
+
value="<?php echo $this->options['check_threshold']; ?>" size='5' maxlength='3'/>
|
612 |
hours
|
613 |
<br/>
|
614 |
+
<span class="description">
|
615 |
+
Existing links will be checked this often. New links will usually be checked ASAP.
|
616 |
+
</span>
|
617 |
|
618 |
</td>
|
619 |
</tr>
|
620 |
|
621 |
<tr valign="top">
|
622 |
+
<th scope="row">Broken link CSS</th>
|
623 |
<td>
|
624 |
<input type="checkbox" name="mark_broken_links" id="mark_broken_links"
|
625 |
<?php if ($this->options['mark_broken_links']) echo ' checked="checked"'; ?>/>
|
626 |
<label for='mark_broken_links'>Apply <em>class="broken_link"</em> to broken links</label><br/>
|
627 |
+
<textarea name="broken_link_css" id="broken_link_css" cols='45' rows='4'/><?php
|
628 |
if( isset($this->options['broken_link_css']) )
|
629 |
echo $this->options['broken_link_css'];
|
630 |
?></textarea>
|
633 |
</tr>
|
634 |
|
635 |
<tr valign="top">
|
636 |
+
<th scope="row">Exclusion list</th>
|
637 |
+
<td>Don't check links where the URL contains any of these words (one per line) :<br/>
|
638 |
+
<textarea name="exclusion_list" id="exclusion_list" cols='45' rows='4' wrap='off'/><?php
|
639 |
if( isset($this->options['exclusion_list']) )
|
640 |
echo implode("\n", $this->options['exclusion_list']);
|
641 |
?></textarea>
|
642 |
|
643 |
</td>
|
644 |
</tr>
|
645 |
+
|
646 |
<tr valign="top">
|
647 |
+
<th scope="row">Custom fields</th>
|
648 |
+
<td>Check URLs entered in these custom fields (one per line) : <br/>
|
649 |
+
<textarea name="blc_custom_fields" id="blc_custom_fields" cols='45' rows='4' /><?php
|
650 |
+
if( isset($this->options['custom_fields']) )
|
651 |
+
echo implode("\n", $this->options['custom_fields']);
|
652 |
+
?></textarea>
|
|
|
|
|
|
|
653 |
|
654 |
</td>
|
655 |
</tr>
|
656 |
|
657 |
<tr valign="top">
|
658 |
+
<th scope="row">Max. execution time (advanced)</th>
|
659 |
<td>
|
660 |
|
661 |
+
<input type="text" name="max_execution_time" id="max_execution_time"
|
662 |
+
value="<?php echo $this->options['max_execution_time']; ?>" size='5' maxlength='3'/>
|
663 |
+
seconds
|
664 |
+
<br/><span class="description">
|
665 |
+
The plugin works by periodically creating a background worker instance that parses your posts looking for links,
|
666 |
+
checks the discovered URLs, and performs other time-consuming tasks. Here you can set for how long, at most,
|
667 |
+
the background instance may run each time before stopping.
|
668 |
+
</span>
|
669 |
|
670 |
</td>
|
671 |
</tr>
|
672 |
|
673 |
</table>
|
674 |
|
675 |
+
<p class="submit"><input type="submit" name="submit" class='button-primary' value="<?php _e('Save Changes') ?>" /></p>
|
676 |
</form>
|
677 |
</div>
|
678 |
<?php
|
679 |
}
|
680 |
|
681 |
+
function links_page(){
|
682 |
global $wpdb;
|
683 |
+
|
684 |
+
//Available filters by link type + the appropriate WHERE expressions
|
685 |
+
$filters = array(
|
686 |
+
'broken' => array(
|
687 |
+
'where_expr' => '( http_code < 200 OR http_code >= 400 OR timeout = 1 ) AND ( check_count > 0 )',
|
688 |
+
'name' => 'Broken',
|
689 |
+
'heading' => 'Broken Links',
|
690 |
+
'heading_zero' => 'No broken links found'
|
691 |
+
),
|
692 |
+
'redirects' => array(
|
693 |
+
'where_expr' => '( redirect_count > 0 )',
|
694 |
+
'name' => 'Redirects',
|
695 |
+
'heading' => 'Redirected Links',
|
696 |
+
'heading_zero' => 'No redirects found'
|
697 |
+
),
|
698 |
+
|
699 |
+
'all' => array(
|
700 |
+
'where_expr' => '1',
|
701 |
+
'name' => 'All',
|
702 |
+
'heading' => 'Detected Links',
|
703 |
+
'heading_zero' => 'No links found (yet)'
|
704 |
+
),
|
705 |
+
);
|
706 |
+
|
707 |
+
$link_type = isset($_GET['link_type'])?$_GET['link_type']:'broken';
|
708 |
+
if ( !isset($filters[$link_type]) ){
|
709 |
+
$link_type = 'broken';
|
710 |
+
}
|
711 |
+
|
712 |
+
//Get the desired page number (must be > 0)
|
713 |
+
$page = isset($_GET['paged'])?intval($_GET['paged']):'1';
|
714 |
+
if ($page < 1) $page = 1;
|
715 |
+
|
716 |
+
//Links per page [1 - 200]
|
717 |
+
$per_page = isset($_GET['per_page'])?intval($_GET['per_page']):'30';
|
718 |
+
if ($per_page < 1){
|
719 |
+
$per_page = 30;
|
720 |
+
} else if ($per_page > 200){
|
721 |
+
$per_page = 200;
|
722 |
+
}
|
723 |
+
|
724 |
+
//calculate the number of various links
|
725 |
+
foreach ($filters as $filter => $data){
|
726 |
+
$filters[$filter]['count'] = $wpdb->get_var(
|
727 |
+
"SELECT COUNT(*) FROM {$wpdb->prefix}blc_links WHERE ".$data['where_expr'] );
|
728 |
+
}
|
729 |
+
$current_filter = $filters[$link_type];
|
730 |
+
$max_pages = ceil($current_filter['count'] / $per_page);
|
731 |
+
|
732 |
+
|
733 |
+
//Select the required links + 1 instance per link.
|
734 |
+
//Note : The query might be somewhat inefficient, but I can't think of any better way to do this.
|
735 |
+
$q = "SELECT
|
736 |
+
links.*,
|
737 |
+
instances.instance_id, instances.source_id, instances.source_type,
|
738 |
+
instances.link_text, instances.instance_type,
|
739 |
+
COUNT(*) as instance_count,
|
740 |
+
posts.post_title,
|
741 |
+
posts.post_date
|
742 |
+
|
743 |
+
FROM
|
744 |
+
{$wpdb->prefix}blc_links AS links,
|
745 |
+
{$wpdb->prefix}blc_instances as instances LEFT JOIN {$wpdb->posts} as posts ON instances.source_id = posts.ID
|
746 |
+
|
747 |
+
WHERE
|
748 |
+
links.link_id = instances.link_id
|
749 |
+
AND ". $current_filter['where_expr'] ."
|
750 |
+
|
751 |
+
GROUP BY links.link_id
|
752 |
+
LIMIT ".( ($page-1) * $per_page ).", $per_page";
|
753 |
+
//echo "<pre>$q</pre>";
|
754 |
+
|
755 |
+
$links = $wpdb->get_results($q, ARRAY_A);
|
756 |
+
if ($links){
|
757 |
+
/*
|
758 |
+
echo '<pre>';
|
759 |
+
print_r($links);
|
760 |
+
echo '</pre>';
|
761 |
+
//*/
|
762 |
+
} else {
|
763 |
+
echo $wpdb->last_error;
|
764 |
+
}
|
765 |
?>
|
766 |
+
|
767 |
+
<script type='text/javascript'>
|
768 |
+
var blc_current_filter = '<?php echo $link_type; ?>';
|
769 |
+
</script>
|
770 |
+
|
771 |
+
<style type='text/css'>
|
772 |
+
.blc-link-editor {
|
773 |
+
font-size: 1em;
|
774 |
+
width: 95%;
|
775 |
+
}
|
776 |
+
|
777 |
+
.blc-excluded-link {
|
778 |
+
background-color: #E2E2E2;
|
779 |
+
}
|
780 |
+
|
781 |
+
.blc-small-image {
|
782 |
+
display : block;
|
783 |
+
float: left;
|
784 |
+
padding-top: 2px;
|
785 |
+
margin-right: 3px;
|
786 |
+
}
|
787 |
+
</style>
|
788 |
+
|
789 |
<div class="wrap">
|
790 |
<h2><?php
|
791 |
+
//Output a header matching the current filter
|
792 |
+
if ( $current_filter['count'] > 0 ){
|
793 |
+
echo "<span class='current-link-count'>{$current_filter[count]}</span> " . $current_filter['heading'];
|
794 |
+
} else {
|
795 |
+
echo "<span class='current-link-count'></span>" . $current_filter['heading_zero'];
|
796 |
+
}
|
797 |
?></h2>
|
798 |
+
|
799 |
+
<div class='tablenav'>
|
800 |
+
<ul class="subsubsub">
|
801 |
+
<?php
|
802 |
+
//Construct a submenu of filter types
|
803 |
+
$items = array();
|
804 |
+
foreach ($filters as $filter => $data){
|
805 |
+
$class = $number_class = '';
|
806 |
+
|
807 |
+
if ( $link_type == $filter ) $class = 'class="current"';
|
808 |
+
if ( $link_type == $filter ) $number_class = 'current-link-count';
|
809 |
+
|
810 |
+
$items[] = "<li><a href='tools.php?page=view-broken-links&link_type=$filter' $class>
|
811 |
+
{$data[name]}</a> <span class='count'>(<span class='$number_class'>{$data[count]}</span>)</span>";
|
812 |
+
}
|
813 |
+
echo implode(' |</li>', $items);
|
814 |
+
unset($items);
|
815 |
+
?>
|
816 |
+
</ul>
|
817 |
+
<?php
|
818 |
+
//Display pagination links
|
819 |
+
$page_links = paginate_links( array(
|
820 |
+
'base' => add_query_arg( 'paged', '%#%' ),
|
821 |
+
'format' => '',
|
822 |
+
'prev_text' => __('«'),
|
823 |
+
'next_text' => __('»'),
|
824 |
+
'total' => $max_pages,
|
825 |
+
'current' => $page
|
826 |
+
));
|
827 |
+
|
828 |
+
if ( $page_links ) {
|
829 |
+
echo '<div class="tablenav-pages">';
|
830 |
+
$page_links_text = sprintf( '<span class="displaying-num">' . __( 'Displaying %s–%s of <span class="current-link-count">%s</span>' ) . '</span>%s',
|
831 |
+
number_format_i18n( ( $page - 1 ) * $per_page + 1 ),
|
832 |
+
number_format_i18n( min( $page * $per_page, count($links) ) ),
|
833 |
+
number_format_i18n( $current_filter['count'] ),
|
834 |
+
$page_links
|
835 |
+
);
|
836 |
+
echo $page_links_text;
|
837 |
+
echo '</div>';
|
838 |
+
}
|
839 |
+
?>
|
840 |
+
|
841 |
+
</div>
|
842 |
+
|
843 |
+
|
844 |
+
|
845 |
<?php
|
|
|
|
|
|
|
846 |
if($links && (count($links)>0)){
|
847 |
?>
|
848 |
<table class="widefat">
|
849 |
<thead>
|
850 |
<tr>
|
851 |
|
852 |
+
<th scope="col">Source
|
|
|
|
|
853 |
</th>
|
854 |
<th scope="col">Link Text</th>
|
855 |
<th scope="col">URL</th>
|
856 |
|
857 |
+
<?php if ( 'broken' == $link_type ) { ?>
|
858 |
+
<th scope="col"> </th>
|
859 |
+
<?php } ?>
|
860 |
|
861 |
</tr>
|
862 |
</thead>
|
863 |
<tbody id="the-list">
|
864 |
<?php
|
865 |
+
$rowclass = ''; $rownum = 0;
|
|
|
|
|
|
|
|
|
866 |
foreach ($links as $link) {
|
867 |
+
$rownum++;
|
868 |
+
|
869 |
+
$rowclass = 'alternate' == $rowclass ? '' : 'alternate';
|
870 |
+
$excluded = $this->is_excluded( $link['url'] );
|
871 |
+
if ( $excluded ) $rowclass .= ' blc-excluded-link';
|
872 |
+
|
873 |
+
?>
|
874 |
+
<tr id='<?php echo "blc-row-$rownum"; ?>' class='blc-row <?php echo $rowclass; ?>'>
|
875 |
+
<td class='post-title column-title'>
|
876 |
+
<span class='blc-link-id' style='display:none;'><?php echo $link['link_id']; ?></span>
|
877 |
+
<?php
|
878 |
+
if ( ('post' == $link['source_type']) || ('custom_field' == $link['source_type']) ){
|
879 |
+
|
880 |
+
echo "<a class='row-title' href='post.php?action=edit&post=$link[source_id]' title='Edit this post'>{$link[post_title]}</a>";
|
881 |
+
|
882 |
+
//Output inline action links (copied from edit-post-rows.php)
|
883 |
+
$actions = array();
|
884 |
+
if ( current_user_can('edit_post', $link['source_id']) ) {
|
885 |
+
$actions['edit'] = '<span class="edit"><a href="' . get_edit_post_link($link['source_id'], true) . '" title="' . attribute_escape(__('Edit this post')) . '">' . __('Edit') . '</a>';
|
886 |
+
$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>";
|
887 |
+
}
|
888 |
+
$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>';
|
889 |
+
echo '<div class="row-actions">';
|
890 |
+
echo implode(' | </span>', $actions);
|
891 |
+
echo '</div>';
|
892 |
+
|
893 |
+
} elseif ( 'blogroll' == $link['source_type'] ) {
|
894 |
+
|
895 |
+
echo "<a class='row-title' href='link.php?action=edit&link_id=$link[source_id]' title='Edit this bookmark'>{$link[link_text]}</a>";
|
896 |
+
|
897 |
+
//Output inline action links
|
898 |
+
$actions = array();
|
899 |
+
if ( current_user_can('manage_links') ) {
|
900 |
+
$actions['edit'] = '<span class="edit"><a href="link.php?action=edit&link_id=' . $link['source_id'] . '" title="' . attribute_escape(__('Edit this bookmark')) . '">' . __('Edit') . '</a>';
|
901 |
+
$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>";
|
902 |
+
}
|
903 |
+
|
904 |
+
echo '<div class="row-actions">';
|
905 |
+
echo implode(' | </span>', $actions);
|
906 |
+
echo '</div>';
|
907 |
+
|
908 |
+
} elseif ( empty($link['source_type']) ){
|
909 |
+
|
910 |
+
echo "[An orphaned link! This is a bug.]";
|
911 |
+
|
912 |
+
}
|
913 |
+
?>
|
914 |
+
</td>
|
915 |
+
<td class='blc-link-text'><?php
|
916 |
+
if ( 'post' == $link['source_type'] ){
|
917 |
+
|
918 |
+
if ( 'link' == $link['instance_type'] ) {
|
919 |
+
print strip_tags($link['link_text']);
|
920 |
+
} elseif ( 'image' == $link['instance_type'] ){
|
921 |
+
echo "<img src='" . WP_PLUGIN_URL . "/broken-link-checker/images/image.png' class='blc-small-image' alt='Image' title='Image'> Image";
|
922 |
+
} else {
|
923 |
+
echo '[ ??? ]';
|
924 |
+
}
|
925 |
+
|
926 |
+
} elseif ( 'custom_field' == $link['source_type'] ){
|
927 |
+
|
928 |
+
echo "<img src='" . WP_PLUGIN_URL . "/broken-link-checker/images/script_code.png' class='blc-small-image' title='Custom field' alt='Custom field'> ";
|
929 |
+
echo "<code>".$link['link_text']."</code>";
|
930 |
+
|
931 |
+
} elseif ( 'blogroll' == $link['source_type'] ){
|
932 |
+
//echo $link['link_text'];
|
933 |
+
echo "<img src='" . WP_PLUGIN_URL . "/broken-link-checker/images/link.png' class='blc-small-image' title='Bookmark' alt='Bookmark'> Bookmark";
|
934 |
+
}
|
935 |
+
?>
|
936 |
+
</td>
|
937 |
+
<td class='column-url'>
|
938 |
+
<a href='<?php print $link['url']; ?>' target='_blank' class='blc-link-url'>
|
939 |
+
<?php print $this->mytruncate($link['url']); ?></a>
|
940 |
+
<input type='text' id='link-editor-<?php print $rownum; ?>'
|
941 |
+
value='<?php print attribute_escape($link['url']); ?>'
|
942 |
+
class='blc-link-editor' style='display:none' />
|
943 |
+
<?php
|
944 |
+
//Output inline action links for the link/URL
|
945 |
+
$actions = array();
|
946 |
+
|
947 |
+
$actions['details'] = "<span class='view'><a class='blc-details-button' href='javascript:void(0)' title='Show more info about this link'>Details</a>";
|
948 |
+
|
949 |
+
$actions['delete'] = "<span class='delete'><a class='submitdelete blc-unlink-button' title='Remove this link from all posts' ".
|
950 |
+
"id='unlink-button-$rownum' href='javascript:void(0);'>Unlink</a>";
|
951 |
+
|
952 |
+
if ( $excluded ){
|
953 |
+
$actions['exclude'] = "<span class='delete'>Excluded";
|
954 |
+
} else {
|
955 |
+
$actions['exclude'] = "<span class='delete'><a class='submitdelete blc-exclude-button' title='Add this URL to the exclusion list' ".
|
956 |
+
"id='exclude-button-$rownum' href='javascript:void(0);'>Exclude</a>";
|
957 |
+
}
|
958 |
+
|
959 |
+
$actions['edit'] = "<span class='edit'><a href='javascript:void(0)' class='blc-edit-button' title='Edit link URL'>Edit URL</a>";
|
960 |
+
|
961 |
+
echo '<div class="row-actions">';
|
962 |
+
echo implode(' | </span>', $actions);
|
963 |
+
|
964 |
+
echo "<span style='display:none' class='blc-cancel-button-container'> ",
|
965 |
+
"| <a href='javascript:void(0)' class='blc-cancel-button' title='Cancel URL editing'>Cancel</a></span>";
|
966 |
+
|
967 |
+
echo '</div>';
|
968 |
?>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
969 |
</td>
|
970 |
+
<?php if ( 'broken' == $link_type ) { ?>
|
971 |
+
<td><a href='javascript:void(0);'
|
972 |
+
id='discard_button-<?php print $rownum; ?>'
|
973 |
+
class='blc-discard-button'
|
974 |
+
title='Remove this message and mark the link as valid'>Discard</a>
|
975 |
+
</td>
|
976 |
+
<?php } ?>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
977 |
</tr>
|
978 |
<!-- Link details -->
|
979 |
+
<tr id='<?php print "link-details-$rownum"; ?>' style='display:none;' class='blc-link-details'>
|
980 |
+
<td colspan='4'><?php $this->link_details_row($link); ?></td>
|
981 |
+
</tr><?php
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
982 |
}
|
983 |
?></tbody></table><?php
|
984 |
+
|
985 |
+
//Also display pagination links at the bottom
|
986 |
+
if ( $page_links ) {
|
987 |
+
echo '<div class="tablenav"><div class="tablenav-pages">';
|
988 |
+
$page_links_text = sprintf( '<span class="displaying-num">' . __( 'Displaying %s–%s of <span class="current-link-count">%s</span>' ) . '</span>%s',
|
989 |
+
number_format_i18n( ( $page - 1 ) * $per_page + 1 ),
|
990 |
+
number_format_i18n( min( $page * $per_page, count($links) ) ),
|
991 |
+
number_format_i18n( $current_filter['count'] ),
|
992 |
+
$page_links
|
993 |
+
);
|
994 |
+
echo $page_links_text;
|
995 |
+
echo '</div></div>';
|
996 |
+
}
|
997 |
};
|
998 |
?>
|
999 |
+
<?php $this->links_page_js(); ?>
|
1000 |
+
</div>
|
1001 |
+
<?php
|
1002 |
+
}
|
1003 |
+
|
1004 |
+
function links_page_js(){
|
1005 |
+
?>
|
1006 |
+
<script type='text/javascript'>
|
1007 |
+
|
1008 |
+
function alterLinkCounter(factor){
|
1009 |
+
cnt = parseInt(jQuery('.current-link-count').eq(0).html());
|
1010 |
+
cnt = cnt + factor;
|
1011 |
+
jQuery('.current-link-count').html(cnt);
|
1012 |
}
|
|
|
1013 |
|
1014 |
+
jQuery(function($){
|
1015 |
+
|
1016 |
+
//The discard button - manually mark the link as valid. The link will be checked again later.
|
1017 |
+
$(".blc-discard-button").click(function () {
|
1018 |
+
var me = this;
|
1019 |
+
$(me).html('Wait...');
|
1020 |
+
|
1021 |
+
var link_id = $(me).parents('.blc-row').find('.blc-link-id').html();
|
1022 |
+
|
1023 |
+
$.post(
|
1024 |
+
"<?php bloginfo( 'wpurl' ); ?>/wp-admin/admin-ajax.php",
|
1025 |
+
{
|
1026 |
+
'action' : 'blc_discard',
|
1027 |
+
'link_id' : link_id
|
1028 |
+
},
|
1029 |
+
function (data, textStatus){
|
1030 |
+
if (data == 'OK'){
|
1031 |
+
var master = $(me).parents('.blc-row');
|
1032 |
+
var details = master.next('.blc-link-details');
|
1033 |
+
|
1034 |
+
details.hide();
|
1035 |
+
//Flash the main row green to indicate success, then hide it.
|
1036 |
+
var oldColor = master.css('background-color');
|
1037 |
+
master.animate({ backgroundColor: "#E0FFB3" }, 200).animate({ backgroundColor: oldColor }, 300, function(){
|
1038 |
+
master.hide();
|
1039 |
+
});
|
1040 |
+
|
1041 |
+
alterLinkCounter(-1);
|
1042 |
+
} else {
|
1043 |
+
$(me).html('Discard');
|
1044 |
+
alert(data);
|
1045 |
+
}
|
1046 |
+
}
|
1047 |
+
);
|
1048 |
+
});
|
1049 |
+
|
1050 |
+
//The details button - display/hide detailed info about a link
|
1051 |
+
$(".blc-details-button, .blc-link-text").click(function () {
|
1052 |
+
$(this).parents('.blc-row').next('.blc-link-details').toggle();
|
1053 |
+
});
|
1054 |
+
|
1055 |
+
//The edit button - edit/save the link's URL
|
1056 |
+
$(".blc-edit-button").click(function () {
|
1057 |
+
var edit_button = $(this);
|
1058 |
+
var master = $(edit_button).parents('.blc-row');
|
1059 |
+
var editor = $(master).find('.blc-link-editor');
|
1060 |
+
var url_el = $(master).find('.blc-link-url');
|
1061 |
+
var cancel_button_container = $(master).find('.blc-cancel-button-container');
|
1062 |
+
|
1063 |
+
//Find the current/original URL
|
1064 |
+
var orig_url = url_el.attr('href');
|
1065 |
+
//Find the link ID
|
1066 |
+
var link_id = $(master).find('.blc-link-id').html();
|
1067 |
+
|
1068 |
+
if ( !$(editor).is(':visible') ){
|
1069 |
+
//Begin editing
|
1070 |
+
url_el.hide();
|
1071 |
+
editor.show();
|
1072 |
+
cancel_button_container.show();
|
1073 |
+
editor.focus();
|
1074 |
+
editor.select();
|
1075 |
+
edit_button.html('Save URL');
|
1076 |
+
} else {
|
1077 |
+
editor.hide();
|
1078 |
+
cancel_button_container.hide();
|
1079 |
+
url_el.show();
|
1080 |
+
|
1081 |
+
new_url = editor.val();
|
1082 |
+
|
1083 |
+
if (new_url != orig_url){
|
1084 |
+
//Save the changed link
|
1085 |
+
url_el.html('Saving changes...');
|
1086 |
+
|
1087 |
+
$.getJSON(
|
1088 |
+
"<?php bloginfo( 'wpurl' ); ?>/wp-admin/admin-ajax.php",
|
1089 |
+
{
|
1090 |
+
'action' : 'blc_edit',
|
1091 |
+
'link_id' : link_id,
|
1092 |
+
'new_url' : new_url
|
1093 |
+
},
|
1094 |
+
function (data, textStatus){
|
1095 |
+
var display_url = '';
|
1096 |
+
|
1097 |
+
if ( typeof(data['error']) != 'undefined'){
|
1098 |
+
//data.error is an error message
|
1099 |
+
alert(data.error);
|
1100 |
+
display_url = orig_url;
|
1101 |
+
} else {
|
1102 |
+
//data contains info about the performed edit
|
1103 |
+
if ( data.cnt_okay > 0 ){
|
1104 |
+
display_url = new_url;
|
1105 |
+
|
1106 |
+
url_el.attr('href', new_url);
|
1107 |
+
|
1108 |
+
if ( data.cnt_error > 0 ){
|
1109 |
+
var msg = "The link was successfully modifed.";
|
1110 |
+
msg = msg + "\nHowever, "+data.cnt_error+" instances couldn't be edited and still point to the old URL."
|
1111 |
+
alert(msg);
|
1112 |
+
} else {
|
1113 |
+
//Flash the row green to indicate success
|
1114 |
+
var oldColor = master.css('background-color');
|
1115 |
+
master.animate({ backgroundColor: "#E0FFB3" }, 200).animate({ backgroundColor: oldColor }, 300);
|
1116 |
+
|
1117 |
+
//Save the new ID
|
1118 |
+
master.find('.blc-link-id').html(data.new_link_id);
|
1119 |
+
//Load up the new link info (so sue me)
|
1120 |
+
master.next('.blc-link-details').find('td').html('<center>Loading...</center>').load(
|
1121 |
+
"<?php bloginfo( 'wpurl' ); ?>/wp-admin/admin-ajax.php",
|
1122 |
+
{
|
1123 |
+
'action' : 'blc_link_details',
|
1124 |
+
'link_id' : data.new_link_id
|
1125 |
+
}
|
1126 |
+
);
|
1127 |
+
}
|
1128 |
+
} else {
|
1129 |
+
alert("Something went wrong. The plugin failed to edit "+
|
1130 |
+
data.cnt_error + ' instance(s) of this link.');
|
1131 |
+
|
1132 |
+
display_url = orig_url;
|
1133 |
+
}
|
1134 |
+
};
|
1135 |
+
|
1136 |
+
//Shorten the displayed URL if it's > 50 characters
|
1137 |
+
if ( display_url.length > 50 ){
|
1138 |
+
display_url = display_url.substr(0, 47) + '...';
|
1139 |
+
}
|
1140 |
+
url_el.html(display_url);
|
1141 |
+
}
|
1142 |
+
);
|
1143 |
+
|
1144 |
+
} else {
|
1145 |
+
//It's the same URL, so do nothing.
|
1146 |
+
}
|
1147 |
+
edit_button.html('Edit URL');
|
1148 |
}
|
1149 |
+
});
|
1150 |
+
|
1151 |
+
$(".blc-cancel-button").click(function () {
|
1152 |
+
var master = $(this).parents('.blc-row');
|
1153 |
+
var url_el = $(master).find('.blc-link-url');
|
1154 |
+
|
1155 |
+
//Hide the cancel button
|
1156 |
+
$(this).parent().hide();
|
1157 |
+
//Show the un-editable URL again
|
1158 |
+
url_el.show();
|
1159 |
+
//reset and hide the editor
|
1160 |
+
master.find('.blc-link-editor').hide().val(url_el.attr('href'));
|
1161 |
+
//Set the edit button to say "Edit URL"
|
1162 |
+
master.find('.blc-edit-button').html('Edit URL');
|
1163 |
+
});
|
1164 |
+
|
1165 |
+
//The unlink button - remove the link/image from all posts, custom fields, etc.
|
1166 |
+
$(".blc-unlink-button").click(function () {
|
1167 |
+
var me = this;
|
1168 |
+
var master = $(me).parents('.blc-row');
|
1169 |
+
$(me).html('Wait...');
|
1170 |
+
|
1171 |
+
var link_id = $(me).parents('.blc-row').find('.blc-link-id').html();
|
1172 |
+
|
1173 |
+
$.post(
|
1174 |
+
"<?php bloginfo( 'wpurl' ); ?>/wp-admin/admin-ajax.php",
|
1175 |
+
{
|
1176 |
+
'action' : 'blc_unlink',
|
1177 |
+
'link_id' : link_id
|
1178 |
+
},
|
1179 |
+
function (data, textStatus){
|
1180 |
+
eval('data = ' + data);
|
1181 |
+
|
1182 |
+
if ( typeof(data['ok']) != 'undefined'){
|
1183 |
+
//Hide the details
|
1184 |
+
master.next('.blc-link-details').hide();
|
1185 |
+
//Flash the main row green to indicate success, then hide it.
|
1186 |
+
var oldColor = master.css('background-color');
|
1187 |
+
master.animate({ backgroundColor: "#E0FFB3" }, 200).animate({ backgroundColor: oldColor }, 300, function(){
|
1188 |
+
master.hide();
|
1189 |
+
});
|
1190 |
+
|
1191 |
+
alterLinkCounter(-1);
|
1192 |
+
} else {
|
1193 |
+
$(me).html('Unlink');
|
1194 |
+
//Show the error message
|
1195 |
+
alert(data.error);
|
1196 |
+
}
|
1197 |
+
}
|
1198 |
+
);
|
1199 |
+
});
|
1200 |
+
|
1201 |
+
//The exclude button - Add this link to the exclusion list
|
1202 |
+
$(".blc-exclude-button").click(function () {
|
1203 |
+
var me = this;
|
1204 |
+
var master = $(me).parents('.blc-row');
|
1205 |
+
var details = master.next('.blc-link-details');
|
1206 |
+
$(me).html('Wait...');
|
1207 |
+
|
1208 |
+
var link_id = $(me).parents('.blc-row').find('.blc-link-id').html();
|
1209 |
+
|
1210 |
+
$.post(
|
1211 |
+
"<?php bloginfo( 'wpurl' ); ?>/wp-admin/admin-ajax.php",
|
1212 |
+
{
|
1213 |
+
'action' : 'blc_exclude_link',
|
1214 |
+
'link_id' : link_id
|
1215 |
+
},
|
1216 |
+
function (data, textStatus){
|
1217 |
+
eval('data = ' + data);
|
1218 |
+
|
1219 |
+
if ( typeof(data['ok']) != 'undefined'){
|
1220 |
+
|
1221 |
+
if ( 'broken' == blc_current_filter ){
|
1222 |
+
//Flash the row green to indicate success, then hide it.
|
1223 |
+
$(me).replaceWith('Excluded');
|
1224 |
+
master.animate({ backgroundColor: "#E0FFB3" }, 200).animate({ backgroundColor: '#E2E2E2' }, 200, function(){
|
1225 |
+
details.hide();
|
1226 |
+
master.hide();
|
1227 |
+
alterLinkCounter(-1);
|
1228 |
+
});
|
1229 |
+
master.addClass('blc-excluded-link');
|
1230 |
+
} else {
|
1231 |
+
//Flash the row green to indicate success and fade to the "excluded link" color
|
1232 |
+
master.animate({ backgroundColor: "#E0FFB3" }, 200).animate({ backgroundColor: '#E2E2E2' }, 300);
|
1233 |
+
master.addClass('blc-excluded-link');
|
1234 |
+
$(me).replaceWith('Excluded');
|
1235 |
+
}
|
1236 |
+
} else {
|
1237 |
+
$(me).html('Exclude');
|
1238 |
+
alert(data.error);
|
1239 |
+
}
|
1240 |
+
}
|
1241 |
+
);
|
1242 |
+
});
|
1243 |
+
|
1244 |
+
});
|
1245 |
|
|
|
1246 |
function removeLinkFromPost(link_id){
|
1247 |
+
if (!confirm('Do you really want to remove this link from all posts, custom fields and the blogroll?')) return;
|
1248 |
+
|
1249 |
$('unlink_button-'+link_id).innerHTML = 'Wait...';
|
1250 |
|
1251 |
new Ajax.Request(
|
1269 |
}
|
1270 |
);
|
1271 |
}
|
1272 |
+
</script>
|
1273 |
+
<?php
|
1274 |
+
}
|
1275 |
+
|
1276 |
+
function link_details_row($link){
|
1277 |
+
?>
|
1278 |
+
<span id='post_date_full' style='display:none;'><?php
|
1279 |
+
print $link['post_date'];
|
1280 |
+
?></span>
|
1281 |
+
<span id='check_date_full' style='display:none;'><?php
|
1282 |
+
print $link['last_check'];
|
1283 |
+
?></span>
|
1284 |
+
<ol style='list-style-type: none; width: 50%; float: right;'>
|
1285 |
+
<li><strong>Log :</strong>
|
1286 |
+
<span class='blc_log'><?php
|
1287 |
+
print nl2br($link['log']);
|
1288 |
+
?></span></li>
|
1289 |
+
</ol>
|
1290 |
+
|
1291 |
+
<ol style='list-style-type: none; padding-left: 2px;'>
|
1292 |
+
<?php if ( !empty($link['post_date']) ) { ?>
|
1293 |
+
<li><strong>Post published on :</strong>
|
1294 |
+
<span class='post_date'><?php
|
1295 |
+
print strftime("%B %d, %Y",strtotime($link['post_date']));
|
1296 |
+
?></span></li>
|
1297 |
+
<?php } ?>
|
1298 |
+
<li><strong>Link last checked :</strong>
|
1299 |
+
<span class='check_date'><?php
|
1300 |
+
$last_check = strtotime($link['last_check']);
|
1301 |
+
if ( $last_check < strtotime('-10 years') ){
|
1302 |
+
echo 'Never';
|
1303 |
+
} else {
|
1304 |
+
echo strftime( "%B %d, %Y", $last_check );
|
1305 |
+
}
|
1306 |
+
?></span></li>
|
1307 |
+
|
1308 |
+
<li><strong>HTTP code :</strong>
|
1309 |
+
<span class='http_code'><?php
|
1310 |
+
print $link['http_code'];
|
1311 |
+
?></span></li>
|
1312 |
+
|
1313 |
+
<li><strong>Response time :</strong>
|
1314 |
+
<span class='request_duration'><?php
|
1315 |
+
printf('%2.3f seconds', $link['request_duration']);
|
1316 |
+
?></span></li>
|
1317 |
+
|
1318 |
+
<li><strong>Final URL :</strong>
|
1319 |
+
<span class='final_url'><?php
|
1320 |
+
print $link['final_url'];
|
1321 |
+
?></span></li>
|
1322 |
+
|
1323 |
+
<li><strong>Redirect count :</strong>
|
1324 |
+
<span class='redirect_count'><?php
|
1325 |
+
print $link['redirect_count'];
|
1326 |
+
?></span></li>
|
1327 |
+
|
1328 |
+
<li><strong>Instance count :</strong>
|
1329 |
+
<span class='instance_count'><?php
|
1330 |
+
print $link['instance_count'];
|
1331 |
+
?></span></li>
|
1332 |
+
|
1333 |
+
<?php if ( intval( $link['check_count'] ) > 0 ){ ?>
|
1334 |
+
<li><br/>This link has failed
|
1335 |
+
<span class='check_count'><?php
|
1336 |
+
echo $link['check_count'];
|
1337 |
+
if ( intval($link['check_count'])==1 ){
|
1338 |
+
echo ' time';
|
1339 |
+
} else {
|
1340 |
+
echo ' times';
|
1341 |
+
}
|
1342 |
+
?></span>.</li>
|
1343 |
+
<?php } ?>
|
1344 |
+
</ol>
|
1345 |
+
<?php
|
1346 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1347 |
|
1348 |
+
/**
|
1349 |
+
* ws_broken_link_checker::cleanup_links()
|
1350 |
+
* Remove orphaned links that have no corresponding instances
|
1351 |
+
*
|
1352 |
+
* @param int $link_id (optional) Only check this link
|
1353 |
+
* @return bool
|
1354 |
+
*/
|
1355 |
+
function cleanup_links( $link_id = null ){
|
1356 |
+
global $wpdb;
|
1357 |
+
|
1358 |
+
$q = "DELETE FROM {$wpdb->prefix}blc_links
|
1359 |
+
USING {$wpdb->prefix}blc_links LEFT JOIN {$wpdb->prefix}blc_instances
|
1360 |
+
ON {$wpdb->prefix}blc_instances.link_id = {$wpdb->prefix}blc_links.link_id
|
1361 |
+
WHERE
|
1362 |
+
{$wpdb->prefix}blc_instances.link_id IS NULL";
|
1363 |
+
|
1364 |
+
if ( $link_id !==null ) {
|
1365 |
+
$q .= " AND {$wpdb->prefix}blc_links.link_id = " . intval( $link_id );
|
1366 |
+
}
|
1367 |
+
|
1368 |
+
return $wpdb->query( $q );
|
1369 |
+
}
|
1370 |
+
|
1371 |
+
/**
|
1372 |
+
* ws_broken_link_checker::cleanup_instances()
|
1373 |
+
* Remove instances that reference invalid posts or bookmarks
|
1374 |
+
*
|
1375 |
+
* @return bool
|
1376 |
+
*/
|
1377 |
+
function cleanup_instances(){
|
1378 |
+
global $wpdb;
|
1379 |
+
|
1380 |
+
//Delete all instances that reference non-existent posts
|
1381 |
+
$q = "DELETE FROM {$wpdb->prefix}blc_instances
|
1382 |
+
USING {$wpdb->prefix}blc_instances LEFT JOIN {$wpdb->posts} ON {$wpdb->prefix}blc_instances.source_id = {$wpdb->posts}.ID
|
1383 |
+
WHERE
|
1384 |
+
{$wpdb->posts}.ID IS NULL
|
1385 |
+
AND ( ( {$wpdb->prefix}blc_instances.source_type = 'post' ) OR ( {$wpdb->prefix}blc_instances.source_type = 'custom_field' ) )";
|
1386 |
+
$rez = $wpdb->query($q);
|
1387 |
+
|
1388 |
+
//Delete all instances that reference non-existant bookmarks
|
1389 |
+
$q = "DELETE FROM {$wpdb->prefix}blc_instances
|
1390 |
+
USING {$wpdb->prefix}blc_instances LEFT JOIN {$wpdb->links} ON {$wpdb->prefix}blc_instances.source_id = {$wpdb->links}.link_id
|
1391 |
+
WHERE
|
1392 |
+
{$wpdb->links}.link_id IS NULL
|
1393 |
+
AND {$wpdb->prefix}blc_instances.source_type = 'blogroll' ";
|
1394 |
+
$rez2 = $wpdb->query($q);
|
1395 |
+
|
1396 |
+
return $rez and $rez2;
|
1397 |
+
}
|
1398 |
+
|
1399 |
+
function load_options(){
|
1400 |
+
$this->options = get_option($this->options_name);
|
1401 |
+
if(!is_array($this->options)){
|
1402 |
+
$this->options = $this->defaults;
|
1403 |
} else {
|
1404 |
+
$this->options = array_merge($this->defaults, $this->options);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1405 |
}
|
1406 |
+
}
|
1407 |
+
|
1408 |
+
function save_options(){
|
1409 |
+
update_option($this->options_name,$this->options);
|
1410 |
+
}
|
1411 |
+
|
1412 |
+
/**
|
1413 |
+
* ws_broken_link_checker::parse_post()
|
1414 |
+
* Parse a post for links and save them to the DB.
|
1415 |
+
*
|
1416 |
+
* @param string $content Post content
|
1417 |
+
* @param int $post_id Post ID
|
1418 |
+
* @return void
|
1419 |
+
*/
|
1420 |
+
function parse_post($content, $post_id){
|
1421 |
+
//remove all <code></code> blocks first
|
1422 |
+
$content = preg_replace('/<code>.+?<\/code>/i', ' ', $content);
|
1423 |
+
|
1424 |
+
//Find links
|
1425 |
+
if(preg_match_all(blcUtility::link_pattern(), $content, $matches, PREG_SET_ORDER)){
|
1426 |
+
foreach($matches as $link){
|
1427 |
+
$url = $link[3];
|
1428 |
+
$text = strip_tags( $link[5] );
|
1429 |
+
//FB::log($url, "Found link");
|
1430 |
+
|
1431 |
+
$url = blcUtility::normalize_url($url);
|
1432 |
+
//Skip invalid links
|
1433 |
+
if ( !$url || (strlen($url)<6) ) continue;
|
1434 |
+
|
1435 |
+
//Create or load the link
|
1436 |
+
$link_obj = new blcLink($url);
|
1437 |
+
//Add & save a new instance
|
1438 |
+
$link_obj->add_instance($post_id, 'post', $text, 'link');
|
1439 |
+
}
|
1440 |
+
};
|
1441 |
+
|
1442 |
+
//Find images (<img src=...>)
|
1443 |
+
if(preg_match_all(blcUtility::img_pattern(), $content, $matches, PREG_SET_ORDER)){
|
1444 |
+
foreach($matches as $img){
|
1445 |
+
$url = $img[3];
|
1446 |
+
//FB::log($url, "Found image");
|
1447 |
+
|
1448 |
+
$url = blcUtility::normalize_url($url);
|
1449 |
+
if ( !$url || (strlen($url)<6) ) continue; //skip invalid URLs
|
1450 |
+
|
1451 |
+
//Create or load the link
|
1452 |
+
$link = new blcLink($url);
|
1453 |
+
//Add & save a new image instance
|
1454 |
+
$link->add_instance($post_id, 'post', '', 'image');
|
1455 |
+
}
|
1456 |
+
};
|
1457 |
+
}
|
1458 |
+
|
1459 |
+
/**
|
1460 |
+
* ws_broken_link_checker::parse_post_meta()
|
1461 |
+
* Parse a post's custom fields for links and save them in the DB.
|
1462 |
+
*
|
1463 |
+
* @param id $post_id
|
1464 |
+
* @return void
|
1465 |
+
*/
|
1466 |
+
function parse_post_meta($post_id){
|
1467 |
+
//Get all custom fields of this post
|
1468 |
+
$custom_fields = get_post_custom( $post_id );
|
1469 |
+
//FB::log($custom_fields, "Custom fields loaded");
|
1470 |
+
|
1471 |
+
//Parse the enabled fields
|
1472 |
+
foreach( $this->options['custom_fields'] as $field ){
|
1473 |
+
if ( !isset($custom_fields[$field]) ) continue;
|
1474 |
+
|
1475 |
+
//FB::log($field, "Parsing field");
|
1476 |
+
|
1477 |
+
$values = $custom_fields[$field];
|
1478 |
+
if ( !is_array( $values ) ) $values = array($values);
|
1479 |
+
|
1480 |
+
foreach( $values as $value ){
|
1481 |
+
|
1482 |
+
//Attempt to parse the $value as URL
|
1483 |
+
$url = blcUtility::normalize_url($value);
|
1484 |
+
if ( empty($url) ){
|
1485 |
+
//FB::warn($value, "Invalid URL in custom field ".$field);
|
1486 |
+
continue;
|
1487 |
+
}
|
1488 |
+
|
1489 |
+
//FB::log($url, "Found URL");
|
1490 |
+
$link = new blcLink( $url );
|
1491 |
+
//FB::log($link, 'Created/loaded link');
|
1492 |
+
$inst = $link->add_instance( $post_id, 'custom_field', $field, 'link' );
|
1493 |
+
//FB::log($inst, 'Created instance');
|
1494 |
+
}
|
1495 |
+
}
|
1496 |
+
|
1497 |
+
}
|
1498 |
+
|
1499 |
+
function parse_blogroll_link( $the_link ){
|
1500 |
+
//FB::log($the_link, "Parsing blogroll link");
|
1501 |
+
|
1502 |
+
//Attempt to parse the URL
|
1503 |
+
$url = blcUtility::normalize_url( $the_link['link_url'] );
|
1504 |
+
if ( empty($url) ){
|
1505 |
+
//FB::warn( $the_link['link_url'], "Invalid URL in for a blogroll link".$the_link['link_name'] );
|
1506 |
+
return false;
|
1507 |
+
}
|
1508 |
+
|
1509 |
+
//FB::log($url, "Found URL");
|
1510 |
+
$link = new blcLink( $url );
|
1511 |
+
return $link->add_instance( $the_link['link_id'], 'blogroll', $the_link['link_name'], 'link' );
|
1512 |
+
}
|
1513 |
+
|
1514 |
+
function start_timer(){
|
1515 |
+
$this->execution_start_time = microtime_float();
|
1516 |
+
}
|
1517 |
+
|
1518 |
+
function execution_time(){
|
1519 |
+
return microtime_float() - $this->execution_start_time;
|
1520 |
+
}
|
1521 |
+
|
1522 |
+
/**
|
1523 |
+
* ws_broken_link_checker::work()
|
1524 |
+
* The main worker function that does all kinds of things.
|
1525 |
+
*
|
1526 |
+
* @return void
|
1527 |
+
*/
|
1528 |
+
function work(){
|
1529 |
+
global $wpdb;
|
1530 |
+
|
1531 |
+
if ( !$this->acquire_lock() ){
|
1532 |
+
//FB::warn("Another instance of BLC is already working. Stop.");
|
1533 |
+
return false;
|
1534 |
+
}
|
1535 |
+
|
1536 |
+
$this->start_timer();
|
1537 |
+
|
1538 |
+
$max_execution_time = $this->options['max_execution_time'];
|
1539 |
+
|
1540 |
+
/*****************************************
|
1541 |
+
Preparation
|
1542 |
+
******************************************/
|
1543 |
+
// Check for safe mode
|
1544 |
+
if( ini_get('safe_mode') ){
|
1545 |
+
// Do it the safe mode way
|
1546 |
+
$t=ini_get('max_execution_time');
|
1547 |
+
if ($t && ($t < $max_execution_time))
|
1548 |
+
$max_execution_time = $t-1;
|
1549 |
+
} else {
|
1550 |
+
// Do it the regular way
|
1551 |
+
@set_time_limit( $max_execution_time * 2 ); //x2 should be plenty, running any longer would mean a glitch.
|
1552 |
+
}
|
1553 |
+
@ignore_user_abort(true);
|
1554 |
+
|
1555 |
+
$check_threshold = date('Y-m-d H:i:s', strtotime('-'.$this->options['check_threshold'].' hours'));
|
1556 |
+
$recheck_threshold = date('Y-m-d H:i:s', strtotime('-20 minutes'));
|
1557 |
+
|
1558 |
+
$orphans_possible = false;
|
1559 |
+
|
1560 |
+
$still_need_resynch = false;
|
1561 |
+
|
1562 |
+
/*****************************************
|
1563 |
+
Parse posts and bookmarks
|
1564 |
+
******************************************/
|
1565 |
+
|
1566 |
+
if ( $this->options['need_resynch'] ) {
|
1567 |
+
|
1568 |
+
//FB::log("Looking for posts and bookmarks that need parsing...");
|
1569 |
+
|
1570 |
+
$tsynch = $wpdb->prefix.'blc_synch';
|
1571 |
+
$tposts = $wpdb->posts;
|
1572 |
+
$tlinks = $wpdb->links;
|
1573 |
+
|
1574 |
+
$synch_q = "SELECT $tsynch.source_id, $tsynch.source_type, $tposts.post_content, $tlinks.link_url, $tlinks.link_id, $tlinks.link_name
|
1575 |
+
|
1576 |
+
FROM
|
1577 |
+
$tsynch LEFT JOIN $tposts
|
1578 |
+
ON ($tposts.id = $tsynch.source_id AND $tsynch.source_type='post')
|
1579 |
+
LEFT JOIN wp_links
|
1580 |
+
ON ($tlinks.link_id = $tsynch.source_id AND $tsynch.source_type='blogroll')
|
1581 |
+
|
1582 |
+
WHERE
|
1583 |
+
$tsynch.synched = 0
|
1584 |
+
|
1585 |
+
LIMIT 50";
|
1586 |
+
|
1587 |
+
while ( $rows = $wpdb->get_results($synch_q, ARRAY_A) ) {
|
1588 |
+
|
1589 |
+
//FB::log("Found ".count($rows)." items to analyze.");
|
1590 |
+
|
1591 |
+
foreach ($rows as $row) {
|
1592 |
+
|
1593 |
+
if ( $row['source_type'] == 'post' ){
|
1594 |
+
|
1595 |
+
//FB::log("Parsing post ".$row['source_id']);
|
1596 |
+
|
1597 |
+
//Remove instances associated with this post
|
1598 |
+
$q = "DELETE FROM {$wpdb->prefix}blc_instances
|
1599 |
+
WHERE source_id = %d AND (source_type = 'post' OR source_type='custom_field')";
|
1600 |
+
$q = $wpdb->prepare($q, intval($row['source_id']));
|
1601 |
+
|
1602 |
+
//FB::log($q, "Executing query");
|
1603 |
+
|
1604 |
+
if ( $wpdb->query( $q ) === false ){
|
1605 |
+
//FB::error($wpdb->last_error, "Database error");
|
1606 |
+
}
|
1607 |
+
|
1608 |
+
//Gather links and images from the post
|
1609 |
+
$this->parse_post( $row['post_content'], $row['source_id'] );
|
1610 |
+
//Gather links from custom fields
|
1611 |
+
$this->parse_post_meta( $row['source_id'] );
|
1612 |
+
|
1613 |
+
//Some link records might be orhpaned now
|
1614 |
+
$orphans_possible = true;
|
1615 |
+
|
1616 |
+
} else {
|
1617 |
+
|
1618 |
+
//FB::log("Parsing bookmark ".$row['source_id']);
|
1619 |
+
|
1620 |
+
//Remove instances associated with this bookmark
|
1621 |
+
$q = "DELETE FROM {$wpdb->prefix}blc_instances
|
1622 |
+
WHERE source_id = %d AND source_type = 'blogroll'";
|
1623 |
+
$q = $wpdb->prepare($q, intval($row['source_id']));
|
1624 |
+
//FB::log($q, "Executing query");
|
1625 |
+
|
1626 |
+
if ( $wpdb->query( $q ) === false ){
|
1627 |
+
//FB::error($wpdb->last_error, "Database error");
|
1628 |
+
}
|
1629 |
+
|
1630 |
+
//(Re)add the instance and link
|
1631 |
+
$this->parse_blogroll_link( $row );
|
1632 |
+
|
1633 |
+
//Some link records might be orhpaned now
|
1634 |
+
$orphans_possible = true;
|
1635 |
+
|
1636 |
+
}
|
1637 |
+
|
1638 |
+
//Update the table to indicate the item has been parsed
|
1639 |
+
$this->mark_synched( $row['source_id'], $row['source_type'] );
|
1640 |
+
|
1641 |
+
//Check if we still have some execution time left
|
1642 |
+
if( $this->execution_time() > $max_execution_time ){
|
1643 |
+
//FB::log('The alloted execution time has run out');
|
1644 |
+
$this->cleanup_links();
|
1645 |
+
$this->release_lock();
|
1646 |
+
return;
|
1647 |
+
}
|
1648 |
+
|
1649 |
+
}
|
1650 |
+
|
1651 |
+
}
|
1652 |
+
|
1653 |
+
//FB::log('No unparsed items found.');
|
1654 |
+
$still_need_resynch = false;
|
1655 |
+
|
1656 |
+
if ( $wpdb->last_error ){
|
1657 |
+
//FB::error($wpdb->last_error, "Database error");
|
1658 |
+
}
|
1659 |
+
|
1660 |
+
} else {
|
1661 |
+
//FB::log('Resynch not required.');
|
1662 |
+
}
|
1663 |
+
|
1664 |
+
/******************************************
|
1665 |
+
Resynch done?
|
1666 |
+
*******************************************/
|
1667 |
+
if ( $this->options['need_resynch'] && !$still_need_resynch ){
|
1668 |
+
$this->options['need_resynch'] = $still_need_resynch;
|
1669 |
+
$this->save_options();
|
1670 |
+
}
|
1671 |
+
|
1672 |
+
/******************************************
|
1673 |
+
Remove orphaned links
|
1674 |
+
*******************************************/
|
1675 |
+
|
1676 |
+
if ( $orphans_possible ) {
|
1677 |
+
//FB::log('Cleaning up the link table.');
|
1678 |
+
$this->cleanup_links();
|
1679 |
+
}
|
1680 |
+
|
1681 |
+
//Check if we still have some execution time left
|
1682 |
+
if( $this->execution_time() > $max_execution_time ){
|
1683 |
+
//FB::log('The alloted execution time has run out');
|
1684 |
+
$this->release_lock();
|
1685 |
+
return;
|
1686 |
+
}
|
1687 |
+
|
1688 |
+
/*****************************************
|
1689 |
+
Check links
|
1690 |
+
******************************************/
|
1691 |
+
//FB::log('Looking for links to check (threshold : '.$check_threshold.')...');
|
1692 |
+
|
1693 |
+
//Select some links that haven't been checked for a long time or
|
1694 |
+
//that are broken and need to be re-checked again.
|
1695 |
+
|
1696 |
+
//Note : This is a slow query, but AFAIK there is no way to speed it up.
|
1697 |
+
//I could put an index on last_check, but that value is almost certainly unique
|
1698 |
+
//for each row so it wouldn't be much better than a full table scan.
|
1699 |
+
$q = "SELECT *, ( last_check < %s ) AS meets_check_threshold
|
1700 |
+
FROM {$wpdb->prefix}blc_links
|
1701 |
+
WHERE
|
1702 |
+
( last_check < %s )
|
1703 |
+
OR
|
1704 |
+
(
|
1705 |
+
( http_code >= 400 OR http_code < 200 OR timeout = 1)
|
1706 |
+
AND check_count < %d
|
1707 |
+
AND check_count > 0
|
1708 |
+
AND last_check < %s
|
1709 |
+
)
|
1710 |
+
ORDER BY last_check ASC
|
1711 |
+
LIMIT 50";
|
1712 |
+
$link_q = $wpdb->prepare($q, $check_threshold, $check_threshold, $this->options['recheck_count'], $recheck_threshold);
|
1713 |
+
//FB::log($link_q);
|
1714 |
+
|
1715 |
+
while ( $links = $wpdb->get_results($link_q, ARRAY_A) ){
|
1716 |
+
|
1717 |
+
//some unchecked links found
|
1718 |
+
//FB::log("Checking ".count($links)." link(s)");
|
1719 |
+
|
1720 |
+
foreach ($links as $link) {
|
1721 |
+
$link_obj = new blcLink($link);
|
1722 |
+
|
1723 |
+
//Does this link need to be checked?
|
1724 |
+
if ( !$this->is_excluded( $link['url'] ) ) {
|
1725 |
+
//Yes, do it
|
1726 |
+
//FB::log("Checking link {$link[link_id]}");
|
1727 |
+
$link_obj->check();
|
1728 |
+
$link_obj->save();
|
1729 |
+
} else {
|
1730 |
+
//Nope, mark it as already checked.
|
1731 |
+
//FB::info("The URL {$link_obj->url} is excluded, marking link {$link_obj->link_id} as already checked.");
|
1732 |
+
$link_obj->last_check = date('Y-m-d H:i:s');
|
1733 |
+
$link_obj->http_code = 200; //Use a fake code so that the link doesn't show up in queries looking for broken links.
|
1734 |
+
$link_obj->timeout = false;
|
1735 |
+
$link_obj->request_duration = 0;
|
1736 |
+
$link_obj->log = "This link wasn't checked because a matching keyword was found on your exclusion list.";
|
1737 |
+
$link_obj->save();
|
1738 |
+
}
|
1739 |
+
|
1740 |
+
//Check if we still have some execution time left
|
1741 |
+
if( $this->execution_time() > $max_execution_time ){
|
1742 |
+
//FB::log('The alloted execution time has run out');
|
1743 |
+
$this->release_lock();
|
1744 |
+
return;
|
1745 |
+
}
|
1746 |
+
}
|
1747 |
+
}
|
1748 |
+
//FB::log('No links need to be checked right now.');
|
1749 |
+
|
1750 |
+
$this->release_lock();
|
1751 |
+
//FB::log('All done.');
|
1752 |
+
}
|
1753 |
+
|
1754 |
+
function ajax_full_status( ){
|
1755 |
+
$status = $this->get_status();
|
1756 |
+
$text = $this->status_text( $status );
|
1757 |
+
|
1758 |
+
echo json_encode( array(
|
1759 |
+
'text' => $text,
|
1760 |
+
'status' => $status,
|
1761 |
+
) );
|
1762 |
+
|
1763 |
+
die();
|
1764 |
+
}
|
1765 |
+
|
1766 |
+
function status_text( $status ){
|
1767 |
+
$text = '';
|
1768 |
+
|
1769 |
+
if( $status['broken_links'] > 0 ){
|
1770 |
+
$text .= sprintf( "<a href='%stools.php?page=view-broken-links' title='View broken links'><strong>Found %d broken link%s</strong></a>",
|
1771 |
+
get_option('wpurl'), $status['broken_links'], ( $status['broken_links'] == 1 )?'':'s' );
|
1772 |
+
} else {
|
1773 |
+
$text .= "No broken links found.";
|
1774 |
+
}
|
1775 |
+
|
1776 |
+
$text .= "<br/>";
|
1777 |
+
|
1778 |
+
if( $status['unchecked_links'] > 0) {
|
1779 |
+
$text .= sprintf( '%d URL%s in the work queue', $status['unchecked_links'], ($status['unchecked_links'] == 1)?'':'s' );
|
1780 |
+
} else {
|
1781 |
+
$text .= "No URLs in the work queue.";
|
1782 |
+
}
|
1783 |
+
|
1784 |
+
$text .= "<br/>";
|
1785 |
+
if ( $status['known_links'] > 0 ){
|
1786 |
+
$text .= sprintf( "Detected %d unique URL%s in %d link%s",
|
1787 |
+
$status['known_links'], $status['known_links'] == 1 ? '' : 's',
|
1788 |
+
$status['known_instances'], $status['known_instances'] == 1 ? '' : 's'
|
1789 |
+
);
|
1790 |
+
if ($this->options['need_resynch']){
|
1791 |
+
$text .= ' and still searching...';
|
1792 |
+
} else {
|
1793 |
+
$text .= '.';
|
1794 |
+
}
|
1795 |
+
} else {
|
1796 |
+
if ($this->options['need_resynch']){
|
1797 |
+
$text .= 'Searching your blog for links...';
|
1798 |
+
} else {
|
1799 |
+
$text .= 'No links detected.';
|
1800 |
+
}
|
1801 |
+
}
|
1802 |
+
|
1803 |
+
return $text;
|
1804 |
+
}
|
1805 |
+
|
1806 |
+
function ajax_dashboard_status(){
|
1807 |
+
//Just display the full status.
|
1808 |
+
$this->ajax_full_status( false );
|
1809 |
+
/*
|
1810 |
+
global $wpdb;
|
1811 |
+
|
1812 |
+
//displays a notification if broken links have been found
|
1813 |
+
$q = "SELECT count(*) FROM {$wpdb->prefix}blc_links
|
1814 |
+
WHERE check_count > 0 AND ( http_code < 200 OR http_code >= 400 OR timeout = 1 )";
|
1815 |
+
$broken_links = $wpdb->get_var($q);
|
1816 |
+
|
1817 |
+
|
1818 |
+
if($broken_links>0){
|
1819 |
+
printf( "<a href='%stools.php?page=view-broken-links' title='View broken links'><strong>Found %d broken link%s</strong></a>",
|
1820 |
+
get_option('wpurl'), $broken_links, ($broken_links==1)?'':'s' );
|
1821 |
+
} else {
|
1822 |
+
echo "No broken links found.";
|
1823 |
+
}
|
1824 |
+
die();
|
1825 |
+
*/
|
1826 |
+
}
|
1827 |
+
|
1828 |
+
function get_status(){
|
1829 |
+
global $wpdb;
|
1830 |
+
|
1831 |
+
$check_threshold=date('Y-m-d H:i:s', strtotime('-'.$this->options['check_threshold'].' hours'));
|
1832 |
+
$recheck_threshold=date('Y-m-d H:i:s', strtotime('-20 minutes'));
|
1833 |
+
|
1834 |
+
$q = "SELECT count(*) FROM {$wpdb->prefix}blc_links WHERE 1";
|
1835 |
+
$known_links = $wpdb->get_var($q);
|
1836 |
+
|
1837 |
+
$q = "SELECT count(*) FROM {$wpdb->prefix}blc_instances WHERE 1";
|
1838 |
+
$known_instances = $wpdb->get_var($q);
|
1839 |
+
|
1840 |
+
$q = "SELECT count(*) FROM {$wpdb->prefix}blc_links
|
1841 |
+
WHERE check_count > 0 AND ( http_code < 200 OR http_code >= 400 OR timeout = 1 )";
|
1842 |
+
$broken_links = $wpdb->get_var($q);
|
1843 |
+
|
1844 |
+
$q = "SELECT count(*) FROM {$wpdb->prefix}blc_links
|
1845 |
+
WHERE
|
1846 |
+
( ( last_check < '$check_threshold' ) OR
|
1847 |
+
(
|
1848 |
+
( http_code >= 400 OR http_code < 200 )
|
1849 |
+
AND check_count < 3
|
1850 |
+
AND last_check < '$recheck_threshold' )
|
1851 |
+
)";
|
1852 |
+
$unchecked_links = $wpdb->get_var($q);
|
1853 |
+
|
1854 |
+
return array(
|
1855 |
+
'check_threshold' => $check_threshold,
|
1856 |
+
'recheck_threshold' => $recheck_threshold,
|
1857 |
+
'known_links' => $known_links,
|
1858 |
+
'known_instances' => $known_instances,
|
1859 |
+
'broken_links' => $broken_links,
|
1860 |
+
'unchecked_links' => $unchecked_links,
|
1861 |
+
);
|
1862 |
+
}
|
1863 |
+
|
1864 |
+
function ajax_work(){
|
1865 |
+
//Run the worker function
|
1866 |
+
$this->work();
|
1867 |
+
die();
|
1868 |
+
}
|
1869 |
+
|
1870 |
+
function ajax_discard(){
|
1871 |
+
//TODO:Rewrite to use JSON instead of plaintext
|
1872 |
+
if (!current_user_can('edit_others_posts')){
|
1873 |
+
die( "You're not allowed to do that!" );
|
1874 |
+
}
|
1875 |
+
|
1876 |
+
if ( isset($_POST['link_id']) ){
|
1877 |
+
//Load the link
|
1878 |
+
$link = new blcLink( intval($_POST['link_id']) );
|
1879 |
+
|
1880 |
+
if ( !$link->valid() ){
|
1881 |
+
die("Oops, I can't find the link ".intval($_POST['link_id']) );
|
1882 |
+
}
|
1883 |
+
//Make it appear "not broken"
|
1884 |
+
$link->last_check = date('Y-m-d H:i:s');
|
1885 |
+
$link->http_code = 200;
|
1886 |
+
$link->timeout = 0;
|
1887 |
+
$link->check_count = 0;
|
1888 |
+
$link->log = "This link was manually marked as working by the user.";
|
1889 |
+
|
1890 |
+
//Save the changes
|
1891 |
+
if ( $link->save() ){
|
1892 |
+
die("OK");
|
1893 |
+
} else {
|
1894 |
+
die("Oops, couldn't modify the link!");
|
1895 |
+
}
|
1896 |
+
} else {
|
1897 |
+
die("Error : link_id not specified");
|
1898 |
+
}
|
1899 |
+
}
|
1900 |
+
|
1901 |
+
function ajax_edit(){
|
1902 |
+
if (!current_user_can('edit_others_posts')){
|
1903 |
+
die( json_encode( array(
|
1904 |
+
'error' => "You're not allowed to do that!"
|
1905 |
+
)));
|
1906 |
+
}
|
1907 |
+
|
1908 |
+
if ( isset($_GET['link_id']) && !empty($_GET['new_url']) ){
|
1909 |
+
//Load the link
|
1910 |
+
$link = new blcLink( intval($_GET['link_id']) );
|
1911 |
+
|
1912 |
+
if ( !$link->valid() ){
|
1913 |
+
die( json_encode( array(
|
1914 |
+
'error' => "Oops, I can't find the link ".intval($_GET['link_id'])
|
1915 |
+
)));
|
1916 |
+
}
|
1917 |
+
|
1918 |
+
$new_url = blcUtility::normalize_url($_GET['new_url']);
|
1919 |
+
if ( !$new_url ){
|
1920 |
+
die( json_encode( array(
|
1921 |
+
'error' => "Oops, the new URL is invalid!"
|
1922 |
+
)));
|
1923 |
+
}
|
1924 |
+
|
1925 |
+
//Try and edit the link
|
1926 |
+
$rez = $link->edit($new_url);
|
1927 |
+
|
1928 |
+
if ( $rez == false ){
|
1929 |
+
die( json_encode( array(
|
1930 |
+
'error' => "An unexpected error occured!"
|
1931 |
+
)));
|
1932 |
+
} else {
|
1933 |
+
$rez['ok'] = 'OK';
|
1934 |
+
die( json_encode($rez) );
|
1935 |
+
}
|
1936 |
+
|
1937 |
+
} else {
|
1938 |
+
die( json_encode( array(
|
1939 |
+
'error' => "Error : link_id or new_url not specified"
|
1940 |
+
)));
|
1941 |
+
}
|
1942 |
+
}
|
1943 |
+
|
1944 |
+
function ajax_unlink(){
|
1945 |
+
if (!current_user_can('edit_others_posts')){
|
1946 |
+
die( json_encode( array(
|
1947 |
+
'error' => "You're not allowed to do that!"
|
1948 |
+
)));
|
1949 |
+
}
|
1950 |
+
|
1951 |
+
if ( isset($_POST['link_id']) ){
|
1952 |
+
//Load the link
|
1953 |
+
$link = new blcLink( intval($_POST['link_id']) );
|
1954 |
+
|
1955 |
+
if ( !$link->valid() ){
|
1956 |
+
die( json_encode( array(
|
1957 |
+
'error' => "Oops, I can't find the link ".intval($_POST['link_id'])
|
1958 |
+
)));
|
1959 |
+
}
|
1960 |
+
|
1961 |
+
//Try and unlink it
|
1962 |
+
if ( $link->unlink() ){
|
1963 |
+
die( json_encode( array(
|
1964 |
+
'ok' => "URL {$link->url} was removed."
|
1965 |
+
)));
|
1966 |
+
} else {
|
1967 |
+
die( json_encode( array(
|
1968 |
+
'error' => "The plugin failed to remove the link."
|
1969 |
+
)));
|
1970 |
+
}
|
1971 |
+
|
1972 |
+
} else {
|
1973 |
+
die( json_encode( array(
|
1974 |
+
'error' => "Error : link_id not specified"
|
1975 |
+
)));
|
1976 |
+
}
|
1977 |
+
}
|
1978 |
+
|
1979 |
+
function ajax_link_details(){
|
1980 |
+
global $wpdb;
|
1981 |
+
|
1982 |
+
if (!current_user_can('edit_others_posts')){
|
1983 |
+
die("You don't have sufficient privileges to access this information!");
|
1984 |
+
}
|
1985 |
+
|
1986 |
+
//FB::log("Loading link details via AJAX");
|
1987 |
+
|
1988 |
+
if ( isset($_GET['link_id']) ){
|
1989 |
+
//FB::info("Link ID found in GET");
|
1990 |
+
$link_id = intval($_GET['link_id']);
|
1991 |
+
} else if ( isset($_POST['link_id']) ){
|
1992 |
+
//FB::info("Link ID found in POST");
|
1993 |
+
$link_id = intval($_POST['link_id']);
|
1994 |
+
} else {
|
1995 |
+
//FB::error('Link ID not specified, you hacking bastard.');
|
1996 |
+
die('Error : link ID not specified');
|
1997 |
+
}
|
1998 |
+
|
1999 |
+
//Load the link. link_details_rwo needs it as an array, so
|
2000 |
+
//we'll have to do this the long way.
|
2001 |
+
$q = "SELECT
|
2002 |
+
links.*,
|
2003 |
+
COUNT(*) as instance_count
|
2004 |
+
|
2005 |
+
FROM
|
2006 |
+
{$wpdb->prefix}blc_links AS links,
|
2007 |
+
{$wpdb->prefix}blc_instances as instances
|
2008 |
+
|
2009 |
+
WHERE
|
2010 |
+
links.link_id = %d
|
2011 |
+
|
2012 |
+
GROUP BY links.link_id";
|
2013 |
+
|
2014 |
+
$link = $wpdb->get_row( $wpdb->prepare($q, $link_id), ARRAY_A );
|
2015 |
+
if ( is_array($link) ){
|
2016 |
+
//FB::info($link, 'Link loaded');
|
2017 |
+
$this->link_details_row($link);
|
2018 |
+
die();
|
2019 |
+
} else {
|
2020 |
+
die ("Failed to load link details (" . $wpdb->last_error . ")");
|
2021 |
+
}
|
2022 |
+
}
|
2023 |
+
|
2024 |
+
function ajax_exclude_link(){
|
2025 |
+
if ( !current_user_can('manage_options') ){
|
2026 |
+
die( json_encode( array(
|
2027 |
+
'error' => "You're not allowed to do that!"
|
2028 |
+
)));
|
2029 |
+
}
|
2030 |
+
|
2031 |
+
if ( isset($_POST['link_id']) ){
|
2032 |
+
//Load the link
|
2033 |
+
$link = new blcLink( intval($_POST['link_id']) );
|
2034 |
+
|
2035 |
+
if ( !$link->valid() ){
|
2036 |
+
die( json_encode( array(
|
2037 |
+
'error' => "Oops, I can't find the link ".intval($_POST['link_id'])
|
2038 |
+
)));
|
2039 |
+
}
|
2040 |
+
|
2041 |
+
//Add the URL to the exclusion list
|
2042 |
+
if ( !in_array( $link->url, $this->options['exclusion_list'] ) ){
|
2043 |
+
$this->options['exclusion_list'][] = $link->url;
|
2044 |
+
//Also mark it as already checked so that it doesn't show up with other broken links.
|
2045 |
+
//FB::info("The URL {$link->url} is excluded, marking link {$link->link_id} as already checked.");
|
2046 |
+
$link->last_check = date('Y-m-d H:i:s');
|
2047 |
+
$link->http_code = 200; //Use a fake code so that the link doesn't show up in queries looking for broken links.
|
2048 |
+
$link->timeout = false;
|
2049 |
+
$link->request_duration = 0;
|
2050 |
+
$link->log = "This link wasn't checked because a matching keyword was found on your exclusion list.";
|
2051 |
+
$link->save();
|
2052 |
+
}
|
2053 |
+
|
2054 |
+
$this->save_options();
|
2055 |
+
|
2056 |
+
die( json_encode( array(
|
2057 |
+
'ok' => "URL {$link->url} added to the exclusion list"
|
2058 |
+
)));
|
2059 |
+
} else {
|
2060 |
+
die( json_encode( array(
|
2061 |
+
'error' => "Link ID not specified"
|
2062 |
+
)));
|
2063 |
+
}
|
2064 |
+
}
|
2065 |
+
|
2066 |
+
/**
|
2067 |
+
* ws_broken_link_checker::acquire_lock()
|
2068 |
+
* Create and lock a temporary file.
|
2069 |
+
*
|
2070 |
+
* @return bool
|
2071 |
+
*/
|
2072 |
+
function acquire_lock(){
|
2073 |
+
//Maybe we already have the lock?
|
2074 |
+
if ( $this->lockfile_handle ){
|
2075 |
+
return true;
|
2076 |
+
}
|
2077 |
+
|
2078 |
+
$fn = $this->lockfile_name();
|
2079 |
+
if ( $fn ){
|
2080 |
+
//Open the lockfile
|
2081 |
+
$this->lockfile_handle = fopen($fn, 'w+');
|
2082 |
+
if ( $this->lockfile_handle ){
|
2083 |
+
//Do an exclusive lock
|
2084 |
+
if (flock($this->lockfile_handle, LOCK_EX | LOCK_NB)) {
|
2085 |
+
//File locked successfully
|
2086 |
+
return true;
|
2087 |
+
} else {
|
2088 |
+
//Something went wrong
|
2089 |
+
fclose($this->lockfile_handle);
|
2090 |
+
$this->lockfile_handle = null;
|
2091 |
+
return false;
|
2092 |
+
}
|
2093 |
+
} else {
|
2094 |
+
//Can't open the file, fail.
|
2095 |
+
return false;
|
2096 |
+
}
|
2097 |
+
} else {
|
2098 |
+
//Uh oh, can't generate a lockfile name. Locking will be disabled.
|
2099 |
+
return true;
|
2100 |
+
};
|
2101 |
+
}
|
2102 |
+
|
2103 |
+
/**
|
2104 |
+
* ws_broken_link_checker::release_lock()
|
2105 |
+
* Unlock and delete the temporary file
|
2106 |
+
*
|
2107 |
+
* @return bool
|
2108 |
+
*/
|
2109 |
+
function release_lock(){
|
2110 |
+
if ( $this->lockfile_handle ){
|
2111 |
+
//Close the file (implicitly releasing the lock)
|
2112 |
+
fclose( $this->lockfile_handle );
|
2113 |
+
//Delete the file
|
2114 |
+
$fn = $this->lockfile_name();
|
2115 |
+
if ( file_exists( $fn ) ) {
|
2116 |
+
unlink( $fn );
|
2117 |
+
}
|
2118 |
+
$this->lockfile_handle = null;
|
2119 |
+
return true;
|
2120 |
+
} else {
|
2121 |
+
//We didn't have the lock anyway...
|
2122 |
+
return false;
|
2123 |
+
}
|
2124 |
+
}
|
2125 |
+
|
2126 |
+
/**
|
2127 |
+
* ws_broken_link_checker::lockfile_name()
|
2128 |
+
* Generate system-specific lockfile filename
|
2129 |
+
*
|
2130 |
+
* @return string A filename or FALSE on error
|
2131 |
+
*/
|
2132 |
+
function lockfile_name(){
|
2133 |
+
//Try the plugin's directory first.
|
2134 |
+
if ( is_writable( dirname(__FILE__) ) ){
|
2135 |
+
return dirname(__FILE__) . '/wp_blc_lock';
|
2136 |
+
} else {
|
2137 |
+
//Try to find the temp directory.
|
2138 |
+
$dummy = tempnam('asdf', 'blc');
|
2139 |
+
if ( $dummy ) {
|
2140 |
+
$path = dirname($dummy);
|
2141 |
+
//Kill the dummy file
|
2142 |
+
if ( file_exists( $dummy ) ) unlink($dummy);
|
2143 |
+
return $path . '/wp_blc_lock';
|
2144 |
+
} else {
|
2145 |
+
//Try /tmp as a last resort
|
2146 |
+
if ( is_writable('/tmp') ){
|
2147 |
+
return '/tmp/wp_blc_lock';
|
2148 |
+
} else {
|
2149 |
+
return false;
|
2150 |
+
}
|
2151 |
+
}
|
2152 |
+
}
|
2153 |
+
}
|
2154 |
+
|
2155 |
+
function hook_add_link( $link_id ){
|
2156 |
+
$this->mark_unsynched( $link_id, 'blogroll' );
|
2157 |
+
}
|
2158 |
+
|
2159 |
+
function hook_edit_link( $link_id ){
|
2160 |
+
$this->mark_unsynched( $link_id, 'blogroll' );
|
2161 |
+
}
|
2162 |
+
|
2163 |
+
function hook_delete_link( $link_id ){
|
2164 |
+
global $wpdb;
|
2165 |
+
//Delete the synch record
|
2166 |
+
$wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->prefix}blc_synch WHERE source_id = %d AND source_type='blogroll'", $link_id ) );
|
2167 |
+
|
2168 |
+
//Get the matching instance record.
|
2169 |
+
$inst = $wpdb->get_row( $wpdb->prepare("SELECT * FROM {$wpdb->prefix}blc_instances WHERE source_id = %d AND source_type = 'blogroll'", $link_id), ARRAY_A );
|
2170 |
+
|
2171 |
+
if ( !$inst ) {
|
2172 |
+
//No instance record? No problem.
|
2173 |
+
return;
|
2174 |
+
}
|
2175 |
|
2176 |
+
//Remove it
|
2177 |
+
$wpdb->query( $wpdb->prepare("DELETE FROM {$wpdb->prefix}blc_instances WHERE instance_id = %d", $inst['instance_id']) );
|
2178 |
+
|
2179 |
+
//Remove the link that was associated with this instance if it has no more related instances.
|
2180 |
+
$this->cleanup_links( $inst['link_id'] );
|
2181 |
+
}
|
2182 |
+
|
2183 |
+
function hook_wp_dashboard_setup(){
|
2184 |
+
if ( function_exists( 'wp_add_dashboard_widget' ) ) {
|
2185 |
+
wp_add_dashboard_widget(
|
2186 |
+
'blc_dashboard_widget',
|
2187 |
+
'Broken Link Checker',
|
2188 |
+
array( &$this, 'dashboard_widget' ),
|
2189 |
+
array( &$this, 'dashboard_widget_control' )
|
2190 |
+
);
|
2191 |
+
}
|
2192 |
+
}
|
2193 |
|
2194 |
}//class ends here
|
2195 |
|
2196 |
} // if class_exists...
|
2197 |
|
2198 |
+
if ( !isset($ws_link_checker) )
|
2199 |
+
$ws_link_checker = new ws_broken_link_checker();
|
2200 |
|
2201 |
?>
|
images/image.png
ADDED
Binary file
|
images/link.png
ADDED
Binary file
|
images/script_code.png
ADDED
Binary file
|
instance-classes.php
ADDED
@@ -0,0 +1,465 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* @author W-Shadow
|
5 |
+
* @copyright 2009
|
6 |
+
*/
|
7 |
+
|
8 |
+
if (!class_exists('blcLinkInstance')) {
|
9 |
+
class blcLinkInstance {
|
10 |
+
|
11 |
+
//Object state
|
12 |
+
var $is_new = false;
|
13 |
+
|
14 |
+
//DB fields
|
15 |
+
var $instance_id = 0;
|
16 |
+
var $link_id = 0;
|
17 |
+
var $source_id = 0;
|
18 |
+
var $source_type = '';
|
19 |
+
var $link_text = '';
|
20 |
+
var $instance_type = '';
|
21 |
+
|
22 |
+
//These are used to pass info to callbacks when editing an instance
|
23 |
+
var $old_url = null;
|
24 |
+
var $new_url = null;
|
25 |
+
|
26 |
+
/**
|
27 |
+
* blcLinkInstance::__construct()
|
28 |
+
* Class constructor
|
29 |
+
*
|
30 |
+
* @param mixed $arg XXXXX look up how to do a multiline doc here (phpdoc)
|
31 |
+
* @return void
|
32 |
+
*/
|
33 |
+
function __construct($arg = null){
|
34 |
+
|
35 |
+
if (is_int($arg)){
|
36 |
+
//Load an instance with ID = $arg from the DB.
|
37 |
+
$q = $wpdb->prepare("SELECT * FROM {$wpdb->prefix}blc_instances WHERE instance_id=%d LIMIT 1", $arg);
|
38 |
+
$arr = $wpdb->get_row( $q, ARRAY_A );
|
39 |
+
|
40 |
+
if ( is_array($arr) ){ //Loaded successfully
|
41 |
+
$this->set_values($arr);
|
42 |
+
} else {
|
43 |
+
//Link instance not found. The object is invalid.
|
44 |
+
}
|
45 |
+
|
46 |
+
} else if (is_array($arg)){
|
47 |
+
$this->set_values($arg);
|
48 |
+
|
49 |
+
//Is this a new instance?
|
50 |
+
$this->is_new = empty($this->instance_id);
|
51 |
+
|
52 |
+
} else {
|
53 |
+
$this->is_new = true;
|
54 |
+
}
|
55 |
+
}
|
56 |
+
|
57 |
+
/**
|
58 |
+
* blcLinkInstance::blcLinkInstance()
|
59 |
+
* Old-style constructor for PHP 4. Do not use.
|
60 |
+
*
|
61 |
+
* @param mixed $arg
|
62 |
+
* @return void
|
63 |
+
*/
|
64 |
+
function blcLinkInstance($arg = null){
|
65 |
+
$this->__construct($arg);
|
66 |
+
}
|
67 |
+
|
68 |
+
/**
|
69 |
+
* blcLinkInstance::valid()
|
70 |
+
* Verifies whether the object represents a valid link instance
|
71 |
+
*
|
72 |
+
* @return bool
|
73 |
+
*/
|
74 |
+
function valid(){
|
75 |
+
//Some basic validation to ensure the required properties are set.
|
76 |
+
return !empty($this->link_id) && !empty($this->instance_type) && !empty($this->source_id)
|
77 |
+
&& !empty($this->source_type) && (!empty($this->instance_id) || $this->is_new);
|
78 |
+
}
|
79 |
+
|
80 |
+
/**
|
81 |
+
* blcLinkInstance::set_values()
|
82 |
+
* Set property values to the ones provided in an array (doesn't sanitize).
|
83 |
+
*
|
84 |
+
* @param array $arr An associative array
|
85 |
+
* @return void
|
86 |
+
*/
|
87 |
+
function set_values($arr){
|
88 |
+
foreach( $arr as $key => $value ){
|
89 |
+
$this->$key = $value;
|
90 |
+
}
|
91 |
+
}
|
92 |
+
|
93 |
+
/**
|
94 |
+
* blcLinkInstance::edit()
|
95 |
+
* Replace this instance's URL with a new one.
|
96 |
+
* Warning : this shouldn't be called directly. Use blcLink->edit() instead.
|
97 |
+
*
|
98 |
+
* @param string $new_url
|
99 |
+
* @return bool
|
100 |
+
*/
|
101 |
+
function edit($old_url, $new_url){
|
102 |
+
echo "Error : The stub function blcLinkInstance->edit() was executed!\r\n";
|
103 |
+
return false;
|
104 |
+
}
|
105 |
+
|
106 |
+
/**
|
107 |
+
* blcLinkInstance::unlink()
|
108 |
+
* Remove this instance from the post/blogroll/etc. Also deletes the appropriate DB record(s).
|
109 |
+
*
|
110 |
+
* @return bool
|
111 |
+
*/
|
112 |
+
function unlink( $url = null ) {
|
113 |
+
//FB::warn("The stub function blcLinkInstance->unlink() was executed!");
|
114 |
+
return false;
|
115 |
+
}
|
116 |
+
|
117 |
+
/**
|
118 |
+
* blcLinkInstance::forget()
|
119 |
+
* Remove the link instance record from database. Doesn't affect the post/link/whatever.
|
120 |
+
*
|
121 |
+
* @return mixed 1 on success, 0 if the instance wasn't found, false on error
|
122 |
+
*/
|
123 |
+
function forget(){
|
124 |
+
global $wpdb;
|
125 |
+
|
126 |
+
if ( !$this->valid() ) return false;
|
127 |
+
|
128 |
+
if ( !empty($this->link_id) ) {
|
129 |
+
$rez = $wpdb->query( $wpdb->prepare("DELETE FROM {$wpdb->prefix}blc_instances WHERE instance_id=%d", $this->instance_id) );
|
130 |
+
$this->link_id = 0;
|
131 |
+
return $rez;
|
132 |
+
} else {
|
133 |
+
return false;
|
134 |
+
}
|
135 |
+
}
|
136 |
+
|
137 |
+
/**
|
138 |
+
* blcLinkInstance::save()
|
139 |
+
* Store the instance in the database.
|
140 |
+
*
|
141 |
+
* @return bool TRUE on success, FALSE on failure
|
142 |
+
*/
|
143 |
+
function save(){
|
144 |
+
global $wpdb;
|
145 |
+
|
146 |
+
if ( !$this->valid() ) return false;
|
147 |
+
|
148 |
+
if ( $this->is_new ){
|
149 |
+
|
150 |
+
//Insert a new row
|
151 |
+
$q = "
|
152 |
+
INSERT INTO {$wpdb->prefix}blc_instances
|
153 |
+
( link_id, source_id, source_type, link_text, instance_type )
|
154 |
+
VALUES( %d, %d, %s, %s, %s )";
|
155 |
+
|
156 |
+
$q = $wpdb->prepare($q, $this->link_id, $this->source_id, $this->source_type,
|
157 |
+
$this->link_text, $this->instance_type );
|
158 |
+
|
159 |
+
$rez = $wpdb->query($q);
|
160 |
+
$rez = $rez !== false;
|
161 |
+
|
162 |
+
if ($rez){
|
163 |
+
$this->instance_id = $wpdb->insert_id;
|
164 |
+
//If the instance was successfully saved then it's no longer "new".
|
165 |
+
$this->is_new = !$rez;
|
166 |
+
}
|
167 |
+
|
168 |
+
return $rez;
|
169 |
+
|
170 |
+
} else {
|
171 |
+
|
172 |
+
//Create a new DB record
|
173 |
+
$q = "UPDATE {$wpdb->prefix}blc_instances
|
174 |
+
SET link_id = %d, source_id = %d, source_type = %s, link_text = %s, instance_type = %s
|
175 |
+
WHERE instance_id = %d";
|
176 |
+
|
177 |
+
$q = $wpdb->prepare($q, $this->link_id, $this->source_id, $this->source_type,
|
178 |
+
$this->link_text, $this->instance_type, $this->instance_id );
|
179 |
+
|
180 |
+
$rez = $wpdb->query($q) !== false;
|
181 |
+
|
182 |
+
if ($rez){
|
183 |
+
//FB::info($this, "Instance updated");
|
184 |
+
} else {
|
185 |
+
//FB::error("DB error while updating instance {$this->instance_id} : {$wpdb->last_error}");
|
186 |
+
}
|
187 |
+
|
188 |
+
return $rez;
|
189 |
+
|
190 |
+
}
|
191 |
+
}
|
192 |
+
|
193 |
+
/**
|
194 |
+
* blcLinkInstance::get_url()
|
195 |
+
* Get the URL associated with this instance
|
196 |
+
*
|
197 |
+
* @return string
|
198 |
+
*/
|
199 |
+
function get_url(){
|
200 |
+
if ( !$this->valid() ){
|
201 |
+
return false;
|
202 |
+
}
|
203 |
+
|
204 |
+
//If the URL isn't specified get it from the link record
|
205 |
+
$link = new blcLink( intval($this->link_id) );
|
206 |
+
return $link->url;
|
207 |
+
}
|
208 |
+
}
|
209 |
+
|
210 |
+
class blcLinkInstance_post_link extends blcLinkInstance {
|
211 |
+
|
212 |
+
function edit($old_url, $new_url){
|
213 |
+
global $wpdb;
|
214 |
+
|
215 |
+
if ( !$this->valid() ){
|
216 |
+
return false;
|
217 |
+
}
|
218 |
+
|
219 |
+
//If the old URL isn't specified get it from the link record
|
220 |
+
if ( empty($old_url) ){
|
221 |
+
$old_url = $this->get_url();
|
222 |
+
}
|
223 |
+
|
224 |
+
//Load the post
|
225 |
+
$post = get_post($this->source_id, ARRAY_A);
|
226 |
+
if (!$post){
|
227 |
+
//FB::error('Can\'t load post ' . $this->source_id);
|
228 |
+
return false;
|
229 |
+
}
|
230 |
+
//FB::info('Post ' . $this->source_id . ' loaded successfully');
|
231 |
+
|
232 |
+
$this->old_url = $old_url;
|
233 |
+
$this->new_url = $new_url;
|
234 |
+
|
235 |
+
//Find all links and replace those that match $old_url.
|
236 |
+
$content = preg_replace_callback(blcUtility::link_pattern(), array(&$this, 'edit_callback'), $post['post_content']);
|
237 |
+
$q = $wpdb->prepare("UPDATE $wpdb->posts SET post_content = %s WHERE id = %d", $content, $this->source_id);
|
238 |
+
|
239 |
+
return $wpdb->query($q) !== false;
|
240 |
+
}
|
241 |
+
|
242 |
+
function edit_callback($matches){
|
243 |
+
$url = blcUtility::normalize_url($matches[3]);
|
244 |
+
|
245 |
+
if ($url == $this->old_url){
|
246 |
+
return $matches[1].$matches[2].$this->new_url.$matches[2].$matches[4].$matches[5].$matches[6];
|
247 |
+
} else {
|
248 |
+
return $matches[0];
|
249 |
+
}
|
250 |
+
}
|
251 |
+
|
252 |
+
function unlink( $url = null ){
|
253 |
+
global $wpdb;
|
254 |
+
|
255 |
+
if ( !$this->valid() ){
|
256 |
+
return false;
|
257 |
+
}
|
258 |
+
|
259 |
+
//If the URL isn't specified get it from the link record
|
260 |
+
if ( empty($url) ){
|
261 |
+
$url = $this->get_url();
|
262 |
+
}
|
263 |
+
|
264 |
+
//Load the post
|
265 |
+
$post = get_post($this->source_id, ARRAY_A);
|
266 |
+
if (!$post){
|
267 |
+
//FB::error('Can\'t load post ' . $this->source_id);
|
268 |
+
return false;
|
269 |
+
}
|
270 |
+
//FB::info('Post ' . $this->source_id . ' loaded successfully');
|
271 |
+
|
272 |
+
//Find all links and remove those that match $url.
|
273 |
+
$this->old_url = $url; //used by the callback
|
274 |
+
$content = preg_replace_callback(blcUtility::link_pattern(), array(&$this, 'unlink_callback'), $post['post_content']);
|
275 |
+
$q = $wpdb->prepare("UPDATE $wpdb->posts SET post_content = %s WHERE id = %d", $content, $this->source_id);
|
276 |
+
|
277 |
+
if ( $wpdb->query($q) !== false ){
|
278 |
+
//Delete the instance record
|
279 |
+
//FB::info("Post updated, deleting instance from DB");
|
280 |
+
return $this->forget() !== false;
|
281 |
+
} else {
|
282 |
+
//FB::error("Failed to update the post");
|
283 |
+
return false;
|
284 |
+
};
|
285 |
+
}
|
286 |
+
|
287 |
+
function unlink_callback($matches){
|
288 |
+
$url = blcUtility::normalize_url($matches[3]);
|
289 |
+
|
290 |
+
if ($url == $this->old_url){
|
291 |
+
return $matches[5]; //just the anchor text
|
292 |
+
} else {
|
293 |
+
return $matches[0]; //return the link unchanged
|
294 |
+
}
|
295 |
+
}
|
296 |
+
|
297 |
+
}
|
298 |
+
|
299 |
+
class blcLinkInstance_post_image extends blcLinkInstance {
|
300 |
+
|
301 |
+
function edit($old_url, $new_url){
|
302 |
+
global $wpdb;
|
303 |
+
|
304 |
+
if ( !$this->valid() ){
|
305 |
+
return false;
|
306 |
+
}
|
307 |
+
|
308 |
+
//If the URL isn't specified get it from the link record
|
309 |
+
if ( empty($old_url) ){
|
310 |
+
$old_url = $this->get_url();
|
311 |
+
}
|
312 |
+
|
313 |
+
//Load the post
|
314 |
+
$post = get_post($this->source_id, ARRAY_A);
|
315 |
+
if (!$post){
|
316 |
+
return false;
|
317 |
+
}
|
318 |
+
|
319 |
+
$this->old_url = $old_url;
|
320 |
+
$this->new_url = $new_url;
|
321 |
+
|
322 |
+
//Find all images and change the URL of those that match $old_url.
|
323 |
+
//Note : this might be inefficient if there's more than one instance of the sme link
|
324 |
+
//in one post, as each instances would be called when editing the link.
|
325 |
+
//Either way, I thing the overhead is small enough to ignore for now.
|
326 |
+
$content = preg_replace_callback(blcUtility::img_pattern(), array(&$this, 'edit_callback'), $post['post_content']);
|
327 |
+
$q = $wpdb->prepare("UPDATE $wpdb->posts SET post_content = %s WHERE id = %d", $content, $this->source_id);
|
328 |
+
|
329 |
+
return $wpdb->query($q) !== false;
|
330 |
+
}
|
331 |
+
|
332 |
+
function edit_callback($matches){
|
333 |
+
$url = blcUtility::normalize_url($matches[3]);
|
334 |
+
|
335 |
+
if ($url == $this->old_url){
|
336 |
+
return $matches[1].$matches[2].$this->new_url.$matches[2].$matches[4].$matches[5];
|
337 |
+
} else {
|
338 |
+
return $matches[0];
|
339 |
+
}
|
340 |
+
}
|
341 |
+
|
342 |
+
function unlink( $url = null ){
|
343 |
+
global $wpdb;
|
344 |
+
|
345 |
+
if ( !$this->valid() ){
|
346 |
+
return false;
|
347 |
+
}
|
348 |
+
|
349 |
+
//If the URL isn't specified get it from the link record
|
350 |
+
if ( empty($url) ){
|
351 |
+
$url = $this->get_url();
|
352 |
+
}
|
353 |
+
|
354 |
+
//Load the post
|
355 |
+
$post = get_post($this->source_id, ARRAY_A);
|
356 |
+
if (!$post){
|
357 |
+
//FB::error('Can\'t load post ' . $this->source_id);
|
358 |
+
return false;
|
359 |
+
}
|
360 |
+
//FB::info('Post ' . $this->source_id . ' loaded successfully');
|
361 |
+
|
362 |
+
//Find all links and remove those that match $url.
|
363 |
+
$this->old_url = $url; //used by the callback
|
364 |
+
$content = preg_replace_callback(blcUtility::img_pattern(), array(&$this, 'unlink_callback'), $post['post_content']);
|
365 |
+
$q = $wpdb->prepare("UPDATE $wpdb->posts SET post_content = %s WHERE id = %d", $content, $this->source_id);
|
366 |
+
|
367 |
+
if ( $wpdb->query($q) !== false ){
|
368 |
+
//Delete the instance record
|
369 |
+
//FB::info("Post updated, deleting instance from DB");
|
370 |
+
return $this->forget() !== false;
|
371 |
+
} else {
|
372 |
+
//FB::error("Failed to update the post");
|
373 |
+
return false;
|
374 |
+
};
|
375 |
+
}
|
376 |
+
|
377 |
+
function unlink_callback($matches){
|
378 |
+
$url = blcUtility::normalize_url($matches[3]);
|
379 |
+
|
380 |
+
if ($url == $this->old_url){
|
381 |
+
return ''; //remove the image completely
|
382 |
+
} else {
|
383 |
+
return $matches[0]; //return the image unchanged
|
384 |
+
}
|
385 |
+
}
|
386 |
+
|
387 |
+
}
|
388 |
+
|
389 |
+
class blcLinkInstance_custom_field_link extends blcLinkInstance {
|
390 |
+
|
391 |
+
function edit($old_url, $new_url){
|
392 |
+
if ( !$this->valid() ){
|
393 |
+
return false;
|
394 |
+
}
|
395 |
+
|
396 |
+
//If the URL isn't specified get it from the link record
|
397 |
+
if ( empty($old_url) ){
|
398 |
+
$old_url = $this->old_url;
|
399 |
+
}
|
400 |
+
|
401 |
+
//FB::log("Changing [{$this->link_text}] to '$new_url' on post {$this->source_id}");
|
402 |
+
//Change the meta value
|
403 |
+
return update_post_meta( $this->source_id, $this->link_text, $new_url, $old_url );
|
404 |
+
}
|
405 |
+
|
406 |
+
function unlink( $url = null ){
|
407 |
+
//Get the URL from the link record if it wasn't specified
|
408 |
+
if ( empty($url) ){
|
409 |
+
$url = $this->get_url();
|
410 |
+
}
|
411 |
+
|
412 |
+
//FB::log("Removing [{$this->link_text}] from post {$this->source_id} where value is '$url'");
|
413 |
+
delete_post_meta( $this->source_id, $this->link_text, $url );
|
414 |
+
|
415 |
+
return $this->forget() !== false;
|
416 |
+
}
|
417 |
+
|
418 |
+
}
|
419 |
+
|
420 |
+
class blcLinkInstance_blogroll_link extends blcLinkInstance {
|
421 |
+
|
422 |
+
function edit($old_url, $new_url){
|
423 |
+
if ( !$this->valid() ){
|
424 |
+
return false;
|
425 |
+
}
|
426 |
+
|
427 |
+
//FB::log("Changing the bookmark [{$this->link_text}] to '$new_url'");
|
428 |
+
//Update the bookmark. Note : wp_update_link calls the edit_link hook, which is also
|
429 |
+
//hooked by the plugin for maintaining bookmark->instance integrity... Conclusion :
|
430 |
+
//don't ever call $instance->edit() in that hook!
|
431 |
+
return wp_update_link( array(
|
432 |
+
'link_id' => $this->source_id,
|
433 |
+
'link_url' => $new_url
|
434 |
+
) );
|
435 |
+
}
|
436 |
+
|
437 |
+
function unlink( $url = null ){
|
438 |
+
if ( !$this->valid() ){
|
439 |
+
return false;
|
440 |
+
}
|
441 |
+
|
442 |
+
//Get the URL from the link record if it wasn't specified
|
443 |
+
if ( empty($url) ){
|
444 |
+
$url = $this->get_url();
|
445 |
+
}
|
446 |
+
|
447 |
+
//FB::log("Removing bookmark [{$this->link_text}] ( ID : {$this->source_id} )");
|
448 |
+
//Note : wp_delete_link calls the delete_link hook, which is also used by the plugin
|
449 |
+
//for removing instances associated with links deleted through the WP link manager.
|
450 |
+
//This means that when you delete a bookmark via the plugin's interface, the plugin will
|
451 |
+
//attempt to delete it twice. Anybody have a better idea?
|
452 |
+
if ( wp_delete_link( $this->source_id ) ){
|
453 |
+
return $this->forget() !== false;
|
454 |
+
} else {
|
455 |
+
//FB::error("Failed to delete the bookmark.");
|
456 |
+
return false;
|
457 |
+
};
|
458 |
+
|
459 |
+
}
|
460 |
+
|
461 |
+
}
|
462 |
+
|
463 |
+
}//class_exists
|
464 |
+
|
465 |
+
?>
|
link-classes.php
ADDED
@@ -0,0 +1,541 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* @author W-Shadow
|
5 |
+
* @copyright 2009
|
6 |
+
*/
|
7 |
+
|
8 |
+
if (!class_exists('blcLink')){
|
9 |
+
class blcLink {
|
10 |
+
|
11 |
+
//Object state
|
12 |
+
var $is_new = false;
|
13 |
+
var $last_headers = '';
|
14 |
+
var $meets_check_threshold = false; //currently unused
|
15 |
+
|
16 |
+
//DB fields
|
17 |
+
var $link_id = 0;
|
18 |
+
var $url = '';
|
19 |
+
var $last_check='0000-00-00 00:00:00';
|
20 |
+
var $check_count = 0;
|
21 |
+
var $final_url = '';
|
22 |
+
var $log = '';
|
23 |
+
var $http_code = 0;
|
24 |
+
var $request_duration = 0;
|
25 |
+
var $timeout = false;
|
26 |
+
var $redirect_count = 0;
|
27 |
+
|
28 |
+
function __construct($arg = null){
|
29 |
+
global $wpdb;
|
30 |
+
|
31 |
+
if (is_int($arg)){
|
32 |
+
//Load a link with ID = $arg from the DB.
|
33 |
+
$q = $wpdb->prepare("SELECT * FROM {$wpdb->prefix}blc_links WHERE link_id=%d LIMIT 1", $arg);
|
34 |
+
$arr = $wpdb->get_row( $q, ARRAY_A );
|
35 |
+
|
36 |
+
if ( is_array($arr) ){ //Loaded successfully
|
37 |
+
$this->set_values($arr);
|
38 |
+
} else {
|
39 |
+
//Link not found. The object is invalid.
|
40 |
+
//I'd throw an error, but that wouldn't be PHP 4 compatible...
|
41 |
+
}
|
42 |
+
|
43 |
+
} else if (is_string($arg)){
|
44 |
+
//Load a link with URL = $arg from the DB. Create a new one if the record isn't found.
|
45 |
+
$q = $wpdb->prepare("SELECT * FROM {$wpdb->prefix}blc_links WHERE url=%s LIMIT 1", $arg);
|
46 |
+
$arr = $wpdb->get_row( $q, ARRAY_A );
|
47 |
+
|
48 |
+
if ( is_array($arr) ){ //Loaded successfully
|
49 |
+
$this->set_values($arr);
|
50 |
+
} else { //Link not found, treat as new
|
51 |
+
$this->url = $arg;
|
52 |
+
$this->is_new = true;
|
53 |
+
}
|
54 |
+
|
55 |
+
} else if (is_array($arg)){
|
56 |
+
$this->set_values($arg);
|
57 |
+
//Is this a new link?
|
58 |
+
$this->is_new = empty($this->link_id);
|
59 |
+
} else {
|
60 |
+
$this->is_new = true;
|
61 |
+
}
|
62 |
+
}
|
63 |
+
|
64 |
+
function blcLink($arg = null){
|
65 |
+
$this->__construct($arg);
|
66 |
+
}
|
67 |
+
|
68 |
+
/**
|
69 |
+
* blcLink::set_values()
|
70 |
+
* Set the internal values to the ones provided in an array (doesn't sanitize).
|
71 |
+
*
|
72 |
+
* @param array $arr An associative array of values
|
73 |
+
* @return void
|
74 |
+
*/
|
75 |
+
function set_values($arr){
|
76 |
+
foreach( $arr as $key => $value ){
|
77 |
+
$this->$key = $value;
|
78 |
+
}
|
79 |
+
}
|
80 |
+
|
81 |
+
/**
|
82 |
+
* blcLink::valid()
|
83 |
+
* Verifies whether the object represents a valid link
|
84 |
+
*
|
85 |
+
* @return bool
|
86 |
+
*/
|
87 |
+
function valid(){
|
88 |
+
return !empty( $this->url ) && ( !empty($this->link_id) || $this->is_new );
|
89 |
+
}
|
90 |
+
|
91 |
+
/**
|
92 |
+
* blcLink::check()
|
93 |
+
* Check if the link is working.
|
94 |
+
*
|
95 |
+
* @return bool
|
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 ) && ( !$this->http_code ) ) {
|
106 |
+
$this->timeout = 1;
|
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 |
+
|
113 |
+
//Update the DB record before actually performing the check.
|
114 |
+
//Useful if something goes terribly wrong while checkint this particular URL.
|
115 |
+
//Note : might be unnecessary.
|
116 |
+
$this->check_count++;
|
117 |
+
$this->last_check = date('Y-m-d H:i:s');
|
118 |
+
$this->log = '';
|
119 |
+
$this->final_url = '';
|
120 |
+
$this->http_code = 0;
|
121 |
+
$this->request_duration = 0;
|
122 |
+
$this->timeout = false;
|
123 |
+
$this->redirect_count = 0;
|
124 |
+
$this->save();
|
125 |
+
|
126 |
+
//Empty some variables before running the check
|
127 |
+
$this->last_headers = '';
|
128 |
+
|
129 |
+
//Save the URL into a local var; we'll need it later.
|
130 |
+
$url = $this->url;
|
131 |
+
|
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 |
+
|
139 |
+
//Kill the #anchor if it's present
|
140 |
+
$anchor_start = strpos($url, '#');
|
141 |
+
if ( $anchor_start !== false ){
|
142 |
+
$url = substr($url, 0, $anchor_start);
|
143 |
+
}
|
144 |
+
|
145 |
+
//******* Use CURL if available ***********
|
146 |
+
if (function_exists('curl_init')) {
|
147 |
+
$ch = curl_init();
|
148 |
+
curl_setopt($ch, CURLOPT_URL, $url);
|
149 |
+
//Masquerade as Internet explorer
|
150 |
+
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)');
|
151 |
+
//Add a semi-plausible referer header to avoid tripping up some bot traps
|
152 |
+
curl_setopt($ch, CURLOPT_REFERER, get_option('home'));
|
153 |
+
|
154 |
+
curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);
|
155 |
+
|
156 |
+
@curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
|
157 |
+
curl_setopt($ch, CURLOPT_MAXREDIRS, 10);
|
158 |
+
|
159 |
+
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 20);
|
160 |
+
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
|
161 |
+
|
162 |
+
curl_setopt($ch, CURLOPT_FAILONERROR, false);
|
163 |
+
|
164 |
+
$nobody=false;
|
165 |
+
if($parts['scheme']=='https'){
|
166 |
+
//TODO: Redirects don't work with HTTPS
|
167 |
+
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
|
168 |
+
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
|
169 |
+
} else {
|
170 |
+
$nobody=true;
|
171 |
+
curl_setopt($ch, CURLOPT_NOBODY, true);
|
172 |
+
//curl_setopt($ch, CURLOPT_RANGE, '0-1023');
|
173 |
+
}
|
174 |
+
|
175 |
+
//We definitely want headers.
|
176 |
+
curl_setopt($ch, CURLOPT_HEADER, true);
|
177 |
+
//register a callback function which will process the headers
|
178 |
+
//this assumes your code is into a class method, and uses $this->readHeader
|
179 |
+
//as the callback function.
|
180 |
+
curl_setopt($ch, CURLOPT_HEADERFUNCTION, array(&$this,'read_header'));
|
181 |
+
|
182 |
+
//Execute the request
|
183 |
+
$response = curl_exec($ch);
|
184 |
+
|
185 |
+
$info = curl_getinfo($ch);
|
186 |
+
$code = intval( $info['http_code'] );
|
187 |
+
|
188 |
+
$this->log .= "=== First try : $code ".(!$code?'(No response) ':'')."===\n\n";
|
189 |
+
$this->log .= $this->last_headers."\n";
|
190 |
+
|
191 |
+
if ( (($code<200) || ($code>=400)) && $nobody) {
|
192 |
+
$this->log .= "Trying a second time with different settings...\n";
|
193 |
+
$this->last_headers = '';
|
194 |
+
|
195 |
+
curl_setopt($ch, CURLOPT_NOBODY, false);
|
196 |
+
curl_setopt($ch, CURLOPT_HTTPGET, true);
|
197 |
+
curl_setopt($ch, CURLOPT_RANGE, '0-2047');
|
198 |
+
$response = curl_exec($ch);
|
199 |
+
|
200 |
+
$info = curl_getinfo($ch);
|
201 |
+
$code = intval( $info['http_code'] );
|
202 |
+
|
203 |
+
$this->log .= "=== Second try : $code ".(!$code?'(No response) ':'')."===\n\n";
|
204 |
+
$this->log .= $this->last_headers."\n";
|
205 |
+
}
|
206 |
+
|
207 |
+
$this->http_code = $code;
|
208 |
+
$this->final_url = $info['url'];
|
209 |
+
$this->request_duration = $info['total_time'];
|
210 |
+
$this->redirect_count = $info['redirect_count'];
|
211 |
+
|
212 |
+
curl_close($ch);
|
213 |
+
|
214 |
+
} elseif (class_exists('Snoopy')) {
|
215 |
+
//******** Use Snoopy if CURL is not available *********
|
216 |
+
//Note : Snoopy doesn't work too well with HTTPS URLs.
|
217 |
+
$this->log .= "<em>(Using Snoopy)</em>\n";
|
218 |
+
|
219 |
+
$start_time = microtime_float(true);
|
220 |
+
|
221 |
+
$snoopy = new Snoopy;
|
222 |
+
$snoopy->read_timeout = 60; //read timeout in seconds
|
223 |
+
$snoopy->fetch($url);
|
224 |
+
|
225 |
+
$this->request_duration = $start_time - microtime_float(true);
|
226 |
+
|
227 |
+
$this->http_code = $snoopy->status; //HTTP status code
|
228 |
+
|
229 |
+
if ($snoopy->error)
|
230 |
+
$this->log .= $snoopy->error."\n";
|
231 |
+
if ($snoopy->timed_out)
|
232 |
+
$this->log .= "Request timed out.\n";
|
233 |
+
|
234 |
+
if ( is_array($snoopy->headers) )
|
235 |
+
$this->log .= implode("", $snoopy->headers)."\n"; //those headers already contain newlines
|
236 |
+
|
237 |
+
if ($snoopy->lastredirectaddr) {
|
238 |
+
$this->final_url = $snoopy->lastredirectaddr;
|
239 |
+
$this->redirect_count = $snoopy->_redirectdepth;
|
240 |
+
}
|
241 |
+
}
|
242 |
+
|
243 |
+
/*"Good" response codes are anything in the 2XX range (e.g "200 OK") and redirects - the 3XX range.
|
244 |
+
HTTP 401 Unauthorized is a special case that is considered OK as well. Other errors - the 4XX range -
|
245 |
+
are treated as "page doesn't exist'". */
|
246 |
+
//TODO: Treat circular redirects as broken links.
|
247 |
+
if ( (($this->http_code>=200) && ($this->http_code<400)) || ($this->http_code == 401) ) {
|
248 |
+
$this->log .= "Link is valid.";
|
249 |
+
//Reset the check count for valid links.
|
250 |
+
$this->check_count = 0;
|
251 |
+
return true;
|
252 |
+
} else {
|
253 |
+
$this->log .= "Link is broken.";
|
254 |
+
if ($this->http_code == 0){
|
255 |
+
//This is probably a timeout
|
256 |
+
$this->timeout = true;
|
257 |
+
$this->log .= "\r\n(Most likely the connection timed out)";
|
258 |
+
}
|
259 |
+
return false;
|
260 |
+
}
|
261 |
+
}
|
262 |
+
|
263 |
+
function read_header($ch, $header){
|
264 |
+
//extracting example data: filename from header field Content-Disposition
|
265 |
+
$this->last_headers .= $header;
|
266 |
+
return strlen($header);
|
267 |
+
}
|
268 |
+
|
269 |
+
/**
|
270 |
+
* blcLink::save()
|
271 |
+
* Save link data to DB.
|
272 |
+
*
|
273 |
+
* @return bool True if saved successfully, false otherwise.
|
274 |
+
*/
|
275 |
+
function save(){
|
276 |
+
global $wpdb;
|
277 |
+
|
278 |
+
if ( !$this->valid() ) return false;
|
279 |
+
|
280 |
+
if ( $this->is_new ){
|
281 |
+
|
282 |
+
//Insert a new row
|
283 |
+
$q = "
|
284 |
+
INSERT INTO {$wpdb->prefix}blc_links
|
285 |
+
( url, last_check, check_count, final_url, redirect_count, log, http_code, request_duration, timeout )
|
286 |
+
VALUES( %s, %s, %d, %s, %d, %s, %d, %f, %d )";
|
287 |
+
$q = $wpdb->prepare($q, $this->url, $this->last_check, $this->check_count, $this->final_url,
|
288 |
+
$this->redirect_count, $this->log, $this->http_code, $this->request_duration, (integer)$this->timeout );
|
289 |
+
$rez = $wpdb->query($q);
|
290 |
+
|
291 |
+
$rez = $rez !== false;
|
292 |
+
|
293 |
+
if ($rez){
|
294 |
+
$this->link_id = $wpdb->insert_id;
|
295 |
+
//echo "Link added, ID : {$this->link_id}\r\n<br>";
|
296 |
+
//If the link was successfully saved then it's no longer "new"
|
297 |
+
$this->is_new = !$rez;
|
298 |
+
} else {
|
299 |
+
echo "Error adding link $url : {$wpdb->last_error}\r\n<br>";
|
300 |
+
}
|
301 |
+
|
302 |
+
return $rez;
|
303 |
+
|
304 |
+
} else {
|
305 |
+
|
306 |
+
//Update an existing DB record
|
307 |
+
$q = "UPDATE {$wpdb->prefix}blc_links SET url=%s, last_check=%s, check_count=%d, final_url=%s,
|
308 |
+
redirect_count=%d, log=%s, http_code=%d, request_duration=%f, timeout=%d
|
309 |
+
WHERE link_id=%d";
|
310 |
+
|
311 |
+
$q = $wpdb->prepare($q, $this->url, $this->last_check, $this->check_count, $this->final_url,
|
312 |
+
$this->redirect_count, $this->log, $this->http_code, $this->request_duration, (integer)$this->timeout, $this->link_id );
|
313 |
+
|
314 |
+
$rez = $wpdb->query($q);
|
315 |
+
if ( $rez !== false ){
|
316 |
+
//echo "Link updated, ID : {$this->link_id}\r\n<br>";
|
317 |
+
} else {
|
318 |
+
echo "Error updating link {$this->link_id} : {$wpdb->last_error}\r\n<br>";
|
319 |
+
}
|
320 |
+
return $rez !== false;
|
321 |
+
}
|
322 |
+
}
|
323 |
+
|
324 |
+
/**
|
325 |
+
* blcLink::edit()
|
326 |
+
* Edit all instances of the link by changing the URL.
|
327 |
+
*
|
328 |
+
* Here's how this really works : create a new link with the new URL. Then edit()
|
329 |
+
* all instances and point them to the new link record. If some instance can't be
|
330 |
+
* edited they will still point to the old record. The old record is deleted
|
331 |
+
* if all instances were edited successfully.
|
332 |
+
*
|
333 |
+
* @param string $new_url
|
334 |
+
* @return array An associative array with the new link ID, the number of successfully edited instances and the number of failed edits.
|
335 |
+
*/
|
336 |
+
function edit($new_url){
|
337 |
+
if ( !$this->valid() ){
|
338 |
+
return false;
|
339 |
+
}
|
340 |
+
|
341 |
+
//FB::info('Changing link '.$this->link_id .' to URL "'.$new_url.'"');
|
342 |
+
|
343 |
+
$instances = $this->get_instances();
|
344 |
+
//Fail if there are no instances
|
345 |
+
if (empty($instances)) return false;
|
346 |
+
|
347 |
+
//Load or create a link with the URL = $new_url
|
348 |
+
$new_link = new blcLink($new_url);
|
349 |
+
$was_new = $new_link->is_new;
|
350 |
+
if ($new_link->is_new) {
|
351 |
+
//FB::log($new_link, 'Saving a new link');
|
352 |
+
$new_link->save(); //so that we get a valid link_id
|
353 |
+
}
|
354 |
+
|
355 |
+
if ( empty($new_link->link_id) ){
|
356 |
+
//FB::error("Failed to create a new link record");
|
357 |
+
return false;
|
358 |
+
}
|
359 |
+
|
360 |
+
//Edit each instance.
|
361 |
+
//FB::info('Editing ' . count($instances) . ' instances');
|
362 |
+
$cnt_okay = $cnt_error = 0;
|
363 |
+
foreach ( $instances as $instance ){
|
364 |
+
if ( $instance->edit( $this->url, $new_url ) ){
|
365 |
+
$cnt_okay++;
|
366 |
+
$instance->link_id = $new_link->link_id;
|
367 |
+
$instance->save();
|
368 |
+
//FB::info($instance, 'Successfully edited instance ' . $instance->instance_id);
|
369 |
+
} else {
|
370 |
+
$cnt_error++;
|
371 |
+
//FB::error($instance, 'Failed to edit instance ' . $instance->instance_id);
|
372 |
+
}
|
373 |
+
}
|
374 |
+
|
375 |
+
//If all instances were edited successfully we can delete the old link record.
|
376 |
+
//And copy the new link data into this object. UNLESS this link is equal to the new link
|
377 |
+
//(which should never happen, but whatever)
|
378 |
+
if ( ( $cnt_error == 0 ) && ( $cnt_okay > 0 ) && ( $this->link_id != $new_link->link_id ) ){
|
379 |
+
$this->forget( false );
|
380 |
+
|
381 |
+
$this->link_id = $new_link->link_id;
|
382 |
+
$this->url = $new_link->url;
|
383 |
+
$this->final_url = $new_link->url;
|
384 |
+
$this->log = $new_link->log;
|
385 |
+
$this->http_code = $new_link->http_code;
|
386 |
+
$this->redirect_count = $new_link->redirect_count;
|
387 |
+
$this->timeout = $new_link->timeout;
|
388 |
+
}
|
389 |
+
|
390 |
+
//On the other hand, if no instances could be edited and the $new_link was really new,
|
391 |
+
//then delete it.
|
392 |
+
if ( ( $cnt_okay == 0 ) && $was_new ){
|
393 |
+
$new_link->forget( false );
|
394 |
+
}
|
395 |
+
|
396 |
+
return array(
|
397 |
+
'new_link_id' => $this->link_id,
|
398 |
+
'cnt_okay' => $cnt_okay,
|
399 |
+
'cnt_error' => $cnt_error,
|
400 |
+
);
|
401 |
+
}
|
402 |
+
|
403 |
+
//Delete (unlink) all instances and the link itself
|
404 |
+
function unlink(){
|
405 |
+
if ( !$this->valid() ){
|
406 |
+
return false;
|
407 |
+
}
|
408 |
+
|
409 |
+
//FB::info($this, 'Removing link');
|
410 |
+
|
411 |
+
$instances = $this->get_instances();
|
412 |
+
//Fail if there are no instances
|
413 |
+
if (empty($instances)) {
|
414 |
+
//FB::warn("This link has no instances. Deleting the link.");
|
415 |
+
return $this->forget( false ) !== false;
|
416 |
+
}
|
417 |
+
|
418 |
+
//Unlink each instance.
|
419 |
+
//FB::info('Unlinking ' . count($instances) . ' instances');
|
420 |
+
$cnt_okay = $cnt_error = 0;
|
421 |
+
foreach ( $instances as $instance ){
|
422 |
+
if ( $instance->unlink( $this->url ) ){
|
423 |
+
$cnt_okay++;
|
424 |
+
//FB::info( $instance, 'Successfully unlinked instance' );
|
425 |
+
} else {
|
426 |
+
$cnt_error++;
|
427 |
+
//FB::error( $instance, 'Failed to unlink instance' );
|
428 |
+
}
|
429 |
+
}
|
430 |
+
|
431 |
+
//If all instances were unlinked successfully we can delete the link record.
|
432 |
+
if ( ( $cnt_error == 0 ) && ( $cnt_okay > 0 ) ){
|
433 |
+
//FB::log('Instances removed, deleting the link.');
|
434 |
+
return $this->forget() !== false;
|
435 |
+
} else {
|
436 |
+
//FB::error("Something went wrong. Unlinked instances : $cnt_okay, errors : $cnt_error");
|
437 |
+
return false;
|
438 |
+
}
|
439 |
+
}
|
440 |
+
|
441 |
+
/**
|
442 |
+
* blcLink::forget()
|
443 |
+
* Remove the link and instance records from the DB. Doesn't alter posts/etc.
|
444 |
+
*
|
445 |
+
* @return mixed 1 on success, 0 if link not found, false on error.
|
446 |
+
*/
|
447 |
+
function forget($remove_instances = true){
|
448 |
+
global $wpdb;
|
449 |
+
if ( !$this->valid() ) return false;
|
450 |
+
|
451 |
+
if ( !empty($this->link_id) ){
|
452 |
+
//FB::info($this, 'Deleting link from DB');
|
453 |
+
|
454 |
+
if ( $remove_instances ){
|
455 |
+
//Remove instances, if any
|
456 |
+
$wpdb->query( $wpdb->prepare("DELETE FROM {$wpdb->prefix}blc_instances WHERE link_id=%d", $this->link_id) );
|
457 |
+
}
|
458 |
+
|
459 |
+
//Remove the link itself
|
460 |
+
$rez = $wpdb->query( $wpdb->prepare("DELETE FROM {$wpdb->prefix}blc_links WHERE link_id=%d", $this->link_id) );
|
461 |
+
$this->link_id = 0;
|
462 |
+
|
463 |
+
return $rez;
|
464 |
+
} else {
|
465 |
+
return false;
|
466 |
+
}
|
467 |
+
|
468 |
+
}
|
469 |
+
|
470 |
+
/**
|
471 |
+
* blcLink::get_instances()
|
472 |
+
* Get a list of the link's instances
|
473 |
+
*
|
474 |
+
* @param integer $max_count The maximum number of instances to return. The default is -1 (no limit)
|
475 |
+
* @return array An array of instance objects or FALSE on failure.
|
476 |
+
*/
|
477 |
+
function get_instances($max_count = -1){
|
478 |
+
global $wpdb;
|
479 |
+
if ( !$this->valid() || empty($this->link_id) ) return false;
|
480 |
+
|
481 |
+
$limit = $max_count > 0 ? "LIMIT $max_count":'';
|
482 |
+
|
483 |
+
//Get all instances of this link
|
484 |
+
$q = $wpdb->prepare("SELECT * FROM {$wpdb->prefix}blc_instances WHERE link_id=%d $limit", $this->link_id);
|
485 |
+
$results = $wpdb->get_results($q, ARRAY_A);
|
486 |
+
|
487 |
+
if ( !empty($results) ) {
|
488 |
+
//Create an object for each instance
|
489 |
+
$instances = array();
|
490 |
+
foreach ($results as $result){
|
491 |
+
//Each source/link type combination has it's own subclass. E.g. _post_image or _blogroll_link.
|
492 |
+
$classname = 'blcLinkInstance_' . $result['source_type'] . '_' . $result['instance_type'];
|
493 |
+
$instances[] = new $classname($result);
|
494 |
+
}
|
495 |
+
return $instances;
|
496 |
+
} else {
|
497 |
+
return false;
|
498 |
+
}
|
499 |
+
}
|
500 |
+
|
501 |
+
/**
|
502 |
+
* blcLink::add_instance()
|
503 |
+
* Record a new instance of the link.
|
504 |
+
*
|
505 |
+
* @param int $source_id
|
506 |
+
* @param string $source_type
|
507 |
+
* @param string $link_text
|
508 |
+
* @param string $instance_type
|
509 |
+
* @return object The created instance or FALSE on error.
|
510 |
+
*/
|
511 |
+
function add_instance($source_id, $source_type, $link_text, $instance_type){
|
512 |
+
|
513 |
+
//The link must be saved before an instance can be added
|
514 |
+
if ($this->is_new) {
|
515 |
+
if ( !$this->save()) return false;
|
516 |
+
}
|
517 |
+
|
518 |
+
//Create a new instance tied to this link
|
519 |
+
$classname = 'blcLinkInstance_' . $source_type . '_' . $instance_type;
|
520 |
+
if ( !class_exists($classname) ){
|
521 |
+
$classname = 'blcLinkInstance';
|
522 |
+
}
|
523 |
+
$inst = new $classname( array(
|
524 |
+
'link_id' => $this->link_id,
|
525 |
+
'source_id' => $source_id,
|
526 |
+
'source_type' => $source_type,
|
527 |
+
'link_text' => $link_text,
|
528 |
+
'instance_type' => $instance_type,
|
529 |
+
) );
|
530 |
+
|
531 |
+
//Save the instance to the DB
|
532 |
+
if ( $inst->save() ){
|
533 |
+
return $inst;
|
534 |
+
} else {
|
535 |
+
return false;
|
536 |
+
};
|
537 |
+
}
|
538 |
+
}
|
539 |
+
} //class_exists
|
540 |
+
|
541 |
+
?>
|
readme.txt
CHANGED
@@ -1,30 +1,43 @@
|
|
1 |
=== Broken Link Checker ===
|
2 |
Contributors: whiteshadow
|
3 |
-
Tags: links, broken, maintenance
|
4 |
-
Requires at least: 2.0
|
5 |
-
Tested up to: 2.
|
6 |
-
Stable tag: 0.
|
7 |
|
8 |
-
This plugin will check your posts for broken links and missing images
|
9 |
|
10 |
== Description ==
|
11 |
-
This plugin
|
12 |
|
13 |
-
|
|
|
|
|
14 |
* Detects links that don't work and missing images.
|
15 |
* Notifies you on the Dashboard if any are found.
|
|
|
16 |
* Makes broken links display differently in posts (optional).
|
17 |
* Link checking intervals can be configured.
|
18 |
* New/modified posts are checked ASAP.
|
19 |
-
* You
|
|
|
|
|
|
|
|
|
|
|
|
|
20 |
|
21 |
-
|
22 |
-
The broken links, if any are found, will show up in a new tab of WP admin panel - Manage -> Broken Links. A notification will also appear on the Dashboard.
|
23 |
|
24 |
-
|
25 |
|
26 |
-
|
27 |
|
|
|
|
|
|
|
|
|
|
|
28 |
|
29 |
== Installation ==
|
30 |
|
1 |
=== Broken Link Checker ===
|
2 |
Contributors: whiteshadow
|
3 |
+
Tags: links, broken, maintenance, blogroll, custom fields, admin
|
4 |
+
Requires at least: 2.7.0
|
5 |
+
Tested up to: 2.8
|
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 on the dashboard if any are found.
|
9 |
|
10 |
== Description ==
|
11 |
+
This plugin will monitor your blog looking for broken links and let you know if any are found.
|
12 |
|
13 |
+
**Features**
|
14 |
+
|
15 |
+
* Monitors links in your posts, pages, the blogroll, and custom fields (optional).
|
16 |
* Detects links that don't work and missing images.
|
17 |
* Notifies you on the Dashboard if any are found.
|
18 |
+
* Also detects redirected links.
|
19 |
* Makes broken links display differently in posts (optional).
|
20 |
* Link checking intervals can be configured.
|
21 |
* New/modified posts are checked ASAP.
|
22 |
+
* You view broken links, redirects, and a complete list of links used on your site, in the *Tools -> Broken Links* tab.
|
23 |
+
* Each link can be edited or unlinked directly via the plugin's page, without manually editing each post.
|
24 |
+
|
25 |
+
**Basic Usage**
|
26 |
+
Once installed, the plugin will begin parsing your posts, bookmarks (AKA blogroll), etc and looking for links. Depending on the size of your site this can take a few minutes or even several hours. When parsing is complete the plugin will start checking each link to see if it works. Again, how long this takes depends on how big your site is and how many links there are. You can monitor the progress and set various link checking options in *Settings -> Link Checker*.
|
27 |
+
|
28 |
+
Note : Currently the plugin only runs when you have at least one tab of the Dashboard open. Cron support will likely be added in a later version.
|
29 |
|
30 |
+
The broken links, if any are found, will show up in a new tab of WP admin panel - *Tools -> Broken Links*. A notification will also appear in the "Broken Link Checker" widget on the Dashboard. To save display space, you can keep the widget closed and configure it to expand automatically when problematic links are detected.
|
|
|
31 |
|
32 |
+
The "Broken Links" tab will by default display broken links that have been detected so far. However, you can use the subnavigation links on that page to view redirects or see a listing of all links - working or not - instead.
|
33 |
|
34 |
+
There are several actions associated with each link listed -
|
35 |
|
36 |
+
* "Details" shows more info about the link. You can also toggle link details by clicking on the "link text" cell.
|
37 |
+
* "Edit URL" lets you change the URL of that link. If the link is present in more than one place (e.g. both in a post and in the blogroll) then all instances of that URL will be changed.
|
38 |
+
* "Unlink" removes the link but leaves the link text intact.
|
39 |
+
* "Exclude" adds the link's URL to the exclusion list. Excluded URLs won't be checked again.
|
40 |
+
* "Discard" lets you manually mark the link as valid. This is useful if you know it was detected as broken only due to a temporary glitch or similar. The link will still be checked normally later.
|
41 |
|
42 |
== Installation ==
|
43 |
|
uninstall.php
ADDED
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* @author W-Shadow
|
5 |
+
* @copyright 2009
|
6 |
+
*
|
7 |
+
* The terrifying uninstallation script.
|
8 |
+
*/
|
9 |
+
|
10 |
+
if( !defined( 'ABSPATH') && !defined('WP_UNINSTALL_PLUGIN') && !current_user_can('delete_plugins') )
|
11 |
+
exit();
|
12 |
+
|
13 |
+
if( !isset($wpdb) )
|
14 |
+
exit();
|
15 |
+
|
16 |
+
//Remove the plugin's settings
|
17 |
+
delete_option('wsblc_options');
|
18 |
+
|
19 |
+
//EXTERMINATE!
|
20 |
+
$wpdb->query( "DROP TABLE IF EXISTS {$wpdb->prefix}blc_linkdata, {$wpdb->prefix}blc_postdata, {$wpdb->prefix}blc_instances, {$wpdb->prefix}blc_links, {$wpdb->prefix}blc_synch" );
|
21 |
+
|
22 |
+
?>
|
utility-class.php
ADDED
@@ -0,0 +1,140 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* @author W-Shadow
|
5 |
+
* @copyright 2009
|
6 |
+
*/
|
7 |
+
|
8 |
+
|
9 |
+
if (!function_exists('json_encode')){
|
10 |
+
//Load JSON functions for PHP < 5.2
|
11 |
+
if (!class_exists('Services_JSON')){
|
12 |
+
require 'JSON.php';
|
13 |
+
}
|
14 |
+
|
15 |
+
//Backwards fompatible json_encode.
|
16 |
+
function json_encode($data) {
|
17 |
+
$json = new Services_JSON();
|
18 |
+
return( $json->encode($data) );
|
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 \3 URL \4
|
29 |
+
return '/(<img[\s]+[^>]*src\s*=\s*)([\"\']?)([^\'\">]+)\2([^<>]*>)/i';
|
30 |
+
}
|
31 |
+
|
32 |
+
//A regexp for links
|
33 |
+
function link_pattern(){
|
34 |
+
// \1 \2 \3 URL \4 \5 Text \6
|
35 |
+
return '/(<a[\s]+[^>]*href\s*=\s*)([\"\']+)([^\'\">]+)\2([^<>]*>)((?sU).*)(<\/a>)/i';
|
36 |
+
}
|
37 |
+
|
38 |
+
/**
|
39 |
+
* blcUtility::normalize_url()
|
40 |
+
*
|
41 |
+
* @param string $url
|
42 |
+
* @return string A normalized URL or FALSE if the URL is invalid
|
43 |
+
*/
|
44 |
+
function normalize_url($url){
|
45 |
+
$parts=@parse_url($url);
|
46 |
+
if(!$parts) return false;
|
47 |
+
|
48 |
+
if(isset($parts['scheme'])) {
|
49 |
+
//Only HTTP(S) links are checked. Other protocols are not supported.
|
50 |
+
if ( ($parts['scheme'] != 'http') && ($parts['scheme'] != 'https') )
|
51 |
+
return false;
|
52 |
+
}
|
53 |
+
|
54 |
+
$url = html_entity_decode($url);
|
55 |
+
$url = preg_replace(
|
56 |
+
array('/([\?&]PHPSESSID=\w+)$/i',
|
57 |
+
'/(#[^\/]*)$/',
|
58 |
+
'/&/',
|
59 |
+
'/^(javascript:.*)/i',
|
60 |
+
'/([\?&]sid=\w+)$/i'
|
61 |
+
),
|
62 |
+
array('','','&','',''),
|
63 |
+
$url);
|
64 |
+
$url=trim($url);
|
65 |
+
|
66 |
+
if($url=='') return false;
|
67 |
+
|
68 |
+
// turn relative URLs into absolute URLs
|
69 |
+
$url = blcUtility::relative2absolute( get_option('siteurl'), $url);
|
70 |
+
return $url;
|
71 |
+
}
|
72 |
+
|
73 |
+
/**
|
74 |
+
* blcUtility::relative2absolute()
|
75 |
+
* Turns a relative URL into an absolute one given a base URL.
|
76 |
+
*
|
77 |
+
* @param string $absolute Base URL
|
78 |
+
* @param string $relative A relative URL
|
79 |
+
* @return string
|
80 |
+
*/
|
81 |
+
function relative2absolute($absolute, $relative) {
|
82 |
+
$p = @parse_url($relative);
|
83 |
+
if(!$p) {
|
84 |
+
//WTF? $relative is a seriously malformed URL
|
85 |
+
return false;
|
86 |
+
}
|
87 |
+
if(isset($p["scheme"])) return $relative;
|
88 |
+
|
89 |
+
$parts=(parse_url($absolute));
|
90 |
+
|
91 |
+
if(substr($relative,0,1)=='/') {
|
92 |
+
$cparts = (explode("/", $relative));
|
93 |
+
array_shift($cparts);
|
94 |
+
} else {
|
95 |
+
if(isset($parts['path'])){
|
96 |
+
$aparts=explode('/',$parts['path']);
|
97 |
+
array_pop($aparts);
|
98 |
+
$aparts=array_filter($aparts);
|
99 |
+
} else {
|
100 |
+
$aparts=array();
|
101 |
+
}
|
102 |
+
|
103 |
+
$rparts = (explode("/", $relative));
|
104 |
+
|
105 |
+
$cparts = array_merge($aparts, $rparts);
|
106 |
+
foreach($cparts as $i => $part) {
|
107 |
+
if($part == '.') {
|
108 |
+
unset($cparts[$i]);
|
109 |
+
} else if($part == '..') {
|
110 |
+
unset($cparts[$i]);
|
111 |
+
unset($cparts[$i-1]);
|
112 |
+
}
|
113 |
+
}
|
114 |
+
}
|
115 |
+
$path = implode("/", $cparts);
|
116 |
+
|
117 |
+
$url = '';
|
118 |
+
if($parts['scheme']) {
|
119 |
+
$url = "$parts[scheme]://";
|
120 |
+
}
|
121 |
+
if(isset($parts['user'])) {
|
122 |
+
$url .= $parts['user'];
|
123 |
+
if(isset($parts['pass'])) {
|
124 |
+
$url .= ":".$parts['pass'];
|
125 |
+
}
|
126 |
+
$url .= "@";
|
127 |
+
}
|
128 |
+
if(isset($parts['host'])) {
|
129 |
+
$url .= $parts['host']."/";
|
130 |
+
}
|
131 |
+
$url .= $path;
|
132 |
+
|
133 |
+
return $url;
|
134 |
+
}
|
135 |
+
|
136 |
+
}//class
|
137 |
+
|
138 |
+
}//class_exists
|
139 |
+
|
140 |
+
?>
|
wsblc_ajax.php
CHANGED
@@ -1,451 +1,5 @@
|
|
1 |
<?php
|
2 |
/*
|
3 |
-
|
4 |
-
*/
|
5 |
-
require_once("../../../wp-config.php");
|
6 |
-
if(!isset($wpdb)) {
|
7 |
-
require_once("../../../wp-includes/wp-db.php");
|
8 |
-
}
|
9 |
-
|
10 |
-
//error_reporting(E_ALL);
|
11 |
-
|
12 |
-
$execution_start_time=microtime(true);
|
13 |
-
|
14 |
-
function execution_time(){
|
15 |
-
global $execution_start_time;
|
16 |
-
return microtime(true)-$execution_start_time;
|
17 |
-
}
|
18 |
-
|
19 |
-
if (!current_user_can('read')) {
|
20 |
-
die("Error: You can't do that. Access denied.");
|
21 |
-
}
|
22 |
-
|
23 |
-
if(!is_object($ws_link_checker)) {
|
24 |
-
die('Fatal error : undefined object; plugin may not be active.');
|
25 |
-
};
|
26 |
-
|
27 |
-
//Regexp for HTML links
|
28 |
-
// \1 \2 \3 \4 \5 \6
|
29 |
-
$url_pattern='/(<a[\s]+[^>]*href\s*=\s*)([\"\']+)([^\'\">]+)\2([^<>]*>)((?sU).*)(<\/a>)/i';
|
30 |
-
//Regexp for IMG tags
|
31 |
-
// \1 \2 \3 \4
|
32 |
-
$img_pattern='/(<img[\s]+[^>]*src\s*=\s*)([\"\']?)([^\'\">]+)\2([^<>]*>)/i';
|
33 |
-
|
34 |
-
$url_to_replace = '';
|
35 |
-
|
36 |
-
$postdata_name=$wpdb->prefix . "blc_postdata";
|
37 |
-
$linkdata_name=$wpdb->prefix . "blc_linkdata";
|
38 |
-
|
39 |
-
$options=$ws_link_checker->options; //get_option('wsblc_options');
|
40 |
-
$max_execution_time=isset($options['max_work_session'])?intval($options['max_work_session']):27;
|
41 |
-
|
42 |
-
// Check for safe mode
|
43 |
-
if( ini_get('safe_mode') ){
|
44 |
-
// Do it the safe mode way
|
45 |
-
$t=ini_get('max_execution_time');
|
46 |
-
if ($t && ($t < $max_execution_time))
|
47 |
-
$max_execution_time = $t-1;
|
48 |
-
} else {
|
49 |
-
// Do it the regular way
|
50 |
-
@set_time_limit(0);
|
51 |
-
}
|
52 |
-
@ignore_user_abort(true);
|
53 |
-
|
54 |
-
$check_treshold=date('Y-m-d H:i:s', strtotime('-'.$options['check_treshold'].' hours'));
|
55 |
-
$recheck_treshold=date('Y-m-d H:i:s', strtotime('-20 minutes'));
|
56 |
-
|
57 |
-
if (!empty($_POST['action'])){
|
58 |
-
$action = $_POST['action'];
|
59 |
-
} else {
|
60 |
-
$action=isset($_GET['action'])?$_GET['action']:'run_check';
|
61 |
-
}
|
62 |
-
|
63 |
-
if($action=='dashboard_status'){
|
64 |
-
/* displays a notification if broken links have been found */
|
65 |
-
$sql="SELECT count(*) FROM $linkdata_name WHERE broken=1";
|
66 |
-
$broken_links=$wpdb->get_var($sql);
|
67 |
-
if($broken_links>0){
|
68 |
-
echo "<div>
|
69 |
-
<h3>Broken Links</h3>
|
70 |
-
<p><a href='".get_option('siteurl')."/wp-admin/edit.php?page=".
|
71 |
-
$ws_link_checker->mybasename."' title='View broken links'>Found $broken_links broken links</a></p>
|
72 |
-
</div>";
|
73 |
-
};
|
74 |
-
|
75 |
-
} else if($action=='full_status'){
|
76 |
-
/* give some stats about the current situation */
|
77 |
-
$sql="SELECT count(*) FROM $postdata_name WHERE last_check<'$check_treshold'";
|
78 |
-
$posts_unchecked=$wpdb->get_var($sql);
|
79 |
-
|
80 |
-
$sql="SELECT count(*) FROM $linkdata_name WHERE last_check<'$check_treshold'";
|
81 |
-
$links_unchecked=$wpdb->get_var($sql);
|
82 |
-
|
83 |
-
$sql="SELECT count(*) FROM $linkdata_name WHERE broken=1";
|
84 |
-
$broken_links=$wpdb->get_var($sql);
|
85 |
-
|
86 |
-
if($broken_links>0){
|
87 |
-
echo "<a href='".get_option('siteurl')."/wp-admin/edit.php?page=".
|
88 |
-
$ws_link_checker->mybasename."' title='View broken links'><strong>Found $broken_links broken links</strong></a>";
|
89 |
-
} else {
|
90 |
-
echo "No broken links found.";
|
91 |
-
}
|
92 |
-
|
93 |
-
echo "<br/>";
|
94 |
-
|
95 |
-
if($posts_unchecked || $links_unchecked) {
|
96 |
-
echo "$posts_unchecked posts and $links_unchecked links in the work queue.";
|
97 |
-
} else {
|
98 |
-
echo "The work queue is empty.";
|
99 |
-
}
|
100 |
-
|
101 |
-
|
102 |
-
} else if($action=='run_check'){
|
103 |
-
/* check for posts that haven't been checked for a long time & parse them for links, put the links in queue */
|
104 |
-
echo "<!-- run_check -->";
|
105 |
-
|
106 |
-
$sql="SELECT b.* FROM $postdata_name a, $wpdb->posts b
|
107 |
-
WHERE a.last_check<'$check_treshold' AND a.post_id=b.id
|
108 |
-
AND b.post_status = 'publish' AND b.post_type IN ('post', 'page')
|
109 |
-
ORDER BY a.last_check ASC LIMIT 40";
|
110 |
-
|
111 |
-
$rows=$wpdb->get_results($sql, OBJECT);
|
112 |
-
if($rows && (count($rows)>0)){
|
113 |
-
//some rows found
|
114 |
-
echo "<!-- parsing pages (rand : ".rand(1,1000).") -->";
|
115 |
-
foreach ($rows as $post) {
|
116 |
-
$wpdb->query("DELETE FROM $linkdata_name WHERE post_id=$post->ID");
|
117 |
-
gather_and_save_links($post->post_content, $post->ID);
|
118 |
-
$wpdb->query("UPDATE $postdata_name SET last_check=NOW() WHERE post_id=$post->ID");
|
119 |
-
}
|
120 |
-
};
|
121 |
-
|
122 |
-
if(execution_time()>$max_execution_time){
|
123 |
-
die('<!-- general timeout -->');
|
124 |
-
}
|
125 |
-
|
126 |
-
/* check the queue and process any links unchecked */
|
127 |
-
$sql="SELECT * FROM $linkdata_name WHERE ".
|
128 |
-
" ((last_check<'$check_treshold') OR ".
|
129 |
-
" (broken=1 AND check_count<5 AND last_check<'$recheck_treshold')) ".
|
130 |
-
" LIMIT 100";
|
131 |
-
|
132 |
-
$links=$wpdb->get_results($sql, OBJECT);
|
133 |
-
if($links && (count($links)>0)){
|
134 |
-
//some unchecked links found
|
135 |
-
echo "<!-- checking ".count($links)." links (rand : ".rand(1,1000).") -->";
|
136 |
-
foreach ($links as $link) {
|
137 |
-
$ws_link_checker->check_link($link);
|
138 |
-
|
139 |
-
if(execution_time()>$max_execution_time){
|
140 |
-
die('<!-- url loop timeout -->');
|
141 |
-
}
|
142 |
-
}
|
143 |
-
};
|
144 |
-
|
145 |
-
die('<!-- /run_check -->');
|
146 |
-
|
147 |
-
} else if ($action=='discard_link'){
|
148 |
-
if (!current_user_can('edit_others_posts')) {
|
149 |
-
die("Error: You can't do that. Access denied.");
|
150 |
-
}
|
151 |
-
$id=intval($_GET['id']);
|
152 |
-
$wpdb->query("DELETE FROM $linkdata_name WHERE id=$id LIMIT 1");
|
153 |
-
if($wpdb->rows_affected<1){
|
154 |
-
die('Error: Couldn\'t remove the link record (DB error).');
|
155 |
-
}
|
156 |
-
die('OK: Link discarded');
|
157 |
-
|
158 |
-
} else if ($action=='remove_link'){
|
159 |
-
|
160 |
-
//actually deletes the link from the post
|
161 |
-
if (!current_user_can('edit_others_posts')) {
|
162 |
-
die("Error: You can't do that. Access denied.");
|
163 |
-
}
|
164 |
-
|
165 |
-
$id=intval($_GET['id']);
|
166 |
-
$sql="SELECT * FROM $linkdata_name WHERE id = $id LIMIT 1";
|
167 |
-
$the_link=$wpdb->get_row($sql, OBJECT, 0);
|
168 |
-
if (!$the_link){
|
169 |
-
die('Error: Link not found');
|
170 |
-
}
|
171 |
-
$the_post = get_post($the_link->post_id, ARRAY_A);
|
172 |
-
if (!$the_post){
|
173 |
-
die('Error: Post not found');
|
174 |
-
}
|
175 |
-
|
176 |
-
//Remove a link or an image from the post HTML
|
177 |
-
if ($the_link->type == 'link')
|
178 |
-
$new_content = unlink_the_link($the_post['post_content'], $the_link->url);
|
179 |
-
elseif ($the_link->type == 'image')
|
180 |
-
$new_content = unlink_image($the_post['post_content'], $the_link->url);
|
181 |
-
|
182 |
-
//Update database
|
183 |
-
$new_content = $wpdb->escape($new_content);
|
184 |
-
$wpdb->query("UPDATE $wpdb->posts SET post_content = '$new_content' WHERE id = $the_link->post_id");
|
185 |
-
if($wpdb->rows_affected<1){
|
186 |
-
die('Error: Couldn\'t update the post ('.mysql_error().').');
|
187 |
-
}
|
188 |
-
$wpdb->query("DELETE FROM $linkdata_name WHERE id=$id LIMIT 1");
|
189 |
-
if($wpdb->rows_affected<1){
|
190 |
-
die('Error: Couldn\'t remove the link record (DB error).');
|
191 |
-
}
|
192 |
-
|
193 |
-
die('OK: Link deleted');
|
194 |
-
|
195 |
-
} else if ($action == 'edit_link'){
|
196 |
-
//edits the link's URL inside the post
|
197 |
-
if (!current_user_can('edit_others_posts')) {
|
198 |
-
die("Error: You can't do that. Access denied.");
|
199 |
-
}
|
200 |
-
|
201 |
-
$id = intval($_GET['id']);
|
202 |
-
$new_url = $_GET['new_url'];
|
203 |
-
|
204 |
-
$sql="SELECT * FROM $linkdata_name WHERE id = $id LIMIT 1";
|
205 |
-
$the_link=$wpdb->get_row($sql, OBJECT, 0);
|
206 |
-
if (!$the_link){
|
207 |
-
die('Error: Link not found');
|
208 |
-
}
|
209 |
-
$the_post = get_post($the_link->post_id, ARRAY_A);
|
210 |
-
if (!$the_post){
|
211 |
-
die('Error: Post not found');
|
212 |
-
}
|
213 |
-
|
214 |
-
if ($the_link->type == 'link')
|
215 |
-
$new_content = edit_the_link($the_post['post_content'], $the_link->url, $new_url);
|
216 |
-
elseif ($the_link->type == 'image')
|
217 |
-
$new_content = edit_image($the_post['post_content'], $the_link->url, $new_url);
|
218 |
-
|
219 |
-
|
220 |
-
if (function_exists('mysql_real_escape_string')){
|
221 |
-
$new_content = mysql_real_escape_string($new_content);
|
222 |
-
} else {
|
223 |
-
$new_content = $wpdb->escape($new_content);
|
224 |
-
}
|
225 |
-
$q = "UPDATE $wpdb->posts SET post_content = '$new_content' WHERE id = $the_link->post_id";
|
226 |
-
//@file_put_contents('q.txt', $q);
|
227 |
-
$wpdb->query($q);
|
228 |
-
if($wpdb->rows_affected<1){
|
229 |
-
die('Error: Couldn\'t update the post ('.mysql_error().').');
|
230 |
-
}
|
231 |
-
$wpdb->query("DELETE FROM $linkdata_name WHERE id=$id LIMIT 1");
|
232 |
-
if($wpdb->rows_affected<1){
|
233 |
-
die('Error: Couldn\'t remove the link record (DB error).');
|
234 |
-
}
|
235 |
-
|
236 |
-
die('OK: Link changed and deleted from the list of broken links.');
|
237 |
-
};
|
238 |
-
|
239 |
-
function parse_link($matches, $post_id){
|
240 |
-
global $wpdb, $linkdata_name, $ws_link_checker;
|
241 |
-
|
242 |
-
$url = $matches[3];
|
243 |
-
$text = $matches[5];
|
244 |
-
|
245 |
-
$url = $ws_link_checker->normalize_url($url);
|
246 |
-
if (!$url) return false;
|
247 |
-
|
248 |
-
if(strlen($url)>5){
|
249 |
-
$wpdb->query(
|
250 |
-
"INSERT INTO $linkdata_name(post_id, url, link_text, type, final_url)
|
251 |
-
VALUES($post_id, '".$wpdb->escape($url)."',
|
252 |
-
'".$wpdb->escape(strip_tags($text))."', 'link',
|
253 |
-
'".$wpdb->escape($url)."')"
|
254 |
-
);
|
255 |
-
};
|
256 |
-
|
257 |
-
return true;
|
258 |
-
}
|
259 |
-
|
260 |
-
function parse_image($matches, $post_id){
|
261 |
-
global $wpdb, $linkdata_name, $ws_link_checker;
|
262 |
-
|
263 |
-
$url=$matches[3];
|
264 |
-
$url = $ws_link_checker->normalize_url($url);
|
265 |
-
if(!$url) return false;
|
266 |
-
|
267 |
-
if(strlen($url)>3){
|
268 |
-
$wpdb->query(
|
269 |
-
"INSERT INTO $linkdata_name(post_id, url, link_text, type, final_url)
|
270 |
-
VALUES($post_id, '".$wpdb->escape($url)."', '[image]', 'image','".$wpdb->escape($url)."')"
|
271 |
-
);
|
272 |
-
};
|
273 |
-
|
274 |
-
return true;
|
275 |
-
}
|
276 |
-
|
277 |
-
function gather_and_save_links($content, $post_id){
|
278 |
-
//gather links (<a href=...>)
|
279 |
-
global $url_pattern, $img_pattern;
|
280 |
-
|
281 |
-
//remove all <code></code> blocks first
|
282 |
-
$content = preg_replace('/<code>.+?<\/code>/i', ' ', $content);
|
283 |
-
|
284 |
-
//echo "Analyzing post $post_id<br>Content = ".htmlspecialchars($content)."<br>";
|
285 |
-
|
286 |
-
if(preg_match_all($url_pattern, $content, $matches, PREG_SET_ORDER)){
|
287 |
-
foreach($matches as $link){
|
288 |
-
//echo "Found link : ".print_r($link,true)."<br>";
|
289 |
-
parse_link($link, $post_id);
|
290 |
-
}
|
291 |
-
};
|
292 |
-
|
293 |
-
//gather images (<img src=...>)
|
294 |
-
if(preg_match_all($img_pattern, $content, $matches, PREG_SET_ORDER)){
|
295 |
-
foreach($matches as $img){
|
296 |
-
parse_image($img, $post_id);
|
297 |
-
}
|
298 |
-
};
|
299 |
-
|
300 |
-
return $content;
|
301 |
-
}
|
302 |
-
|
303 |
-
function page_exists_simple($url){
|
304 |
-
//echo "Checking $url...<br/>";
|
305 |
-
|
306 |
-
$parts=parse_url($url);
|
307 |
-
if(!$parts) return false;
|
308 |
-
|
309 |
-
if(!isset($parts['scheme'])) {
|
310 |
-
$url='http://'.$url;
|
311 |
-
$parts['scheme'] = 'http';
|
312 |
-
}
|
313 |
-
|
314 |
-
//Only HTTP links are checked. All others are automatically considered okay.
|
315 |
-
if ( ($parts['scheme'] != 'http') && ($parts['scheme'] != 'https') )
|
316 |
-
return true;
|
317 |
-
|
318 |
-
$ch = curl_init();
|
319 |
-
curl_setopt($ch, CURLOPT_URL, $url);
|
320 |
-
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)');
|
321 |
-
//curl_setopt($ch, CURLOPT_USERAGENT, 'WordPress/Broken Link Checker (bot)');
|
322 |
-
curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);
|
323 |
-
|
324 |
-
@curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
|
325 |
-
curl_setopt($ch, CURLOPT_MAXREDIRS, 10);
|
326 |
-
|
327 |
-
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 20);
|
328 |
-
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
|
329 |
-
|
330 |
-
curl_setopt($ch, CURLOPT_FAILONERROR, false);
|
331 |
-
|
332 |
-
$nobody=false;
|
333 |
-
if($parts['scheme']=='https'){
|
334 |
-
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
|
335 |
-
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
|
336 |
-
} else {
|
337 |
-
$nobody=true;
|
338 |
-
curl_setopt($ch, CURLOPT_NOBODY, true);
|
339 |
-
//curl_setopt($ch, CURLOPT_RANGE, '0-1023');
|
340 |
-
}
|
341 |
-
curl_setopt($ch, CURLOPT_HEADER, true);
|
342 |
-
|
343 |
-
$response = curl_exec($ch);
|
344 |
-
//echo 'Response 1 : <pre>',$response,'</pre>';
|
345 |
-
$code=intval(curl_getinfo($ch, CURLINFO_HTTP_CODE));
|
346 |
-
//echo "Code 1 : $code<br/>";
|
347 |
-
|
348 |
-
if ( (($code<200) || ($code>=400)) && $nobody) {
|
349 |
-
curl_setopt($ch, CURLOPT_NOBODY, false);
|
350 |
-
curl_setopt($ch, CURLOPT_HTTPGET, true);
|
351 |
-
curl_setopt($ch, CURLOPT_RANGE, '0-2047');
|
352 |
-
$response = curl_exec($ch);
|
353 |
-
//echo 'Response 2 : <pre>',$response,'</pre>';
|
354 |
-
$code=intval(curl_getinfo($ch, CURLINFO_HTTP_CODE));
|
355 |
-
//echo "Code 2 : $code<br/>";
|
356 |
-
}
|
357 |
-
|
358 |
-
curl_close($ch);
|
359 |
-
|
360 |
-
/*"Good" response codes are anything in the 2XX range (e.g "200 OK") and redirects - the 3XX range.
|
361 |
-
HTTP 401 Unauthorized is a special case that is considered OK as well. Other errors - the 4XX range
|
362 |
-
are treated as "page doesn't exist'". */
|
363 |
-
return (($code>=200) && ($code<400)) || ($code == 401);
|
364 |
-
}
|
365 |
-
|
366 |
-
function unlink_image($content, $url){
|
367 |
-
global $img_pattern, $url_to_replace;
|
368 |
-
$url_to_replace = $url;
|
369 |
-
//$url_pattern='/(<a[\s]+[^>]*href\s*=\s*[\"\']?)([^\'\">]+)([\'\"]+[^<>]*>)((?sU).*)(<\/a>)/i';
|
370 |
-
$content = preg_replace_callback($img_pattern, unlink_image_callback, $content);
|
371 |
-
return $content;
|
372 |
-
}
|
373 |
-
|
374 |
-
function unlink_image_callback($matches){
|
375 |
-
global $url_to_replace, $ws_link_checker;
|
376 |
-
$url = $ws_link_checker->normalize_url($matches[3]);
|
377 |
-
|
378 |
-
if ($url == $url_to_replace){
|
379 |
-
return ''; //completely remove the IMG tag
|
380 |
-
} else {
|
381 |
-
return $matches[0];
|
382 |
-
}
|
383 |
-
}
|
384 |
-
|
385 |
-
function unlink_the_link($content, $url){
|
386 |
-
global $url_pattern, $url_to_replace;
|
387 |
-
$url_to_replace = $url;
|
388 |
-
//$url_pattern='/(<a[\s]+[^>]*href\s*=\s*[\"\']?)([^\'\">]+)([\'\"]+[^<>]*>)((?sU).*)(<\/a>)/i';
|
389 |
-
$content = preg_replace_callback($url_pattern, unlink_link_callback, $content);
|
390 |
-
return $content;
|
391 |
-
}
|
392 |
-
|
393 |
-
function unlink_link_callback($matches){
|
394 |
-
global $url_to_replace, $ws_link_checker;
|
395 |
-
$url = $ws_link_checker->normalize_url($matches[3]);
|
396 |
-
$text = $matches[5];
|
397 |
-
|
398 |
-
//echo "$url || $url_to_replace\n";
|
399 |
-
if ($url == $url_to_replace){
|
400 |
-
//echo "Removed '$text' - '$url'\n";
|
401 |
-
return $text;
|
402 |
-
//return "<span class='broken_link'>$text</span>";
|
403 |
-
} else {
|
404 |
-
return $matches[0];
|
405 |
-
}
|
406 |
-
}
|
407 |
-
|
408 |
-
function edit_the_link($content, $url, $newurl){
|
409 |
-
global $url_pattern, $url_to_replace, $new_url;
|
410 |
-
$url_to_replace = $url;
|
411 |
-
$new_url = $newurl;
|
412 |
-
//$url_pattern='/(<a[\s]+[^>]*href\s*=\s*[\"\']?)([^\'\" >]+)\2([^<>]*>)((?sU).*)(<\/a>)/i';
|
413 |
-
$content = preg_replace_callback($url_pattern, edit_link_callback, $content);
|
414 |
-
return $content;
|
415 |
-
}
|
416 |
-
|
417 |
-
function edit_link_callback($matches){
|
418 |
-
global $url_to_replace, $new_url, $ws_link_checker;
|
419 |
-
$url = $ws_link_checker->normalize_url($matches[3]);
|
420 |
-
$text = $matches[5];
|
421 |
-
|
422 |
-
//echo "$url || $url_to_replace\n";
|
423 |
-
if ($url == $url_to_replace){
|
424 |
-
//return $text;
|
425 |
-
// \<a.. \",' \",' \...> \</a>
|
426 |
-
return $matches[1].$matches[2].$new_url.$matches[2].$matches[4].$text.$matches[6];
|
427 |
-
} else {
|
428 |
-
return $matches[0];
|
429 |
-
}
|
430 |
-
}
|
431 |
-
|
432 |
-
function edit_image($content, $url, $newurl){
|
433 |
-
global $img_pattern, $url_to_replace, $new_url;
|
434 |
-
$url_to_replace = $url;
|
435 |
-
$new_url = $newurl;
|
436 |
-
$content = preg_replace_callback($img_pattern, edit_link_callback, $content);
|
437 |
-
return $content;
|
438 |
-
}
|
439 |
-
|
440 |
-
function edit_image_callback($matches){
|
441 |
-
global $url_to_replace, $new_url, $ws_link_checker;
|
442 |
-
$url = $ws_link_checker->normalize_url($matches[3]);
|
443 |
-
|
444 |
-
if ($url == $url_to_replace){
|
445 |
-
// \<img... \",' \url \",' \...>
|
446 |
-
return $matches[1].$matches[2].$new_url.$matches[3].$matches[4];
|
447 |
-
} else {
|
448 |
-
return $matches[0];
|
449 |
-
}
|
450 |
-
}
|
451 |
?>
|
1 |
<?php
|
2 |
/*
|
3 |
+
This file isn't used anymore.
|
4 |
+
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5 |
?>
|